Merge "Always trigger assistant gesture on STEM_PRIMARY long press." into main
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index 0914a83..6b8baf8 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -99,6 +99,7 @@
         "framework_graphics_flags_java_lib",
         "hwui_flags_java_lib",
         "libcore_exported_aconfig_flags_lib",
+        "libgui_flags_java_lib",
         "power_flags_lib",
         "sdk_sandbox_flags_lib",
         "surfaceflinger_flags_java_lib",
@@ -242,6 +243,12 @@
     aconfig_declarations: "com.android.text.flags-aconfig",
 }
 
+rust_aconfig_library {
+    name: "libandroid_text_flags_rust",
+    crate_name: "android_text_flags",
+    aconfig_declarations: "com.android.text.flags-aconfig",
+}
+
 // Location
 aconfig_declarations {
     name: "android.location.flags-aconfig",
@@ -413,17 +420,6 @@
 cc_aconfig_library {
     name: "android.companion.virtualdevice.flags-aconfig-cc",
     aconfig_declarations: "android.companion.virtualdevice.flags-aconfig",
-}
-
-cc_aconfig_library {
-    name: "android.companion.virtualdevice.flags-aconfig-cc-host",
-    aconfig_declarations: "android.companion.virtualdevice.flags-aconfig",
-    host_supported: true,
-}
-
-cc_aconfig_library {
-    name: "android.companion.virtualdevice.flags-aconfig-cc-test",
-    aconfig_declarations: "android.companion.virtualdevice.flags-aconfig",
     host_supported: true,
     mode: "test",
 }
@@ -1213,6 +1209,12 @@
     defaults: ["framework-minus-apex-aconfig-java-defaults"],
 }
 
+java_aconfig_library {
+    name: "libgui_flags_java_lib",
+    aconfig_declarations: "libgui_flags",
+    defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
+
 // Content Capture
 aconfig_declarations {
     name: "android.view.contentcapture.flags-aconfig",
diff --git a/Android.bp b/Android.bp
index f0aa62c..7f4871f 100644
--- a/Android.bp
+++ b/Android.bp
@@ -255,7 +255,7 @@
         "android.hardware.vibrator-V1.1-java",
         "android.hardware.vibrator-V1.2-java",
         "android.hardware.vibrator-V1.3-java",
-        "android.hardware.vibrator-V2-java",
+        "android.hardware.vibrator-V3-java",
         "android.se.omapi-V1-java",
         "android.system.suspend.control.internal-java",
         "devicepolicyprotosnano",
@@ -401,6 +401,7 @@
     ],
     sdk_version: "core_platform",
     static_libs: [
+        "aconfig_storage_reader_java",
         "android.hardware.common.fmq-V1-java",
         "bouncycastle-repackaged-unbundled",
         "com.android.sysprop.foldlockbehavior",
@@ -417,7 +418,6 @@
         "modules-utils-fastxmlserializer",
         "modules-utils-preconditions",
         "modules-utils-statemachine",
-        "modules-utils-synchronous-result-receiver",
         "modules-utils-os",
         "modules-utils-uieventlogger-interface",
         "framework-permission-aidl-java",
@@ -637,7 +637,6 @@
         "core/java/com/android/internal/util/AsyncService.java",
         "core/java/com/android/internal/util/Protocol.java",
         "telephony/java/android/telephony/Annotation.java",
-        ":net-utils-framework-wifi-common-srcs",
     ],
     libs: [
         "framework-annotations-lib",
diff --git a/OWNERS b/OWNERS
index 7ceca32..096da29 100644
--- a/OWNERS
+++ b/OWNERS
@@ -14,6 +14,7 @@
 nandana@google.com #{LAST_RESORT_SUGGESTION}
 narayan@google.com #{LAST_RESORT_SUGGESTION}
 ogunwale@google.com #{LAST_RESORT_SUGGESTION}
+omakoto@google.com #{LAST_RESORT_SUGGESTION}
 roosa@google.com #{LAST_RESORT_SUGGESTION}
 smoreland@google.com #{LAST_RESORT_SUGGESTION}
 yamasani@google.com #{LAST_RESORT_SUGGESTION}
@@ -28,7 +29,7 @@
 # Support bulk translation updates
 per-file */res*/values*/*.xml = byi@google.com, delphij@google.com
 
-per-file **.bp,**.mk = hansson@google.com, joeo@google.com, lamontjones@google.com
+per-file **.bp,**.mk =joeo@google.com, lamontjones@google.com
 per-file TestProtoLibraries.bp = file:platform/platform_testing:/libraries/health/OWNERS
 per-file TestProtoLibraries.bp = file:platform/tools/tradefederation:/OWNERS
 
diff --git a/apct-tests/perftests/core/src/android/conscrypt/conscrypt/CipherEncryptPerfTest.java b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/CipherEncryptPerfTest.java
index c69ae39..36266de 100644
--- a/apct-tests/perftests/core/src/android/conscrypt/conscrypt/CipherEncryptPerfTest.java
+++ b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/CipherEncryptPerfTest.java
@@ -23,6 +23,9 @@
 import org.conscrypt.TestUtils;
 
 import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
 import java.security.Key;
 import java.security.NoSuchAlgorithmException;
 import javax.crypto.Cipher;
@@ -91,21 +94,17 @@
         }
     }
 
-    private Object[] getParams() {
-        return new Object[][] {
-            new Object[] {new Config(BufferType.ARRAY,
-                              MyCipherFactory.CONSCRYPT,
-                              Transformation.AES_CBC_PKCS5)},
-            new Object[] {new Config(BufferType.ARRAY,
-                              MyCipherFactory.CONSCRYPT,
-                              Transformation.AES_ECB_PKCS5)},
-            new Object[] {new Config(BufferType.ARRAY,
-                              MyCipherFactory.CONSCRYPT,
-                              Transformation.AES_GCM_NO)},
-            new Object[] {new Config(BufferType.ARRAY,
-                              MyCipherFactory.CONSCRYPT,
-                              Transformation.AES_GCM_SIV)},
-        };
+    public Collection <Object[]> getParams() {
+        final List<Object[]> params = new ArrayList<>();
+        for (BufferType bufferType : BufferType.values()) {
+            for (CipherFactory cipherFactory : MyCipherFactory.values()) {
+                for (Transformation transformation : Transformation.values()) {
+                  params.add(new Object[] {new Config(
+                                bufferType, cipherFactory, transformation)});
+                }
+            }
+        }
+        return params;
     }
 
     private EncryptStrategy encryptStrategy;
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 dd9f4eb..d7b1c9a2 100644
--- a/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ClientSocketPerfTest.java
+++ b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ClientSocketPerfTest.java
@@ -30,6 +30,9 @@
 import java.io.OutputStream;
 import java.net.SocketException;
 import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.concurrent.Future;
@@ -40,6 +43,7 @@
 import javax.crypto.Cipher;
 import javax.crypto.NoSuchPaddingException;
 
+import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -104,19 +108,26 @@
         }
     }
 
-    private Object[] getParams() {
-        return new Object[][] {
-            new Object[] {new Config(
-                              EndpointFactory.CONSCRYPT,
-                              EndpointFactory.CONSCRYPT,
-                              64,
-                              "AES128-GCM",
-                              ChannelType.CHANNEL,
-                              PerfTestProtocol.TLSv13)},
-        };
+    public Collection getParams() {
+        final List<Object[]> params = new ArrayList<>();
+        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)});
+                }
+            }
+        }
+        return params;
     }
 
-
     private ClientEndpoint client;
     private ServerEndpoint server;
     private byte[] message;
@@ -186,6 +197,7 @@
      */
     @Test
     @Parameters(method = "getParams")
+    @Ignore("b/351034205")
     public void time(Config config) throws Exception {
         reset();
         setup(config);
diff --git a/apct-tests/perftests/core/src/android/conscrypt/conscrypt/EngineFactory.java b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/EngineFactory.java
new file mode 100644
index 0000000..8a0d52d
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/EngineFactory.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.conscrypt;
+
+import org.conscrypt.TestUtils;
+import java.security.Security;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLEngine;
+
+
+/**
+ * Factory for {@link SSLEngine} instances.
+ */
+public class EngineFactory {
+    public EngineFactory() {
+        this(newConscryptClientContext(), newConscryptServerContext());
+    }
+
+    private EngineFactory(SSLContext clientContext, SSLContext serverContext) {
+        this.clientContext = clientContext;
+        this.serverContext = serverContext;
+    }
+
+    private final SSLContext clientContext;
+    private final SSLContext serverContext;
+
+    public SSLEngine newClientEngine(String cipher) {
+        SSLEngine engine = initEngine(clientContext.createSSLEngine(), cipher, true);
+        return engine;
+    }
+
+    public SSLEngine newServerEngine(String cipher) {
+        SSLEngine engine = initEngine(serverContext.createSSLEngine(), cipher, false);
+        return engine;
+    }
+
+    public void dispose(SSLEngine engine) {
+        engine.closeOutbound();
+    }
+
+    private static SSLContext newConscryptClientContext() {
+        return TestUtils.newClientSslContext(TestUtils.getConscryptProvider());
+    }
+
+    private static SSLContext newConscryptServerContext() {
+        return TestUtils.newServerSslContext(TestUtils.getConscryptProvider());
+    }
+
+    static SSLEngine initEngine(SSLEngine engine, String cipher, boolean client) {
+        engine.setEnabledProtocols(new String[]{"TLSv1.2", "TLSv1.3"});
+        engine.setEnabledCipherSuites(new String[] {cipher});
+        engine.setUseClientMode(client);
+        return engine;
+    }
+}
\ No newline at end of file
diff --git a/apct-tests/perftests/core/src/android/conscrypt/conscrypt/EngineHandshakePerfTest.java b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/EngineHandshakePerfTest.java
new file mode 100644
index 0000000..cd0ac96
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/EngineHandshakePerfTest.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Copyright 2017 The Netty Project
+ *
+ * The Netty Project licenses this file to you under the Apache License,
+ * version 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at:
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES 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.conscrypt;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLEngineResult;
+import javax.net.ssl.SSLEngineResult.HandshakeStatus;
+import javax.net.ssl.SSLException;
+
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import androidx.test.filters.LargeTest;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Benchmark comparing handshake performance of various engine implementations to conscrypt.
+ */
+@RunWith(JUnitParamsRunner.class)
+@LargeTest
+public final class EngineHandshakePerfTest {
+    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+    /**
+     * Provider for the test configuration
+     */
+    private class Config {
+        BufferType a_bufferType;
+        String c_cipher;
+        int d_rttMillis;
+        Config(BufferType bufferType,
+            String cipher,
+            int rttMillis) {
+          a_bufferType = bufferType;
+          c_cipher = cipher;
+          d_rttMillis = rttMillis;
+        }
+        public BufferType bufferType() {
+            return a_bufferType;
+        }
+
+        public String cipher() {
+            return c_cipher;
+        }
+
+        public int rttMillis() {
+            return d_rttMillis;
+        }
+    }
+
+    public Collection getParams() {
+        final List<Object[]> params = new ArrayList<>();
+        for (BufferType bufferType : BufferType.values()) {
+            params.add(new Object[] {new Config(bufferType,
+                "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", 100)});
+        }
+        return params;
+    }
+
+    private static final ByteBuffer EMPTY_BUFFER = ByteBuffer.allocateDirect(0);
+
+    private EngineFactory engineFactory = new EngineFactory();
+    private String cipher;
+    private int rttMillis;
+
+    private ByteBuffer clientApplicationBuffer;
+    private ByteBuffer clientPacketBuffer;
+    private ByteBuffer serverApplicationBuffer;
+    private ByteBuffer serverPacketBuffer;
+
+    private void setup(Config config) throws Exception {
+        cipher = config.cipher();
+        rttMillis = config.rttMillis();
+        BufferType bufferType = config.bufferType();
+
+        SSLEngine clientEngine = engineFactory.newClientEngine(cipher);
+        SSLEngine serverEngine = engineFactory.newServerEngine(cipher);
+
+        // Create the application and packet buffers for both endpoints.
+        clientApplicationBuffer = bufferType.newApplicationBuffer(clientEngine);
+        serverApplicationBuffer = bufferType.newApplicationBuffer(serverEngine);
+        clientPacketBuffer = bufferType.newPacketBuffer(clientEngine);
+        serverPacketBuffer = bufferType.newPacketBuffer(serverEngine);
+
+        engineFactory.dispose(clientEngine);
+        engineFactory.dispose(serverEngine);
+    }
+
+    @Test
+    @Parameters(method = "getParams")
+    public void handshake(Config config) throws Exception {
+        setup(config);
+        SSLEngine client = engineFactory.newClientEngine(cipher);
+        SSLEngine server = engineFactory.newServerEngine(cipher);
+        clientApplicationBuffer.clear();
+        clientPacketBuffer.clear();
+        serverApplicationBuffer.clear();
+        serverPacketBuffer.clear();
+
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            client.beginHandshake();
+            server.beginHandshake();
+            doHandshake(client, server);
+        }
+
+        engineFactory.dispose(client);
+        engineFactory.dispose(server);
+    }
+
+    private void doHandshake(SSLEngine client, SSLEngine server) throws SSLException {
+
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            // Send as many client-to-server messages as possible
+            doHalfHandshake(client, server, clientPacketBuffer, serverApplicationBuffer);
+
+            if (client.getHandshakeStatus() == HandshakeStatus.NOT_HANDSHAKING
+                    && server.getHandshakeStatus() == HandshakeStatus.NOT_HANDSHAKING) {
+                return;
+            }
+
+            // Do the same with server-to-client messages
+            doHalfHandshake(server, client, serverPacketBuffer, clientApplicationBuffer);
+
+            if (client.getHandshakeStatus() == HandshakeStatus.NOT_HANDSHAKING
+                    && server.getHandshakeStatus() == HandshakeStatus.NOT_HANDSHAKING) {
+                return;
+            }
+        }
+    }
+
+    private void doHalfHandshake(SSLEngine sender, SSLEngine receiver,
+            ByteBuffer senderPacketBuffer, ByteBuffer receiverApplicationBuffer)
+            throws SSLException {
+        SSLEngineResult senderResult;
+        SSLEngineResult receiverResult;
+
+        do {
+            senderResult = sender.wrap(EMPTY_BUFFER, senderPacketBuffer);
+            runDelegatedTasks(senderResult, sender);
+            senderPacketBuffer.flip();
+            receiverResult = receiver.unwrap(senderPacketBuffer, receiverApplicationBuffer);
+            runDelegatedTasks(receiverResult, receiver);
+            senderPacketBuffer.compact();
+        } while (senderResult.getHandshakeStatus() == HandshakeStatus.NEED_WRAP);
+
+        if (rttMillis > 0) {
+            try {
+                Thread.sleep(rttMillis / 2);
+            } catch (InterruptedException e) {
+                throw new RuntimeException(e);
+            }
+        }
+    }
+
+    private static void runDelegatedTasks(SSLEngineResult result, SSLEngine engine) {
+        if (result.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NEED_TASK) {
+            for (;;) {
+                Runnable task = engine.getDelegatedTask();
+                if (task == null) {
+                    break;
+                }
+                task.run();
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/apct-tests/perftests/core/src/android/conscrypt/conscrypt/EngineWrapPerfTest.java b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/EngineWrapPerfTest.java
new file mode 100644
index 0000000..1fee218
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/EngineWrapPerfTest.java
@@ -0,0 +1,221 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Copyright 2017 The Netty Project
+ *
+ * The Netty Project licenses this file to you under the Apache License,
+ * version 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at:
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES 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.conscrypt;
+
+import static org.conscrypt.TestUtils.doEngineHandshake;
+import static org.conscrypt.TestUtils.newTextMessage;
+import static org.junit.Assert.assertEquals;
+
+import java.nio.ByteBuffer;
+import java.util.Locale;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLEngineResult;
+import javax.net.ssl.SSLException;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import androidx.test.filters.LargeTest;
+
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Benchmark comparing performance of various engine implementations to conscrypt.
+ */
+@RunWith(JUnitParamsRunner.class)
+@LargeTest
+public final class EngineWrapPerfTest {
+    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+    /**
+     * Provider for the benchmark configuration
+     */
+    private class Config {
+        BufferType a_bufferType;
+        int c_messageSize;
+        String d_cipher;
+        Config(BufferType bufferType,
+            int messageSize,
+            String cipher) {
+          a_bufferType = bufferType;
+          c_messageSize = messageSize;
+          d_cipher = cipher;
+        }
+        public BufferType bufferType() {
+            return a_bufferType;
+        }
+
+        public int messageSize() {
+            return c_messageSize;
+        }
+
+        public String cipher() {
+            return d_cipher;
+        }
+    }
+
+    public Collection getParams() {
+        final List<Object[]> params = new ArrayList<>();
+        for (BufferType bufferType : BufferType.values()) {
+            params.add(new Object[] {new Config(bufferType, 64,
+                                    "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256")});
+            params.add(new Object[] {new Config(bufferType, 512,
+                                    "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256")});
+            params.add(new Object[] {new Config(bufferType, 4096,
+                                    "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256")});
+        }
+        return params;
+    }
+
+
+    private EngineFactory engineFactory = new EngineFactory();
+    private String cipher;
+    private SSLEngine clientEngine;
+    private SSLEngine serverEngine;
+
+    private ByteBuffer messageBuffer;
+    private ByteBuffer clientApplicationBuffer;
+    private ByteBuffer clientPacketBuffer;
+    private ByteBuffer serverApplicationBuffer;
+    private ByteBuffer serverPacketBuffer;
+    private ByteBuffer preEncryptedBuffer;
+
+    private void setup(Config config) throws Exception {
+        cipher = config.cipher();
+        BufferType bufferType = config.bufferType();
+
+        clientEngine = engineFactory.newClientEngine(cipher);
+        serverEngine = engineFactory.newServerEngine(cipher);
+
+        // Create the application and packet buffers for both endpoints.
+        clientApplicationBuffer = bufferType.newApplicationBuffer(clientEngine);
+        serverApplicationBuffer = bufferType.newApplicationBuffer(serverEngine);
+        clientPacketBuffer = bufferType.newPacketBuffer(clientEngine);
+        serverPacketBuffer = bufferType.newPacketBuffer(serverEngine);
+
+        // Generate the message to be sent from the client.
+        int messageSize = config.messageSize();
+        messageBuffer = bufferType.newBuffer(messageSize);
+        messageBuffer.put(newTextMessage(messageSize));
+        messageBuffer.flip();
+
+        // Complete the initial TLS handshake.
+        doEngineHandshake(clientEngine, serverEngine, clientApplicationBuffer, clientPacketBuffer,
+                serverApplicationBuffer, serverPacketBuffer, true);
+
+        // Populate the pre-encrypted buffer for use with the unwrap benchmark.
+        preEncryptedBuffer = bufferType.newBuffer(clientEngine.getSession().getPacketBufferSize());
+        doWrap(messageBuffer, preEncryptedBuffer);
+        doUnwrap(preEncryptedBuffer, serverApplicationBuffer);
+    }
+
+    void teardown() {
+        engineFactory.dispose(clientEngine);
+        engineFactory.dispose(serverEngine);
+    }
+
+    @Test
+    @Parameters(method = "getParams")
+    public void wrap(Config config) throws Exception {
+        setup(config);
+
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            // Reset the buffers.
+            messageBuffer.position(0);
+            clientPacketBuffer.clear();
+            // Wrap the original message and create the encrypted data.
+            doWrap(messageBuffer, clientPacketBuffer);
+
+            // Lightweight comparison - just make sure the data length is correct.
+            assertEquals(preEncryptedBuffer.limit(), clientPacketBuffer.limit());
+        }
+        teardown();
+    }
+
+    /**
+     * Simple benchmark that sends a single message from client to server.
+     */
+    @Test
+    @Parameters(method = "getParams")
+    public void wrapAndUnwrap(Config config) throws Exception {
+        setup(config);
+
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            // Reset the buffers.
+            messageBuffer.position(0);
+            clientPacketBuffer.clear();
+            serverApplicationBuffer.clear();
+            // Wrap the original message and create the encrypted data.
+            doWrap(messageBuffer, clientPacketBuffer);
+
+            // Unwrap the encrypted data and get back the original result.
+            doUnwrap(clientPacketBuffer, serverApplicationBuffer);
+
+            // Lightweight comparison - just make sure the unencrypted data length is correct.
+            assertEquals(messageBuffer.limit(), serverApplicationBuffer.limit());
+        }
+        teardown();
+    }
+
+    private void doWrap(ByteBuffer src, ByteBuffer dst) throws SSLException {
+        // Wrap the original message and create the encrypted data.
+        verifyResult(src, clientEngine.wrap(src, dst));
+        dst.flip();
+    }
+
+    private void doUnwrap(ByteBuffer src, ByteBuffer dst) throws SSLException {
+        verifyResult(src, serverEngine.unwrap(src, dst));
+        dst.flip();
+    }
+
+    private void verifyResult(ByteBuffer src, SSLEngineResult result) {
+        if (result.getStatus() != SSLEngineResult.Status.OK) {
+            throw new RuntimeException("Operation returned unexpected result " + result);
+        }
+        if (result.bytesConsumed() != src.limit()) {
+            throw new RuntimeException(
+                    String.format(Locale.US,
+                            "Operation didn't consume all bytes. Expected %d, consumed %d.",
+                            src.limit(), result.bytesConsumed()));
+        }
+    }
+}
\ No newline at end of file
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 ba2a65a..8916a3c 100644
--- a/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ServerSocketPerfTest.java
+++ b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ServerSocketPerfTest.java
@@ -24,6 +24,9 @@
 import java.io.IOException;
 import java.io.OutputStream;
 import java.net.SocketException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.concurrent.Future;
@@ -40,6 +43,7 @@
 import junitparams.JUnitParamsRunner;
 import junitparams.Parameters;
 
+import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -94,15 +98,22 @@
         }
     }
 
-    private Object[] getParams() {
-        return new Object[][] {
-            new Object[] {new Config(
-                              EndpointFactory.CONSCRYPT,
-                              EndpointFactory.CONSCRYPT,
-                              64,
-                              "AES128-GCM",
-                              ChannelType.CHANNEL)},
-        };
+    public Collection getParams() {
+        final List<Object[]> params = new ArrayList<>();
+        for (EndpointFactory endpointFactory : EndpointFactory.values()) {
+            for (ChannelType channelType : ChannelType.values()) {
+                params.add(new Object[] {new Config(endpointFactory,
+                    endpointFactory, 64,
+                    "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", channelType)});
+                params.add(new Object[] {new Config(endpointFactory,
+                    endpointFactory, 512,
+                    "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", channelType)});
+                params.add(new Object[] {new Config(endpointFactory,
+                    endpointFactory, 4096,
+                    "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", channelType)});
+            }
+        }
+        return params;
     }
 
     private ClientEndpoint client;
@@ -121,7 +132,8 @@
         final ChannelType channelType = config.channelType();
 
         server = config.serverFactory().newServer(
-            channelType, config.messageSize(), getCommonProtocolSuites(), ciphers(config));
+            channelType, config.messageSize(),
+            new String[] {"TLSv1.3", "TLSv1.2"}, ciphers(config));
         server.setMessageProcessor(new MessageProcessor() {
             @Override
             public void processMessage(byte[] inMessage, int numBytes, OutputStream os) {
@@ -145,7 +157,8 @@
 
         // Always use the same client for consistency across the benchmarks.
         client = config.clientFactory().newClient(
-                ChannelType.CHANNEL, server.port(), getCommonProtocolSuites(), ciphers(config));
+                ChannelType.CHANNEL, server.port(),
+                new String[] {"TLSv1.3", "TLSv1.2"}, ciphers(config));
         client.start();
 
         // Wait for the initial connection to complete.
@@ -189,6 +202,7 @@
 
     @Test
     @Parameters(method = "getParams")
+    @Ignore("b/351034205")
     public void throughput(Config config) throws Exception {
         setup(config);
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
diff --git a/apct-tests/perftests/core/src/android/conscrypt/conscrypt/Transformation.java b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/Transformation.java
index 78fe732..3542b0a 100644
--- a/apct-tests/perftests/core/src/android/conscrypt/conscrypt/Transformation.java
+++ b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/Transformation.java
@@ -31,7 +31,6 @@
     AES_CBC_PKCS5("AES", "CBC", "PKCS5Padding", new AesKeyGen()),
     AES_ECB_PKCS5("AES", "ECB", "PKCS5Padding", new AesKeyGen()),
     AES_GCM_NO("AES", "GCM", "NoPadding", new AesKeyGen()),
-    AES_GCM_SIV("AES", "GCM_SIV", "NoPadding", new AesKeyGen()),
     RSA_ECB_PKCS1("RSA", "ECB", "PKCS1Padding", new RsaKeyGen());
 
     Transformation(String algorithm, String mode, String padding, KeyGen keyGen) {
diff --git a/apct-tests/perftests/multiuser/Android.bp b/apct-tests/perftests/multiuser/Android.bp
index 1653edc..856dba3 100644
--- a/apct-tests/perftests/multiuser/Android.bp
+++ b/apct-tests/perftests/multiuser/Android.bp
@@ -38,3 +38,10 @@
     ],
     certificate: "platform",
 }
+
+filegroup {
+    name: "multi_user_trace_config",
+    srcs: [
+        "trace_configs/trace_config_multi_user.textproto",
+    ],
+}
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
index ee03e4b..18ee6f2 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -116,7 +116,6 @@
 import android.os.ShellCallback;
 import android.os.ShellCommand;
 import android.os.SystemClock;
-import android.os.SystemProperties;
 import android.os.ThreadLocalWorkSource;
 import android.os.Trace;
 import android.os.UserHandle;
@@ -166,6 +165,7 @@
 import com.android.server.SystemServiceManager;
 import com.android.server.SystemTimeZone;
 import com.android.server.SystemTimeZone.TimeZoneConfidence;
+import com.android.server.pm.UserManagerInternal;
 import com.android.server.pm.permission.PermissionManagerService;
 import com.android.server.pm.permission.PermissionManagerServiceInternal;
 import com.android.server.pm.pkg.AndroidPackage;
@@ -179,9 +179,6 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.text.SimpleDateFormat;
-import java.time.Instant;
-import java.time.zone.ZoneOffsetTransition;
-import java.time.zone.ZoneRules;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Calendar;
@@ -193,7 +190,6 @@
 import java.util.TimeZone;
 import java.util.TreeSet;
 import java.util.concurrent.ThreadLocalRandom;
-import java.util.concurrent.TimeUnit;
 import java.util.function.Predicate;
 
 /**
@@ -233,13 +229,6 @@
 
     private static final long TEMPORARY_QUOTA_DURATION = INTERVAL_DAY;
 
-    // System properties read on some device configurations to initialize time properly and
-    // perform DST transitions at the bootloader level.
-    private static final String TIMEOFFSET_PROPERTY = "persist.sys.time.offset";
-    private static final String DST_TRANSITION_PROPERTY = "persist.sys.time.dst_transition";
-    private static final String DST_OFFSET_PROPERTY = "persist.sys.time.dst_offset";
-
-
     private final Intent mBackgroundIntent
             = new Intent().addFlags(Intent.FLAG_FROM_BACKGROUND);
 
@@ -2127,22 +2116,6 @@
             // "GMT" if the ID is unrecognized). The parameter ID is used here rather than
             // newZone.getId(). It will be rejected if it is invalid.
             timeZoneWasChanged = SystemTimeZone.setTimeZoneId(tzId, confidence, logInfo);
-
-            final int gmtOffset = newZone.getOffset(mInjector.getCurrentTimeMillis());
-            SystemProperties.set(TIMEOFFSET_PROPERTY, String.valueOf(gmtOffset));
-
-            final ZoneRules rules = newZone.toZoneId().getRules();
-            final ZoneOffsetTransition transition = rules.nextTransition(Instant.now());
-            if (null != transition) {
-                // Get the offset between the time after the DST transition and before.
-                final long transitionOffset = TimeUnit.SECONDS.toMillis((
-                        transition.getOffsetAfter().getTotalSeconds()
-                        - transition.getOffsetBefore().getTotalSeconds()));
-                // Time when the next DST transition is programmed.
-                final long nextTransition = TimeUnit.SECONDS.toMillis(transition.toEpochSecond());
-                SystemProperties.set(DST_TRANSITION_PROPERTY, String.valueOf(nextTransition));
-                SystemProperties.set(DST_OFFSET_PROPERTY, String.valueOf(transitionOffset));
-            }
         }
 
         // Clear the default time zone in the system server process. This forces the next call
@@ -3791,8 +3764,10 @@
             }
             mNextAlarmClockForUser.put(userId, alarmClock);
             if (mStartUserBeforeScheduledAlarms) {
-                mUserWakeupStore.addUserWakeup(userId, convertToElapsed(
-                        mNextAlarmClockForUser.get(userId).getTriggerTime(), RTC));
+                if (shouldAddWakeupForUser(userId)) {
+                    mUserWakeupStore.addUserWakeup(userId, convertToElapsed(
+                            mNextAlarmClockForUser.get(userId).getTriggerTime(), RTC));
+                }
             }
         } else {
             if (DEBUG_ALARM_CLOCK) {
@@ -3812,6 +3787,23 @@
     }
 
     /**
+     * Checks whether the user is of type that needs to be started before the alarm.
+     */
+    @VisibleForTesting
+    boolean shouldAddWakeupForUser(@UserIdInt int userId) {
+        final UserManagerInternal umInternal = LocalServices.getService(UserManagerInternal.class);
+        if (umInternal.getUserInfo(userId) == null || umInternal.getUserInfo(userId).isGuest()) {
+            // Guest user should not be started in the background.
+            return false;
+        } else {
+            // SYSTEM user is always running, so no need to schedule wakeup for it.
+            // Profiles are excluded from the wakeup list because users can explicitly stop them and
+            // so starting them in the background would go against the user's intent.
+            return userId != UserHandle.USER_SYSTEM && umInternal.getUserInfo(userId).isFull();
+        }
+    }
+
+    /**
      * Updates NEXT_ALARM_FORMATTED and sends NEXT_ALARM_CLOCK_CHANGED_INTENT for all users
      * for which alarm clocks have changed since the last call to this.
      *
@@ -4523,8 +4515,9 @@
                             final int[] userIds =
                                     mUserWakeupStore.getUserIdsToWakeup(nowELAPSED);
                             for (int i = 0; i < userIds.length; i++) {
-                                if (!mActivityManagerInternal.startUserInBackground(
-                                        userIds[i])) {
+                                if (mActivityManagerInternal.isUserRunning(userIds[i], 0)
+                                        || !mActivityManagerInternal.startUserInBackground(
+                                                userIds[i])) {
                                     mUserWakeupStore.removeUserWakeup(userIds[i]);
                                 }
                             }
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/UserWakeupStore.java b/apex/jobscheduler/service/java/com/android/server/alarm/UserWakeupStore.java
index 93904a7..9fe197d 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/UserWakeupStore.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/UserWakeupStore.java
@@ -20,7 +20,6 @@
 import android.annotation.Nullable;
 import android.os.Environment;
 import android.os.SystemClock;
-import android.os.UserHandle;
 import android.util.AtomicFile;
 import android.util.IndentingPrintWriter;
 import android.util.Pair;
@@ -119,13 +118,10 @@
      * @param alarmTime time when alarm is expected to trigger.
      */
     public void addUserWakeup(int userId, long alarmTime) {
-        // SYSTEM user is always running, so no need to schedule wakeup for it.
-        if (userId != UserHandle.USER_SYSTEM) {
-            synchronized (mUserWakeupLock) {
-                mUserStarts.put(userId, alarmTime - BUFFER_TIME_MS + getUserWakeupOffset());
-            }
-            updateUserListFile();
+        synchronized (mUserWakeupLock) {
+            mUserStarts.put(userId, alarmTime - BUFFER_TIME_MS + getUserWakeupOffset());
         }
+        updateUserListFile();
     }
 
     /**
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
index c3fe031..d92351d 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
@@ -1990,7 +1990,7 @@
             }
         }
         if (android.app.admin.flags.Flags.disallowUserControlBgUsageFix()) {
-            if (!Flags.avoidIdleCheck()) {
+            if (!Flags.avoidIdleCheck() || mInjector.getBootPhase() >= PHASE_BOOT_COMPLETED) {
                 postCheckIdleStates(userId);
             }
         }
diff --git a/api/api.go b/api/api.go
index f0d1f42..b6b1a7e 100644
--- a/api/api.go
+++ b/api/api.go
@@ -63,7 +63,6 @@
 
 type CombinedApis struct {
 	android.ModuleBase
-	android.DefaultableModuleBase
 
 	properties CombinedApisProperties
 }
@@ -74,7 +73,6 @@
 
 func registerBuildComponents(ctx android.RegistrationContext) {
 	ctx.RegisterModuleType("combined_apis", combinedApisModuleFactory)
-	ctx.RegisterModuleType("combined_apis_defaults", CombinedApisModuleDefaultsFactory)
 }
 
 var PrepareForCombinedApisTest = android.FixtureRegisterWithContext(registerBuildComponents)
@@ -576,7 +574,6 @@
 	module := &CombinedApis{}
 	module.AddProperties(&module.properties)
 	android.InitAndroidModule(module)
-	android.InitDefaultableModule(module)
 	android.AddLoadHook(module, func(ctx android.LoadHookContext) { module.createInternalModules(ctx) })
 	return module
 }
@@ -613,16 +610,3 @@
 	}
 	return s2
 }
-
-// Defaults
-type CombinedApisModuleDefaults struct {
-	android.ModuleBase
-	android.DefaultsModuleBase
-}
-
-func CombinedApisModuleDefaultsFactory() android.Module {
-	module := &CombinedApisModuleDefaults{}
-	module.AddProperties(&CombinedApisProperties{})
-	android.InitDefaultsModule(module)
-	return module
-}
diff --git a/core/api/current.txt b/core/api/current.txt
index 69c409b..354e26b 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -34161,6 +34161,7 @@
     field public static final int USAGE_CLASS_UNKNOWN = 0; // 0x0
     field public static final int USAGE_COMMUNICATION_REQUEST = 65; // 0x41
     field public static final int USAGE_HARDWARE_FEEDBACK = 50; // 0x32
+    field @FlaggedApi("android.os.vibrator.vibration_attribute_ime_usage_api") public static final int USAGE_IME_FEEDBACK = 82; // 0x52
     field public static final int USAGE_MEDIA = 19; // 0x13
     field public static final int USAGE_NOTIFICATION = 49; // 0x31
     field public static final int USAGE_PHYSICAL_EMULATION = 34; // 0x22
@@ -43728,6 +43729,7 @@
     field public static final String KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY = "carrier_nr_availabilities_int_array";
     field public static final String KEY_CARRIER_PROVISIONS_WIFI_MERGED_NETWORKS_BOOL = "carrier_provisions_wifi_merged_networks_bool";
     field public static final String KEY_CARRIER_RCS_PROVISIONING_REQUIRED_BOOL = "carrier_rcs_provisioning_required_bool";
+    field @FlaggedApi("com.android.internal.telephony.flags.carrier_roaming_nb_iot_ntn") public static final String KEY_CARRIER_ROAMING_NTN_EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_INT = "carrier_roaming_ntn_emergency_call_to_satellite_handover_type_int";
     field public static final String KEY_CARRIER_SERVICE_NAME_STRING_ARRAY = "carrier_service_name_array";
     field public static final String KEY_CARRIER_SERVICE_NUMBER_STRING_ARRAY = "carrier_service_number_array";
     field public static final String KEY_CARRIER_SETTINGS_ACTIVITY_COMPONENT_NAME_STRING = "carrier_settings_activity_component_name_string";
@@ -43910,6 +43912,7 @@
     field @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") public static final String KEY_SATELLITE_ENTITLEMENT_STATUS_REFRESH_DAYS_INT = "satellite_entitlement_status_refresh_days_int";
     field @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") public static final String KEY_SATELLITE_ENTITLEMENT_SUPPORTED_BOOL = "satellite_entitlement_supported_bool";
     field @FlaggedApi("com.android.internal.telephony.flags.carrier_roaming_nb_iot_ntn") public static final String KEY_SATELLITE_ESOS_SUPPORTED_BOOL = "satellite_esos_supported_bool";
+    field @FlaggedApi("com.android.internal.telephony.flags.carrier_roaming_nb_iot_ntn") public static final String KEY_SATELLITE_SCREEN_OFF_INACTIVITY_TIMEOUT_SEC_INT = "satellite_screen_off_inactivity_timeout_duration_sec_int";
     field public static final String KEY_SHOW_4G_FOR_3G_DATA_ICON_BOOL = "show_4g_for_3g_data_icon_bool";
     field public static final String KEY_SHOW_4G_FOR_LTE_DATA_ICON_BOOL = "show_4g_for_lte_data_icon_bool";
     field public static final String KEY_SHOW_APN_SETTING_CDMA_BOOL = "show_apn_setting_cdma_bool";
@@ -54862,8 +54865,6 @@
     method @Deprecated public void addAction(int);
     method public void addChild(android.view.View);
     method public void addChild(android.view.View, int);
-    method @FlaggedApi("android.view.accessibility.support_multiple_labeledby") public void addLabeledBy(@NonNull android.view.View);
-    method @FlaggedApi("android.view.accessibility.support_multiple_labeledby") public void addLabeledBy(@NonNull android.view.View, int);
     method public boolean canOpenPopup();
     method public int describeContents();
     method public java.util.List<android.view.accessibility.AccessibilityNodeInfo> findAccessibilityNodeInfosByText(String);
@@ -54892,7 +54893,6 @@
     method public int getInputType();
     method public android.view.accessibility.AccessibilityNodeInfo getLabelFor();
     method public android.view.accessibility.AccessibilityNodeInfo getLabeledBy();
-    method @FlaggedApi("android.view.accessibility.support_multiple_labeledby") @NonNull public java.util.List<android.view.accessibility.AccessibilityNodeInfo> getLabeledByList();
     method public int getLiveRegion();
     method public int getMaxTextLength();
     method @NonNull public java.time.Duration getMinDurationBetweenContentChanges();
@@ -54953,8 +54953,6 @@
     method public boolean removeAction(android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction);
     method public boolean removeChild(android.view.View);
     method public boolean removeChild(android.view.View, int);
-    method @FlaggedApi("android.view.accessibility.support_multiple_labeledby") public boolean removeLabeledBy(@NonNull android.view.View);
-    method @FlaggedApi("android.view.accessibility.support_multiple_labeledby") public boolean removeLabeledBy(@NonNull android.view.View, int);
     method public void setAccessibilityDataSensitive(boolean);
     method public void setAccessibilityFocused(boolean);
     method public void setAvailableExtraData(java.util.List<java.lang.String>);
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index e148b5c..df707d1 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -321,7 +321,7 @@
 package android.net.wifi {
 
   public final class WifiMigration {
-    method @FlaggedApi("android.net.wifi.flags.legacy_keystore_to_wifi_blobstore_migration") public static void migrateLegacyKeystoreToWifiBlobstore();
+    method @FlaggedApi("android.net.wifi.flags.legacy_keystore_to_wifi_blobstore_migration_read_only") public static void migrateLegacyKeystoreToWifiBlobstore();
   }
 
 }
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 36a335e..bed1b43 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -409,6 +409,7 @@
     field @FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence") public static final String USE_ON_DEVICE_INTELLIGENCE = "android.permission.USE_ON_DEVICE_INTELLIGENCE";
     field public static final String USE_RESERVED_DISK = "android.permission.USE_RESERVED_DISK";
     field public static final String UWB_PRIVILEGED = "android.permission.UWB_PRIVILEGED";
+    field @FlaggedApi("android.os.vibrator.vendor_vibration_effects") public static final String VIBRATE_VENDOR_EFFECTS = "android.permission.VIBRATE_VENDOR_EFFECTS";
     field public static final String WHITELIST_AUTO_REVOKE_PERMISSIONS = "android.permission.WHITELIST_AUTO_REVOKE_PERMISSIONS";
     field public static final String WHITELIST_RESTRICTED_PERMISSIONS = "android.permission.WHITELIST_RESTRICTED_PERMISSIONS";
     field public static final String WIFI_ACCESS_COEX_UNSAFE_CHANNELS = "android.permission.WIFI_ACCESS_COEX_UNSAFE_CHANNELS";
@@ -3464,6 +3465,7 @@
   public static class VirtualDeviceManager.VirtualDevice implements java.lang.AutoCloseable {
     method public void addActivityListener(@NonNull java.util.concurrent.Executor, @NonNull android.companion.virtual.VirtualDeviceManager.ActivityListener);
     method @FlaggedApi("android.companion.virtual.flags.dynamic_policy") @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void addActivityPolicyExemption(@NonNull android.content.ComponentName);
+    method @FlaggedApi("android.companion.virtualdevice.flags.activity_control_api") @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void addActivityPolicyExemption(@NonNull android.content.ComponentName, int);
     method public void addSoundEffectListener(@NonNull java.util.concurrent.Executor, @NonNull android.companion.virtual.VirtualDeviceManager.SoundEffectListener);
     method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void close();
     method @NonNull public android.content.Context createContext();
@@ -3488,8 +3490,10 @@
     method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void registerIntentInterceptor(@NonNull android.content.IntentFilter, @NonNull java.util.concurrent.Executor, @NonNull android.companion.virtual.VirtualDeviceManager.IntentInterceptorCallback);
     method public void removeActivityListener(@NonNull android.companion.virtual.VirtualDeviceManager.ActivityListener);
     method @FlaggedApi("android.companion.virtual.flags.dynamic_policy") @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void removeActivityPolicyExemption(@NonNull android.content.ComponentName);
+    method @FlaggedApi("android.companion.virtualdevice.flags.activity_control_api") @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void removeActivityPolicyExemption(@NonNull android.content.ComponentName, int);
     method public void removeSoundEffectListener(@NonNull android.companion.virtual.VirtualDeviceManager.SoundEffectListener);
     method @FlaggedApi("android.companion.virtual.flags.dynamic_policy") @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void setDevicePolicy(int, int);
+    method @FlaggedApi("android.companion.virtualdevice.flags.activity_control_api") @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void setDevicePolicy(int, int, int);
     method @FlaggedApi("android.companion.virtual.flags.vdm_custom_ime") @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void setDisplayImePolicy(int, int);
     method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void setShowPointerIcon(boolean);
     method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void unregisterIntentInterceptor(@NonNull android.companion.virtual.VirtualDeviceManager.IntentInterceptorCallback);
@@ -11354,6 +11358,10 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.os.UserManager.EnforcingUser> CREATOR;
   }
 
+  public abstract class VibrationEffect implements android.os.Parcelable {
+    method @FlaggedApi("android.os.vibrator.vendor_vibration_effects") @NonNull @RequiresPermission(android.Manifest.permission.VIBRATE_VENDOR_EFFECTS) public static android.os.VibrationEffect createVendorEffect(@NonNull android.os.PersistableBundle);
+  }
+
   public abstract class Vibrator {
     method @RequiresPermission(android.Manifest.permission.ACCESS_VIBRATOR_STATE) public void addVibratorStateListener(@NonNull android.os.Vibrator.OnVibratorStateChangedListener);
     method @RequiresPermission(android.Manifest.permission.ACCESS_VIBRATOR_STATE) public void addVibratorStateListener(@NonNull java.util.concurrent.Executor, @NonNull android.os.Vibrator.OnVibratorStateChangedListener);
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 88b5275..90af259 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -2577,6 +2577,16 @@
   public static final class VibrationEffect.Composition.UnreachableAfterRepeatingIndefinitelyException extends java.lang.IllegalStateException {
   }
 
+  @FlaggedApi("android.os.vibrator.vendor_vibration_effects") public static final class VibrationEffect.VendorEffect extends android.os.VibrationEffect {
+    method @Nullable public long[] computeCreateWaveformOffOnTimingsOrNull();
+    method public long getDuration();
+    method public int getEffectStrength();
+    method public float getLinearScale();
+    method @NonNull public android.os.PersistableBundle getVendorData();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.os.VibrationEffect.VendorEffect> CREATOR;
+  }
+
   public static class VibrationEffect.VibrationParameter {
     method @NonNull public static android.os.VibrationEffect.VibrationParameter targetAmplitude(@FloatRange(from=0, to=1) float);
     method @NonNull public static android.os.VibrationEffect.VibrationParameter targetFrequency(@FloatRange(from=1) float);
diff --git a/core/java/Android.bp b/core/java/Android.bp
index 128fb62..92bca3c 100644
--- a/core/java/Android.bp
+++ b/core/java/Android.bp
@@ -22,11 +22,9 @@
         ":framework-nfc-non-updatable-sources",
         ":messagequeue-gen",
     ],
-    // Exactly one of the below will be added to srcs by messagequeue-gen
+    // Exactly one MessageQueue.java will be added to srcs by messagequeue-gen
     exclude_srcs: [
-        "android/os/LegacyMessageQueue/MessageQueue.java",
-        "android/os/ConcurrentMessageQueue/MessageQueue.java",
-        "android/os/SemiConcurrentMessageQueue/MessageQueue.java",
+        "android/os/*MessageQueue/**/*.java",
     ],
     visibility: ["//frameworks/base"],
 }
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index cbabb02..90de7ab 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -2214,6 +2214,9 @@
         notifyVoiceInteractionManagerServiceActivityEvent(
                 VoiceInteractionSession.VOICE_INTERACTION_ACTIVITY_EVENT_RESUME);
 
+        // Notify autofill
+        getAutofillClientController().onActivityPostResumed();
+
         mCalled = true;
     }
 
diff --git a/core/java/android/app/ActivityClient.java b/core/java/android/app/ActivityClient.java
index 10dc3c6..62aa5e0 100644
--- a/core/java/android/app/ActivityClient.java
+++ b/core/java/android/app/ActivityClient.java
@@ -25,7 +25,6 @@
 import android.content.ContentProvider;
 import android.content.Intent;
 import android.content.res.Configuration;
-import android.content.res.Resources;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.IBinder;
@@ -660,28 +659,6 @@
         }
     }
 
-    /**
-     * Shows or hides a Camera app compat toggle for stretched issues with the requested state.
-     *
-     * @param token The token for the window that needs a control.
-     * @param showControl Whether the control should be shown or hidden.
-     * @param transformationApplied Whether the treatment is already applied.
-     * @param callback The callback executed when the user clicks on a control.
-     */
-    void requestCompatCameraControl(Resources res, IBinder token, boolean showControl,
-            boolean transformationApplied, ICompatCameraControlCallback callback) {
-        if (!res.getBoolean(com.android.internal.R.bool
-                .config_isCameraCompatControlForStretchedIssuesEnabled)) {
-            return;
-        }
-        try {
-            getActivityClientController().requestCompatCameraControl(
-                    token, showControl, transformationApplied, callback);
-        } catch (RemoteException e) {
-            e.rethrowFromSystemServer();
-        }
-    }
-
     public static ActivityClient getInstance() {
         return sInstance.get();
     }
diff --git a/core/java/android/app/ActivityTaskManager.java b/core/java/android/app/ActivityTaskManager.java
index c8ab260..799df1f 100644
--- a/core/java/android/app/ActivityTaskManager.java
+++ b/core/java/android/app/ActivityTaskManager.java
@@ -172,7 +172,7 @@
         }
     }
 
-    /** Removes root tasks of the activity types from the system. */
+    /** Removes root tasks of the activity types from the Default TDA of all displays. */
     @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS)
     public void removeRootTasksWithActivityTypes(@NonNull int[] activityTypes) {
         try {
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 36b1eab..d455853 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -232,6 +232,7 @@
 import com.android.internal.content.ReferrerIntent;
 import com.android.internal.os.BinderCallsStats;
 import com.android.internal.os.BinderInternal;
+import com.android.internal.os.DebugStore;
 import com.android.internal.os.RuntimeInit;
 import com.android.internal.os.SafeZipPathValidatorCallback;
 import com.android.internal.os.SomeArgs;
@@ -358,6 +359,15 @@
     private static final long BINDER_CALLBACK_THROTTLE = 10_100L;
     private long mBinderCallbackLast = -1;
 
+    private static final boolean DEBUG_STORE_ENABLED =
+            com.android.internal.os.Flags.debugStoreEnabled();
+
+    /**
+    * Threshold for identifying long-running looper messages (in milliseconds).
+    * Calculated as 2 seconds multiplied by the hardware timeout multiplier.
+    */
+    private static final long LONG_MESSAGE_THRESHOLD_MS = 2000 * Build.HW_TIMEOUT_MULTIPLIER;
+
     /**
      * Denotes the sequence number of the process state change for which the main thread needs
      * to block until the network rules are updated for it.
@@ -734,19 +744,6 @@
                             activityWindowInfo,
                             false /* alwaysReportChange */);
                 }
-
-                @Override
-                public void requestCompatCameraControl(boolean showControl,
-                        boolean transformationApplied, ICompatCameraControlCallback callback) {
-                    if (activity == null) {
-                        throw new IllegalStateException(
-                                "Received camera compat control update for non-existing activity");
-                    }
-                    ActivityClient.getInstance().requestCompatCameraControl(
-                            activity.getResources(), token, showControl, transformationApplied,
-                            callback);
-                }
-
             };
         }
 
@@ -2105,8 +2102,7 @@
         @Override
         public void scheduleTaskFragmentTransaction(@NonNull ITaskFragmentOrganizer organizer,
                 @NonNull TaskFragmentTransaction transaction) throws RemoteException {
-            // TODO(b/260873529): ITaskFragmentOrganizer can be cleanup to be a IBinder token
-            // after flag removal.
+            // TODO(b/352665082): ITaskFragmentOrganizer can be cleanup to be a IBinder token
             organizer.onTransactionReady(transaction);
         }
 
@@ -2409,6 +2405,12 @@
         }
         public void handleMessage(Message msg) {
             if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
+            long debugStoreId = -1;
+            // By default, log all long messages when the debug store is enabled,
+            // unless this is overridden for certain message types, for which we have
+            // more granular debug store logging.
+            boolean shouldLogLongMessage = DEBUG_STORE_ENABLED;
+            final long messageStartUptimeMs = SystemClock.uptimeMillis();
             switch (msg.what) {
                 case BIND_APPLICATION:
                     Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "bindApplication");
@@ -2433,24 +2435,61 @@
                                     "broadcastReceiveComp");
                         }
                     }
-                    handleReceiver((ReceiverData)msg.obj);
-                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+                    ReceiverData receiverData = (ReceiverData) msg.obj;
+                    if (DEBUG_STORE_ENABLED) {
+                        debugStoreId =
+                                DebugStore.recordBroadcastHandleReceiver(receiverData.intent);
+                    }
+
+                    try {
+                        handleReceiver(receiverData);
+                    } finally {
+                        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+                        if (DEBUG_STORE_ENABLED) {
+                            DebugStore.recordEventEnd(debugStoreId);
+                            shouldLogLongMessage = false;
+                        }
+                    }
                     break;
                 case CREATE_SERVICE:
                     if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
                         Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
                                 ("serviceCreate: " + String.valueOf(msg.obj)));
                     }
-                    handleCreateService((CreateServiceData)msg.obj);
-                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+                    CreateServiceData createServiceData = (CreateServiceData) msg.obj;
+                    if (DEBUG_STORE_ENABLED) {
+                        debugStoreId = DebugStore.recordServiceCreate(createServiceData.info);
+                    }
+
+                    try {
+                        handleCreateService(createServiceData);
+                    } finally {
+                        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+                        if (DEBUG_STORE_ENABLED) {
+                            DebugStore.recordEventEnd(debugStoreId);
+                            shouldLogLongMessage = false;
+                        }
+                    }
                     break;
                 case BIND_SERVICE:
                     if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
                         Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceBind: "
                                 + String.valueOf(msg.obj));
                     }
-                    handleBindService((BindServiceData)msg.obj);
-                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+                    BindServiceData bindData = (BindServiceData) msg.obj;
+                    if (DEBUG_STORE_ENABLED) {
+                        debugStoreId =
+                                DebugStore.recordServiceBind(bindData.rebind, bindData.intent);
+                    }
+                    try {
+                        handleBindService(bindData);
+                    } finally {
+                        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+                        if (DEBUG_STORE_ENABLED) {
+                            DebugStore.recordEventEnd(debugStoreId);
+                            shouldLogLongMessage = false;
+                        }
+                    }
                     break;
                 case UNBIND_SERVICE:
                     if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
@@ -2466,8 +2505,21 @@
                         Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
                                 ("serviceStart: " + String.valueOf(msg.obj)));
                     }
-                    handleServiceArgs((ServiceArgsData)msg.obj);
-                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+                    ServiceArgsData serviceData = (ServiceArgsData) msg.obj;
+                    if (DEBUG_STORE_ENABLED) {
+                        debugStoreId = DebugStore.recordServiceOnStart(serviceData.startId,
+                                serviceData.flags, serviceData.args);
+                    }
+
+                    try {
+                        handleServiceArgs(serviceData);
+                    } finally {
+                        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+                        if (DEBUG_STORE_ENABLED) {
+                            DebugStore.recordEventEnd(debugStoreId);
+                            shouldLogLongMessage = false;
+                        }
+                    }
                     break;
                 case STOP_SERVICE:
                     if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
@@ -2637,13 +2689,6 @@
                     } finally {
                         controller.onClientTransactionFinished();
                     }
-                    if (isSystem()) {
-                        // Client transactions inside system process are recycled on the client side
-                        // instead of ClientLifecycleManager to avoid being cleared before this
-                        // message is handled.
-                        transaction.recycle();
-                    }
-                    // TODO(lifecycler): Recycle locally scheduled transactions.
                     break;
                 case RELAUNCH_ACTIVITY:
                     handleRelaunchActivityLocally((IBinder) msg.obj);
@@ -2670,11 +2715,17 @@
                     handleFinishInstrumentationWithoutRestart();
                     break;
             }
+            long messageElapsedTimeMs = SystemClock.uptimeMillis() - messageStartUptimeMs;
             Object obj = msg.obj;
             if (obj instanceof SomeArgs) {
                 ((SomeArgs) obj).recycle();
             }
             if (DEBUG_MESSAGES) Slog.v(TAG, "<<< done: " + codeToString(msg.what));
+            if (shouldLogLongMessage
+                    && messageElapsedTimeMs > LONG_MESSAGE_THRESHOLD_MS) {
+                DebugStore.recordLongLooperMessage(msg.what, msg.getTarget().getClass().getName(),
+                        messageElapsedTimeMs);
+            }
         }
     }
 
@@ -3825,9 +3876,8 @@
                 + " req=" + requestCode + " res=" + resultCode + " data=" + data);
         final ArrayList<ResultInfo> list = new ArrayList<>();
         list.add(new ResultInfo(id, requestCode, resultCode, data, activityToken));
-        final ClientTransaction clientTransaction = ClientTransaction.obtain(mAppThread);
-        final ActivityResultItem activityResultItem = ActivityResultItem.obtain(
-                activityToken, list);
+        final ClientTransaction clientTransaction = new ClientTransaction(mAppThread);
+        final ActivityResultItem activityResultItem = new ActivityResultItem(activityToken, list);
         clientTransaction.addTransactionItem(activityResultItem);
         try {
             mAppThread.scheduleTransaction(clientTransaction);
@@ -4621,8 +4671,8 @@
     }
 
     private void schedulePauseWithUserLeavingHint(ActivityClientRecord r) {
-        final ClientTransaction transaction = ClientTransaction.obtain(mAppThread);
-        final PauseActivityItem pauseActivityItem = PauseActivityItem.obtain(r.token,
+        final ClientTransaction transaction = new ClientTransaction(mAppThread);
+        final PauseActivityItem pauseActivityItem = new PauseActivityItem(r.token,
                 r.activity.isFinishing(), /* userLeaving */ true,
                 /* dontReport */ false, /* autoEnteringPip */ false);
         transaction.addTransactionItem(pauseActivityItem);
@@ -4630,8 +4680,8 @@
     }
 
     private void scheduleResume(ActivityClientRecord r) {
-        final ClientTransaction transaction = ClientTransaction.obtain(mAppThread);
-        final ResumeActivityItem resumeActivityItem = ResumeActivityItem.obtain(r.token,
+        final ClientTransaction transaction = new ClientTransaction(mAppThread);
+        final ResumeActivityItem resumeActivityItem = new ResumeActivityItem(r.token,
                 /* isForward */ false, /* shouldSendCompatFakeFocus */ false);
         transaction.addTransactionItem(resumeActivityItem);
         executeTransaction(transaction);
@@ -6247,7 +6297,7 @@
                 r.createdConfig != null
                         ? r.createdConfig : mConfigurationController.getConfiguration(),
                 r.overrideConfig);
-        final ActivityRelaunchItem activityRelaunchItem = ActivityRelaunchItem.obtain(
+        final ActivityRelaunchItem activityRelaunchItem = new ActivityRelaunchItem(
                 r.token, null /* pendingResults */, null /* pendingIntents */,
                 0 /* configChanges */, mergedConfiguration, r.mPreserveWindow,
                 r.getActivityWindowInfo());
@@ -6255,7 +6305,7 @@
         final ActivityLifecycleItem lifecycleRequest =
                 TransactionExecutorHelper.getLifecycleRequestForCurrentState(r);
         // Schedule the transaction.
-        final ClientTransaction transaction = ClientTransaction.obtain(mAppThread);
+        final ClientTransaction transaction = new ClientTransaction(mAppThread);
         transaction.addTransactionItem(activityRelaunchItem);
         transaction.addTransactionItem(lifecycleRequest);
         executeTransaction(transaction);
@@ -7088,9 +7138,6 @@
                     && level >= ComponentCallbacks2.TRIM_MEMORY_BACKGROUND) {
                 return;
             }
-            if (level >= ComponentCallbacks2.TRIM_MEMORY_COMPLETE) {
-                PropertyInvalidatedCache.onTrimMemory();
-            }
 
             final ArrayList<ComponentCallbacks2> callbacks =
                     collectComponentCallbacks(true /* includeUiContexts */);
diff --git a/core/java/android/app/AppCompatTaskInfo.java b/core/java/android/app/AppCompatTaskInfo.java
index 92543b1..a07f620 100644
--- a/core/java/android/app/AppCompatTaskInfo.java
+++ b/core/java/android/app/AppCompatTaskInfo.java
@@ -32,7 +32,7 @@
     public boolean topActivityEligibleForLetterboxEducation;
 
     /**
-     * Whether the letterbox education is enabled
+     * Whether the letterbox education is enabled.
      */
     public boolean isLetterboxEducationEnabled;
 
@@ -73,26 +73,26 @@
     public boolean isFromLetterboxDoubleTap;
 
     /**
-     * If {@link isLetterboxDoubleTapEnabled} it contains the current letterbox vertical position or
-     * {@link TaskInfo.PROPERTY_VALUE_UNSET} otherwise.
+     * If {@link #isLetterboxDoubleTapEnabled} it contains the current letterbox vertical position
+     * or {@link TaskInfo#PROPERTY_VALUE_UNSET} otherwise.
      */
     public int topActivityLetterboxVerticalPosition;
 
     /**
-     * If {@link isLetterboxDoubleTapEnabled} it contains the current letterbox vertical position or
-     * {@link TaskInfo.PROPERTY_VALUE_UNSET} otherwise.
+     * If {@link #isLetterboxDoubleTapEnabled} it contains the current letterbox vertical position
+     * or {@link TaskInfo#PROPERTY_VALUE_UNSET} otherwise.
      */
     public int topActivityLetterboxHorizontalPosition;
 
     /**
-     * If {@link isLetterboxDoubleTapEnabled} it contains the current width of the letterboxed
-     * activity or {@link TaskInfo.PROPERTY_VALUE_UNSET} otherwise.
+     * If {@link #isLetterboxDoubleTapEnabled} it contains the current width of the letterboxed
+     * activity or {@link TaskInfo#PROPERTY_VALUE_UNSET} otherwise.
      */
     public int topActivityLetterboxWidth;
 
     /**
-     * If {@link isLetterboxDoubleTapEnabled} it contains the current height of the letterboxed
-     * activity or {@link TaskInfo.PROPERTY_VALUE_UNSET} otherwise.
+     * If {@link #isLetterboxDoubleTapEnabled} it contains the current height of the letterboxed
+     * activity or {@link TaskInfo#PROPERTY_VALUE_UNSET} otherwise.
      */
     public int topActivityLetterboxHeight;
 
@@ -133,24 +133,23 @@
             };
 
     /**
-     * @return {@value true} if the task has some compat ui.
+     * @return {@code true} if the task has some compat ui.
      */
     public boolean hasCompatUI() {
-        return cameraCompatTaskInfo.hasCameraCompatUI() || topActivityInSizeCompat
-                || topActivityEligibleForLetterboxEducation
+        return topActivityInSizeCompat || topActivityEligibleForLetterboxEducation
                 || isLetterboxDoubleTapEnabled
                 || topActivityEligibleForUserAspectRatioButton;
     }
 
     /**
-     * @return {@value true} if top activity is pillarboxed.
+     * @return {@code true} if top activity is pillarboxed.
      */
     public boolean isTopActivityPillarboxed() {
         return topActivityLetterboxWidth < topActivityLetterboxHeight;
     }
 
     /**
-     * @return  {@code true} if the app compat parameters that are important for task organizers
+     * @return {@code true} if the app compat parameters that are important for task organizers
      * are equal.
      */
     public boolean equalsForTaskOrganizer(@Nullable AppCompatTaskInfo that) {
diff --git a/core/java/android/app/CameraCompatTaskInfo.java b/core/java/android/app/CameraCompatTaskInfo.java
index 1e116b7..53eddbe 100644
--- a/core/java/android/app/CameraCompatTaskInfo.java
+++ b/core/java/android/app/CameraCompatTaskInfo.java
@@ -31,45 +31,6 @@
  */
 public class CameraCompatTaskInfo implements Parcelable {
     /**
-     * Camera compat control isn't shown because it's not requested by heuristics.
-     */
-    public static final int CAMERA_COMPAT_CONTROL_HIDDEN = 0;
-
-    /**
-     * Camera compat control is shown with the treatment suggested.
-     */
-    public static final int CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED = 1;
-
-    /**
-     * Camera compat control is shown to allow reverting the applied treatment.
-     */
-    public static final int CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED = 2;
-
-    /**
-     * Camera compat control is dismissed by user.
-     */
-    public static final int CAMERA_COMPAT_CONTROL_DISMISSED = 3;
-
-    /**
-     * Enum for the Camera app compat control states.
-     */
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef(prefix = { "CAMERA_COMPAT_CONTROL_" }, value = {
-            CAMERA_COMPAT_CONTROL_HIDDEN,
-            CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED,
-            CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED,
-            CAMERA_COMPAT_CONTROL_DISMISSED,
-    })
-    public @interface CameraCompatControlState {}
-
-    /**
-     * State of the Camera app compat control which is used to correct stretched viewfinder
-     * in apps that don't handle all possible configurations and changes between them correctly.
-     */
-    @CameraCompatControlState
-    public int cameraCompatControlState = CAMERA_COMPAT_CONTROL_HIDDEN;
-
-    /**
      * The value to use when no camera compat treatment should be applied to a windowed task.
      */
     public static final int CAMERA_COMPAT_FREEFORM_NONE = 0;
@@ -137,7 +98,6 @@
      * Reads the CameraCompatTaskInfo from a parcel.
      */
     void readFromParcel(Parcel source) {
-        cameraCompatControlState = source.readInt();
         freeformCameraCompatMode = source.readInt();
     }
 
@@ -146,26 +106,10 @@
      */
     @Override
     public void writeToParcel(Parcel dest, int flags) {
-        dest.writeInt(cameraCompatControlState);
         dest.writeInt(freeformCameraCompatMode);
     }
 
     /**
-     * @return {@value true} if the task has camera compat controls.
-     */
-    public boolean hasCameraCompatControl() {
-        return cameraCompatControlState != CAMERA_COMPAT_CONTROL_HIDDEN
-                && cameraCompatControlState != CAMERA_COMPAT_CONTROL_DISMISSED;
-    }
-
-    /**
-     * @return {@value true} if the task has some compat ui.
-     */
-    public boolean hasCameraCompatUI() {
-        return hasCameraCompatControl();
-    }
-
-    /**
      * @return  {@code true} if the camera compat parameters that are important for task organizers
      * are equal.
      */
@@ -183,33 +127,16 @@
         if (that == null) {
             return false;
         }
-        return cameraCompatControlState == that.cameraCompatControlState
-                && freeformCameraCompatMode == that.freeformCameraCompatMode;
+        return freeformCameraCompatMode == that.freeformCameraCompatMode;
     }
 
     @Override
     public String toString() {
-        return "CameraCompatTaskInfo { cameraCompatControlState="
-                + cameraCompatControlStateToString(cameraCompatControlState)
-                + " freeformCameraCompatMode="
+        return "CameraCompatTaskInfo { freeformCameraCompatMode="
                 + freeformCameraCompatModeToString(freeformCameraCompatMode)
                 + "}";
     }
 
-    /** Human readable version of the camera control state. */
-    @NonNull
-    public static String cameraCompatControlStateToString(
-            @CameraCompatControlState int cameraCompatControlState) {
-        return switch (cameraCompatControlState) {
-            case CAMERA_COMPAT_CONTROL_HIDDEN -> "hidden";
-            case CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED -> "treatment-suggested";
-            case CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED -> "treatment-applied";
-            case CAMERA_COMPAT_CONTROL_DISMISSED -> "dismissed";
-            default -> throw new AssertionError(
-                    "Unexpected camera compat control state: " + cameraCompatControlState);
-        };
-    }
-
     /** Human readable version of the freeform camera compat mode. */
     @NonNull
     public static String freeformCameraCompatModeToString(
diff --git a/core/java/android/app/ClientTransactionHandler.java b/core/java/android/app/ClientTransactionHandler.java
index f0c3196..f2f5374 100644
--- a/core/java/android/app/ClientTransactionHandler.java
+++ b/core/java/android/app/ClientTransactionHandler.java
@@ -42,6 +42,7 @@
 /**
  * Defines operations that a {@link android.app.servertransaction.ClientTransaction} or its items
  * can perform on client.
+ *
  * @hide
  */
 public abstract class ClientTransactionHandler {
@@ -68,7 +69,6 @@
             getTransactionExecutor().execute(transaction);
         } finally {
             mIsExecutingLocalTransaction = false;
-            transaction.recycle();
         }
     }
 
diff --git a/core/java/android/app/IActivityClientController.aidl b/core/java/android/app/IActivityClientController.aidl
index 9c8fea1..9615015 100644
--- a/core/java/android/app/IActivityClientController.aidl
+++ b/core/java/android/app/IActivityClientController.aidl
@@ -17,7 +17,6 @@
 package android.app;
 
 import android.app.ActivityManager;
-import android.app.ICompatCameraControlCallback;
 import android.app.IRequestFinishCallback;
 import android.app.PictureInPictureParams;
 import android.content.ComponentName;
@@ -172,17 +171,6 @@
     oneway void splashScreenAttached(in IBinder token);
 
     /**
-     * Shows or hides a Camera app compat toggle for stretched issues with the requested state.
-     *
-     * @param token The token for the window that needs a control.
-     * @param showControl Whether the control should be shown or hidden.
-     * @param transformationApplied Whether the treatment is already applied.
-     * @param callback The callback executed when the user clicks on a control.
-     */
-    oneway void requestCompatCameraControl(in IBinder token, boolean showControl,
-            boolean transformationApplied, in ICompatCameraControlCallback callback);
-
-    /**
      * If set, any activity launch in the same task will be overridden to the locale of activity
      * that started the task.
      */
diff --git a/core/java/android/app/ICompatCameraControlCallback.aidl b/core/java/android/app/ICompatCameraControlCallback.aidl
deleted file mode 100644
index 1a7f210..0000000
--- a/core/java/android/app/ICompatCameraControlCallback.aidl
+++ /dev/null
@@ -1,30 +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 android.app;
-
-/**
- * This callback allows ActivityRecord to ask the calling View to apply the treatment for stretched
- * issues affecting camera viewfinders when the user clicks on the camera compat control.
- *
- * {@hide}
- */
-oneway interface ICompatCameraControlCallback {
-
-    void applyCameraCompatTreatment();
-
-    void revertCameraCompatTreatment();
-}
diff --git a/core/java/android/app/Person.java b/core/java/android/app/Person.java
index 96f6f4e..c7432c5 100644
--- a/core/java/android/app/Person.java
+++ b/core/java/android/app/Person.java
@@ -189,10 +189,8 @@
      */
     public void visitUris(@NonNull Consumer<Uri> visitor) {
         visitor.accept(getIconUri());
-        if (Flags.visitPersonUri()) {
-            if (mUri != null && !mUri.isEmpty()) {
-                visitor.accept(Uri.parse(mUri));
-            }
+        if (mUri != null && !mUri.isEmpty()) {
+            visitor.accept(Uri.parse(mUri));
         }
     }
 
diff --git a/core/java/android/app/PropertyInvalidatedCache.java b/core/java/android/app/PropertyInvalidatedCache.java
index 27f9f54..22804a2 100644
--- a/core/java/android/app/PropertyInvalidatedCache.java
+++ b/core/java/android/app/PropertyInvalidatedCache.java
@@ -1617,18 +1617,4 @@
             Log.e(TAG, "Failed to dump PropertyInvalidatedCache instances");
         }
     }
-
-    /**
-     * Trim memory by clearing all the caches.
-     * @hide
-     */
-    public static void onTrimMemory() {
-        ArrayList<PropertyInvalidatedCache> activeCaches;
-        synchronized (sGlobalLock) {
-            activeCaches = getActiveCaches();
-        }
-        for (int i = 0; i < activeCaches.size(); i++) {
-            activeCaches.get(i).clear();
-        }
-    }
 }
diff --git a/core/java/android/app/TaskInfo.java b/core/java/android/app/TaskInfo.java
index 531537c..c83dd65 100644
--- a/core/java/android/app/TaskInfo.java
+++ b/core/java/android/app/TaskInfo.java
@@ -304,6 +304,12 @@
     public boolean isTopActivityStyleFloating;
 
     /**
+     * The last non-fullscreen bounds the task was launched in or resized to.
+     * @hide
+     */
+    public Rect lastNonFullscreenBounds;
+
+    /**
      * The URI of the intent that generated the top-most activity opened using a URL.
      * @hide
      */
@@ -450,6 +456,7 @@
                 && Objects.equals(topActivity, that.topActivity)
                 && isTopActivityTransparent == that.isTopActivityTransparent
                 && isTopActivityStyleFloating == that.isTopActivityStyleFloating
+                && lastNonFullscreenBounds == this.lastNonFullscreenBounds
                 && Objects.equals(capturedLink, that.capturedLink)
                 && capturedLinkTimestamp == that.capturedLinkTimestamp
                 && appCompatTaskInfo.equalsForTaskOrganizer(that.appCompatTaskInfo);
@@ -522,6 +529,7 @@
         displayAreaFeatureId = source.readInt();
         isTopActivityTransparent = source.readBoolean();
         isTopActivityStyleFloating = source.readBoolean();
+        lastNonFullscreenBounds = source.readTypedObject(Rect.CREATOR);
         capturedLink = source.readTypedObject(Uri.CREATOR);
         capturedLinkTimestamp = source.readLong();
         appCompatTaskInfo = source.readTypedObject(AppCompatTaskInfo.CREATOR);
@@ -572,6 +580,7 @@
         dest.writeInt(displayAreaFeatureId);
         dest.writeBoolean(isTopActivityTransparent);
         dest.writeBoolean(isTopActivityStyleFloating);
+        dest.writeTypedObject(lastNonFullscreenBounds, flags);
         dest.writeTypedObject(capturedLink, flags);
         dest.writeLong(capturedLinkTimestamp);
         dest.writeTypedObject(appCompatTaskInfo, flags);
@@ -612,6 +621,7 @@
                 + " displayAreaFeatureId=" + displayAreaFeatureId
                 + " isTopActivityTransparent=" + isTopActivityTransparent
                 + " isTopActivityStyleFloating=" + isTopActivityStyleFloating
+                + " lastNonFullscreenBounds=" + lastNonFullscreenBounds
                 + " capturedLink=" + capturedLink
                 + " capturedLinkTimestamp=" + capturedLinkTimestamp
                 + " appCompatTaskInfo=" + appCompatTaskInfo
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index a7070b9..965e3c4 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -11964,6 +11964,34 @@
     }
 
     /**
+     * Adds a user restriction on {@code targetUser}, specified by the {@code key}.
+     *
+     * <p>Called by a system service only, meaning that the caller's UID must be equal to
+     * {@link Process#SYSTEM_UID}.
+     *
+     * @param systemEntity  The service entity that adds the restriction. A user restriction set by
+     *                       a service entity can only be cleared by the same entity. This can be
+     *                       just the calling package name, or any string of the caller's choice
+     *                       can be used.
+     * @param key  The key of the restriction.
+     * @param targetUser  The user to add the restriction on.
+     * @throws SecurityException if the caller is not a system service
+     *
+     * @hide
+     */
+    public void addUserRestriction(@NonNull String systemEntity,
+            @NonNull @UserManager.UserRestrictionKey String key, @UserIdInt int targetUser) {
+        if (mService != null) {
+            try {
+                mService.setUserRestrictionForUser(
+                        systemEntity, key, /* enable= */ true, targetUser);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+    }
+
+    /**
      * Called by a profile owner, device owner or a holder of any permission that is associated with
      *  a user restriction to set a user restriction specified by the provided {@code key} globally
      *  on all users. To clear the restriction use {@link #clearUserRestriction}.
@@ -11971,7 +11999,7 @@
      * <p>For a given user, a restriction will be set if it was applied globally or locally by any
      * admin.
      *
-     * <p> The calling device admin must be a profile owner, device owner or or a holder of any
+     * <p> The calling device admin must be a profile owner, device owner or a holder of any
      * permission that is associated with a user restriction; if it is not, a security
      * exception will be thrown.
      *
@@ -12072,6 +12100,34 @@
     }
 
     /**
+     * Clears a user restriction from {@code targetUser}, specified by the {@code key}.
+     *
+     * <p>Called by a system service only, meaning that the caller's UID must be equal to
+     * {@link Process#SYSTEM_UID}.
+     *
+     * @param systemEntity  The system entity that clears the restriction. A user restriction
+     *                         set by a system entity can only be cleared by the same entity. This
+     *                         can be just the calling package name, or any string of the caller's
+     *                         choice can be used.
+     * @param key  The key of the restriction.
+     * @param targetUser  The user to clear the restriction from.
+     * @throws SecurityException if the caller is not a system service
+     *
+     * @hide
+     */
+    public void clearUserRestriction(@NonNull String systemEntity,
+            @NonNull @UserManager.UserRestrictionKey String key, @UserIdInt int targetUser) {
+        if (mService != null) {
+            try {
+                mService.setUserRestrictionForUser(
+                        systemEntity, key, /* enable= */ false, targetUser);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+    }
+
+    /**
      * Called by an admin to get user restrictions set by themselves with
      * {@link #addUserRestriction(ComponentName, String)}.
      * <p>
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 381f996..c393a9e 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -255,6 +255,7 @@
     ComponentName getRestrictionsProvider(int userHandle);
 
     void setUserRestriction(in ComponentName who, in String callerPackage, in String key, boolean enable, boolean parent);
+    void setUserRestrictionForUser(in String systemEntity, in String key, boolean enable, int targetUser);
     void setUserRestrictionGlobally(in String callerPackage, in String key);
     Bundle getUserRestrictions(in ComponentName who, in String callerPackage, boolean parent);
     Bundle getUserRestrictionsGlobally(in String callerPackage);
diff --git a/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java b/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java
index 11d7ff8..2b52681 100644
--- a/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java
+++ b/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java
@@ -19,6 +19,8 @@
 import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
 import static android.view.Display.INVALID_DISPLAY;
 
+import static java.util.Objects.requireNonNull;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityThread.ActivityClientRecord;
@@ -34,12 +36,23 @@
 
 /**
  * Activity configuration changed callback.
+ *
  * @hide
  */
 public class ActivityConfigurationChangeItem extends ActivityTransactionItem {
 
-    private Configuration mConfiguration;
-    private ActivityWindowInfo mActivityWindowInfo;
+    @NonNull
+    private final Configuration mConfiguration;
+
+    @NonNull
+    private final ActivityWindowInfo mActivityWindowInfo;
+
+    public ActivityConfigurationChangeItem(@NonNull IBinder activityToken,
+            @NonNull Configuration config, @NonNull ActivityWindowInfo activityWindowInfo) {
+        super(activityToken);
+        mConfiguration = new Configuration(config);
+        mActivityWindowInfo = new ActivityWindowInfo(activityWindowInfo);
+    }
 
     @Override
     public void preExecute(@NonNull ClientTransactionHandler client) {
@@ -59,38 +72,9 @@
         Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
     }
 
-    // ObjectPoolItem implementation
-
-    private ActivityConfigurationChangeItem() {}
-
-    /** Obtain an instance initialized with provided params. */
-    @NonNull
-    public static ActivityConfigurationChangeItem obtain(@NonNull IBinder activityToken,
-            @NonNull Configuration config, @NonNull ActivityWindowInfo activityWindowInfo) {
-        ActivityConfigurationChangeItem instance =
-                ObjectPool.obtain(ActivityConfigurationChangeItem.class);
-        if (instance == null) {
-            instance = new ActivityConfigurationChangeItem();
-        }
-        instance.setActivityToken(activityToken);
-        instance.mConfiguration = new Configuration(config);
-        instance.mActivityWindowInfo = new ActivityWindowInfo(activityWindowInfo);
-
-        return instance;
-    }
-
-    @Override
-    public void recycle() {
-        super.recycle();
-        mConfiguration = null;
-        mActivityWindowInfo = null;
-        ObjectPool.recycle(this);
-    }
-
-
     // Parcelable implementation
 
-    /** Write to Parcel. */
+    /** Writes to Parcel. */
     @Override
     public void writeToParcel(@NonNull Parcel dest, int flags) {
         super.writeToParcel(dest, flags);
@@ -98,11 +82,11 @@
         dest.writeTypedObject(mActivityWindowInfo, flags);
     }
 
-    /** Read from Parcel. */
+    /** Reads from Parcel. */
     private ActivityConfigurationChangeItem(@NonNull Parcel in) {
         super(in);
-        mConfiguration = in.readTypedObject(Configuration.CREATOR);
-        mActivityWindowInfo = in.readTypedObject(ActivityWindowInfo.CREATOR);
+        mConfiguration = requireNonNull(in.readTypedObject(Configuration.CREATOR));
+        mActivityWindowInfo = requireNonNull(in.readTypedObject(ActivityWindowInfo.CREATOR));
     }
 
     public static final @NonNull Creator<ActivityConfigurationChangeItem> CREATOR =
diff --git a/core/java/android/app/servertransaction/ActivityLifecycleItem.java b/core/java/android/app/servertransaction/ActivityLifecycleItem.java
index 48db18f..778f134 100644
--- a/core/java/android/app/servertransaction/ActivityLifecycleItem.java
+++ b/core/java/android/app/servertransaction/ActivityLifecycleItem.java
@@ -18,6 +18,7 @@
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.os.IBinder;
 import android.os.Parcel;
 
 import java.lang.annotation.Retention;
@@ -25,6 +26,7 @@
 
 /**
  * Request for lifecycle state that an activity should reach.
+ *
  * @hide
  */
 public abstract class ActivityLifecycleItem extends ActivityTransactionItem {
@@ -52,8 +54,13 @@
     public static final int ON_DESTROY = 6;
     public static final int ON_RESTART = 7;
 
-    ActivityLifecycleItem() {}
+    ActivityLifecycleItem(@NonNull IBinder activityToken) {
+        super(activityToken);
+    }
 
+    // Parcelable implementation
+
+    /** Reads from Parcel. */
     ActivityLifecycleItem(@NonNull Parcel in) {
         super(in);
     }
diff --git a/core/java/android/app/servertransaction/ActivityRelaunchItem.java b/core/java/android/app/servertransaction/ActivityRelaunchItem.java
index 45bf235..cecf701 100644
--- a/core/java/android/app/servertransaction/ActivityRelaunchItem.java
+++ b/core/java/android/app/servertransaction/ActivityRelaunchItem.java
@@ -18,6 +18,8 @@
 
 import static android.app.ActivityThread.DEBUG_ORDER;
 
+import static java.util.Objects.requireNonNull;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityThread.ActivityClientRecord;
@@ -39,25 +41,50 @@
 
 /**
  * Activity relaunch callback.
+ *
  * @hide
  */
 public class ActivityRelaunchItem extends ActivityTransactionItem {
 
     private static final String TAG = "ActivityRelaunchItem";
 
-    private List<ResultInfo> mPendingResults;
-    private List<ReferrerIntent> mPendingNewIntents;
-    private int mConfigChanges;
-    private MergedConfiguration mConfig;
-    private boolean mPreserveWindow;
-    private ActivityWindowInfo mActivityWindowInfo;
+    @Nullable
+    private final List<ResultInfo> mPendingResults;
+
+    @Nullable
+    private final List<ReferrerIntent> mPendingNewIntents;
+
+    @NonNull
+    private final MergedConfiguration mConfig;
+
+    @NonNull
+    private final ActivityWindowInfo mActivityWindowInfo;
+
+    private final int mConfigChanges;
+    private final boolean mPreserveWindow;
 
     /**
      * A record that was properly configured for relaunch. Execution will be cancelled if not
      * initialized after {@link #preExecute(ClientTransactionHandler)}.
      */
+    @Nullable
     private ActivityClientRecord mActivityClientRecord;
 
+    public ActivityRelaunchItem(@NonNull IBinder activityToken,
+            @Nullable List<ResultInfo> pendingResults,
+            @Nullable List<ReferrerIntent> pendingNewIntents, int configChanges,
+            @NonNull MergedConfiguration config, boolean preserveWindow,
+            @NonNull ActivityWindowInfo activityWindowInfo) {
+        super(activityToken);
+        mPendingResults = pendingResults != null ? new ArrayList<>(pendingResults) : null;
+        mPendingNewIntents =
+                pendingNewIntents != null ? new ArrayList<>(pendingNewIntents) : null;
+        mConfig = new MergedConfiguration(config);
+        mActivityWindowInfo = new ActivityWindowInfo(activityWindowInfo);
+        mConfigChanges = configChanges;
+        mPreserveWindow = preserveWindow;
+    }
+
     @Override
     public void preExecute(@NonNull ClientTransactionHandler client) {
         // The local config is already scaled so only apply if this item is from server side.
@@ -87,70 +114,29 @@
         client.reportRelaunch(r);
     }
 
-    // ObjectPoolItem implementation
-
-    private ActivityRelaunchItem() {}
-
-    /** Obtain an instance initialized with provided params. */
-    @NonNull
-    public static ActivityRelaunchItem obtain(@NonNull IBinder activityToken,
-            @Nullable List<ResultInfo> pendingResults,
-            @Nullable List<ReferrerIntent> pendingNewIntents, int configChanges,
-            @NonNull MergedConfiguration config, boolean preserveWindow,
-            @NonNull ActivityWindowInfo activityWindowInfo) {
-        ActivityRelaunchItem instance = ObjectPool.obtain(ActivityRelaunchItem.class);
-        if (instance == null) {
-            instance = new ActivityRelaunchItem();
-        }
-        instance.setActivityToken(activityToken);
-        instance.mPendingResults = pendingResults != null ? new ArrayList<>(pendingResults) : null;
-        instance.mPendingNewIntents =
-                pendingNewIntents != null ? new ArrayList<>(pendingNewIntents) : null;
-        instance.mConfigChanges = configChanges;
-        instance.mConfig = new MergedConfiguration(config);
-        instance.mPreserveWindow = preserveWindow;
-        instance.mActivityWindowInfo = new ActivityWindowInfo(activityWindowInfo);
-
-        return instance;
-    }
-
-    @Override
-    public void recycle() {
-        super.recycle();
-        mPendingResults = null;
-        mPendingNewIntents = null;
-        mConfigChanges = 0;
-        mConfig = null;
-        mPreserveWindow = false;
-        mActivityClientRecord = null;
-        mActivityWindowInfo = null;
-        ObjectPool.recycle(this);
-    }
-
-
     // Parcelable implementation
 
-    /** Write to Parcel. */
+    /** Writes to Parcel. */
     @Override
     public void writeToParcel(@NonNull Parcel dest, int flags) {
         super.writeToParcel(dest, flags);
         dest.writeTypedList(mPendingResults, flags);
         dest.writeTypedList(mPendingNewIntents, flags);
-        dest.writeInt(mConfigChanges);
         dest.writeTypedObject(mConfig, flags);
-        dest.writeBoolean(mPreserveWindow);
         dest.writeTypedObject(mActivityWindowInfo, flags);
+        dest.writeInt(mConfigChanges);
+        dest.writeBoolean(mPreserveWindow);
     }
 
-    /** Read from Parcel. */
+    /** Reads from Parcel. */
     private ActivityRelaunchItem(@NonNull Parcel in) {
         super(in);
         mPendingResults = in.createTypedArrayList(ResultInfo.CREATOR);
         mPendingNewIntents = in.createTypedArrayList(ReferrerIntent.CREATOR);
+        mConfig = requireNonNull(in.readTypedObject(MergedConfiguration.CREATOR));
+        mActivityWindowInfo = requireNonNull(in.readTypedObject(ActivityWindowInfo.CREATOR));
         mConfigChanges = in.readInt();
-        mConfig = in.readTypedObject(MergedConfiguration.CREATOR);
         mPreserveWindow = in.readBoolean();
-        mActivityWindowInfo = in.readTypedObject(ActivityWindowInfo.CREATOR);
     }
 
     public static final @NonNull Creator<ActivityRelaunchItem> CREATOR =
@@ -175,9 +161,10 @@
         final ActivityRelaunchItem other = (ActivityRelaunchItem) o;
         return Objects.equals(mPendingResults, other.mPendingResults)
                 && Objects.equals(mPendingNewIntents, other.mPendingNewIntents)
-                && mConfigChanges == other.mConfigChanges && Objects.equals(mConfig, other.mConfig)
-                && mPreserveWindow == other.mPreserveWindow
-                && Objects.equals(mActivityWindowInfo, other.mActivityWindowInfo);
+                && Objects.equals(mConfig, other.mConfig)
+                && Objects.equals(mActivityWindowInfo, other.mActivityWindowInfo)
+                && mConfigChanges == other.mConfigChanges
+                && mPreserveWindow == other.mPreserveWindow;
     }
 
     @Override
@@ -186,10 +173,10 @@
         result = 31 * result + super.hashCode();
         result = 31 * result + Objects.hashCode(mPendingResults);
         result = 31 * result + Objects.hashCode(mPendingNewIntents);
-        result = 31 * result + mConfigChanges;
         result = 31 * result + Objects.hashCode(mConfig);
-        result = 31 * result + (mPreserveWindow ? 1 : 0);
         result = 31 * result + Objects.hashCode(mActivityWindowInfo);
+        result = 31 * result + mConfigChanges;
+        result = 31 * result + (mPreserveWindow ? 1 : 0);
         return result;
     }
 
@@ -198,9 +185,9 @@
         return "ActivityRelaunchItem{" + super.toString()
                 + ",pendingResults=" + mPendingResults
                 + ",pendingNewIntents=" + mPendingNewIntents
-                + ",configChanges="  + mConfigChanges
                 + ",config=" + mConfig
-                + ",preserveWindow=" + mPreserveWindow
-                + ",activityWindowInfo=" + mActivityWindowInfo + "}";
+                + ",activityWindowInfo=" + mActivityWindowInfo
+                + ",configChanges=" + mConfigChanges
+                + ",preserveWindow=" + mPreserveWindow + "}";
     }
 }
diff --git a/core/java/android/app/servertransaction/ActivityResultItem.java b/core/java/android/app/servertransaction/ActivityResultItem.java
index 51a09fb..761c519 100644
--- a/core/java/android/app/servertransaction/ActivityResultItem.java
+++ b/core/java/android/app/servertransaction/ActivityResultItem.java
@@ -41,10 +41,13 @@
 
 /**
  * Activity result delivery callback.
+ *
  * @hide
  */
 public class ActivityResultItem extends ActivityTransactionItem {
 
+    // TODO(b/170729553): Mark this with @NonNull and final once @UnsupportedAppUsage removed.
+    //  We cannot do it now to avoid app compatibility regression.
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     private List<ResultInfo> mResultInfoList;
 
@@ -56,6 +59,12 @@
     @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.S)
     public static final long CALL_ACTIVITY_RESULT_BEFORE_RESUME = 78294732L;
 
+    public ActivityResultItem(@NonNull IBinder activityToken,
+            @NonNull List<ResultInfo> resultInfoList) {
+        super(activityToken);
+        mResultInfoList = new ArrayList<>(resultInfoList);
+    }
+
     @Override
     public int getPostExecutionState() {
         return CompatChanges.isChangeEnabled(CALL_ACTIVITY_RESULT_BEFORE_RESUME)
@@ -70,43 +79,19 @@
         Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
     }
 
-    // ObjectPoolItem implementation
-
-    private ActivityResultItem() {}
-
-    /** Obtain an instance initialized with provided params. */
-    @NonNull
-    public static ActivityResultItem obtain(@NonNull IBinder activityToken,
-            @NonNull List<ResultInfo> resultInfoList) {
-        ActivityResultItem instance = ObjectPool.obtain(ActivityResultItem.class);
-        if (instance == null) {
-            instance = new ActivityResultItem();
-        }
-        instance.setActivityToken(activityToken);
-        instance.mResultInfoList = new ArrayList<>(resultInfoList);
-
-        return instance;
-    }
-
-    @Override
-    public void recycle() {
-        super.recycle();
-        mResultInfoList = null;
-        ObjectPool.recycle(this);
-    }
-
     // Parcelable implementation
 
-    /** Write to Parcel. */
+    /** Writes to Parcel. */
     @Override
     public void writeToParcel(@NonNull Parcel dest, int flags) {
         super.writeToParcel(dest, flags);
         dest.writeTypedList(mResultInfoList, flags);
     }
 
-    /** Read from Parcel. */
+    /** Reads from Parcel. */
     private ActivityResultItem(@NonNull Parcel in) {
         super(in);
+        // TODO(b/170729553): Wrap with requireNonNull once @UnsupportedAppUsage removed.
         mResultInfoList = in.createTypedArrayList(ResultInfo.CREATOR);
     }
 
diff --git a/core/java/android/app/servertransaction/ActivityTransactionItem.java b/core/java/android/app/servertransaction/ActivityTransactionItem.java
index b4ff476f..506a158 100644
--- a/core/java/android/app/servertransaction/ActivityTransactionItem.java
+++ b/core/java/android/app/servertransaction/ActivityTransactionItem.java
@@ -49,9 +49,12 @@
 public abstract class ActivityTransactionItem extends ClientTransactionItem {
 
     /** Target client activity. */
-    private IBinder mActivityToken;
+    @NonNull
+    private final IBinder mActivityToken;
 
-    ActivityTransactionItem() {}
+    public ActivityTransactionItem(@NonNull IBinder activityToken) {
+        mActivityToken = requireNonNull(activityToken);
+    }
 
     @Override
     public final void execute(@NonNull ClientTransactionHandler client,
@@ -94,26 +97,18 @@
         return mActivityToken;
     }
 
-    void setActivityToken(@NonNull IBinder activityToken) {
-        mActivityToken = requireNonNull(activityToken);
-    }
+    // Parcelable implementation
 
-    // To be overridden
-
-    ActivityTransactionItem(@NonNull Parcel in) {
-        mActivityToken = in.readStrongBinder();
-    }
-
+    /** Writes to Parcel. */
     @CallSuper
     @Override
     public void writeToParcel(@NonNull Parcel dest, int flags) {
         dest.writeStrongBinder(mActivityToken);
     }
 
-    @CallSuper
-    @Override
-    public void recycle() {
-        mActivityToken = null;
+    /** Reads from Parcel. */
+    ActivityTransactionItem(@NonNull Parcel in) {
+        this(in.readStrongBinder());
     }
 
     @Override
diff --git a/core/java/android/app/servertransaction/BaseClientRequest.java b/core/java/android/app/servertransaction/BaseClientRequest.java
index f275175..bcbb514 100644
--- a/core/java/android/app/servertransaction/BaseClientRequest.java
+++ b/core/java/android/app/servertransaction/BaseClientRequest.java
@@ -22,31 +22,34 @@
 /**
  * Base interface for individual requests from server to client.
  * Each of them can be prepared before scheduling and, eventually, executed.
+ *
  * @hide
  */
-public interface BaseClientRequest extends ObjectPoolItem {
+public interface BaseClientRequest {
 
     /**
      * Prepares the client request before scheduling.
      * An example of this might be informing about pending updates for some values.
      *
-     * @param client Target client handler.
+     * @param client target client handler.
      */
     default void preExecute(@NonNull ClientTransactionHandler client) {
     }
 
     /**
      * Executes the request.
-     * @param client Target client handler.
-     * @param pendingActions Container that may have data pending to be used.
+     *
+     * @param client         target client handler.
+     * @param pendingActions container that may have data pending to be used.
      */
     void execute(@NonNull ClientTransactionHandler client,
             @NonNull PendingTransactionActions pendingActions);
 
     /**
      * Performs all actions that need to happen after execution, e.g. report the result to server.
-     * @param client Target client handler.
-     * @param pendingActions Container that may have data pending to be used.
+     *
+     * @param client         target client handler.
+     * @param pendingActions container that may have data pending to be used.
      */
     default void postExecute(@NonNull ClientTransactionHandler client,
             @NonNull PendingTransactionActions pendingActions) {
diff --git a/core/java/android/app/servertransaction/ClientTransaction.java b/core/java/android/app/servertransaction/ClientTransaction.java
index 6fb852f..6e3e86c 100644
--- a/core/java/android/app/servertransaction/ClientTransaction.java
+++ b/core/java/android/app/servertransaction/ClientTransaction.java
@@ -18,6 +18,8 @@
 
 import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
 
+import static java.util.Objects.requireNonNull;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ClientTransactionHandler;
@@ -30,7 +32,6 @@
 import android.os.RemoteException;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.window.flags.Flags;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -46,7 +47,7 @@
  * @see ActivityLifecycleItem
  * @hide
  */
-public class ClientTransaction implements Parcelable, ObjectPoolItem {
+public class ClientTransaction implements Parcelable {
 
     /**
      * List of transaction items that should be executed in order. Including both
@@ -76,10 +77,39 @@
     @Nullable
     private IBinder mActivityToken;
 
-    /** Target client. */
-    private IApplicationThread mClient;
+    /**
+     * The target client.
+     * <p>
+     * This field is null only if the object is:
+     * - Read from a Parcel on the client side.
+     * - Constructed for testing purposes.
+     * <p>
+     * When created directly on the server, this field represents the server's connection to the
+     * target client's application thread. It is omitted during parceling and not sent to the
+     * client. On the client side, this field becomes unnecessary.
+     */
+    @Nullable
+    private final IApplicationThread mClient;
 
-    /** Get the target client of the transaction. */
+    @VisibleForTesting
+    public ClientTransaction() {
+        mClient = null;
+    }
+
+    public ClientTransaction(@NonNull IApplicationThread client) {
+        mClient = requireNonNull(client);
+    }
+
+    /**
+     * Gets the target client associated with this transaction.
+     * <p>
+     * This method is intended for server-side use only. Calling it from the client side
+     * will always return {@code null}.
+     *
+     * @return the {@link IApplicationThread} representing the target client, or {@code null} if
+     * called from the client side.
+     * @see #mClient
+     */
     public IApplicationThread getClient() {
         return mClient;
     }
@@ -211,51 +241,18 @@
         mClient.scheduleTransaction(this);
     }
 
-
-    // ObjectPoolItem implementation
-
-    private ClientTransaction() {}
-
-    /** Obtains an instance initialized with provided params. */
-    @NonNull
-    public static ClientTransaction obtain(@Nullable IApplicationThread client) {
-        ClientTransaction instance = ObjectPool.obtain(ClientTransaction.class);
-        if (instance == null) {
-            instance = new ClientTransaction();
-        }
-        instance.mClient = client;
-
-        return instance;
-    }
-
-    @Override
-    public void recycle() {
-        if (Flags.disableObjectPool()) {
-            return;
-        }
-        int size = mTransactionItems.size();
-        for (int i = 0; i < size; i++) {
-            mTransactionItems.get(i).recycle();
-        }
-        mTransactionItems.clear();
-        mActivityCallbacks = null;
-        mLifecycleStateRequest = null;
-        mClient = null;
-        mActivityToken = null;
-        ObjectPool.recycle(this);
-    }
-
     // Parcelable implementation
 
-    /** Write to Parcel. */
+    /** Writes to Parcel. */
     @SuppressWarnings("AndroidFrameworkEfficientParcelable") // Item class is not final.
     @Override
     public void writeToParcel(@NonNull Parcel dest, int flags) {
         dest.writeParcelableList(mTransactionItems, flags);
     }
 
-    /** Read from Parcel. */
+    /** Reads from Parcel. */
     private ClientTransaction(@NonNull Parcel in) {
+        mClient = null;  // This field is unnecessary on the client side.
         in.readParcelableList(mTransactionItems, getClass().getClassLoader(),
                 ClientTransactionItem.class);
 
diff --git a/core/java/android/app/servertransaction/ConfigurationChangeItem.java b/core/java/android/app/servertransaction/ConfigurationChangeItem.java
index 22da706..123d792 100644
--- a/core/java/android/app/servertransaction/ConfigurationChangeItem.java
+++ b/core/java/android/app/servertransaction/ConfigurationChangeItem.java
@@ -16,6 +16,8 @@
 
 package android.app.servertransaction;
 
+import static java.util.Objects.requireNonNull;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ClientTransactionHandler;
@@ -27,12 +29,20 @@
 
 /**
  * App configuration change message.
+ *
  * @hide
  */
 public class ConfigurationChangeItem extends ClientTransactionItem {
 
-    private Configuration mConfiguration;
-    private int mDeviceId;
+    @NonNull
+    private final Configuration mConfiguration;
+
+    private final int mDeviceId;
+
+    public ConfigurationChangeItem(@NonNull Configuration config, int deviceId) {
+        mConfiguration = new Configuration(config);
+        mDeviceId = deviceId;
+    }
 
     @Override
     public void preExecute(@NonNull ClientTransactionHandler client) {
@@ -46,55 +56,31 @@
         client.handleConfigurationChanged(mConfiguration, mDeviceId);
     }
 
-    // ObjectPoolItem implementation
-
-    private ConfigurationChangeItem() {}
-
-    /** Obtain an instance initialized with provided params. */
-    public static ConfigurationChangeItem obtain(@NonNull Configuration config, int deviceId) {
-        ConfigurationChangeItem instance = ObjectPool.obtain(ConfigurationChangeItem.class);
-        if (instance == null) {
-            instance = new ConfigurationChangeItem();
-        }
-        instance.mConfiguration = new Configuration(config);
-        instance.mDeviceId = deviceId;
-
-        return instance;
-    }
-
-    @Override
-    public void recycle() {
-        mConfiguration = null;
-        mDeviceId = 0;
-        ObjectPool.recycle(this);
-    }
-
-
     // Parcelable implementation
 
-    /** Write to Parcel. */
+    /** Writes to Parcel. */
     @Override
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeTypedObject(mConfiguration, flags);
         dest.writeInt(mDeviceId);
     }
 
-    /** Read from Parcel. */
+    /** Reads from Parcel. */
     private ConfigurationChangeItem(Parcel in) {
-        mConfiguration = in.readTypedObject(Configuration.CREATOR);
+        mConfiguration = requireNonNull(in.readTypedObject(Configuration.CREATOR));
         mDeviceId = in.readInt();
     }
 
     public static final @android.annotation.NonNull Creator<ConfigurationChangeItem> CREATOR =
-            new Creator<ConfigurationChangeItem>() {
-        public ConfigurationChangeItem createFromParcel(Parcel in) {
-            return new ConfigurationChangeItem(in);
-        }
+            new Creator<>() {
+                public ConfigurationChangeItem createFromParcel(Parcel in) {
+                    return new ConfigurationChangeItem(in);
+                }
 
-        public ConfigurationChangeItem[] newArray(int size) {
-            return new ConfigurationChangeItem[size];
-        }
-    };
+                public ConfigurationChangeItem[] newArray(int size) {
+                    return new ConfigurationChangeItem[size];
+                }
+            };
 
     @Override
     public boolean equals(@Nullable Object o) {
diff --git a/core/java/android/app/servertransaction/DestroyActivityItem.java b/core/java/android/app/servertransaction/DestroyActivityItem.java
index b0213d7..8a81143 100644
--- a/core/java/android/app/servertransaction/DestroyActivityItem.java
+++ b/core/java/android/app/servertransaction/DestroyActivityItem.java
@@ -28,11 +28,17 @@
 
 /**
  * Request to destroy an activity.
+ *
  * @hide
  */
 public class DestroyActivityItem extends ActivityLifecycleItem {
 
-    private boolean mFinished;
+    private final boolean mFinished;
+
+    public DestroyActivityItem(@NonNull IBinder activityToken, boolean finished) {
+        super(activityToken);
+        mFinished = finished;
+    }
 
     @Override
     public void preExecute(@NonNull ClientTransactionHandler client) {
@@ -60,40 +66,16 @@
         return ON_DESTROY;
     }
 
-    // ObjectPoolItem implementation
-
-    private DestroyActivityItem() {}
-
-    /** Obtain an instance initialized with provided params. */
-    @NonNull
-    public static DestroyActivityItem obtain(@NonNull IBinder activityToken, boolean finished) {
-        DestroyActivityItem instance = ObjectPool.obtain(DestroyActivityItem.class);
-        if (instance == null) {
-            instance = new DestroyActivityItem();
-        }
-        instance.setActivityToken(activityToken);
-        instance.mFinished = finished;
-
-        return instance;
-    }
-
-    @Override
-    public void recycle() {
-        super.recycle();
-        mFinished = false;
-        ObjectPool.recycle(this);
-    }
-
     // Parcelable implementation
 
-    /** Write to Parcel. */
+    /** Writes to Parcel. */
     @Override
     public void writeToParcel(@NonNull Parcel dest, int flags) {
         super.writeToParcel(dest, flags);
         dest.writeBoolean(mFinished);
     }
 
-    /** Read from Parcel. */
+    /** Reads from Parcel. */
     private DestroyActivityItem(@NonNull Parcel in) {
         super(in);
         mFinished = in.readBoolean();
diff --git a/core/java/android/app/servertransaction/EnterPipRequestedItem.java b/core/java/android/app/servertransaction/EnterPipRequestedItem.java
index 743653f..b6f8655 100644
--- a/core/java/android/app/servertransaction/EnterPipRequestedItem.java
+++ b/core/java/android/app/servertransaction/EnterPipRequestedItem.java
@@ -24,39 +24,24 @@
 
 /**
  * Request an activity to enter picture-in-picture mode.
+ *
  * @hide
  */
 public final class EnterPipRequestedItem extends ActivityTransactionItem {
 
+    public EnterPipRequestedItem(@NonNull IBinder activityToken) {
+        super(activityToken);
+    }
+
     @Override
     public void execute(@NonNull ClientTransactionHandler client, @NonNull ActivityClientRecord r,
             @NonNull PendingTransactionActions pendingActions) {
         client.handlePictureInPictureRequested(r);
     }
 
-    // ObjectPoolItem implementation
-
-    private EnterPipRequestedItem() {}
-
-    /** Obtain an instance initialized with provided params. */
-    @NonNull
-    public static EnterPipRequestedItem obtain(@NonNull IBinder activityToken) {
-        EnterPipRequestedItem instance = ObjectPool.obtain(EnterPipRequestedItem.class);
-        if (instance == null) {
-            instance = new EnterPipRequestedItem();
-        }
-        instance.setActivityToken(activityToken);
-        return instance;
-    }
-
-    @Override
-    public void recycle() {
-        super.recycle();
-        ObjectPool.recycle(this);
-    }
-
     // Parcelable implementation
 
+    /** Reads from Parcel. */
     private EnterPipRequestedItem(@NonNull Parcel in) {
         super(in);
     }
diff --git a/core/java/android/app/servertransaction/LaunchActivityItem.java b/core/java/android/app/servertransaction/LaunchActivityItem.java
index 7819e1e..235a9f7 100644
--- a/core/java/android/app/servertransaction/LaunchActivityItem.java
+++ b/core/java/android/app/servertransaction/LaunchActivityItem.java
@@ -20,9 +20,12 @@
 
 import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
 
+import static java.util.Objects.requireNonNull;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityClient;
+import android.app.ActivityManager.ProcessState;
 import android.app.ActivityOptions.SceneTransitionInfo;
 import android.app.ActivityThread.ActivityClientRecord;
 import android.app.ClientTransactionHandler;
@@ -52,41 +55,148 @@
 
 /**
  * Request to launch an activity.
+ *
  * @hide
  */
 public class LaunchActivityItem extends ClientTransactionItem {
 
-    private IBinder mActivityToken;
+    @NonNull
+    private final IBinder mActivityToken;
+
+    // TODO(b/170729553): Mark this with @NonNull and final once @UnsupportedAppUsage removed.
+    //  We cannot do it now to avoid app compatibility regression.
     @UnsupportedAppUsage
     private Intent mIntent;
-    private int mIdent;
+
+    // TODO(b/170729553): Mark this with @NonNull and final once @UnsupportedAppUsage removed.
+    //  We cannot do it now to avoid app compatibility regression.
     @UnsupportedAppUsage
     private ActivityInfo mInfo;
-    private Configuration mCurConfig;
-    private Configuration mOverrideConfig;
-    private int mDeviceId;
-    private String mReferrer;
-    private IVoiceInteractor mVoiceInteractor;
-    private int mProcState;
-    private Bundle mState;
-    private PersistableBundle mPersistentState;
-    private List<ResultInfo> mPendingResults;
-    private List<ReferrerIntent> mPendingNewIntents;
-    private SceneTransitionInfo mSceneTransitionInfo;
-    private boolean mIsForward;
-    private ProfilerInfo mProfilerInfo;
-    private IBinder mAssistToken;
-    private IBinder mShareableActivityToken;
-    private boolean mLaunchedFromBubble;
-    private IBinder mTaskFragmentToken;
-    private IBinder mInitialCallerInfoAccessToken;
-    private ActivityWindowInfo mActivityWindowInfo;
+
+    @NonNull
+    private final Configuration mCurConfig;
+
+    @NonNull
+    private final Configuration mOverrideConfig;
+
+    @Nullable
+    private final String mReferrer;
+
+    @Nullable
+    private final IVoiceInteractor mVoiceInteractor;
+
+    @Nullable
+    private final Bundle mState;
+
+    @Nullable
+    private final PersistableBundle mPersistentState;
+
+    @Nullable
+    private final List<ResultInfo> mPendingResults;
+
+    @Nullable
+    private final List<ReferrerIntent> mPendingNewIntents;
+
+    @Nullable
+    private final SceneTransitionInfo mSceneTransitionInfo;
+
+    @Nullable
+    private final ProfilerInfo mProfilerInfo;
+
+    @NonNull
+    private final IBinder mAssistToken;
+
+    @NonNull
+    private final IBinder mShareableActivityToken;
+
+    @Nullable
+    private final IBinder mTaskFragmentToken;
+
+    @NonNull
+    private final IBinder mInitialCallerInfoAccessToken;
+
+    @NonNull
+    private final ActivityWindowInfo mActivityWindowInfo;
 
     /**
      * It is only non-null if the process is the first time to launch activity. It is only an
      * optimization for quick look up of the interface so the field is ignored for comparison.
      */
-    private IActivityClientController mActivityClientController;
+    @Nullable
+    private final IActivityClientController mActivityClientController;
+
+    private final int mIdent;
+    private final int mDeviceId;
+    private final int mProcState;
+    private final boolean mIsForward;
+    private final boolean mLaunchedFromBubble;
+
+    public LaunchActivityItem(@NonNull IBinder activityToken, @NonNull Intent intent,
+            int ident, @NonNull ActivityInfo info, @NonNull Configuration curConfig,
+            @NonNull Configuration overrideConfig, int deviceId, @Nullable String referrer,
+            @Nullable IVoiceInteractor voiceInteractor, @ProcessState int procState,
+            @Nullable Bundle state, @Nullable PersistableBundle persistentState,
+            @Nullable List<ResultInfo> pendingResults,
+            @Nullable List<ReferrerIntent> pendingNewIntents,
+            @Nullable SceneTransitionInfo sceneTransitionInfo,
+            boolean isForward, @Nullable ProfilerInfo profilerInfo, @NonNull IBinder assistToken,
+            @Nullable IActivityClientController activityClientController,
+            @NonNull IBinder shareableActivityToken, boolean launchedFromBubble,
+            @Nullable IBinder taskFragmentToken, @NonNull IBinder initialCallerInfoAccessToken,
+            @NonNull ActivityWindowInfo activityWindowInfo) {
+        this(activityToken, ident, new Configuration(curConfig), new Configuration(overrideConfig),
+                deviceId, referrer, voiceInteractor, procState,
+                state != null ? new Bundle(state) : null,
+                persistentState != null ? new PersistableBundle(persistentState) : null,
+                pendingResults != null ? new ArrayList<>(pendingResults) : null,
+                pendingNewIntents != null ? new ArrayList<>(pendingNewIntents) : null,
+                sceneTransitionInfo, isForward,
+                profilerInfo != null ? new ProfilerInfo(profilerInfo) : null,
+                assistToken, activityClientController, shareableActivityToken, launchedFromBubble,
+                taskFragmentToken, initialCallerInfoAccessToken,
+                new ActivityWindowInfo(activityWindowInfo));
+        mIntent = new Intent(intent);
+        mInfo = new ActivityInfo(info);
+    }
+
+    // TODO(b/170729553): Merge this constructor with previous one if no @UnsupportedAppUsage filed.
+    //  We cannot do it now to avoid app compatibility regression.
+    private LaunchActivityItem(@NonNull IBinder activityToken, int ident,
+            @NonNull Configuration curConfig,
+            @NonNull Configuration overrideConfig, int deviceId, @Nullable String referrer,
+            @Nullable IVoiceInteractor voiceInteractor, @ProcessState int procState,
+            @Nullable Bundle state, @Nullable PersistableBundle persistentState,
+            @Nullable List<ResultInfo> pendingResults,
+            @Nullable List<ReferrerIntent> pendingNewIntents,
+            @Nullable SceneTransitionInfo sceneTransitionInfo,
+            boolean isForward, @Nullable ProfilerInfo profilerInfo, @NonNull IBinder assistToken,
+            @Nullable IActivityClientController activityClientController,
+            @NonNull IBinder shareableActivityToken, boolean launchedFromBubble,
+            @Nullable IBinder taskFragmentToken, @NonNull IBinder initialCallerInfoAccessToken,
+            @NonNull ActivityWindowInfo activityWindowInfo) {
+        mActivityToken = activityToken;
+        mIdent = ident;
+        mCurConfig = curConfig;
+        mOverrideConfig = overrideConfig;
+        mDeviceId = deviceId;
+        mReferrer = referrer;
+        mVoiceInteractor = voiceInteractor;
+        mProcState = procState;
+        mState = state;
+        mPersistentState = persistentState;
+        mPendingResults = pendingResults;
+        mPendingNewIntents = pendingNewIntents;
+        mSceneTransitionInfo = sceneTransitionInfo;
+        mIsForward = isForward;
+        mProfilerInfo = profilerInfo;
+        mAssistToken = assistToken;
+        mActivityClientController = activityClientController;
+        mShareableActivityToken = shareableActivityToken;
+        mLaunchedFromBubble = launchedFromBubble;
+        mTaskFragmentToken = taskFragmentToken;
+        mInitialCallerInfoAccessToken = initialCallerInfoAccessToken;
+        mActivityWindowInfo = activityWindowInfo;
+    }
 
     @Override
     public void preExecute(@NonNull ClientTransactionHandler client) {
@@ -119,44 +229,6 @@
         client.countLaunchingActivities(-1);
     }
 
-    // ObjectPoolItem implementation
-
-    private LaunchActivityItem() {}
-
-    /** Obtain an instance initialized with provided params. */
-    @NonNull
-    public static LaunchActivityItem obtain(@NonNull IBinder activityToken, @NonNull Intent intent,
-            int ident, @NonNull ActivityInfo info, @NonNull Configuration curConfig,
-            @NonNull Configuration overrideConfig, int deviceId, @Nullable String referrer,
-            @Nullable IVoiceInteractor voiceInteractor, int procState, @Nullable Bundle state,
-            @Nullable PersistableBundle persistentState, @Nullable List<ResultInfo> pendingResults,
-            @Nullable List<ReferrerIntent> pendingNewIntents,
-            @Nullable SceneTransitionInfo sceneTransitionInfo,
-            boolean isForward, @Nullable ProfilerInfo profilerInfo, @NonNull IBinder assistToken,
-            @Nullable IActivityClientController activityClientController,
-            @NonNull IBinder shareableActivityToken, boolean launchedFromBubble,
-            @Nullable IBinder taskFragmentToken, @NonNull IBinder initialCallerInfoAccessToken,
-            @NonNull ActivityWindowInfo activityWindowInfo) {
-        LaunchActivityItem instance = ObjectPool.obtain(LaunchActivityItem.class);
-        if (instance == null) {
-            instance = new LaunchActivityItem();
-        }
-        setValues(instance, activityToken, new Intent(intent), ident,  new ActivityInfo(info),
-                new Configuration(curConfig), new Configuration(overrideConfig), deviceId,
-                referrer, voiceInteractor, procState,
-                state != null ? new Bundle(state) : null,
-                persistentState != null ? new PersistableBundle(persistentState) : null,
-                pendingResults != null ? new ArrayList<>(pendingResults) : null,
-                pendingNewIntents != null ? new ArrayList<>(pendingNewIntents) : null,
-                sceneTransitionInfo, isForward,
-                profilerInfo != null ? new ProfilerInfo(profilerInfo) : null,
-                assistToken, activityClientController, shareableActivityToken,
-                launchedFromBubble, taskFragmentToken, initialCallerInfoAccessToken,
-                new ActivityWindowInfo(activityWindowInfo));
-
-        return instance;
-    }
-
     @VisibleForTesting(visibility = PACKAGE)
     @NonNull
     @Override
@@ -164,22 +236,13 @@
         return mActivityToken;
     }
 
-    @Override
-    public void recycle() {
-        setValues(this, null, null, 0, null, null, null, 0, null, null, 0, null, null, null, null,
-                null, false, null, null, null, null, false, null, null, null);
-        ObjectPool.recycle(this);
-    }
-
     // Parcelable implementation
 
-    /** Write from Parcel. */
+    /** Writes to Parcel. */
     @Override
     public void writeToParcel(@NonNull Parcel dest, int flags) {
         dest.writeStrongBinder(mActivityToken);
-        dest.writeTypedObject(mIntent, flags);
         dest.writeInt(mIdent);
-        dest.writeTypedObject(mInfo, flags);
         dest.writeTypedObject(mCurConfig, flags);
         dest.writeTypedObject(mOverrideConfig, flags);
         dest.writeInt(mDeviceId);
@@ -200,28 +263,40 @@
         dest.writeStrongBinder(mTaskFragmentToken);
         dest.writeStrongBinder(mInitialCallerInfoAccessToken);
         dest.writeTypedObject(mActivityWindowInfo, flags);
+
+        dest.writeTypedObject(mIntent, flags);
+        dest.writeTypedObject(mInfo, flags);
     }
 
-    /** Read from Parcel. */
+    /** Reads from Parcel. */
     private LaunchActivityItem(@NonNull Parcel in) {
-        setValues(this, in.readStrongBinder(), in.readTypedObject(Intent.CREATOR), in.readInt(),
-                in.readTypedObject(ActivityInfo.CREATOR), in.readTypedObject(Configuration.CREATOR),
-                in.readTypedObject(Configuration.CREATOR), in.readInt(), in.readString(),
-                IVoiceInteractor.Stub.asInterface(in.readStrongBinder()), in.readInt(),
-                in.readBundle(getClass().getClassLoader()),
-                in.readPersistableBundle(getClass().getClassLoader()),
-                in.createTypedArrayList(ResultInfo.CREATOR),
-                in.createTypedArrayList(ReferrerIntent.CREATOR),
-                in.readTypedObject(SceneTransitionInfo.CREATOR),
-                in.readBoolean(),
-                in.readTypedObject(ProfilerInfo.CREATOR),
-                in.readStrongBinder(),
-                IActivityClientController.Stub.asInterface(in.readStrongBinder()),
-                in.readStrongBinder(),
-                in.readBoolean(),
-                in.readStrongBinder(),
-                in.readStrongBinder(),
-                in.readTypedObject(ActivityWindowInfo.CREATOR));
+        this(in.readStrongBinder() /* activityToken */,
+                in.readInt() /* ident */,
+                requireNonNull(in.readTypedObject(Configuration.CREATOR)) /* curConfig */,
+                requireNonNull(in.readTypedObject(Configuration.CREATOR)) /* overrideConfig */,
+                in.readInt() /* deviceId */,
+                in.readString() /* referrer */,
+                IVoiceInteractor.Stub.asInterface(in.readStrongBinder()) /* voiceInteractor */,
+                in.readInt() /* procState */,
+                in.readBundle(in.getClass().getClassLoader()) /* state */,
+                in.readPersistableBundle(in.getClass().getClassLoader()) /* persistentState */,
+                in.createTypedArrayList(ResultInfo.CREATOR) /* pendingResults */,
+                in.createTypedArrayList(ReferrerIntent.CREATOR) /* pendingNewIntents */,
+                in.readTypedObject(SceneTransitionInfo.CREATOR) /* sceneTransitionInfo */,
+                in.readBoolean() /* isForward */,
+                in.readTypedObject(ProfilerInfo.CREATOR) /* profilerInfo */,
+                in.readStrongBinder() /* assistToken */,
+                IActivityClientController.Stub.asInterface(
+                        in.readStrongBinder()) /* activityClientController */,
+                in.readStrongBinder() /* shareableActivityToken */,
+                in.readBoolean() /* launchedFromBubble */,
+                in.readStrongBinder() /* taskFragmentToken */,
+                in.readStrongBinder() /* initialCallerInfoAccessToken */,
+                requireNonNull(in.readTypedObject(ActivityWindowInfo.CREATOR))
+                /* activityWindowInfo */
+        );
+        mIntent = in.readTypedObject(Intent.CREATOR);
+        mInfo = in.readTypedObject(ActivityInfo.CREATOR);
     }
 
     public static final @NonNull Creator<LaunchActivityItem> CREATOR = new Creator<>() {
@@ -339,45 +414,4 @@
                 + ",activityWindowInfo=" + mActivityWindowInfo
                 + "}";
     }
-
-    // Using the same method to set and clear values to make sure we don't forget anything
-    private static void setValues(@Nullable LaunchActivityItem instance,
-            @Nullable IBinder activityToken, @Nullable Intent intent, int ident,
-            @Nullable ActivityInfo info, @Nullable Configuration curConfig,
-            @Nullable Configuration overrideConfig, int deviceId,
-            @Nullable String referrer, @Nullable IVoiceInteractor voiceInteractor,
-            int procState, @Nullable Bundle state, @Nullable PersistableBundle persistentState,
-            @Nullable List<ResultInfo> pendingResults,
-            @Nullable List<ReferrerIntent> pendingNewIntents,
-            @Nullable SceneTransitionInfo sceneTransitionInfo, boolean isForward,
-            @Nullable ProfilerInfo profilerInfo, @Nullable IBinder assistToken,
-            @Nullable IActivityClientController activityClientController,
-            @Nullable IBinder shareableActivityToken, boolean launchedFromBubble,
-            @Nullable IBinder taskFragmentToken, @Nullable IBinder initialCallerInfoAccessToken,
-            @Nullable ActivityWindowInfo activityWindowInfo) {
-        instance.mActivityToken = activityToken;
-        instance.mIntent = intent;
-        instance.mIdent = ident;
-        instance.mInfo = info;
-        instance.mCurConfig = curConfig;
-        instance.mOverrideConfig = overrideConfig;
-        instance.mDeviceId = deviceId;
-        instance.mReferrer = referrer;
-        instance.mVoiceInteractor = voiceInteractor;
-        instance.mProcState = procState;
-        instance.mState = state;
-        instance.mPersistentState = persistentState;
-        instance.mPendingResults = pendingResults;
-        instance.mPendingNewIntents = pendingNewIntents;
-        instance.mSceneTransitionInfo = sceneTransitionInfo;
-        instance.mIsForward = isForward;
-        instance.mProfilerInfo = profilerInfo;
-        instance.mAssistToken = assistToken;
-        instance.mActivityClientController = activityClientController;
-        instance.mShareableActivityToken = shareableActivityToken;
-        instance.mLaunchedFromBubble = launchedFromBubble;
-        instance.mTaskFragmentToken = taskFragmentToken;
-        instance.mInitialCallerInfoAccessToken = initialCallerInfoAccessToken;
-        instance.mActivityWindowInfo = activityWindowInfo;
-    }
 }
diff --git a/core/java/android/app/servertransaction/MoveToDisplayItem.java b/core/java/android/app/servertransaction/MoveToDisplayItem.java
index 8706edd..1aa563a 100644
--- a/core/java/android/app/servertransaction/MoveToDisplayItem.java
+++ b/core/java/android/app/servertransaction/MoveToDisplayItem.java
@@ -18,6 +18,8 @@
 
 import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
 
+import static java.util.Objects.requireNonNull;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityThread.ActivityClientRecord;
@@ -33,13 +35,26 @@
 
 /**
  * Activity move to a different display message.
+ *
  * @hide
  */
 public class MoveToDisplayItem extends ActivityTransactionItem {
 
-    private int mTargetDisplayId;
-    private Configuration mConfiguration;
-    private ActivityWindowInfo mActivityWindowInfo;
+    private final int mTargetDisplayId;
+
+    @NonNull
+    private final Configuration mConfiguration;
+
+    @NonNull
+    private final ActivityWindowInfo mActivityWindowInfo;
+
+    public MoveToDisplayItem(@NonNull IBinder activityToken, int targetDisplayId,
+            @NonNull Configuration configuration, @NonNull ActivityWindowInfo activityWindowInfo) {
+        super(activityToken);
+        mTargetDisplayId = targetDisplayId;
+        mConfiguration = new Configuration(configuration);
+        mActivityWindowInfo = new ActivityWindowInfo(activityWindowInfo);
+    }
 
     @Override
     public void preExecute(@NonNull ClientTransactionHandler client) {
@@ -58,38 +73,9 @@
         Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
     }
 
-    // ObjectPoolItem implementation
-
-    private MoveToDisplayItem() {}
-
-    /** Obtain an instance initialized with provided params. */
-    @NonNull
-    public static MoveToDisplayItem obtain(@NonNull IBinder activityToken, int targetDisplayId,
-            @NonNull Configuration configuration, @NonNull ActivityWindowInfo activityWindowInfo) {
-        MoveToDisplayItem instance = ObjectPool.obtain(MoveToDisplayItem.class);
-        if (instance == null) {
-            instance = new MoveToDisplayItem();
-        }
-        instance.setActivityToken(activityToken);
-        instance.mTargetDisplayId = targetDisplayId;
-        instance.mConfiguration = new Configuration(configuration);
-        instance.mActivityWindowInfo = new ActivityWindowInfo(activityWindowInfo);
-
-        return instance;
-    }
-
-    @Override
-    public void recycle() {
-        super.recycle();
-        mTargetDisplayId = 0;
-        mConfiguration = null;
-        mActivityWindowInfo = null;
-        ObjectPool.recycle(this);
-    }
-
     // Parcelable implementation
 
-    /** Write to Parcel. */
+    /** Writes to Parcel. */
     @Override
     public void writeToParcel(@NonNull Parcel dest, int flags) {
         super.writeToParcel(dest, flags);
@@ -98,12 +84,12 @@
         dest.writeTypedObject(mActivityWindowInfo, flags);
     }
 
-    /** Read from Parcel. */
+    /** Reads from Parcel. */
     private MoveToDisplayItem(@NonNull Parcel in) {
         super(in);
         mTargetDisplayId = in.readInt();
-        mConfiguration = in.readTypedObject(Configuration.CREATOR);
-        mActivityWindowInfo = in.readTypedObject(ActivityWindowInfo.CREATOR);
+        mConfiguration = requireNonNull(in.readTypedObject(Configuration.CREATOR));
+        mActivityWindowInfo = requireNonNull(in.readTypedObject(ActivityWindowInfo.CREATOR));
     }
 
     public static final @NonNull Creator<MoveToDisplayItem> CREATOR = new Creator<>() {
diff --git a/core/java/android/app/servertransaction/NewIntentItem.java b/core/java/android/app/servertransaction/NewIntentItem.java
index acf2ea4..b5e9d66 100644
--- a/core/java/android/app/servertransaction/NewIntentItem.java
+++ b/core/java/android/app/servertransaction/NewIntentItem.java
@@ -38,13 +38,24 @@
 
 /**
  * New intent message.
+ *
  * @hide
  */
 public class NewIntentItem extends ActivityTransactionItem {
 
+    // TODO(b/170729553): Mark this with @NonNull and final once @UnsupportedAppUsage removed.
+    //  We cannot do it now to avoid app compatibility regression.
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     private List<ReferrerIntent> mIntents;
-    private boolean mResume;
+
+    private final boolean mResume;
+
+    public NewIntentItem(@NonNull IBinder activityToken,
+            @NonNull List<ReferrerIntent> intents, boolean resume) {
+        super(activityToken);
+        mIntents = new ArrayList<>(intents);
+        mResume = resume;
+    }
 
     @Override
     public int getPostExecutionState() {
@@ -59,36 +70,9 @@
         Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
     }
 
-    // ObjectPoolItem implementation
-
-    private NewIntentItem() {}
-
-    /** Obtain an instance initialized with provided params. */
-    @NonNull
-    public static NewIntentItem obtain(@NonNull IBinder activityToken,
-            @NonNull List<ReferrerIntent> intents, boolean resume) {
-        NewIntentItem instance = ObjectPool.obtain(NewIntentItem.class);
-        if (instance == null) {
-            instance = new NewIntentItem();
-        }
-        instance.setActivityToken(activityToken);
-        instance.mIntents = new ArrayList<>(intents);
-        instance.mResume = resume;
-
-        return instance;
-    }
-
-    @Override
-    public void recycle() {
-        super.recycle();
-        mIntents = null;
-        mResume = false;
-        ObjectPool.recycle(this);
-    }
-
     // Parcelable implementation
 
-    /** Write to Parcel. */
+    /** Writes to Parcel. */
     @Override
     public void writeToParcel(@NonNull Parcel dest, int flags) {
         super.writeToParcel(dest, flags);
@@ -96,10 +80,11 @@
         dest.writeTypedList(mIntents, flags);
     }
 
-    /** Read from Parcel. */
+    /** Reads from Parcel. */
     private NewIntentItem(@NonNull Parcel in) {
         super(in);
         mResume = in.readBoolean();
+        // TODO(b/170729553): Wrap with requireNonNull once @UnsupportedAppUsage removed.
         mIntents = in.createTypedArrayList(ReferrerIntent.CREATOR);
     }
 
diff --git a/core/java/android/app/servertransaction/ObjectPool.java b/core/java/android/app/servertransaction/ObjectPool.java
deleted file mode 100644
index 598bd8a..0000000
--- a/core/java/android/app/servertransaction/ObjectPool.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.app.servertransaction;
-
-import com.android.window.flags.Flags;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * An object pool that can provide reused objects if available.
- * @hide
- */
-class ObjectPool {
-
-    private static final Object sPoolSync = new Object();
-    private static final Map<Class, ArrayList<? extends ObjectPoolItem>> sPoolMap =
-            new HashMap<>();
-
-    private static final int MAX_POOL_SIZE = 50;
-
-    /**
-     * Obtain an instance of a specific class from the pool
-     * @param itemClass The class of the object we're looking for.
-     * @return An instance or null if there is none.
-     */
-    public static <T extends ObjectPoolItem> T obtain(Class<T> itemClass) {
-        if (Flags.disableObjectPool()) {
-            return null;
-        }
-        synchronized (sPoolSync) {
-            @SuppressWarnings("unchecked")
-            final ArrayList<T> itemPool = (ArrayList<T>) sPoolMap.get(itemClass);
-            if (itemPool != null && !itemPool.isEmpty()) {
-                return itemPool.remove(itemPool.size() - 1);
-            }
-            return null;
-        }
-    }
-
-    /**
-     * Recycle the object to the pool. The object should be properly cleared before this.
-     * @param item The object to recycle.
-     * @see ObjectPoolItem#recycle()
-     */
-    public static <T extends ObjectPoolItem> void recycle(T item) {
-        if (Flags.disableObjectPool()) {
-            return;
-        }
-        synchronized (sPoolSync) {
-            @SuppressWarnings("unchecked")
-            ArrayList<T> itemPool = (ArrayList<T>) sPoolMap.get(item.getClass());
-            if (itemPool == null) {
-                itemPool = new ArrayList<>();
-                sPoolMap.put(item.getClass(), itemPool);
-            }
-            // Check if the item is already in the pool
-            final int size = itemPool.size();
-            for (int i = 0; i < size; i++) {
-                if (itemPool.get(i) == item) {
-                    throw new IllegalStateException("Trying to recycle already recycled item");
-                }
-            }
-
-            if (size < MAX_POOL_SIZE) {
-                itemPool.add(item);
-            }
-        }
-    }
-}
diff --git a/core/java/android/app/servertransaction/ObjectPoolItem.java b/core/java/android/app/servertransaction/ObjectPoolItem.java
deleted file mode 100644
index 17bd4f3..0000000
--- a/core/java/android/app/servertransaction/ObjectPoolItem.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.app.servertransaction;
-
-/**
- * Base interface for all lifecycle items that can be put in object pool.
- * @hide
- */
-public interface ObjectPoolItem {
-    /**
-     * Clear the contents of the item and putting it to a pool. The implementation should call
-     * {@link ObjectPool#recycle(ObjectPoolItem)} passing itself.
-     */
-    void recycle();
-}
diff --git a/core/java/android/app/servertransaction/PauseActivityItem.java b/core/java/android/app/servertransaction/PauseActivityItem.java
index d230284..09fc51b 100644
--- a/core/java/android/app/servertransaction/PauseActivityItem.java
+++ b/core/java/android/app/servertransaction/PauseActivityItem.java
@@ -29,16 +29,29 @@
 
 /**
  * Request to move an activity to paused state.
+ *
  * @hide
  */
 public class PauseActivityItem extends ActivityLifecycleItem {
 
-    private static final String TAG = "PauseActivityItem";
+    private final boolean mFinished;
+    private final boolean mUserLeaving;
+    private final boolean mDontReport;
+    private final boolean mAutoEnteringPip;
 
-    private boolean mFinished;
-    private boolean mUserLeaving;
-    private boolean mDontReport;
-    private boolean mAutoEnteringPip;
+    public PauseActivityItem(@NonNull IBinder activityToken) {
+        this(activityToken, false /* finished */, false /* userLeaving */,
+                true /* dontReport */, false /* autoEnteringPip*/);
+    }
+
+    public PauseActivityItem(@NonNull IBinder activityToken, boolean finished,
+            boolean userLeaving, boolean dontReport, boolean autoEnteringPip) {
+        super(activityToken);
+        mFinished = finished;
+        mUserLeaving = userLeaving;
+        mDontReport = dontReport;
+        mAutoEnteringPip = autoEnteringPip;
+    }
 
     @Override
     public void execute(@NonNull ClientTransactionHandler client, @NonNull ActivityClientRecord r,
@@ -64,47 +77,9 @@
         ActivityClient.getInstance().activityPaused(getActivityToken());
     }
 
-    // ObjectPoolItem implementation
-
-    private PauseActivityItem() {}
-
-    /** Obtain an instance initialized with provided params. */
-    @NonNull
-    public static PauseActivityItem obtain(@NonNull IBinder activityToken, boolean finished,
-            boolean userLeaving, boolean dontReport, boolean autoEnteringPip) {
-        PauseActivityItem instance = ObjectPool.obtain(PauseActivityItem.class);
-        if (instance == null) {
-            instance = new PauseActivityItem();
-        }
-        instance.setActivityToken(activityToken);
-        instance.mFinished = finished;
-        instance.mUserLeaving = userLeaving;
-        instance.mDontReport = dontReport;
-        instance.mAutoEnteringPip = autoEnteringPip;
-
-        return instance;
-    }
-
-    /** Obtain an instance initialized with default params. */
-    @NonNull
-    public static PauseActivityItem obtain(@NonNull IBinder activityToken) {
-        return obtain(activityToken, false /* finished */, false /* userLeaving */,
-                true /* dontReport */, false /* autoEnteringPip*/);
-    }
-
-    @Override
-    public void recycle() {
-        super.recycle();
-        mFinished = false;
-        mUserLeaving = false;
-        mDontReport = false;
-        mAutoEnteringPip = false;
-        ObjectPool.recycle(this);
-    }
-
     // Parcelable implementation
 
-    /** Write to Parcel. */
+    /** Writes to Parcel. */
     @Override
     public void writeToParcel(@NonNull Parcel dest, int flags) {
         super.writeToParcel(dest, flags);
@@ -114,7 +89,7 @@
         dest.writeBoolean(mAutoEnteringPip);
     }
 
-    /** Read from Parcel. */
+    /** Reads from Parcel. */
     private PauseActivityItem(@NonNull Parcel in) {
         super(in);
         mFinished = in.readBoolean();
diff --git a/core/java/android/app/servertransaction/PipStateTransactionItem.java b/core/java/android/app/servertransaction/PipStateTransactionItem.java
index 30289ef..ddeb2c1 100644
--- a/core/java/android/app/servertransaction/PipStateTransactionItem.java
+++ b/core/java/android/app/servertransaction/PipStateTransactionItem.java
@@ -28,11 +28,19 @@
 
 /**
  * Request an activity to enter picture-in-picture mode.
+ *
  * @hide
  */
 public final class PipStateTransactionItem extends ActivityTransactionItem {
 
-    private PictureInPictureUiState mPipState;
+    @NonNull
+    private final PictureInPictureUiState mPipState;
+
+    public PipStateTransactionItem(@NonNull IBinder activityToken,
+            @NonNull PictureInPictureUiState pipState) {
+        super(activityToken);
+        mPipState = pipState;
+    }
 
     @Override
     public void execute(@NonNull ClientTransactionHandler client, @NonNull ActivityClientRecord r,
@@ -40,41 +48,16 @@
         client.handlePictureInPictureStateChanged(r, mPipState);
     }
 
-    // ObjectPoolItem implementation
-
-    private PipStateTransactionItem() {}
-
-    /** Obtain an instance initialized with provided params. */
-    @NonNull
-    public static PipStateTransactionItem obtain(@NonNull IBinder activityToken,
-            @NonNull PictureInPictureUiState pipState) {
-        PipStateTransactionItem instance = ObjectPool.obtain(PipStateTransactionItem.class);
-        if (instance == null) {
-            instance = new PipStateTransactionItem();
-        }
-        instance.setActivityToken(activityToken);
-        instance.mPipState = pipState;
-
-        return instance;
-    }
-
-    @Override
-    public void recycle() {
-        super.recycle();
-        mPipState = null;
-        ObjectPool.recycle(this);
-    }
-
     // Parcelable implementation
 
-    /** Write to Parcel. */
+    /** Writes to Parcel. */
     @Override
     public void writeToParcel(@NonNull Parcel dest, int flags) {
         super.writeToParcel(dest, flags);
         mPipState.writeToParcel(dest, flags);
     }
 
-    /** Read from Parcel. */
+    /** Reads from Parcel. */
     private PipStateTransactionItem(@NonNull Parcel in) {
         super(in);
         mPipState = PictureInPictureUiState.CREATOR.createFromParcel(in);
diff --git a/core/java/android/app/servertransaction/RefreshCallbackItem.java b/core/java/android/app/servertransaction/RefreshCallbackItem.java
index a3f82e9..57fd97a 100644
--- a/core/java/android/app/servertransaction/RefreshCallbackItem.java
+++ b/core/java/android/app/servertransaction/RefreshCallbackItem.java
@@ -44,7 +44,20 @@
     // Whether refresh should happen using the "stopped -> resumed" cycle or
     // "paused -> resumed" cycle.
     @LifecycleState
-    private int mPostExecutionState;
+    private final int mPostExecutionState;
+
+    /**
+     * Creates a new RefreshCallbackItem.
+     *
+     * @param activityToken      the target client activity.
+     * @param postExecutionState indicating whether refresh should happen using the
+     *                           "stopped -> "resumed" cycle or "paused -> resumed" cycle.
+     */
+    public RefreshCallbackItem(
+            @NonNull IBinder activityToken, @LifecycleState int postExecutionState) {
+        super(activityToken);
+        mPostExecutionState = postExecutionState;
+    }
 
     @Override
     public void execute(@NonNull ClientTransactionHandler client,
@@ -67,47 +80,21 @@
         return false;
     }
 
-    // ObjectPoolItem implementation
-
-    @Override
-    public void recycle() {
-        super.recycle();
-        ObjectPool.recycle(this);
-    }
-
-    /**
-    * Obtain an instance initialized with provided params.
-    * @param postExecutionState indicating whether refresh should happen using the
-    *        "stopped -> resumed" cycle or "paused -> resumed" cycle.
-    */
-    @NonNull
-    public static RefreshCallbackItem obtain(@NonNull IBinder activityToken,
-            @LifecycleState int postExecutionState) {
-        if (postExecutionState != ON_STOP && postExecutionState != ON_PAUSE) {
-            throw new IllegalArgumentException(
-                    "Only ON_STOP or ON_PAUSE are allowed as a post execution state for "
-                            + "RefreshCallbackItem but got " + postExecutionState);
-        }
-        RefreshCallbackItem instance =
-                ObjectPool.obtain(RefreshCallbackItem.class);
-        if (instance == null) {
-            instance = new RefreshCallbackItem();
-        }
-        instance.setActivityToken(activityToken);
-        instance.mPostExecutionState = postExecutionState;
-        return instance;
-    }
-
-    private RefreshCallbackItem() {}
-
     // Parcelable implementation
 
+    /** Writes to Parcel. */
     @Override
     public void writeToParcel(@NonNull Parcel dest, int flags) {
         super.writeToParcel(dest, flags);
         dest.writeInt(mPostExecutionState);
     }
 
+    /** Reads from Parcel. */
+    private RefreshCallbackItem(@NonNull Parcel in) {
+        super(in);
+        mPostExecutionState = in.readInt();
+    }
+
     @Override
     public boolean equals(@Nullable Object o) {
         if (this == o) {
@@ -134,11 +121,6 @@
                 + ",mPostExecutionState=" + mPostExecutionState + "}";
     }
 
-    private RefreshCallbackItem(@NonNull Parcel in) {
-        super(in);
-        mPostExecutionState = in.readInt();
-    }
-
     public static final @NonNull Creator<RefreshCallbackItem> CREATOR = new Creator<>() {
 
         public RefreshCallbackItem createFromParcel(@NonNull Parcel in) {
diff --git a/core/java/android/app/servertransaction/ResumeActivityItem.java b/core/java/android/app/servertransaction/ResumeActivityItem.java
index 4a0ea98..a28791f 100644
--- a/core/java/android/app/servertransaction/ResumeActivityItem.java
+++ b/core/java/android/app/servertransaction/ResumeActivityItem.java
@@ -22,6 +22,7 @@
 import android.annotation.Nullable;
 import android.app.ActivityClient;
 import android.app.ActivityManager;
+import android.app.ActivityManager.ProcessState;
 import android.app.ActivityThread.ActivityClientRecord;
 import android.app.ClientTransactionHandler;
 import android.os.IBinder;
@@ -30,22 +31,37 @@
 
 /**
  * Request to move an activity to resumed state.
+ *
  * @hide
  */
 public class ResumeActivityItem extends ActivityLifecycleItem {
 
-    private static final String TAG = "ResumeActivityItem";
+    @ProcessState
+    private final int mProcState;
 
-    private int mProcState;
-    private boolean mUpdateProcState;
-    private boolean mIsForward;
+    private final boolean mIsForward;
+
     // Whether we should send compat fake focus when the activity is resumed. This is needed
     // because some game engines wait to get focus before drawing the content of the app.
-    private boolean mShouldSendCompatFakeFocus;
+    private final boolean mShouldSendCompatFakeFocus;
+
+    public ResumeActivityItem(@NonNull IBinder activityToken, boolean isForward,
+            boolean shouldSendCompatFakeFocus) {
+        this(activityToken, ActivityManager.PROCESS_STATE_UNKNOWN, isForward,
+                shouldSendCompatFakeFocus);
+    }
+
+    public ResumeActivityItem(@NonNull IBinder activityToken, @ProcessState int procState,
+            boolean isForward, boolean shouldSendCompatFakeFocus) {
+        super(activityToken);
+        mProcState = procState;
+        mIsForward = isForward;
+        mShouldSendCompatFakeFocus = shouldSendCompatFakeFocus;
+    }
 
     @Override
     public void preExecute(@NonNull ClientTransactionHandler client) {
-        if (mUpdateProcState) {
+        if (mProcState != ActivityManager.PROCESS_STATE_UNKNOWN) {
             client.updateProcessState(mProcState, false);
         }
     }
@@ -72,71 +88,21 @@
         return ON_RESUME;
     }
 
-    // ObjectPoolItem implementation
-
-    private ResumeActivityItem() {}
-
-    /** Obtain an instance initialized with provided params. */
-    @NonNull
-    public static ResumeActivityItem obtain(@NonNull IBinder activityToken, int procState,
-            boolean isForward, boolean shouldSendCompatFakeFocus) {
-        ResumeActivityItem instance = ObjectPool.obtain(ResumeActivityItem.class);
-        if (instance == null) {
-            instance = new ResumeActivityItem();
-        }
-        instance.setActivityToken(activityToken);
-        instance.mProcState = procState;
-        instance.mUpdateProcState = true;
-        instance.mIsForward = isForward;
-        instance.mShouldSendCompatFakeFocus = shouldSendCompatFakeFocus;
-
-        return instance;
-    }
-
-    /** Obtain an instance initialized with provided params. */
-    @NonNull
-    public static ResumeActivityItem obtain(@NonNull IBinder activityToken, boolean isForward,
-            boolean shouldSendCompatFakeFocus) {
-        ResumeActivityItem instance = ObjectPool.obtain(ResumeActivityItem.class);
-        if (instance == null) {
-            instance = new ResumeActivityItem();
-        }
-        instance.setActivityToken(activityToken);
-        instance.mProcState = ActivityManager.PROCESS_STATE_UNKNOWN;
-        instance.mUpdateProcState = false;
-        instance.mIsForward = isForward;
-        instance.mShouldSendCompatFakeFocus = shouldSendCompatFakeFocus;
-
-        return instance;
-    }
-
-    @Override
-    public void recycle() {
-        super.recycle();
-        mProcState = ActivityManager.PROCESS_STATE_UNKNOWN;
-        mUpdateProcState = false;
-        mIsForward = false;
-        mShouldSendCompatFakeFocus = false;
-        ObjectPool.recycle(this);
-    }
-
     // Parcelable implementation
 
-    /** Write to Parcel. */
+    /** Writes to Parcel. */
     @Override
     public void writeToParcel(@NonNull Parcel dest, int flags) {
         super.writeToParcel(dest, flags);
         dest.writeInt(mProcState);
-        dest.writeBoolean(mUpdateProcState);
         dest.writeBoolean(mIsForward);
         dest.writeBoolean(mShouldSendCompatFakeFocus);
     }
 
-    /** Read from Parcel. */
+    /** Reads from Parcel. */
     private ResumeActivityItem(@NonNull Parcel in) {
         super(in);
         mProcState = in.readInt();
-        mUpdateProcState = in.readBoolean();
         mIsForward = in.readBoolean();
         mShouldSendCompatFakeFocus = in.readBoolean();
     }
@@ -160,7 +126,7 @@
             return false;
         }
         final ResumeActivityItem other = (ResumeActivityItem) o;
-        return mProcState == other.mProcState && mUpdateProcState == other.mUpdateProcState
+        return mProcState == other.mProcState
                 && mIsForward == other.mIsForward
                 && mShouldSendCompatFakeFocus == other.mShouldSendCompatFakeFocus;
     }
@@ -170,7 +136,6 @@
         int result = 17;
         result = 31 * result + super.hashCode();
         result = 31 * result + mProcState;
-        result = 31 * result + (mUpdateProcState ? 1 : 0);
         result = 31 * result + (mIsForward ? 1 : 0);
         result = 31 * result + (mShouldSendCompatFakeFocus ? 1 : 0);
         return result;
@@ -180,7 +145,6 @@
     public String toString() {
         return "ResumeActivityItem{" + super.toString()
                 + ",procState=" + mProcState
-                + ",updateProcState=" + mUpdateProcState
                 + ",isForward=" + mIsForward
                 + ",shouldSendCompatFakeFocus=" + mShouldSendCompatFakeFocus + "}";
     }
diff --git a/core/java/android/app/servertransaction/StartActivityItem.java b/core/java/android/app/servertransaction/StartActivityItem.java
index a0f93ce..6bae92b 100644
--- a/core/java/android/app/servertransaction/StartActivityItem.java
+++ b/core/java/android/app/servertransaction/StartActivityItem.java
@@ -29,13 +29,19 @@
 
 /**
  * Request to move an activity to started and visible state.
+ *
  * @hide
  */
 public class StartActivityItem extends ActivityLifecycleItem {
 
-    private static final String TAG = "StartActivityItem";
+    @Nullable
+    private final SceneTransitionInfo mSceneTransitionInfo;
 
-    private SceneTransitionInfo mSceneTransitionInfo;
+    public StartActivityItem(@NonNull IBinder activityToken,
+            @Nullable SceneTransitionInfo sceneTransitionInfo) {
+        super(activityToken);
+        mSceneTransitionInfo = sceneTransitionInfo;
+    }
 
     @Override
     public void execute(@NonNull ClientTransactionHandler client, @NonNull ActivityClientRecord r,
@@ -50,41 +56,16 @@
         return ON_START;
     }
 
-    // ObjectPoolItem implementation
-
-    private StartActivityItem() {}
-
-    /** Obtain an instance initialized with provided params. */
-    @NonNull
-    public static StartActivityItem obtain(@NonNull IBinder activityToken,
-            @Nullable SceneTransitionInfo sceneTransitionInfo) {
-        StartActivityItem instance = ObjectPool.obtain(StartActivityItem.class);
-        if (instance == null) {
-            instance = new StartActivityItem();
-        }
-        instance.setActivityToken(activityToken);
-        instance.mSceneTransitionInfo = sceneTransitionInfo;
-
-        return instance;
-    }
-
-    @Override
-    public void recycle() {
-        super.recycle();
-        mSceneTransitionInfo = null;
-        ObjectPool.recycle(this);
-    }
-
     // Parcelable implementation
 
-    /** Write to Parcel. */
+    /** Writes to Parcel. */
     @Override
     public void writeToParcel(@NonNull Parcel dest, int flags) {
         super.writeToParcel(dest, flags);
         dest.writeTypedObject(mSceneTransitionInfo, flags);
     }
 
-    /** Read from Parcel. */
+    /** Reads from Parcel. */
     private StartActivityItem(@NonNull Parcel in) {
         super(in);
         mSceneTransitionInfo = in.readTypedObject(SceneTransitionInfo.CREATOR);
diff --git a/core/java/android/app/servertransaction/StopActivityItem.java b/core/java/android/app/servertransaction/StopActivityItem.java
index def7b3f..012b82e 100644
--- a/core/java/android/app/servertransaction/StopActivityItem.java
+++ b/core/java/android/app/servertransaction/StopActivityItem.java
@@ -27,11 +27,14 @@
 
 /**
  * Request to move an activity to stopped state.
+ *
  * @hide
  */
 public class StopActivityItem extends ActivityLifecycleItem {
 
-    private static final String TAG = "StopActivityItem";
+    public StopActivityItem(@NonNull IBinder activityToken) {
+        super(activityToken);
+    }
 
     @Override
     public void execute(@NonNull ClientTransactionHandler client, @NonNull ActivityClientRecord r,
@@ -53,34 +56,9 @@
         return ON_STOP;
     }
 
-    // ObjectPoolItem implementation
-
-    private StopActivityItem() {}
-
-    /**
-     * Obtain an instance initialized with provided params.
-     * @param activityToken the activity that stops.
-     */
-    @NonNull
-    public static StopActivityItem obtain(@NonNull IBinder activityToken) {
-        StopActivityItem instance = ObjectPool.obtain(StopActivityItem.class);
-        if (instance == null) {
-            instance = new StopActivityItem();
-        }
-        instance.setActivityToken(activityToken);
-
-        return instance;
-    }
-
-    @Override
-    public void recycle() {
-        super.recycle();
-        ObjectPool.recycle(this);
-    }
-
     // Parcelable implementation
 
-    /** Read from Parcel. */
+    /** Reads from Parcel. */
     private StopActivityItem(@NonNull Parcel in) {
         super(in);
     }
diff --git a/core/java/android/app/servertransaction/TopResumedActivityChangeItem.java b/core/java/android/app/servertransaction/TopResumedActivityChangeItem.java
index 23d4505..b5f8345 100644
--- a/core/java/android/app/servertransaction/TopResumedActivityChangeItem.java
+++ b/core/java/android/app/servertransaction/TopResumedActivityChangeItem.java
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package android.app.servertransaction;
 
 import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
@@ -28,11 +29,17 @@
 
 /**
  * Top resumed activity changed callback.
+ *
  * @hide
  */
 public class TopResumedActivityChangeItem extends ActivityTransactionItem {
 
-    private boolean mOnTop;
+    private final boolean mOnTop;
+
+    public TopResumedActivityChangeItem(@NonNull IBinder activityToken, boolean onTop) {
+        super(activityToken);
+        mOnTop = onTop;
+    }
 
     @Override
     public void execute(@NonNull ClientTransactionHandler client, @NonNull ActivityClientRecord r,
@@ -58,42 +65,16 @@
         ActivityClient.getInstance().activityTopResumedStateLost();
     }
 
-    // ObjectPoolItem implementation
-
-    private TopResumedActivityChangeItem() {}
-
-    /** Obtain an instance initialized with provided params. */
-    @NonNull
-    public static TopResumedActivityChangeItem obtain(@NonNull IBinder activityToken,
-            boolean onTop) {
-        TopResumedActivityChangeItem instance =
-                ObjectPool.obtain(TopResumedActivityChangeItem.class);
-        if (instance == null) {
-            instance = new TopResumedActivityChangeItem();
-        }
-        instance.setActivityToken(activityToken);
-        instance.mOnTop = onTop;
-
-        return instance;
-    }
-
-    @Override
-    public void recycle() {
-        super.recycle();
-        mOnTop = false;
-        ObjectPool.recycle(this);
-    }
-
     // Parcelable implementation
 
-    /** Write to Parcel. */
+    /** Writes to Parcel. */
     @Override
     public void writeToParcel(@NonNull Parcel dest, int flags) {
         super.writeToParcel(dest, flags);
         dest.writeBoolean(mOnTop);
     }
 
-    /** Read from Parcel. */
+    /** Reads from Parcel. */
     private TopResumedActivityChangeItem(@NonNull Parcel in) {
         super(in);
         mOnTop = in.readBoolean();
@@ -131,7 +112,6 @@
 
     @Override
     public String toString() {
-        return "TopResumedActivityChangeItem{" + super.toString()
-                + ",onTop=" + mOnTop + "}";
+        return "TopResumedActivityChangeItem{" + super.toString() + ",onTop=" + mOnTop + "}";
     }
 }
diff --git a/core/java/android/app/servertransaction/TransactionExecutor.java b/core/java/android/app/servertransaction/TransactionExecutor.java
index 68012e2..3a23e6b 100644
--- a/core/java/android/app/servertransaction/TransactionExecutor.java
+++ b/core/java/android/app/servertransaction/TransactionExecutor.java
@@ -45,6 +45,7 @@
 
 /**
  * Class that manages transaction execution in the correct order.
+ *
  * @hide
  */
 public class TransactionExecutor {
diff --git a/core/java/android/app/servertransaction/TransactionExecutorHelper.java b/core/java/android/app/servertransaction/TransactionExecutorHelper.java
index 9f622e9..785fa59 100644
--- a/core/java/android/app/servertransaction/TransactionExecutorHelper.java
+++ b/core/java/android/app/servertransaction/TransactionExecutorHelper.java
@@ -42,6 +42,7 @@
 
 /**
  * Helper class for {@link TransactionExecutor} that contains utils for lifecycle path resolution.
+ *
  * @hide
  */
 public class TransactionExecutorHelper {
@@ -54,7 +55,7 @@
     // Temp holder for lifecycle path.
     // No direct transition between two states should take more than one complete cycle of 6 states.
     @ActivityLifecycleItem.LifecycleState
-    private IntArray mLifecycleSequence = new IntArray(6);
+    private final IntArray mLifecycleSequence = new IntArray(6 /* initialCapacity */);
 
     /**
      * Calculate the path through main lifecycle states for an activity and fill
@@ -197,13 +198,13 @@
                 // Fall through to return the PAUSE item to ensure the activity is properly
                 // resumed while relaunching.
             case ON_PAUSE:
-                lifecycleItem = PauseActivityItem.obtain(r.token);
+                lifecycleItem = new PauseActivityItem(r.token);
                 break;
             case ON_STOP:
-                lifecycleItem = StopActivityItem.obtain(r.token);
+                lifecycleItem = new StopActivityItem(r.token);
                 break;
             default:
-                lifecycleItem = ResumeActivityItem.obtain(r.token, false /* isForward */,
+                lifecycleItem = new ResumeActivityItem(r.token, false /* isForward */,
                         false /* shouldSendCompatFakeFocus */);
                 break;
         }
diff --git a/core/java/android/app/servertransaction/TransferSplashScreenViewStateItem.java b/core/java/android/app/servertransaction/TransferSplashScreenViewStateItem.java
index 11947e9..5068c39 100644
--- a/core/java/android/app/servertransaction/TransferSplashScreenViewStateItem.java
+++ b/core/java/android/app/servertransaction/TransferSplashScreenViewStateItem.java
@@ -29,12 +29,24 @@
 
 /**
  * Transfer a splash screen view to an Activity.
+ *
  * @hide
  */
 public class TransferSplashScreenViewStateItem extends ActivityTransactionItem {
 
-    private SplashScreenViewParcelable mSplashScreenViewParcelable;
-    private SurfaceControl mStartingWindowLeash;
+    @Nullable
+    private final SplashScreenViewParcelable mSplashScreenViewParcelable;
+
+    @Nullable
+    private final SurfaceControl mStartingWindowLeash;
+
+    public TransferSplashScreenViewStateItem(@NonNull IBinder activityToken,
+            @Nullable SplashScreenViewParcelable parcelable,
+            @Nullable SurfaceControl startingWindowLeash) {
+        super(activityToken);
+        mSplashScreenViewParcelable = parcelable;
+        mStartingWindowLeash = startingWindowLeash;
+    }
 
     @Override
     public void execute(@NonNull ClientTransactionHandler client,
@@ -43,14 +55,9 @@
         client.handleAttachSplashScreenView(r, mSplashScreenViewParcelable, mStartingWindowLeash);
     }
 
-    @Override
-    public void recycle() {
-        super.recycle();
-        mSplashScreenViewParcelable = null;
-        mStartingWindowLeash = null;
-        ObjectPool.recycle(this);
-    }
+    // Parcelable implementation
 
+    /** Writes to Parcel. */
     @Override
     public void writeToParcel(@NonNull Parcel dest, int flags) {
         super.writeToParcel(dest, flags);
@@ -58,31 +65,13 @@
         dest.writeTypedObject(mStartingWindowLeash, flags);
     }
 
-    private TransferSplashScreenViewStateItem() {}
-
+    /** Reads from Parcel. */
     private TransferSplashScreenViewStateItem(@NonNull Parcel in) {
         super(in);
         mSplashScreenViewParcelable = in.readTypedObject(SplashScreenViewParcelable.CREATOR);
         mStartingWindowLeash = in.readTypedObject(SurfaceControl.CREATOR);
     }
 
-    /** Obtain an instance initialized with provided params. */
-    @NonNull
-    public static TransferSplashScreenViewStateItem obtain(
-            @NonNull IBinder activityToken, @Nullable SplashScreenViewParcelable parcelable,
-            @Nullable SurfaceControl startingWindowLeash) {
-        TransferSplashScreenViewStateItem instance =
-                ObjectPool.obtain(TransferSplashScreenViewStateItem.class);
-        if (instance == null) {
-            instance = new TransferSplashScreenViewStateItem();
-        }
-        instance.setActivityToken(activityToken);
-        instance.mSplashScreenViewParcelable = parcelable;
-        instance.mStartingWindowLeash = startingWindowLeash;
-
-        return instance;
-    }
-
     public static final @NonNull Creator<TransferSplashScreenViewStateItem> CREATOR =
             new Creator<>() {
                 public TransferSplashScreenViewStateItem createFromParcel(@NonNull Parcel in) {
diff --git a/core/java/android/app/servertransaction/WindowContextInfoChangeItem.java b/core/java/android/app/servertransaction/WindowContextInfoChangeItem.java
index f6a7291..b2e87bd 100644
--- a/core/java/android/app/servertransaction/WindowContextInfoChangeItem.java
+++ b/core/java/android/app/servertransaction/WindowContextInfoChangeItem.java
@@ -30,14 +30,22 @@
 
 /**
  * {@link android.window.WindowContext} configuration change message.
+ *
  * @hide
  */
 public class WindowContextInfoChangeItem extends ClientTransactionItem {
 
-    @Nullable
-    private IBinder mClientToken;
-    @Nullable
-    private WindowContextInfo mInfo;
+    @NonNull
+    private final IBinder mClientToken;
+
+    @NonNull
+    private final WindowContextInfo mInfo;
+
+    public WindowContextInfoChangeItem(
+            @NonNull IBinder clientToken, @NonNull Configuration config, int displayId) {
+        mClientToken = requireNonNull(clientToken);
+        mInfo = new WindowContextInfo(new Configuration(config), displayId);
+    }
 
     @Override
     public void execute(@NonNull ClientTransactionHandler client,
@@ -45,31 +53,6 @@
         client.handleWindowContextInfoChanged(mClientToken, mInfo);
     }
 
-    // ObjectPoolItem implementation
-
-    private WindowContextInfoChangeItem() {}
-
-    /** Obtains an instance initialized with provided params. */
-    public static WindowContextInfoChangeItem obtain(
-            @NonNull IBinder clientToken, @NonNull Configuration config, int displayId) {
-        WindowContextInfoChangeItem instance =
-                ObjectPool.obtain(WindowContextInfoChangeItem.class);
-        if (instance == null) {
-            instance = new WindowContextInfoChangeItem();
-        }
-        instance.mClientToken = requireNonNull(clientToken);
-        instance.mInfo = new WindowContextInfo(new Configuration(config), displayId);
-
-        return instance;
-    }
-
-    @Override
-    public void recycle() {
-        mClientToken = null;
-        mInfo = null;
-        ObjectPool.recycle(this);
-    }
-
     // Parcelable implementation
 
     /** Writes to Parcel. */
@@ -82,7 +65,7 @@
     /** Reads from Parcel. */
     private WindowContextInfoChangeItem(@NonNull Parcel in) {
         mClientToken = in.readStrongBinder();
-        mInfo = in.readTypedObject(WindowContextInfo.CREATOR);
+        mInfo = requireNonNull(in.readTypedObject(WindowContextInfo.CREATOR));
     }
 
     public static final @NonNull Creator<WindowContextInfoChangeItem> CREATOR =
diff --git a/core/java/android/app/servertransaction/WindowContextWindowRemovalItem.java b/core/java/android/app/servertransaction/WindowContextWindowRemovalItem.java
index 1bea468..76b39d5 100644
--- a/core/java/android/app/servertransaction/WindowContextWindowRemovalItem.java
+++ b/core/java/android/app/servertransaction/WindowContextWindowRemovalItem.java
@@ -28,12 +28,17 @@
 
 /**
  * {@link android.window.WindowContext} window removal message.
+ *
  * @hide
  */
 public class WindowContextWindowRemovalItem extends ClientTransactionItem {
 
-    @Nullable
-    private IBinder mClientToken;
+    @NonNull
+    private final IBinder mClientToken;
+
+    public WindowContextWindowRemovalItem(@NonNull IBinder clientToken) {
+        mClientToken = requireNonNull(clientToken);
+    }
 
     @Override
     public void execute(@NonNull ClientTransactionHandler client,
@@ -41,28 +46,6 @@
         client.handleWindowContextWindowRemoval(mClientToken);
     }
 
-    // ObjectPoolItem implementation
-
-    private WindowContextWindowRemovalItem() {}
-
-    /** Obtains an instance initialized with provided params. */
-    public static WindowContextWindowRemovalItem obtain(@NonNull IBinder clientToken) {
-        WindowContextWindowRemovalItem instance =
-                ObjectPool.obtain(WindowContextWindowRemovalItem.class);
-        if (instance == null) {
-            instance = new WindowContextWindowRemovalItem();
-        }
-        instance.mClientToken = requireNonNull(clientToken);
-
-        return instance;
-    }
-
-    @Override
-    public void recycle() {
-        mClientToken = null;
-        ObjectPool.recycle(this);
-    }
-
     // Parcelable implementation
 
     /** Writes to Parcel. */
diff --git a/core/java/android/app/servertransaction/WindowStateInsetsControlChangeItem.java b/core/java/android/app/servertransaction/WindowStateInsetsControlChangeItem.java
index eb31db1..de88da5 100644
--- a/core/java/android/app/servertransaction/WindowStateInsetsControlChangeItem.java
+++ b/core/java/android/app/servertransaction/WindowStateInsetsControlChangeItem.java
@@ -16,6 +16,8 @@
 
 package android.app.servertransaction;
 
+import static java.util.Objects.requireNonNull;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ClientTransactionHandler;
@@ -33,16 +35,47 @@
 
 /**
  * Message to deliver window insets control change info.
+ *
  * @hide
  */
 public class WindowStateInsetsControlChangeItem extends WindowStateTransactionItem {
 
     private static final String TAG = "WindowStateInsetsControlChangeItem";
 
-    private InsetsState mInsetsState;
+    @NonNull
+    private final InsetsState mInsetsState;
+
+    @NonNull
+    private final InsetsSourceControl.Array mActiveControls;
+
+    public WindowStateInsetsControlChangeItem(@NonNull IWindow window,
+            @NonNull InsetsState insetsState, @NonNull InsetsSourceControl.Array activeControls) {
+        this(window, insetsState, activeControls, true /* copyActiveControls */);
+    }
 
     @VisibleForTesting
-    public InsetsSourceControl.Array mActiveControls;
+    public WindowStateInsetsControlChangeItem(@NonNull IWindow window,
+            @NonNull InsetsState insetsState,
+            @NonNull InsetsSourceControl.Array activeControls, boolean copyActiveControls) {
+        super(window);
+        mInsetsState = new InsetsState(insetsState, true /* copySources */);
+        if (copyActiveControls) {
+            mActiveControls = copy(requireNonNull(activeControls));
+        } else {
+            mActiveControls = requireNonNull(activeControls);
+        }
+    }
+
+    @NonNull
+    private static InsetsSourceControl.Array copy(@NonNull InsetsSourceControl.Array controls) {
+        final InsetsSourceControl.Array copiedControls = new InsetsSourceControl.Array(
+                controls, true /* copyControls */);
+        // This source control is an extra copy if the client is not local. By setting
+        // PARCELABLE_WRITE_RETURN_VALUE, the leash will be released at the end of
+        // SurfaceControl.writeToParcel.
+        copiedControls.setParcelableFlags(PARCELABLE_WRITE_RETURN_VALUE);
+        return copiedControls;
+    }
 
     @Override
     public void execute(@NonNull ClientTransactionHandler client, @NonNull IWindow window,
@@ -61,38 +94,6 @@
         Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
     }
 
-    // ObjectPoolItem implementation
-
-    private WindowStateInsetsControlChangeItem() {}
-
-    /** Obtains an instance initialized with provided params. */
-    public static WindowStateInsetsControlChangeItem obtain(@NonNull IWindow window,
-            @NonNull InsetsState insetsState, @NonNull InsetsSourceControl.Array activeControls) {
-        WindowStateInsetsControlChangeItem instance =
-                ObjectPool.obtain(WindowStateInsetsControlChangeItem.class);
-        if (instance == null) {
-            instance = new WindowStateInsetsControlChangeItem();
-        }
-        instance.setWindow(window);
-        instance.mInsetsState = new InsetsState(insetsState, true /* copySources */);
-        instance.mActiveControls = new InsetsSourceControl.Array(
-                activeControls, true /* copyControls */);
-        // This source control is an extra copy if the client is not local. By setting
-        // PARCELABLE_WRITE_RETURN_VALUE, the leash will be released at the end of
-        // SurfaceControl.writeToParcel.
-        instance.mActiveControls.setParcelableFlags(PARCELABLE_WRITE_RETURN_VALUE);
-
-        return instance;
-    }
-
-    @Override
-    public void recycle() {
-        super.recycle();
-        mInsetsState = null;
-        mActiveControls = null;
-        ObjectPool.recycle(this);
-    }
-
     // Parcelable implementation
 
     /** Writes to Parcel. */
@@ -106,9 +107,8 @@
     /** Reads from Parcel. */
     private WindowStateInsetsControlChangeItem(@NonNull Parcel in) {
         super(in);
-        mInsetsState = in.readTypedObject(InsetsState.CREATOR);
-        mActiveControls = in.readTypedObject(InsetsSourceControl.Array.CREATOR);
-
+        mInsetsState = requireNonNull(in.readTypedObject(InsetsState.CREATOR));
+        mActiveControls = requireNonNull(in.readTypedObject(InsetsSourceControl.Array.CREATOR));
     }
 
     public static final @NonNull Creator<WindowStateInsetsControlChangeItem> CREATOR =
diff --git a/core/java/android/app/servertransaction/WindowStateResizeItem.java b/core/java/android/app/servertransaction/WindowStateResizeItem.java
index 3c1fa4b..e1ddf6a 100644
--- a/core/java/android/app/servertransaction/WindowStateResizeItem.java
+++ b/core/java/android/app/servertransaction/WindowStateResizeItem.java
@@ -16,7 +16,7 @@
 
 package android.app.servertransaction;
 
-import static android.view.Display.INVALID_DISPLAY;
+import static java.util.Objects.requireNonNull;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -35,25 +35,54 @@
 
 /**
  * Message to deliver window resize info.
+ *
  * @hide
  */
 public class WindowStateResizeItem extends WindowStateTransactionItem {
 
     private static final String TAG = "WindowStateResizeItem";
 
-    private ClientWindowFrames mFrames;
-    private boolean mReportDraw;
-    private MergedConfiguration mConfiguration;
-    private InsetsState mInsetsState;
-    private boolean mForceLayout;
-    private boolean mAlwaysConsumeSystemBars;
-    private int mDisplayId;
-    private int mSyncSeqId;
-    private boolean mDragResizing;
+    @NonNull
+    private final ClientWindowFrames mFrames;
+
+    @NonNull
+    private final MergedConfiguration mConfiguration;
+
+    @NonNull
+    private final InsetsState mInsetsState;
 
     /** {@code null} if this is not an Activity window. */
     @Nullable
-    private ActivityWindowInfo mActivityWindowInfo;
+    private final ActivityWindowInfo mActivityWindowInfo;
+
+    private final boolean mReportDraw;
+    private final boolean mForceLayout;
+    private final boolean mAlwaysConsumeSystemBars;
+    private final int mDisplayId;
+    private final int mSyncSeqId;
+    private final boolean mDragResizing;
+
+    public WindowStateResizeItem(@NonNull IWindow window,
+            @NonNull ClientWindowFrames frames, boolean reportDraw,
+            @NonNull MergedConfiguration configuration, @NonNull InsetsState insetsState,
+            boolean forceLayout, boolean alwaysConsumeSystemBars, int displayId, int syncSeqId,
+            boolean dragResizing, @Nullable ActivityWindowInfo activityWindowInfo) {
+        super(window);
+        mFrames = new ClientWindowFrames(frames);
+        mConfiguration = new MergedConfiguration(configuration);
+        mInsetsState = new InsetsState(insetsState, true /* copySources */);
+        if (activityWindowInfo != null) {
+            mActivityWindowInfo = new ActivityWindowInfo(activityWindowInfo);
+        } else {
+            mActivityWindowInfo = null;
+        }
+        mReportDraw = reportDraw;
+        mForceLayout = forceLayout;
+        mAlwaysConsumeSystemBars = alwaysConsumeSystemBars;
+        mDisplayId = displayId;
+        mSyncSeqId = syncSeqId;
+        mDragResizing = dragResizing;
+    }
 
     @Override
     public void execute(@NonNull ClientTransactionHandler client, @NonNull IWindow window,
@@ -73,54 +102,6 @@
         Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
     }
 
-    // ObjectPoolItem implementation
-
-    private WindowStateResizeItem() {}
-
-    /** Obtains an instance initialized with provided params. */
-    public static WindowStateResizeItem obtain(@NonNull IWindow window,
-            @NonNull ClientWindowFrames frames, boolean reportDraw,
-            @NonNull MergedConfiguration configuration, @NonNull InsetsState insetsState,
-            boolean forceLayout, boolean alwaysConsumeSystemBars, int displayId, int syncSeqId,
-            boolean dragResizing, @Nullable ActivityWindowInfo activityWindowInfo) {
-        WindowStateResizeItem instance =
-                ObjectPool.obtain(WindowStateResizeItem.class);
-        if (instance == null) {
-            instance = new WindowStateResizeItem();
-        }
-        instance.setWindow(window);
-        instance.mFrames = new ClientWindowFrames(frames);
-        instance.mReportDraw = reportDraw;
-        instance.mConfiguration = new MergedConfiguration(configuration);
-        instance.mInsetsState = new InsetsState(insetsState, true /* copySources */);
-        instance.mForceLayout = forceLayout;
-        instance.mAlwaysConsumeSystemBars = alwaysConsumeSystemBars;
-        instance.mDisplayId = displayId;
-        instance.mSyncSeqId = syncSeqId;
-        instance.mDragResizing = dragResizing;
-        instance.mActivityWindowInfo = activityWindowInfo != null
-                ? new ActivityWindowInfo(activityWindowInfo)
-                : null;
-
-        return instance;
-    }
-
-    @Override
-    public void recycle() {
-        super.recycle();
-        mFrames = null;
-        mReportDraw = false;
-        mConfiguration = null;
-        mInsetsState = null;
-        mForceLayout = false;
-        mAlwaysConsumeSystemBars = false;
-        mDisplayId = INVALID_DISPLAY;
-        mSyncSeqId = -1;
-        mDragResizing = false;
-        mActivityWindowInfo = null;
-        ObjectPool.recycle(this);
-    }
-
     // Parcelable implementation
 
     /** Writes to Parcel. */
@@ -142,10 +123,10 @@
     /** Reads from Parcel. */
     private WindowStateResizeItem(@NonNull Parcel in) {
         super(in);
-        mFrames = in.readTypedObject(ClientWindowFrames.CREATOR);
+        mFrames = requireNonNull(in.readTypedObject(ClientWindowFrames.CREATOR));
         mReportDraw = in.readBoolean();
-        mConfiguration = in.readTypedObject(MergedConfiguration.CREATOR);
-        mInsetsState = in.readTypedObject(InsetsState.CREATOR);
+        mConfiguration = requireNonNull(in.readTypedObject(MergedConfiguration.CREATOR));
+        mInsetsState = requireNonNull(in.readTypedObject(InsetsState.CREATOR));
         mForceLayout = in.readBoolean();
         mAlwaysConsumeSystemBars = in.readBoolean();
         mDisplayId = in.readInt();
diff --git a/core/java/android/app/servertransaction/WindowStateTransactionItem.java b/core/java/android/app/servertransaction/WindowStateTransactionItem.java
index d556363..d6628e7 100644
--- a/core/java/android/app/servertransaction/WindowStateTransactionItem.java
+++ b/core/java/android/app/servertransaction/WindowStateTransactionItem.java
@@ -46,9 +46,12 @@
     }
 
     /** Target window. */
-    private IWindow mWindow;
+    @NonNull
+    private final IWindow mWindow;
 
-    WindowStateTransactionItem() {}
+    public WindowStateTransactionItem(@NonNull IWindow window) {
+        mWindow = requireNonNull(window);
+    }
 
     @Override
     public final void execute(@NonNull ClientTransactionHandler client,
@@ -67,26 +70,18 @@
     public abstract void execute(@NonNull ClientTransactionHandler client,
             @NonNull IWindow window, @NonNull PendingTransactionActions pendingActions);
 
-    void setWindow(@NonNull IWindow window) {
-        mWindow = requireNonNull(window);
-    }
+    // Parcelable implementation
 
-    // To be overridden
-
-    WindowStateTransactionItem(@NonNull Parcel in) {
-        mWindow = IWindow.Stub.asInterface(in.readStrongBinder());
-    }
-
+    /** Writes to Parcel. */
     @CallSuper
     @Override
     public void writeToParcel(@NonNull Parcel dest, int flags) {
         dest.writeStrongBinder(mWindow.asBinder());
     }
 
-    @CallSuper
-    @Override
-    public void recycle() {
-        mWindow = null;
+    /** Reads from Parcel. */
+    WindowStateTransactionItem(@NonNull Parcel in) {
+        mWindow = requireNonNull(IWindow.Stub.asInterface(in.readStrongBinder()));
     }
 
     // Subclass must override and call super.equals to compare the mActivityToken.
diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java
index 1f19f81..933c336 100644
--- a/core/java/android/appwidget/AppWidgetHostView.java
+++ b/core/java/android/appwidget/AppWidgetHostView.java
@@ -132,7 +132,7 @@
      * Create a host view. Uses specified animations when pushing
      * {@link #updateAppWidget(RemoteViews)}.
      *
-     * @param animationIn Resource ID of in animation to use
+     * @param animationIn  Resource ID of in animation to use
      * @param animationOut Resource ID of out animation to use
      */
     @SuppressWarnings({"UnusedDeclaration"})
@@ -148,7 +148,7 @@
      * Pass the given handler to RemoteViews when updating this widget. Unless this
      * is done immediatly after construction, a call to {@link #updateAppWidget(RemoteViews)}
      * should be made.
-     * @param handler
+     *
      * @hide
      */
     public void setInteractionHandler(InteractionHandler handler) {
@@ -206,10 +206,10 @@
      * order for the AppWidgetHost to account for the automatic padding when computing the number
      * of cells to allocate to a particular widget.
      *
-     * @param context the current context
+     * @param context   the current context
      * @param component the component name of the widget
-     * @param padding Rect in which to place the output, if null, a new Rect will be allocated and
-     *                returned
+     * @param padding   Rect in which to place the output, if null, a new Rect will be allocated and
+     *                  returned
      * @return default padding for this widget, in pixels
      */
     public static Rect getDefaultPaddingForWidget(Context context, ComponentName component,
@@ -291,7 +291,7 @@
         }
         mDelayedRestoredInflationId = -1;
         mDelayedRestoredState = null;
-        try  {
+        try {
             super.dispatchRestoreInstanceState(state);
         } catch (Exception e) {
             Log.e(TAG, "failed to restoreInstanceState for widget id: " + mAppWidgetId + ", "
@@ -354,14 +354,14 @@
      * the framework will be accounted for automatically. This information gets embedded into the
      * AppWidget options and causes a callback to the AppWidgetProvider. In addition, the list of
      * sizes is explicitly set to an empty list.
-     * @see AppWidgetProvider#onAppWidgetOptionsChanged(Context, AppWidgetManager, int, Bundle)
      *
      * @param newOptions The bundle of options, in addition to the size information,
-     *          can be null.
-     * @param minWidth The minimum width in dips that the widget will be displayed at.
-     * @param minHeight The maximum height in dips that the widget will be displayed at.
-     * @param maxWidth The maximum width in dips that the widget will be displayed at.
-     * @param maxHeight The maximum height in dips that the widget will be displayed at.
+     *                   can be null.
+     * @param minWidth   The minimum width in dips that the widget will be displayed at.
+     * @param minHeight  The maximum height in dips that the widget will be displayed at.
+     * @param maxWidth   The maximum width in dips that the widget will be displayed at.
+     * @param maxHeight  The maximum height in dips that the widget will be displayed at.
+     * @see AppWidgetProvider#onAppWidgetOptionsChanged(Context, AppWidgetManager, int, Bundle)
      * @deprecated use {@link AppWidgetHostView#updateAppWidgetSize(Bundle, List)} instead.
      */
     @Deprecated
@@ -378,12 +378,14 @@
      * This method will update the option bundle with the list of sizes and the min/max bounds for
      * width and height.
      *
-     * @see AppWidgetProvider#onAppWidgetOptionsChanged(Context, AppWidgetManager, int, Bundle)
-     *
      * @param newOptions The bundle of options, in addition to the size information.
-     * @param sizes Sizes, in dips, the widget may be displayed at without calling the provider
-     *              again. Typically, this will be size of the widget in landscape and portrait.
-     *              On some foldables, this might include the size on the outer and inner screens.
+     * @param sizes      Sizes, in dips, the widget may be displayed at without calling the
+     *                   provider
+     *                   again. Typically, this will be size of the widget in landscape and
+     *                   portrait.
+     *                   On some foldables, this might include the size on the outer and inner
+     *                   screens.
+     * @see AppWidgetProvider#onAppWidgetOptionsChanged(Context, AppWidgetManager, int, Bundle)
      */
     public void updateAppWidgetSize(@NonNull Bundle newOptions, @NonNull List<SizeF> sizes) {
         AppWidgetManager widgetManager = AppWidgetManager.getInstance(mContext);
@@ -470,9 +472,9 @@
     /**
      * Specify some extra information for the widget provider. Causes a callback to the
      * AppWidgetProvider.
-     * @see AppWidgetProvider#onAppWidgetOptionsChanged(Context, AppWidgetManager, int, Bundle)
      *
      * @param options The bundle of options information.
+     * @see AppWidgetProvider#onAppWidgetOptionsChanged(Context, AppWidgetManager, int, Bundle)
      */
     public void updateAppWidgetOptions(Bundle options) {
         AppWidgetManager.getInstance(mContext).updateAppWidgetOptions(mAppWidgetId, options);
@@ -507,6 +509,7 @@
     /**
      * Sets whether the widget is being displayed on a light/white background and use an
      * alternate UI if available.
+     *
      * @see RemoteViews#setLightBackgroundLayoutId(int)
      */
     public void setOnLightBackground(boolean onLightBackground) {
@@ -620,7 +623,7 @@
         if (content == null) {
             if (mViewMode == VIEW_MODE_ERROR) {
                 // We've already done this -- nothing to do.
-                return ;
+                return;
             }
             if (exception != null) {
                 Log.w(TAG, "Error inflating RemoteViews", exception);
@@ -733,7 +736,7 @@
             if (adapter instanceof BaseAdapter) {
                 BaseAdapter baseAdapter = (BaseAdapter) adapter;
                 baseAdapter.notifyDataSetChanged();
-            }  else if (adapter == null && adapterView instanceof RemoteAdapterConnectionCallback) {
+            } else if (adapter == null && adapterView instanceof RemoteAdapterConnectionCallback) {
                 // If the adapter is null, it may mean that the RemoteViewsAapter has not yet
                 // connected to its associated service, and hence the adapter hasn't been set.
                 // In this case, we need to defer the notify call until it has been set.
@@ -745,6 +748,7 @@
     /**
      * Build a {@link Context} cloned into another package name, usually for the
      * purposes of reading remote resources.
+     *
      * @hide
      */
     protected Context getRemoteContextEnsuringCorrectCachedApkPath() {
@@ -760,7 +764,7 @@
             }
             return newContext;
         } catch (NameNotFoundException e) {
-            Log.e(TAG, "Package name " +  mInfo.providerInfo.packageName + " not found");
+            Log.e(TAG, "Package name " + mInfo.providerInfo.packageName + " not found");
             return mContext;
         } catch (NullPointerException e) {
             Log.e(TAG, "Error trying to create the remote context.", e);
@@ -774,7 +778,7 @@
      */
     protected void prepareView(View view) {
         // Take requested dimensions from child, but apply default gravity.
-        FrameLayout.LayoutParams requested = (FrameLayout.LayoutParams)view.getLayoutParams();
+        FrameLayout.LayoutParams requested = (FrameLayout.LayoutParams) view.getLayoutParams();
         if (requested == null) {
             requested = new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT,
                     LayoutParams.MATCH_PARENT);
@@ -839,7 +843,18 @@
         return defaultView;
     }
 
-    private void onDefaultViewClicked(View view) {
+    /**
+     * Handles interactions on the default view of the widget. By default does not use the
+     * {@link InteractionHandler} used by other interactions. However, this can be overridden
+     * in order to customize the click behavior.
+     *
+     * @hide
+     */
+    protected void onDefaultViewClicked(@NonNull View view) {
+        final AppWidgetManager manager = AppWidgetManager.getInstance(mContext);
+        if (manager != null) {
+            manager.noteAppWidgetTapped(mAppWidgetId);
+        }
         if (mInfo != null) {
             LauncherApps launcherApps = getContext().getSystemService(LauncherApps.class);
             List<LauncherActivityInfo> activities = launcherApps.getActivityList(
diff --git a/core/java/android/companion/virtual/IVirtualDevice.aidl b/core/java/android/companion/virtual/IVirtualDevice.aidl
index 0653839..a56bc02 100644
--- a/core/java/android/companion/virtual/IVirtualDevice.aidl
+++ b/core/java/android/companion/virtual/IVirtualDevice.aidl
@@ -112,6 +112,24 @@
     void removeActivityPolicyExemption(in ComponentName exemption);
 
     /**
+     * Specifies a policy for this virtual device on the given display.
+     */
+    @EnforcePermission("CREATE_VIRTUAL_DEVICE")
+    void setDevicePolicyForDisplay(int displayId, int policyType, int devicePolicy);
+
+    /**
+     * Adds an exemption to the default activity launch policy on the given display.
+     */
+    @EnforcePermission("CREATE_VIRTUAL_DEVICE")
+    void addActivityPolicyExemptionForDisplay(int displayId, in ComponentName exemption);
+
+    /**
+     * Removes an exemption to the default activity launch policy on the given display.
+     */
+    @EnforcePermission("CREATE_VIRTUAL_DEVICE")
+    void removeActivityPolicyExemptionForDisplay(int displayId, in ComponentName exemption);
+
+    /**
      * Notifies that an audio session being started.
      */
     @EnforcePermission("CREATE_VIRTUAL_DEVICE")
diff --git a/core/java/android/companion/virtual/VirtualDeviceInternal.java b/core/java/android/companion/virtual/VirtualDeviceInternal.java
index d3fcfc6..d8899b2 100644
--- a/core/java/android/companion/virtual/VirtualDeviceInternal.java
+++ b/core/java/android/companion/virtual/VirtualDeviceInternal.java
@@ -17,7 +17,11 @@
 package android.companion.virtual;
 
 import static android.companion.virtual.VirtualDeviceParams.DEVICE_POLICY_CUSTOM;
+import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_ACTIVITY;
 import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_AUDIO;
+import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_BLOCKED_ACTIVITY_BEHAVIOR;
+import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_CLIPBOARD;
+import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_RECENTS;
 
 import android.annotation.CallbackExecutor;
 import android.annotation.NonNull;
@@ -282,6 +286,16 @@
 
     void setDevicePolicy(@VirtualDeviceParams.DynamicPolicyType int policyType,
             @VirtualDeviceParams.DevicePolicy int devicePolicy) {
+        switch (policyType) {
+            case POLICY_TYPE_RECENTS:
+            case POLICY_TYPE_CLIPBOARD:
+            case POLICY_TYPE_ACTIVITY:
+            case POLICY_TYPE_BLOCKED_ACTIVITY_BEHAVIOR:
+                break;
+            default:
+                throw new IllegalArgumentException("Device policy " + policyType
+                        + " cannot be changed at runtime. ");
+        }
         try {
             mVirtualDevice.setDevicePolicy(policyType, devicePolicy);
         } catch (RemoteException e) {
@@ -305,6 +319,42 @@
         }
     }
 
+    void setDevicePolicyForDisplay(int displayId,
+            @VirtualDeviceParams.DynamicDisplayPolicyType int policyType,
+            @VirtualDeviceParams.DevicePolicy int devicePolicy) {
+        switch (policyType) {
+            case POLICY_TYPE_RECENTS:
+            case POLICY_TYPE_ACTIVITY:
+                break;
+            default:
+                throw new IllegalArgumentException("Device policy " + policyType
+                        + " cannot be changed for a specific display. ");
+        }
+
+        try {
+            mVirtualDevice.setDevicePolicyForDisplay(displayId, policyType, devicePolicy);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    void addActivityPolicyExemptionForDisplay(int displayId, @NonNull ComponentName componentName) {
+        try {
+            mVirtualDevice.addActivityPolicyExemptionForDisplay(displayId, componentName);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    void removeActivityPolicyExemptionForDisplay(int displayId,
+            @NonNull ComponentName componentName) {
+        try {
+            mVirtualDevice.removeActivityPolicyExemptionForDisplay(displayId, componentName);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
     @NonNull
     VirtualDpad createVirtualDpad(@NonNull VirtualDpadConfig config) {
         try {
diff --git a/core/java/android/companion/virtual/VirtualDeviceManager.java b/core/java/android/companion/virtual/VirtualDeviceManager.java
index 296ca33..42da7e9 100644
--- a/core/java/android/companion/virtual/VirtualDeviceManager.java
+++ b/core/java/android/companion/virtual/VirtualDeviceManager.java
@@ -739,6 +739,7 @@
          *
          * @param policyType the type of policy, i.e. which behavior to specify a policy for.
          * @param devicePolicy the value of the policy, i.e. how to interpret the device behavior.
+         * @throws IllegalArgumentException if the policy cannot be changed at runtime.
          *
          * @see VirtualDeviceParams#POLICY_TYPE_RECENTS
          * @see VirtualDeviceParams#POLICY_TYPE_ACTIVITY
@@ -797,6 +798,81 @@
         }
 
         /**
+         * Specifies a policy for this virtual device to be applied on the given virtual display.
+         * <p>
+         * Any policy specified for a particular display takes precedence over the policy specified
+         * for the device itself.
+         * </p>
+         *
+         * @param policyType the type of policy, i.e. which behavior to specify a policy for.
+         * @param devicePolicy the value of the policy, i.e. how to interpret the device behavior.
+         * @param displayId the ID of the display, for which to apply the policy.
+         * @throws IllegalArgumentException if the specified policy cannot be changed per
+         *   display or if the specified display does not belong to the virtual device.
+         *
+         * @see #setDevicePolicy(int, int)
+         * @see VirtualDeviceParams#POLICY_TYPE_RECENTS
+         * @see VirtualDeviceParams#POLICY_TYPE_ACTIVITY
+         */
+        @FlaggedApi(android.companion.virtualdevice.flags.Flags.FLAG_ACTIVITY_CONTROL_API)
+        @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
+        public void setDevicePolicy(
+                @VirtualDeviceParams.DynamicDisplayPolicyType int policyType,
+                @VirtualDeviceParams.DevicePolicy int devicePolicy,
+                int displayId) {
+            mVirtualDeviceInternal.setDevicePolicyForDisplay(displayId, policyType, devicePolicy);
+        }
+
+        /**
+         * Specifies a component name to be exempt from the given display's activity launch policy.
+         *
+         * <p>If the current {@link VirtualDeviceParams#POLICY_TYPE_ACTIVITY} allows activity
+         * launches by default, (i.e. it is {@link VirtualDeviceParams#DEVICE_POLICY_DEFAULT}),
+         * then the specified component will be blocked from launching.
+         * If the current {@link VirtualDeviceParams#POLICY_TYPE_ACTIVITY} blocks activity launches
+         * by default, (i.e. it is {@link VirtualDeviceParams#DEVICE_POLICY_CUSTOM}), then the
+         * specified component will be allowed to launch.</p>
+         *
+         * <p>Note that changing the activity launch policy will clear current set of exempt
+         * components.</p>
+         * <p>Any change to the exemptions will only be applied for new activity launches.</p>
+         *
+         * @see #removeActivityPolicyExemption
+         * @see #setDevicePolicy
+         */
+        @FlaggedApi(android.companion.virtualdevice.flags.Flags.FLAG_ACTIVITY_CONTROL_API)
+        @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
+        public void addActivityPolicyExemption(
+                @NonNull ComponentName componentName, int displayId) {
+            mVirtualDeviceInternal.addActivityPolicyExemptionForDisplay(
+                    displayId, Objects.requireNonNull(componentName));
+        }
+
+        /**
+         * Makes the specified component name adhere to the given display's activity launch policy.
+         *
+         * <p>If the current {@link VirtualDeviceParams#POLICY_TYPE_ACTIVITY} allows activity
+         * launches by default, (i.e. it is {@link VirtualDeviceParams#DEVICE_POLICY_DEFAULT}),
+         * then the specified component will be allowed to launch.
+         * If the current {@link VirtualDeviceParams#POLICY_TYPE_ACTIVITY} blocks activity launches
+         * by default, (i.e. it is {@link VirtualDeviceParams#DEVICE_POLICY_CUSTOM}), then the
+         * specified component will be blocked from launching.</p>
+         *
+         * <p>Note that changing the activity launch policy will clear current set of exempt
+         * components.</p>
+         *
+         * @see #addActivityPolicyExemption
+         * @see #setDevicePolicy
+         */
+        @FlaggedApi(android.companion.virtualdevice.flags.Flags.FLAG_ACTIVITY_CONTROL_API)
+        @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
+        public void removeActivityPolicyExemption(
+                @NonNull ComponentName componentName, int displayId) {
+            mVirtualDeviceInternal.removeActivityPolicyExemptionForDisplay(
+                    displayId, Objects.requireNonNull(componentName));
+        }
+
+        /**
          * Creates a virtual dpad.
          *
          * @param config the configurations of the virtual dpad.
diff --git a/core/java/android/companion/virtual/VirtualDeviceParams.java b/core/java/android/companion/virtual/VirtualDeviceParams.java
index f7f842f..c1fc51d 100644
--- a/core/java/android/companion/virtual/VirtualDeviceParams.java
+++ b/core/java/android/companion/virtual/VirtualDeviceParams.java
@@ -159,7 +159,8 @@
      * @hide
      */
     @IntDef(prefix = "POLICY_TYPE_", value = {POLICY_TYPE_SENSORS, POLICY_TYPE_AUDIO,
-            POLICY_TYPE_RECENTS, POLICY_TYPE_ACTIVITY, POLICY_TYPE_CAMERA})
+            POLICY_TYPE_RECENTS, POLICY_TYPE_ACTIVITY, POLICY_TYPE_CAMERA,
+            POLICY_TYPE_BLOCKED_ACTIVITY_BEHAVIOR})
     @Retention(RetentionPolicy.SOURCE)
     @Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE})
     public @interface PolicyType {}
@@ -177,6 +178,17 @@
     public @interface DynamicPolicyType {}
 
     /**
+     * Policy types that can be dynamically changed for a specific display.
+     *
+     * @see VirtualDeviceManager.VirtualDevice#setDevicePolicyForDisplay
+     * @hide
+     */
+    @IntDef(prefix = "POLICY_TYPE_", value = {POLICY_TYPE_RECENTS, POLICY_TYPE_ACTIVITY})
+    @Retention(RetentionPolicy.SOURCE)
+    @Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE})
+    public @interface DynamicDisplayPolicyType {}
+
+    /**
      * Tells the sensor framework how to handle sensor requests from contexts associated with this
      * virtual device, namely the sensors returned by
      * {@link android.hardware.SensorManager#getSensorList}:
@@ -229,6 +241,8 @@
      * @see VirtualDeviceManager.VirtualDevice#addActivityPolicyExemption
      * @see VirtualDeviceManager.VirtualDevice#removeActivityPolicyExemption
      */
+    // TODO(b/333443509): Update the documentation of custom policy and link to the new policy
+    // POLICY_TYPE_BLOCKED_ACTIVITY_BEHAVIOR
     @FlaggedApi(Flags.FLAG_DYNAMIC_POLICY)
     public static final int POLICY_TYPE_ACTIVITY = 3;
 
@@ -276,6 +290,7 @@
      *     experience on the virtual device.
      * </ul>
      */
+    // TODO(b/333443509): Link to POLICY_TYPE_ACTIVITY
     @FlaggedApi(android.companion.virtualdevice.flags.Flags.FLAG_ACTIVITY_CONTROL_API)
     public static final int POLICY_TYPE_BLOCKED_ACTIVITY_BEHAVIOR = 6;
 
diff --git a/core/java/android/companion/virtual/flags.aconfig b/core/java/android/companion/virtual/flags.aconfig
index b29b52d..91586b6 100644
--- a/core/java/android/companion/virtual/flags.aconfig
+++ b/core/java/android/companion/virtual/flags.aconfig
@@ -81,13 +81,6 @@
 }
 
 flag {
-  name: "stream_permissions"
-  namespace: "virtual_devices"
-  description: "Enable streaming permission dialogs to Virtual Devices"
-  bug: "291737919"
-}
-
-flag {
   name: "persistent_device_id_api"
   is_exported: true
   namespace: "virtual_devices"
@@ -96,13 +89,6 @@
 }
 
 flag {
-  name: "express_metrics"
-  namespace: "virtual_devices"
-  description: "Enable express metrics in VDM"
-  bug: "307297730"
-}
-
-flag {
   name: "interactive_screen_mirror"
   is_exported: true
   namespace: "virtual_devices"
@@ -119,17 +105,6 @@
 }
 
 flag {
-  name: "intercept_intents_before_applying_policy"
-  is_exported: true
-  namespace: "virtual_devices"
-  description: "Apply intent interception before applying activity policy"
-  bug: "333444131"
-  metadata {
-    purpose: PURPOSE_BUGFIX
-  }
-}
-
-flag {
   name: "impulse_velocity_strategy_for_touch_navigation"
   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 55278f6..c3c3f0e 100644
--- a/core/java/android/companion/virtual/flags/flags.aconfig
+++ b/core/java/android/companion/virtual/flags/flags.aconfig
@@ -47,14 +47,6 @@
 
 flag {
      namespace: "virtual_devices"
-     name: "metrics_collection"
-     description: "Enable collection of VDM-related metrics"
-     bug: "324842215"
-     is_fixed_read_only: true
-}
-
-flag {
-     namespace: "virtual_devices"
      name: "activity_control_api"
      description: "Enable APIs for fine grained activity policy, fallback and callbacks"
      bug: "333443509"
@@ -84,24 +76,10 @@
 }
 
 flag {
-    namespace: "virtual_devices"
-    name: "virtual_display_multi_window_mode_support"
-    description: "Add support for WINDOWING_MODE_MULTI_WINDOW to virtual displays by default"
-    is_fixed_read_only: true
-    bug: "341151395"
-    metadata {
-        purpose: PURPOSE_BUGFIX
-    }
-}
-
-flag {
-    namespace: "virtual_devices"
-    name: "intent_interception_action_matching_fix"
-    description: "Do not match intents without actions if the filter has actions"
-    bug: "343805037"
-    metadata {
-        purpose: PURPOSE_BUGFIX
-    }
+     namespace: "virtual_devices"
+     name: "enforce_remote_device_opt_out_on_all_virtual_displays"
+     description: "Respect canDisplayOnRemoteDevices on all virtual displays"
+     bug: "338973239"
 }
 
 flag {
@@ -117,3 +95,11 @@
     description: "Enable high resolution scroll"
     bug: "335160780"
 }
+
+flag {
+  name: "camera_multiple_input_streams"
+  is_exported: true
+  namespace: "virtual_devices"
+  description: "Expose multiple surface for the virtual camera owner for different stream resolution"
+  bug: "341083465"
+}
diff --git a/core/java/android/content/BroadcastReceiver.java b/core/java/android/content/BroadcastReceiver.java
index d7195a7..964a8be 100644
--- a/core/java/android/content/BroadcastReceiver.java
+++ b/core/java/android/content/BroadcastReceiver.java
@@ -34,6 +34,8 @@
 import android.util.Log;
 import android.util.Slog;
 
+import com.android.internal.os.DebugStore;
+
 /**
  * Base class for code that receives and handles broadcast intents sent by
  * {@link android.content.Context#sendBroadcast(Intent)}.
@@ -55,6 +57,9 @@
     private PendingResult mPendingResult;
     private boolean mDebugUnregister;
 
+    private static final boolean DEBUG_STORE_ENABLED =
+            com.android.internal.os.Flags.debugStoreEnabled();
+
     /**
      * State for a result that is pending for a broadcast receiver.  Returned
      * by {@link BroadcastReceiver#goAsync() goAsync()}
@@ -255,6 +260,9 @@
                         "PendingResult#finish#ClassName:" + mReceiverClassName,
                         1);
             }
+            if (DEBUG_STORE_ENABLED) {
+                DebugStore.recordFinish(mReceiverClassName);
+            }
 
             if (mType == TYPE_COMPONENT) {
                 final IActivityManager mgr = ActivityManager.getService();
@@ -433,7 +441,9 @@
     public final PendingResult goAsync() {
         PendingResult res = mPendingResult;
         mPendingResult = null;
-
+        if (DEBUG_STORE_ENABLED) {
+            DebugStore.recordGoAsync(getClass().getName());
+        }
         if (res != null && Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
             res.mReceiverClassName = getClass().getName();
             Trace.traceCounter(Trace.TRACE_TAG_ACTIVITY_MANAGER,
diff --git a/core/java/android/content/IntentSender.java b/core/java/android/content/IntentSender.java
index 32d1964..ca6d86a 100644
--- a/core/java/android/content/IntentSender.java
+++ b/core/java/android/content/IntentSender.java
@@ -16,6 +16,8 @@
 
 package android.content;
 
+import static android.os.Build.VERSION_CODES.VANILLA_ICE_CREAM;
+
 import android.annotation.FlaggedApi;
 import android.annotation.Nullable;
 import android.app.ActivityManager;
@@ -23,6 +25,9 @@
 import android.app.ActivityOptions;
 import android.app.ActivityThread;
 import android.app.IApplicationThread;
+import android.app.compat.CompatChanges;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledAfter;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Bundle;
 import android.os.Handler;
@@ -65,6 +70,11 @@
  * {@link android.app.PendingIntent#getIntentSender() PendingIntent.getIntentSender()}.
  */
 public class IntentSender implements Parcelable {
+    /** If enabled consider the deprecated @hide method as removed. */
+    @ChangeId
+    @EnabledAfter(targetSdkVersion = VANILLA_ICE_CREAM)
+    private static final long REMOVE_HIDDEN_SEND_INTENT_METHOD = 356174596;
+
     private static final Bundle SEND_INTENT_DEFAULT_OPTIONS =
             ActivityOptions.makeBasic().setPendingIntentBackgroundActivityStartMode(
                     ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_COMPAT).toBundle();
@@ -220,6 +230,44 @@
      * original Intent.  Use {@code null} to not modify the original Intent.
      * @param onFinished The object to call back on when the send has
      * completed, or {@code null} for no callback.
+     * @param handler Handler identifying the thread on which the callback
+     * should happen.  If {@code null}, the callback will happen from the thread
+     * pool of the process.
+     * @param options Additional options the caller would like to provide to modify the sending
+     * behavior.  Typically built from using {@link ActivityOptions} to apply to an activity start.
+     *
+     * @throws SendIntentException Throws CanceledIntentException if the IntentSender
+     * is no longer allowing more intents to be sent through it.
+     *
+     * @deprecated use {@link #sendIntent(Context, int, Intent, String, Bundle, Executor,
+     *         OnFinished)}
+     *
+     * @hide
+     */
+    @Deprecated public void sendIntent(Context context, int code, Intent intent,
+            OnFinished onFinished, Handler handler, String requiredPermission,
+            @Nullable Bundle options)
+            throws SendIntentException {
+        if (CompatChanges.isChangeEnabled(REMOVE_HIDDEN_SEND_INTENT_METHOD)) {
+            throw new NoSuchMethodError("This overload of sendIntent was removed.");
+        }
+        sendIntent(context, code, intent, requiredPermission, options,
+                handler == null ? null : handler::post, onFinished);
+    }
+
+    /**
+     * Perform the operation associated with this IntentSender, allowing the
+     * caller to specify information about the Intent to use and be notified
+     * when the send has completed.
+     *
+     * @param context The Context of the caller.  This may be {@code null} if
+     * <var>intent</var> is also {@code null}.
+     * @param code Result code to supply back to the IntentSender's target.
+     * @param intent Additional Intent data.  See {@link Intent#fillIn
+     * Intent.fillIn()} for information on how this is applied to the
+     * original Intent.  Use {@code null} to not modify the original Intent.
+     * @param onFinished The object to call back on when the send has
+     * completed, or {@code null} for no callback.
      * @param executor Executor identifying the thread on which the callback
      * should happen.  If {@code null}, the callback will happen from the thread
      * pool of the process.
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index 6952a09..481e6b5 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -617,7 +617,7 @@
      */
     public static final int FLAG_ENABLE_VR_MODE = 0x8000;
     /**
-     * Bit in {@link #flags} indicating if the activity can be displayed on a remote device.
+     * Bit in {@link #flags} indicating if the activity can be displayed on a virtual display.
      * Corresponds to {@link android.R.attr#canDisplayOnRemoteDevices}
      * @hide
      */
diff --git a/core/java/android/content/pm/multiuser.aconfig b/core/java/android/content/pm/multiuser.aconfig
index 5a39702..77f2323 100644
--- a/core/java/android/content/pm/multiuser.aconfig
+++ b/core/java/android/content/pm/multiuser.aconfig
@@ -171,6 +171,16 @@
 }
 
 flag {
+    name: "fix_disabling_of_mu_toggle_when_restriction_applied"
+    namespace: "multiuser"
+    description: "When no_user_switch is set but no EnforcedAdmin is present, the toggle has to be disabled"
+    bug: "356387759"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+  }
+}
+
+flag {
     name: "cache_quiet_mode_state"
     namespace: "multiuser"
     description: "Optimise quiet mode state retrieval"
@@ -190,6 +200,18 @@
   }
 }
 
+flag {
+    name: "cache_user_serial_number_read_only"
+    namespace: "multiuser"
+    description: "Optimise user serial number retrieval. Read only flag, so that it can be used before the flags are initialized."
+    bug: "353134536"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+  }
+  is_fixed_read_only: true
+}
+
+
 # This flag guards the private space feature and all its implementations excluding the APIs. APIs are guarded by android.os.Flags.allow_private_profile.
 flag {
     name: "enable_private_space_features"
@@ -337,3 +359,23 @@
     purpose: PURPOSE_BUGFIX
   }
 }
+
+flag {
+  name: "private_space_search_illustration_config"
+  namespace: "profile_experiences"
+  description: "Check config to show/hide the private space search illustration and search tile content in Hide Private Space settings page"
+  bug: "346612477"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
+
+flag {
+  name: "show_different_creation_error_for_unsupported_devices"
+  namespace: "profile_experiences"
+  description: "On private space create error due to child account added/fully managed user show message with link to the Help Center to find out more."
+  bug: "340130375"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
diff --git a/core/java/android/database/sqlite/SQLiteConnection.java b/core/java/android/database/sqlite/SQLiteConnection.java
index e0dc568..914aa51 100644
--- a/core/java/android/database/sqlite/SQLiteConnection.java
+++ b/core/java/android/database/sqlite/SQLiteConnection.java
@@ -34,17 +34,22 @@
 import android.util.LruCache;
 import android.util.Pair;
 import android.util.Printer;
+import com.android.internal.util.RingBuffer;
 import dalvik.system.BlockGuard;
 import dalvik.system.CloseGuard;
+
 import java.io.File;
 import java.io.IOException;
 import java.lang.ref.Reference;
 import java.nio.file.FileSystems;
 import java.nio.file.Files;
 import java.nio.file.Path;
-import java.text.SimpleDateFormat;
+import java.time.Instant;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
 import java.util.ArrayList;
 import java.util.Date;
+import java.util.Locale;
 import java.util.Map;
 import java.util.function.BinaryOperator;
 import java.util.function.UnaryOperator;
@@ -185,7 +190,7 @@
             SQLiteDatabaseConfiguration configuration,
             int connectionId, boolean primaryConnection) {
         mPool = pool;
-        mRecentOperations = new OperationLog(mPool);
+        mRecentOperations = new OperationLog();
         mConfiguration = new SQLiteDatabaseConfiguration(configuration);
         mConnectionId = connectionId;
         mIsPrimaryConnection = primaryConnection;
@@ -305,6 +310,16 @@
         }
     }
 
+    /** Record the start of a transaction for logging and debugging. */
+    void recordBeginTransaction(String mode) {
+        mRecentOperations.beginTransaction(mode);
+    }
+
+    /** Record the end of a transaction for logging and debugging. */
+    void recordEndTransaction(boolean successful) {
+        mRecentOperations.endTransaction(successful);
+    }
+
     private void setPageSize() {
         if (!mConfiguration.isInMemoryDb() && !mIsReadOnlyConnection) {
             final long newValue = SQLiteGlobal.getDefaultPageSize();
@@ -1335,6 +1350,7 @@
         }
         printer.println("  isPrimaryConnection: " + mIsPrimaryConnection);
         printer.println("  onlyAllowReadOnlyOperations: " + mOnlyAllowReadOnlyOperations);
+        printer.println("  totalLongOperations: " + mRecentOperations.getTotalLongOperations());
 
         mRecentOperations.dump(printer);
 
@@ -1593,51 +1609,84 @@
         }
     }
 
-    private static final class OperationLog {
+    /**
+     * This class implements a leaky bucket strategy to rate-limit operations.  A client
+     * accumulates one credit every <n> milliseconds; a credit allows the client execute an
+     * operation (which then deducts the credit).  Credits accumulate up to a maximum amount after
+     * which they no longer accumulate.  The strategy allows a client to execute an operation
+     * every <n> milliseconds, or to execute a burst, after a period of no operations.
+     */
+    private static class RateLimiter {
+        // When the bucket was created, in ms.
+        private final long mCreationUptimeMs;
+        // The time required to accumulate a single credit.
+        private final long mMsPerCredit;
+        // The maximum number of credits the process can accumulate.
+        private final int mMaxCredits;
+        // Total credits consumed so far.
+        private long mSpent = 0;
+
+        RateLimiter(long msPerCredit, int maxCredits) {
+            mMsPerCredit = msPerCredit;
+            mMaxCredits = maxCredits;
+            mCreationUptimeMs = SystemClock.uptimeMillis() - (mMsPerCredit * mMaxCredits);
+        }
+
+        /** Return true if there is a credit available (and consume that credit). */
+        boolean tryAcquire() {
+            final long now = SystemClock.uptimeMillis();
+            long credits = (now - mCreationUptimeMs) / mMsPerCredit;
+
+            long available = credits - mSpent;
+            if (available > mMaxCredits) {
+                mSpent += available - mMaxCredits;
+                available = credits - mSpent;
+            }
+            if (available > 0) {
+                mSpent++;
+                return true;
+            } else {
+                return false;
+            }
+        }
+    }
+
+    private final class OperationLog {
         private static final int MAX_RECENT_OPERATIONS = 20;
         private static final int COOKIE_GENERATION_SHIFT = 8;
         private static final int COOKIE_INDEX_MASK = 0xff;
 
+        // Operations over 2s are long.  Save the last ten.
+        private static final long LONG_OPERATION_THRESHOLD_MS = 2_000;
+        private static final int MAX_LONG_OPERATIONS = 10;
+
         private final Operation[] mOperations = new Operation[MAX_RECENT_OPERATIONS];
-        private int mIndex;
-        private int mGeneration;
-        private final SQLiteConnectionPool mPool;
+        private int mIndex = -1;
+        private int mGeneration = 0;
+        private final Operation mTransaction = new Operation();
         private long mResultLong = Long.MIN_VALUE;
         private String mResultString;
 
-        OperationLog(SQLiteConnectionPool pool) {
-            mPool = pool;
-        }
+        private final RingBuffer<Operation> mLongOperations =
+                new RingBuffer<>(()->{return new Operation();},
+                        (n) ->{return new Operation[n];},
+                        MAX_LONG_OPERATIONS);
+        private int mTotalLongOperations = 0;
+
+        // Limit log messages to one every 5 minutes, except that a burst may be 10 messages long.
+        private final RateLimiter mLongLimiter = new RateLimiter(300_000, 10);
 
         public int beginOperation(String kind, String sql, Object[] bindArgs) {
             mResultLong = Long.MIN_VALUE;
             mResultString = null;
 
             synchronized (mOperations) {
-                final int index = (mIndex + 1) % MAX_RECENT_OPERATIONS;
-                Operation operation = mOperations[index];
-                if (operation == null) {
-                    operation = new Operation();
-                    mOperations[index] = operation;
-                } else {
-                    operation.mFinished = false;
-                    operation.mException = null;
-                    if (operation.mBindArgs != null) {
-                        operation.mBindArgs.clear();
-                    }
-                }
-                operation.mStartWallTime = System.currentTimeMillis();
-                operation.mStartTime = SystemClock.uptimeMillis();
+                Operation operation = newOperationLocked();
                 operation.mKind = kind;
                 operation.mSql = sql;
-                operation.mPath = mPool.getPath();
-                operation.mResultLong = Long.MIN_VALUE;
-                operation.mResultString = null;
                 if (bindArgs != null) {
                     if (operation.mBindArgs == null) {
                         operation.mBindArgs = new ArrayList<Object>();
-                    } else {
-                        operation.mBindArgs.clear();
                     }
                     for (int i = 0; i < bindArgs.length; i++) {
                         final Object arg = bindArgs[i];
@@ -1649,16 +1698,45 @@
                         }
                     }
                 }
-                operation.mCookie = newOperationCookieLocked(index);
-                if (Trace.isTagEnabled(Trace.TRACE_TAG_DATABASE)) {
+                operation.mTraced = Trace.isTagEnabled(Trace.TRACE_TAG_DATABASE);
+                if (operation.mTraced) {
                     Trace.asyncTraceBegin(Trace.TRACE_TAG_DATABASE, operation.getTraceMethodName(),
                             operation.mCookie);
                 }
-                mIndex = index;
                 return operation.mCookie;
             }
         }
 
+        public void beginTransaction(String kind) {
+            synchronized (mOperations) {
+                Operation operation = newOperationLocked();
+                operation.mKind = kind;
+                mTransaction.copyFrom(operation);
+
+                if (operation.mTraced) {
+                    Trace.asyncTraceBegin(Trace.TRACE_TAG_DATABASE, operation.getTraceMethodName(),
+                            operation.mCookie);
+                }
+            }
+        }
+
+        /**
+         * Fetch a new operation from the ring buffer.  The operation is properly initialized.
+         * This advances mIndex to point to the next element.
+         */
+        private Operation newOperationLocked() {
+            final int index = (mIndex + 1) % MAX_RECENT_OPERATIONS;
+            Operation operation = mOperations[index];
+            if (operation == null) {
+                mOperations[index] = new Operation();
+                operation = mOperations[index];
+            }
+            operation.start();
+            operation.mCookie = newOperationCookieLocked(index);
+            mIndex = index;
+            return operation;
+        }
+
         public void failOperation(int cookie, Exception ex) {
             synchronized (mOperations) {
                 final Operation operation = getOperationLocked(cookie);
@@ -1682,6 +1760,20 @@
             }
         }
 
+        public boolean endTransaction(boolean success) {
+            synchronized (mOperations) {
+                mTransaction.mResultLong = success ? 1 : 0;
+                final long execTime = finishOperationLocked(mTransaction);
+                final Operation operation = getOperationLocked(mTransaction.mCookie);
+                if (operation != null) {
+                    operation.copyFrom(mTransaction);
+                }
+                mTransaction.setEmpty();
+                return NoPreloadHolder.DEBUG_LOG_SLOW_QUERIES
+                        && SQLiteDebug.shouldLogSlowQuery(execTime);
+            }
+        }
+
         public void logOperation(int cookie, String detail) {
             synchronized (mOperations) {
                 logOperationLocked(cookie, detail);
@@ -1699,13 +1791,11 @@
         private boolean endOperationDeferLogLocked(int cookie) {
             final Operation operation = getOperationLocked(cookie);
             if (operation != null) {
-                if (Trace.isTagEnabled(Trace.TRACE_TAG_DATABASE)) {
+                if (operation.mTraced) {
                     Trace.asyncTraceEnd(Trace.TRACE_TAG_DATABASE, operation.getTraceMethodName(),
                             operation.mCookie);
                 }
-                operation.mEndTime = SystemClock.uptimeMillis();
-                operation.mFinished = true;
-                final long execTime = operation.mEndTime - operation.mStartTime;
+                final long execTime = finishOperationLocked(operation);
                 mPool.onStatementExecuted(execTime);
                 return NoPreloadHolder.DEBUG_LOG_SLOW_QUERIES && SQLiteDebug.shouldLogSlowQuery(
                         execTime);
@@ -1730,10 +1820,25 @@
             return generation << COOKIE_GENERATION_SHIFT | index;
         }
 
+        /** Close out the operation and return the elapsed time. */
+        private long finishOperationLocked(Operation operation) {
+            operation.mEndTime = SystemClock.uptimeMillis();
+            operation.mFinished = true;
+            final long elapsed = operation.mEndTime - operation.mStartTime;
+            if (elapsed > LONG_OPERATION_THRESHOLD_MS) {
+                mLongOperations.getNextSlot().copyFrom(operation);
+                mTotalLongOperations++;
+                if (mLongLimiter.tryAcquire()) {
+                    Log.i(TAG, "Long db operation: " + mConfiguration.label);
+                }
+            }
+            return elapsed;
+        }
+
         private Operation getOperationLocked(int cookie) {
             final int index = cookie & COOKIE_INDEX_MASK;
             final Operation operation = mOperations[index];
-            return operation.mCookie == cookie ? operation : null;
+            return (operation != null && operation.mCookie == cookie) ? operation : null;
         }
 
         public String describeCurrentOperation() {
@@ -1748,48 +1853,87 @@
             }
         }
 
-        public void dump(Printer printer) {
+        /**
+         * Dump an Operation if it is not in the recent operations list.  Return 1 if the
+         * operation was dumped and 0 if not.
+         */
+        private int dumpIfNotRecentLocked(Printer pw, Operation op, int counter) {
+            if (op == null || op.isEmpty() || getOperationLocked(op.mCookie) != null) {
+                return 0;
+            }
+            pw.println(op.describe(counter));
+            return 1;
+        }
+
+        private void dumpRecentLocked(Printer printer) {
             synchronized (mOperations) {
                 printer.println("  Most recently executed operations:");
                 int index = mIndex;
-                Operation operation = mOperations[index];
-                if (operation != null) {
-                    // Note: SimpleDateFormat is not thread-safe, cannot be compile-time created,
-                    // and is relatively expensive to create during preloading. This method is only
-                    // used when dumping a connection, which is a rare (mainly error) case.
-                    SimpleDateFormat opDF = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
-                    int n = 0;
-                    do {
-                        StringBuilder msg = new StringBuilder();
-                        msg.append("    ").append(n).append(": [");
-                        String formattedStartTime = opDF.format(new Date(operation.mStartWallTime));
-                        msg.append(formattedStartTime);
-                        msg.append("] ");
-                        operation.describe(msg, false); // Never dump bingargs in a bugreport
-                        printer.println(msg.toString());
-
-                        if (index > 0) {
-                            index -= 1;
-                        } else {
-                            index = MAX_RECENT_OPERATIONS - 1;
-                        }
-                        n += 1;
-                        operation = mOperations[index];
-                    } while (operation != null && n < MAX_RECENT_OPERATIONS);
-                } else {
+                if (index == 0) {
                     printer.println("    <none>");
+                    return;
                 }
+
+                // Operations are dumped in order of most recent first.
+                int counter = 0;
+                int n = 0;
+                Operation operation = mOperations[index];
+                do {
+                    printer.println(operation.describe(counter));
+
+                    if (index > 0) {
+                        index -= 1;
+                    } else {
+                        index = MAX_RECENT_OPERATIONS - 1;
+                    }
+                    n++;
+                    counter++;
+                    operation = mOperations[index];
+                } while (operation != null && n < MAX_RECENT_OPERATIONS);
+                counter += dumpIfNotRecentLocked(printer, mTransaction, counter);
+            }
+        }
+
+        private void dumpLongLocked(Printer printer) {
+            printer.println("  Operations exceeding " + LONG_OPERATION_THRESHOLD_MS + "ms:");
+            if (mLongOperations.isEmpty()) {
+                printer.println("    <none>");
+                return;
+            }
+            Operation[] longOps = mLongOperations.toArray();
+            for (int i = 0; i < longOps.length; i++) {
+                if (longOps[i] != null) {
+                    printer.println(longOps[i].describe(i));
+                }
+            }
+        }
+
+        public long getTotalLongOperations() {
+            return mTotalLongOperations;
+        }
+
+        public void dump(Printer printer) {
+            synchronized (mOperations) {
+                dumpRecentLocked(printer);
+                dumpLongLocked(printer);
             }
         }
     }
 
-    private static final class Operation {
+    private final class Operation {
         // Trim all SQL statements to 256 characters inside the trace marker.
         // This limit gives plenty of context while leaving space for other
         // entries in the trace buffer (and ensures atrace doesn't truncate the
         // marker for us, potentially losing metadata in the process).
         private static final int MAX_TRACE_METHOD_NAME_LEN = 256;
 
+        // The reserved start time that indicates the Operation is empty.
+        private static final long EMPTY_OPERATION = -1;
+
+        // The formatter for the timestamp.
+        private static final DateTimeFormatter sDateTime =
+                DateTimeFormatter.ofPattern("MM-dd HH:mm:ss.SSS", Locale.US);
+
         public long mStartWallTime; // in System.currentTimeMillis()
         public long mStartTime; // in SystemClock.uptimeMillis();
         public long mEndTime; // in SystemClock.uptimeMillis();
@@ -1799,16 +1943,61 @@
         public boolean mFinished;
         public Exception mException;
         public int mCookie;
-        public String mPath;
         public long mResultLong; // MIN_VALUE means "value not set".
         public String mResultString;
+        public boolean mTraced;
+
+        /** Reset the object to begin a new operation. */
+        void start() {
+            mStartWallTime = System.currentTimeMillis();
+            mStartTime = SystemClock.uptimeMillis();
+            mEndTime = Long.MIN_VALUE;
+            mKind = null;
+            mSql = null;
+            if (mBindArgs != null) mBindArgs.clear();
+            mFinished = false;
+            mException = null;
+            mCookie = -1;
+            mResultLong = Long.MIN_VALUE;
+            mResultString = null;
+            mTraced = false;
+        }
+
+        /**
+         * Initialize from the source object.  This is meant to clone the object for use in a
+         * transaction operation.  To that end, the local bind args are set to null.
+         */
+        void copyFrom(Operation r) {
+            mStartWallTime = r.mStartWallTime;
+            mStartTime = r.mStartTime;
+            mEndTime = r.mEndTime;
+            mKind = r.mKind;
+            mSql = r.mSql;
+            mBindArgs = null;
+            mFinished = r.mFinished;
+            mException = r.mException;
+            mCookie = r.mCookie;
+            mResultLong = r.mResultLong;
+            mResultString = r.mResultString;
+            mTraced = r.mTraced;
+        }
+
+        /** Mark the operation empty. */
+        void setEmpty() {
+            mStartWallTime = EMPTY_OPERATION;
+        }
+
+        /** Return true if the operation is empty. */
+        boolean isEmpty() {
+            return mStartWallTime == EMPTY_OPERATION;
+        }
 
         public void describe(StringBuilder msg, boolean allowDetailedLog) {
             msg.append(mKind);
             if (mFinished) {
                 msg.append(" took ").append(mEndTime - mStartTime).append("ms");
             } else {
-                msg.append(" started ").append(System.currentTimeMillis() - mStartWallTime)
+                msg.append(" started ").append(SystemClock.uptimeMillis() - mStartTime)
                         .append("ms ago");
             }
             msg.append(" - ").append(getStatus());
@@ -1837,7 +2026,7 @@
                 }
                 msg.append("]");
             }
-            msg.append(", path=").append(mPath);
+            msg.append(", path=").append(mPool.getPath());
             if (mException != null) {
                 msg.append(", exception=\"").append(mException.getMessage()).append("\"");
             }
@@ -1849,6 +2038,21 @@
             }
         }
 
+        /**
+         * Convert a wall-clock time in milliseconds to logcat format.
+         */
+        private String timeString(long millis) {
+            return sDateTime.withZone(ZoneId.systemDefault()).format(Instant.ofEpochMilli(millis));
+        }
+
+        public String describe(int n) {
+            final StringBuilder msg = new StringBuilder();
+            final String start = timeString(mStartWallTime);
+            msg.append("    ").append(n).append(": [").append(start).append("] ");
+            describe(msg, false); // Never dump bindargs in a bugreport
+            return msg.toString();
+        }
+
         private String getStatus() {
             if (!mFinished) {
                 return "running";
@@ -1862,7 +2066,6 @@
                 return methodName.substring(0, MAX_TRACE_METHOD_NAME_LEN);
             return methodName;
         }
-
     }
 
     /**
diff --git a/core/java/android/database/sqlite/SQLiteConnectionPool.java b/core/java/android/database/sqlite/SQLiteConnectionPool.java
index ad335b6..15d7d66 100644
--- a/core/java/android/database/sqlite/SQLiteConnectionPool.java
+++ b/core/java/android/database/sqlite/SQLiteConnectionPool.java
@@ -1175,7 +1175,7 @@
                     + ", isLegacyCompatibilityWalEnabled=" + isCompatibilityWalEnabled
                     + ", journalMode=" + TextUtils.emptyIfNull(mConfiguration.resolveJournalMode())
                     + ", syncMode=" + TextUtils.emptyIfNull(mConfiguration.resolveSyncMode()));
-            printer.println("  IsReadOnlyDatabase=" + mConfiguration.isReadOnlyDatabase());
+            printer.println("  IsReadOnlyDatabase: " + mConfiguration.isReadOnlyDatabase());
 
             if (isCompatibilityWalEnabled) {
                 printer.println("  Compatibility WAL enabled: wal_syncmode="
diff --git a/core/java/android/database/sqlite/SQLiteSession.java b/core/java/android/database/sqlite/SQLiteSession.java
index 7d9f02d..3b14d9d 100644
--- a/core/java/android/database/sqlite/SQLiteSession.java
+++ b/core/java/android/database/sqlite/SQLiteSession.java
@@ -312,6 +312,15 @@
                 cancellationSignal);
     }
 
+    private String modeString(int transactionMode) {
+        switch (transactionMode) {
+            case TRANSACTION_MODE_IMMEDIATE: return "TRANSACTION-IMMEDIATE";
+            case TRANSACTION_MODE_EXCLUSIVE: return "TRANSACTION-EXCLUSIVE";
+            case TRANSACTION_MODE_DEFERRED:  return "TRANSACTION-DEFERRED";
+            default: return "TRANSACTION";
+        }
+    }
+
     private void beginTransactionUnchecked(int transactionMode,
             SQLiteTransactionListener transactionListener, int connectionFlags,
             CancellationSignal cancellationSignal) {
@@ -321,6 +330,7 @@
 
         if (mTransactionStack == null) {
             acquireConnection(null, connectionFlags, cancellationSignal); // might throw
+            mConnection.recordBeginTransaction(modeString(transactionMode));
         }
         try {
             // Set up the transaction such that we can back out safely
@@ -465,6 +475,7 @@
                     mConnection.execute("ROLLBACK;", null, cancellationSignal); // might throw
                 }
             } finally {
+                mConnection.recordEndTransaction(successful);
                 releaseConnection(); // might throw
             }
         }
diff --git a/core/java/android/hardware/SensorEvent.java b/core/java/android/hardware/SensorEvent.java
index 6113932..e8d7e1e 100644
--- a/core/java/android/hardware/SensorEvent.java
+++ b/core/java/android/hardware/SensorEvent.java
@@ -600,7 +600,7 @@
      *
      * <p>
      * This sensor must be able to detect and report an on-body to off-body
-     * transition within 1 second of the device being removed from the body,
+     * transition within 3 seconds of the device being removed from the body,
      * and must be able to detect and report an off-body to on-body transition
      * within 5 seconds of the device being put back onto the body.
      * </p>
diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java
index 42f5fc8..9007b62 100644
--- a/core/java/android/hardware/biometrics/BiometricPrompt.java
+++ b/core/java/android/hardware/biometrics/BiometricPrompt.java
@@ -240,18 +240,19 @@
          *
          * @param logoDescription The logo description text that will be shown on the prompt.
          * @return This builder.
-         * @throws IllegalArgumentException If logo description is null or exceeds certain character
-         *                                  limit.
+         * @throws IllegalArgumentException If logo description is null.
          */
         @FlaggedApi(FLAG_CUSTOM_BIOMETRIC_PROMPT)
         @RequiresPermission(SET_BIOMETRIC_DIALOG_ADVANCED)
         @NonNull
         public BiometricPrompt.Builder setLogoDescription(@NonNull String logoDescription) {
-            if (logoDescription == null
-                    || logoDescription.length() > MAX_LOGO_DESCRIPTION_CHARACTER_NUMBER) {
-                throw new IllegalArgumentException(
-                        "Logo description passed in can not be null or exceed "
-                                + MAX_LOGO_DESCRIPTION_CHARACTER_NUMBER + " character number.");
+            if (logoDescription == null || logoDescription.isEmpty()) {
+                throw new IllegalArgumentException("Logo description passed in can not be null");
+            }
+            if (logoDescription.length() > MAX_LOGO_DESCRIPTION_CHARACTER_NUMBER) {
+                Log.w(TAG,
+                        "Logo description passed in exceeds" + MAX_LOGO_DESCRIPTION_CHARACTER_NUMBER
+                                + " character number and may be truncated.");
             }
             mPromptInfo.setLogoDescription(logoDescription);
             return this;
diff --git a/core/java/android/hardware/devicestate/feature/flags.aconfig b/core/java/android/hardware/devicestate/feature/flags.aconfig
index a09c84d..98ba9192 100644
--- a/core/java/android/hardware/devicestate/feature/flags.aconfig
+++ b/core/java/android/hardware/devicestate/feature/flags.aconfig
@@ -17,4 +17,16 @@
     description: "Client migration to updated DeviceStateManager API's"
     bug: "336640888"
     is_fixed_read_only: true
+}
+
+flag {
+    name: "device_state_requester_cancel_state"
+    is_exported: true
+    namespace: "windowing_sdk"
+    description: "Removes foreground requirement if process attempting to cancel a state request is the requester"
+    bug: "354772125"
+    is_fixed_read_only: true
+    metadata {
+      purpose: PURPOSE_BUGFIX
+    }
 }
\ No newline at end of file
diff --git a/core/java/android/hardware/display/BrightnessInfo.java b/core/java/android/hardware/display/BrightnessInfo.java
index c091062..109b0a8 100644
--- a/core/java/android/hardware/display/BrightnessInfo.java
+++ b/core/java/android/hardware/display/BrightnessInfo.java
@@ -60,7 +60,8 @@
     @IntDef(prefix = {"BRIGHTNESS_MAX_REASON_"}, value = {
             BRIGHTNESS_MAX_REASON_NONE,
             BRIGHTNESS_MAX_REASON_THERMAL,
-            BRIGHTNESS_MAX_REASON_POWER_IC
+            BRIGHTNESS_MAX_REASON_POWER_IC,
+            BRIGHTNESS_MAX_REASON_WEAR_BEDTIME_MODE
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface BrightnessMaxReason {}
diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java
index 811a834..7353dde 100644
--- a/core/java/android/hardware/display/DisplayManagerInternal.java
+++ b/core/java/android/hardware/display/DisplayManagerInternal.java
@@ -37,6 +37,7 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.List;
+import java.util.Objects;
 import java.util.Set;
 
 /**
@@ -470,6 +471,9 @@
         // Set to PowerManager.BRIGHTNESS_INVALID if there's no override.
         public float screenBrightnessOverride;
 
+        // Tag used to identify the app window requesting the brightness override.
+        public CharSequence screenBrightnessOverrideTag;
+
         // An override of the screen auto-brightness adjustment factor in the range -1 (dimmer) to
         // 1 (brighter). Set to Float.NaN if there's no override.
         public float screenAutoBrightnessAdjustmentOverride;
@@ -524,6 +528,7 @@
             policy = other.policy;
             useProximitySensor = other.useProximitySensor;
             screenBrightnessOverride = other.screenBrightnessOverride;
+            screenBrightnessOverrideTag = other.screenBrightnessOverrideTag;
             screenAutoBrightnessAdjustmentOverride = other.screenAutoBrightnessAdjustmentOverride;
             screenLowPowerBrightnessFactor = other.screenLowPowerBrightnessFactor;
             blockScreenOn = other.blockScreenOn;
@@ -544,8 +549,9 @@
             return other != null
                     && policy == other.policy
                     && useProximitySensor == other.useProximitySensor
-                    && floatEquals(screenBrightnessOverride,
-                            other.screenBrightnessOverride)
+                    && floatEquals(screenBrightnessOverride, other.screenBrightnessOverride)
+                    && Objects.equals(screenBrightnessOverrideTag,
+                            other.screenBrightnessOverrideTag)
                     && floatEquals(screenAutoBrightnessAdjustmentOverride,
                             other.screenAutoBrightnessAdjustmentOverride)
                     && screenLowPowerBrightnessFactor
diff --git a/core/java/android/hardware/input/InputSettings.java b/core/java/android/hardware/input/InputSettings.java
index bec1c9e..d85e41d 100644
--- a/core/java/android/hardware/input/InputSettings.java
+++ b/core/java/android/hardware/input/InputSettings.java
@@ -25,6 +25,7 @@
 import static com.android.hardware.input.Flags.keyboardA11yStickyKeysFlag;
 import static com.android.hardware.input.Flags.keyboardA11yMouseKeys;
 import static com.android.hardware.input.Flags.touchpadTapDragging;
+import static com.android.hardware.input.Flags.touchpadVisualizer;
 import static com.android.input.flags.Flags.enableInputFilterRustImpl;
 
 import android.Manifest;
@@ -326,6 +327,15 @@
     }
 
     /**
+     * Returns true if the feature flag for touchpad visualizer is enabled.
+     *
+     * @hide
+     */
+    public static boolean isTouchpadVisualizerFeatureFlagEnabled() {
+        return touchpadVisualizer();
+    }
+
+    /**
      * Returns true if the touchpad should allow tap dragging.
      *
      * The returned value only applies to gesture-compatible touchpads.
diff --git a/core/java/android/hardware/input/VirtualKeyEvent.java b/core/java/android/hardware/input/VirtualKeyEvent.java
index c0102bf..da959af 100644
--- a/core/java/android/hardware/input/VirtualKeyEvent.java
+++ b/core/java/android/hardware/input/VirtualKeyEvent.java
@@ -286,6 +286,10 @@
          * obtained from {@link SystemClock#uptimeMillis()} (with nanosecond precision instead of
          * millisecond), but can be different depending on the use case.
          * This field is optional and can be omitted.
+         * <p>
+         * If this field is unset, then the time at which this event is sent to the framework would
+         * be considered as the event time (even though
+         * {@link VirtualKeyEvent#getEventTimeNanos()}) would return {@code 0L}).
          *
          * @return this builder, to allow for chaining of calls
          * @see InputEvent#getEventTime()
diff --git a/core/java/android/hardware/input/VirtualMouseButtonEvent.java b/core/java/android/hardware/input/VirtualMouseButtonEvent.java
index fc42b15..333c3c7 100644
--- a/core/java/android/hardware/input/VirtualMouseButtonEvent.java
+++ b/core/java/android/hardware/input/VirtualMouseButtonEvent.java
@@ -197,6 +197,10 @@
          * obtained from {@link SystemClock#uptimeMillis()} (with nanosecond precision instead of
          * millisecond), but can be different depending on the use case.
          * This field is optional and can be omitted.
+         * <p>
+         * If this field is unset, then the time at which this event is sent to the framework would
+         * be considered as the event time (even though
+         * {@link VirtualMouseButtonEvent#getEventTimeNanos()}) would return {@code 0L}).
          *
          * @return this builder, to allow for chaining of calls
          * @see InputEvent#getEventTime()
diff --git a/core/java/android/hardware/input/VirtualMouseRelativeEvent.java b/core/java/android/hardware/input/VirtualMouseRelativeEvent.java
index 2a42cfc..86d759d 100644
--- a/core/java/android/hardware/input/VirtualMouseRelativeEvent.java
+++ b/core/java/android/hardware/input/VirtualMouseRelativeEvent.java
@@ -135,6 +135,10 @@
          * obtained from {@link SystemClock#uptimeMillis()} (with nanosecond precision instead of
          * millisecond), but can be different depending on the use case.
          * This field is optional and can be omitted.
+         * <p>
+         * If this field is unset, then the time at which this event is sent to the framework would
+         * be considered as the event time (even though
+         * {@link VirtualMouseRelativeEvent#getEventTimeNanos()}) would return {@code 0L}).
          *
          * @return this builder, to allow for chaining of calls
          * @see InputEvent#getEventTime()
diff --git a/core/java/android/hardware/input/VirtualMouseScrollEvent.java b/core/java/android/hardware/input/VirtualMouseScrollEvent.java
index c89c188..a4958c7 100644
--- a/core/java/android/hardware/input/VirtualMouseScrollEvent.java
+++ b/core/java/android/hardware/input/VirtualMouseScrollEvent.java
@@ -146,6 +146,10 @@
          * obtained from {@link SystemClock#uptimeMillis()} (with nanosecond precision instead of
          * millisecond), but can be different depending on the use case.
          * This field is optional and can be omitted.
+         * <p>
+         * If this field is unset, then the time at which this event is sent to the framework would
+         * be considered as the event time (even though
+         * {@link VirtualMouseScrollEvent#getEventTimeNanos()}) would return {@code 0L}).
          *
          * @return this builder, to allow for chaining of calls
          * @see InputEvent#getEventTime()
diff --git a/core/java/android/hardware/input/VirtualRotaryEncoderScrollEvent.java b/core/java/android/hardware/input/VirtualRotaryEncoderScrollEvent.java
index 8c98abd..033b1c1 100644
--- a/core/java/android/hardware/input/VirtualRotaryEncoderScrollEvent.java
+++ b/core/java/android/hardware/input/VirtualRotaryEncoderScrollEvent.java
@@ -129,6 +129,10 @@
          * obtained from {@link SystemClock#uptimeMillis()} (with nanosecond precision instead of
          * millisecond), but can be different depending on the use case.
          * This field is optional and can be omitted.
+         * <p>
+         * If this field is unset, then the time at which this event is sent to the framework would
+         * be considered as the event time (even though
+         * {@link VirtualRotaryEncoderScrollEvent#getEventTimeNanos()}) would return {@code 0L}).
          *
          * @return this builder, to allow for chaining of calls
          * @see InputEvent#getEventTime()
diff --git a/core/java/android/hardware/input/VirtualStylusButtonEvent.java b/core/java/android/hardware/input/VirtualStylusButtonEvent.java
index 97a4cd0..8fcf561b 100644
--- a/core/java/android/hardware/input/VirtualStylusButtonEvent.java
+++ b/core/java/android/hardware/input/VirtualStylusButtonEvent.java
@@ -187,6 +187,10 @@
          * obtained from {@link SystemClock#uptimeMillis()} (with nanosecond precision instead of
          * millisecond), but can be different depending on the use case.
          * This field is optional and can be omitted.
+         * <p>
+         * If this field is unset, then the time at which this event is sent to the framework would
+         * be considered as the event time (even though
+         * {@link VirtualStylusButtonEvent#getEventTimeNanos()}) would return {@code 0L}).
          *
          * @return this builder, to allow for chaining of calls
          * @see InputEvent#getEventTime()
diff --git a/core/java/android/hardware/input/VirtualStylusMotionEvent.java b/core/java/android/hardware/input/VirtualStylusMotionEvent.java
index 2ab76ae..0ac6f3a 100644
--- a/core/java/android/hardware/input/VirtualStylusMotionEvent.java
+++ b/core/java/android/hardware/input/VirtualStylusMotionEvent.java
@@ -377,6 +377,10 @@
          * obtained from {@link SystemClock#uptimeMillis()} (with nanosecond precision instead of
          * millisecond), but can be different depending on the use case.
          * This field is optional and can be omitted.
+         * <p>
+         * If this field is unset, then the time at which this event is sent to the framework would
+         * be considered as the event time (even though
+         * {@link VirtualStylusMotionEvent#getEventTimeNanos()}) would return {@code 0L}).
          *
          * @return this builder, to allow for chaining of calls
          * @see InputEvent#getEventTime()
diff --git a/core/java/android/hardware/input/VirtualTouchEvent.java b/core/java/android/hardware/input/VirtualTouchEvent.java
index 7936dfe..0cccd25 100644
--- a/core/java/android/hardware/input/VirtualTouchEvent.java
+++ b/core/java/android/hardware/input/VirtualTouchEvent.java
@@ -354,6 +354,10 @@
          * obtained from {@link SystemClock#uptimeMillis()} (with nanosecond precision instead of
          * millisecond), but can be different depending on the use case.
          * This field is optional and can be omitted.
+         * <p>
+         * If this field is unset, then the time at which this event is sent to the framework would
+         * be considered as the event time (even though
+         * {@link VirtualTouchEvent#getEventTimeNanos()}) would return {@code 0L}).
          *
          * @return this builder, to allow for chaining of calls
          * @see InputEvent#getEventTime()
diff --git a/core/java/android/hardware/input/input_framework.aconfig b/core/java/android/hardware/input/input_framework.aconfig
index 0cd2800..6f1d63d8 100644
--- a/core/java/android/hardware/input/input_framework.aconfig
+++ b/core/java/android/hardware/input/input_framework.aconfig
@@ -75,3 +75,20 @@
     description: "Enables a developer overlay that displays raw touchpad input data and gesture recognition status in real-time."
     bug: "286551975"
 }
+
+flag {
+    namespace: "input_native"
+    name: "keyboard_layout_manager_multi_user_ime_setup"
+    description: "Update KeyboardLayoutManager to work correctly with multi-user IME setup"
+    bug: "354333072"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
+
+flag {
+  name: "modifier_shortcut_dump"
+  namespace: "input"
+  description: "Dump keyboard shortcuts in dumpsys window"
+  bug: "351963350"
+}
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index da7997d..00ce949 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -974,6 +974,11 @@
                                 : InputMethodManager.RESULT_UNCHANGED_HIDDEN), null);
             }
             Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
+            if (android.view.inputmethod.Flags.refactorInsetsController()) {
+                // The hide request first finishes the animation and then proceeds to the server
+                // side, finally reaching here, marking this the end state.
+                ImeTracker.forLogging().onHidden(statsToken);
+            }
         }
 
         /**
@@ -1166,12 +1171,14 @@
             }
             switch (motionEvent.getAction()) {
                 case MotionEvent.ACTION_DOWN:
+                case MotionEvent.ACTION_HOVER_ENTER:
                     // Consume and ignore all touches while stylus is down to prevent
                     // accidental touches from going to the app while writing.
                     mPrivOps.setHandwritingSurfaceNotTouchable(false);
                     break;
                 case MotionEvent.ACTION_UP:
                 case MotionEvent.ACTION_CANCEL:
+                case MotionEvent.ACTION_HOVER_EXIT:
                     // Go back to only consuming stylus events so that the user
                     // can continue to interact with the app using touch
                     // when the stylus is not down.
@@ -3104,6 +3111,13 @@
 
         ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_IME_SHOW_WINDOW);
 
+        if (android.view.inputmethod.Flags.refactorInsetsController()) {
+            // The ImeInsetsSourceProvider need the statsToken when dispatching the control
+            // (whenever the IME has drawn and its window is visible). Therefore, sending the
+            // statsToken here first.
+            notifyPreImeWindowVisibilityChanged(true /* visible */, statsToken);
+        }
+
         ImeTracing.getInstance().triggerServiceDump("InputMethodService#showWindow", mDumper,
                 null /* icProto */);
         Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.showWindow");
@@ -3127,7 +3141,9 @@
         if (DEBUG) Log.v(TAG, "showWindow: draw decorView!");
         mWindow.show();
         mDecorViewWasVisible = true;
-        applyVisibilityInInsetsConsumerIfNecessary(true /* setVisible */, statsToken);
+        if (!android.view.inputmethod.Flags.refactorInsetsController()) {
+            applyVisibilityInInsetsConsumerIfNecessary(true /* setVisible */, statsToken);
+        }
         cancelImeSurfaceRemoval();
         mInShowWindow = false;
         Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
@@ -3238,6 +3254,20 @@
                 ? mCurShowInputToken : mCurHideInputToken, setVisible, statsToken);
     }
 
+    /**
+     * Notifies the ImeInsetsSourceProvider before the IME visibility changes.
+     *
+     * @param visible {@code true} if it became visible, {@code false} otherwise.
+     * @param statsToken the token tracking the current IME request.
+     */
+    private void notifyPreImeWindowVisibilityChanged(boolean visible,
+            @NonNull ImeTracker.Token statsToken) {
+        final var viewRootImpl = getWindow().getWindow().getDecorView().getViewRootImpl();
+        if (viewRootImpl != null) {
+            viewRootImpl.notifyImeVisibilityChanged(visible, statsToken);
+        }
+    }
+
     private void finishViews(boolean finishingInput) {
         if (mInputViewStarted) {
             if (DEBUG) Log.v(TAG, "CALL: onFinishInputView");
@@ -3279,7 +3309,13 @@
         ImeTracing.getInstance().triggerServiceDump("InputMethodService#hideWindow", mDumper,
                 null /* icProto */);
         setImeWindowStatus(0, mBackDisposition);
-        applyVisibilityInInsetsConsumerIfNecessary(false /* setVisible */, statsToken);
+        if (android.view.inputmethod.Flags.refactorInsetsController()) {
+            // The ImeInsetsSourceProvider need the statsToken when dispatching the control. We
+            // send the token here, so that another request in the provider can be cancelled.
+            notifyPreImeWindowVisibilityChanged(false /* visible */, statsToken);
+        } else {
+            applyVisibilityInInsetsConsumerIfNecessary(false /* setVisible */, statsToken);
+        }
         mWindowVisible = false;
         finishViews(false /* finishingInput */);
         if (mDecorViewVisible) {
diff --git a/core/java/android/os/BatteryConsumer.java b/core/java/android/os/BatteryConsumer.java
index 2447ff9..623196b 100644
--- a/core/java/android/os/BatteryConsumer.java
+++ b/core/java/android/os/BatteryConsumer.java
@@ -66,6 +66,7 @@
             POWER_COMPONENT_WAKELOCK,
             POWER_COMPONENT_MEMORY,
             POWER_COMPONENT_PHONE,
+            POWER_COMPONENT_AMBIENT_DISPLAY,
             POWER_COMPONENT_IDLE,
             POWER_COMPONENT_REATTRIBUTED_TO_OTHER_CONSUMERS,
     })
@@ -208,7 +209,8 @@
                 POWER_COMPONENT_VIDEO,
                 POWER_COMPONENT_FLASHLIGHT,
                 POWER_COMPONENT_CAMERA,
-                POWER_COMPONENT_GNSS};
+                POWER_COMPONENT_GNSS,
+                POWER_COMPONENT_SENSORS};
         Arrays.sort(supportedPowerComponents);
         SUPPORTED_POWER_COMPONENTS_PER_PROCESS_STATE = IntArray.wrap(supportedPowerComponents);
     };
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index b2f333a..c7751e3 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -2491,7 +2491,7 @@
     public static final int SCREEN_BRIGHTNESS_LIGHT = 3;
     public static final int SCREEN_BRIGHTNESS_BRIGHT = 4;
 
-    static final String[] SCREEN_BRIGHTNESS_NAMES = {
+    public static final String[] SCREEN_BRIGHTNESS_NAMES = {
         "dark", "dim", "medium", "light", "bright"
     };
 
@@ -3077,7 +3077,7 @@
     public static final String[] HISTORY_EVENT_NAMES = new String[] {
             "null", "proc", "fg", "top", "sync", "wake_lock_in", "job", "user", "userfg", "conn",
             "active", "pkginst", "pkgunin", "alarm", "stats", "pkginactive", "pkgactive",
-            "tmpwhitelist", "screenwake", "wakeupap", "longwake", "est_capacity", "state"
+            "tmpwhitelist", "screenwake", "wakeupap", "longwake", "state"
     };
 
     public static final String[] HISTORY_EVENT_CHECKIN_NAMES = new String[] {
diff --git a/core/java/android/os/BatteryUsageStats.java b/core/java/android/os/BatteryUsageStats.java
index dd484f6..e039953 100644
--- a/core/java/android/os/BatteryUsageStats.java
+++ b/core/java/android/os/BatteryUsageStats.java
@@ -124,8 +124,8 @@
     static final String XML_ATTR_TIME_IN_BACKGROUND = "time_in_background";
     static final String XML_ATTR_TIME_IN_FOREGROUND_SERVICE = "time_in_foreground_service";
 
-    // We need about 700 bytes per UID
-    private static final long BATTERY_CONSUMER_CURSOR_WINDOW_SIZE = 5_000 * 700;
+    // Max window size. CursorWindow uses only as much memory as needed.
+    private static final long BATTERY_CONSUMER_CURSOR_WINDOW_SIZE = 20_000_000; // bytes
 
     private static final int STATSD_PULL_ATOM_MAX_BYTES = 45000;
 
diff --git a/core/java/android/os/CombinedVibration.java b/core/java/android/os/CombinedVibration.java
index f32a1f8..77d6cb7 100644
--- a/core/java/android/os/CombinedVibration.java
+++ b/core/java/android/os/CombinedVibration.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.TestApi;
+import android.os.vibrator.Flags;
 import android.util.SparseArray;
 
 import com.android.internal.util.Preconditions;
@@ -152,6 +153,9 @@
     /** @hide */
     public abstract boolean hasVibrator(int vibratorId);
 
+    /** @hide */
+    public abstract boolean hasVendorEffects();
+
     /**
      * Returns a compact version of the {@link #toString()} result for debugging purposes.
      *
@@ -424,6 +428,15 @@
             return true;
         }
 
+        /** @hide */
+        @Override
+        public boolean hasVendorEffects() {
+            if (!Flags.vendorVibrationEffects()) {
+                return false;
+            }
+            return mEffect instanceof VibrationEffect.VendorEffect;
+        }
+
         @Override
         public boolean equals(Object o) {
             if (this == o) {
@@ -605,6 +618,20 @@
             return mEffects.indexOfKey(vibratorId) >= 0;
         }
 
+        /** @hide */
+        @Override
+        public boolean hasVendorEffects() {
+            if (!Flags.vendorVibrationEffects()) {
+                return false;
+            }
+            for (int i = 0; i < mEffects.size(); i++) {
+                if (mEffects.get(i) instanceof VibrationEffect.VendorEffect) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
         @Override
         public boolean equals(Object o) {
             if (this == o) {
@@ -838,6 +865,17 @@
             return false;
         }
 
+        /** @hide */
+        @Override
+        public boolean hasVendorEffects() {
+            for (int i = 0; i < mEffects.size(); i++) {
+                if (mEffects.get(i).hasVendorEffects()) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
         @Override
         public boolean equals(Object o) {
             if (this == o) {
diff --git a/core/java/android/os/ConcurrentMessageQueue/MessageQueue.java b/core/java/android/os/ConcurrentMessageQueue/MessageQueue.java
index 72b5cf7..90678df 100644
--- a/core/java/android/os/ConcurrentMessageQueue/MessageQueue.java
+++ b/core/java/android/os/ConcurrentMessageQueue/MessageQueue.java
@@ -214,7 +214,7 @@
     private volatile long mNextInsertSeqValue = 0;
     /*
      * The exception to the FIFO order rule is sendMessageAtFrontOfQueue().
-     * Those messages must be in LIFO order - SIGH.
+     * Those messages must be in LIFO order.
      * Decrements on each front of queue insert.
      */
     private static final VarHandle sNextFrontInsertSeq;
diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java
index e6b1c07..14005b3 100644
--- a/core/java/android/os/FileUtils.java
+++ b/core/java/android/os/FileUtils.java
@@ -54,7 +54,6 @@
 import android.provider.MediaStore;
 import android.system.ErrnoException;
 import android.system.Os;
-import android.system.OsConstants;
 import android.system.StructStat;
 import android.text.TextUtils;
 import android.util.DataUnit;
@@ -1535,7 +1534,6 @@
     }
 
     /** {@hide} */
-    @android.ravenwood.annotation.RavenwoodThrow(blockedBy = OsConstants.class)
     public static int translateModeStringToPosix(String mode) {
         // Quick check for invalid chars
         for (int i = 0; i < mode.length(); i++) {
@@ -1570,7 +1568,6 @@
     }
 
     /** {@hide} */
-    @android.ravenwood.annotation.RavenwoodThrow(blockedBy = OsConstants.class)
     public static String translateModePosixToString(int mode) {
         String res = "";
         if ((mode & O_ACCMODE) == O_RDWR) {
@@ -1592,7 +1589,6 @@
     }
 
     /** {@hide} */
-    @android.ravenwood.annotation.RavenwoodThrow(blockedBy = OsConstants.class)
     public static int translateModePosixToPfd(int mode) {
         int res = 0;
         if ((mode & O_ACCMODE) == O_RDWR) {
@@ -1617,7 +1613,6 @@
     }
 
     /** {@hide} */
-    @android.ravenwood.annotation.RavenwoodThrow(blockedBy = OsConstants.class)
     public static int translateModePfdToPosix(int mode) {
         int res = 0;
         if ((mode & MODE_READ_WRITE) == MODE_READ_WRITE) {
@@ -1642,7 +1637,6 @@
     }
 
     /** {@hide} */
-    @android.ravenwood.annotation.RavenwoodThrow(blockedBy = OsConstants.class)
     public static int translateModeAccessToPosix(int mode) {
         if (mode == F_OK) {
             // There's not an exact mapping, so we attempt a read-only open to
diff --git a/core/java/android/os/LocaleList.java b/core/java/android/os/LocaleList.java
index d7e440b..c152c2d 100644
--- a/core/java/android/os/LocaleList.java
+++ b/core/java/android/os/LocaleList.java
@@ -37,6 +37,7 @@
  * LocaleList is an immutable list of Locales, typically used to keep an ordered list of user
  * preferences for locales.
  */
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
 public final class LocaleList implements Parcelable {
     private final Locale[] mList;
     // This is a comma-separated list of the locales in the LocaleList created at construction time,
diff --git a/core/java/android/os/LockedMessageQueue/MessageQueue.java b/core/java/android/os/LockedMessageQueue/MessageQueue.java
new file mode 100644
index 0000000..b24e14b
--- /dev/null
+++ b/core/java/android/os/LockedMessageQueue/MessageQueue.java
@@ -0,0 +1,1351 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.TestApi;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Handler;
+import android.os.Trace;
+import android.util.Log;
+import android.util.Printer;
+import android.util.SparseArray;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.io.FileDescriptor;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * Low-level class holding the list of messages to be dispatched by a
+ * {@link Looper}.  Messages are not added directly to a MessageQueue,
+ * but rather through {@link Handler} objects associated with the Looper.
+ *
+ * <p>You can retrieve the MessageQueue for the current thread with
+ * {@link Looper#myQueue() Looper.myQueue()}.
+ */
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
+@android.ravenwood.annotation.RavenwoodNativeSubstitutionClass(
+        "com.android.platform.test.ravenwood.nativesubstitution.MessageQueue_host")
+public final class MessageQueue {
+    private static final String TAG = "LockedMessageQueue";
+    private static final boolean DEBUG = false;
+    private static final boolean TRACE = false;
+
+    static final class MessageHeap {
+        static final int MESSAGE_HEAP_INITIAL_SIZE = 16;
+
+        Message[] mHeap = new Message[MESSAGE_HEAP_INITIAL_SIZE];
+        int mNumElements = 0;
+
+        static int parentNodeIdx(int i) {
+            return (i - 1) >>> 1;
+        }
+
+        Message getParentNode(int i) {
+            return mHeap[(i - 1) >>> 1];
+        }
+
+        static int rightNodeIdx(int i) {
+            return 2 * i + 2;
+        }
+
+        Message getRightNode(int i) {
+            return mHeap[2 * i + 2];
+        }
+
+        static int leftNodeIdx(int i) {
+            return 2 * i + 1;
+        }
+
+        Message getLeftNode(int i) {
+            return mHeap[2 * i + 1];
+        }
+
+        int size() {
+            return mHeap.length;
+        }
+
+        int numElements() {
+            return mNumElements;
+        }
+
+        boolean isEmpty() {
+            return mNumElements == 0;
+        }
+
+        Message getMessageAt(int index) {
+            return mHeap[index];
+        }
+
+        /*
+        * Returns:
+        *    0 if x==y.
+        *    A value less than 0 if x<y.
+        *    A value greater than 0 if x>y.
+        */
+        int compareMessage(Message x, Message y) {
+            int compared = Long.compare(x.when, y.when);
+            if (compared == 0) {
+                compared = Long.compare(x.mInsertSeq, y.mInsertSeq);
+            }
+            return compared;
+        }
+
+        int compareMessageByIdx(int x, int y) {
+            return compareMessage(mHeap[x], mHeap[y]);
+        }
+
+        void swap(int x, int y) {
+            Message tmp = mHeap[x];
+            mHeap[x] = mHeap[y];
+            mHeap[y] = tmp;
+        }
+
+        void siftDown(int i) {
+            int smallest = i;
+            int r, l;
+
+            while (true) {
+                r = rightNodeIdx(i);
+                l = leftNodeIdx(i);
+
+                if (r < mNumElements && compareMessageByIdx(r, smallest) < 0) {
+                    smallest = r;
+                }
+
+                if (l < mNumElements && compareMessageByIdx(l, smallest) < 0) {
+                    smallest = l;
+                }
+
+                if (smallest != i) {
+                    swap(i, smallest);
+                    i = smallest;
+                    continue;
+                }
+                break;
+            }
+        }
+
+        boolean siftUp(int i) {
+            boolean swapped = false;
+            while (i != 0 && compareMessage(mHeap[i], getParentNode(i)) < 0) {
+                int p = parentNodeIdx(i);
+
+                swap(i, p);
+                swapped = true;
+                i = p;
+            }
+
+            return swapped;
+        }
+
+        void maybeGrowHeap() {
+            if (mNumElements == mHeap.length) {
+                /* Grow by 1.5x */
+                int newSize = mHeap.length + (mHeap.length >>> 1);
+                Message[] newHeap;
+                if (DEBUG) {
+                    Log.v(TAG, "maybeGrowHeap mNumElements " + mNumElements + " mHeap.length "
+                            + mHeap.length + " newSize " + newSize);
+                }
+
+                newHeap = Arrays.copyOf(mHeap, newSize);
+                mHeap = newHeap;
+            }
+        }
+
+        void add(Message m) {
+            int i;
+
+            maybeGrowHeap();
+
+            i = mNumElements;
+            mNumElements++;
+            mHeap[i] = m;
+
+            siftUp(i);
+        }
+
+        void maybeShrinkHeap() {
+            /* Shrink by 2x */
+            int newSize = mHeap.length >>> 1;
+
+            if (newSize >= MESSAGE_HEAP_INITIAL_SIZE
+                    && mNumElements <= newSize) {
+                Message[] newHeap;
+
+                if (DEBUG) {
+                    Log.v(TAG, "maybeShrinkHeap mNumElements " + mNumElements + " mHeap.length "
+                            + mHeap.length + " newSize " + newSize);
+                }
+
+                newHeap = Arrays.copyOf(mHeap, newSize);
+                mHeap = newHeap;
+            }
+        }
+
+        Message poll() {
+            if (mNumElements > 0) {
+                Message ret = mHeap[0];
+                mNumElements--;
+                mHeap[0] = mHeap[mNumElements];
+                mHeap[mNumElements] = null;
+
+                siftDown(0);
+
+                maybeShrinkHeap();
+                return ret;
+            }
+            return null;
+        }
+
+        Message peek() {
+            if (mNumElements > 0) {
+                return mHeap[0];
+            }
+            return null;
+        }
+
+        private void remove(int i) throws IllegalArgumentException {
+            if (i > mNumElements || mNumElements == 0) {
+                throw new IllegalArgumentException("Index " + i + " out of bounds: "
+                        + mNumElements);
+            } else if (i == (mNumElements - 1)) {
+                mHeap[i] = null;
+                mNumElements--;
+            } else {
+                mNumElements--;
+                mHeap[i] = mHeap[mNumElements];
+                mHeap[mNumElements] = null;
+                if (!siftUp(i)) {
+                    siftDown(i);
+                }
+            }
+            /* Don't shink here, let the caller do this once it has removed all matching items. */
+        }
+
+        void removeAll() {
+            Message m;
+            for (int i = 0; i < mNumElements; i++) {
+                m = mHeap[i];
+                mHeap[i] = null;
+                m.recycleUnchecked();
+            }
+            mNumElements = 0;
+            maybeShrinkHeap();
+        }
+
+        abstract static class MessageHeapCompare {
+            public abstract boolean compareMessage(Message m, Handler h, int what, Object object,
+                    Runnable r, long when);
+        }
+
+        boolean findOrRemoveMessages(Handler h, int what, Object object, Runnable r, long when,
+                MessageHeapCompare compare, boolean removeMatches) {
+            boolean found = false;
+            /*
+             * Walk the heap backwards so we don't have to re-visit an array element due to
+             * sifting
+             */
+            for (int i = mNumElements - 1; i >= 0; i--) {
+                if (compare.compareMessage(mHeap[i], h, what, object, r, when)) {
+                    found = true;
+                    if (removeMatches) {
+                        Message m = mHeap[i];
+                        try {
+                            remove(i);
+                        } catch (IllegalArgumentException e) {
+                            Log.wtf(TAG, "Index out of bounds during remove " + e);
+                        }
+                        m.recycleUnchecked();
+                        continue;
+                    }
+                    break;
+                }
+            }
+            if (found && removeMatches) {
+                maybeShrinkHeap();
+            }
+            return found;
+        }
+
+        /*
+        * Keep this for manual debugging. It's easier to pepper the code with this function
+        * than MessageQueue.dump()
+        */
+        void print() {
+            Log.v(TAG, "heap num elem: " + mNumElements + " mHeap.length " + mHeap.length);
+            for (int i = 0; i < mNumElements; i++) {
+                Log.v(TAG, "[" + i + "]\t" + mHeap[i] + " seq: " + mHeap[i].mInsertSeq + " async: "
+                        + mHeap[i].isAsynchronous());
+            }
+        }
+
+        boolean verify(int root) {
+            int r = rightNodeIdx(root);
+            int l = leftNodeIdx(root);
+
+            if (l >= mNumElements && r >= mNumElements) {
+                return true;
+            }
+
+            if (l < mNumElements && compareMessageByIdx(l, root) < 0) {
+                Log.wtf(TAG, "Verify failure: root idx/when: " + root + "/" + mHeap[root].when
+                        + " left node idx/when: " + l + "/" + mHeap[l].when);
+                return false;
+            }
+
+            if (r < mNumElements && compareMessageByIdx(r, root) < 0) {
+                Log.wtf(TAG, "Verify failure: root idx/when: " + root + "/" + mHeap[root].when
+                        + " right node idx/when: " + r + "/" + mHeap[r].when);
+                return false;
+            }
+
+            if (!verify(r) || !verify(l)) {
+                return false;
+            }
+            return true;
+        }
+
+        boolean checkDanglingReferences(String where) {
+            /* First, let's make sure we didn't leave any dangling references */
+            for (int i = mNumElements; i < mHeap.length; i++) {
+                if (mHeap[i] != null) {
+                    Log.wtf(TAG, "[" + where
+                            + "] Verify failure: dangling reference found at index "
+                            + i + ": " + mHeap[i] + " Async " + mHeap[i].isAsynchronous()
+                            + " mNumElements " + mNumElements + " mHeap.length " + mHeap.length);
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        boolean verify() {
+            if (!checkDanglingReferences(TAG)) {
+                return false;
+            }
+            return verify(0);
+        }
+    }
+
+    // True if the message queue can be quit.
+    @UnsupportedAppUsage
+    private final boolean mQuitAllowed;
+
+    @UnsupportedAppUsage
+    @SuppressWarnings("unused")
+    private long mPtr; // used by native code
+
+    private final MessageHeap mPriorityQueue = new MessageHeap();
+    private final MessageHeap mAsyncPriorityQueue = new MessageHeap();
+
+    /*
+     * This helps us ensure that messages with the same timestamp are inserted in FIFO order.
+     * Increments on each insert, starting at 0. MessaeHeap.compareMessage() will compare sequences
+     * when delivery timestamps are identical.
+     */
+    private long mNextInsertSeq;
+
+    /*
+     * The exception to the FIFO order rule is sendMessageAtFrontOfQueue().
+     * Those messages must be in LIFO order.
+     * Decrements on each front of queue insert.
+     */
+    private long mNextFrontInsertSeq = -1;
+
+    @UnsupportedAppUsage
+    private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>();
+    private SparseArray<FileDescriptorRecord> mFileDescriptorRecords;
+    private IdleHandler[] mPendingIdleHandlers;
+    private boolean mQuitting;
+
+    // Indicates whether next() is blocked waiting in pollOnce() with a non-zero timeout.
+    private boolean mBlocked;
+
+    // The next barrier token.
+    // Barriers are indicated by messages with a null target whose arg1 field carries the token.
+    @UnsupportedAppUsage
+    private int mNextBarrierToken;
+
+    private native static long nativeInit();
+    private native static void nativeDestroy(long ptr);
+    @UnsupportedAppUsage
+    private native void nativePollOnce(long ptr, int timeoutMillis); /*non-static for callbacks*/
+    private native static void nativeWake(long ptr);
+    private native static boolean nativeIsPolling(long ptr);
+    private native static void nativeSetFileDescriptorEvents(long ptr, int fd, int events);
+
+    MessageQueue(boolean quitAllowed) {
+        mQuitAllowed = quitAllowed;
+        mPtr = nativeInit();
+    }
+
+    @GuardedBy("this")
+    private void removeRootFromPriorityQueue(Message msg) {
+        Message tmp;
+        if (msg.isAsynchronous()) {
+            tmp = mAsyncPriorityQueue.poll();
+        } else {
+            tmp = mPriorityQueue.poll();
+        }
+        if (DEBUG && tmp != msg) {
+            Log.wtf(TAG, "Unexpected message at head of heap. Wanted: " + msg + " msg.isAsync "
+                    + msg.isAsynchronous() + " Found: " + tmp);
+
+            mPriorityQueue.print();
+            mAsyncPriorityQueue.print();
+        }
+    }
+
+    @GuardedBy("this")
+    private Message pickEarliestMessage(Message x, Message y) {
+        if (x != null && y != null) {
+            if (mPriorityQueue.compareMessage(x, y) < 0) {
+                return x;
+            }
+            return y;
+        }
+
+        return x != null ? x : y;
+    }
+
+    @GuardedBy("this")
+    private Message peekEarliestMessage() {
+        Message x = mPriorityQueue.peek();
+        Message y = mAsyncPriorityQueue.peek();
+
+        return pickEarliestMessage(x, y);
+    }
+
+    @GuardedBy("this")
+    private boolean priorityQueuesAreEmpty() {
+        return mPriorityQueue.isEmpty() && mAsyncPriorityQueue.isEmpty();
+    }
+
+    @GuardedBy("this")
+    private boolean priorityQueueHasBarrier() {
+        Message m = mPriorityQueue.peek();
+
+        if (m != null && m.target == null) {
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        try {
+            dispose();
+        } finally {
+            super.finalize();
+        }
+    }
+
+    // Disposes of the underlying message queue.
+    // Must only be called on the looper thread or the finalizer.
+    private void dispose() {
+        if (mPtr != 0) {
+            nativeDestroy(mPtr);
+            mPtr = 0;
+        }
+    }
+
+    /**
+     * Returns true if the looper has no pending messages which are due to be processed.
+     *
+     * <p>This method is safe to call from any thread.
+     *
+     * @return True if the looper is idle.
+     */
+    public boolean isIdle() {
+        synchronized (this) {
+            Message m = peekEarliestMessage();
+            final long now = SystemClock.uptimeMillis();
+
+            return (priorityQueuesAreEmpty() || now < m.when);
+        }
+    }
+
+    /**
+     * Add a new {@link IdleHandler} to this message queue.  This may be
+     * removed automatically for you by returning false from
+     * {@link IdleHandler#queueIdle IdleHandler.queueIdle()} when it is
+     * invoked, or explicitly removing it with {@link #removeIdleHandler}.
+     *
+     * <p>This method is safe to call from any thread.
+     *
+     * @param handler The IdleHandler to be added.
+     */
+    public void addIdleHandler(@NonNull IdleHandler handler) {
+        if (handler == null) {
+            throw new NullPointerException("Can't add a null IdleHandler");
+        }
+        synchronized (this) {
+            mIdleHandlers.add(handler);
+        }
+    }
+
+    /**
+     * Remove an {@link IdleHandler} from the queue that was previously added
+     * with {@link #addIdleHandler}.  If the given object is not currently
+     * in the idle list, nothing is done.
+     *
+     * <p>This method is safe to call from any thread.
+     *
+     * @param handler The IdleHandler to be removed.
+     */
+    public void removeIdleHandler(@NonNull IdleHandler handler) {
+        synchronized (this) {
+            mIdleHandlers.remove(handler);
+        }
+    }
+
+    /**
+     * Returns whether this looper's thread is currently polling for more work to do.
+     * This is a good signal that the loop is still alive rather than being stuck
+     * handling a callback.  Note that this method is intrinsically racy, since the
+     * state of the loop can change before you get the result back.
+     *
+     * <p>This method is safe to call from any thread.
+     *
+     * @return True if the looper is currently polling for events.
+     * @hide
+     */
+    public boolean isPolling() {
+        synchronized (this) {
+            return isPollingLocked();
+        }
+    }
+
+    private boolean isPollingLocked() {
+        // If the loop is quitting then it must not be idling.
+        // We can assume mPtr != 0 when mQuitting is false.
+        return !mQuitting && nativeIsPolling(mPtr);
+    }
+
+    /**
+     * Adds a file descriptor listener to receive notification when file descriptor
+     * related events occur.
+     * <p>
+     * If the file descriptor has already been registered, the specified events
+     * and listener will replace any that were previously associated with it.
+     * It is not possible to set more than one listener per file descriptor.
+     * </p><p>
+     * It is important to always unregister the listener when the file descriptor
+     * is no longer of use.
+     * </p>
+     *
+     * @param fd The file descriptor for which a listener will be registered.
+     * @param events The set of events to receive: a combination of the
+     * {@link OnFileDescriptorEventListener#EVENT_INPUT},
+     * {@link OnFileDescriptorEventListener#EVENT_OUTPUT}, and
+     * {@link OnFileDescriptorEventListener#EVENT_ERROR} event masks.  If the requested
+     * set of events is zero, then the listener is unregistered.
+     * @param listener The listener to invoke when file descriptor events occur.
+     *
+     * @see OnFileDescriptorEventListener
+     * @see #removeOnFileDescriptorEventListener
+     */
+    @android.ravenwood.annotation.RavenwoodThrow(blockedBy = android.os.ParcelFileDescriptor.class)
+    public void addOnFileDescriptorEventListener(@NonNull FileDescriptor fd,
+            @OnFileDescriptorEventListener.Events int events,
+            @NonNull OnFileDescriptorEventListener listener) {
+        if (fd == null) {
+            throw new IllegalArgumentException("fd must not be null");
+        }
+        if (listener == null) {
+            throw new IllegalArgumentException("listener must not be null");
+        }
+
+        synchronized (this) {
+            updateOnFileDescriptorEventListenerLocked(fd, events, listener);
+        }
+    }
+
+    /**
+     * Removes a file descriptor listener.
+     * <p>
+     * This method does nothing if no listener has been registered for the
+     * specified file descriptor.
+     * </p>
+     *
+     * @param fd The file descriptor whose listener will be unregistered.
+     *
+     * @see OnFileDescriptorEventListener
+     * @see #addOnFileDescriptorEventListener
+     */
+    @android.ravenwood.annotation.RavenwoodThrow(blockedBy = android.os.ParcelFileDescriptor.class)
+    public void removeOnFileDescriptorEventListener(@NonNull FileDescriptor fd) {
+        if (fd == null) {
+            throw new IllegalArgumentException("fd must not be null");
+        }
+
+        synchronized (this) {
+            updateOnFileDescriptorEventListenerLocked(fd, 0, null);
+        }
+    }
+
+    @android.ravenwood.annotation.RavenwoodThrow(blockedBy = android.os.ParcelFileDescriptor.class)
+    private void updateOnFileDescriptorEventListenerLocked(FileDescriptor fd, int events,
+            OnFileDescriptorEventListener listener) {
+        final int fdNum = fd.getInt$();
+
+        int index = -1;
+        FileDescriptorRecord record = null;
+        if (mFileDescriptorRecords != null) {
+            index = mFileDescriptorRecords.indexOfKey(fdNum);
+            if (index >= 0) {
+                record = mFileDescriptorRecords.valueAt(index);
+                if (record != null && record.mEvents == events) {
+                    return;
+                }
+            }
+        }
+
+        if (events != 0) {
+            events |= OnFileDescriptorEventListener.EVENT_ERROR;
+            if (record == null) {
+                if (mFileDescriptorRecords == null) {
+                    mFileDescriptorRecords = new SparseArray<FileDescriptorRecord>();
+                }
+                record = new FileDescriptorRecord(fd, events, listener);
+                mFileDescriptorRecords.put(fdNum, record);
+            } else {
+                record.mListener = listener;
+                record.mEvents = events;
+                record.mSeq += 1;
+            }
+            nativeSetFileDescriptorEvents(mPtr, fdNum, events);
+        } else if (record != null) {
+            record.mEvents = 0;
+            mFileDescriptorRecords.removeAt(index);
+            nativeSetFileDescriptorEvents(mPtr, fdNum, 0);
+        }
+    }
+
+    // Called from native code.
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    private int dispatchEvents(int fd, int events) {
+        // Get the file descriptor record and any state that might change.
+        final FileDescriptorRecord record;
+        final int oldWatchedEvents;
+        final OnFileDescriptorEventListener listener;
+        final int seq;
+        synchronized (this) {
+            record = mFileDescriptorRecords.get(fd);
+            if (record == null) {
+                return 0; // spurious, no listener registered
+            }
+
+            oldWatchedEvents = record.mEvents;
+            events &= oldWatchedEvents; // filter events based on current watched set
+            if (events == 0) {
+                return oldWatchedEvents; // spurious, watched events changed
+            }
+
+            listener = record.mListener;
+            seq = record.mSeq;
+        }
+
+        // Invoke the listener outside of the lock.
+        int newWatchedEvents = listener.onFileDescriptorEvents(
+                record.mDescriptor, events);
+        if (newWatchedEvents != 0) {
+            newWatchedEvents |= OnFileDescriptorEventListener.EVENT_ERROR;
+        }
+
+        // Update the file descriptor record if the listener changed the set of
+        // events to watch and the listener itself hasn't been updated since.
+        if (newWatchedEvents != oldWatchedEvents) {
+            synchronized (this) {
+                int index = mFileDescriptorRecords.indexOfKey(fd);
+                if (index >= 0 && mFileDescriptorRecords.valueAt(index) == record
+                        && record.mSeq == seq) {
+                    record.mEvents = newWatchedEvents;
+                    if (newWatchedEvents == 0) {
+                        mFileDescriptorRecords.removeAt(index);
+                    }
+                }
+            }
+        }
+
+        // Return the new set of events to watch for native code to take care of.
+        return newWatchedEvents;
+    }
+
+    private static final AtomicLong mMessagesDelivered = new AtomicLong();
+
+    @UnsupportedAppUsage
+    Message next() {
+        // 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.
+        final long ptr = mPtr;
+        if (ptr == 0) {
+            return null;
+        }
+
+        int pendingIdleHandlerCount = -1; // -1 only during first iteration
+        int nextPollTimeoutMillis = 0;
+        for (;;) {
+            if (nextPollTimeoutMillis != 0) {
+                Binder.flushPendingCommands();
+            }
+
+            nativePollOnce(ptr, nextPollTimeoutMillis);
+
+            synchronized (this) {
+                // Try to retrieve the next message.  Return if found.
+                final long now = SystemClock.uptimeMillis();
+                Message prevMsg = null;
+                Message msg = peekEarliestMessage();
+
+                if (DEBUG && msg != null) {
+                    Log.v(TAG, "Next found message " + msg + " isAsynchronous: "
+                            + msg.isAsynchronous() + " target " + msg.target);
+                }
+
+                if (msg != null && !msg.isAsynchronous() && msg.target == null) {
+                    // Stalled by a barrier.  Find the next asynchronous message in the queue.
+                    msg = mAsyncPriorityQueue.peek();
+                    if (DEBUG) {
+                        Log.v(TAG, "Next message was barrier async msg: " + msg);
+                    }
+                }
+
+                if (msg != null) {
+                    if (now < msg.when) {
+                        // Next message is not ready.  Set a timeout to wake up when it is ready.
+                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
+                    } else {
+                        mBlocked = false;
+                        removeRootFromPriorityQueue(msg);
+                        if (DEBUG) Log.v(TAG, "Returning message: " + msg);
+                        msg.markInUse();
+                        if (TRACE) {
+                            Trace.setCounter("MQ.Delivered", mMessagesDelivered.incrementAndGet());
+                        }
+                        return msg;
+                    }
+                } else {
+                    // No more messages.
+                    nextPollTimeoutMillis = -1;
+                }
+
+                // Process the quit message now that all pending messages have been handled.
+                if (mQuitting) {
+                    dispose();
+                    return null;
+                }
+
+                // If first time idle, then get the number of idlers to run.
+                // Idle handles only run if the queue is empty or if the first message
+                // in the queue (possibly a barrier) is due to be handled in the future.
+                Message next = peekEarliestMessage();
+                if (pendingIdleHandlerCount < 0
+                        && (next == null || now < next.when)) {
+                    pendingIdleHandlerCount = mIdleHandlers.size();
+                }
+                if (pendingIdleHandlerCount <= 0) {
+                    // No idle handlers to run.  Loop and wait some more.
+                    mBlocked = true;
+                    continue;
+                }
+
+                if (mPendingIdleHandlers == null) {
+                    mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
+                }
+                mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
+            }
+
+            // Run the idle handlers.
+            // We only ever reach this code block during the first iteration.
+            for (int i = 0; i < pendingIdleHandlerCount; i++) {
+                final IdleHandler idler = mPendingIdleHandlers[i];
+                mPendingIdleHandlers[i] = null; // release the reference to the handler
+
+                boolean keep = false;
+                try {
+                    keep = idler.queueIdle();
+                } catch (Throwable t) {
+                    Log.wtf(TAG, "IdleHandler threw exception", t);
+                }
+
+                if (!keep) {
+                    synchronized (this) {
+                        mIdleHandlers.remove(idler);
+                    }
+                }
+            }
+
+            // Reset the idle handler count to 0 so we do not run them again.
+            pendingIdleHandlerCount = 0;
+
+            // While calling an idle handler, a new message could have been delivered
+            // so go back and look again for a pending message without waiting.
+            nextPollTimeoutMillis = 0;
+        }
+    }
+
+    void quit(boolean safe) {
+        if (!mQuitAllowed) {
+            throw new IllegalStateException("Main thread not allowed to quit.");
+        }
+
+        synchronized (this) {
+            if (mQuitting) {
+                return;
+            }
+            mQuitting = true;
+
+            if (safe) {
+                removeAllFutureMessagesLocked();
+            } else {
+                removeAllMessagesLocked();
+            }
+
+            // We can assume mPtr != 0 because mQuitting was previously false.
+            nativeWake(mPtr);
+        }
+    }
+
+    /**
+     * Posts a synchronization barrier to the Looper's message queue.
+     *
+     * Message processing occurs as usual until the message queue encounters the
+     * synchronization barrier that has been posted.  When the barrier is encountered,
+     * later synchronous messages in the queue are stalled (prevented from being executed)
+     * until the barrier is released by calling {@link #removeSyncBarrier} and specifying
+     * the token that identifies the synchronization barrier.
+     *
+     * This method is used to immediately postpone execution of all subsequently posted
+     * synchronous messages until a condition is met that releases the barrier.
+     * Asynchronous messages (see {@link Message#isAsynchronous} are exempt from the barrier
+     * and continue to be processed as usual.
+     *
+     * This call must be always matched by a call to {@link #removeSyncBarrier} with
+     * the same token to ensure that the message queue resumes normal operation.
+     * Otherwise the application will probably hang!
+     *
+     * @return A token that uniquely identifies the barrier.  This token must be
+     * passed to {@link #removeSyncBarrier} to release the barrier.
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage
+    @TestApi
+    public int postSyncBarrier() {
+        return postSyncBarrier(SystemClock.uptimeMillis());
+    }
+
+    private int postSyncBarrier(long when) {
+        // Enqueue a new sync barrier token.
+        // We don't need to wake the queue because the purpose of a barrier is to stall it.
+        synchronized (this) {
+            final int token = mNextBarrierToken++;
+            final Message msg = Message.obtain();
+            msg.arg1 = token;
+
+            enqueueMessageUnchecked(msg, when);
+            return token;
+        }
+    }
+
+    private class MatchBarrierToken extends MessageHeap.MessageHeapCompare {
+        int mBarrierToken;
+
+        MatchBarrierToken(int token) {
+            super();
+            mBarrierToken = token;
+        }
+
+        @Override
+        public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
+                long when) {
+            if (m.target == null && m.arg1 == mBarrierToken) {
+                return true;
+            }
+            return false;
+        }
+    }
+
+    /**
+     * 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) {
+        final MatchBarrierToken matchBarrierToken = new MatchBarrierToken(token);
+
+        // Remove a sync barrier token from the queue.
+        // If the queue is no longer stalled by a barrier then wake it.
+        synchronized (this) {
+            boolean removed;
+            Message first = mPriorityQueue.peek();
+
+            removed = mPriorityQueue.findOrRemoveMessages(null, 0, null, null, 0,
+                    matchBarrierToken, true);
+            if (removed && first != null) {
+                // If the loop is quitting then it is already awake.
+                // We can assume mPtr != 0 when mQuitting is false.
+                if (first.target == null && first.arg1 == token && !mQuitting) {
+                    nativeWake(mPtr);
+                }
+            } else if (!removed) {
+                throw new IllegalStateException("The specified message queue synchronization "
+                        + " barrier token has not been posted or has already been removed.");
+            }
+        }
+    }
+
+    boolean enqueueMessage(Message msg, long when) {
+        if (msg.target == null) {
+            throw new IllegalArgumentException("Message must have a target.");
+        }
+
+        return enqueueMessageUnchecked(msg, when);
+    }
+
+    boolean enqueueMessageUnchecked(Message msg, long when) {
+        synchronized (this) {
+            if (mQuitting) {
+                IllegalStateException e = new IllegalStateException(
+                        msg.target + " sending message to a Handler on a dead thread");
+                Log.w(TAG, e.getMessage(), e);
+                msg.recycle();
+                return false;
+            }
+
+            if (msg.isInUse()) {
+                throw new IllegalStateException(msg + " This message is already in use.");
+            }
+
+            msg.markInUse();
+            msg.when = when;
+            msg.mInsertSeq = when != 0 ? mNextInsertSeq++ : mNextFrontInsertSeq--;
+            if (DEBUG) Log.v(TAG, "Enqueue message: " + msg);
+            boolean needWake;
+            boolean isBarrier = msg.target == null;
+            Message first = peekEarliestMessage();
+
+            if (priorityQueuesAreEmpty() || when == 0 || when < first.when) {
+                needWake = mBlocked && !isBarrier;
+            } else {
+                Message firstNonAsyncMessage =
+                        first.isAsynchronous() ? mPriorityQueue.peek() : first;
+
+                needWake = mBlocked && firstNonAsyncMessage != null
+                        && firstNonAsyncMessage.target == null && msg.isAsynchronous();
+            }
+
+            if (msg.isAsynchronous()) {
+                mAsyncPriorityQueue.add(msg);
+            } else {
+                mPriorityQueue.add(msg);
+            }
+
+            // We can assume mPtr != 0 because mQuitting is false.
+            if (needWake) {
+                nativeWake(mPtr);
+            }
+        }
+        return true;
+    }
+
+    @GuardedBy("this")
+    boolean findOrRemoveMessages(Handler h, int what, Object object, Runnable r, long when,
+                MessageHeap.MessageHeapCompare compare, boolean removeMatches) {
+        boolean found = mPriorityQueue.findOrRemoveMessages(h, what, object, r, when, compare,
+                removeMatches);
+        boolean foundAsync = mAsyncPriorityQueue.findOrRemoveMessages(h, what, object, r, when,
+                compare, removeMatches);
+        return found || foundAsync;
+    }
+
+    private static class MatchHandlerWhatAndObject extends MessageHeap.MessageHeapCompare {
+        @Override
+        public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
+                long when) {
+            if (m.target == h && m.what == what && (object == null || m.obj == object)) {
+                return true;
+            }
+            return false;
+        }
+    }
+    private static final MatchHandlerWhatAndObject sMatchHandlerWhatAndObject =
+            new MatchHandlerWhatAndObject();
+
+    boolean hasMessages(Handler h, int what, Object object) {
+        if (h == null) {
+            return false;
+        }
+
+        synchronized (this) {
+            return findOrRemoveMessages(h, what, object, null, 0, sMatchHandlerWhatAndObject,
+                    false);
+        }
+    }
+
+    private static class MatchHandlerWhatAndObjectEquals extends MessageHeap.MessageHeapCompare {
+        @Override
+        public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
+                long when) {
+            if (m.target == h && m.what == what && (object == null || object.equals(m.obj))) {
+                return true;
+            }
+            return false;
+        }
+    }
+    private static final MatchHandlerWhatAndObjectEquals sMatchHandlerWhatAndObjectEquals =
+            new MatchHandlerWhatAndObjectEquals();
+    boolean hasEqualMessages(Handler h, int what, Object object) {
+        if (h == null) {
+            return false;
+        }
+
+        synchronized (this) {
+            return findOrRemoveMessages(h, what, object, null, 0,
+                    sMatchHandlerWhatAndObjectEquals, false);
+        }
+    }
+
+    private static class MatchHandlerRunnableAndObject extends MessageHeap.MessageHeapCompare {
+        @Override
+        public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
+                long when) {
+            if (m.target == h && m.callback == r && (object == null || m.obj == object)) {
+                return true;
+            }
+            return false;
+        }
+    }
+    private static final MatchHandlerRunnableAndObject sMatchHandlerRunnableAndObject =
+            new MatchHandlerRunnableAndObject();
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    boolean hasMessages(Handler h, Runnable r, Object object) {
+        if (h == null) {
+            return false;
+        }
+
+        synchronized (this) {
+            return findOrRemoveMessages(h, -1, object, r, 0, sMatchHandlerRunnableAndObject,
+                    false);
+        }
+    }
+
+    private static class MatchHandler extends MessageHeap.MessageHeapCompare {
+        @Override
+        public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
+                long when) {
+            if (m.target == h) {
+                return true;
+            }
+            return false;
+        }
+    }
+    private static final MatchHandler sMatchHandler = new MatchHandler();
+    boolean hasMessages(Handler h) {
+        if (h == null) {
+            return false;
+        }
+
+        synchronized (this) {
+            return findOrRemoveMessages(h, -1, null, null, 0, sMatchHandler, false);
+        }
+    }
+
+    void removeMessages(Handler h, int what, Object object) {
+        if (h == null) {
+            return;
+        }
+
+        synchronized (this) {
+            findOrRemoveMessages(h, what, object, null, 0, sMatchHandlerWhatAndObject, true);
+        }
+    }
+
+    void removeEqualMessages(Handler h, int what, Object object) {
+        if (h == null) {
+            return;
+        }
+
+        synchronized (this) {
+            findOrRemoveMessages(h, what, object, null, 0, sMatchHandlerWhatAndObjectEquals, true);
+        }
+    }
+
+    void removeMessages(Handler h, Runnable r, Object object) {
+        if (h == null || r == null) {
+            return;
+        }
+
+        synchronized (this) {
+            findOrRemoveMessages(h, -1, object, r, 0, sMatchHandlerRunnableAndObject, true);
+        }
+    }
+
+    private static class MatchHandlerRunnableAndObjectEquals
+            extends MessageHeap.MessageHeapCompare {
+        @Override
+        public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
+                long when) {
+            if (m.target == h && m.callback == r && (object == null || object.equals(m.obj))) {
+                return true;
+            }
+            return false;
+        }
+    }
+    private static final MatchHandlerRunnableAndObjectEquals sMatchHandlerRunnableAndObjectEquals =
+            new MatchHandlerRunnableAndObjectEquals();
+    void removeEqualMessages(Handler h, Runnable r, Object object) {
+        if (h == null || r == null) {
+            return;
+        }
+
+        synchronized (this) {
+            findOrRemoveMessages(h, -1, object, r, 0, sMatchHandlerRunnableAndObjectEquals, true);
+        }
+    }
+
+    private static class MatchHandlerAndObject extends MessageHeap.MessageHeapCompare {
+        @Override
+        public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
+                long when) {
+            if (m.target == h && (object == null || m.obj == object)) {
+                return true;
+            }
+            return false;
+        }
+    }
+    private static final MatchHandlerAndObject sMatchHandlerAndObject = new MatchHandlerAndObject();
+    void removeCallbacksAndMessages(Handler h, Object object) {
+        if (h == null) {
+            return;
+        }
+
+        synchronized (this) {
+            findOrRemoveMessages(h, -1, object, null, 0, sMatchHandlerAndObject, true);
+        }
+    }
+
+    private static class MatchHandlerAndObjectEquals extends MessageHeap.MessageHeapCompare {
+        @Override
+        public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
+                long when) {
+            if (m.target == h && (object == null || object.equals(m.obj))) {
+                return true;
+            }
+            return false;
+        }
+    }
+    private static final MatchHandlerAndObjectEquals sMatchHandlerAndObjectEquals =
+            new MatchHandlerAndObjectEquals();
+    void removeCallbacksAndEqualMessages(Handler h, Object object) {
+        if (h == null) {
+            return;
+        }
+
+        synchronized (this) {
+            findOrRemoveMessages(h, -1, object, null, 0, sMatchHandlerAndObjectEquals, true);
+        }
+    }
+
+    @GuardedBy("this")
+    private void removeAllMessagesLocked() {
+        mPriorityQueue.removeAll();
+        mAsyncPriorityQueue.removeAll();
+    }
+
+    private static class MatchAllFutureMessages extends MessageHeap.MessageHeapCompare {
+        @Override
+        public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
+                long when) {
+            if (m.when > when) {
+                return true;
+            }
+            return false;
+        }
+    }
+    private static final MatchAllFutureMessages sMatchAllFutureMessages =
+            new MatchAllFutureMessages();
+    @GuardedBy("this")
+    private void removeAllFutureMessagesLocked() {
+        findOrRemoveMessages(null, -1, null, null, SystemClock.uptimeMillis(),
+                sMatchAllFutureMessages, true);
+    }
+
+    int dumpPriorityQueue(Printer pw, String prefix, Handler h, MessageHeap priorityQueue) {
+        int n = 0;
+        long now = SystemClock.uptimeMillis();
+        for (int i = 0; i < priorityQueue.numElements(); i++) {
+            Message m = priorityQueue.getMessageAt(i);
+            if (h == null && h == m.target) {
+                pw.println(prefix + "Message " + n + ": " + m.toString(now));
+                n++;
+            }
+        }
+        return n;
+    }
+
+    void dumpPriorityQueue(ProtoOutputStream proto, MessageHeap priorityQueue) {
+        for (int i = 0; i < priorityQueue.numElements(); i++) {
+            Message m = priorityQueue.getMessageAt(i);
+            m.dumpDebug(proto, MessageQueueProto.MESSAGES);
+        }
+    }
+
+    void dump(Printer pw, String prefix, Handler h) {
+        synchronized (this) {
+            pw.println(prefix + "(MessageQueue is using Locked implementation)");
+            long now = SystemClock.uptimeMillis();
+            int n = dumpPriorityQueue(pw, prefix, h, mPriorityQueue);
+            n += dumpPriorityQueue(pw, prefix, h, mAsyncPriorityQueue);
+            pw.println(prefix + "(Total messages: " + n + ", polling=" + isPollingLocked()
+                    + ", quitting=" + mQuitting + ")");
+        }
+    }
+
+    void dumpDebug(ProtoOutputStream proto, long fieldId) {
+        final long messageQueueToken = proto.start(fieldId);
+        synchronized (this) {
+            dumpPriorityQueue(proto, mPriorityQueue);
+            dumpPriorityQueue(proto, mAsyncPriorityQueue);
+            proto.write(MessageQueueProto.IS_POLLING_LOCKED, isPollingLocked());
+            proto.write(MessageQueueProto.IS_QUITTING, mQuitting);
+        }
+        proto.end(messageQueueToken);
+    }
+
+    /**
+     * Callback interface for discovering when a thread is going to block
+     * waiting for more messages.
+     */
+    public static interface IdleHandler {
+        /**
+         * Called when the message queue has run out of messages and will now
+         * wait for more.  Return true to keep your idle handler active, false
+         * to have it removed.  This may be called if there are still messages
+         * pending in the queue, but they are all scheduled to be dispatched
+         * after the current time.
+         */
+        boolean queueIdle();
+    }
+
+    /**
+     * A listener which is invoked when file descriptor related events occur.
+     */
+    public interface OnFileDescriptorEventListener {
+        /**
+         * File descriptor event: Indicates that the file descriptor is ready for input
+         * operations, such as reading.
+         * <p>
+         * The listener should read all available data from the file descriptor
+         * then return <code>true</code> to keep the listener active or <code>false</code>
+         * to remove the listener.
+         * </p><p>
+         * In the case of a socket, this event may be generated to indicate
+         * that there is at least one incoming connection that the listener
+         * should accept.
+         * </p><p>
+         * This event will only be generated if the {@link #EVENT_INPUT} event mask was
+         * specified when the listener was added.
+         * </p>
+         */
+        public static final int EVENT_INPUT = 1 << 0;
+
+        /**
+         * File descriptor event: Indicates that the file descriptor is ready for output
+         * operations, such as writing.
+         * <p>
+         * The listener should write as much data as it needs.  If it could not
+         * write everything at once, then it should return <code>true</code> to
+         * keep the listener active.  Otherwise, it should return <code>false</code>
+         * to remove the listener then re-register it later when it needs to write
+         * something else.
+         * </p><p>
+         * This event will only be generated if the {@link #EVENT_OUTPUT} event mask was
+         * specified when the listener was added.
+         * </p>
+         */
+        public static final int EVENT_OUTPUT = 1 << 1;
+
+        /**
+         * File descriptor event: Indicates that the file descriptor encountered a
+         * fatal error.
+         * <p>
+         * File descriptor errors can occur for various reasons.  One common error
+         * is when the remote peer of a socket or pipe closes its end of the connection.
+         * </p><p>
+         * This event may be generated at any time regardless of whether the
+         * {@link #EVENT_ERROR} event mask was specified when the listener was added.
+         * </p>
+         */
+        public static final int EVENT_ERROR = 1 << 2;
+
+        /** @hide */
+        @Retention(RetentionPolicy.SOURCE)
+        @IntDef(flag = true, prefix = { "EVENT_" }, value = {
+                EVENT_INPUT,
+                EVENT_OUTPUT,
+                EVENT_ERROR
+        })
+        public @interface Events {}
+
+        /**
+         * Called when a file descriptor receives events.
+         *
+         * @param fd The file descriptor.
+         * @param events The set of events that occurred: a combination of the
+         * {@link #EVENT_INPUT}, {@link #EVENT_OUTPUT}, and {@link #EVENT_ERROR} event masks.
+         * @return The new set of events to watch, or 0 to unregister the listener.
+         *
+         * @see #EVENT_INPUT
+         * @see #EVENT_OUTPUT
+         * @see #EVENT_ERROR
+         */
+        @Events int onFileDescriptorEvents(@NonNull FileDescriptor fd, @Events int events);
+    }
+
+    private static final class FileDescriptorRecord {
+        public final FileDescriptor mDescriptor;
+        public int mEvents;
+        public OnFileDescriptorEventListener mListener;
+        public int mSeq;
+
+        public FileDescriptorRecord(FileDescriptor descriptor,
+                int events, OnFileDescriptorEventListener listener) {
+            mDescriptor = descriptor;
+            mEvents = events;
+            mListener = listener;
+        }
+    }
+}
diff --git a/core/java/android/os/Message.java b/core/java/android/os/Message.java
index 161951e..a1db9be 100644
--- a/core/java/android/os/Message.java
+++ b/core/java/android/os/Message.java
@@ -126,6 +126,10 @@
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
     public long when;
 
+    /** @hide */
+    @SuppressWarnings("unused")
+    public long mInsertSeq;
+
     /*package*/ Bundle data;
 
     @UnsupportedAppUsage
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 136c45d..47096db 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -434,7 +434,6 @@
     @RavenwoodThrow
     private static native void nativeWriteStrongBinder(long nativePtr, IBinder val);
     @FastNative
-    @RavenwoodThrow
     private static native void nativeWriteFileDescriptor(long nativePtr, FileDescriptor val);
 
     private static native byte[] nativeCreateByteArray(long nativePtr);
@@ -456,7 +455,6 @@
     @RavenwoodThrow
     private static native IBinder nativeReadStrongBinder(long nativePtr);
     @FastNative
-    @RavenwoodThrow
     private static native FileDescriptor nativeReadFileDescriptor(long nativePtr);
 
     private static native long nativeCreate();
diff --git a/core/java/android/os/ParcelFileDescriptor.java b/core/java/android/os/ParcelFileDescriptor.java
index 71957ee..4cc057a 100644
--- a/core/java/android/os/ParcelFileDescriptor.java
+++ b/core/java/android/os/ParcelFileDescriptor.java
@@ -340,7 +340,6 @@
         return pfd;
     }
 
-    @RavenwoodReplace
     private static FileDescriptor openInternal(File file, int mode) throws FileNotFoundException {
         if ((mode & MODE_WRITE_ONLY) != 0 && (mode & MODE_APPEND) == 0
                 && (mode & MODE_TRUNCATE) == 0 && ((mode & MODE_READ_ONLY) == 0)
@@ -364,24 +363,16 @@
         }
     }
 
-    private static FileDescriptor openInternal$ravenwood(File file, int mode)
-            throws FileNotFoundException {
-        try {
-            return native_open$ravenwood(file, mode);
-        } catch (FileNotFoundException e) {
-            throw e;
-        } catch (IOException e) {
-            throw new FileNotFoundException(e.getMessage());
-        }
-    }
-
     @RavenwoodReplace
     private static void closeInternal(FileDescriptor fd) {
         IoUtils.closeQuietly(fd);
     }
 
     private static void closeInternal$ravenwood(FileDescriptor fd) {
-        native_close$ravenwood(fd);
+        try {
+            Os.close(fd);
+        } catch (ErrnoException ignored) {
+        }
     }
 
     /**
@@ -741,7 +732,6 @@
      * Return the total size of the file representing this fd, as determined by
      * {@code stat()}. Returns -1 if the fd is not a file.
      */
-    @RavenwoodThrow(reason = "Os.readlink() and Os.stat()")
     public long getStatSize() {
         if (mWrapped != null) {
             return mWrapped.getStatSize();
@@ -1275,32 +1265,19 @@
         }
     }
 
-    // These native methods are currently only implemented by Ravenwood, as it's the only
-    // mechanism we have to jump to our RavenwoodNativeSubstitutionClass
-    private static native void native_setFdInt$ravenwood(FileDescriptor fd, int fdInt);
-    private static native int native_getFdInt$ravenwood(FileDescriptor fd);
-    private static native FileDescriptor native_open$ravenwood(File file, int pfdMode)
-            throws IOException;
-    private static native void native_close$ravenwood(FileDescriptor fd);
+    private static native void setFdInt$ravenwood(FileDescriptor fd, int fdInt);
+    private static native int getFdInt$ravenwood(FileDescriptor fd);
 
     @RavenwoodReplace
     private static void setFdInt(FileDescriptor fd, int fdInt) {
         fd.setInt$(fdInt);
     }
 
-    private static void setFdInt$ravenwood(FileDescriptor fd, int fdInt) {
-        native_setFdInt$ravenwood(fd, fdInt);
-    }
-
     @RavenwoodReplace
     private static int getFdInt(FileDescriptor fd) {
         return fd.getInt$();
     }
 
-    private static int getFdInt$ravenwood(FileDescriptor fd) {
-        return native_getFdInt$ravenwood(fd);
-    }
-
     @RavenwoodReplace
     private void setFdOwner(FileDescriptor fd) {
         IoUtils.setFdOwner(fd, this);
@@ -1318,7 +1295,6 @@
     private int acquireRawFd$ravenwood(FileDescriptor fd) {
         // FD owners currently unsupported under Ravenwood; return FD directly
         return getFdInt(fd);
-
     }
 
     @RavenwoodReplace
diff --git a/core/java/android/os/PowerManagerInternal.java b/core/java/android/os/PowerManagerInternal.java
index df353e5..1fb7937 100644
--- a/core/java/android/os/PowerManagerInternal.java
+++ b/core/java/android/os/PowerManagerInternal.java
@@ -104,8 +104,10 @@
      * This method must only be called by the window manager.
      *
      * @param brightness The overridden brightness, or Float.NaN to disable the override.
+     * @param tag Source identifier of the app window that requests the override.
      */
-    public abstract void setScreenBrightnessOverrideFromWindowManager(float brightness);
+    public abstract void setScreenBrightnessOverrideFromWindowManager(
+            float brightness, CharSequence tag);
 
     /**
      * Used by the window manager to override the user activity timeout based on the
@@ -137,11 +139,16 @@
      * @param screenState The overridden screen state, or {@link Display#STATE_UNKNOWN}
      * to disable the override.
      * @param reason The reason for overriding the screen state.
-     * @param screenBrightness The overridden screen brightness, or
-     * {@link PowerManager#BRIGHTNESS_DEFAULT} to disable the override.
+     * @param screenBrightnessFloat The overridden screen brightness between
+     * {@link PowerManager#BRIGHTNESS_MIN} and {@link PowerManager#BRIGHTNESS_MAX}, or
+     * {@link PowerManager#BRIGHTNESS_INVALID_FLOAT} if screenBrightnessInt should be used instead.
+     * @param screenBrightnessInt The overridden screen brightness between 1 and 255, or
+     * {@link PowerManager#BRIGHTNESS_DEFAULT} to disable the override. Not used if
+     *                            screenBrightnessFloat is provided (is not NaN).
      */
     public abstract void setDozeOverrideFromDreamManager(
-            int screenState, @Display.StateReason int reason, int screenBrightness);
+            int screenState, @Display.StateReason int reason, float screenBrightnessFloat,
+            int screenBrightnessInt);
 
     /**
      * Used by sidekick manager to tell the power manager if it shouldn't change the display state
diff --git a/core/java/android/os/SemiConcurrentMessageQueue/MessageQueue.java b/core/java/android/os/SemiConcurrentMessageQueue/MessageQueue.java
index 967332f..79f229a 100644
--- a/core/java/android/os/SemiConcurrentMessageQueue/MessageQueue.java
+++ b/core/java/android/os/SemiConcurrentMessageQueue/MessageQueue.java
@@ -209,7 +209,7 @@
     private volatile long mNextInsertSeqValue = 0;
     /*
      * The exception to the FIFO order rule is sendMessageAtFrontOfQueue().
-     * Those messages must be in LIFO order - SIGH.
+     * Those messages must be in LIFO order.
      * Decrements on each front of queue insert.
      */
     private static final VarHandle sNextFrontInsertSeq;
diff --git a/core/java/android/os/ServiceManager.java b/core/java/android/os/ServiceManager.java
index 0be2d3e3..8aec7eb 100644
--- a/core/java/android/os/ServiceManager.java
+++ b/core/java/android/os/ServiceManager.java
@@ -277,7 +277,7 @@
             if (service != null) {
                 return service;
             } else {
-                return Binder.allowBlocking(getIServiceManager().checkService(name));
+                return Binder.allowBlocking(getIServiceManager().checkService(name).getBinder());
             }
         } catch (RemoteException e) {
             Log.e(TAG, "error in checkService", e);
@@ -425,7 +425,7 @@
     private static IBinder rawGetService(String name) throws RemoteException {
         final long start = sStatLogger.getTime();
 
-        final IBinder binder = getIServiceManager().getService(name);
+        final IBinder binder = getIServiceManager().getService2(name).getBinder();
 
         final int time = (int) sStatLogger.logDurationStat(Stats.GET_SERVICE, start);
 
diff --git a/core/java/android/os/ServiceManagerNative.java b/core/java/android/os/ServiceManagerNative.java
index 7b91dd5..5a9c878 100644
--- a/core/java/android/os/ServiceManagerNative.java
+++ b/core/java/android/os/ServiceManagerNative.java
@@ -57,13 +57,19 @@
         return mRemote;
     }
 
+    // TODO(b/355394904): This function has been deprecated, please use getService2 instead.
     @UnsupportedAppUsage
     public IBinder getService(String name) throws RemoteException {
         // Same as checkService (old versions of servicemanager had both methods).
-        return mServiceManager.checkService(name);
+        return checkService(name).getBinder();
     }
 
-    public IBinder checkService(String name) throws RemoteException {
+    public Service getService2(String name) throws RemoteException {
+        // Same as checkService (old versions of servicemanager had both methods).
+        return checkService(name);
+    }
+
+    public Service checkService(String name) throws RemoteException {
         return mServiceManager.checkService(name);
     }
 
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index 292e6bd..50b73a9 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -126,20 +126,18 @@
  * method:
  *
  * <pre>
- * public void onCreate() {
- *     StrictMode.setThreadPolicy(new {@link ThreadPolicy.Builder StrictMode.ThreadPolicy.Builder}()
- *             .detectDiskReads()
- *             .detectDiskWrites()
- *             .detectNetwork()   // or .detectAll() for all detectable problems
- *             .penaltyLog()
- *             .build());
- *     StrictMode.setVmPolicy(new {@link VmPolicy.Builder StrictMode.VmPolicy.Builder}()
- *             .detectLeakedSqlLiteObjects()
- *             .detectLeakedClosableObjects()
- *             .penaltyLog()
- *             .penaltyDeath()
- *             .build());
- *     super.onCreate();
+ * override fun onCreate(savedInstanceState: Bundle?) {
+ *     super.onCreate(savedInstanceState)
+ *     StrictMode.setThreadPolicy(
+ *         StrictMode.ThreadPolicy.Builder()
+ *         .detectAll()
+ *         .build()
+ *     )
+ *     StrictMode.setVmPolicy(
+ *         StrictMode.VmPolicy.Builder()
+ *         .detectAll()
+ *         .build()
+ *     )
  * }
  * </pre>
  *
@@ -354,7 +352,7 @@
     public static final int NETWORK_POLICY_LOG = 1;
     /** {@hide} */
     public static final int NETWORK_POLICY_REJECT = 2;
-  
+
     /**
      * Detect explicit calls to {@link Runtime#gc()}.
      */
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 3aa42c6..392b6eb 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -6442,7 +6442,11 @@
      */
     @UnsupportedAppUsage
     public int getUserSerialNumber(@UserIdInt int userId) {
-        if (android.multiuser.Flags.cacheUserSerialNumber()) {
+        // Read only flag should is to fix early access to this API
+        // cacheUserSerialNumber to be removed after the
+        // cacheUserSerialNumberReadOnly is fully rolled out
+        if (android.multiuser.Flags.cacheUserSerialNumberReadOnly()
+                || android.multiuser.Flags.cacheUserSerialNumber()) {
             // System user serial number is always 0, and it always exists.
             // There is no need to call binder for that.
             if (userId == UserHandle.USER_SYSTEM) {
diff --git a/core/java/android/os/VibrationAttributes.java b/core/java/android/os/VibrationAttributes.java
index 9df5b85..da863e5 100644
--- a/core/java/android/os/VibrationAttributes.java
+++ b/core/java/android/os/VibrationAttributes.java
@@ -16,6 +16,9 @@
 
 package android.os;
 
+import static android.os.vibrator.Flags.FLAG_VIBRATION_ATTRIBUTE_IME_USAGE_API;
+
+import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -55,6 +58,7 @@
             USAGE_PHYSICAL_EMULATION,
             USAGE_RINGTONE,
             USAGE_TOUCH,
+            USAGE_IME_FEEDBACK,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface Usage {}
@@ -136,6 +140,12 @@
      */
     public static final int USAGE_ACCESSIBILITY = 0x40 | USAGE_CLASS_FEEDBACK;
     /**
+     * Usage value to use for input method editor (IME) haptic feedback.
+     */
+    @FlaggedApi(FLAG_VIBRATION_ATTRIBUTE_IME_USAGE_API)
+    public static final int USAGE_IME_FEEDBACK = 0x50 | USAGE_CLASS_FEEDBACK;
+
+    /**
      * Usage value to use for media vibrations, such as music, movie, soundtrack, animations, games,
      * or any interactive media that isn't for touch feedback specifically.
      */
@@ -174,7 +184,6 @@
             FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF,
             FLAG_INVALIDATE_SETTINGS_CACHE,
             FLAG_PIPELINED_EFFECT,
-            FLAG_BYPASS_USER_VIBRATION_INTENSITY_SCALE
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface Flag{}
@@ -228,31 +237,12 @@
     public static final int FLAG_PIPELINED_EFFECT = 1 << 3;
 
     /**
-     * Flag requesting that this vibration effect to be played without applying the user
-     * intensity setting to scale the vibration.
-     *
-     * <p>The user setting is still applied to enable/disable the vibration, but the vibration
-     * effect strength will not be scaled based on the enabled setting value.
-     *
-     * <p>This is intended to be used on scenarios where the system needs to enforce a specific
-     * strength for the vibration effect, regardless of the user preference. Only privileged apps
-     * can ignore user settings, and this flag will be ignored otherwise.
-     *
-     * <p>If you need to bypass the user setting when it's disabling vibrations then this also
-     * needs the flag {@link #FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF} to be set.
-     *
-     * @hide
-     */
-    public static final int FLAG_BYPASS_USER_VIBRATION_INTENSITY_SCALE = 1 << 4;
-
-    /**
      * All flags supported by vibrator service, update it when adding new flag.
      * @hide
      */
     public static final int FLAG_ALL_SUPPORTED =
             FLAG_BYPASS_INTERRUPTION_POLICY | FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF
-                    | FLAG_INVALIDATE_SETTINGS_CACHE | FLAG_PIPELINED_EFFECT
-                    | FLAG_BYPASS_USER_VIBRATION_INTENSITY_SCALE;
+                    | FLAG_INVALIDATE_SETTINGS_CACHE | FLAG_PIPELINED_EFFECT;
 
     /** Creates a new {@link VibrationAttributes} instance with given usage. */
     public static @NonNull VibrationAttributes createForUsage(@Usage int usage) {
@@ -349,6 +339,7 @@
             case USAGE_RINGTONE:
                 return AudioAttributes.USAGE_NOTIFICATION_RINGTONE;
             case USAGE_TOUCH:
+            case USAGE_IME_FEEDBACK:
                 return AudioAttributes.USAGE_ASSISTANCE_SONIFICATION;
             case USAGE_ALARM:
                 return AudioAttributes.USAGE_ALARM;
@@ -447,6 +438,8 @@
                 return "PHYSICAL_EMULATION";
             case USAGE_HARDWARE_FEEDBACK:
                 return "HARDWARE_FEEDBACK";
+            case USAGE_IME_FEEDBACK:
+                return "IME";
             default:
                 return "unknown usage " + usage;
         }
diff --git a/core/java/android/os/VibrationEffect.java b/core/java/android/os/VibrationEffect.java
index efbd96b..f3ef9e1 100644
--- a/core/java/android/os/VibrationEffect.java
+++ b/core/java/android/os/VibrationEffect.java
@@ -16,18 +16,25 @@
 
 package android.os;
 
+import static android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS;
+
+import android.annotation.FlaggedApi;
 import android.annotation.FloatRange;
 import android.annotation.IntDef;
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
 import android.annotation.TestApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ContentResolver;
 import android.content.Context;
+import android.hardware.vibrator.IVibrator;
 import android.hardware.vibrator.V1_0.EffectStrength;
 import android.hardware.vibrator.V1_3.Effect;
 import android.net.Uri;
+import android.os.vibrator.Flags;
 import android.os.vibrator.PrebakedSegment;
 import android.os.vibrator.PrimitiveSegment;
 import android.os.vibrator.RampSegment;
@@ -46,6 +53,7 @@
 import java.util.Locale;
 import java.util.Objects;
 import java.util.StringJoiner;
+import java.util.function.BiFunction;
 
 /**
  * A VibrationEffect describes a haptic effect to be performed by a {@link Vibrator}.
@@ -53,6 +61,9 @@
  * <p>These effects may be any number of things, from single shot vibrations to complex waveforms.
  */
 public abstract class VibrationEffect implements Parcelable {
+    private static final int PARCEL_TOKEN_COMPOSED = 1;
+    private static final int PARCEL_TOKEN_VENDOR_EFFECT = 2;
+
     // Stevens' coefficient to scale the perceived vibration intensity.
     private static final float SCALE_GAMMA = 0.65f;
     // If a vibration is playing for longer than 1s, it's probably not haptic feedback
@@ -316,6 +327,31 @@
     }
 
     /**
+     * Create a vendor-defined vibration effect.
+     *
+     * <p>Vendor effects offer more flexibility for accessing vendor-specific vibrator capabilities,
+     * enabling control over any vibration parameter and more generic vibration waveforms for apps
+     * provided by the device vendor.
+     *
+     * <p>This requires hardware-specific implementation of the effect and will not have any
+     * platform fallback support.
+     *
+     * @param effect An opaque representation of the vibration effect which can also be serialized.
+     * @return The desired effect.
+     * @hide
+     */
+    @NonNull
+    @SystemApi
+    @FlaggedApi(FLAG_VENDOR_VIBRATION_EFFECTS)
+    @RequiresPermission(android.Manifest.permission.VIBRATE_VENDOR_EFFECTS)
+    public static VibrationEffect createVendorEffect(@NonNull PersistableBundle effect) {
+        VibrationEffect vendorEffect = new VendorEffect(effect, VendorEffect.DEFAULT_STRENGTH,
+                VendorEffect.DEFAULT_SCALE);
+        vendorEffect.validate();
+        return vendorEffect;
+    }
+
+    /**
      * Get a predefined vibration effect.
      *
      * <p>Predefined effects are a set of common vibration effects that should be identical,
@@ -508,7 +544,7 @@
      * Gets the estimated duration of the vibration in milliseconds.
      *
      * <p>For effects without a defined end (e.g. a Waveform with a non-negative repeat index), this
-     * returns Long.MAX_VALUE. For effects with an unknown duration (e.g. Prebaked effects where
+     * returns Long.MAX_VALUE. For effects with an unknown duration (e.g. predefined effects where
      * the length is device and potentially run-time dependent), this returns -1.
      *
      * @hide
@@ -550,7 +586,19 @@
      *
      * @hide
      */
-    public abstract <T extends VibrationEffect> T resolve(int defaultAmplitude);
+    @NonNull
+    public abstract VibrationEffect resolve(int defaultAmplitude);
+
+    /**
+     * Applies given effect strength to predefined and vendor-specific effects.
+     *
+     * @param effectStrength new effect strength to be applied, one of
+     *                       VibrationEffect.EFFECT_STRENGTH_*.
+     * @return this if there is no change, or a copy of this effect with new strength otherwise
+     * @hide
+     */
+    @NonNull
+    public abstract VibrationEffect applyEffectStrength(int effectStrength);
 
     /**
      * Scale the vibration effect intensity with the given constraints.
@@ -562,7 +610,20 @@
      *
      * @hide
      */
-    public abstract <T extends VibrationEffect> T scale(float scaleFactor);
+    @NonNull
+    public abstract VibrationEffect scale(float scaleFactor);
+
+    /**
+     * Performs a linear scaling on the effect intensity with the given factor.
+     *
+     * @param scaleFactor scale factor to be applied to the intensity. Values within [0,1) will
+     *                    scale down the intensity, values larger than 1 will scale up
+     * @return this if there is no scaling to be done, or a copy of this effect with scaled
+     *         vibration intensity otherwise
+     * @hide
+     */
+    @NonNull
+    public abstract VibrationEffect scaleLinearly(float scaleFactor);
 
     /**
      * Ensures that the effect is repeating indefinitely or not. This is a lossy operation and
@@ -651,38 +712,26 @@
 
     /** @hide */
     public static String effectIdToString(int effectId) {
-        switch (effectId) {
-            case EFFECT_CLICK:
-                return "CLICK";
-            case EFFECT_TICK:
-                return "TICK";
-            case EFFECT_HEAVY_CLICK:
-                return "HEAVY_CLICK";
-            case EFFECT_DOUBLE_CLICK:
-                return "DOUBLE_CLICK";
-            case EFFECT_POP:
-                return "POP";
-            case EFFECT_THUD:
-                return "THUD";
-            case EFFECT_TEXTURE_TICK:
-                return "TEXTURE_TICK";
-            default:
-                return Integer.toString(effectId);
-        }
+        return switch (effectId) {
+            case EFFECT_CLICK -> "CLICK";
+            case EFFECT_TICK -> "TICK";
+            case EFFECT_HEAVY_CLICK -> "HEAVY_CLICK";
+            case EFFECT_DOUBLE_CLICK -> "DOUBLE_CLICK";
+            case EFFECT_POP -> "POP";
+            case EFFECT_THUD -> "THUD";
+            case EFFECT_TEXTURE_TICK -> "TEXTURE_TICK";
+            default -> Integer.toString(effectId);
+        };
     }
 
     /** @hide */
     public static String effectStrengthToString(int effectStrength) {
-        switch (effectStrength) {
-            case EFFECT_STRENGTH_LIGHT:
-                return "LIGHT";
-            case EFFECT_STRENGTH_MEDIUM:
-                return "MEDIUM";
-            case EFFECT_STRENGTH_STRONG:
-                return "STRONG";
-            default:
-                return Integer.toString(effectStrength);
-        }
+        return switch (effectStrength) {
+            case EFFECT_STRENGTH_LIGHT -> "LIGHT";
+            case EFFECT_STRENGTH_MEDIUM -> "MEDIUM";
+            case EFFECT_STRENGTH_STRONG -> "STRONG";
+            default -> Integer.toString(effectStrength);
+        };
     }
 
     /**
@@ -712,12 +761,15 @@
         private final ArrayList<VibrationEffectSegment> mSegments;
         private final int mRepeatIndex;
 
+        /** @hide */
         Composed(@NonNull Parcel in) {
-            this(in.readArrayList(
-                    VibrationEffectSegment.class.getClassLoader(), VibrationEffectSegment.class),
+            this(Objects.requireNonNull(in.readArrayList(
+                            VibrationEffectSegment.class.getClassLoader(),
+                            VibrationEffectSegment.class)),
                     in.readInt());
         }
 
+        /** @hide */
         Composed(@NonNull VibrationEffectSegment segment) {
             this(Arrays.asList(segment), /* repeatIndex= */ -1);
         }
@@ -844,7 +896,7 @@
             }
             int segmentCount = mSegments.size();
             if (segmentCount > MAX_HAPTIC_FEEDBACK_COMPOSITION_SIZE) {
-                // Vibration has some prebaked or primitive constants, it should be limited to the
+                // Vibration has some predefined or primitive constants, it should be limited to the
                 // max composition size used to classify haptic feedbacks.
                 return false;
             }
@@ -867,34 +919,28 @@
         @NonNull
         @Override
         public Composed resolve(int defaultAmplitude) {
-            int segmentCount = mSegments.size();
-            ArrayList<VibrationEffectSegment> resolvedSegments = new ArrayList<>(segmentCount);
-            for (int i = 0; i < segmentCount; i++) {
-                resolvedSegments.add(mSegments.get(i).resolve(defaultAmplitude));
-            }
-            if (resolvedSegments.equals(mSegments)) {
-                return this;
-            }
-            Composed resolved = new Composed(resolvedSegments, mRepeatIndex);
-            resolved.validate();
-            return resolved;
+            return applyToSegments(VibrationEffectSegment::resolve, defaultAmplitude);
+        }
+
+        /** @hide */
+        @NonNull
+        @Override
+        public VibrationEffect applyEffectStrength(int effectStrength) {
+            return applyToSegments(VibrationEffectSegment::applyEffectStrength, effectStrength);
         }
 
         /** @hide */
         @NonNull
         @Override
         public Composed scale(float scaleFactor) {
-            int segmentCount = mSegments.size();
-            ArrayList<VibrationEffectSegment> scaledSegments = new ArrayList<>(segmentCount);
-            for (int i = 0; i < segmentCount; i++) {
-                scaledSegments.add(mSegments.get(i).scale(scaleFactor));
-            }
-            if (scaledSegments.equals(mSegments)) {
-                return this;
-            }
-            Composed scaled = new Composed(scaledSegments, mRepeatIndex);
-            scaled.validate();
-            return scaled;
+            return applyToSegments(VibrationEffectSegment::scale, scaleFactor);
+        }
+
+        /** @hide */
+        @NonNull
+        @Override
+        public Composed scaleLinearly(float scaleFactor) {
+            return applyToSegments(VibrationEffectSegment::scaleLinearly, scaleFactor);
         }
 
         /** @hide */
@@ -926,10 +972,9 @@
             if (this == o) {
                 return true;
             }
-            if (!(o instanceof Composed)) {
+            if (!(o instanceof Composed other)) {
                 return false;
             }
-            Composed other = (Composed) o;
             return mSegments.equals(other.mSegments) && mRepeatIndex == other.mRepeatIndex;
         }
 
@@ -969,6 +1014,7 @@
 
         @Override
         public void writeToParcel(@NonNull Parcel out, int flags) {
+            out.writeInt(PARCEL_TOKEN_COMPOSED);
             out.writeList(mSegments);
             out.writeInt(mRepeatIndex);
         }
@@ -978,6 +1024,7 @@
                 new Creator<Composed>() {
                     @Override
                     public Composed createFromParcel(Parcel in) {
+                        in.readInt(); // Skip the parcel type token
                         return new Composed(in);
                     }
 
@@ -1011,6 +1058,256 @@
 
             return stepSegment;
         }
+
+        private <T> Composed applyToSegments(
+                BiFunction<VibrationEffectSegment, T, VibrationEffectSegment> function, T param) {
+            int segmentCount = mSegments.size();
+            ArrayList<VibrationEffectSegment> updatedSegments = new ArrayList<>(segmentCount);
+            for (int i = 0; i < segmentCount; i++) {
+                updatedSegments.add(function.apply(mSegments.get(i), param));
+            }
+            if (mSegments.equals(updatedSegments)) {
+                return this;
+            }
+            Composed updated = new Composed(updatedSegments, mRepeatIndex);
+            updated.validate();
+            return updated;
+        }
+    }
+
+    /**
+     * Implementation of {@link VibrationEffect} described by a generic {@link PersistableBundle}
+     * defined by vendors.
+     *
+     * @hide
+     */
+    @TestApi
+    @FlaggedApi(FLAG_VENDOR_VIBRATION_EFFECTS)
+    public static final class VendorEffect extends VibrationEffect {
+        /** @hide */
+        public static final int DEFAULT_STRENGTH = VibrationEffect.EFFECT_STRENGTH_MEDIUM;
+        /** @hide */
+        public static final float DEFAULT_SCALE = 1.0f;
+
+        private final PersistableBundle mVendorData;
+        private final int mEffectStrength;
+        private final float mLinearScale;
+
+        /** @hide */
+        VendorEffect(@NonNull Parcel in) {
+            this(Objects.requireNonNull(
+                    in.readPersistableBundle(VibrationEffect.class.getClassLoader())),
+                    in.readInt(), in.readFloat());
+        }
+
+        /** @hide */
+        public VendorEffect(@NonNull PersistableBundle vendorData, int effectStrength,
+                float linearScale) {
+            mVendorData = vendorData;
+            mEffectStrength = effectStrength;
+            mLinearScale = linearScale;
+        }
+
+        @NonNull
+        public PersistableBundle getVendorData() {
+            return mVendorData;
+        }
+
+        public int getEffectStrength() {
+            return mEffectStrength;
+        }
+
+        public float getLinearScale() {
+            return mLinearScale;
+        }
+
+        /** @hide */
+        @Override
+        @Nullable
+        public long[] computeCreateWaveformOffOnTimingsOrNull() {
+            return null;
+        }
+
+        /** @hide */
+        @Override
+        public void validate() {
+            Preconditions.checkArgument(!mVendorData.isEmpty(),
+                    "Vendor effect bundle must be non-empty");
+        }
+
+        @Override
+        public long getDuration() {
+            return -1; // UNKNOWN
+        }
+
+        /** @hide */
+        @Override
+        public boolean areVibrationFeaturesSupported(@NonNull VibratorInfo vibratorInfo) {
+            return vibratorInfo.hasCapability(IVibrator.CAP_PERFORM_VENDOR_EFFECTS);
+        }
+
+        /** @hide */
+        @Override
+        public boolean isHapticFeedbackCandidate() {
+            return false;
+        }
+
+        /** @hide */
+        @NonNull
+        @Override
+        public VendorEffect resolve(int defaultAmplitude) {
+            return this;
+        }
+
+        /** @hide */
+        @NonNull
+        @Override
+        public VibrationEffect applyEffectStrength(int effectStrength) {
+            if (mEffectStrength == effectStrength) {
+                return this;
+            }
+            VendorEffect updated = new VendorEffect(mVendorData, effectStrength, mLinearScale);
+            updated.validate();
+            return updated;
+        }
+
+        /** @hide */
+        @NonNull
+        @Override
+        public VendorEffect scale(float scaleFactor) {
+            // Vendor effect strength cannot be scaled with this method.
+            return this;
+        }
+
+        /** @hide */
+        @NonNull
+        @Override
+        public VibrationEffect scaleLinearly(float scaleFactor) {
+            if (Float.compare(mLinearScale, scaleFactor) == 0) {
+                return this;
+            }
+            VendorEffect updated = new VendorEffect(mVendorData, mEffectStrength, scaleFactor);
+            updated.validate();
+            return updated;
+        }
+
+        /** @hide */
+        @NonNull
+        @Override
+        public VendorEffect applyRepeatingIndefinitely(boolean wantRepeating, int loopDelayMs) {
+            return this;
+        }
+
+        @Override
+        public boolean equals(@Nullable Object o) {
+            if (this == o) {
+                return true;
+            }
+            if (!(o instanceof VendorEffect other)) {
+                return false;
+            }
+            return mEffectStrength == other.mEffectStrength
+                    && (Float.compare(mLinearScale, other.mLinearScale) == 0)
+                    && isPersistableBundleEquals(mVendorData, other.mVendorData);
+        }
+
+        @Override
+        public int hashCode() {
+            // PersistableBundle does not implement hashCode, so use its size as a shortcut.
+            return Objects.hash(mVendorData.size(), mEffectStrength, mLinearScale);
+        }
+
+        @Override
+        public String toString() {
+            return String.format(Locale.ROOT,
+                    "VendorEffect{vendorData=%s, strength=%s, scale=%.2f}",
+                    mVendorData, effectStrengthToString(mEffectStrength), mLinearScale);
+        }
+
+        /** @hide */
+        @Override
+        public String toDebugString() {
+            return String.format(Locale.ROOT, "vendorEffect=%s, strength=%s, scale=%.2f",
+                    mVendorData.toShortString(), effectStrengthToString(mEffectStrength),
+                    mLinearScale);
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(@NonNull Parcel out, int flags) {
+            out.writeInt(PARCEL_TOKEN_VENDOR_EFFECT);
+            out.writePersistableBundle(mVendorData);
+            out.writeInt(mEffectStrength);
+            out.writeFloat(mLinearScale);
+        }
+
+        /**
+         * Compares two {@link PersistableBundle} objects are equals.
+         */
+        private static boolean isPersistableBundleEquals(
+                PersistableBundle first, PersistableBundle second) {
+            if (first == second) {
+                return true;
+            }
+            if (first == null || second == null || first.size() != second.size()) {
+                return false;
+            }
+            for (String key : first.keySet()) {
+                if (!isPersistableBundleSupportedValueEquals(first.get(key), second.get(key))) {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        /**
+         * Compares two values which type is supported by {@link PersistableBundle}.
+         *
+         * <p>If the type isn't supported. The equality is done by {@link Object#equals(Object)}.
+         */
+        private static boolean isPersistableBundleSupportedValueEquals(
+                Object first, Object second) {
+            if (first == second) {
+                return true;
+            } else if (first == null || second == null
+                    || !first.getClass().equals(second.getClass())) {
+                return false;
+            } else if (first instanceof PersistableBundle) {
+                return isPersistableBundleEquals(
+                        (PersistableBundle) first, (PersistableBundle) second);
+            } else if (first instanceof int[]) {
+                return Arrays.equals((int[]) first, (int[]) second);
+            } else if (first instanceof long[]) {
+                return Arrays.equals((long[]) first, (long[]) second);
+            } else if (first instanceof double[]) {
+                return Arrays.equals((double[]) first, (double[]) second);
+            } else if (first instanceof boolean[]) {
+                return Arrays.equals((boolean[]) first, (boolean[]) second);
+            } else if (first instanceof String[]) {
+                return Arrays.equals((String[]) first, (String[]) second);
+            } else {
+                return Objects.equals(first, second);
+            }
+        }
+
+        @NonNull
+        public static final Creator<VendorEffect> CREATOR =
+                new Creator<VendorEffect>() {
+                    @Override
+                    public VendorEffect createFromParcel(Parcel in) {
+                        in.readInt(); // Skip the parcel type token
+                        return new VendorEffect(in);
+                    }
+
+                    @Override
+                    public VendorEffect[] newArray(int size) {
+                        return new VendorEffect[size];
+                    }
+                };
     }
 
     /**
@@ -1249,7 +1546,9 @@
             if (mRepeatIndex >= 0) {
                 throw new UnreachableAfterRepeatingIndefinitelyException();
             }
-            Composed composed = (Composed) effect;
+            if (!(effect instanceof Composed composed)) {
+                throw new IllegalArgumentException("Can't add vendor effects to composition.");
+            }
             if (composed.getRepeatIndex() >= 0) {
                 // Start repeating from the index relative to the composed waveform.
                 mRepeatIndex = mSegments.size() + composed.getRepeatIndex();
@@ -1285,28 +1584,18 @@
          * @hide
          */
         public static String primitiveToString(@PrimitiveType int id) {
-            switch (id) {
-                case PRIMITIVE_NOOP:
-                    return "NOOP";
-                case PRIMITIVE_CLICK:
-                    return "CLICK";
-                case PRIMITIVE_THUD:
-                    return "THUD";
-                case PRIMITIVE_SPIN:
-                    return "SPIN";
-                case PRIMITIVE_QUICK_RISE:
-                    return "QUICK_RISE";
-                case PRIMITIVE_SLOW_RISE:
-                    return "SLOW_RISE";
-                case PRIMITIVE_QUICK_FALL:
-                    return "QUICK_FALL";
-                case PRIMITIVE_TICK:
-                    return "TICK";
-                case PRIMITIVE_LOW_TICK:
-                    return "LOW_TICK";
-                default:
-                    return Integer.toString(id);
-            }
+            return switch (id) {
+                case PRIMITIVE_NOOP -> "NOOP";
+                case PRIMITIVE_CLICK -> "CLICK";
+                case PRIMITIVE_THUD -> "THUD";
+                case PRIMITIVE_SPIN -> "SPIN";
+                case PRIMITIVE_QUICK_RISE -> "QUICK_RISE";
+                case PRIMITIVE_SLOW_RISE -> "SLOW_RISE";
+                case PRIMITIVE_QUICK_FALL -> "QUICK_FALL";
+                case PRIMITIVE_TICK -> "TICK";
+                case PRIMITIVE_LOW_TICK -> "LOW_TICK";
+                default -> Integer.toString(id);
+            };
         }
     }
 
@@ -1640,7 +1929,17 @@
             new Parcelable.Creator<VibrationEffect>() {
                 @Override
                 public VibrationEffect createFromParcel(Parcel in) {
-                    return new Composed(in);
+                    switch (in.readInt()) {
+                        case PARCEL_TOKEN_COMPOSED:
+                            return new Composed(in);
+                        case PARCEL_TOKEN_VENDOR_EFFECT:
+                            if (Flags.vendorVibrationEffects()) {
+                                return new VendorEffect(in);
+                            } // else fall through
+                        default:
+                            throw new IllegalStateException(
+                                    "Unexpected vibration effect type token in parcel.");
+                    }
                 }
                 @Override
                 public VibrationEffect[] newArray(int size) {
diff --git a/core/java/android/os/Vibrator.java b/core/java/android/os/Vibrator.java
index 71c83f2..161cce0 100644
--- a/core/java/android/os/Vibrator.java
+++ b/core/java/android/os/Vibrator.java
@@ -497,7 +497,27 @@
      */
     @RequiresPermission(android.Manifest.permission.VIBRATE)
     public void vibrate(@NonNull VibrationEffect vibe, @NonNull VibrationAttributes attributes) {
-        vibrate(Process.myUid(), mPackageName, vibe, null, attributes);
+        vibrate(vibe, attributes, null);
+    }
+
+    /**
+     * Vibrate with a given effect.
+     *
+     * <p>The app should be in the foreground for the vibration to happen. Background apps should
+     * specify a ringtone, notification or alarm usage in order to vibrate.</p>
+     *
+     * @param vibe       {@link VibrationEffect} describing the vibration to be performed.
+     * @param attributes {@link VibrationAttributes} corresponding to the vibration. For example,
+     *                   specify {@link VibrationAttributes#USAGE_ALARM} for alarm vibrations or
+     *                   {@link VibrationAttributes#USAGE_RINGTONE} for vibrations associated with
+     *                   incoming calls.
+     * @param reason     the reason for this vibration, used for debugging purposes.
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.VIBRATE)
+    public void vibrate(@NonNull VibrationEffect vibe,
+            @NonNull VibrationAttributes attributes, String reason) {
+        vibrate(Process.myUid(), mPackageName, vibe, reason, attributes);
     }
 
     /**
diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java
index b1ef05a..6a2daea 100644
--- a/core/java/android/os/ZygoteProcess.java
+++ b/core/java/android/os/ZygoteProcess.java
@@ -1102,7 +1102,6 @@
     /**
      * Instructs the zygote to pre-load the application code for the given Application.
      * Only the app zygote supports this function.
-     * TODO preloadPackageForAbi() can probably be removed and the callers an use this instead.
      */
     public boolean preloadApp(ApplicationInfo appInfo, String abi)
             throws ZygoteStartFailedEx, IOException {
@@ -1130,39 +1129,6 @@
     }
 
     /**
-     * Instructs the zygote to pre-load the classes and native libraries at the given paths
-     * for the specified abi. Not all zygotes support this function.
-     */
-    public boolean preloadPackageForAbi(
-            String packagePath, String libsPath, String libFileName, String cacheKey, String abi)
-            throws ZygoteStartFailedEx, IOException {
-        synchronized (mLock) {
-            ZygoteState state = openZygoteSocketIfNeeded(abi);
-            state.mZygoteOutputWriter.write("5");
-            state.mZygoteOutputWriter.newLine();
-
-            state.mZygoteOutputWriter.write("--preload-package");
-            state.mZygoteOutputWriter.newLine();
-
-            state.mZygoteOutputWriter.write(packagePath);
-            state.mZygoteOutputWriter.newLine();
-
-            state.mZygoteOutputWriter.write(libsPath);
-            state.mZygoteOutputWriter.newLine();
-
-            state.mZygoteOutputWriter.write(libFileName);
-            state.mZygoteOutputWriter.newLine();
-
-            state.mZygoteOutputWriter.write(cacheKey);
-            state.mZygoteOutputWriter.newLine();
-
-            state.mZygoteOutputWriter.flush();
-
-            return (state.mZygoteInputStream.readInt() == 0);
-        }
-    }
-
-    /**
      * Instructs the zygote to preload the default set of classes and resources. Returns
      * {@code true} if a preload was performed as a result of this call, and {@code false}
      * otherwise. The latter usually means that the zygote eagerly preloaded at startup
diff --git a/core/java/android/os/vibrator/VibrationConfig.java b/core/java/android/os/vibrator/VibrationConfig.java
index f6e73b3..a4164e9 100644
--- a/core/java/android/os/vibrator/VibrationConfig.java
+++ b/core/java/android/os/vibrator/VibrationConfig.java
@@ -20,6 +20,7 @@
 import static android.os.VibrationAttributes.USAGE_ALARM;
 import static android.os.VibrationAttributes.USAGE_COMMUNICATION_REQUEST;
 import static android.os.VibrationAttributes.USAGE_HARDWARE_FEEDBACK;
+import static android.os.VibrationAttributes.USAGE_IME_FEEDBACK;
 import static android.os.VibrationAttributes.USAGE_MEDIA;
 import static android.os.VibrationAttributes.USAGE_NOTIFICATION;
 import static android.os.VibrationAttributes.USAGE_PHYSICAL_EMULATION;
@@ -67,6 +68,8 @@
     private final int mDefaultNotificationVibrationIntensity;
     @VibrationIntensity
     private final int mDefaultRingVibrationIntensity;
+    @VibrationIntensity
+    private final int mDefaultKeyboardVibrationIntensity;
 
     private final boolean mKeyboardVibrationSettingsSupported;
 
@@ -98,6 +101,8 @@
                 com.android.internal.R.integer.config_defaultNotificationVibrationIntensity);
         mDefaultRingVibrationIntensity = loadDefaultIntensity(resources,
                 com.android.internal.R.integer.config_defaultRingVibrationIntensity);
+        mDefaultKeyboardVibrationIntensity = loadDefaultIntensity(resources,
+                com.android.internal.R.integer.config_defaultKeyboardVibrationIntensity);
     }
 
     @VibrationIntensity
@@ -213,6 +218,9 @@
             case USAGE_PHYSICAL_EMULATION:
             case USAGE_ACCESSIBILITY:
                 return mDefaultHapticFeedbackIntensity;
+            case USAGE_IME_FEEDBACK:
+                return isKeyboardVibrationSettingsSupported()
+                        ? mDefaultKeyboardVibrationIntensity : mDefaultHapticFeedbackIntensity;
             case USAGE_MEDIA:
             case USAGE_UNKNOWN:
                 // fall through
@@ -236,6 +244,7 @@
                 + ", mDefaultMediaIntensity=" + mDefaultMediaVibrationIntensity
                 + ", mDefaultNotificationIntensity=" + mDefaultNotificationVibrationIntensity
                 + ", mDefaultRingIntensity=" + mDefaultRingVibrationIntensity
+                + ", mDefaultKeyboardIntensity=" + mDefaultKeyboardVibrationIntensity
                 + ", mKeyboardVibrationSettingsSupported=" + mKeyboardVibrationSettingsSupported
                 + "}";
     }
diff --git a/core/java/android/os/vibrator/flags.aconfig b/core/java/android/os/vibrator/flags.aconfig
index ad2f59d..67c3464 100644
--- a/core/java/android/os/vibrator/flags.aconfig
+++ b/core/java/android/os/vibrator/flags.aconfig
@@ -53,3 +53,35 @@
         purpose: PURPOSE_FEATURE
     }
 }
+
+flag {
+    namespace: "haptics"
+    name: "vendor_vibration_effects"
+    is_exported: true
+    description: "Enabled System APIs for vendor-defined vibration effects"
+    bug: "345454923"
+    metadata {
+        purpose: PURPOSE_FEATURE
+    }
+}
+
+flag {
+    namespace: "haptics"
+    name: "throttle_vibration_params_requests"
+    description: "Control the frequency of vibration params requests to prevent overloading the vendor service"
+    bug: "355320860"
+    metadata {
+      purpose: PURPOSE_BUGFIX
+    }
+}
+
+flag {
+    namespace: "haptics"
+    name: "vibration_attribute_ime_usage_api"
+    is_exported: true
+    description: "A public API for IME usage vibration attribute"
+    bug: "332661766"
+    metadata {
+        purpose: PURPOSE_FEATURE
+    }
+}
diff --git a/core/java/android/provider/CallLog.java b/core/java/android/provider/CallLog.java
index 708c196..192afb1 100644
--- a/core/java/android/provider/CallLog.java
+++ b/core/java/android/provider/CallLog.java
@@ -28,6 +28,7 @@
 import android.annotation.SystemApi;
 import android.annotation.UserHandleAware;
 import android.compat.annotation.UnsupportedAppUsage;
+import android.content.ComponentName;
 import android.content.ContentProvider;
 import android.content.ContentResolver;
 import android.content.ContentValues;
@@ -1624,6 +1625,19 @@
                 "is_call_log_phone_account_migration_pending";
 
         /**
+         * The default maximum number of call log entries stored in the call log provider for each
+         * {@link PhoneAccountHandle}.
+         */
+        private static final int DEFAULT_MAX_CALL_LOG_SIZE = 500;
+
+        /**
+         * Expected component name of Telephony phone accounts.
+         */
+        private static final ComponentName TELEPHONY_COMPONENT_NAME =
+                new ComponentName("com.android.phone",
+                        "com.android.services.telephony.TelephonyConnectionService");
+
+        /**
          * Adds a call to the call log.
          *
          * @param ci the CallerInfo object to get the target contact from.  Can be null
@@ -2084,25 +2098,35 @@
                 }
 
                 int numDeleted;
-                if (values.containsKey(PHONE_ACCOUNT_ID)
-                        && !TextUtils.isEmpty(values.getAsString(PHONE_ACCOUNT_ID))
-                        && values.containsKey(PHONE_ACCOUNT_COMPONENT_NAME)
-                        && !TextUtils.isEmpty(values.getAsString(PHONE_ACCOUNT_COMPONENT_NAME))) {
+                final String phoneAccountId =
+                        values.containsKey(PHONE_ACCOUNT_ID)
+                                ? values.getAsString(PHONE_ACCOUNT_ID) : null;
+                final String phoneAccountComponentName =
+                        values.containsKey(PHONE_ACCOUNT_COMPONENT_NAME)
+                                ? values.getAsString(PHONE_ACCOUNT_COMPONENT_NAME) : null;
+                int maxCallLogSize = DEFAULT_MAX_CALL_LOG_SIZE;
+                if (!TextUtils.isEmpty(phoneAccountId)
+                        && !TextUtils.isEmpty(phoneAccountComponentName)) {
+                    if (android.provider.Flags.allowConfigMaximumCallLogEntriesPerSim()
+                            && TELEPHONY_COMPONENT_NAME
+                                    .flattenToString().equals(phoneAccountComponentName)) {
+                        maxCallLogSize = context.getResources().getInteger(
+                                com.android.internal.R.integer.config_maximumCallLogEntriesPerSim);
+                    }
                     // Only purge entries for the same phone account.
                     numDeleted = resolver.delete(uri, "_id IN "
                             + "(SELECT _id FROM calls"
                             + " WHERE " + PHONE_ACCOUNT_COMPONENT_NAME + " = ?"
                             + " AND " + PHONE_ACCOUNT_ID + " = ?"
                             + " ORDER BY " + DEFAULT_SORT_ORDER
-                            + " LIMIT -1 OFFSET 500)", new String[] {
-                            values.getAsString(PHONE_ACCOUNT_COMPONENT_NAME),
-                            values.getAsString(PHONE_ACCOUNT_ID)
-                    });
+                            + " LIMIT -1 OFFSET " + maxCallLogSize + ")",
+                            new String[] { phoneAccountComponentName, phoneAccountId }
+                    );
                 } else {
                     // No valid phone account specified, so default to the old behavior.
                     numDeleted = resolver.delete(uri, "_id IN "
                             + "(SELECT _id FROM calls ORDER BY " + DEFAULT_SORT_ORDER
-                            + " LIMIT -1 OFFSET 500)", null);
+                            + " LIMIT -1 OFFSET " + maxCallLogSize + ")", null);
                 }
                 Log.i(LOG_TAG, "addEntry: cleaned up " + numDeleted + " old entries");
 
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 2562c8e..5703f69 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -2630,7 +2630,7 @@
     /**
      * Activity Action: Show screen that let user select its Autofill Service.
      * <p>
-     * Input: Intent's data URI set with an application name, using the
+     * Input: Intent's data URI set with an application package name, using the
      * "package" schema (like "package:com.my.app").
      *
      * <p>
@@ -2650,7 +2650,7 @@
     /**
      * Activity Action: Show screen that let user enable a Credential Manager provider.
      * <p>
-     * Input: Intent's data URI set with an application name, using the
+     * Input: Intent's data URI set with an application package name, using the
      * "package" schema (like "package:com.my.app").
      *
      * <p>
@@ -5912,6 +5912,14 @@
         public static final String SHOW_KEY_PRESSES = "show_key_presses";
 
         /**
+         * Show touchpad input visualization on screen.
+         * 0 = no
+         * 1 = yes
+         * @hide
+         */
+        public static final String TOUCHPAD_VISUALIZER = "touchpad_visualizer";
+
+        /**
          * Show rotary input dispatched to focused windows on the screen.
          * 0 = no
          * 1 = yes
@@ -11075,6 +11083,13 @@
         public static final String MANDATORY_BIOMETRICS = "mandatory_biometrics";
 
         /**
+         * Whether or not requirements for mandatory biometrics is satisfied.
+         * @hide
+         */
+        public static final String MANDATORY_BIOMETRICS_REQUIREMENTS_SATISFIED =
+                "mandatory_biometrics_requirements_satisfied";
+
+        /**
          * Whether or not active unlock triggers on wake.
          * @hide
          */
@@ -12553,6 +12568,14 @@
                 "contextual_screen_timeout_enabled";
 
         /**
+         * Whether hinge angle lidevent is enabled.
+         *
+         * @hide
+         */
+        public static final String HINGE_ANGLE_LIDEVENT_ENABLED =
+                "hinge_angle_lidevent_enabled";
+
+        /**
          * Whether lockscreen weather is enabled.
          *
          * @hide
@@ -15805,7 +15828,7 @@
          * The following keys are supported:
          *
          * <pre>
-         * screen_brightness_array         (int[])
+         * screen_brightness_array         (int[], values in range [1, 255])
          * dimming_scrim_array             (int[])
          * prox_screen_off_delay           (long)
          * prox_cooldown_trigger           (long)
diff --git a/core/java/android/provider/flags.aconfig b/core/java/android/provider/flags.aconfig
index ff98fc4..53d0c62 100644
--- a/core/java/android/provider/flags.aconfig
+++ b/core/java/android/provider/flags.aconfig
@@ -30,4 +30,16 @@
     namespace: "backstage_power"
     description: "Add a new settings page for the RUN_BACKUP_JOBS permission."
     bug: "320563660"
-}
\ No newline at end of file
+}
+
+# OWNER = tgunn TARGET=25Q1
+flag {
+    name: "allow_config_maximum_call_log_entries_per_sim"
+    is_exported: true
+    namespace: "telecom"
+    description: "Allow partners to modify the maximum number of call log size for each sim card."
+    bug: "352235494"
+    metadata {
+        purpose: PURPOSE_FEATURE
+    }
+}
diff --git a/core/java/android/service/contextualsearch/OWNERS b/core/java/android/service/contextualsearch/OWNERS
index 463adf4..b723872 100644
--- a/core/java/android/service/contextualsearch/OWNERS
+++ b/core/java/android/service/contextualsearch/OWNERS
@@ -1,3 +1,2 @@
 srazdan@google.com
-volnov@google.com
 hackz@google.com
diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java
index e4fc1cd..06e53ac 100644
--- a/core/java/android/service/dreams/DreamService.java
+++ b/core/java/android/service/dreams/DreamService.java
@@ -74,6 +74,7 @@
 
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.display.BrightnessSynchronizer;
 import com.android.internal.util.DumpUtils;
 
 import java.io.FileDescriptor;
@@ -266,9 +267,10 @@
     private boolean mDozing;
     private boolean mWindowless;
     private boolean mPreviewMode;
-    private int mDozeScreenState = Display.STATE_UNKNOWN;
-    private @Display.StateReason int mDozeScreenStateReason = Display.STATE_REASON_UNKNOWN;
-    private int mDozeScreenBrightness = PowerManager.BRIGHTNESS_DEFAULT;
+    private volatile int mDozeScreenState = Display.STATE_UNKNOWN;
+    private volatile @Display.StateReason int mDozeScreenStateReason = Display.STATE_REASON_UNKNOWN;
+    private volatile int mDozeScreenBrightness = PowerManager.BRIGHTNESS_DEFAULT;
+    private volatile float mDozeScreenBrightnessFloat = PowerManager.BRIGHTNESS_INVALID_FLOAT;
 
     private boolean mDebug = false;
 
@@ -787,7 +789,6 @@
      */
     public void setInteractive(boolean interactive) {
         mInteractive = interactive;
-        updateAccessibilityMessage();
     }
 
     /**
@@ -928,12 +929,12 @@
             try {
                 if (startAndStopDozingInBackground()) {
                     mDreamManager.startDozingOneway(
-                        mDreamToken, mDozeScreenState, mDozeScreenStateReason,
-                        mDozeScreenBrightness);
+                            mDreamToken, mDozeScreenState, mDozeScreenStateReason,
+                            mDozeScreenBrightnessFloat, mDozeScreenBrightness);
                 } else {
                     mDreamManager.startDozing(
                             mDreamToken, mDozeScreenState, mDozeScreenStateReason,
-                            mDozeScreenBrightness);
+                            mDozeScreenBrightnessFloat, mDozeScreenBrightness);
                 }
 
             } catch (RemoteException ex) {
@@ -1058,7 +1059,7 @@
      * Gets the screen brightness to use while dozing.
      *
      * @return The screen brightness while dozing as a value between
-     * {@link PowerManager#BRIGHTNESS_OFF} (0) and {@link PowerManager#BRIGHTNESS_ON} (255),
+     * {@link PowerManager#BRIGHTNESS_OFF + 1} (1) and {@link PowerManager#BRIGHTNESS_ON} (255),
      * or {@link PowerManager#BRIGHTNESS_DEFAULT} (-1) to ask the system to apply
      * its default policy based on the screen state.
      *
@@ -1079,11 +1080,11 @@
      * The dream may set a different brightness before starting to doze and may adjust
      * the brightness while dozing to conserve power and achieve various effects.
      * </p><p>
-     * Note that dream may specify any brightness in the full 0-255 range, including
+     * Note that dream may specify any brightness in the full 1-255 range, including
      * values that are less than the minimum value for manual screen brightness
-     * adjustments by the user. In particular, the value may be set to 0 which may
-     * turn off the backlight entirely while still leaving the screen on although
-     * this behavior is device dependent and not guaranteed.
+     * adjustments by the user. In particular, the value may be set to
+     * {@link PowerManager.BRIGHTNESS_OFF} which may turn off the backlight entirely while still
+     * leaving the screen on although this behavior is device dependent and not guaranteed.
      * </p><p>
      * The available range of display brightness values and their behavior while dozing is
      * hardware dependent and may vary across devices. The dream may therefore
@@ -1091,7 +1092,7 @@
      * </p>
      *
      * @param brightness The screen brightness while dozing as a value between
-     * {@link PowerManager#BRIGHTNESS_OFF} (0) and {@link PowerManager#BRIGHTNESS_ON} (255),
+     * {@link PowerManager#BRIGHTNESS_OFF + 1} (1) and {@link PowerManager#BRIGHTNESS_ON} (255),
      * or {@link PowerManager#BRIGHTNESS_DEFAULT} (-1) to ask the system to apply
      * its default policy based on the screen state.
      *
@@ -1109,6 +1110,44 @@
     }
 
     /**
+     * Sets the screen brightness to use while dozing.
+     * <p>
+     * The value of this property determines the power state of the primary display
+     * once {@link #startDozing} has been called. The default value is
+     * {@link PowerManager#BRIGHTNESS_INVALID_FLOAT} which lets the system decide.
+     * The dream may set a different brightness before starting to doze and may adjust
+     * the brightness while dozing to conserve power and achieve various effects.
+     * </p><p>
+     * Note that dream may specify any brightness in the full 0-1 range, including
+     * values that are less than the minimum value for manual screen brightness
+     * adjustments by the user. In particular, the value may be set to
+     * {@link PowerManager#BRIGHTNESS_OFF_FLOAT} which may turn off the backlight entirely while
+     * still leaving the screen on although this behavior is device dependent and not guaranteed.
+     * </p><p>
+     * The available range of display brightness values and their behavior while dozing is
+     * hardware dependent and may vary across devices. The dream may therefore
+     * need to be modified or configured to correctly support the hardware.
+     * </p>
+     *
+     * @param brightness The screen brightness while dozing as a value between
+     * {@link PowerManager#BRIGHTNESS_MIN} (0) and {@link PowerManager#BRIGHTNESS_MAX} (1),
+     * or {@link PowerManager#BRIGHTNESS_INVALID_FLOAT} (Float.NaN) to ask the system to apply
+     * its default policy based on the screen state.
+     *
+     * @hide For use by system UI components only.
+     */
+    @UnsupportedAppUsage
+    public void setDozeScreenBrightnessFloat(float brightness) {
+        if (!Float.isNaN(brightness)) {
+            brightness = clampAbsoluteBrightnessFloat(brightness);
+        }
+        if (!BrightnessSynchronizer.floatEquals(mDozeScreenBrightnessFloat, brightness)) {
+            mDozeScreenBrightnessFloat = brightness;
+            updateDoze();
+        }
+    }
+
+    /**
      * Called when this Dream is constructed.
      */
     @Override
@@ -1347,7 +1386,11 @@
                     Slog.w(mTag, "WakeUp was called before the dream was attached.");
                 } else {
                     try {
-                        mDreamManager.finishSelf(mDreamToken, false /*immediate*/);
+                        if (startAndStopDozingInBackground()) {
+                            mDreamManager.finishSelfOneway(mDreamToken, false /*immediate*/);
+                        } else {
+                            mDreamManager.finishSelf(mDreamToken, false /*immediate*/);
+                        }
                     } catch (RemoteException ex) {
                         // system server died
                     }
@@ -1498,7 +1541,11 @@
         if (mFinished || mWaking) {
             Slog.w(mTag, "attach() called after dream already finished");
             try {
-                mDreamManager.finishSelf(dreamToken, true /*immediate*/);
+                if (startAndStopDozingInBackground()) {
+                    mDreamManager.finishSelfOneway(dreamToken, true /*immediate*/);
+                } else {
+                    mDreamManager.finishSelf(dreamToken, true /*immediate*/);
+                }
             } catch (RemoteException ex) {
                 // system server died
             }
@@ -1641,9 +1688,9 @@
         if (mWindow == null) return;
         if (mDreamAccessibility == null) {
             final View rootView = mWindow.getDecorView();
-            mDreamAccessibility = new DreamAccessibility(this, rootView);
+            mDreamAccessibility = new DreamAccessibility(this, rootView, this::wakeUp);
         }
-        mDreamAccessibility.updateAccessibilityConfiguration(isInteractive());
+        mDreamAccessibility.updateAccessibilityConfiguration();
     }
 
     private boolean getWindowFlagValue(int flag, boolean defaultValue) {
@@ -1744,6 +1791,13 @@
         return MathUtils.constrain(value, PowerManager.BRIGHTNESS_OFF, PowerManager.BRIGHTNESS_ON);
     }
 
+    private static float clampAbsoluteBrightnessFloat(float value) {
+        if (value == PowerManager.BRIGHTNESS_OFF_FLOAT) {
+            return value;
+        }
+        return MathUtils.constrain(value, PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX);
+    }
+
     /**
      * The DreamServiceWrapper is used as a gateway to the system_server, where DreamController
      * uses it to control the DreamService. It is also used to receive callbacks from the
diff --git a/core/java/android/service/dreams/IDreamManager.aidl b/core/java/android/service/dreams/IDreamManager.aidl
index 620eef6..611e791 100644
--- a/core/java/android/service/dreams/IDreamManager.aidl
+++ b/core/java/android/service/dreams/IDreamManager.aidl
@@ -39,8 +39,11 @@
     @UnsupportedAppUsage
     boolean isDreamingOrInPreview();
     boolean canStartDreaming(boolean isScreenOn);
+    /** @deprecated Please use finishSelfOneway instead. */
     void finishSelf(in IBinder token, boolean immediate);
-    void startDozing(in IBinder token, int screenState, int reason, int screenBrightness);
+    /** @deprecated Please use startDozingOneway instead. */
+    void startDozing(in IBinder token, int screenState, int reason, float screenBrightnessFloat,
+            int screenBrightnessInt);
     void stopDozing(in IBinder token);
     void forceAmbientDisplayEnabled(boolean enabled);
     ComponentName[] getDreamComponentsForUser(int userId);
@@ -50,6 +53,7 @@
     void startDreamActivity(in Intent intent);
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.WRITE_DREAM_STATE)")
     oneway void setDreamIsObscured(in boolean isObscured);
-    oneway void startDozingOneway(in IBinder token, int screenState, int reason, int screenBrightness);
+    oneway void startDozingOneway(in IBinder token, int screenState, int reason,
+            float screenBrightnessFloat, int screenBrightnessInt);
     oneway void finishSelfOneway(in IBinder token, boolean immediate);
 }
diff --git a/core/java/android/service/dreams/utils/DreamAccessibility.java b/core/java/android/service/dreams/utils/DreamAccessibility.java
index c38f41b..f504ff7 100644
--- a/core/java/android/service/dreams/utils/DreamAccessibility.java
+++ b/core/java/android/service/dreams/utils/DreamAccessibility.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.content.Context;
+import android.os.Bundle;
 import android.view.View;
 import android.view.accessibility.AccessibilityNodeInfo;
 
@@ -32,22 +33,22 @@
     private final Context mContext;
     private final View mView;
     private final View.AccessibilityDelegate mAccessibilityDelegate;
+    private final Runnable mDismissCallback;
 
-    public DreamAccessibility(@NonNull Context context, @NonNull View view) {
+    public DreamAccessibility(@NonNull Context context, @NonNull View view,
+            @NonNull Runnable dismissCallback) {
         mContext = context;
         mView = view;
         mAccessibilityDelegate = createNewAccessibilityDelegate(mContext);
+        mDismissCallback = dismissCallback;
     }
 
     /**
-     * @param interactive
-     * Removes and add accessibility configuration depending if the dream is interactive or not
+     *  Adds default accessibility configuration if none exist on the dream
      */
-    public void updateAccessibilityConfiguration(Boolean interactive) {
-        if (!interactive) {
+    public void updateAccessibilityConfiguration() {
+        if (mView.getAccessibilityDelegate() == null) {
             addAccessibilityConfiguration();
-        } else {
-            removeCustomAccessibilityAction();
         }
     }
 
@@ -58,31 +59,28 @@
         mView.setAccessibilityDelegate(mAccessibilityDelegate);
     }
 
-    /**
-     * Removes Configured the accessibility actions for the given root view.
-     */
-    private void removeCustomAccessibilityAction() {
-        if (mView.getAccessibilityDelegate() == mAccessibilityDelegate) {
-            mView.setAccessibilityDelegate(null);
-        }
-    }
-
     private View.AccessibilityDelegate createNewAccessibilityDelegate(Context context) {
         return new View.AccessibilityDelegate() {
             @Override
             public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
                 super.onInitializeAccessibilityNodeInfo(host, info);
-                for (AccessibilityNodeInfo.AccessibilityAction action : info.getActionList()) {
-                    if (action.getId() == AccessibilityNodeInfo.ACTION_CLICK) {
-                        info.removeAction(action);
-                        break;
-                    }
-                }
                 info.addAction(new AccessibilityNodeInfo.AccessibilityAction(
-                        AccessibilityNodeInfo.ACTION_CLICK,
+                        AccessibilityNodeInfo.ACTION_DISMISS,
                         context.getResources().getString(R.string.dream_accessibility_action_click)
                 ));
             }
+
+            @Override
+            public boolean performAccessibilityAction(View host, int action, Bundle args) {
+                switch(action){
+                    case AccessibilityNodeInfo.ACTION_DISMISS:
+                        if (mDismissCallback != null) {
+                            mDismissCallback.run();
+                        }
+                        break;
+                }
+                return true;
+            }
         };
     }
 }
diff --git a/core/java/android/service/notification/SystemZenRules.java b/core/java/android/service/notification/SystemZenRules.java
index 22234a9..1d18643 100644
--- a/core/java/android/service/notification/SystemZenRules.java
+++ b/core/java/android/service/notification/SystemZenRules.java
@@ -129,10 +129,7 @@
         }
         sb.append(daysSummary);
         sb.append(context.getString(R.string.zen_mode_trigger_summary_divider_text));
-        sb.append(context.getString(
-                R.string.zen_mode_trigger_summary_range_symbol_combination,
-                timeString(context, schedule.startHour, schedule.startMinute),
-                timeString(context, schedule.endHour, schedule.endMinute)));
+        sb.append(getTimeSummary(context, schedule));
 
         return sb.toString();
     }
@@ -142,7 +139,7 @@
      * adjacent days grouped together ("Sun-Wed" instead of "Sun,Mon,Tue,Wed").
      */
     @Nullable
-    private static String getShortDaysSummary(Context context, @NonNull ScheduleInfo schedule) {
+    public static String getShortDaysSummary(Context context, @NonNull ScheduleInfo schedule) {
         // Compute a list of days with contiguous days grouped together, for example: "Sun-Thu" or
         // "Sun-Mon,Wed,Fri"
         final int[] days = schedule.days;
@@ -224,6 +221,14 @@
         return null;
     }
 
+    /** Returns the time part of a {@link ScheduleInfo}, e.g. {@code 9:00-17:00}. */
+    public static String getTimeSummary(Context context, @NonNull ScheduleInfo schedule) {
+        return context.getString(
+                R.string.zen_mode_trigger_summary_range_symbol_combination,
+                timeString(context, schedule.startHour, schedule.startMinute),
+                timeString(context, schedule.endHour, schedule.endMinute));
+    }
+
     /**
      * Convenience method for representing the specified time in string format.
      */
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index e16a6a1..7ca248d 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -211,6 +211,9 @@
             SUPPRESSED_EFFECT_SCREEN_OFF | SUPPRESSED_EFFECT_FULL_SCREEN_INTENT
                     | SUPPRESSED_EFFECT_LIGHTS | SUPPRESSED_EFFECT_PEEK | SUPPRESSED_EFFECT_AMBIENT;
 
+    private static final int LEGACY_SUPPRESSED_EFFECTS =
+            Policy.SUPPRESSED_EFFECT_SCREEN_ON | Policy.SUPPRESSED_EFFECT_SCREEN_OFF;
+
     // ZenModeConfig XML versions distinguishing key changes.
     public static final int XML_VERSION_ZEN_UPGRADE = 8;
     public static final int XML_VERSION_MODES_API = 11;
@@ -284,6 +287,7 @@
     private static final String RULE_ATT_TRIGGER_DESC = "triggerDesc";
     private static final String RULE_ATT_DELETION_INSTANT = "deletionInstant";
     private static final String RULE_ATT_DISABLED_ORIGIN = "disabledOrigin";
+    private static final String RULE_ATT_LEGACY_SUPPRESSED_EFFECTS = "legacySuppressedEffects";
 
     private static final String DEVICE_EFFECT_DISPLAY_GRAYSCALE = "zdeDisplayGrayscale";
     private static final String DEVICE_EFFECT_SUPPRESS_AMBIENT_DISPLAY =
@@ -1171,6 +1175,8 @@
             if (Flags.modesUi()) {
                 rt.disabledOrigin = safeInt(parser, RULE_ATT_DISABLED_ORIGIN,
                         UPDATE_ORIGIN_UNKNOWN);
+                rt.legacySuppressedEffects = safeInt(parser,
+                        RULE_ATT_LEGACY_SUPPRESSED_EFFECTS, 0);
             }
         }
         return rt;
@@ -1228,6 +1234,8 @@
             }
             if (Flags.modesUi()) {
                 out.attributeInt(null, RULE_ATT_DISABLED_ORIGIN, rule.disabledOrigin);
+                out.attributeInt(null, RULE_ATT_LEGACY_SUPPRESSED_EFFECTS,
+                        rule.legacySuppressedEffects);
             }
         }
     }
@@ -1903,6 +1911,13 @@
                             ZenPolicy.VISUAL_EFFECT_NOTIFICATION_LIST))) {
                 suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST;
             }
+
+            // Restore legacy suppressed effects (obsolete fields which are not in ZenPolicy).
+            // These are deprecated and have no effect on behavior, however apps should get them
+            // back if provided to setNotificationPolicy() earlier.
+            suppressedVisualEffects &= ~LEGACY_SUPPRESSED_EFFECTS;
+            suppressedVisualEffects |=
+                    (LEGACY_SUPPRESSED_EFFECTS & manualRule.legacySuppressedEffects);
         } else {
             if (isAllowConversations()) {
                 priorityCategories |= Policy.PRIORITY_CATEGORY_CONVERSATIONS;
@@ -1996,6 +2011,8 @@
         if (policy == null) return;
         if (Flags.modesUi()) {
             manualRule.zenPolicy = ZenAdapters.notificationPolicyToZenPolicy(policy);
+            manualRule.legacySuppressedEffects =
+                    LEGACY_SUPPRESSED_EFFECTS & policy.suppressedVisualEffects;
         } else {
             setAllowAlarms((policy.priorityCategories & Policy.PRIORITY_CATEGORY_ALARMS) != 0);
             allowMedia = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_MEDIA) != 0;
@@ -2521,6 +2538,10 @@
         @Nullable public Instant deletionInstant; // Only set on deleted rules.
         @FlaggedApi(Flags.FLAG_MODES_UI)
         @ConfigChangeOrigin public int disabledOrigin = UPDATE_ORIGIN_UNKNOWN;
+        // The obsolete suppressed effects in NM.Policy (SCREEN_ON, SCREEN_OFF) cannot be put in a
+        // ZenPolicy, so we store them here, only for the manual rule.
+        @FlaggedApi(Flags.FLAG_MODES_UI)
+        int legacySuppressedEffects;
 
         public ZenRule() { }
 
@@ -2561,6 +2582,7 @@
                 }
                 if (Flags.modesUi()) {
                     disabledOrigin = source.readInt();
+                    legacySuppressedEffects = source.readInt();
                 }
             }
         }
@@ -2638,6 +2660,7 @@
                 }
                 if (Flags.modesUi()) {
                     dest.writeInt(disabledOrigin);
+                    dest.writeInt(legacySuppressedEffects);
                 }
             }
         }
@@ -2686,6 +2709,7 @@
                 }
                 if (Flags.modesUi()) {
                     sb.append(",disabledOrigin=").append(disabledOrigin);
+                    sb.append(",legacySuppressedEffects=").append(legacySuppressedEffects);
                 }
             }
 
@@ -2754,7 +2778,8 @@
 
                 if (Flags.modesUi()) {
                     finalEquals = finalEquals
-                            && other.disabledOrigin == disabledOrigin;
+                            && other.disabledOrigin == disabledOrigin
+                            && other.legacySuppressedEffects == legacySuppressedEffects;
                 }
             }
 
@@ -2769,15 +2794,15 @@
                             component, configurationActivity, pkg, id, enabler, zenPolicy,
                             zenDeviceEffects, modified, allowManualInvocation, iconResName,
                             triggerDescription, type, userModifiedFields,
-                            zenPolicyUserModifiedFields,
-                            zenDeviceEffectsUserModifiedFields, deletionInstant, disabledOrigin);
+                            zenPolicyUserModifiedFields, zenDeviceEffectsUserModifiedFields,
+                            deletionInstant, disabledOrigin, legacySuppressedEffects);
                 } else {
                     return Objects.hash(enabled, snoozing, name, zenMode, conditionId, condition,
                             component, configurationActivity, pkg, id, enabler, zenPolicy,
                             zenDeviceEffects, modified, allowManualInvocation, iconResName,
                             triggerDescription, type, userModifiedFields,
-                            zenPolicyUserModifiedFields,
-                            zenDeviceEffectsUserModifiedFields, deletionInstant);
+                            zenPolicyUserModifiedFields, zenDeviceEffectsUserModifiedFields,
+                            deletionInstant);
                 }
             }
             return Objects.hash(enabled, snoozing, name, zenMode, conditionId, condition,
diff --git a/core/java/android/service/notification/ZenModeDiff.java b/core/java/android/service/notification/ZenModeDiff.java
index 91ef11c..a37e227 100644
--- a/core/java/android/service/notification/ZenModeDiff.java
+++ b/core/java/android/service/notification/ZenModeDiff.java
@@ -472,6 +472,7 @@
         public static final String FIELD_ICON_RES = "iconResName";
         public static final String FIELD_TRIGGER_DESCRIPTION = "triggerDescription";
         public static final String FIELD_TYPE = "type";
+        public static final String FIELD_LEGACY_SUPPRESSED_EFFECTS = "legacySuppressedEffects";
         // NOTE: new field strings must match the variable names in ZenModeConfig.ZenRule
 
         // Special field to track whether this rule became active or inactive
@@ -567,6 +568,13 @@
                 if (!Objects.equals(from.iconResName, to.iconResName)) {
                     addField(FIELD_ICON_RES, new FieldDiff<>(from.iconResName, to.iconResName));
                 }
+                if (android.app.Flags.modesUi()) {
+                    if (from.legacySuppressedEffects != to.legacySuppressedEffects) {
+                        addField(FIELD_LEGACY_SUPPRESSED_EFFECTS,
+                                new FieldDiff<>(from.legacySuppressedEffects,
+                                        to.legacySuppressedEffects));
+                    }
+                }
             }
         }
 
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 46b222b..66d08f9 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -404,8 +404,7 @@
             }
 
             private void prepareToDraw() {
-                if (mDisplayState == Display.STATE_DOZE
-                        || mDisplayState == Display.STATE_DOZE_SUSPEND) {
+                if (mDisplayState == Display.STATE_DOZE) {
                     try {
                         mSession.pokeDrawLock(mWindow);
                     } catch (RemoteException e) {
diff --git a/core/java/android/speech/OWNERS b/core/java/android/speech/OWNERS
index 0f2f8ad..32f4822 100644
--- a/core/java/android/speech/OWNERS
+++ b/core/java/android/speech/OWNERS
@@ -1,4 +1,3 @@
 volnov@google.com
 eugeniom@google.com
 schfan@google.com
-hackz@google.com
diff --git a/core/java/android/telephony/PhoneStateListener.java b/core/java/android/telephony/PhoneStateListener.java
index 4281da1..5ac0c50 100644
--- a/core/java/android/telephony/PhoneStateListener.java
+++ b/core/java/android/telephony/PhoneStateListener.java
@@ -1689,6 +1689,10 @@
         public final void onCarrierRoamingNtnModeChanged(boolean active) {
             // not supported on the deprecated interface - Use TelephonyCallback instead
         }
+
+        public final void onCarrierRoamingNtnEligibleStateChanged(boolean eligible) {
+            // not supported on the deprecated interface - Use TelephonyCallback instead
+        }
     }
 
     private void log(String s) {
diff --git a/core/java/android/telephony/TelephonyCallback.java b/core/java/android/telephony/TelephonyCallback.java
index b8b84d9..c360e64 100644
--- a/core/java/android/telephony/TelephonyCallback.java
+++ b/core/java/android/telephony/TelephonyCallback.java
@@ -653,6 +653,27 @@
     public static final int EVENT_CARRIER_ROAMING_NTN_MODE_CHANGED = 42;
 
     /**
+     * Event for listening to changes in carrier roaming non-terrestrial network eligibility.
+     *
+     * @see CarrierRoamingNtnModeListener
+     *
+     * Device is eligible for satellite communication if all the following conditions are met:
+     * <ul>
+     * <li>Any subscription on the device supports P2P satellite messaging which is defined by
+     * {@link CarrierConfigManager#KEY_SATELLITE_ATTACH_SUPPORTED_BOOL} </li>
+     * <li>{@link CarrierConfigManager#KEY_CARRIER_ROAMING_NTN_CONNECT_TYPE_INT} set to
+     * {@link CarrierConfigManager#CARRIER_ROAMING_NTN_CONNECT_MANUAL} </li>
+     * <li>The device is in {@link ServiceState#STATE_OUT_OF_SERVICE}, not connected to Wi-Fi,
+     * and the hysteresis timer defined by {@link CarrierConfigManager
+     * #KEY_CARRIER_SUPPORTED_SATELLITE_NOTIFICATION_HYSTERESIS_SEC_INT} is expired.
+     * </li>
+     * </ul>
+     *
+     * @hide
+     */
+    public static final int EVENT_CARRIER_ROAMING_NTN_ELIGIBLE_STATE_CHANGED = 43;
+
+    /**
      * @hide
      */
     @IntDef(prefix = {"EVENT_"}, value = {
@@ -697,7 +718,8 @@
             EVENT_MEDIA_QUALITY_STATUS_CHANGED,
             EVENT_EMERGENCY_CALLBACK_MODE_CHANGED,
             EVENT_SIMULTANEOUS_CELLULAR_CALLING_SUBSCRIPTIONS_CHANGED,
-            EVENT_CARRIER_ROAMING_NTN_MODE_CHANGED
+            EVENT_CARRIER_ROAMING_NTN_MODE_CHANGED,
+            EVENT_CARRIER_ROAMING_NTN_ELIGIBLE_STATE_CHANGED
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface TelephonyEvent {
@@ -1711,6 +1733,23 @@
          *                           {code false} otherwise.
          */
         void onCarrierRoamingNtnModeChanged(boolean active);
+
+        /**
+         * Callback invoked when carrier roaming non-terrestrial network eligibility changes.
+         *
+         * @param eligible {@code true} when the device is eligible for satellite
+         * communication if all the following conditions are met:
+         * <ul>
+         * <li>Any subscription on the device supports P2P satellite messaging which is defined by
+         * {@link CarrierConfigManager#KEY_SATELLITE_ATTACH_SUPPORTED_BOOL} </li>
+         * <li>{@link CarrierConfigManager#KEY_CARRIER_ROAMING_NTN_CONNECT_TYPE_INT} set to
+         * {@link CarrierConfigManager#CARRIER_ROAMING_NTN_CONNECT_MANUAL} </li>
+         * <li>The device is in {@link ServiceState#STATE_OUT_OF_SERVICE}, not connected to Wi-Fi,
+         * and the hysteresis timer defined by {@link CarrierConfigManager
+         * #KEY_CARRIER_SUPPORTED_SATELLITE_NOTIFICATION_HYSTERESIS_SEC_INT} is expired. </li>
+         * </ul>
+         */
+        default void onCarrierRoamingNtnEligibleStateChanged(boolean eligible) {}
     }
 
     /**
@@ -2125,5 +2164,16 @@
             Binder.withCleanCallingIdentity(
                     () -> mExecutor.execute(() -> listener.onCarrierRoamingNtnModeChanged(active)));
         }
+
+        public void onCarrierRoamingNtnEligibleStateChanged(boolean eligible) {
+            if (!Flags.carrierRoamingNbIotNtn()) return;
+
+            CarrierRoamingNtnModeListener listener =
+                    (CarrierRoamingNtnModeListener) mTelephonyCallbackWeakRef.get();
+            if (listener == null) return;
+
+            Binder.withCleanCallingIdentity(() -> mExecutor.execute(
+                    () -> listener.onCarrierRoamingNtnEligibleStateChanged(eligible)));
+        }
     }
 }
diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java
index 6160fdb..10f03c1 100644
--- a/core/java/android/telephony/TelephonyRegistryManager.java
+++ b/core/java/android/telephony/TelephonyRegistryManager.java
@@ -1091,6 +1091,34 @@
     }
 
     /**
+     * Notify external listeners that device eligibility to connect to carrier roaming
+     * non-terrestrial network changed.
+     *
+     * @param subId subscription ID.
+     * @param eligible {@code true} when the device is eligible for satellite
+     * communication if all the following conditions are met:
+     * <ul>
+     * <li>Any subscription supports P2P satellite messaging which is defined by
+     * {@link CarrierConfigManager#KEY_SATELLITE_ATTACH_SUPPORTED_BOOL} </li>
+     * <li>{@link CarrierConfigManager#KEY_CARRIER_ROAMING_NTN_CONNECT_TYPE_INT} set to
+     * {@link CarrierConfigManager#CARRIER_ROAMING_NTN_CONNECT_MANUAL} </li>
+     * <li>The device is in {@link ServiceState#STATE_OUT_OF_SERVICE}, not connected to Wi-Fi,
+     * and the hysteresis timer defined by {@link CarrierConfigManager
+     * #KEY_CARRIER_SUPPORTED_SATELLITE_NOTIFICATION_HYSTERESIS_SEC_INT} is expired. </li>
+     * </ul>
+     *
+     * @hide
+     */
+    public void notifyCarrierRoamingNtnEligibleStateChanged(int subId, boolean eligible) {
+        try {
+            sRegistry.notifyCarrierRoamingNtnEligibleStateChanged(subId, eligible);
+        } catch (RemoteException ex) {
+            // system server crash
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Processes potential event changes from the provided {@link TelephonyCallback}.
      *
      * @param telephonyCallback callback for monitoring callback changes to the telephony state.
@@ -1246,6 +1274,11 @@
         if (telephonyCallback instanceof TelephonyCallback.CarrierRoamingNtnModeListener) {
             eventList.add(TelephonyCallback.EVENT_CARRIER_ROAMING_NTN_MODE_CHANGED);
         }
+
+        if (telephonyCallback instanceof TelephonyCallback.CarrierRoamingNtnModeListener) {
+            eventList.add(TelephonyCallback.EVENT_CARRIER_ROAMING_NTN_ELIGIBLE_STATE_CHANGED);
+        }
+
         return eventList;
     }
 
diff --git a/core/java/android/text/BoringLayout.java b/core/java/android/text/BoringLayout.java
index 9db8aa1..4fdcecc 100644
--- a/core/java/android/text/BoringLayout.java
+++ b/core/java/android/text/BoringLayout.java
@@ -30,6 +30,8 @@
 import android.graphics.text.LineBreakConfig;
 import android.text.style.ParagraphStyle;
 
+import com.android.text.flags.Flags;
+
 /**
  * A BoringLayout is a very simple Layout implementation for text that
  * fits on a single line and is all left-to-right characters.
@@ -589,7 +591,7 @@
             fm.reset();
         }
 
-        if (ClientFlags.fixLineHeightForLocale()) {
+        if (Flags.fixLineHeightForLocale()) {
             if (minimumFontMetrics != null) {
                 fm.set(minimumFontMetrics);
                 // Because the font metrics is provided by public APIs, adjust the top/bottom with
diff --git a/core/java/android/text/DynamicLayout.java b/core/java/android/text/DynamicLayout.java
index a78a417..6b1aef7 100644
--- a/core/java/android/text/DynamicLayout.java
+++ b/core/java/android/text/DynamicLayout.java
@@ -1320,7 +1320,11 @@
                         // It's possible that a Span is removed when the text covering it is
                         // deleted, in this case, the original start and end of the span might be
                         // OOB. So it'll reflow the entire string instead.
-                        reflow(s, 0, 0, s.length());
+                        if (Flags.insertModeCrashUpdateLayoutSpan()) {
+                            transformAndReflow(s, 0, s.length());
+                        } else {
+                            reflow(s, 0, 0, s.length());
+                        }
                     } else {
                         reflow(s, start, end - start, end - start);
                     }
@@ -1343,7 +1347,11 @@
                         // When text is changed, it'll also trigger onSpanChanged. In this case we
                         // can't determine the updated range in the transformed text. So it'll
                         // reflow the entire range instead.
-                        reflow(s, 0, 0, s.length());
+                        if (Flags.insertModeCrashUpdateLayoutSpan()) {
+                            transformAndReflow(s, 0, s.length());
+                        } else {
+                            reflow(s, 0, 0, s.length());
+                        }
                     } else {
                         reflow(s, start, end - start, end - start);
                         reflow(s, nstart, nend - nstart, nend - nstart);
diff --git a/core/java/android/text/MeasuredParagraph.java b/core/java/android/text/MeasuredParagraph.java
index 161a79b..896e087 100644
--- a/core/java/android/text/MeasuredParagraph.java
+++ b/core/java/android/text/MeasuredParagraph.java
@@ -42,6 +42,8 @@
 import android.text.style.ReplacementSpan;
 import android.util.Pools.SynchronizedPool;
 
+import com.android.text.flags.Flags;
+
 import java.util.Arrays;
 
 /**
@@ -199,7 +201,7 @@
      * @hide
      */
     public @Layout.Direction int getParagraphDir() {
-        if (ClientFlags.icuBidiMigration()) {
+        if (Flags.icuBidiMigration()) {
             if (mBidi == null) {
                 return Layout.DIR_LEFT_TO_RIGHT;
             }
@@ -217,7 +219,7 @@
      */
     public Directions getDirections(@IntRange(from = 0) int start,  // inclusive
                                     @IntRange(from = 0) int end) {  // exclusive
-        if (ClientFlags.icuBidiMigration()) {
+        if (Flags.icuBidiMigration()) {
             // Easy case: mBidi == null means the text is all LTR and no bidi suppot is needed.
             if (mBidi == null) {
                 return Layout.DIRS_ALL_LEFT_TO_RIGHT;
@@ -679,7 +681,7 @@
             }
         }
 
-        if (ClientFlags.icuBidiMigration()) {
+        if (Flags.icuBidiMigration()) {
             if ((textDir == TextDirectionHeuristics.LTR
                     || textDir == TextDirectionHeuristics.FIRSTSTRONG_LTR
                     || textDir == TextDirectionHeuristics.ANYRTL_LTR)
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index 95460a3..cb49850 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -41,6 +41,7 @@
 
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.GrowingArrayUtils;
+import com.android.text.flags.Flags;
 
 import java.util.Arrays;
 
@@ -803,7 +804,7 @@
         final int defaultAscent;
         final int defaultDescent;
         int defaultBottom;
-        if (ClientFlags.fixLineHeightForLocale() && b.mMinimumFontMetrics != null) {
+        if (Flags.fixLineHeightForLocale() && b.mMinimumFontMetrics != null) {
             defaultTop = (int) Math.floor(b.mMinimumFontMetrics.top);
             defaultAscent = Math.round(b.mMinimumFontMetrics.ascent);
             defaultDescent = Math.round(b.mMinimumFontMetrics.descent);
diff --git a/core/java/android/text/flags/flags.aconfig b/core/java/android/text/flags/flags.aconfig
index 8836c8a..88a1b9c 100644
--- a/core/java/android/text/flags/flags.aconfig
+++ b/core/java/android/text/flags/flags.aconfig
@@ -125,6 +125,16 @@
 }
 
 flag {
+  name: "insert_mode_crash_update_layout_span"
+  namespace: "text"
+  description: "Fix insert mode crash when the text has UpdateLayout span attached."
+  bug: "355137282"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
+
+flag {
   name: "icu_bidi_migration"
   namespace: "text"
   description: "A flag for replacing AndroidBidi with android.icu.text.Bidi."
@@ -220,3 +230,33 @@
   is_fixed_read_only: true
   bug: "346915432"
 }
+
+flag {
+  name: "clear_font_variation_settings"
+  namespace: "text"
+  description: "The font variation settings must be cleared when the new Typeface is set"
+  bug: "353609778"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
+
+flag {
+  name: "portuguese_hyphenator"
+  namespace: "text"
+  description: "Portuguese taiored hyphenator"
+  bug: "344656282"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
+
+flag {
+  name: "dont_break_email_in_nobreak_tag"
+  namespace: "text"
+  description: "Prevent line break inside email."
+  bug: "350691716"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
\ No newline at end of file
diff --git a/core/java/android/util/TypedValue.java b/core/java/android/util/TypedValue.java
index 330a9fc..9668b6ad 100644
--- a/core/java/android/util/TypedValue.java
+++ b/core/java/android/util/TypedValue.java
@@ -65,10 +65,11 @@
     /** The <var>data</var> field holds a number that was
      *  originally specified in hexadecimal (0xn). */
     public static final int TYPE_INT_HEX = 0x11;
-    /** The <var>data</var> field holds 0 or 1 that was originally
-     *  specified as "false" or "true". */
+    /**
+     * {@link #data} holds 0 to represent {@code false}, or a value different from 0 to represent
+     * {@code true}.
+     */
     public static final int TYPE_INT_BOOLEAN = 0x12;
-
     /** Identifies the start of integer values that were specified as
      *  color constants (starting with '#'). */
     public static final int TYPE_FIRST_COLOR_INT = 0x1c;
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index 14407ca..762a302 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -30,6 +30,7 @@
 import android.view.IWindowId;
 import android.view.MotionEvent;
 import android.view.WindowManager;
+import android.view.inputmethod.ImeTracker;
 import android.view.InsetsSourceControl;
 import android.view.InsetsState;
 import android.view.Surface;
@@ -276,7 +277,8 @@
     /**
      * Updates the requested visible types of insets.
      */
-    oneway void updateRequestedVisibleTypes(IWindow window, int requestedVisibleTypes);
+    oneway void updateRequestedVisibleTypes(IWindow window, int requestedVisibleTypes,
+            in @nullable ImeTracker.Token imeStatsToken);
 
     /**
      * Called when the system gesture exclusion has changed.
@@ -369,4 +371,14 @@
      * @return {@code true} if the focus changes. Otherwise, {@code false}.
      */
     boolean moveFocusToAdjacentWindow(IWindow fromWindow, int direction);
+
+    /**
+     * Notifies the statsToken and IME visibility to the ImeInsetsSourceProvider.
+     *
+     * @param window The window that is used to get the ImeInsetsSourceProvider.
+     * @param visible {@code true} to make it visible, {@code false} to hide it.
+     * @param statsToken the token tracking the current IME request.
+     */
+    oneway void notifyImeWindowVisibilityChangedFromClient(IWindow window, boolean visible,
+            in ImeTracker.Token statsToken);
 }
diff --git a/core/java/android/view/InputEventAssigner.java b/core/java/android/view/InputEventAssigner.java
index 7fac6c5..30d9aaa 100644
--- a/core/java/android/view/InputEventAssigner.java
+++ b/core/java/android/view/InputEventAssigner.java
@@ -17,7 +17,8 @@
 package android.view;
 
 import static android.os.IInputConstants.INVALID_INPUT_EVENT_ID;
-import static android.view.InputDevice.SOURCE_TOUCHSCREEN;
+import static android.view.InputDevice.SOURCE_CLASS_POINTER;
+import static android.view.InputDevice.SOURCE_CLASS_POSITION;
 
 /**
  * Process input events and assign input event id to a specific frame.
@@ -64,18 +65,19 @@
     public int processEvent(InputEvent event) {
         if (event instanceof MotionEvent) {
             MotionEvent motionEvent = (MotionEvent) event;
-            if (motionEvent.isFromSource(SOURCE_TOUCHSCREEN)) {
+            if (motionEvent.isFromSource(SOURCE_CLASS_POINTER) || motionEvent.isFromSource(
+                    SOURCE_CLASS_POSITION)) {
                 final int action = motionEvent.getActionMasked();
                 if (action == MotionEvent.ACTION_DOWN) {
                     mHasUnprocessedDown = true;
                     mDownEventId = event.getId();
                 }
-                if (mHasUnprocessedDown && action == MotionEvent.ACTION_MOVE) {
-                    return mDownEventId;
-                }
                 if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
                     mHasUnprocessedDown = false;
                 }
+                if (mHasUnprocessedDown) {
+                    return mDownEventId;
+                }
             }
         }
         return event.getId();
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index f166b89..d83f344 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -121,8 +121,10 @@
          * The visibilities should be reported back to WM.
          *
          * @param types Bitwise flags of types requested visible.
+         * @param statsToken the token tracking the current IME request or {@code null} otherwise.
          */
-        void updateRequestedVisibleTypes(@InsetsType int types);
+        void updateRequestedVisibleTypes(@InsetsType int types,
+                @Nullable ImeTracker.Token statsToken);
 
         /**
          * @return Whether the host has any callbacks it wants to synchronize the animations with.
@@ -974,6 +976,7 @@
         int consumedControlCount = 0;
         final @InsetsType int[] showTypes = new int[1];
         final @InsetsType int[] hideTypes = new int[1];
+        ImeTracker.Token statsToken = null;
 
         // Ensure to update all existing source consumers
         for (int i = mSourceConsumers.size() - 1; i >= 0; i--) {
@@ -988,6 +991,12 @@
             if (control != null) {
                 controllableTypes |= control.getType();
                 consumedControlCount++;
+
+                if (Flags.refactorInsetsController()) {
+                    if (control.getId() == ID_IME) {
+                        statsToken = control.getImeStatsToken();
+                    }
+                }
             }
 
             // control may be null, but we still need to update the control to null if it got
@@ -1021,34 +1030,31 @@
         if (Flags.refactorInsetsController()) {
             if (mPendingImeControlRequest != null && getImeSourceConsumer().getControl() != null
                     && getImeSourceConsumer().getControl().getLeash() != null) {
-                // TODO we need to pass the statsToken
-                handlePendingControlRequest(null);
+                handlePendingControlRequest(statsToken);
             } else {
                 if (showTypes[0] != 0) {
-                    applyAnimation(showTypes[0], true /* show */, false /* fromIme */,
-                            null /* statsToken */);
+                    applyAnimation(showTypes[0], true /* show */, false /* fromIme */, statsToken);
                 }
                 if (hideTypes[0] != 0) {
-                    applyAnimation(hideTypes[0], false /* show */, false /* fromIme */,
-                            null /* statsToken */);
+                    applyAnimation(hideTypes[0], false /* show */, false /* fromIme */, statsToken);
                 }
             }
         } else {
             if (showTypes[0] != 0) {
-                final var statsToken =
+                final var newStatsToken =
                         (showTypes[0] & ime()) == 0 ? null : ImeTracker.forLogging().onStart(
                                 ImeTracker.TYPE_SHOW, ImeTracker.ORIGIN_CLIENT,
                                 SoftInputShowHideReason.CONTROLS_CHANGED,
                                 mHost.isHandlingPointerEvent() /* fromUser */);
-                applyAnimation(showTypes[0], true /* show */, false /* fromIme */, statsToken);
+                applyAnimation(showTypes[0], true /* show */, false /* fromIme */, newStatsToken);
             }
             if (hideTypes[0] != 0) {
-                final var statsToken =
+                final var newStatsToken =
                         (hideTypes[0] & ime()) == 0 ? null : ImeTracker.forLogging().onStart(
                                 ImeTracker.TYPE_HIDE, ImeTracker.ORIGIN_CLIENT,
                                 SoftInputShowHideReason.CONTROLS_CHANGED,
                                 mHost.isHandlingPointerEvent() /* fromUser */);
-                applyAnimation(hideTypes[0], false /* show */, false /* fromIme */, statsToken);
+                applyAnimation(hideTypes[0], false /* show */, false /* fromIme */, newStatsToken);
             }
         }
 
@@ -1065,7 +1071,9 @@
         }
 
         // InsetsSourceConsumer#setControl might change the requested visibility.
-        reportRequestedVisibleTypes();
+        // TODO(b/353463205) check this: if the requestedVisibleTypes for the IME were already
+        //  sent, the request would fail. Therefore, don't send the statsToken here.
+        reportRequestedVisibleTypes(null /* statsToken */);
     }
 
     @VisibleForTesting(visibility = PACKAGE)
@@ -1176,6 +1184,7 @@
         }
         if (DEBUG) Log.d(TAG, "show typesReady: " + typesReady);
         if ((Flags.refactorInsetsController() || fromIme) && (typesReady & Type.ime()) != 0) {
+            // TODO(b/353463205) check if this is needed here
             ImeTracker.forLatency().onShown(statsToken, ActivityThread::currentApplication);
         }
         applyAnimation(typesReady, true /* show */, fromIme, statsToken);
@@ -1243,6 +1252,8 @@
                     // an animation again (mRequestedVisibleTypes are reported at the end of the IME
                     // hide animation but set at the beginning)
                     if ((mRequestedVisibleTypes & ime()) == 0) {
+                        ImeTracker.forLogging().onCancelled(statsToken,
+                                ImeTracker.PHASE_CLIENT_ALREADY_HIDDEN);
                         continue;
                     }
                 }
@@ -1346,7 +1357,7 @@
 
         // We are finishing setting the requested visible types. Report them to the server
         // and/or the app.
-        reportRequestedVisibleTypes();
+        reportRequestedVisibleTypes(statsToken);
     }
 
     private void controlAnimationUncheckedInner(@InsetsType int types,
@@ -1396,8 +1407,8 @@
             // Ime will not be contained in typesReady nor in controls, if we don't have a leash
             Pair<Integer, Integer> typesReadyPair = collectSourceControlsV2(types, controls);
             typesReady = typesReadyPair.first;
-            @InsetsType int typesWithoutLeash = typesReadyPair.second;
             if (animationType == ANIMATION_TYPE_USER) {
+                @InsetsType int typesWithoutLeash = typesReadyPair.second;
                 // When using an app-driven animation, the IME won't have a leash (because the
                 // window isn't created yet). If we have a control, but no leash, defers the
                 // request until the leash gets created.
@@ -1431,6 +1442,11 @@
                 }
                 // We need to wait until all types are ready
                 if (typesReady != types) {
+                    if (DEBUG) {
+                        Log.d(TAG, TextUtils.formatSimple(
+                                "not all types are ready yet, waiting. typesReady: %s, types: %s",
+                                typesReady, types));
+                    }
                     return;
                 }
             }
@@ -1728,9 +1744,13 @@
         } else {
             ImeTracker.forLogging().onProgress(statsToken,
                     ImeTracker.PHASE_CLIENT_ANIMATION_FINISHED_HIDE);
-            ImeTracker.forLogging().onHidden(statsToken);
+            // The requestedVisibleTypes are only send at the end of the hide animation.
+            // Therefore, the requested is not finished at this point.
+            if (!Flags.refactorInsetsController()) {
+                ImeTracker.forLogging().onHidden(statsToken);
+            }
         }
-        reportRequestedVisibleTypes();
+        reportRequestedVisibleTypes(shown ? null : runner.getStatsToken());
     }
 
     @Override
@@ -1787,7 +1807,7 @@
                             if (mHost != null) {
                                 // if the (hide) animation is cancelled, the
                                 // requestedVisibleTypes should be reported at this point.
-                                reportRequestedVisibleTypes();
+                                reportRequestedVisibleTypes(control.getStatsToken());
                                 mHost.getInputMethodManager().removeImeSurface(
                                         mHost.getWindowToken());
                             }
@@ -1923,8 +1943,10 @@
 
     /**
      * Called when finishing setting requested visible types or finishing setting controls.
+     *
+     * @param statsToken the token tracking the current IME request or {@code null} otherwise.
      */
-    private void reportRequestedVisibleTypes() {
+    private void reportRequestedVisibleTypes(@Nullable ImeTracker.Token statsToken) {
         final @InsetsType int typesToReport;
         if (Flags.refactorInsetsController()) {
             // If the IME is currently animating out, it is still visible, therefore we only
@@ -1941,8 +1963,23 @@
             if (WindowInsets.Type.hasCompatSystemBars(diff)) {
                 mCompatSysUiVisibilityStaled = true;
             }
+            if (Flags.refactorInsetsController()) {
+                ImeTracker.forLogging().onProgress(statsToken,
+                        ImeTracker.PHASE_CLIENT_REPORT_REQUESTED_VISIBLE_TYPES);
+            }
             mReportedRequestedVisibleTypes = mRequestedVisibleTypes;
-            mHost.updateRequestedVisibleTypes(mReportedRequestedVisibleTypes);
+            mHost.updateRequestedVisibleTypes(mReportedRequestedVisibleTypes, statsToken);
+        } else if (Flags.refactorInsetsController()) {
+            if ((typesToReport & ime()) != 0 && mImeSourceConsumer != null) {
+                InsetsSourceControl control = mImeSourceConsumer.getControl();
+                if (control != null && control.getLeash() == null) {
+                    // If the IME was requested twice, and we didn't receive the controls
+                    // yet, this request will not continue. It should be cancelled here, as
+                    // it would time out otherwise.
+                    ImeTracker.forLogging().onCancelled(statsToken,
+                            ImeTracker.PHASE_CLIENT_REPORT_REQUESTED_VISIBLE_TYPES);
+                }
+            }
         }
         updateCompatSysUiVisibility();
     }
diff --git a/core/java/android/view/InsetsSource.java b/core/java/android/view/InsetsSource.java
index 5c10db1..b796e0b 100644
--- a/core/java/android/view/InsetsSource.java
+++ b/core/java/android/view/InsetsSource.java
@@ -104,12 +104,23 @@
      */
     public static final int FLAG_ANIMATE_RESIZING = 1 << 3;
 
+    /**
+     * Controls whether the {@link WindowInsets.Type#captionBar()} insets provided by this source
+     * should always be forcibly consumed. Unlike with {@link #FLAG_FORCE_CONSUMING}, when this
+     * flag is used the caption bar will be consumed even when the bar is requested to be visible.
+     *
+     * Note: this flag does not take effect when the window applies
+     * {@link WindowInsetsController.Appearance#APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND}.
+     */
+    public static final int FLAG_FORCE_CONSUMING_OPAQUE_CAPTION_BAR = 1 << 4;
+
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(flag = true, prefix = "FLAG_", value = {
             FLAG_SUPPRESS_SCRIM,
             FLAG_INSETS_ROUNDED_CORNER,
             FLAG_FORCE_CONSUMING,
             FLAG_ANIMATE_RESIZING,
+            FLAG_FORCE_CONSUMING_OPAQUE_CAPTION_BAR
     })
     public @interface Flags {}
 
@@ -555,6 +566,9 @@
         if ((flags & FLAG_ANIMATE_RESIZING) != 0) {
             joiner.add("ANIMATE_RESIZING");
         }
+        if ((flags & FLAG_FORCE_CONSUMING_OPAQUE_CAPTION_BAR) != 0) {
+            joiner.add("FORCE_CONSUMING_OPAQUE_CAPTION_BAR");
+        }
         return joiner.toString();
     }
 
diff --git a/core/java/android/view/InsetsSourceControl.java b/core/java/android/view/InsetsSourceControl.java
index 2efa647..7877352 100644
--- a/core/java/android/view/InsetsSourceControl.java
+++ b/core/java/android/view/InsetsSourceControl.java
@@ -31,6 +31,7 @@
 import android.os.Parcelable;
 import android.util.proto.ProtoOutputStream;
 import android.view.WindowInsets.Type.InsetsType;
+import android.view.inputmethod.ImeTracker;
 
 import java.io.PrintWriter;
 import java.util.Arrays;
@@ -56,6 +57,9 @@
     private boolean mSkipAnimationOnce;
     private int mParcelableFlags;
 
+    /** The token tracking the current IME request */
+    private @Nullable ImeTracker.Token mImeStatsToken;
+
     public InsetsSourceControl(int id, @InsetsType int type, @Nullable SurfaceControl leash,
             boolean initiallyVisible, Point surfacePosition, Insets insetsHint) {
         mId = id;
@@ -78,6 +82,7 @@
         mSurfacePosition = new Point(other.mSurfacePosition);
         mInsetsHint = other.mInsetsHint;
         mSkipAnimationOnce = other.getAndClearSkipAnimationOnce();
+        mImeStatsToken = other.getImeStatsToken();
     }
 
     public InsetsSourceControl(Parcel in) {
@@ -88,6 +93,7 @@
         mSurfacePosition = in.readTypedObject(Point.CREATOR);
         mInsetsHint = in.readTypedObject(Insets.CREATOR);
         mSkipAnimationOnce = in.readBoolean();
+        mImeStatsToken = in.readTypedObject(ImeTracker.Token.CREATOR);
     }
 
     public int getId() {
@@ -153,6 +159,15 @@
         return result;
     }
 
+    @Nullable
+    public ImeTracker.Token getImeStatsToken() {
+        return mImeStatsToken;
+    }
+
+    public void setImeStatsToken(@Nullable ImeTracker.Token imeStatsToken) {
+        mImeStatsToken = imeStatsToken;
+    }
+
     public void setParcelableFlags(int parcelableFlags) {
         mParcelableFlags = parcelableFlags;
     }
@@ -171,6 +186,7 @@
         dest.writeTypedObject(mSurfacePosition, mParcelableFlags);
         dest.writeTypedObject(mInsetsHint, mParcelableFlags);
         dest.writeBoolean(mSkipAnimationOnce);
+        dest.writeTypedObject(mImeStatsToken, mParcelableFlags);
     }
 
     public void release(Consumer<SurfaceControl> surfaceReleaseConsumer) {
@@ -196,13 +212,14 @@
                 && mInitiallyVisible == that.mInitiallyVisible
                 && mSurfacePosition.equals(that.mSurfacePosition)
                 && mInsetsHint.equals(that.mInsetsHint)
-                && mSkipAnimationOnce == that.mSkipAnimationOnce;
+                && mSkipAnimationOnce == that.mSkipAnimationOnce
+                && Objects.equals(mImeStatsToken, that.mImeStatsToken);
     }
 
     @Override
     public int hashCode() {
         return Objects.hash(mId, mType, mLeash, mInitiallyVisible, mSurfacePosition, mInsetsHint,
-                mSkipAnimationOnce);
+                mSkipAnimationOnce, mImeStatsToken);
     }
 
     @Override
@@ -225,6 +242,7 @@
         pw.print(" mSurfacePosition="); pw.print(mSurfacePosition);
         pw.print(" mInsetsHint="); pw.print(mInsetsHint);
         pw.print(" mSkipAnimationOnce="); pw.print(mSkipAnimationOnce);
+        pw.print(" mImeStatsToken="); pw.print(mImeStatsToken);
         pw.println();
     }
 
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
index bbd9acf..6b4340a 100644
--- a/core/java/android/view/InsetsState.java
+++ b/core/java/android/view/InsetsState.java
@@ -19,6 +19,7 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.util.SequenceUtils.getInitSeq;
 import static android.view.InsetsSource.FLAG_FORCE_CONSUMING;
+import static android.view.InsetsSource.FLAG_FORCE_CONSUMING_OPAQUE_CAPTION_BAR;
 import static android.view.InsetsSource.FLAG_INSETS_ROUNDED_CORNER;
 import static android.view.InsetsStateProto.DISPLAY_CUTOUT;
 import static android.view.InsetsStateProto.DISPLAY_FRAME;
@@ -54,6 +55,7 @@
 import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.window.flags.Flags;
 
 import java.io.PrintWriter;
 import java.util.Objects;
@@ -131,18 +133,25 @@
         final Rect relativeFrame = new Rect(frame);
         final Rect relativeFrameMax = new Rect(frame);
         @InsetsType int forceConsumingTypes = 0;
+        boolean forceConsumingOpaqueCaptionBar = false;
         @InsetsType int suppressScrimTypes = 0;
         final Rect[][] typeBoundingRectsMap = new Rect[Type.SIZE][];
         final Rect[][] typeMaxBoundingRectsMap = new Rect[Type.SIZE][];
         for (int i = mSources.size() - 1; i >= 0; i--) {
             final InsetsSource source = mSources.valueAt(i);
             final @InsetsType int type = source.getType();
+            final @InsetsSource.Flags int flags = source.getFlags();
 
-            if ((source.getFlags() & InsetsSource.FLAG_FORCE_CONSUMING) != 0) {
+            if ((flags & InsetsSource.FLAG_FORCE_CONSUMING) != 0) {
                 forceConsumingTypes |= type;
             }
 
-            if ((source.getFlags() & InsetsSource.FLAG_SUPPRESS_SCRIM) != 0) {
+            if (Flags.enableCaptionCompatInsetForceConsumptionAlways()
+                    && (flags & FLAG_FORCE_CONSUMING_OPAQUE_CAPTION_BAR) != 0) {
+                forceConsumingOpaqueCaptionBar = true;
+            }
+
+            if ((flags & InsetsSource.FLAG_SUPPRESS_SCRIM) != 0) {
                 suppressScrimTypes |= type;
             }
 
@@ -177,7 +186,8 @@
         }
 
         return new WindowInsets(typeInsetsMap, typeMaxInsetsMap, typeVisibilityMap, isScreenRound,
-                forceConsumingTypes, suppressScrimTypes, calculateRelativeCutout(frame),
+                forceConsumingTypes, forceConsumingOpaqueCaptionBar, suppressScrimTypes,
+                calculateRelativeCutout(frame),
                 calculateRelativeRoundedCorners(frame),
                 calculateRelativePrivacyIndicatorBounds(frame),
                 calculateRelativeDisplayShape(frame),
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 634469d..a7641c0 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -164,6 +164,9 @@
             float width, float height, float vecX, float vecY,
             float maxStretchAmountX, float maxStretchAmountY, float childRelativeLeft,
             float childRelativeTop, float childRelativeRight, float childRelativeBottom);
+    private static native void nativeSetEdgeExtensionEffect(long transactionObj, long nativeObj,
+                                                            boolean leftEdge, boolean rightEdge,
+                                                            boolean topEdge, boolean bottomEdge);
     private static native void nativeSetTrustedOverlay(long transactionObj, long nativeObject,
             int isTrustedOverlay);
     private static native void nativeSetDropInputMode(
@@ -3513,6 +3516,19 @@
         /**
          * @hide
          */
+        public Transaction setEdgeExtensionEffect(SurfaceControl sc, int edge) {
+            checkPreconditions(sc);
+
+            nativeSetEdgeExtensionEffect(
+                    mNativeObject, sc.mNativeObject,
+                    (edge & WindowInsets.Side.LEFT) != 0, (edge & WindowInsets.Side.RIGHT) != 0,
+                    (edge & WindowInsets.Side.TOP) != 0, (edge & WindowInsets.Side.BOTTOM) != 0);
+            return this;
+        }
+
+        /**
+         * @hide
+         */
         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.O)
         public Transaction setLayerStack(SurfaceControl sc, int layerStack) {
             checkPreconditions(sc);
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index fedbe4a..42d66ce 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -366,13 +366,8 @@
                         Log.e(TAG, "Received invalid input event");
                         return;
                     }
-                    try {
-                        vri.processingBackKey(true);
-                        vri.enqueueInputEvent(keyEvent, null /* receiver */, 0 /* flags */,
-                                true /* processImmediately */);
-                    } finally {
-                        vri.processingBackKey(false);
-                    }
+                    vri.enqueueInputEvent(keyEvent, null /* receiver */, 0 /* flags */,
+                            true /* processImmediately */);
                 });
         }
     };
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index a23e383..5f8bea1 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -11004,6 +11004,11 @@
             ? afm.isAutofillable(this) : false;
     }
 
+    /**
+     * Returns whether the view is autofillable.
+     *
+     * @return whether the view is autofillable, and should send out autofill request to provider.
+     */
     private boolean isAutofillable() {
         if (DBG) {
             Log.d(VIEW_LOG_TAG, "isAutofillable() entered.");
@@ -27631,6 +27636,23 @@
         return null;
     }
 
+
+    /**
+     * Performs the traversal to find views that are autofillable.
+     * Autofillable views are added to the provided list.
+     *
+     * <strong>Note:</strong>This method does not stop at the root namespace
+     * boundary.
+     *
+     * @param autofillableViews The output list of autofillable Views.
+     * @hide
+     */
+    public void findAutofillableViewsByTraversal(@NonNull List<View> autofillableViews) {
+        if (isAutofillable()) {
+            autofillableViews.add(this);
+        }
+    }
+
     /**
      * Look for a child view with the given tag.  If this view has the given
      * tag, return this view.
@@ -30597,6 +30619,8 @@
         }
     }
 
+    // Note that if the function returns true, it indicates aapt did not generate this id.
+    // However false value does not indicate that aapt did generated this id.
     private static boolean isViewIdGenerated(int id) {
         return (id & 0xFF000000) == 0 && (id & 0x00FFFFFF) != 0;
     }
@@ -34047,14 +34071,21 @@
     }
 
     private float convertVelocityToFrameRate(float velocityPps) {
-        // From UXR study, premium experience is:
-        // 1500+    dp/s: 120fps
-        // 0 - 1500 dp/s:  80fps
-        // OEMs are likely to modify this to balance battery and user experience for their
-        // specific device.
+        // Internal testing has shown that this gives a premium experience:
+        // above 300dp/s => 120fps
+        // between 300dp/s and 125fps => 80fps
+        // below 125dp/s => 60fps
         float density = mAttachInfo.mDensity;
         float velocityDps = velocityPps / density;
-        return (velocityDps >= 1500f) ? MAX_FRAME_RATE : 80f;
+        float frameRate;
+        if (velocityDps > 300f) {
+            frameRate = MAX_FRAME_RATE; // Use maximum at fast motion
+        } else if (velocityDps > 125f) {
+            frameRate = 80f; // Use medium frame rate when motion is slower
+        } else {
+            frameRate = 60f; // Use minimum frame rate when motion is very slow
+        }
+        return frameRate;
     }
 
     /**
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index b2c39b1..6f88386 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -1500,6 +1500,19 @@
         return null;
     }
 
+    /** @hide */
+    @Override
+    public void findAutofillableViewsByTraversal(@NonNull List<View> autofillableViews) {
+        super.findAutofillableViewsByTraversal(autofillableViews);
+
+        final int childrenCount = mChildrenCount;
+        final View[] children = mChildren;
+        for (int i = 0; i < childrenCount; i++) {
+            View child = children[i];
+            child.findAutofillableViewsByTraversal(autofillableViews);
+        }
+    }
+
     @Override
     public void dispatchWindowFocusChanged(boolean hasFocus) {
         super.dispatchWindowFocusChanged(hasFocus);
@@ -2798,9 +2811,10 @@
                     if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
                         handled = true;
                     } else {
-                        final boolean cancelChild = resetCancelNextUpFlag(target.child)
-                                || intercepted;
-                        if (dispatchTransformedTouchEvent(ev, cancelChild,
+                        final boolean cancelChild =
+                                (target.child != null && resetCancelNextUpFlag(target.child))
+                                        || intercepted;
+                        if (target.child != null && dispatchTransformedTouchEvent(ev, cancelChild,
                                 target.child, target.pointerIdBits)) {
                             handled = true;
                         }
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 3a1d833..9518abf 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -145,7 +145,6 @@
 import android.annotation.UiContext;
 import android.app.ActivityManager;
 import android.app.ActivityThread;
-import android.app.ICompatCameraControlCallback;
 import android.app.ResourcesManager;
 import android.app.WindowConfiguration;
 import android.app.compat.CompatChanges;
@@ -516,17 +515,6 @@
                 int newDisplayId, @Nullable ActivityWindowInfo activityWindowInfo) {
             onConfigurationChanged(overrideConfig, newDisplayId);
         }
-
-        /**
-         * Notify the corresponding activity about the request to show or hide a camera compat
-         * control for stretched issues in the viewfinder.
-         *
-         * @param showControl Whether the control should be shown or hidden.
-         * @param transformationApplied Whether the treatment is already applied.
-         * @param callback The callback executed when the user clicks on a control.
-         */
-        void requestCompatCameraControl(boolean showControl, boolean transformationApplied,
-                ICompatCameraControlCallback callback);
     }
 
     /**
@@ -727,8 +715,6 @@
     boolean mUpcomingWindowFocus;
     @GuardedBy("this")
     boolean mUpcomingInTouchMode;
-    // While set, allow this VRI to handle back key without drop it.
-    private boolean mProcessingBackKey;
     /**
      * Compatibility {@link OnBackInvokedCallback} for windowless window, to forward the back
      * key event host app.
@@ -7269,7 +7255,7 @@
             // Find a reason for dropping or canceling the event.
             final String reason;
             // The embedded window is focused, allow this VRI to handle back key.
-            if (!mAttachInfo.mHasWindowFocus && !(mProcessingBackKey && isBack(q.mEvent))
+            if (!mAttachInfo.mHasWindowFocus && !isBack(q.mEvent)
                     && !q.mEvent.isFromSource(InputDevice.SOURCE_CLASS_POINTER)
                     && !isAutofillUiShowing()) {
                 // This is a non-pointer event and the window doesn't currently have input focus
@@ -10059,6 +10045,24 @@
     }
 
     /**
+     * Dispatches the statsToken and IME visibility to the ImeInsetsSourceProvider.
+     *
+     * @param visible {@code true} if it became visible, {@code false} otherwise.
+     * @param statsToken the token tracking the current IME request.
+     *
+     * @hide
+     */
+    public void notifyImeVisibilityChanged(boolean visible, @NonNull ImeTracker.Token statsToken) {
+        ImeTracker.forLogging().onProgress(statsToken,
+                ImeTracker.PHASE_CLIENT_NOTIFY_IME_VISIBILITY_CHANGED);
+        try {
+            mWindowSession.notifyImeWindowVisibilityChangedFromClient(mWindow, visible, statsToken);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Represents a pending input event that is waiting in a queue.
      *
      * Input events are processed in serial order by the timestamp specified by
@@ -11218,11 +11222,6 @@
         mHandler.obtainMessage(MSG_REQUEST_SCROLL_CAPTURE, listener).sendToTarget();
     }
 
-    // Make this VRI able to process back key without drop it.
-    void processingBackKey(boolean processing) {
-        mProcessingBackKey = processing;
-    }
-
     /**
      * Collect and include any ScrollCaptureCallback instances registered with the window.
      *
@@ -12492,20 +12491,6 @@
         }
     }
 
-    /**
-     * Shows or hides a Camera app compat toggle for stretched issues with the requested state
-     * for the corresponding activity.
-     *
-     * @param showControl Whether the control should be shown or hidden.
-     * @param transformationApplied Whether the treatment is already applied.
-     * @param callback The callback executed when the user clicks on a control.
-    */
-    public void requestCompatCameraControl(boolean showControl, boolean transformationApplied,
-                ICompatCameraControlCallback callback) {
-        mActivityConfigCallback.requestCompatCameraControl(
-                showControl, transformationApplied, callback);
-    }
-
     boolean wasRelayoutRequested() {
         return mRelayoutRequested;
     }
@@ -12554,15 +12539,8 @@
      * @return whether the event was handled (i.e. onKeyPreIme consumed it if preImeOnly=true)
      */
     public boolean injectBackKeyEvents(boolean preImeOnly) {
-        boolean consumed;
-        try {
-            processingBackKey(true);
-            sendBackKeyEvent(KeyEvent.ACTION_DOWN, preImeOnly);
-            consumed = sendBackKeyEvent(KeyEvent.ACTION_UP, preImeOnly);
-        } finally {
-            processingBackKey(false);
-        }
-        return consumed;
+        sendBackKeyEvent(KeyEvent.ACTION_DOWN, preImeOnly);
+        return sendBackKeyEvent(KeyEvent.ACTION_UP, preImeOnly);
     }
 
     private boolean sendBackKeyEvent(int action, boolean preImeOnly) {
@@ -12896,11 +12874,6 @@
                     mFrameRateCategoryHighCount = FRAME_RATE_CATEGORY_COUNT;
         }
 
-        // If it's currently an intermittent update,
-        // we should keep mPreferredFrameRateCategory as NORMAL
-        if (intermittentUpdateState() == INTERMITTENT_STATE_INTERMITTENT) {
-            return;
-        }
 
         if (mFrameRateCategoryHighCount > 0) {
             mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_HIGH;
diff --git a/core/java/android/view/ViewRootInsetsControllerHost.java b/core/java/android/view/ViewRootInsetsControllerHost.java
index b66c59a..889acca4 100644
--- a/core/java/android/view/ViewRootInsetsControllerHost.java
+++ b/core/java/android/view/ViewRootInsetsControllerHost.java
@@ -19,12 +19,14 @@
 import static android.view.InsetsController.DEBUG;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.Context;
 import android.content.res.CompatibilityInfo;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.util.Log;
+import android.view.inputmethod.ImeTracker;
 import android.view.inputmethod.InputMethodManager;
 
 import java.util.List;
@@ -151,10 +153,17 @@
     }
 
     @Override
-    public void updateRequestedVisibleTypes(@WindowInsets.Type.InsetsType int types) {
+    public void updateRequestedVisibleTypes(@WindowInsets.Type.InsetsType int types,
+            @Nullable ImeTracker.Token statsToken) {
         try {
             if (mViewRoot.mAdded) {
-                mViewRoot.mWindowSession.updateRequestedVisibleTypes(mViewRoot.mWindow, types);
+                ImeTracker.forLogging().onProgress(statsToken,
+                        ImeTracker.PHASE_CLIENT_UPDATE_REQUESTED_VISIBLE_TYPES);
+                mViewRoot.mWindowSession.updateRequestedVisibleTypes(mViewRoot.mWindow, types,
+                        statsToken);
+            } else {
+                ImeTracker.forLogging().onFailed(statsToken,
+                        ImeTracker.PHASE_CLIENT_UPDATE_REQUESTED_VISIBLE_TYPES);
             }
         } catch (RemoteException e) {
             Log.e(TAG, "Failed to call insetsModified", e);
diff --git a/core/java/android/view/WindowInsets.java b/core/java/android/view/WindowInsets.java
index 987c8c8..e3ea6b22 100644
--- a/core/java/android/view/WindowInsets.java
+++ b/core/java/android/view/WindowInsets.java
@@ -97,6 +97,7 @@
     private final int mFrameHeight;
 
     private final @InsetsType int mForceConsumingTypes;
+    private final boolean mForceConsumingOpaqueCaptionBar;
     private final @InsetsType int mSuppressScrimTypes;
     private final boolean mSystemWindowInsetsConsumed;
     private final boolean mStableInsetsConsumed;
@@ -123,7 +124,7 @@
 
     static {
         CONSUMED = new WindowInsets(createCompatTypeMap(null), createCompatTypeMap(null),
-                createCompatVisibilityMap(createCompatTypeMap(null)), false, 0, 0, null,
+                createCompatVisibilityMap(createCompatTypeMap(null)), false, 0, false, 0, null,
                 null, null, null, systemBars(), false, null, null, 0, 0);
     }
 
@@ -144,6 +145,7 @@
             boolean[] typeVisibilityMap,
             boolean isRound,
             @InsetsType int forceConsumingTypes,
+            boolean forceConsumingOpaqueCaptionBar,
             @InsetsType int suppressScrimTypes,
             DisplayCutout displayCutout,
             RoundedCorners roundedCorners,
@@ -166,6 +168,7 @@
         mTypeVisibilityMap = typeVisibilityMap;
         mIsRound = isRound;
         mForceConsumingTypes = forceConsumingTypes;
+        mForceConsumingOpaqueCaptionBar = forceConsumingOpaqueCaptionBar;
         mSuppressScrimTypes = suppressScrimTypes;
         mCompatInsetsTypes = compatInsetsTypes;
         mCompatIgnoreVisibility = compatIgnoreVisibility;
@@ -196,7 +199,9 @@
         this(src.mSystemWindowInsetsConsumed ? null : src.mTypeInsetsMap,
                 src.mStableInsetsConsumed ? null : src.mTypeMaxInsetsMap,
                 src.mTypeVisibilityMap, src.mIsRound,
-                src.mForceConsumingTypes, src.mSuppressScrimTypes,
+                src.mForceConsumingTypes,
+                src.mForceConsumingOpaqueCaptionBar,
+                src.mSuppressScrimTypes,
                 displayCutoutCopyConstructorArgument(src),
                 src.mRoundedCorners,
                 src.mPrivacyIndicatorBounds,
@@ -257,7 +262,7 @@
     /** @hide */
     @UnsupportedAppUsage
     public WindowInsets(Rect systemWindowInsets) {
-        this(createCompatTypeMap(systemWindowInsets), null, new boolean[SIZE], false, 0, 0,
+        this(createCompatTypeMap(systemWindowInsets), null, new boolean[SIZE], false, 0, false, 0,
                 null, null, null, null, systemBars(), false /* compatIgnoreVisibility */,
                 new Rect[SIZE][], null, 0, 0);
     }
@@ -675,10 +680,10 @@
         return new WindowInsets(mSystemWindowInsetsConsumed ? null : mTypeInsetsMap,
                 mStableInsetsConsumed ? null : mTypeMaxInsetsMap,
                 mTypeVisibilityMap,
-                mIsRound, mForceConsumingTypes, mSuppressScrimTypes,
-                null /* displayCutout */, mRoundedCorners, mPrivacyIndicatorBounds, mDisplayShape,
-                mCompatInsetsTypes, mCompatIgnoreVisibility,
-                mSystemWindowInsetsConsumed ? null : mTypeBoundingRectsMap,
+                mIsRound, mForceConsumingTypes, mForceConsumingOpaqueCaptionBar,
+                mSuppressScrimTypes, null /* displayCutout */, mRoundedCorners,
+                mPrivacyIndicatorBounds, mDisplayShape, mCompatInsetsTypes,
+                mCompatIgnoreVisibility, mSystemWindowInsetsConsumed ? null : mTypeBoundingRectsMap,
                 mStableInsetsConsumed ? null : mTypeMaxBoundingRectsMap,
                 mFrameWidth, mFrameHeight);
     }
@@ -729,7 +734,8 @@
     public WindowInsets consumeSystemWindowInsets() {
         return new WindowInsets(null, null,
                 mTypeVisibilityMap,
-                mIsRound, mForceConsumingTypes, mSuppressScrimTypes,
+                mIsRound, mForceConsumingTypes, mForceConsumingOpaqueCaptionBar,
+                mSuppressScrimTypes,
                 // If the system window insets types contain displayCutout, we should also consume
                 // it.
                 (mCompatInsetsTypes & displayCutout()) != 0
@@ -1024,6 +1030,13 @@
     /**
      * @hide
      */
+    public boolean isForceConsumingOpaqueCaptionBar() {
+        return mForceConsumingOpaqueCaptionBar;
+    }
+
+    /**
+     * @hide
+     */
     public @InsetsType int getSuppressScrimTypes() {
         return mSuppressScrimTypes;
     }
@@ -1058,6 +1071,8 @@
         result.append("\n    ");
         result.append("forceConsumingTypes=" + Type.toString(mForceConsumingTypes));
         result.append("\n    ");
+        result.append("forceConsumingOpaqueCaptionBar=" + mForceConsumingOpaqueCaptionBar);
+        result.append("\n    ");
         result.append("suppressScrimTypes=" + Type.toString(mSuppressScrimTypes));
         result.append("\n    ");
         result.append("compatInsetsTypes=" + Type.toString(mCompatInsetsTypes));
@@ -1180,7 +1195,8 @@
                         ? null
                         : insetInsets(mTypeMaxInsetsMap, left, top, right, bottom),
                 mTypeVisibilityMap,
-                mIsRound, mForceConsumingTypes, mSuppressScrimTypes,
+                mIsRound, mForceConsumingTypes, mForceConsumingOpaqueCaptionBar,
+                mSuppressScrimTypes,
                 mDisplayCutoutConsumed
                         ? null
                         : mDisplayCutout == null
@@ -1214,6 +1230,7 @@
 
         return mIsRound == that.mIsRound
                 && mForceConsumingTypes == that.mForceConsumingTypes
+                && mForceConsumingOpaqueCaptionBar == that.mForceConsumingOpaqueCaptionBar
                 && mSuppressScrimTypes == that.mSuppressScrimTypes
                 && mSystemWindowInsetsConsumed == that.mSystemWindowInsetsConsumed
                 && mStableInsetsConsumed == that.mStableInsetsConsumed
@@ -1235,9 +1252,9 @@
     public int hashCode() {
         return Objects.hash(Arrays.hashCode(mTypeInsetsMap), Arrays.hashCode(mTypeMaxInsetsMap),
                 Arrays.hashCode(mTypeVisibilityMap), mIsRound, mDisplayCutout, mRoundedCorners,
-                mForceConsumingTypes, mSuppressScrimTypes, mSystemWindowInsetsConsumed,
-                mStableInsetsConsumed, mDisplayCutoutConsumed, mPrivacyIndicatorBounds,
-                mDisplayShape, Arrays.deepHashCode(mTypeBoundingRectsMap),
+                mForceConsumingTypes, mForceConsumingOpaqueCaptionBar, mSuppressScrimTypes,
+                mSystemWindowInsetsConsumed, mStableInsetsConsumed, mDisplayCutoutConsumed,
+                mPrivacyIndicatorBounds, mDisplayShape, Arrays.deepHashCode(mTypeBoundingRectsMap),
                 Arrays.deepHashCode(mTypeMaxBoundingRectsMap), mFrameWidth, mFrameHeight);
     }
 
@@ -1367,6 +1384,7 @@
 
         private boolean mIsRound;
         private @InsetsType int mForceConsumingTypes;
+        private boolean mForceConsumingOpaqueCaptionBar;
         private @InsetsType int mSuppressScrimTypes;
 
         private PrivacyIndicatorBounds mPrivacyIndicatorBounds = new PrivacyIndicatorBounds();
@@ -1399,6 +1417,7 @@
             mRoundedCorners = insets.mRoundedCorners;
             mIsRound = insets.mIsRound;
             mForceConsumingTypes = insets.mForceConsumingTypes;
+            mForceConsumingOpaqueCaptionBar = insets.mForceConsumingOpaqueCaptionBar;
             mSuppressScrimTypes = insets.mSuppressScrimTypes;
             mPrivacyIndicatorBounds = insets.mPrivacyIndicatorBounds;
             mDisplayShape = insets.mDisplayShape;
@@ -1687,6 +1706,13 @@
 
         /** @hide */
         @NonNull
+        public Builder setForceConsumingOpaqueCaptionBar(boolean forceConsumingOpaqueCaptionBar) {
+            mForceConsumingOpaqueCaptionBar = forceConsumingOpaqueCaptionBar;
+            return this;
+        }
+
+        /** @hide */
+        @NonNull
         public Builder setSuppressScrimTypes(@InsetsType int suppressScrimTypes) {
             mSuppressScrimTypes = suppressScrimTypes;
             return this;
@@ -1765,9 +1791,9 @@
         public WindowInsets build() {
             return new WindowInsets(mSystemInsetsConsumed ? null : mTypeInsetsMap,
                     mStableInsetsConsumed ? null : mTypeMaxInsetsMap, mTypeVisibilityMap,
-                    mIsRound, mForceConsumingTypes, mSuppressScrimTypes, mDisplayCutout,
-                    mRoundedCorners, mPrivacyIndicatorBounds, mDisplayShape, systemBars(),
-                    false /* compatIgnoreVisibility */,
+                    mIsRound, mForceConsumingTypes, mForceConsumingOpaqueCaptionBar,
+                    mSuppressScrimTypes, mDisplayCutout, mRoundedCorners, mPrivacyIndicatorBounds,
+                    mDisplayShape, systemBars(), false /* compatIgnoreVisibility */,
                     mSystemInsetsConsumed ? null : mTypeBoundingRectsMap,
                     mStableInsetsConsumed ? null : mTypeMaxBoundingRectsMap,
                     mFrameWidth, mFrameHeight);
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 14978ed..85d4ec0 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -478,6 +478,12 @@
      */
     int TRANSIT_SLEEP = 12;
     /**
+     * An Activity was going to be visible from back navigation.
+     * @hide
+     */
+    int TRANSIT_PREPARE_BACK_NAVIGATION = 13;
+
+    /**
      * The first slot for custom transition types. Callers (like Shell) can make use of custom
      * transition types for dealing with special cases. These types are effectively ignored by
      * Core and will just be passed along as part of TransitionInfo objects. An example is
@@ -505,6 +511,7 @@
             TRANSIT_PIP,
             TRANSIT_WAKE,
             TRANSIT_SLEEP,
+            TRANSIT_PREPARE_BACK_NAVIGATION,
             TRANSIT_FIRST_CUSTOM
     })
     @Retention(RetentionPolicy.SOURCE)
@@ -1918,6 +1925,7 @@
             case TRANSIT_PIP: return "PIP";
             case TRANSIT_WAKE: return "WAKE";
             case TRANSIT_SLEEP: return "SLEEP";
+            case TRANSIT_PREPARE_BACK_NAVIGATION: return "PREDICTIVE_BACK";
             case TRANSIT_FIRST_CUSTOM: return "FIRST_CUSTOM";
             default:
                 if (type > TRANSIT_FIRST_CUSTOM) {
diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java
index 7871858..0d027f1 100644
--- a/core/java/android/view/WindowlessWindowManager.java
+++ b/core/java/android/view/WindowlessWindowManager.java
@@ -30,6 +30,7 @@
 import android.util.MergedConfiguration;
 import android.view.View.FocusDirection;
 import android.view.WindowInsets.Type.InsetsType;
+import android.view.inputmethod.ImeTracker;
 import android.window.ClientWindowFrames;
 import android.window.InputTransferToken;
 import android.window.OnBackInvokedCallbackInfo;
@@ -596,7 +597,7 @@
 
     @Override
     public void updateRequestedVisibleTypes(IWindow window,
-            @InsetsType int requestedVisibleTypes)  {
+            @InsetsType int requestedVisibleTypes, @Nullable ImeTracker.Token imeStatsToken)  {
     }
 
     @Override
@@ -677,6 +678,11 @@
         return false;
     }
 
+    @Override
+    public void notifyImeWindowVisibilityChangedFromClient(IWindow window, boolean visible,
+            @NonNull ImeTracker.Token statsToken) {
+    }
+
     void setParentInterface(@Nullable ISurfaceControlViewHostParent parentInterface) {
         IBinder oldInterface = mParentInterface == null ? null : mParentInterface.asBinder();
         IBinder newInterface = parentInterface == null ? null : parentInterface.asBinder();
diff --git a/core/java/android/view/accessibility/AccessibilityCache.java b/core/java/android/view/accessibility/AccessibilityCache.java
index f3cde43..376e66f 100644
--- a/core/java/android/view/accessibility/AccessibilityCache.java
+++ b/core/java/android/view/accessibility/AccessibilityCache.java
@@ -19,6 +19,7 @@
 
 import static android.view.accessibility.AccessibilityNodeInfo.FOCUS_ACCESSIBILITY;
 
+import android.annotation.Nullable;
 import android.os.Build;
 import android.os.SystemClock;
 import android.util.ArraySet;
@@ -48,6 +49,8 @@
 
     private boolean mEnabled = true;
 
+    private final SparseArray<String> mWindowIdToEventSourceClassName = new SparseArray<>();
+
     /**
      * {@link AccessibilityEvent} types that are critical for the cache to stay up to date
      *
@@ -273,8 +276,11 @@
                     clearSubTreeLocked(event.getWindowId(), event.getSourceNodeId());
                 } break;
 
-                case AccessibilityEvent.TYPE_WINDOWS_CHANGED:
+                case AccessibilityEvent.TYPE_WINDOWS_CHANGED: {
                     mValidWindowCacheTimeStamp = event.getEventTime();
+                    if (event.getWindowChanges() == AccessibilityEvent.WINDOWS_CHANGE_REMOVED) {
+                        mWindowIdToEventSourceClassName.remove(event.getWindowId());
+                    }
                     if (event.getWindowChanges()
                             == AccessibilityEvent.WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED) {
                         // Don't need to clear all cache. Unless the changes are related to
@@ -282,8 +288,15 @@
                         clearWindowCacheLocked();
                         break;
                     }
+                    clear();
+                }
+                break;
                 case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED: {
                     mValidWindowCacheTimeStamp = event.getEventTime();
+                    if (event.getContentChangeTypes() == 0 && event.getClassName() != null) {
+                        mWindowIdToEventSourceClassName.put(event.getWindowId(),
+                                event.getClassName().toString());
+                    }
                     clear();
                 } break;
             }
@@ -907,6 +920,12 @@
         }
     }
 
+    /** Returns the source class associated with the window with the given id. */
+    @Nullable
+    public String getEventSourceClassName(int windowId) {
+        return mWindowIdToEventSourceClassName.get(windowId);
+    }
+
     // Layer of indirection included to break dependency chain for testing
     public static class AccessibilityNodeRefresher {
         /** Refresh the given AccessibilityNodeInfo object. */
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index 90cfcb1..a5ba294 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -982,7 +982,6 @@
     private long mParentNodeId = UNDEFINED_NODE_ID;
     private long mLabelForId = UNDEFINED_NODE_ID;
     private long mLabeledById = UNDEFINED_NODE_ID;
-    private LongArray mLabeledByIds;
     private long mTraversalBefore = UNDEFINED_NODE_ID;
     private long mTraversalAfter = UNDEFINED_NODE_ID;
 
@@ -3600,131 +3599,6 @@
     }
 
     /**
-     * Adds the view which serves as the label of the view represented by
-     * this info for accessibility purposes. When more than one labels are
-     * added, the content from each label is combined in the order that
-     * they are added.
-     * <p>
-     * If visible text can be used to describe or give meaning to this UI,
-     * this method is preferred. For example, a TextView before an EditText
-     * in the UI usually specifies what information is contained in the
-     * EditText. Hence, the EditText is labelled by the TextView.
-     * </p>
-     *
-     * @param label A view that labels this node's source.
-     */
-    @FlaggedApi(Flags.FLAG_SUPPORT_MULTIPLE_LABELEDBY)
-    public void addLabeledBy(@NonNull View label) {
-        addLabeledBy(label, AccessibilityNodeProvider.HOST_VIEW_ID);
-    }
-
-    /**
-     * Adds the view which serves as the label of the view represented by
-     * this info for accessibility purposes. If <code>virtualDescendantId</code>
-     * is {@link View#NO_ID} the root is set as the label. When more than one
-     * labels are added, the content from each label is combined in the order
-     * that they are added.
-     * <p>
-     * A virtual descendant is an imaginary View that is reported as a part of the view
-     * hierarchy for accessibility purposes. This enables custom views that draw complex
-     * content to report themselves as a tree of virtual views, thus conveying their
-     * logical structure.
-     * </p>
-     * <p>
-     * If visible text can be used to describe or give meaning to this UI,
-     * this method is preferred. For example, a TextView before an EditText
-     * in the UI usually specifies what information is contained in the
-     * EditText. Hence, the EditText is labelled by the TextView.
-     * </p>
-     * <p>
-     *   <strong>Note:</strong> Cannot be called from an
-     *   {@link android.accessibilityservice.AccessibilityService}.
-     *   This class is made immutable before being delivered to an AccessibilityService.
-     * </p>
-     *
-     * @param root A root whose virtual descendant labels this node's source.
-     * @param virtualDescendantId The id of the virtual descendant.
-     */
-    @FlaggedApi(Flags.FLAG_SUPPORT_MULTIPLE_LABELEDBY)
-    public void addLabeledBy(@NonNull View root, int virtualDescendantId) {
-        enforceNotSealed();
-        Preconditions.checkNotNull(root, "%s must not be null", root);
-        if (mLabeledByIds == null) {
-            mLabeledByIds = new LongArray();
-        }
-        mLabeledById = makeNodeId(root.getAccessibilityViewId(), virtualDescendantId);
-        mLabeledByIds.add(mLabeledById);
-    }
-
-    /**
-     * Gets the list of node infos which serve as the labels of the view represented by
-     * this info for accessibility purposes.
-     *
-     * @return The list of labels in the order that they were added.
-     */
-    @FlaggedApi(Flags.FLAG_SUPPORT_MULTIPLE_LABELEDBY)
-    public @NonNull List<AccessibilityNodeInfo> getLabeledByList() {
-        enforceSealed();
-        List<AccessibilityNodeInfo> labels = new ArrayList<>();
-        if (mLabeledByIds == null) {
-            return labels;
-        }
-        for (int i = 0; i < mLabeledByIds.size(); i++) {
-            labels.add(getNodeForAccessibilityId(mConnectionId, mWindowId, mLabeledByIds.get(i)));
-        }
-        return labels;
-    }
-
-    /**
-     * Removes a label. If the label was not previously added to the node,
-     * calling this method has no effect.
-     * <p>
-     * <strong>Note:</strong> Cannot be called from an
-     * {@link android.accessibilityservice.AccessibilityService}.
-     * This class is made immutable before being delivered to an AccessibilityService.
-     * </p>
-     *
-     * @param label The node which serves as this node's label.
-     * @return true if the label was present
-     * @see #addLabeledBy(View)
-     */
-    @FlaggedApi(Flags.FLAG_SUPPORT_MULTIPLE_LABELEDBY)
-    public boolean removeLabeledBy(@NonNull View label) {
-        return removeLabeledBy(label, AccessibilityNodeProvider.HOST_VIEW_ID);
-    }
-
-    /**
-     * Removes a virtual label which is a descendant of the given
-     * <code>root</code>. If the label was not previously added to the node,
-     * calling this method has no effect.
-     *
-     * @param root The root of the virtual subtree.
-     * @param virtualDescendantId The id of the virtual node which serves as this node's label.
-     * @return true if the label was present
-     * @see #addLabeledBy(View, int)
-     */
-    @FlaggedApi(Flags.FLAG_SUPPORT_MULTIPLE_LABELEDBY)
-    public boolean removeLabeledBy(@NonNull View root, int virtualDescendantId) {
-        enforceNotSealed();
-        final LongArray labeledByIds = mLabeledByIds;
-        if (labeledByIds == null) {
-            return false;
-        }
-        final int rootAccessibilityViewId =
-                (root != null) ? root.getAccessibilityViewId() : UNDEFINED_ITEM_ID;
-        final long labeledById = makeNodeId(rootAccessibilityViewId, virtualDescendantId);
-        if (mLabeledById == labeledById) {
-            mLabeledById = UNDEFINED_NODE_ID;
-        }
-        final int index = labeledByIds.indexOf(labeledById);
-        if (index < 0) {
-            return false;
-        }
-        labeledByIds.remove(index);
-        return true;
-    }
-
-    /**
      * Sets the view which serves as the label of the view represented by
      * this info for accessibility purposes.
      *
@@ -3757,17 +3631,7 @@
         enforceNotSealed();
         final int rootAccessibilityViewId = (root != null)
                 ? root.getAccessibilityViewId() : UNDEFINED_ITEM_ID;
-        if (Flags.supportMultipleLabeledby()) {
-            if (mLabeledByIds == null) {
-                mLabeledByIds = new LongArray();
-            } else {
-                mLabeledByIds.clear();
-            }
-        }
         mLabeledById = makeNodeId(rootAccessibilityViewId, virtualDescendantId);
-        if (Flags.supportMultipleLabeledby()) {
-            mLabeledByIds.add(mLabeledById);
-        }
     }
 
     /**
@@ -4378,12 +4242,6 @@
         fieldIndex++;
         if (mLabeledById != DEFAULT.mLabeledById) nonDefaultFields |= bitAt(fieldIndex);
         fieldIndex++;
-        if (Flags.supportMultipleLabeledby()) {
-            if (!LongArray.elementsEqual(mLabeledByIds, DEFAULT.mLabeledByIds)) {
-                nonDefaultFields |= bitAt(fieldIndex);
-            }
-            fieldIndex++;
-        }
         if (mTraversalBefore != DEFAULT.mTraversalBefore) nonDefaultFields |= bitAt(fieldIndex);
         fieldIndex++;
         if (mTraversalAfter != DEFAULT.mTraversalAfter) nonDefaultFields |= bitAt(fieldIndex);
@@ -4525,20 +4383,6 @@
         if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeLong(mParentNodeId);
         if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeLong(mLabelForId);
         if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeLong(mLabeledById);
-        if (Flags.supportMultipleLabeledby()) {
-            if (isBitSet(nonDefaultFields, fieldIndex++)) {
-                final LongArray labeledByIds = mLabeledByIds;
-                if (labeledByIds == null) {
-                    parcel.writeInt(0);
-                } else {
-                    final int labeledByIdsSize = labeledByIds.size();
-                    parcel.writeInt(labeledByIdsSize);
-                    for (int i = 0; i < labeledByIdsSize; i++) {
-                        parcel.writeLong(labeledByIds.get(i));
-                    }
-                }
-            }
-        }
         if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeLong(mTraversalBefore);
         if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeLong(mTraversalAfter);
         if (isBitSet(nonDefaultFields, fieldIndex++)) {
@@ -4706,9 +4550,6 @@
         mParentNodeId = other.mParentNodeId;
         mLabelForId = other.mLabelForId;
         mLabeledById = other.mLabeledById;
-        if (Flags.supportMultipleLabeledby()) {
-            mLabeledByIds = other.mLabeledByIds;
-        }
         mTraversalBefore = other.mTraversalBefore;
         mTraversalAfter = other.mTraversalAfter;
         mMinDurationBetweenContentChanges = other.mMinDurationBetweenContentChanges;
@@ -4815,20 +4656,6 @@
         if (isBitSet(nonDefaultFields, fieldIndex++)) mParentNodeId = parcel.readLong();
         if (isBitSet(nonDefaultFields, fieldIndex++)) mLabelForId = parcel.readLong();
         if (isBitSet(nonDefaultFields, fieldIndex++)) mLabeledById = parcel.readLong();
-        if (Flags.supportMultipleLabeledby()) {
-            if (isBitSet(nonDefaultFields, fieldIndex++)) {
-                final int labeledByIdsSize = parcel.readInt();
-                if (labeledByIdsSize <= 0) {
-                    mLabeledByIds = null;
-                } else {
-                    mLabeledByIds = new LongArray(labeledByIdsSize);
-                    for (int i = 0; i < labeledByIdsSize; i++) {
-                        final long labeledById = parcel.readLong();
-                        mLabeledByIds.add(labeledById);
-                    }
-                }
-            }
-        }
         if (isBitSet(nonDefaultFields, fieldIndex++)) mTraversalBefore = parcel.readLong();
         if (isBitSet(nonDefaultFields, fieldIndex++)) mTraversalAfter = parcel.readLong();
         if (isBitSet(nonDefaultFields, fieldIndex++)) {
diff --git a/core/java/android/view/autofill/AutofillClientController.java b/core/java/android/view/autofill/AutofillClientController.java
index 2f7adaa..abef7cf 100644
--- a/core/java/android/view/autofill/AutofillClientController.java
+++ b/core/java/android/view/autofill/AutofillClientController.java
@@ -39,6 +39,7 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.List;
 
 /**
  * A controller to manage the autofill requests for the {@link Activity}.
@@ -71,6 +72,7 @@
     private AutofillPopupWindow mAutofillPopupWindow;
     private boolean mAutoFillResetNeeded;
     private boolean mAutoFillIgnoreFirstResumePause;
+    private Boolean mRelayoutFix;
 
     /**
      * AutofillClientController constructor.
@@ -86,6 +88,25 @@
         return mAutofillManager;
     }
 
+    /**
+     * Whether to apply relayout fixes.
+     *
+     * @hide
+     */
+    public boolean isRelayoutFixEnabled() {
+        AutofillManager autofillManager = getAutofillManager();
+        if (autofillManager == null) {
+            if (Helper.sDebug) {
+                Log.d(TAG, "isRelayoutFixEnabled() : getAutofillManager() == null");
+            }
+            return false;
+        }
+        if (mRelayoutFix == null) {
+            mRelayoutFix = autofillManager.isRelayoutFixEnabled();
+        }
+        return mRelayoutFix;
+    }
+
     // ------------------ Called for Activity events ------------------
 
     /**
@@ -119,7 +140,54 @@
      * Called when the {@link Activity#onResume()} is called.
      */
     public void onActivityResumed() {
+        if (Helper.sVerbose) {
+            Log.v(TAG, "onActivityResumed()");
+        }
+        if (isRelayoutFixEnabled()) {
+            // Do nothing here. We'll handle it in onActivityPostResumed()
+            return;
+        }
+        if (Helper.sVerbose) {
+            Log.v(TAG, "onActivityResumed(): Relayout fix not enabled");
+        }
+        forResume();
+    }
+
+    /**
+     * Called when the {@link Activity#onPostResume()} is called.
+     */
+    public void onActivityPostResumed() {
+        if (Helper.sVerbose) {
+            Log.v(TAG, "onActivityPostResumed()");
+        }
+        if (!isRelayoutFixEnabled()) {
+            return;
+        }
+        if (Helper.sVerbose) {
+            Log.v(TAG, "onActivityPostResumed(): Relayout fix enabled");
+        }
+        forResume();
+    }
+
+    /**
+     * Code to execute when an app has resumed (or is about to resume)
+     */
+    private void forResume() {
         enableAutofillCompatibilityIfNeeded();
+        boolean relayoutFix = isRelayoutFixEnabled();
+        if (relayoutFix) {
+            if (getAutofillManager().shouldRetryFill()) {
+                if (Helper.sVerbose) {
+                    Log.v(TAG, "forResume(): Autofill potential relayout. Retrying fill.");
+                }
+                getAutofillManager().attemptRefill();
+            } else {
+                if (Helper.sVerbose) {
+                    Log.v(TAG, "forResume(): Not attempting refill.");
+                }
+            }
+        }
+
         if (mAutoFillResetNeeded) {
             if (!mAutoFillIgnoreFirstResumePause) {
                 View focus = mActivity.getCurrentFocus();
@@ -131,7 +199,16 @@
                     // ViewRootImpl.performTraversals() changes window visibility to VISIBLE.
                     // So we cannot call View.notifyEnterOrExited() which will do nothing
                     // when View.isVisibleToUser() is false.
-                    getAutofillManager().notifyViewEntered(focus);
+                    if (relayoutFix && getAutofillManager().isAuthenticationPending()) {
+                        if (Helper.sVerbose) {
+                            Log.v(TAG, "forResume(): ignoring focus due to auth pending");
+                        }
+                    } else {
+                        if (Helper.sVerbose) {
+                            Log.v(TAG, "forResume(): notifyViewEntered");
+                        }
+                        getAutofillManager().notifyViewEntered(focus);
+                    }
                 }
             }
         }
@@ -427,6 +504,22 @@
     }
 
     @Override
+    public List<View> autofillClientFindAutofillableViewsByTraversal() {
+        final ArrayList<View> views = new ArrayList<>();
+        final ArrayList<ViewRootImpl> roots =
+                WindowManagerGlobal.getInstance().getRootViews(mActivity.getActivityToken());
+
+        for (int rootNum = 0; rootNum < roots.size(); rootNum++) {
+            final View rootView = roots.get(rootNum).getView();
+
+            if (rootView != null) {
+                rootView.findAutofillableViewsByTraversal(views);
+            }
+        }
+        return views;
+    }
+
+    @Override
     public boolean autofillClientIsFillUiShowing() {
         return mAutofillPopupWindow != null && mAutofillPopupWindow.isShowing();
     }
@@ -496,4 +589,9 @@
             Log.e(TAG, "authenticate() failed for intent:" + intent, e);
         }
     }
+
+    @Override
+    public boolean isActivityResumed() {
+        return mActivity.isResumed();
+    }
 }
diff --git a/core/java/android/view/autofill/AutofillFeatureFlags.java b/core/java/android/view/autofill/AutofillFeatureFlags.java
index 5b1c7d5..0ab51e4 100644
--- a/core/java/android/view/autofill/AutofillFeatureFlags.java
+++ b/core/java/android/view/autofill/AutofillFeatureFlags.java
@@ -563,7 +563,7 @@
         return DeviceConfig.getBoolean(
                 DeviceConfig.NAMESPACE_AUTOFILL,
                 DEVICE_CONFIG_ENABLE_RELAYOUT,
-                true);
+                false);
     }
 
     /** @hide */
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 515ed0e..79ecfe1e 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -114,6 +114,7 @@
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
+import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicBoolean;
@@ -774,6 +775,13 @@
     // dataset in responses. Used to avoid request pre-fill request again and again.
     private final ArraySet<AutofillId> mAllTrackedViews = new ArraySet<>();
 
+    // Whether we need to re-attempt fill again. Needed for case of relayout.
+    private boolean mFillReAttemptNeeded = false;
+
+    private Map<Integer, AutofillId> mFingerprintToViewMap = new ArrayMap<>();
+
+    private AutofillStateFingerprint mAutofillStateFingerprint;
+
     /** @hide */
     public interface AutofillClient {
         /**
@@ -874,6 +882,13 @@
         @Nullable View autofillClientFindViewByAccessibilityIdTraversal(int viewId, int windowId);
 
         /**
+         * Finds all the autofillable views on the screen.
+         *
+         * @return The list of views that are autofillable.
+         */
+        List<View> autofillClientFindAutofillableViewsByTraversal();
+
+        /**
          * Runs the specified action on the UI thread.
          */
         void autofillClientRunOnUiThread(Runnable action);
@@ -902,6 +917,11 @@
          * @return An ID that is unique in the activity.
          */
         @Nullable AutofillId autofillClientGetNextAutofillId();
+
+        /**
+         * @return Whether the activity is resumed or not.
+         */
+        boolean isActivityResumed();
     }
 
     /**
@@ -912,6 +932,7 @@
         mService = service;
         mOptions = context.getAutofillOptions();
         mIsFillRequested = new AtomicBoolean(false);
+        mAutofillStateFingerprint = AutofillStateFingerprint.createInstance();
 
         mIsFillDialogEnabled = AutofillFeatureFlags.isFillDialogEnabled();
         mFillDialogEnabledHints = AutofillFeatureFlags.getFillDialogEnabledHints();
@@ -1350,6 +1371,20 @@
             mOnInvisibleCalled = true;
 
             if (isExpiredResponse) {
+                if (mRelayoutFix && isAuthenticationPending()) {
+                    Log.i(TAG, "onInvisibleForAutofill(): Ignoring expiringResponse due to pending"
+                            + " authentication");
+                    try {
+                        mService.notifyNotExpiringResponseDuringAuth(
+                                mSessionId, mContext.getUserId());
+                    } catch (RemoteException e) {
+                        // The failure could be a consequence of something going wrong on the
+                        // server side. Do nothing here since it's just logging, but it's
+                        // possible follow-up actions may fail.
+                    }
+                    return;
+                }
+                Log.i(TAG, "onInvisibleForAutofill(): expiringResponse");
                 // Notify service the response has expired.
                 updateSessionLocked(/* id= */ null, /* bounds= */ null, /* value= */ null,
                         ACTION_RESPONSE_EXPIRED, /* flags= */ 0);
@@ -1498,6 +1533,57 @@
     }
 
     /**
+     * Called to know whether authentication was pending.
+     * @hide
+     */
+    public boolean isAuthenticationPending() {
+        return mState == STATE_PENDING_AUTHENTICATION;
+    }
+
+    /**
+     * Called to log notify view entered was ignored due to pending auth
+     * @hide
+     */
+    public void notifyViewEnteredIgnoredDuringAuthCount() {
+        try {
+            mService.notifyViewEnteredIgnoredDuringAuthCount(mSessionId, mContext.getUserId());
+        } catch (RemoteException e) {
+            // The failure could be a consequence of something going wrong on the
+            // server side. Do nothing here since it's just logging, but it's
+            // possible follow-up actions may fail.
+        }
+    }
+
+    /**
+     * Called to check if we should retry fill.
+     * Useful for knowing whether to attempt refill after relayout.
+     *
+     * @hide
+     */
+    public boolean shouldRetryFill() {
+        synchronized (mLock) {
+            return isAuthenticationPending() && mFillReAttemptNeeded;
+        }
+    }
+
+    /**
+     * Called when a potential relayout may have occurred.
+     *
+     * @return whether refill was done. True if refill was done partially or fully.
+     * @hide
+     */
+    public boolean attemptRefill() {
+        Log.i(TAG, "Attempting refill");
+        // Find active autofillable views. Compute their fingerprints
+        List<View> autofillableViews =
+                getClient().autofillClientFindAutofillableViewsByTraversal();
+        if (sDebug) {
+            Log.d(TAG, "Autofillable views count:" + autofillableViews.size());
+        }
+        return mAutofillStateFingerprint.attemptRefill(autofillableViews, this);
+    }
+
+    /**
      * Called when a {@link View} that supports autofill is entered.
      *
      * @param view {@link View} that was entered.
@@ -2455,7 +2541,13 @@
 
     /** @hide */
     public void onAuthenticationResult(int authenticationId, Intent data, View focusView) {
+        if (sVerbose) {
+            Log.v(TAG, "onAuthenticationResult(): authId= " + authenticationId + ", data=" + data);
+        }
         if (!hasAutofillFeature()) {
+            if (sVerbose) {
+                Log.v(TAG, "onAuthenticationResult(): autofill not enabled");
+            }
             return;
         }
         // TODO: the result code is being ignored, so this method is not reliably
@@ -2463,10 +2555,6 @@
         // set the EXTRA_AUTHENTICATION_RESULT extra, but it could cause weird results if the
         // service set the extra and returned RESULT_CANCELED...
 
-        if (sDebug) {
-            Log.d(TAG, "onAuthenticationResult(): id= " + authenticationId + ", data=" + data);
-        }
-
         synchronized (mLock) {
             if (!isActiveLocked()) {
                 Log.w(TAG, "onAuthenticationResult(): sessionId=" + mSessionId + " not active");
@@ -2623,6 +2711,7 @@
             mSessionId = receiver.getIntResult();
             if (mSessionId != NO_SESSION) {
                 mState = STATE_ACTIVE;
+                mAutofillStateFingerprint.setSessionId(mSessionId);
             }
             final int extraFlags = receiver.getOptionalExtraIntResult(0);
             if ((extraFlags & RECEIVER_FLAG_SESSION_FOR_AUGMENTED_AUTOFILL_ONLY) != 0) {
@@ -2684,6 +2773,9 @@
         if (resetEnteredIds) {
             mEnteredIds = null;
         }
+        mFillReAttemptNeeded = false;
+        mFingerprintToViewMap.clear();
+        mAutofillStateFingerprint = AutofillStateFingerprint.createInstance();
     }
 
     @GuardedBy("mLock")
@@ -2946,8 +3038,12 @@
             Intent fillInIntent, boolean authenticateInline) {
         synchronized (mLock) {
             if (sessionId == mSessionId) {
-                if (mRelayoutFixDeprecated) {
+                if (mRelayoutFixDeprecated || mRelayoutFix) {
                     mState = STATE_PENDING_AUTHENTICATION;
+                    if (sVerbose) {
+                        Log.v(TAG, "entering STATE_PENDING_AUTHENTICATION : mRelayoutFix:"
+                                + mRelayoutFix);
+                    }
                 }
                 final AutofillClient client = getClient();
                 if (client != null) {
@@ -3153,17 +3249,56 @@
 
     @GuardedBy("mLock")
     private void handleFailedIdsLocked(@NonNull ArrayList<AutofillId> failedIds) {
+        handleFailedIdsLocked(failedIds, null, false, false);
+    }
+
+    @GuardedBy("mLock")
+    private void handleFailedIdsLocked(@NonNull ArrayList<AutofillId> failedIds,
+            ArrayList<AutofillValue> failedAutofillValues, boolean hideHighlight,
+            boolean isRefill) {
         if (!failedIds.isEmpty() && sVerbose) {
             Log.v(TAG, "autofill(): total failed views: " + failedIds);
         }
+
+        if (mRelayoutFix && !failedIds.isEmpty()) {
+            // Activity isn't in resumed state, so it's very possible that relayout could've
+            // occurred, so wait for it to declare proper failure. It's a temporary failure at the
+            // moment. We'll try again later when the activity is resumed.
+
+            // The above doesn't seem to be the correct way. Look for pending auth cases.
+            // TODO(b/238252288): Check whether there was any auth done at all
+            mFillReAttemptNeeded = true;
+            mAutofillStateFingerprint.storeFailedIdsAndValues(
+                    failedIds, failedAutofillValues, hideHighlight);
+        }
         try {
-            mService.setAutofillFailure(mSessionId, failedIds, mContext.getUserId());
+            mService.setAutofillFailure(mSessionId, failedIds, isRefill, mContext.getUserId());
         } catch (RemoteException e) {
             // In theory, we could ignore this error since it's not a big deal, but
             // in reality, we rather crash the app anyways, as the failure could be
             // a consequence of something going wrong on the server side...
             throw e.rethrowFromSystemServer();
         }
+        if (mRelayoutFix && !failedIds.isEmpty()) {
+            if (!getClient().isActivityResumed()) {
+                if (sVerbose) {
+                    Log.v(TAG, "handleFailedIdsLocked(): failed id's exist, but activity not"
+                            + " resumed");
+                }
+            } else {
+                if (isRefill) {
+                    Log.i(TAG, "handleFailedIdsLocked(): Attempted refill, but failed");
+                } else {
+                    // activity has been resumed, try to re-fill
+                    // getClient().isActivityResumed() && !failedIds.isEmpty() && !isRefill
+                    // TODO(b/238252288): Do better state management, and only trigger the following
+                    //  if there was auth previously.
+                    Log.i(TAG, "handleFailedIdsLocked(): Attempting refill");
+                    attemptRefill();
+                    mFillReAttemptNeeded = false;
+                }
+            }
+        }
     }
 
     private void autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values,
@@ -3178,13 +3313,46 @@
                 return;
             }
 
-            final int itemCount = ids.size();
-            int numApplied = 0;
-            ArrayMap<View, SparseArray<AutofillValue>> virtualValues = null;
             final View[] views = client.autofillClientFindViewsByAutofillIdTraversal(
                     Helper.toArray(ids));
 
+            autofill(views, ids, values, hideHighlight, false);
+        }
+    }
+
+    void autofill(View[] views, List<AutofillId> ids, List<AutofillValue> values,
+            boolean hideHighlight, boolean isRefill) {
+        if (sVerbose) {
+            Log.v(TAG, "autofill() ids:" + ids + " isRefill:" + isRefill);
+        }
+        synchronized (mLock) {
+            final AutofillClient client = getClient();
+            if (client == null) {
+                return;
+            }
+
+            if (ids == null) {
+                Log.i(TAG, "autofill(): No id's to fill");
+                return;
+            }
+
+            if (mRelayoutFix && isRefill) {
+                try {
+                    mService.setAutofillIdsAttemptedForRefill(
+                            mSessionId, ids, mContext.getUserId());
+                } catch (RemoteException e) {
+                    // The failure could be a consequence of something going wrong on the
+                    // server side. Do nothing here since it's just logging, but it's
+                    // possible follow-up actions may fail.
+                }
+            }
+
+            final int itemCount = ids.size();
+            int numApplied = 0;
+            ArrayMap<View, SparseArray<AutofillValue>> virtualValues = null;
+
             ArrayList<AutofillId> failedIds = new ArrayList<>();
+            ArrayList<AutofillValue> failedAutofillValues = new ArrayList<>();
 
             if (mLastAutofilledData == null) {
                 mLastAutofilledData = new ParcelableMap(itemCount);
@@ -3199,7 +3367,9 @@
                     // the service; this is fine, but we need to update the view status in the
                     // server side so it can be triggered again.
                     Log.d(TAG, "autofill(): no View with id " + id);
+                    // Possible relayout scenario
                     failedIds.add(id);
+                    failedAutofillValues.add(value);
                     continue;
                 }
                 // Mark the view as to be autofilled with 'value'
@@ -3230,7 +3400,8 @@
                 }
             }
 
-            handleFailedIdsLocked(failedIds);
+            handleFailedIdsLocked(
+                    failedIds, failedAutofillValues, hideHighlight, isRefill);
 
             if (virtualValues != null) {
                 for (int i = 0; i < virtualValues.size(); i++) {
@@ -3284,7 +3455,7 @@
     private void reportAutofillContentFailure(AutofillId id) {
         try {
             mService.setAutofillFailure(mSessionId, Collections.singletonList(id),
-                    mContext.getUserId());
+                    false /* isRefill */, mContext.getUserId());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -3311,20 +3482,22 @@
     }
 
     /**
-     *  Set the tracked views.
+     * Set the tracked views.
      *
-     * @param trackedIds The views to be tracked.
+     * @param trackedIds              The views to be tracked.
      * @param saveOnAllViewsInvisible Finish the session once all tracked views are invisible.
-     * @param saveOnFinish Finish the session once the activity is finished.
-     * @param fillableIds Views that might anchor FillUI.
-     * @param saveTriggerId View that when clicked triggers commit().
+     * @param saveOnFinish            Finish the session once the activity is finished.
+     * @param fillableIds             Views that might anchor FillUI.
+     * @param saveTriggerId           View that when clicked triggers commit().
      */
     private void setTrackedViews(int sessionId, @Nullable AutofillId[] trackedIds,
             boolean saveOnAllViewsInvisible, boolean saveOnFinish,
-            @Nullable AutofillId[] fillableIds, @Nullable AutofillId saveTriggerId) {
+            @Nullable AutofillId[] fillableIds, @Nullable AutofillId saveTriggerId,
+            boolean shouldGrabViewFingerprints) {
         if (saveTriggerId != null) {
             saveTriggerId.resetSessionId();
         }
+        final ArraySet<AutofillId> allFillableIds = new ArraySet<>();
         synchronized (mLock) {
             if (sVerbose) {
                 Log.v(TAG, "setTrackedViews(): sessionId=" + sessionId
@@ -3334,6 +3507,7 @@
                         + ", fillableIds=" + Arrays.toString(fillableIds)
                         + ", saveTrigerId=" + saveTriggerId
                         + ", mFillableIds=" + mFillableIds
+                        + ", shouldGrabViewFingerprints=" + shouldGrabViewFingerprints
                         + ", mEnabled=" + mEnabled
                         + ", mSessionId=" + mSessionId);
             }
@@ -3367,7 +3541,6 @@
                     trackedIds = null;
                 }
 
-                final ArraySet<AutofillId> allFillableIds = new ArraySet<>();
                 if (mFillableIds != null) {
                     allFillableIds.addAll(mFillableIds);
                 }
@@ -3386,6 +3559,12 @@
                     mTrackedViews = null;
                 }
             }
+            if (mRelayoutFix && shouldGrabViewFingerprints) {
+                // For all the views: tracked and others, calculate fingerprints and store them.
+                mAutofillStateFingerprint.setUseRelativePosition(mRelativePositionForRelayout);
+                mAutofillStateFingerprint.storeStatePriorToAuthentication(
+                        getClient(), allFillableIds);
+            }
         }
     }
 
@@ -3807,7 +3986,7 @@
 
     @GuardedBy("mLock")
     private boolean isPendingAuthenticationLocked() {
-        return mRelayoutFixDeprecated && mState == STATE_PENDING_AUTHENTICATION;
+        return (mRelayoutFixDeprecated || mRelayoutFix) && mState == STATE_PENDING_AUTHENTICATION;
     }
 
     @GuardedBy("mLock")
@@ -3820,7 +3999,7 @@
         return mState == STATE_FINISHED;
     }
 
-    private void post(Runnable runnable) {
+    void post(Runnable runnable) {
         final AutofillClient client = getClient();
         if (client == null) {
             if (sVerbose) Log.v(TAG, "ignoring post() because client is null");
@@ -4662,11 +4841,11 @@
         @Override
         public void setTrackedViews(int sessionId, AutofillId[] ids,
                 boolean saveOnAllViewsInvisible, boolean saveOnFinish, AutofillId[] fillableIds,
-                AutofillId saveTriggerId) {
+                AutofillId saveTriggerId, boolean shouldGrabViewFingerprints) {
             final AutofillManager afm = mAfm.get();
             if (afm != null) {
                 afm.post(() -> afm.setTrackedViews(sessionId, ids, saveOnAllViewsInvisible,
-                        saveOnFinish, fillableIds, saveTriggerId));
+                        saveOnFinish, fillableIds, saveTriggerId, shouldGrabViewFingerprints));
             }
         }
 
diff --git a/core/java/android/view/autofill/AutofillStateFingerprint.java b/core/java/android/view/autofill/AutofillStateFingerprint.java
new file mode 100644
index 0000000..2db4285
--- /dev/null
+++ b/core/java/android/view/autofill/AutofillStateFingerprint.java
@@ -0,0 +1,352 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.autofill;
+
+import static android.view.autofill.Helper.sDebug;
+
+import android.annotation.NonNull;
+import android.util.ArrayMap;
+import android.util.Log;
+import android.util.Slog;
+import android.view.View;
+import android.widget.TextView;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * This class manages and stores the autofillable views fingerprints for use in relayout situations.
+ * @hide
+ */
+@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+public final class AutofillStateFingerprint {
+
+    ArrayList<AutofillId> mPriorAutofillIds;
+    ArrayList<Integer> mViewHashCodes; // each entry corresponding to mPriorAutofillIds .
+
+    boolean mHideHighlight = false;
+
+    private int mSessionId;
+
+    Map<Integer, AutofillId> mHashToAutofillIdMap = new ArrayMap<>();
+    Map<AutofillId, AutofillId> mOldIdsToCurrentAutofillIdMap = new ArrayMap<>();
+
+    // These failed id's are attempted to be refilled again after relayout.
+    private ArrayList<AutofillId> mFailedIds = new ArrayList<>();
+    private ArrayList<AutofillValue> mFailedAutofillValues = new ArrayList<>();
+
+    // whether to use relative positions for computing hashes.
+    private boolean mUseRelativePosition;
+
+    private static final String TAG = "AutofillStateFingerprint";
+
+    /**
+     * Returns an instance of this class
+     */
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public static AutofillStateFingerprint createInstance() {
+        return new AutofillStateFingerprint();
+    }
+
+    private AutofillStateFingerprint() {
+    }
+
+    /**
+     * Set sessionId for the instance
+     */
+    void setSessionId(int sessionId) {
+        mSessionId = sessionId;
+    }
+
+    /**
+     * Sets whether relative position of the views should be used to calculate fingerprints.
+     */
+    void setUseRelativePosition(boolean useRelativePosition) {
+        mUseRelativePosition = useRelativePosition;
+    }
+
+    /**
+     * Store the state of the views prior to the authentication.
+     */
+    void storeStatePriorToAuthentication(
+            AutofillManager.AutofillClient client, Set<AutofillId> autofillIds) {
+        if (mUseRelativePosition) {
+            List<View> autofillableViews = client.autofillClientFindAutofillableViewsByTraversal();
+            if (sDebug) {
+                Log.d(TAG, "Autofillable views count prior to auth:" + autofillableViews.size());
+            }
+//            ArrayList<Integer> hashes = getFingerprintIds(autofillableViews);
+
+            ArrayMap<Integer, View> hashes = getFingerprintIds(autofillableViews);
+            for (Map.Entry<Integer, View> entry : hashes.entrySet()) {
+                View view = entry.getValue();
+                if (view != null) {
+                    mHashToAutofillIdMap.put(entry.getKey(), view.getAutofillId());
+                } else {
+                    if (sDebug) {
+                        Log.d(TAG, "Encountered null view");
+                    }
+                }
+            }
+        } else {
+            // Just use the provided autofillIds and get their hashes
+            if (sDebug) {
+                Log.d(TAG, "Size of autofillId's being stored: " + autofillIds.size()
+                        + " list:" + autofillIds);
+            }
+            AutofillId[] autofillIdsArr = Helper.toArray(autofillIds);
+            View[] views = client.autofillClientFindViewsByAutofillIdTraversal(autofillIdsArr);
+            for (int i = 0; i < autofillIdsArr.length; i++) {
+                View view = views[i];
+                if (view != null) {
+                    int id = getEphemeralFingerprintId(view, 0 /* position irrelevant */);
+                    AutofillId autofillId = view.getAutofillId();
+                    autofillId.setSessionId(mSessionId);
+                    mHashToAutofillIdMap.put(id, autofillId);
+                } else {
+                    if (sDebug) {
+                        Log.d(TAG, "Encountered null view");
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Store failed ids, so that they can be refilled later
+     */
+    void storeFailedIdsAndValues(
+            @NonNull ArrayList<AutofillId> failedIds,
+            ArrayList<AutofillValue> failedAutofillValues,
+            boolean hideHighlight) {
+        for (AutofillId failedId : failedIds) {
+            if (failedId != null) {
+                failedId.setSessionId(mSessionId);
+            } else {
+                if (sDebug) {
+                    Log.d(TAG, "Got null failed ids");
+                }
+            }
+        }
+        mFailedIds = failedIds;
+        mFailedAutofillValues = failedAutofillValues;
+        mHideHighlight = hideHighlight;
+    }
+
+    private void dumpCurrentState() {
+        Log.d(TAG, "FailedId's: " + mFailedIds);
+        Log.d(TAG, "Hashes from map" + mHashToAutofillIdMap);
+    }
+
+    boolean attemptRefill(
+            List<View> currentAutofillableViews, @NonNull AutofillManager autofillManager) {
+        if (sDebug) {
+            dumpCurrentState();
+        }
+        // For the autofillable views, compute their hashes
+        ArrayMap<Integer, View> currentHashes = getFingerprintIds(currentAutofillableViews);
+
+        // For the computed hashes, try to look for the old fingerprints.
+        // If match found, update the new autofill ids of those views
+        Map<AutofillId, View> oldFailedIdsToCurrentViewMap = new HashMap<>();
+        for (Map.Entry<Integer, View> entry : currentHashes.entrySet()) {
+            View view = entry.getValue();
+            int currentHash = entry.getKey();
+            AutofillId currentAutofillId = view.getAutofillId();
+            currentAutofillId.setSessionId(mSessionId);
+            if (mHashToAutofillIdMap.containsKey(currentHash)) {
+                AutofillId oldAutofillId = mHashToAutofillIdMap.get(currentHash);
+                oldAutofillId.setSessionId(mSessionId);
+                mOldIdsToCurrentAutofillIdMap.put(oldAutofillId, currentAutofillId);
+                Log.i(TAG, "Mapping current autofill id: " + view.getAutofillId()
+                        + " to existing autofill id " + oldAutofillId);
+
+                oldFailedIdsToCurrentViewMap.put(oldAutofillId, view);
+            } else {
+                Log.i(TAG, "Couldn't map current autofill id: " + view.getAutofillId()
+                        + " with currentHash:" + currentHash + " for view:" + view);
+            }
+        }
+
+        int viewsCount = 0;
+        View[] views = new View[mFailedIds.size()];
+        for (int i = 0; i < mFailedIds.size(); i++) {
+            AutofillId oldAutofillId = mFailedIds.get(i);
+            AutofillId currentAutofillId = mOldIdsToCurrentAutofillIdMap.get(oldAutofillId);
+            if (currentAutofillId == null) {
+                if (sDebug) {
+                    Log.d(TAG, "currentAutofillId = null");
+                }
+            }
+            mFailedIds.set(i, currentAutofillId);
+            views[i] = oldFailedIdsToCurrentViewMap.get(oldAutofillId);
+            if (views[i] != null) {
+                viewsCount++;
+            }
+        }
+
+        if (sDebug) {
+            dumpCurrentState();
+        }
+
+        // Attempt autofill now
+        Slog.i(TAG, "Attempting refill of views. Found " + viewsCount
+                + " views to refill from previously " + mFailedIds.size()
+                + " failed ids:" + mFailedIds);
+        autofillManager.post(
+                () -> autofillManager.autofill(
+                        views, mFailedIds, mFailedAutofillValues, mHideHighlight,
+                        true /* isRefill */));
+
+        return false;
+    }
+
+    /**
+     * Retrieves fingerprint hashes for the views
+     */
+    ArrayMap<Integer, View> getFingerprintIds(@NonNull List<View> views) {
+        ArrayMap<Integer, View> map = new ArrayMap<>();
+        if (mUseRelativePosition) {
+            Collections.sort(views, (View v1, View v2) -> {
+                int[] posV1 = v1.getLocationOnScreen();
+                int[] posV2 = v2.getLocationOnScreen();
+
+                int compare = posV1[0] - posV2[0]; // x coordinate
+                if (compare != 0) {
+                    return compare;
+                }
+                compare = posV1[1] - posV2[1]; // y coordinate
+                if (compare != 0) {
+                    return compare;
+                }
+                // Sort on vertical
+                compare = compareTop(v1, v2);
+                if (compare != 0) {
+                    return compare;
+                }
+                compare = compareBottom(v1, v2);
+                if (compare != 0) {
+                    return compare;
+                }
+                compare = compareLeft(v1, v2);
+                if (compare != 0) {
+                    return compare;
+                }
+                return compareRight(v1, v2);
+                // Note that if compareRight also returned 0, that means both the views have exact
+                // same location, so just treat them as equal
+            });
+        }
+        for (int i = 0; i < views.size(); i++) {
+            View view = views.get(i);
+            map.put(getEphemeralFingerprintId(view, i), view);
+        }
+        return map;
+    }
+
+    /**
+     * Returns fingerprint hash for the view.
+     */
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+    public int getEphemeralFingerprintId(View v, int position) {
+        if (v == null) return -1;
+        int inputType = Integer.MIN_VALUE;
+        int imeOptions = Integer.MIN_VALUE;
+        boolean isSingleLine = false;
+        CharSequence hints = "";
+        if (v instanceof TextView) {
+            TextView tv = (TextView) v;
+            inputType = tv.getInputType();
+            hints = tv.getHint();
+            isSingleLine = tv.isSingleLine();
+            imeOptions = tv.getImeOptions();
+            // TODO(b/238252288): Consider adding more IME related fields.
+        }
+        CharSequence contentDesc = v.getContentDescription();
+        CharSequence tooltip = v.getTooltipText();
+
+        int autofillType = v.getAutofillType();
+        String[] autofillHints = v.getAutofillHints();
+        int visibility = v.getVisibility();
+
+        int paddingLeft = v.getPaddingLeft();
+        int paddingRight = v.getPaddingRight();
+        int paddingTop = v.getPaddingTop();
+        int paddingBottom = v.getPaddingBottom();
+
+        // TODO(b/238252288): Following are making relayout flaky. Do more analysis to figure out
+        //  why.
+        int height = v.getHeight();
+        int width = v.getWidth();
+
+        // Order doesn't matter much here. We can change the order, as long as we use the same
+        // order for storing and fetching fingerprints. The order can be changed in platform
+        // versions.
+        int hash = Objects.hash(visibility, inputType, imeOptions, isSingleLine, hints,
+                contentDesc, tooltip, autofillType, Arrays.deepHashCode(autofillHints),
+                paddingBottom, paddingTop, paddingRight, paddingLeft);
+        if (mUseRelativePosition) {
+            hash = Objects.hash(hash, position);
+        }
+        if (sDebug) {
+            Log.d(TAG, "Hash: " + hash + " for AutofillId:" + v.getAutofillId()
+                    + " visibility:" + visibility
+                    + " inputType:" + inputType
+                    + " imeOptions:" + imeOptions
+                    + " isSingleLine:" + isSingleLine
+                    + " hints:" + hints
+                    + " contentDesc:" + contentDesc
+                    + " tooltipText:" + tooltip
+                    + " autofillType:" + autofillType
+                    + " autofillHints:" + Arrays.toString(autofillHints)
+                    + " height:" + height
+                    + " width:" + width
+                    + " paddingLeft:" + paddingLeft
+                    + " paddingRight:" + paddingRight
+                    + " paddingTop:" + paddingTop
+                    + " paddingBottom:" + paddingBottom
+                    + " mUseRelativePosition" + mUseRelativePosition
+                    + " position:" + position
+            );
+        }
+        return hash;
+    }
+
+    private int compareTop(View v1, View v2) {
+        return v1.getTop() - v2.getTop();
+    }
+
+    private int compareBottom(View v1, View v2) {
+        return v1.getBottom() - v2.getBottom();
+    }
+
+    private int compareLeft(View v1, View v2) {
+        return v1.getLeft() - v2.getLeft();
+    }
+
+    private int compareRight(View v1, View v2) {
+        return v1.getRight() - v2.getRight();
+    }
+}
diff --git a/core/java/android/view/autofill/IAutoFillManager.aidl b/core/java/android/view/autofill/IAutoFillManager.aidl
index 2039b4d..f67405f 100644
--- a/core/java/android/view/autofill/IAutoFillManager.aidl
+++ b/core/java/android/view/autofill/IAutoFillManager.aidl
@@ -48,7 +48,7 @@
         in IResultReceiver result);
     void updateSession(int sessionId, in AutofillId id, in Rect bounds,
         in AutofillValue value, int action, int flags, int userId);
-    void setAutofillFailure(int sessionId, in List<AutofillId> ids, int userId);
+    void setAutofillFailure(int sessionId, in List<AutofillId> ids, boolean isRefill, int userId);
     void setViewAutofilled(int sessionId, in AutofillId id, int userId);
     void finishSession(int sessionId, int userId, int commitReason);
     void cancelSession(int sessionId, int userId);
@@ -67,4 +67,7 @@
     void getDefaultFieldClassificationAlgorithm(in IResultReceiver result);
     void setAugmentedAutofillWhitelist(in List<String> packages, in List<ComponentName> activities,
         in IResultReceiver result);
+    void notifyNotExpiringResponseDuringAuth(int sessionId, int userId);
+    void notifyViewEnteredIgnoredDuringAuthCount(int sessionId, int userId);
+    void setAutofillIdsAttemptedForRefill(int sessionId, in List<AutofillId> ids, int userId);
 }
diff --git a/core/java/android/view/autofill/IAutoFillManagerClient.aidl b/core/java/android/view/autofill/IAutoFillManagerClient.aidl
index 904a7e0..39d71da 100644
--- a/core/java/android/view/autofill/IAutoFillManagerClient.aidl
+++ b/core/java/android/view/autofill/IAutoFillManagerClient.aidl
@@ -72,7 +72,8 @@
       */
     void setTrackedViews(int sessionId, in @nullable AutofillId[] savableIds,
             boolean saveOnAllViewsInvisible, boolean saveOnFinish,
-            in @nullable AutofillId[] fillableIds, in AutofillId saveTriggerId);
+            in @nullable AutofillId[] fillableIds, in AutofillId saveTriggerId,
+            in boolean shouldGrabViewFingerprints);
 
     /**
      * Requests showing the fill UI.
diff --git a/core/java/android/view/contentcapture/OWNERS b/core/java/android/view/contentcapture/OWNERS
index e4b0952..9ac273f 100644
--- a/core/java/android/view/contentcapture/OWNERS
+++ b/core/java/android/view/contentcapture/OWNERS
@@ -2,4 +2,3 @@
 
 hackz@google.com
 shivanker@google.com
-volnov@google.com
diff --git a/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java b/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java
index 07a9794..2f515fe 100644
--- a/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java
+++ b/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java
@@ -196,16 +196,22 @@
 
     /**
      * Invokes {@link IInputMethodManager#removeImeSurface()}
+     *
+     * @param displayId display ID from which this request originates
+     * @param exceptionHandler an optional {@link RemoteException} handler
      */
     @AnyThread
-    @RequiresPermission(Manifest.permission.INTERNAL_SYSTEM_WINDOW)
-    static void removeImeSurface(@Nullable Consumer<RemoteException> exceptionHandler) {
+    @RequiresPermission(allOf = {
+            Manifest.permission.INTERNAL_SYSTEM_WINDOW,
+            Manifest.permission.INTERACT_ACROSS_USERS_FULL})
+    static void removeImeSurface(int displayId,
+            @Nullable Consumer<RemoteException> exceptionHandler) {
         final IInputMethodManager service = getService();
         if (service == null) {
             return;
         }
         try {
-            service.removeImeSurface();
+            service.removeImeSurface(displayId);
         } catch (RemoteException e) {
             handleRemoteExceptionOrRethrow(e, exceptionHandler);
         }
@@ -437,7 +443,9 @@
     }
 
     @AnyThread
-    @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
+    @RequiresPermission(allOf = {
+            Manifest.permission.WRITE_SECURE_SETTINGS,
+            Manifest.permission.INTERACT_ACROSS_USERS_FULL})
     static void showInputMethodPickerFromSystem(int auxiliarySubtypeMode, int displayId) {
         final IInputMethodManager service = getService();
         if (service == null) {
@@ -465,7 +473,9 @@
     }
 
     @AnyThread
-    @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
+    @RequiresPermission(allOf = {
+            Manifest.permission.WRITE_SECURE_SETTINGS,
+            Manifest.permission.INTERACT_ACROSS_USERS_FULL})
     static void onImeSwitchButtonClickFromSystem(int displayId) {
         final IInputMethodManager service = getService();
         if (service == null) {
diff --git a/core/java/android/view/inputmethod/ImeTracker.java b/core/java/android/view/inputmethod/ImeTracker.java
index edc9921..b9751c8 100644
--- a/core/java/android/view/inputmethod/ImeTracker.java
+++ b/core/java/android/view/inputmethod/ImeTracker.java
@@ -202,6 +202,24 @@
             PHASE_IME_HIDE_WINDOW,
             PHASE_IME_PRIVILEGED_OPERATIONS,
             PHASE_SERVER_CURRENT_ACTIVE_IME,
+            PHASE_CLIENT_REPORT_REQUESTED_VISIBLE_TYPES,
+            PHASE_WM_SET_REMOTE_TARGET_IME_VISIBILITY,
+            PHASE_WM_POST_LAYOUT_NOTIFY_CONTROLS_CHANGED,
+            PHASE_CLIENT_HANDLE_DISPATCH_IME_VISIBILITY_CHANGED,
+            PHASE_CLIENT_NOTIFY_IME_VISIBILITY_CHANGED,
+            PHASE_CLIENT_UPDATE_REQUESTED_VISIBLE_TYPES,
+            PHASE_WM_REMOTE_INSETS_CONTROL_TARGET_SET_REQUESTED_VISIBILITY,
+            PHASE_WM_GET_CONTROL_WITH_LEASH,
+            PHASE_WM_UPDATE_REQUESTED_VISIBLE_TYPES,
+            PHASE_SERVER_SET_VISIBILITY_ON_FOCUSED_WINDOW,
+            PHASE_CLIENT_HANDLE_SET_IME_VISIBILITY,
+            PHASE_CLIENT_SET_IME_VISIBILITY,
+            PHASE_WM_DISPATCH_IME_REQUESTED_CHANGED,
+            PHASE_CLIENT_NO_ONGOING_USER_ANIMATION,
+            PHASE_WM_NOTIFY_IME_VISIBILITY_CHANGED_FROM_CLIENT,
+            PHASE_WM_POSTING_CHANGED_IME_VISIBILITY,
+            PHASE_WM_INVOKING_IME_REQUESTED_LISTENER,
+            PHASE_CLIENT_ALREADY_HIDDEN,
     })
     @Retention(RetentionPolicy.SOURCE)
     @interface Phase {}
@@ -351,6 +369,62 @@
     /** Checked that the calling IME is the currently active IME. */
     int PHASE_SERVER_CURRENT_ACTIVE_IME = ImeProtoEnums.PHASE_SERVER_CURRENT_ACTIVE_IME;
 
+    /** Reporting the new requested visible types. */
+    int PHASE_CLIENT_REPORT_REQUESTED_VISIBLE_TYPES =
+            ImeProtoEnums.PHASE_CLIENT_REPORT_REQUESTED_VISIBLE_TYPES;
+    /** Setting the IME visibility for the RemoteInsetsControlTarget. */
+    int PHASE_WM_SET_REMOTE_TARGET_IME_VISIBILITY =
+            ImeProtoEnums.PHASE_WM_SET_REMOTE_TARGET_IME_VISIBILITY;
+    /** IME has no insets pending and is server visible. Notify about changed controls. */
+    int PHASE_WM_POST_LAYOUT_NOTIFY_CONTROLS_CHANGED =
+            ImeProtoEnums.PHASE_WM_POST_LAYOUT_NOTIFY_CONTROLS_CHANGED;
+    /** Handling the dispatch of the IME visibility change. */
+    int PHASE_CLIENT_HANDLE_DISPATCH_IME_VISIBILITY_CHANGED =
+            ImeProtoEnums.PHASE_CLIENT_HANDLE_DISPATCH_IME_VISIBILITY_CHANGED;
+    /** Dispatching the IME visibility change. */
+    int PHASE_CLIENT_NOTIFY_IME_VISIBILITY_CHANGED =
+            ImeProtoEnums.PHASE_CLIENT_NOTIFY_IME_VISIBILITY_CHANGED;
+    /** Updating the requested visible types. */
+    int PHASE_CLIENT_UPDATE_REQUESTED_VISIBLE_TYPES =
+            ImeProtoEnums.PHASE_CLIENT_UPDATE_REQUESTED_VISIBLE_TYPES;
+    /** Reached the remote insets control target's setImeInputTargetRequestedVisibility method. */
+    int PHASE_WM_REMOTE_INSETS_CONTROL_TARGET_SET_REQUESTED_VISIBILITY =
+            ImeProtoEnums.PHASE_WM_REMOTE_INSETS_CONTROL_TARGET_SET_REQUESTED_VISIBILITY;
+    /** Received a new insets source control with a leash. */
+    int PHASE_WM_GET_CONTROL_WITH_LEASH =
+            ImeProtoEnums.PHASE_WM_GET_CONTROL_WITH_LEASH;
+    /**
+     * Updating the requested visible types in the WindowState and sending them to state
+     * controller.
+     */
+    int PHASE_WM_UPDATE_REQUESTED_VISIBLE_TYPES =
+            ImeProtoEnums.PHASE_WM_UPDATE_REQUESTED_VISIBLE_TYPES;
+    /** Setting the requested IME visibility of a window. */
+    int PHASE_SERVER_SET_VISIBILITY_ON_FOCUSED_WINDOW =
+            ImeProtoEnums.PHASE_SERVER_SET_VISIBILITY_ON_FOCUSED_WINDOW;
+    /** Reached the redirect of InputMethodManager to InsetsController show/hide. */
+    int PHASE_CLIENT_HANDLE_SET_IME_VISIBILITY =
+            ImeProtoEnums.PHASE_CLIENT_HANDLE_SET_IME_VISIBILITY;
+    /** Reached the InputMethodManager Handler call to send the visibility. */
+    int PHASE_CLIENT_SET_IME_VISIBILITY = ImeProtoEnums.PHASE_CLIENT_SET_IME_VISIBILITY;
+    /** Calling into the listener to show/hide the IME from the ImeInsetsSourceProvider. */
+    int PHASE_WM_DISPATCH_IME_REQUESTED_CHANGED =
+            ImeProtoEnums.PHASE_WM_DISPATCH_IME_REQUESTED_CHANGED;
+    /** An ongoing user animation will not be interrupted by a IMM#showSoftInput. */
+    int PHASE_CLIENT_NO_ONGOING_USER_ANIMATION =
+            ImeProtoEnums.PHASE_CLIENT_NO_ONGOING_USER_ANIMATION;
+    /** Dispatching the token to the ImeInsetsSourceProvider. */
+    int PHASE_WM_NOTIFY_IME_VISIBILITY_CHANGED_FROM_CLIENT =
+            ImeProtoEnums.PHASE_WM_NOTIFY_IME_VISIBILITY_CHANGED_FROM_CLIENT;
+    /** Now posting the IME visibility to the WMS handler. */
+    int PHASE_WM_POSTING_CHANGED_IME_VISIBILITY =
+            ImeProtoEnums.PHASE_WM_POSTING_CHANGED_IME_VISIBILITY;
+    /** Inside the WMS handler calling into the listener that calls into IMMS show/hide. */
+    int PHASE_WM_INVOKING_IME_REQUESTED_LISTENER =
+            ImeProtoEnums.PHASE_WM_INVOKING_IME_REQUESTED_LISTENER;
+    /** IME is requested to be hidden, but already hidden. Don't hide to avoid another animation. */
+    int PHASE_CLIENT_ALREADY_HIDDEN = ImeProtoEnums.PHASE_CLIENT_ALREADY_HIDDEN;
+
     /**
      * Called when an IME request is started.
      *
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index fed8eea..2ac5873 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -1350,11 +1350,15 @@
                     return;
                 }
                 case MSG_SET_VISIBILITY:
-                    final boolean visible = msg.arg1 != 0;
+                    final SomeArgs args = (SomeArgs) msg.obj;
+                    final boolean visible = (boolean) args.arg1;
+                    final ImeTracker.Token statsToken = (ImeTracker.Token) args.arg2;
                     synchronized (mH) {
                         if (mCurRootView != null) {
                             final var insetsController = mCurRootView.getInsetsController();
                             if (insetsController != null) {
+                                ImeTracker.forLogging().onProgress(statsToken,
+                                        ImeTracker.PHASE_CLIENT_HANDLE_SET_IME_VISIBILITY);
                                 if (visible) {
                                     insetsController.show(WindowInsets.Type.ime(),
                                             false /* fromIme */, null /* statsToken */);
@@ -1363,6 +1367,9 @@
                                             false /* fromIme */, null /* statsToken */);
                                 }
                             }
+                        } else {
+                            ImeTracker.forLogging().onFailed(statsToken,
+                                    ImeTracker.PHASE_CLIENT_HANDLE_SET_IME_VISIBILITY);
                         }
                     }
                     break;
@@ -1463,8 +1470,13 @@
         }
 
         @Override
-        public void setImeVisibility(boolean visible) {
-            mH.obtainMessage(MSG_SET_VISIBILITY, visible ? 1 : 0, 0).sendToTarget();
+        public void setImeVisibility(boolean visible, @Nullable ImeTracker.Token statsToken) {
+            final SomeArgs args = SomeArgs.obtain();
+            args.arg1 = visible;
+            args.arg2 = statsToken;
+            ImeTracker.forLogging().onProgress(statsToken,
+                    ImeTracker.PHASE_CLIENT_SET_IME_VISIBILITY);
+            mH.obtainMessage(MSG_SET_VISIBILITY, args).sendToTarget();
         }
 
         @Override
@@ -2344,11 +2356,15 @@
                 if (viewRootImpl != null
                         && (viewRootImpl.getInsetsController().computeUserAnimatingTypes()
                                 & WindowInsets.Type.ime()) == 0) {
+                    ImeTracker.forLogging().onProgress(statsToken,
+                            ImeTracker.PHASE_CLIENT_NO_ONGOING_USER_ANIMATION);
                     // TODO(b/322992891) handle case of SHOW_IMPLICIT
                     viewRootImpl.getInsetsController().show(WindowInsets.Type.ime(),
                             false /* fromIme */, statsToken);
                     return true;
                 }
+                ImeTracker.forLogging().onCancelled(statsToken,
+                        ImeTracker.PHASE_CLIENT_NO_ONGOING_USER_ANIMATION);
                 return false;
             } else {
                 // Makes sure to call ImeInsetsSourceConsumer#onShowRequested on the UI thread.
@@ -2933,7 +2949,8 @@
      * @param callback Consumer callback that provides {@code true} if view belongs to allowed
      *                delegate package declared in
      *                {@link #prepareStylusHandwritingDelegation(View, String)} and handwriting
-     *                session can start.
+     *                session can start. Note: The caller should hold a reference to the callback.
+     *                The framework only holds a weak reference.
      * @see #prepareStylusHandwritingDelegation(View, String)
      * @see #acceptStylusHandwritingDelegation(View)
      */
@@ -2963,7 +2980,8 @@
      * @param delegatorPackageName package name of the delegator that handled initial stylus stroke.
      * @param flags {@link #HANDWRITING_DELEGATE_FLAG_HOME_DELEGATOR_ALLOWED} or {@code 0}
      * @param executor The executor to run the callback on.
-     * @param callback {@code true>} would be received if delegation was accepted.
+     * @param callback {@code true} would be received if delegation was accepted. The caller should
+     *                 hold a reference to the callback. The framework only holds a weak reference.
      * @see #prepareStylusHandwritingDelegation(View, String)
      * @see #acceptStylusHandwritingDelegation(View)
      */
diff --git a/core/java/android/view/inputmethod/InputMethodManagerGlobal.java b/core/java/android/view/inputmethod/InputMethodManagerGlobal.java
index 5df9fd1..244b239 100644
--- a/core/java/android/view/inputmethod/InputMethodManagerGlobal.java
+++ b/core/java/android/view/inputmethod/InputMethodManagerGlobal.java
@@ -95,11 +95,13 @@
     /**
      * Invokes {@link IInputMethodManager#removeImeSurface()}
      *
+     * @param displayId display ID from which this request originates.
      * @param exceptionHandler an optional {@link RemoteException} handler.
      */
     @AnyThread
     @RequiresPermission(Manifest.permission.INTERNAL_SYSTEM_WINDOW)
-    public static void removeImeSurface(@Nullable Consumer<RemoteException> exceptionHandler) {
-        IInputMethodManagerGlobalInvoker.removeImeSurface(exceptionHandler);
+    public static void removeImeSurface(int displayId,
+            @Nullable Consumer<RemoteException> exceptionHandler) {
+        IInputMethodManagerGlobalInvoker.removeImeSurface(displayId, exceptionHandler);
     }
 }
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index d40eeda..8b6ead7 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -76,7 +76,6 @@
 import android.text.Spanned;
 import android.text.SpannedString;
 import android.text.StaticLayout;
-import android.text.TextFlags;
 import android.text.TextUtils;
 import android.text.method.InsertModeTransformationMethod;
 import android.text.method.KeyListener;
@@ -471,7 +470,6 @@
     private static final int LINE_CHANGE_SLOP_MIN_DP = 8;
     private int mLineChangeSlopMax;
     private int mLineChangeSlopMin;
-    private boolean mUseNewContextMenu;
 
     private final AccessibilitySmartActions mA11ySmartActions;
     private InsertModeController mInsertModeController;
@@ -502,9 +500,6 @@
         mLineSlopRatio = AppGlobals.getFloatCoreSetting(
                 WidgetFlags.KEY_LINE_SLOP_RATIO,
                 WidgetFlags.LINE_SLOP_RATIO_DEFAULT);
-        mUseNewContextMenu = AppGlobals.getIntCoreSetting(
-                TextFlags.KEY_ENABLE_NEW_CONTEXT_MENU,
-                TextFlags.ENABLE_NEW_CONTEXT_MENU_DEFAULT ? 1 : 0) != 0;
         if (TextView.DEBUG_CURSOR) {
             logCursor("Editor", "Cursor drag from anywhere is %s.",
                     mFlagCursorDragFromAnywhereEnabled ? "enabled" : "disabled");
@@ -3216,29 +3211,18 @@
         final int menuItemOrderCut = 4;
         final int menuItemOrderCopy = 5;
         final int menuItemOrderPaste = 6;
-        final int menuItemOrderPasteAsPlainText;
-        final int menuItemOrderSelectAll;
-        final int menuItemOrderShare;
-        final int menuItemOrderAutofill;
-        if (mUseNewContextMenu) {
-            menuItemOrderPasteAsPlainText = 7;
-            menuItemOrderSelectAll = 8;
-            menuItemOrderShare = 9;
-            menuItemOrderAutofill = 10;
+        final int menuItemOrderPasteAsPlainText = 7;
+        final int menuItemOrderSelectAll = 8;
+        final int menuItemOrderShare = 9;
+        final int menuItemOrderAutofill = 10;
 
-            menu.setOptionalIconsVisible(true);
-            menu.setGroupDividerEnabled(true);
+        menu.setOptionalIconsVisible(true);
+        menu.setGroupDividerEnabled(true);
 
-            setAssistContextMenuItems(menu);
+        setAssistContextMenuItems(menu);
 
-            final int keyboard = mTextView.getResources().getConfiguration().keyboard;
-            menu.setQwertyMode(keyboard == Configuration.KEYBOARD_QWERTY);
-        } else {
-            menuItemOrderShare = 7;
-            menuItemOrderSelectAll = 8;
-            menuItemOrderAutofill = 10;
-            menuItemOrderPasteAsPlainText = 11;
-        }
+        final int keyboard = mTextView.getResources().getConfiguration().keyboard;
+        menu.setQwertyMode(keyboard == Configuration.KEYBOARD_QWERTY);
 
         final TypedArray a = mTextView.getContext().obtainStyledAttributes(new int[] {
                 // TODO: Make Undo/Redo be public attribute.
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 9512347..eb35817 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -7438,8 +7438,7 @@
             // If the user interacts with a visible element it is safe to assume they consent that
             // something is going to start.
             opts.setPendingIntentBackgroundActivityStartMode(
-                    ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED);
-            opts.setPendingIntentBackgroundActivityLaunchAllowedByPermission(true);
+                    ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS);
             return Pair.create(intent, opts);
         }
     }
@@ -8361,7 +8360,7 @@
         }
     }
 
-    private interface PendingResources<T> {
+    interface PendingResources<T> {
         T create(Context context, Resources appResources, HierarchyRootData rootData, int depth)
                 throws Exception;
     }
diff --git a/core/java/android/widget/RemoteViewsSerializers.java b/core/java/android/widget/RemoteViewsSerializers.java
new file mode 100644
index 0000000..600fea4
--- /dev/null
+++ b/core/java/android/widget/RemoteViewsSerializers.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.widget;
+
+import android.content.res.ColorStateList;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.BlendMode;
+import android.graphics.drawable.Icon;
+import android.util.Log;
+import android.util.LongSparseArray;
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoOutputStream;
+import android.util.proto.ProtoUtils;
+
+import androidx.annotation.NonNull;
+
+import java.io.ByteArrayOutputStream;
+import java.util.function.Function;
+
+/**
+ * This class provides serialization for certain types used within RemoteViews.
+ *
+ * @hide
+ */
+public class RemoteViewsSerializers {
+    private static final String TAG = "RemoteViews";
+
+    /**
+     * Write Icon to proto.
+     */
+    public static void writeIconToProto(@NonNull ProtoOutputStream out,
+            @NonNull Resources appResources, @NonNull Icon icon) {
+        if (icon.getTintList() != null) {
+            final long token = out.start(RemoteViewsProto.Icon.TINT_LIST);
+            icon.getTintList().writeToProto(out);
+            out.end(token);
+        }
+        out.write(RemoteViewsProto.Icon.BLEND_MODE, BlendMode.toValue(icon.getTintBlendMode()));
+        switch (icon.getType()) {
+            case Icon.TYPE_BITMAP:
+                final ByteArrayOutputStream bitmapBytes = new ByteArrayOutputStream();
+                icon.getBitmap().compress(Bitmap.CompressFormat.WEBP_LOSSLESS, 100, bitmapBytes);
+                out.write(RemoteViewsProto.Icon.BITMAP, bitmapBytes.toByteArray());
+                break;
+            case Icon.TYPE_ADAPTIVE_BITMAP:
+                final ByteArrayOutputStream adaptiveBitmapBytes = new ByteArrayOutputStream();
+                icon.getBitmap().compress(Bitmap.CompressFormat.WEBP_LOSSLESS, 100,
+                        adaptiveBitmapBytes);
+                out.write(RemoteViewsProto.Icon.ADAPTIVE_BITMAP, adaptiveBitmapBytes.toByteArray());
+                break;
+            case Icon.TYPE_RESOURCE:
+                out.write(RemoteViewsProto.Icon.RESOURCE,
+                        appResources.getResourceName(icon.getResId()));
+                break;
+            case Icon.TYPE_DATA:
+                out.write(RemoteViewsProto.Icon.DATA, icon.getDataBytes());
+                break;
+            case Icon.TYPE_URI:
+                out.write(RemoteViewsProto.Icon.URI, icon.getUriString());
+                break;
+            case Icon.TYPE_URI_ADAPTIVE_BITMAP:
+                out.write(RemoteViewsProto.Icon.URI_ADAPTIVE_BITMAP, icon.getUriString());
+                break;
+            default:
+                Log.e(TAG, "Tried to serialize unknown Icon type " + icon.getType());
+        }
+    }
+
+    /**
+     * Create Icon from proto.
+     */
+    @NonNull
+    public static Function<Resources, Icon> createIconFromProto(@NonNull ProtoInputStream in)
+            throws Exception {
+        final LongSparseArray<Object> values = new LongSparseArray<>();
+        while (in.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+            switch (in.getFieldNumber()) {
+                case (int) RemoteViewsProto.Icon.BLEND_MODE:
+                    values.put(RemoteViewsProto.Icon.BLEND_MODE,
+                            in.readInt(RemoteViewsProto.Icon.BLEND_MODE));
+                    break;
+                case (int) RemoteViewsProto.Icon.TINT_LIST:
+                    final long tintListToken = in.start(RemoteViewsProto.Icon.TINT_LIST);
+                    values.put(RemoteViewsProto.Icon.TINT_LIST, ColorStateList.createFromProto(in));
+                    in.end(tintListToken);
+                    break;
+                case (int) RemoteViewsProto.Icon.BITMAP:
+                    byte[] bitmapData = in.readBytes(RemoteViewsProto.Icon.BITMAP);
+                    values.put(RemoteViewsProto.Icon.BITMAP,
+                            BitmapFactory.decodeByteArray(bitmapData, 0, bitmapData.length));
+                    break;
+                case (int) RemoteViewsProto.Icon.ADAPTIVE_BITMAP:
+                    final byte[] bitmapAdaptiveData = in.readBytes(
+                            RemoteViewsProto.Icon.ADAPTIVE_BITMAP);
+                    values.put(RemoteViewsProto.Icon.ADAPTIVE_BITMAP,
+                            BitmapFactory.decodeByteArray(bitmapAdaptiveData, 0,
+                                    bitmapAdaptiveData.length));
+                    break;
+                case (int) RemoteViewsProto.Icon.RESOURCE:
+                    values.put(RemoteViewsProto.Icon.RESOURCE,
+                            in.readString(RemoteViewsProto.Icon.RESOURCE));
+                    break;
+                case (int) RemoteViewsProto.Icon.DATA:
+                    values.put(RemoteViewsProto.Icon.DATA,
+                            in.readBytes(RemoteViewsProto.Icon.DATA));
+                    break;
+                case (int) RemoteViewsProto.Icon.URI:
+                    values.put(RemoteViewsProto.Icon.URI, in.readString(RemoteViewsProto.Icon.URI));
+                    break;
+                case (int) RemoteViewsProto.Icon.URI_ADAPTIVE_BITMAP:
+                    values.put(RemoteViewsProto.Icon.URI_ADAPTIVE_BITMAP,
+                            in.readString(RemoteViewsProto.Icon.URI_ADAPTIVE_BITMAP));
+                    break;
+                default:
+                    Log.w(TAG, "Unhandled field while reading Icon proto!\n"
+                            + ProtoUtils.currentFieldToString(in));
+            }
+        }
+
+        return (resources) -> {
+            final int blendMode = (int) values.get(RemoteViewsProto.Icon.BLEND_MODE, -1);
+            final ColorStateList tintList = (ColorStateList) values.get(
+                    RemoteViewsProto.Icon.TINT_LIST);
+            final Bitmap bitmap = (Bitmap) values.get(RemoteViewsProto.Icon.BITMAP);
+            final Bitmap bitmapAdaptive = (Bitmap) values.get(
+                    RemoteViewsProto.Icon.ADAPTIVE_BITMAP);
+            final String resName = (String) values.get(RemoteViewsProto.Icon.RESOURCE);
+            final int resource = resName != null ? resources.getIdentifier(resName, /* defType= */
+                    null,
+                    /* defPackage= */ null) : -1;
+            final byte[] data = (byte[]) values.get(RemoteViewsProto.Icon.DATA);
+            final String uri = (String) values.get(RemoteViewsProto.Icon.URI);
+            final String uriAdaptive = (String) values.get(
+                    RemoteViewsProto.Icon.URI_ADAPTIVE_BITMAP);
+            Icon icon;
+            if (bitmap != null) {
+                icon = Icon.createWithBitmap(bitmap);
+            } else if (bitmapAdaptive != null) {
+                icon = Icon.createWithAdaptiveBitmap(bitmapAdaptive);
+            } else if (resource != -1) {
+                icon = Icon.createWithResource(resources, resource);
+            } else if (data != null) {
+                icon = Icon.createWithData(data, 0, data.length);
+            } else if (uri != null) {
+                icon = Icon.createWithContentUri(uri);
+            } else if (uriAdaptive != null) {
+                icon = Icon.createWithAdaptiveBitmapContentUri(uriAdaptive);
+            } else {
+                // Either this Icon has no data or is of an unknown type.
+                return null;
+            }
+
+            if (tintList != null) {
+                icon.setTintList(tintList);
+            }
+            if (blendMode != -1) {
+                icon.setTintBlendMode(BlendMode.fromValue(blendMode));
+            }
+            return icon;
+        };
+    }
+}
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index f7e0ec8..ac899f4 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -31,6 +31,7 @@
 
 import static com.android.text.flags.Flags.FLAG_FIX_LINE_HEIGHT_FOR_LOCALE;
 import static com.android.text.flags.Flags.FLAG_USE_BOUNDS_FOR_WIDTH;
+import static android.view.inputmethod.Flags.initiationWithoutInputConnection;
 
 import android.R;
 import android.annotation.CallSuper;
@@ -105,7 +106,6 @@
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.text.BoringLayout;
-import android.text.ClientFlags;
 import android.text.DynamicLayout;
 import android.text.Editable;
 import android.text.GetChars;
@@ -1659,7 +1659,7 @@
 
         if (!hasUseBoundForWidthValue) {
             if (CompatChanges.isChangeEnabled(USE_BOUNDS_FOR_WIDTH)) {
-                mUseBoundsForWidth = ClientFlags.useBoundsForWidth();
+                mUseBoundsForWidth = Flags.useBoundsForWidth();
             } else {
                 mUseBoundsForWidth = false;
             }
@@ -2739,6 +2739,8 @@
 
         InputMethodManager imm = getInputMethodManager();
         if (imm != null) imm.restartInput(this);
+
+        ensureEditorFocusedNotifiedToHandwritingInitiator();
     }
 
     private void setInputTypeFromEditor() {
@@ -7844,6 +7846,20 @@
         if (type == InputType.TYPE_NULL && mEditor == null) return; //TYPE_NULL is the default value
         createEditorIfNeeded();
         mEditor.mInputType = type;
+        ensureEditorFocusedNotifiedToHandwritingInitiator();
+    }
+
+    private void ensureEditorFocusedNotifiedToHandwritingInitiator() {
+        if (!initiationWithoutInputConnection() || isHandwritingDelegate()) {
+            return;
+        }
+        ViewRootImpl viewRoot = getViewRootImpl();
+        if (viewRoot == null) {
+            return;
+        }
+        if (isFocused() && hasWindowFocus() && onCheckIsTextEditor()) {
+            viewRoot.getHandwritingInitiator().onEditorFocused(this);
+        }
     }
 
     @Override
@@ -9732,7 +9748,7 @@
                 break;
 
             case KeyEvent.KEYCODE_ESCAPE:
-                if (com.android.text.flags.Flags.escapeClearsFocus() && event.hasNoModifiers()) {
+                if (Flags.escapeClearsFocus() && event.hasNoModifiers()) {
                     if (mEditor != null && mEditor.getTextActionMode() != null) {
                         stopTextActionMode();
                         return KEY_EVENT_HANDLED;
diff --git a/core/java/android/widget/TimePickerSpinnerDelegate.java b/core/java/android/widget/TimePickerSpinnerDelegate.java
index 1a660be..3b25109 100644
--- a/core/java/android/widget/TimePickerSpinnerDelegate.java
+++ b/core/java/android/widget/TimePickerSpinnerDelegate.java
@@ -477,7 +477,7 @@
             } else if (mMinuteSpinnerInput.hasFocus()) {
                 inputMethodManager.hideSoftInputFromView(mMinuteSpinnerInput, 0);
                 mMinuteSpinnerInput.clearFocus();
-            } else if (mAmPmSpinnerInput.hasFocus()) {
+            } else if (mAmPmSpinnerInput != null && mAmPmSpinnerInput.hasFocus()) {
                 inputMethodManager.hideSoftInputFromView(mAmPmSpinnerInput, 0);
                 mAmPmSpinnerInput.clearFocus();
             }
diff --git a/core/java/android/window/DisplayWindowPolicyController.java b/core/java/android/window/DisplayWindowPolicyController.java
index 9cd2a71..62a7283 100644
--- a/core/java/android/window/DisplayWindowPolicyController.java
+++ b/core/java/android/window/DisplayWindowPolicyController.java
@@ -23,7 +23,6 @@
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
 import android.app.WindowConfiguration;
-import android.companion.virtualdevice.flags.Flags;
 import android.content.ComponentName;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
@@ -67,9 +66,7 @@
     public DisplayWindowPolicyController() {
         synchronized (mSupportedWindowingModes) {
             mSupportedWindowingModes.add(WindowConfiguration.WINDOWING_MODE_FULLSCREEN);
-            if (Flags.virtualDisplayMultiWindowModeSupport()) {
-                mSupportedWindowingModes.add(WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW);
-            }
+            mSupportedWindowingModes.add(WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW);
         }
     }
 
diff --git a/core/java/android/window/ITaskOrganizerController.aidl b/core/java/android/window/ITaskOrganizerController.aidl
index 478aeec..1748b9d 100644
--- a/core/java/android/window/ITaskOrganizerController.aidl
+++ b/core/java/android/window/ITaskOrganizerController.aidl
@@ -67,7 +67,4 @@
      * Restarts the top activity in the given task by killing its process if it is visible.
      */
     void restartTaskTopActivityProcessIfVisible(in WindowContainerToken task);
-
-    /** Updates a state of camera compat control for stretched issues in the viewfinder. */
-    void updateCameraCompatControlState(in WindowContainerToken task, int state);
 }
diff --git a/core/java/android/window/TaskFragmentInfo.java b/core/java/android/window/TaskFragmentInfo.java
index fa51957..23a1224 100644
--- a/core/java/android/window/TaskFragmentInfo.java
+++ b/core/java/android/window/TaskFragmentInfo.java
@@ -102,6 +102,8 @@
     @NonNull
     private final Point mMinimumDimensions = new Point();
 
+    private final boolean mIsTopNonFishingChild;
+
     /** @hide */
     public TaskFragmentInfo(
             @NonNull IBinder fragmentToken, @NonNull WindowContainerToken token,
@@ -110,7 +112,7 @@
             @NonNull List<IBinder> inRequestedTaskFragmentActivities,
             @NonNull Point positionInParent, boolean isTaskClearedForReuse,
             boolean isTaskFragmentClearedForPip, boolean isClearedForReorderActivityToFront,
-            @NonNull Point minimumDimensions) {
+            @NonNull Point minimumDimensions, boolean isTopNonFinishingChild) {
         mFragmentToken = requireNonNull(fragmentToken);
         mToken = requireNonNull(token);
         mConfiguration.setTo(configuration);
@@ -123,6 +125,7 @@
         mIsTaskFragmentClearedForPip = isTaskFragmentClearedForPip;
         mIsClearedForReorderActivityToFront = isClearedForReorderActivityToFront;
         mMinimumDimensions.set(minimumDimensions);
+        mIsTopNonFishingChild = isTopNonFinishingChild;
     }
 
     @NonNull
@@ -212,6 +215,16 @@
     }
 
     /**
+     * Indicates that this TaskFragment is the top non-finishing child of its parent container
+     * among all Activities and TaskFragment siblings.
+     *
+     * @hide
+     */
+    public boolean isTopNonFinishingChild() {
+        return mIsTopNonFishingChild;
+    }
+
+    /**
      * Returns {@code true} if the parameters that are important for task fragment organizers are
      * equal between this {@link TaskFragmentInfo} and {@param that}.
      * Note that this method is usually called with
@@ -236,7 +249,8 @@
                 && mIsTaskClearedForReuse == that.mIsTaskClearedForReuse
                 && mIsTaskFragmentClearedForPip == that.mIsTaskFragmentClearedForPip
                 && mIsClearedForReorderActivityToFront == that.mIsClearedForReorderActivityToFront
-                && mMinimumDimensions.equals(that.mMinimumDimensions);
+                && mMinimumDimensions.equals(that.mMinimumDimensions)
+                && mIsTopNonFishingChild == that.mIsTopNonFishingChild;
     }
 
     private TaskFragmentInfo(Parcel in) {
@@ -252,6 +266,7 @@
         mIsTaskFragmentClearedForPip = in.readBoolean();
         mIsClearedForReorderActivityToFront = in.readBoolean();
         mMinimumDimensions.readFromParcel(in);
+        mIsTopNonFishingChild = in.readBoolean();
     }
 
     /** @hide */
@@ -269,6 +284,7 @@
         dest.writeBoolean(mIsTaskFragmentClearedForPip);
         dest.writeBoolean(mIsClearedForReorderActivityToFront);
         mMinimumDimensions.writeToParcel(dest, flags);
+        dest.writeBoolean(mIsTopNonFishingChild);
     }
 
     @NonNull
@@ -299,6 +315,7 @@
                 + " isTaskFragmentClearedForPip=" + mIsTaskFragmentClearedForPip
                 + " mIsClearedForReorderActivityToFront=" + mIsClearedForReorderActivityToFront
                 + " minimumDimensions=" + mMinimumDimensions
+                + " isTopNonFinishingChild=" + mIsTopNonFishingChild
                 + "}";
     }
 
diff --git a/core/java/android/window/TaskOrganizer.java b/core/java/android/window/TaskOrganizer.java
index b9ffdbc..3ecb619 100644
--- a/core/java/android/window/TaskOrganizer.java
+++ b/core/java/android/window/TaskOrganizer.java
@@ -24,7 +24,6 @@
 import android.annotation.SuppressLint;
 import android.annotation.TestApi;
 import android.app.ActivityManager;
-import android.app.CameraCompatTaskInfo.CameraCompatControlState;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.view.SurfaceControl;
@@ -252,20 +251,6 @@
     }
 
     /**
-     * Updates a state of camera compat control for stretched issues in the viewfinder.
-     * @hide
-     */
-    @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS)
-    public void updateCameraCompatControlState(@NonNull WindowContainerToken task,
-            @CameraCompatControlState int state) {
-        try {
-            mTaskOrganizerController.updateCameraCompatControlState(task, state);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    /**
      * Gets the executor to run callbacks on.
      * @hide
      */
diff --git a/core/java/android/window/TransitionFilter.java b/core/java/android/window/TransitionFilter.java
index ec4e3e9..3cfde87 100644
--- a/core/java/android/window/TransitionFilter.java
+++ b/core/java/android/window/TransitionFilter.java
@@ -30,6 +30,8 @@
 import android.os.Parcelable;
 import android.view.WindowManager;
 
+import com.android.window.flags.Flags;
+
 /**
  * A parcelable filter that can be used for rerouting transitions to a remote. This is a local
  * representation so that the transition system doesn't need to make blocking queries over
@@ -183,6 +185,9 @@
         public ComponentName mTopActivity;
         public IBinder mLaunchCookie;
 
+        /** If non-null, requires the change to specifically have or not-have a custom animation. */
+        public Boolean mCustomAnimation = null;
+
         public Requirement() {
         }
 
@@ -196,6 +201,9 @@
             mOrder = in.readInt();
             mTopActivity = in.readTypedObject(ComponentName.CREATOR);
             mLaunchCookie = in.readStrongBinder();
+            // 0: null, 1: false, 2: true
+            final int customAnimRaw = in.readInt();
+            mCustomAnimation = customAnimRaw == 0 ? null : Boolean.valueOf(customAnimRaw == 2);
         }
 
         /** Go through changes and find if at-least one change matches this filter */
@@ -237,6 +245,23 @@
                 if (!matchesCookie(change.getTaskInfo())) {
                     continue;
                 }
+                if (mCustomAnimation != null
+                        // only applies to activity/task
+                        && (change.getTaskInfo() != null
+                                || change.getActivityComponent() != null)) {
+                    final TransitionInfo.AnimationOptions opts =
+                            Flags.moveAnimationOptionsToChange() ? change.getAnimationOptions()
+                                    : info.getAnimationOptions();
+                    if (opts != null) {
+                        boolean canActuallyOverride = change.getTaskInfo() == null
+                                || opts.getOverrideTaskTransition();
+                        if (mCustomAnimation != canActuallyOverride) {
+                            continue;
+                        }
+                    } else if (mCustomAnimation) {
+                        continue;
+                    }
+                }
                 return true;
             }
             return false;
@@ -286,6 +311,8 @@
             dest.writeInt(mOrder);
             dest.writeTypedObject(mTopActivity, flags);
             dest.writeStrongBinder(mLaunchCookie);
+            int customAnimRaw = mCustomAnimation == null ? 0 : (mCustomAnimation ? 2 : 1);
+            dest.writeInt(customAnimRaw);
         }
 
         @NonNull
@@ -327,6 +354,9 @@
             out.append(" order=" + containerOrderToString(mOrder));
             out.append(" topActivity=").append(mTopActivity);
             out.append(" launchCookie=").append(mLaunchCookie);
+            if (mCustomAnimation != null) {
+                out.append(" customAnim=").append(mCustomAnimation.booleanValue());
+            }
             out.append("}");
             return out.toString();
         }
diff --git a/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig b/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig
index 48fb2b3..f739622 100644
--- a/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig
+++ b/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig
@@ -98,6 +98,13 @@
 }
 
 flag {
+  name: "scrolling_from_letterbox"
+  namespace: "large_screen_experiences_app_compat"
+  description: "Whether to enable app scrolling from gestures from letterbox area"
+  bug: "353697519"
+}
+
+flag {
   name: "app_compat_refactoring"
   namespace: "large_screen_experiences_app_compat"
   description: "Whether the changes about app compat refactoring are enabled./n"
diff --git a/core/java/android/window/flags/lse_desktop_experience.aconfig b/core/java/android/window/flags/lse_desktop_experience.aconfig
index 91ac4ff..e5ef95c 100644
--- a/core/java/android/window/flags/lse_desktop_experience.aconfig
+++ b/core/java/android/window/flags/lse_desktop_experience.aconfig
@@ -192,8 +192,22 @@
 }
 
 flag {
+    name: "enable_desktop_windowing_transitions"
+    namespace: "lse_desktop_experience"
+    description: "Enables desktop windowing transition & motion polish changes"
+    bug: "356570693"
+}
+
+flag {
     name: "enable_compat_ui_visibility_status"
     namespace: "lse_desktop_experience"
     description: "Enables the tracking of the status for compat ui elements."
     bug: "350953004"
 }
+
+flag {
+    name: "enable_desktop_windowing_app_to_web_education"
+    namespace: "lse_desktop_experience"
+    description: "Enables desktop windowing app-to-web education"
+    bug: "348205896"
+}
diff --git a/core/java/android/window/flags/windowing_frontend.aconfig b/core/java/android/window/flags/windowing_frontend.aconfig
index c451cc8..80a0102 100644
--- a/core/java/android/window/flags/windowing_frontend.aconfig
+++ b/core/java/android/window/flags/windowing_frontend.aconfig
@@ -65,14 +65,6 @@
 }
 
 flag {
-    name: "defer_display_updates"
-    namespace: "windowing_frontend"
-    description: "Feature flag for deferring DisplayManager updates to WindowManager if Shell transition is running"
-    bug: "259220649"
-    is_fixed_read_only: true
-}
-
-flag {
   name: "close_to_square_config_includes_status_bar"
   namespace: "windowing_frontend"
   description: "On close to square display, when necessary, configuration includes status bar"
@@ -80,14 +72,6 @@
 }
 
 flag {
-  name: "introduce_smoother_dimmer"
-  namespace: "windowing_frontend"
-  description: "Refactor dim to fix flickers"
-  bug: "295291019"
-  is_fixed_read_only: true
-}
-
-flag {
   name: "transit_ready_tracking"
   namespace: "windowing_frontend"
   description: "Enable accurate transition readiness tracking"
@@ -166,17 +150,6 @@
 }
 
 flag {
-  name: "keyguard_appear_transition"
-  namespace: "windowing_frontend"
-  description: "Add transition when keyguard appears"
-  bug: "327970608"
-  is_fixed_read_only: true
-  metadata {
-      purpose: PURPOSE_BUGFIX
-  }
-}
-
-flag {
   name: "get_dimmer_on_closing"
   namespace: "windowing_frontend"
   description: "Change check for when to ignore a closing task's dim"
@@ -188,6 +161,17 @@
 }
 
 flag {
+  name: "use_tasks_dim_only"
+  namespace: "windowing_frontend"
+  description: "Only use the task's dim and reparent it to the display area when needed instead of coordinating multiple dimmers"
+  bug: "352522056"
+  is_fixed_read_only: true
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
+
+flag {
     name: "release_snapshot_aggressively"
     namespace: "windowing_frontend"
     description: "Actively release task snapshot memory"
@@ -217,20 +201,20 @@
 }
 
 flag {
-  name: "enforce_shell_thread_model"
+  name: "custom_animations_behind_translucent"
   namespace: "windowing_frontend"
-  description: "Crash the shell process if someone calls in from the wrong thread"
-  bug: "351189446"
-  is_fixed_read_only: true
+  description: "A change can use its own layer parameters to animate behind a translucent activity"
+  bug: "327332488"
   metadata {
     purpose: PURPOSE_BUGFIX
   }
 }
 flag {
-  name: "custom_animations_behind_translucent"
+  name: "migrate_predictive_back_transition"
   namespace: "windowing_frontend"
-  description: "A change can use its own layer parameters to animate behind a translucent activity"
-  bug: "327332488"
+  description: "Create transition when visibility change from predictive back"
+  bug: "347168362"
+  is_fixed_read_only: true
   metadata {
     purpose: PURPOSE_BUGFIX
   }
diff --git a/core/java/android/window/flags/windowing_sdk.aconfig b/core/java/android/window/flags/windowing_sdk.aconfig
index ae9d757..b8c2a5f 100644
--- a/core/java/android/window/flags/windowing_sdk.aconfig
+++ b/core/java/android/window/flags/windowing_sdk.aconfig
@@ -3,16 +3,6 @@
 
 # Project link: https://gantry.corp.google.com/projects/android_platform_windowing_sdk/changes
 
-# Using a fixed read only flag because there are ClientTransaction scheduling before
-# WindowManagerService creation.
-flag {
-    namespace: "windowing_sdk"
-    name: "bundle_client_transaction_flag"
-    description: "To bundle multiple ClientTransactionItems into one ClientTransaction"
-    bug: "260873529"
-    is_fixed_read_only: true
-}
-
 flag {
     namespace: "windowing_sdk"
     name: "activity_embedding_overlay_presentation_flag"
@@ -52,14 +42,6 @@
 
 flag {
     namespace: "windowing_sdk"
-    name: "activity_window_info_flag"
-    description: "To dispatch ActivityWindowInfo through ClientTransaction"
-    bug: "287582673"
-    is_fixed_read_only: true
-}
-
-flag {
-    namespace: "windowing_sdk"
     name: "untrusted_embedding_state_sharing"
     is_exported: true
     description: "Feature flag to enable state sharing in untrusted embedding when apps opt in."
@@ -69,13 +51,6 @@
 
 flag {
     namespace: "windowing_sdk"
-    name: "embedded_activity_back_nav_flag"
-    description: "Refines embedded activity back navigation behavior"
-    bug: "293642394"
-}
-
-flag {
-    namespace: "windowing_sdk"
     name: "cover_display_opt_in"
     is_exported: true
     description: "Properties to allow apps and activities to opt-in to cover display rendering"
@@ -141,17 +116,6 @@
 
 flag {
     namespace: "windowing_sdk"
-    name: "disable_object_pool"
-    description: "Whether to disable object pool and let the GC handle lifecycle items"
-    bug: "311089192"
-    is_fixed_read_only: true
-    metadata {
-        purpose: PURPOSE_BUGFIX
-    }
-}
-
-flag {
-    namespace: "windowing_sdk"
     name: "rear_display_disable_force_desktop_system_decorations"
     description: "Block system decorations from being added to a rear display when desktop mode is forced"
     bug: "346103150"
@@ -171,14 +135,3 @@
         purpose: PURPOSE_BUGFIX
     }
 }
-
-flag {
-    namespace: "windowing_sdk"
-    name: "per_user_display_window_settings"
-    description: "Whether to store display window settings per user to avoid conflicts"
-    bug: "346668297"
-    is_fixed_read_only: true
-    metadata {
-        purpose: PURPOSE_BUGFIX
-    }
-}
diff --git a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
index f9c2947..e8831ec 100644
--- a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
+++ b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
@@ -36,6 +36,7 @@
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.DialogInterface;
+import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.res.Configuration;
 import android.database.ContentObserver;
@@ -63,6 +64,7 @@
 import com.android.internal.R;
 import com.android.internal.accessibility.dialog.AccessibilityTarget;
 import com.android.internal.accessibility.util.ShortcutUtils;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.function.pooled.PooledLambda;
 
 import java.lang.annotation.Retention;
@@ -122,6 +124,13 @@
             .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
             .setUsage(AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY)
             .build();
+
+    /**
+     * An intent action to launch Extra Dim dialog.
+     */
+    @VisibleForTesting
+    static final String ACTION_LAUNCH_REMOVE_EXTRA_DIM_DIALOG =
+            "com.android.systemui.action.LAUNCH_REMOVE_EXTRA_DIM_DIALOG";
     private static Map<ComponentName, FrameworkFeatureInfo> sFrameworkShortcutFeaturesMap;
 
     private final Context mContext;
@@ -846,7 +855,7 @@
             if (com.android.server.display.feature.flags.Flags.evenDimmer()
                     && context.getResources().getBoolean(
                     com.android.internal.R.bool.config_evenDimmerEnabled)) {
-                launchExtraDimDialog();
+                launchExtraDimDialog(context);
                 return true;
             } else {
                 // Assuming that the default state will be to have the feature off
@@ -863,8 +872,12 @@
             }
         }
 
-        private void launchExtraDimDialog() {
-            // TODO: launch Extra dim dialog for feature migration
+        private void launchExtraDimDialog(Context context) {
+            final Intent intent = new Intent(ACTION_LAUNCH_REMOVE_EXTRA_DIM_DIALOG);
+            intent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+            intent.setPackage(
+                    context.getString(com.android.internal.R.string.config_systemUi));
+            context.sendBroadcastAsUser(intent, UserHandle.SYSTEM);
         }
     }
 
diff --git a/core/java/com/android/internal/accessibility/common/ShortcutConstants.java b/core/java/com/android/internal/accessibility/common/ShortcutConstants.java
index a3fcfad..44dceb9 100644
--- a/core/java/com/android/internal/accessibility/common/ShortcutConstants.java
+++ b/core/java/com/android/internal/accessibility/common/ShortcutConstants.java
@@ -72,7 +72,8 @@
             UserShortcutType.TRIPLETAP,
             UserShortcutType.TWOFINGER_DOUBLETAP,
             UserShortcutType.QUICK_SETTINGS,
-            UserShortcutType.GESTURE
+            UserShortcutType.GESTURE,
+            UserShortcutType.ALL
     })
     public @interface UserShortcutType {
         int DEFAULT = 0;
@@ -84,6 +85,7 @@
         int QUICK_SETTINGS = 1 << 4;
         int GESTURE = 1 << 5;
         // LINT.ThenChange(:shortcut_type_array)
+        int ALL = SOFTWARE | HARDWARE | TRIPLETAP | TWOFINGER_DOUBLETAP | QUICK_SETTINGS | GESTURE;
     }
 
     /**
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index ab456a8..ca4d1b6 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -544,6 +544,14 @@
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
+        if (Settings.Secure.getIntForUser(getContentResolver(),
+                Settings.Secure.SECURE_FRP_MODE, 0,
+                getUserId()) == 1) {
+            Log.e(TAG, "Sharing disabled due to active FRP lock.");
+            super.onCreate(savedInstanceState);
+            finish();
+            return;
+        }
         final long intentReceivedTime = System.currentTimeMillis();
         mLatencyTracker.onActionStart(ACTION_LOAD_SHARE_SHEET);
 
@@ -2952,10 +2960,10 @@
     }
 
     private boolean shouldShowStickyContentPreviewNoOrientationCheck() {
-        return shouldShowTabs()
-                && (mMultiProfilePagerAdapter.getListAdapterForUserHandle(
-                        UserHandle.of(UserHandle.myUserId())).getCount() > 0
-                    || shouldShowStickyContentPreviewWhenEmpty())
+        ResolverListAdapter adapter = mMultiProfilePagerAdapter.getListAdapterForUserHandle(
+                UserHandle.of(UserHandle.myUserId()));
+        boolean isEmpty = adapter == null || adapter.getCount() == 0;
+        return shouldShowTabs() && (!isEmpty || shouldShowStickyContentPreviewWhenEmpty())
                 && shouldShowContentPreview();
     }
 
diff --git a/core/java/com/android/internal/inputmethod/IInputMethodClient.aidl b/core/java/com/android/internal/inputmethod/IInputMethodClient.aidl
index 6a7fa99..6eb9d2e 100644
--- a/core/java/com/android/internal/inputmethod/IInputMethodClient.aidl
+++ b/core/java/com/android/internal/inputmethod/IInputMethodClient.aidl
@@ -16,6 +16,7 @@
 
 package com.android.internal.inputmethod;
 
+import android.view.inputmethod.ImeTracker;
 import com.android.internal.inputmethod.InputBindResult;
 
 /**
@@ -30,7 +31,7 @@
     void onUnbindAccessibilityService(int sequence, int id);
     void setActive(boolean active, boolean fullscreen);
     void setInteractive(boolean active, boolean fullscreen);
-    void setImeVisibility(boolean visible);
+    void setImeVisibility(boolean visible, in @nullable ImeTracker.Token statsToken);
     void scheduleStartInputIfNecessary(boolean fullscreen);
     void reportFullscreenMode(boolean fullscreen);
     void setImeTraceEnabled(boolean enabled);
diff --git a/core/java/com/android/internal/jank/FrameTracker.java b/core/java/com/android/internal/jank/FrameTracker.java
index bf5df03..53ef49b 100644
--- a/core/java/com/android/internal/jank/FrameTracker.java
+++ b/core/java/com/android/internal/jank/FrameTracker.java
@@ -317,6 +317,7 @@
         Trace.asyncTraceForTrackBegin(Trace.TRACE_TAG_APP, name, name, (int) mBeginVsyncId);
         markEvent("FT#beginVsync", mBeginVsyncId);
         markEvent("FT#layerId", mSurfaceControl.getLayerId());
+        markCujUiThread();
         mJankDataListenerRegistration =
                 mSurfaceControlWrapper.addJankStatsListener(this, mSurfaceControl);
         if (!mSurfaceOnly) {
@@ -433,6 +434,13 @@
         }
     }
 
+    private void markCujUiThread() {
+        if (Trace.isTagEnabled(Trace.TRACE_TAG_APP)) {
+            // This is being called from the CUJ ui thread.
+            Trace.instant(Trace.TRACE_TAG_APP, mConfig.getSessionName() + "#UIThread");
+        }
+    }
+
     private void notifyCujEvent(String action, @Reasons int reason) {
         if (mListener == null) return;
         mListener.onCujEvents(this, action, reason);
diff --git a/core/java/com/android/internal/os/DebugStore.java b/core/java/com/android/internal/os/DebugStore.java
new file mode 100644
index 0000000..4c45fee
--- /dev/null
+++ b/core/java/com/android/internal/os/DebugStore.java
@@ -0,0 +1,247 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.os;
+
+import android.annotation.Nullable;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.Intent;
+import android.content.pm.ServiceInfo;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+
+/**
+ * The DebugStore class provides methods for recording various debug events related to service
+ * lifecycle, broadcast receivers and others.
+ * The DebugStore class facilitates debugging ANR issues by recording time-stamped events
+ * related to service lifecycles, broadcast receivers, and other framework operations. It logs
+ * the start and end times of operations within the ANR timer scope called  by framework,
+ * enabling pinpointing of methods and events contributing to ANRs.
+ *
+ * Usage currently includes recording service starts, binds, and asynchronous operations initiated
+ * by broadcast receivers, providing a granular view of system behavior that facilitates
+ * identifying performance bottlenecks and optimizing issue resolution.
+ *
+ * @hide
+ */
+public class DebugStore {
+    private static DebugStoreNative sDebugStoreNative = new DebugStoreNativeImpl();
+
+    @UnsupportedAppUsage
+    @VisibleForTesting
+    public static void setDebugStoreNative(DebugStoreNative nativeImpl) {
+        sDebugStoreNative = nativeImpl;
+    }
+    /**
+     * Records the start of a service.
+     *
+     * @param startId The start ID of the service.
+     * @param flags Additional flags for the service start.
+     * @param intent The Intent associated with the service start.
+     * @return A unique ID for the recorded event.
+     */
+    @UnsupportedAppUsage
+    public static long recordServiceOnStart(int startId, int flags, @Nullable Intent intent) {
+        return sDebugStoreNative.beginEvent(
+                "SvcStart",
+                List.of(
+                        "stId",
+                        String.valueOf(startId),
+                        "flg",
+                        Integer.toHexString(flags),
+                        "act",
+                        Objects.toString(intent != null ? intent.getAction() : null),
+                        "comp",
+                        Objects.toString(intent != null ? intent.getComponent() : null),
+                        "pkg",
+                        Objects.toString(intent != null ? intent.getPackage() : null)));
+    }
+
+    /**
+     * Records the creation of a service.
+     *
+     * @param serviceInfo Information about the service being created.
+     * @return A unique ID for the recorded event.
+     */
+    @UnsupportedAppUsage
+    public static long recordServiceCreate(@Nullable ServiceInfo serviceInfo) {
+        return sDebugStoreNative.beginEvent(
+                "SvcCreate",
+                List.of(
+                        "name",
+                        Objects.toString(serviceInfo != null ? serviceInfo.name : null),
+                        "pkg",
+                        Objects.toString(serviceInfo != null ? serviceInfo.packageName : null)));
+    }
+
+    /**
+     * Records the binding of a service.
+     *
+     * @param isRebind Indicates whether the service is being rebound.
+     * @param intent The Intent associated with the service binding.
+     * @return A unique identifier for the recorded event.
+     */
+    @UnsupportedAppUsage
+    public static long recordServiceBind(boolean isRebind, @Nullable Intent intent) {
+        return sDebugStoreNative.beginEvent(
+                "SvcBind",
+                List.of(
+                        "rebind",
+                        String.valueOf(isRebind),
+                        "act",
+                        Objects.toString(intent != null ? intent.getAction() : null),
+                        "cmp",
+                        Objects.toString(intent != null ? intent.getComponent() : null),
+                        "pkg",
+                        Objects.toString(intent != null ? intent.getPackage() : null)));
+    }
+
+    /**
+     * Records an asynchronous operation initiated by a broadcast receiver through calling GoAsync.
+     *
+     * @param receiverClassName The class name of the broadcast receiver.
+     */
+    @UnsupportedAppUsage
+    public static void recordGoAsync(String receiverClassName) {
+        sDebugStoreNative.recordEvent(
+                "GoAsync",
+                List.of(
+                        "tname",
+                        Thread.currentThread().getName(),
+                        "tid",
+                        String.valueOf(Thread.currentThread().getId()),
+                        "rcv",
+                        Objects.toString(receiverClassName)));
+    }
+
+    /**
+     * Records the completion of a broadcast operation through calling Finish.
+     *
+     * @param receiverClassName The class of the broadcast receiver that completed the operation.
+     */
+    @UnsupportedAppUsage
+    public static void recordFinish(String receiverClassName) {
+        sDebugStoreNative.recordEvent(
+                "Finish",
+                List.of(
+                        "tname",
+                        Thread.currentThread().getName(),
+                        "tid",
+                        String.valueOf(Thread.currentThread().getId()),
+                        "rcv",
+                        Objects.toString(receiverClassName)));
+    }
+    /**
+     * Records the completion of a long-running looper message.
+     *
+     * @param messageCode The code representing the type of the message.
+     * @param targetClass The FQN of the class that handled the message.
+     * @param elapsedTimeMs The time that was taken to process the message, in milliseconds.
+     */
+    @UnsupportedAppUsage
+    public static void recordLongLooperMessage(int messageCode, String targetClass,
+            long elapsedTimeMs) {
+        sDebugStoreNative.recordEvent(
+                "LooperMsg",
+                List.of(
+                        "code",
+                        String.valueOf(messageCode),
+                        "trgt",
+                        Objects.toString(targetClass),
+                        "elapsed",
+                        String.valueOf(elapsedTimeMs)));
+    }
+
+
+    /**
+     * Records the reception of a broadcast.
+     *
+     * @param intent The Intent associated with the broadcast.
+     * @return A unique ID for the recorded event.
+     */
+    @UnsupportedAppUsage
+    public static long recordBroadcastHandleReceiver(@Nullable Intent intent) {
+        return sDebugStoreNative.beginEvent(
+                "HandleReceiver",
+                List.of(
+                        "tname", Thread.currentThread().getName(),
+                        "tid", String.valueOf(Thread.currentThread().getId()),
+                        "act", Objects.toString(intent != null ? intent.getAction() : null),
+                        "cmp", Objects.toString(intent != null ? intent.getComponent() : null),
+                        "pkg", Objects.toString(intent != null ? intent.getPackage() : null)));
+    }
+
+    /**
+     * Ends a previously recorded event.
+     *
+     * @param id The unique ID of the event to be ended.
+     */
+    @UnsupportedAppUsage
+    public static void recordEventEnd(long id) {
+        sDebugStoreNative.endEvent(id, Collections.emptyList());
+    }
+
+    /**
+     * An interface for a class that acts as a wrapper for the static native methods
+     * of the Debug Store.
+     *
+     * It allows us to mock static native methods in our tests and should be removed
+     * once mocking static methods becomes easier.
+     */
+    @VisibleForTesting
+    public interface DebugStoreNative {
+        /**
+         * Begins an event with the given name and attributes.
+         */
+        long beginEvent(String eventName, List<String> attributes);
+        /**
+         * Ends an event with the given ID and attributes.
+         */
+        void endEvent(long id, List<String> attributes);
+        /**
+         * Records an event with the given name and attributes.
+         */
+        void recordEvent(String eventName, List<String> attributes);
+    }
+
+    private static class DebugStoreNativeImpl implements DebugStoreNative {
+        @Override
+        public long beginEvent(String eventName, List<String> attributes) {
+            return DebugStore.beginEventNative(eventName, attributes);
+        }
+
+        @Override
+        public void endEvent(long id, List<String> attributes) {
+            DebugStore.endEventNative(id, attributes);
+        }
+
+        @Override
+        public void recordEvent(String eventName, List<String> attributes) {
+            DebugStore.recordEventNative(eventName, attributes);
+        }
+    }
+
+    private static native long beginEventNative(String eventName, List<String> attributes);
+
+    private static native void endEventNative(long id, List<String> attributes);
+
+    private static native void recordEventNative(String eventName, List<String> attributes);
+}
diff --git a/core/java/com/android/internal/os/PowerStats.java b/core/java/com/android/internal/os/PowerStats.java
index 24971f5..488e06f 100644
--- a/core/java/com/android/internal/os/PowerStats.java
+++ b/core/java/com/android/internal/os/PowerStats.java
@@ -580,10 +580,15 @@
         }
         PowerStatsFormatter uidStatsFormatter = descriptor.getUidStatsFormatter();
         for (int i = 0; i < uidStats.size(); i++) {
+            String formattedStats = uidStatsFormatter.format(uidStats.valueAt(i));
+            if (formattedStats.isBlank()) {
+                continue;
+            }
+
             pw.print("UID ");
             pw.print(UserHandle.formatUid(uidStats.keyAt(i)));
             pw.print(": ");
-            pw.print(uidStatsFormatter.format(uidStats.valueAt(i)));
+            pw.print(formattedStats);
             pw.println();
         }
         pw.decreaseIndent();
diff --git a/core/java/com/android/internal/os/WebViewZygoteInit.java b/core/java/com/android/internal/os/WebViewZygoteInit.java
index 9ed7384..8dee223 100644
--- a/core/java/com/android/internal/os/WebViewZygoteInit.java
+++ b/core/java/com/android/internal/os/WebViewZygoteInit.java
@@ -16,18 +16,15 @@
 
 package com.android.internal.os;
 
-import android.app.ApplicationLoaders;
 import android.app.LoadedApk;
 import android.content.pm.ApplicationInfo;
 import android.net.LocalSocket;
-import android.text.TextUtils;
 import android.util.Log;
 import android.webkit.WebViewFactory;
 import android.webkit.WebViewFactoryProvider;
 import android.webkit.WebViewLibraryLoader;
 
 import java.io.DataOutputStream;
-import java.io.File;
 import java.io.IOException;
 import java.lang.reflect.Method;
 
@@ -83,28 +80,6 @@
             Log.i(TAG, "Application preload done");
         }
 
-        @Override
-        protected void handlePreloadPackage(String packagePath, String libsPath, String libFileName,
-                String cacheKey) {
-            Log.i(TAG, "Beginning package preload");
-            // Ask ApplicationLoaders to create and cache a classloader for the WebView APK so that
-            // our children will reuse the same classloader instead of creating their own.
-            // This enables us to preload Java and native code in the webview zygote process and
-            // have the preloaded versions actually be used post-fork.
-            ClassLoader loader = ApplicationLoaders.getDefault().createAndCacheWebViewClassLoader(
-                    packagePath, libsPath, cacheKey);
-
-            // Add the APK to the Zygote's list of allowed files for children.
-            String[] packageList = TextUtils.split(packagePath, File.pathSeparator);
-            for (String packageEntry : packageList) {
-                Zygote.nativeAllowFileAcrossFork(packageEntry);
-            }
-
-            doPreload(loader, libFileName);
-
-            Log.i(TAG, "Package preload done");
-        }
-
         private void doPreload(ClassLoader loader, String libFileName) {
             // Load the native library using WebViewLibraryLoader to share the RELRO data with other
             // processes.
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index cab84bb..fafa085 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -938,8 +938,6 @@
             throw new IllegalArgumentException(USAP_ERROR_PREFIX + "--get-pid");
         } else if (args.mPreloadDefault) {
             throw new IllegalArgumentException(USAP_ERROR_PREFIX + "--preload-default");
-        } else if (args.mPreloadPackage != null) {
-            throw new IllegalArgumentException(USAP_ERROR_PREFIX + "--preload-package");
         } else if (args.mPreloadApp != null) {
             throw new IllegalArgumentException(USAP_ERROR_PREFIX + "--preload-app");
         } else if (args.mStartChildZygote) {
diff --git a/core/java/com/android/internal/os/ZygoteArguments.java b/core/java/com/android/internal/os/ZygoteArguments.java
index 86b9a59..27ce64e 100644
--- a/core/java/com/android/internal/os/ZygoteArguments.java
+++ b/core/java/com/android/internal/os/ZygoteArguments.java
@@ -141,33 +141,12 @@
     String mAppDataDir;
 
     /**
-     * The APK path of the package to preload, when using --preload-package.
-     */
-    String mPreloadPackage;
-
-    /**
      * A Base64 string representing a serialize ApplicationInfo Parcel,
      when using --preload-app.
      */
     String mPreloadApp;
 
     /**
-     * The native library path of the package to preload, when using --preload-package.
-     */
-    String mPreloadPackageLibs;
-
-    /**
-     * The filename of the native library to preload, when using --preload-package.
-     */
-    String mPreloadPackageLibFileName;
-
-    /**
-     * The cache key under which to enter the preloaded package into the classloader cache, when
-     * using --preload-package.
-     */
-    String mPreloadPackageCacheKey;
-
-    /**
      * Whether this is a request to start preloading the default resources and classes. This
      * argument only makes sense when the zygote is in lazy preload mode (i.e, when it's started
      * with --enable-lazy-preload).
@@ -419,12 +398,6 @@
             } else if (arg.equals("--preload-app")) {
                 ++curArg;
                 mPreloadApp = args.nextArg();
-            } else if (arg.equals("--preload-package")) {
-                curArg += 4;
-                mPreloadPackage = args.nextArg();
-                mPreloadPackageLibs = args.nextArg();
-                mPreloadPackageLibFileName = args.nextArg();
-                mPreloadPackageCacheKey = args.nextArg();
             } else if (arg.equals("--preload-default")) {
                 mPreloadDefault = true;
                 expectRuntimeArgs = false;
@@ -504,11 +477,6 @@
             if (argCount > curArg) {
                 throw new IllegalArgumentException("Unexpected arguments after --query-abi-list.");
             }
-        } else if (mPreloadPackage != null) {
-            if (argCount > curArg) {
-                throw new IllegalArgumentException(
-                    "Unexpected arguments after --preload-package.");
-            }
         } else if (mPreloadApp != null) {
             if (argCount > curArg) {
                 throw new IllegalArgumentException(
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index d4dcec9..2eab081 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -165,14 +165,6 @@
                     return null;
                 }
 
-                if (parsedArgs.mPreloadPackage != null) {
-                    handlePreloadPackage(parsedArgs.mPreloadPackage,
-                            parsedArgs.mPreloadPackageLibs,
-                            parsedArgs.mPreloadPackageLibFileName,
-                            parsedArgs.mPreloadPackageCacheKey);
-                    return null;
-                }
-
                 if (canPreloadApp() && parsedArgs.mPreloadApp != null) {
                     byte[] rawParcelData = Base64.getDecoder().decode(parsedArgs.mPreloadApp);
                     Parcel appInfoParcel = Parcel.obtain();
@@ -475,11 +467,6 @@
         return mSocketOutStream;
     }
 
-    protected void handlePreloadPackage(String packagePath, String libsPath, String libFileName,
-            String cacheKey) {
-        throw new RuntimeException("Zygote does not support package preloading");
-    }
-
     protected boolean canPreloadApp() {
         return false;
     }
diff --git a/core/java/com/android/internal/os/flags.aconfig b/core/java/com/android/internal/os/flags.aconfig
index 2ad6651..c7117e9 100644
--- a/core/java/com/android/internal/os/flags.aconfig
+++ b/core/java/com/android/internal/os/flags.aconfig
@@ -19,4 +19,12 @@
     metadata {
         purpose: PURPOSE_BUGFIX
     }
+}
+
+flag {
+    name: "debug_store_enabled"
+    namespace: "stability"
+    description: "If the debug store is enabled."
+    bug: "314735374"
+    is_fixed_read_only: true
 }
\ No newline at end of file
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index 87e22ed..4828393 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -29,6 +29,7 @@
 import static android.view.WindowInsetsController.APPEARANCE_FORCE_LIGHT_NAVIGATION_BARS;
 import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS;
 import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS;
+import static android.view.WindowInsetsController.APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND;
 import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
 import static android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN;
 import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
@@ -36,6 +37,7 @@
 import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION;
 import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
+import static android.view.flags.Flags.customizableWindowHeaders;
 
 import static com.android.internal.policy.PhoneWindow.FEATURE_OPTIONS_PANEL;
 
@@ -226,6 +228,7 @@
     private boolean mLastHasLeftStableInset = false;
     private int mLastWindowFlags = 0;
     private @InsetsType int mLastForceConsumingTypes = 0;
+    private boolean mLastForceConsumingOpaqueCaptionBar = false;
     private @InsetsType int mLastSuppressScrimTypes = 0;
 
     private int mRootScrollY = 0;
@@ -1068,8 +1071,12 @@
         WindowManager.LayoutParams attrs = mWindow.getAttributes();
         int sysUiVisibility = attrs.systemUiVisibility | getWindowSystemUiVisibility();
 
+        final ViewRootImpl viewRoot = getViewRootImpl();
         final WindowInsetsController controller = getWindowInsetsController();
         final @InsetsType int requestedVisibleTypes = controller.getRequestedVisibleTypes();
+        final @Appearance int appearance = viewRoot != null
+                ? viewRoot.mWindowAttributes.insetsFlags.appearance
+                : controller.getSystemBarsAppearance();
 
         // IME is an exceptional floating window that requires color view.
         final boolean isImeWindow =
@@ -1080,13 +1087,9 @@
                     & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0;
             mLastWindowFlags = attrs.flags;
 
-            final ViewRootImpl viewRoot = getViewRootImpl();
-            final @Appearance int appearance = viewRoot != null
-                    ? viewRoot.mWindowAttributes.insetsFlags.appearance
-                    : controller.getSystemBarsAppearance();
-
             if (insets != null) {
                 mLastForceConsumingTypes = insets.getForceConsumingTypes();
+                mLastForceConsumingOpaqueCaptionBar = insets.isForceConsumingOpaqueCaptionBar();
 
                 final boolean clearsCompatInsets = clearsCompatInsets(attrs.type, attrs.flags,
                         getResources().getConfiguration().windowConfiguration.getActivityType(),
@@ -1209,16 +1212,20 @@
 
         final boolean hideCaptionBar = fullscreen
                 || (requestedVisibleTypes & WindowInsets.Type.captionBar()) == 0;
-        final boolean consumingCaptionBar =
-                ((mLastForceConsumingTypes & WindowInsets.Type.captionBar()) != 0
+        final boolean consumingCaptionBar = Flags.enableCaptionCompatInsetForceConsumption()
+                && ((mLastForceConsumingTypes & WindowInsets.Type.captionBar()) != 0
                         && hideCaptionBar);
 
-        final int consumedTop;
-        if (Flags.enableCaptionCompatInsetForceConsumption()) {
-            consumedTop = (consumingStatusBar || consumingCaptionBar) ? mLastTopInset : 0;
-        } else {
-            consumedTop = consumingStatusBar ? mLastTopInset : 0;
-        }
+        final boolean isOpaqueCaptionBar = customizableWindowHeaders()
+                && (appearance & APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND) == 0;
+        final boolean consumingOpaqueCaptionBar =
+                Flags.enableCaptionCompatInsetForceConsumptionAlways()
+                        && mLastForceConsumingOpaqueCaptionBar
+                        && isOpaqueCaptionBar;
+
+        final int consumedTop =
+                (consumingStatusBar || consumingCaptionBar || consumingOpaqueCaptionBar)
+                        ? mLastTopInset : 0;
         int consumedRight = consumingNavBar ? mLastRightInset : 0;
         int consumedBottom = consumingNavBar ? mLastBottomInset : 0;
         int consumedLeft = consumingNavBar ? mLastLeftInset : 0;
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index 42be4fc..ec004d0 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -2501,6 +2501,9 @@
         if (mEdgeToEdgeEnforced) {
             getAttributes().privateFlags |= PRIVATE_FLAG_EDGE_TO_EDGE_ENFORCED;
             mDecorFitsSystemWindows = false;
+            mStatusBarColor = Color.TRANSPARENT;
+            mNavigationBarDividerColor = Color.TRANSPARENT;
+            // mNavigationBarColor is not reset here because it might be used to draw the scrim.
         }
         if (CompatChanges.isChangeEnabled(OVERRIDE_LAYOUT_IN_DISPLAY_CUTOUT_MODE)
                 && !a.getBoolean(R.styleable.Window_windowOptOutEdgeToEdgeEnforcement,
diff --git a/core/java/com/android/internal/policy/SystemBarUtils.java b/core/java/com/android/internal/policy/SystemBarUtils.java
index efa3697..4ed15fa 100644
--- a/core/java/com/android/internal/policy/SystemBarUtils.java
+++ b/core/java/com/android/internal/policy/SystemBarUtils.java
@@ -92,4 +92,11 @@
         // Equals to status bar height if status bar height is bigger.
         return Math.max(defaultSize, statusBarHeight);
     }
+
+    /**
+     * Gets the taskbar frame height.
+     */
+    public static int getTaskbarHeight(Resources res) {
+        return res.getDimensionPixelSize(R.dimen.taskbar_frame_height);
+    }
 }
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index e29f256..1d43f6f 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -51,8 +51,7 @@
 
     void showWirelessChargingAnimation(int batteryLevel);
 
-    void setImeWindowStatus(int displayId, in IBinder token, int vis, int backDisposition,
-            boolean showImeSwitcher);
+    void setImeWindowStatus(int displayId, int vis, int backDisposition, boolean showImeSwitcher);
     void setWindowState(int display, int window, int state);
 
     void showRecentApps(boolean triggeredFromAltTab);
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index fc60f06..ff08dd2 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -61,8 +61,7 @@
     void setIconVisibility(String slot, boolean visible);
     @UnsupportedAppUsage
     void removeIcon(String slot);
-    void setImeWindowStatus(int displayId, in IBinder token, int vis, int backDisposition,
-            boolean showImeSwitcher);
+    void setImeWindowStatus(int displayId, int vis, int backDisposition, boolean showImeSwitcher);
     void expandSettingsPanel(String subPanel);
 
     // ---- Methods below are for use by the status bar policy services ----
diff --git a/core/java/com/android/internal/statusbar/RegisterStatusBarResult.java b/core/java/com/android/internal/statusbar/RegisterStatusBarResult.java
index 4f827cd..7240aff 100644
--- a/core/java/com/android/internal/statusbar/RegisterStatusBarResult.java
+++ b/core/java/com/android/internal/statusbar/RegisterStatusBarResult.java
@@ -16,7 +16,6 @@
 
 package com.android.internal.statusbar;
 
-import android.os.IBinder;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.ArrayMap;
@@ -35,7 +34,6 @@
     public final int mImeBackDisposition;               // switch[4]
     public final boolean mShowImeSwitcher;              // switch[5]
     public final int mDisabledFlags2;                   // switch[6]
-    public final IBinder mImeToken;
     public final boolean mNavbarColorManagedByIme;
     public final int mBehavior;
     public final int mRequestedVisibleTypes;
@@ -45,7 +43,7 @@
 
     public RegisterStatusBarResult(ArrayMap<String, StatusBarIcon> icons, int disabledFlags1,
             int appearance, AppearanceRegion[] appearanceRegions, int imeWindowVis,
-            int imeBackDisposition, boolean showImeSwitcher, int disabledFlags2, IBinder imeToken,
+            int imeBackDisposition, boolean showImeSwitcher, int disabledFlags2,
             boolean navbarColorManagedByIme, int behavior, int requestedVisibleTypes,
             String packageName, int transientBarTypes, LetterboxDetails[] letterboxDetails) {
         mIcons = new ArrayMap<>(icons);
@@ -56,7 +54,6 @@
         mImeBackDisposition = imeBackDisposition;
         mShowImeSwitcher = showImeSwitcher;
         mDisabledFlags2 = disabledFlags2;
-        mImeToken = imeToken;
         mNavbarColorManagedByIme = navbarColorManagedByIme;
         mBehavior = behavior;
         mRequestedVisibleTypes = requestedVisibleTypes;
@@ -80,7 +77,6 @@
         dest.writeInt(mImeBackDisposition);
         dest.writeBoolean(mShowImeSwitcher);
         dest.writeInt(mDisabledFlags2);
-        dest.writeStrongBinder(mImeToken);
         dest.writeBoolean(mNavbarColorManagedByIme);
         dest.writeInt(mBehavior);
         dest.writeInt(mRequestedVisibleTypes);
@@ -106,7 +102,6 @@
                     final int imeBackDisposition = source.readInt();
                     final boolean showImeSwitcher = source.readBoolean();
                     final int disabledFlags2 = source.readInt();
-                    final IBinder imeToken = source.readStrongBinder();
                     final boolean navbarColorManagedByIme = source.readBoolean();
                     final int behavior = source.readInt();
                     final int requestedVisibleTypes = source.readInt();
@@ -116,7 +111,7 @@
                             source.readParcelableArray(null, LetterboxDetails.class);
                     return new RegisterStatusBarResult(icons, disabledFlags1, appearance,
                             appearanceRegions, imeWindowVis, imeBackDisposition, showImeSwitcher,
-                            disabledFlags2, imeToken, navbarColorManagedByIme, behavior,
+                            disabledFlags2, navbarColorManagedByIme, behavior,
                             requestedVisibleTypes, packageName, transientBarTypes,
                             letterboxDetails);
                 }
diff --git a/core/java/com/android/internal/telephony/IPhoneStateListener.aidl b/core/java/com/android/internal/telephony/IPhoneStateListener.aidl
index 792c223..f177e14 100644
--- a/core/java/com/android/internal/telephony/IPhoneStateListener.aidl
+++ b/core/java/com/android/internal/telephony/IPhoneStateListener.aidl
@@ -82,4 +82,5 @@
     void onCallBackModeStopped(int type, int reason);
     void onSimultaneousCallingStateChanged(in int[] subIds);
     void onCarrierRoamingNtnModeChanged(in boolean active);
+    void onCarrierRoamingNtnEligibleStateChanged(in boolean eligible);
 }
diff --git a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
index 04332cd..e500a37a 100644
--- a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
+++ b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
@@ -121,4 +121,5 @@
     void notifyCallbackModeStarted(int phoneId, int subId, int type);
     void notifyCallbackModeStopped(int phoneId, int subId, int type, int reason);
     void notifyCarrierRoamingNtnModeChanged(int subId, in boolean active);
+    void notifyCarrierRoamingNtnEligibleStateChanged(int subId, in boolean eligible);
 }
diff --git a/core/java/com/android/internal/util/ContrastColorUtil.java b/core/java/com/android/internal/util/ContrastColorUtil.java
index 77de272..0fd1391 100644
--- a/core/java/com/android/internal/util/ContrastColorUtil.java
+++ b/core/java/com/android/internal/util/ContrastColorUtil.java
@@ -31,6 +31,7 @@
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.Icon;
 import android.graphics.drawable.VectorDrawable;
+import android.text.NoCopySpan;
 import android.text.SpannableStringBuilder;
 import android.text.Spanned;
 import android.text.style.BackgroundColorSpan;
@@ -188,6 +189,10 @@
             Object[] spans = ss.getSpans(0, ss.length(), Object.class);
             SpannableStringBuilder builder = new SpannableStringBuilder(ss.toString());
             for (Object span : spans) {
+                if (span instanceof NoCopySpan) {
+                    // These spans can contain external references and should not be copied.
+                    continue;
+                }
                 Object resultSpan = span;
                 if (resultSpan instanceof CharacterStyle) {
                     resultSpan = ((CharacterStyle) span).getUnderlying();
@@ -254,6 +259,10 @@
             Object[] spans = ss.getSpans(0, ss.length(), Object.class);
             SpannableStringBuilder builder = new SpannableStringBuilder(ss.toString());
             for (Object span : spans) {
+                if (span instanceof NoCopySpan) {
+                    // These spans can contain external references and should not be copied.
+                    continue;
+                }
                 Object resultSpan = span;
                 if (resultSpan instanceof CharacterStyle) {
                     resultSpan = ((CharacterStyle) span).getUnderlying();
@@ -300,6 +309,10 @@
             Object[] spans = ss.getSpans(0, ss.length(), Object.class);
             SpannableStringBuilder builder = new SpannableStringBuilder(ss.toString());
             for (Object span : spans) {
+                if (span instanceof NoCopySpan) {
+                    // These spans can contain external references and should not be copied.
+                    continue;
+                }
                 Object resultSpan = span;
                 int spanStart = ss.getSpanStart(span);
                 int spanEnd = ss.getSpanEnd(span);
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index cba27ce..b51678e 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -125,9 +125,9 @@
     void showInputMethodPickerFromClient(in IInputMethodClient client,
             int auxiliarySubtypeMode);
 
-    @EnforcePermission("WRITE_SECURE_SETTINGS")
-    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = "
-            + "android.Manifest.permission.WRITE_SECURE_SETTINGS)")
+    @EnforcePermission(allOf = {"WRITE_SECURE_SETTINGS", "INTERACT_ACROSS_USERS_FULL"})
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf = {android.Manifest."
+    + "permission.WRITE_SECURE_SETTINGS, android.Manifest.permission.INTERACT_ACROSS_USERS_FULL})")
     void showInputMethodPickerFromSystem(int auxiliarySubtypeMode, int displayId);
 
     @EnforcePermission("TEST_INPUT_METHOD")
@@ -143,9 +143,9 @@
      * @param displayId The ID of the display where the input method picker dialog should be shown.
      * @param userId    The ID of the user that triggered the click.
      */
-    @EnforcePermission("WRITE_SECURE_SETTINGS")
-    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = "
-            + "android.Manifest.permission.WRITE_SECURE_SETTINGS)")
+    @EnforcePermission(allOf = {"WRITE_SECURE_SETTINGS" ,"INTERACT_ACROSS_USERS_FULL"})
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf = {android.Manifest."
+    + "permission.WRITE_SECURE_SETTINGS, android.Manifest.permission.INTERACT_ACROSS_USERS_FULL})")
     oneway void onImeSwitchButtonClickFromSystem(int displayId);
 
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = "
@@ -168,10 +168,10 @@
 
     oneway void reportPerceptibleAsync(in IBinder windowToken, boolean perceptible);
 
-    @EnforcePermission("INTERNAL_SYSTEM_WINDOW")
-    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = "
-            + "android.Manifest.permission.INTERNAL_SYSTEM_WINDOW)")
-    void removeImeSurface();
+    @EnforcePermission(allOf = {"INTERNAL_SYSTEM_WINDOW", "INTERACT_ACROSS_USERS_FULL"})
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf = {android.Manifest."
+    + "permission.INTERNAL_SYSTEM_WINDOW, android.Manifest.permission.INTERACT_ACROSS_USERS_FULL})")
+    void removeImeSurface(int displayId);
 
     /** Remove the IME surface. Requires passing the currently focused window. */
     oneway void removeImeSurfaceFromWindowAsync(in IBinder windowToken);
diff --git a/core/java/com/android/internal/view/menu/CascadingMenuPopup.java b/core/java/com/android/internal/view/menu/CascadingMenuPopup.java
index f08e860..c24cf5f 100644
--- a/core/java/com/android/internal/view/menu/CascadingMenuPopup.java
+++ b/core/java/com/android/internal/view/menu/CascadingMenuPopup.java
@@ -5,14 +5,12 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.StyleRes;
-import android.app.AppGlobals;
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Rect;
 import android.os.Handler;
 import android.os.Parcelable;
 import android.os.SystemClock;
-import android.text.TextFlags;
 import android.view.Gravity;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
@@ -217,11 +215,7 @@
 
         mSubMenuHoverHandler = new Handler();
 
-        mItemLayout = AppGlobals.getIntCoreSetting(
-                TextFlags.KEY_ENABLE_NEW_CONTEXT_MENU,
-                TextFlags.ENABLE_NEW_CONTEXT_MENU_DEFAULT ? 1 : 0) != 0
-                ? com.android.internal.R.layout.cascading_menu_item_layout_material
-                : com.android.internal.R.layout.cascading_menu_item_layout;
+        mItemLayout = com.android.internal.R.layout.cascading_menu_item_layout_material;
     }
 
     @Override
diff --git a/core/java/com/android/internal/view/menu/ListMenuItemView.java b/core/java/com/android/internal/view/menu/ListMenuItemView.java
index 468705d..b73cacb 100644
--- a/core/java/com/android/internal/view/menu/ListMenuItemView.java
+++ b/core/java/com/android/internal/view/menu/ListMenuItemView.java
@@ -16,13 +16,10 @@
 
 package com.android.internal.view.menu;
 
-import android.app.AppGlobals;
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
-import android.text.ClientFlags;
-import android.text.TextFlags;
 import android.util.AttributeSet;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -36,6 +33,8 @@
 import android.widget.RadioButton;
 import android.widget.TextView;
 
+import com.android.text.flags.Flags;
+
 /**
  * The item view for each item in the ListView-based MenuViews.
  */
@@ -62,8 +61,6 @@
 
     private int mMenuType;
 
-    private boolean mUseNewContextMenu;
-
     private LayoutInflater mInflater;
 
     private boolean mForceShowIcon;
@@ -90,10 +87,6 @@
 
         a.recycle();
         b.recycle();
-
-        mUseNewContextMenu = AppGlobals.getIntCoreSetting(
-                TextFlags.KEY_ENABLE_NEW_CONTEXT_MENU,
-                TextFlags.ENABLE_NEW_CONTEXT_MENU_DEFAULT ? 1 : 0) != 0;
     }
 
     public ListMenuItemView(Context context, AttributeSet attrs, int defStyleAttr) {
@@ -291,7 +284,7 @@
     private void insertIconView() {
         LayoutInflater inflater = getInflater();
         mIconView = (ImageView) inflater.inflate(
-                mUseNewContextMenu && !ClientFlags.fixMisalignedContextMenu()
+                !Flags.fixMisalignedContextMenu()
                         ? com.android.internal.R.layout.list_menu_item_fixed_size_icon :
                         com.android.internal.R.layout.list_menu_item_icon,
                 this, false);
diff --git a/core/java/com/android/internal/view/menu/StandardMenuPopup.java b/core/java/com/android/internal/view/menu/StandardMenuPopup.java
index c254e99..c43a8c6 100644
--- a/core/java/com/android/internal/view/menu/StandardMenuPopup.java
+++ b/core/java/com/android/internal/view/menu/StandardMenuPopup.java
@@ -16,12 +16,9 @@
 
 package com.android.internal.view.menu;
 
-import android.app.AppGlobals;
 import android.content.Context;
 import android.content.res.Resources;
 import android.os.Parcelable;
-import android.text.ClientFlags;
-import android.text.TextFlags;
 import android.view.Gravity;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
@@ -38,6 +35,8 @@
 import android.widget.PopupWindow.OnDismissListener;
 import android.widget.TextView;
 
+import com.android.text.flags.Flags;
+
 import java.util.Objects;
 
 /**
@@ -120,16 +119,11 @@
     public StandardMenuPopup(Context context, MenuBuilder menu, View anchorView, int popupStyleAttr,
             int popupStyleRes, boolean overflowOnly) {
         mContext = Objects.requireNonNull(context);
-        boolean useNewContextMenu = AppGlobals.getIntCoreSetting(
-                TextFlags.KEY_ENABLE_NEW_CONTEXT_MENU,
-                TextFlags.ENABLE_NEW_CONTEXT_MENU_DEFAULT ? 1 : 0) != 0;
-
         mMenu = menu;
         mOverflowOnly = overflowOnly;
         final LayoutInflater inflater = LayoutInflater.from(context);
         mAdapter = new MenuAdapter(menu, inflater, mOverflowOnly,
-                ClientFlags.fixMisalignedContextMenu() && useNewContextMenu
-                        ? ITEM_LAYOUT_MATERIAL : ITEM_LAYOUT);
+                Flags.fixMisalignedContextMenu() ? ITEM_LAYOUT_MATERIAL : ITEM_LAYOUT);
         mPopupStyleAttr = popupStyleAttr;
         mPopupStyleRes = popupStyleRes;
 
diff --git a/core/java/com/android/internal/widget/remotecompose/core/Operations.java b/core/java/com/android/internal/widget/remotecompose/core/Operations.java
index fc8668e..4b8dbf6 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/Operations.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/Operations.java
@@ -37,6 +37,7 @@
 import com.android.internal.widget.remotecompose.core.operations.FloatConstant;
 import com.android.internal.widget.remotecompose.core.operations.FloatExpression;
 import com.android.internal.widget.remotecompose.core.operations.Header;
+import com.android.internal.widget.remotecompose.core.operations.IntegerExpression;
 import com.android.internal.widget.remotecompose.core.operations.MatrixRestore;
 import com.android.internal.widget.remotecompose.core.operations.MatrixRotate;
 import com.android.internal.widget.remotecompose.core.operations.MatrixSave;
@@ -54,6 +55,8 @@
 import com.android.internal.widget.remotecompose.core.operations.TextMerge;
 import com.android.internal.widget.remotecompose.core.operations.Theme;
 import com.android.internal.widget.remotecompose.core.operations.utilities.IntMap;
+import com.android.internal.widget.remotecompose.core.types.BooleanConstant;
+import com.android.internal.widget.remotecompose.core.types.IntegerConstant;
 
 /**
  * List of operations supported in a RemoteCompose document
@@ -109,6 +112,9 @@
     public static final int TEXT_MERGE = 136;
     public static final int NAMED_VARIABLE = 137;
     public static final int COLOR_CONSTANT = 138;
+    public static final int DATA_INT = 140;
+    public static final int DATA_BOOLEAN = 143;
+    public static final int INTEGER_EXPRESSION = 144;
 
     /////////////////////////////////////////======================
     public static IntMap<CompanionOperation> map = new IntMap<>();
@@ -153,6 +159,9 @@
         map.put(TEXT_MERGE, TextMerge.COMPANION);
         map.put(NAMED_VARIABLE, NamedVariable.COMPANION);
         map.put(COLOR_CONSTANT, ColorConstant.COMPANION);
+        map.put(DATA_INT, IntegerConstant.COMPANION);
+        map.put(INTEGER_EXPRESSION, IntegerExpression.COMPANION);
+        map.put(DATA_BOOLEAN, BooleanConstant.COMPANION);
     }
 
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/PaintContext.java b/core/java/com/android/internal/widget/remotecompose/core/PaintContext.java
index ecd0efc..6d8a442 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/PaintContext.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/PaintContext.java
@@ -59,6 +59,16 @@
 
     public abstract void drawRect(float left, float top, float right, float bottom);
 
+    /**
+     * this caches the paint to a paint stack
+     */
+    public abstract void  savePaint();
+
+    /**
+     * This restores the paint form the paint stack
+     */
+    public abstract void  restorePaint();
+
     public abstract void drawRoundRect(float left,
                                        float top,
                                        float right,
@@ -119,6 +129,10 @@
                                        float start,
                                        float stop);
 
+    /**
+     * This applies changes to the current paint
+     * @param mPaintData the list of changes
+     */
     public abstract void applyPaint(PaintBundle mPaintData);
 
     /**
diff --git a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java
index d462c7d..f5f155e 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java
@@ -37,6 +37,7 @@
 import com.android.internal.widget.remotecompose.core.operations.FloatConstant;
 import com.android.internal.widget.remotecompose.core.operations.FloatExpression;
 import com.android.internal.widget.remotecompose.core.operations.Header;
+import com.android.internal.widget.remotecompose.core.operations.IntegerExpression;
 import com.android.internal.widget.remotecompose.core.operations.MatrixRestore;
 import com.android.internal.widget.remotecompose.core.operations.MatrixRotate;
 import com.android.internal.widget.remotecompose.core.operations.MatrixSave;
@@ -55,6 +56,7 @@
 import com.android.internal.widget.remotecompose.core.operations.Utils;
 import com.android.internal.widget.remotecompose.core.operations.paint.PaintBundle;
 import com.android.internal.widget.remotecompose.core.operations.utilities.easing.FloatAnimation;
+import com.android.internal.widget.remotecompose.core.types.IntegerConstant;
 
 import java.io.File;
 import java.io.FileInputStream;
@@ -876,6 +878,27 @@
         return Utils.asNan(id);
     }
 
+
+    /**
+     * Add a Integer return an id number pointing to that float.
+     * @param value
+     * @return
+     */
+    public int addInteger(int value) {
+        int id = mRemoteComposeState.cacheInteger(value);
+        IntegerConstant.COMPANION.apply(mBuffer, id, value);
+        return id;
+    }
+
+    /**
+     * Add a IntegerId as float ID.
+     * @param id id to be converted
+     * @return
+     */
+    public float asFloatId(int id) {
+        return Utils.asNan(id);
+    }
+
     /**
      * Add a float that is a computation based on variables
      * @param value A RPN style float operation i.e. "4, 3, ADD" outputs 7
@@ -901,6 +924,18 @@
     }
 
     /**
+     * Add and integer expression
+     * @param mask defines which elements are operators or variables
+     * @param value array of values to calculate maximum 32
+     * @return
+     */
+    public int addIntegerExpression(int mask, int[] value) {
+        int id = mRemoteComposeState.cache(value);
+        IntegerExpression.COMPANION.apply(mBuffer, id, mask, value);
+        return  id;
+    }
+
+    /**
      * Add a simple color
      * @param color
      * @return id that represents that color
@@ -1038,5 +1073,6 @@
         NamedVariable.COMPANION.apply(mBuffer, id,
                 NamedVariable.COLOR_TYPE, name);
     }
+
 }
 
diff --git a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeState.java b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeState.java
index bfe67c8..6b06a54 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeState.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeState.java
@@ -21,6 +21,8 @@
 import static com.android.internal.widget.remotecompose.core.RemoteContext.ID_WINDOW_HEIGHT;
 import static com.android.internal.widget.remotecompose.core.RemoteContext.ID_WINDOW_WIDTH;
 
+import com.android.internal.widget.remotecompose.core.operations.utilities.IntFloatMap;
+import com.android.internal.widget.remotecompose.core.operations.utilities.IntIntMap;
 import com.android.internal.widget.remotecompose.core.operations.utilities.IntMap;
 
 import java.util.ArrayList;
@@ -37,16 +39,12 @@
     private final IntMap<Object> mIntDataMap = new IntMap<>();
     private final IntMap<Boolean> mIntWrittenMap = new IntMap<>();
     private final HashMap<Object, Integer> mDataIntMap = new HashMap();
-    private final float[] mFloatMap = new float[MAX_FLOATS]; // efficient cache
-    private final int[] mColorMap = new int[MAX_COLORS]; // efficient cache
+    private final IntFloatMap mFloatMap = new IntFloatMap(); // efficient cache
+    private final IntIntMap mIntegerMap = new IntIntMap(); // efficient cache
+    private final IntIntMap mColorMap = new IntIntMap(); // efficient cache
     private final boolean[] mColorOverride = new boolean[MAX_COLORS];
     private int mNextId = START_ID;
 
-    {
-        for (int i = 0; i < mFloatMap.length; i++) {
-            mFloatMap[i] = Float.NaN;
-        }
-    }
 
     /**
      * Get Object based on id. The system will cache things like bitmaps
@@ -113,7 +111,18 @@
      */
     public int cacheFloat(float item) {
         int id = nextId();
-        mFloatMap[id] = item;
+        mFloatMap.put(id, item);
+        mIntegerMap.put(id, (int) item);
+        return id;
+    }
+
+    /**
+     * Insert an item in the cache
+     */
+    public int cacheInteger(int item) {
+        int id = nextId();
+        mIntegerMap.put(id, item);
+        mFloatMap.put(id, item);
         return id;
     }
 
@@ -121,21 +130,43 @@
      * Insert an item in the cache
      */
     public void cacheFloat(int id, float item) {
-        mFloatMap[id] = item;
+        mFloatMap.put(id, item);
     }
 
     /**
-     * Insert an item in the cache
+     * Insert an float item in the cache
      */
     public void updateFloat(int id, float item) {
-        mFloatMap[id] = item;
+        mFloatMap.put(id, item);
+        mIntegerMap.put(id, (int) item);
     }
 
     /**
-     * get float
+     * Insert an integer item in the cache
+     */
+    public void updateInteger(int id, int item) {
+        mFloatMap.put(id, item);
+        mIntegerMap.put(id, item);
+    }
+
+    /**
+     * get a float from the float cache
+     *
+     * @param id of the float value
+     * @return the float value
      */
     public float getFloat(int id) {
-        return mFloatMap[id];
+        return mFloatMap.get(id);
+    }
+
+    /**
+     * get an integer from the cache
+     *
+     * @param id of the integer value
+     * @return the integer
+     */
+    public int getInteger(int id) {
+        return mIntegerMap.get(id);
     }
 
     /**
@@ -145,11 +176,12 @@
      * @return
      */
     public int getColor(int id) {
-        return mColorMap[id];
+        return mColorMap.get(id);
     }
 
     /**
      * Modify the color at id.
+     *
      * @param id
      * @param color
      */
@@ -157,7 +189,7 @@
         if (mColorOverride[id]) {
             return;
         }
-        mColorMap[id] = color;
+        mColorMap.put(id, color);
     }
 
     /**
@@ -169,7 +201,7 @@
      */
     public void overrideColor(int id, int color) {
         mColorOverride[id] = true;
-        mColorMap[id] = color;
+        mColorMap.put(id, color);
     }
 
     /**
@@ -205,6 +237,7 @@
 
     /**
      * Get the next available id
+     *
      * @return
      */
     public int nextId() {
@@ -213,6 +246,7 @@
 
     /**
      * Set the next id
+     *
      * @param id
      */
     public void setNextId(int id) {
@@ -234,6 +268,7 @@
 
     /**
      * Commands that listen to variables add themselves.
+     *
      * @param id
      * @param variableSupport
      */
@@ -243,6 +278,7 @@
 
     /**
      * List of Commands that need to be updated
+     *
      * @param context
      * @return
      */
@@ -264,6 +300,7 @@
 
     /**
      * Set the width of the overall document on screen.
+     *
      * @param width
      */
     public void setWindowWidth(float width) {
@@ -272,6 +309,7 @@
 
     /**
      * Set the width of the overall document on screen.
+     *
      * @param height
      */
     public void setWindowHeight(float height) {
diff --git a/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java b/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java
index 32027d8..41eeb5b 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java
@@ -39,6 +39,7 @@
 
     public float mWidth = 0f;
     public float mHeight = 0f;
+    private float mAnimationTime;
 
     /**
      * Load a path under an id.
@@ -65,11 +66,20 @@
     public abstract void loadColor(int id, int color);
 
     /**
+     * Set the animation time allowing the creator to control animation rates
+     * @param time
+     */
+    public void setAnimationTime(float time) {
+        mAnimationTime = time;
+    }
+
+    /**
      * gets the time animation clock as float in seconds
      * @return a monotonic time in seconds (arbitrary zero point)
      */
     public float getAnimationTime() {
-        return (System.nanoTime() - mStart) * 1E-9f;
+        mAnimationTime = (System.nanoTime() - mStart) * 1E-9f; // Eliminate
+        return mAnimationTime;
     }
 
     /**
@@ -213,6 +223,13 @@
     public abstract void loadFloat(int id, float value);
 
     /**
+     * Load a float
+     * @param id
+     * @param value
+     */
+    public abstract void loadInteger(int id, int value);
+
+    /**
      * Load an animated float associated with an id
      * Todo: Remove?
      * @param id
@@ -235,6 +252,13 @@
     public abstract float getFloat(int id);
 
     /**
+     * Get a float given an id
+     * @param id
+     * @return
+     */
+    public abstract int getInteger(int id);
+
+    /**
      * Get the color given and ID
      * @param id
      * @return
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase3.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase3.java
index 56b2f1f..5a4a9f3 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase3.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase3.java
@@ -63,23 +63,23 @@
 
     @Override
     public void updateVariables(RemoteContext context) {
-        mV1 = (Float.isNaN(mValue1))
+        mV1 = (Utils.isVariable(mValue1))
                 ? context.getFloat(Utils.idFromNan(mValue1)) : mValue1;
-        mV2 = (Float.isNaN(mValue2))
+        mV2 = (Utils.isVariable(mValue2))
                 ? context.getFloat(Utils.idFromNan(mValue2)) : mValue2;
-        mV3 = (Float.isNaN(mValue3))
+        mV3 = (Utils.isVariable(mValue3))
                 ? context.getFloat(Utils.idFromNan(mValue3)) : mValue3;
     }
 
     @Override
     public void registerListening(RemoteContext context) {
-        if (Float.isNaN(mValue1)) {
+        if (Utils.isVariable(mValue1)) {
             context.listensTo(Utils.idFromNan(mValue1), this);
         }
-        if (Float.isNaN(mValue2)) {
+        if (Utils.isVariable(mValue2)) {
             context.listensTo(Utils.idFromNan(mValue2), this);
         }
-        if (Float.isNaN(mValue3)) {
+        if (Utils.isVariable(mValue3)) {
             context.listensTo(Utils.idFromNan(mValue3), this);
         }
     }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextRun.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextRun.java
deleted file mode 100644
index a099252..0000000
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextRun.java
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.internal.widget.remotecompose.core.operations;
-
-import com.android.internal.widget.remotecompose.core.CompanionOperation;
-import com.android.internal.widget.remotecompose.core.Operation;
-import com.android.internal.widget.remotecompose.core.Operations;
-import com.android.internal.widget.remotecompose.core.PaintContext;
-import com.android.internal.widget.remotecompose.core.PaintOperation;
-import com.android.internal.widget.remotecompose.core.WireBuffer;
-
-import java.util.List;
-
-public class DrawTextRun extends PaintOperation {
-    public static final Companion COMPANION = new Companion();
-    int mTextID;
-    int mStart = 0;
-    int mEnd = 0;
-    int mContextStart = 0;
-    int mContextEnd = 0;
-    float mX = 0f;
-    float mY = 0f;
-    boolean mRtl = false;
-
-    public DrawTextRun(int textID,
-                       int start,
-                       int end,
-                       int contextStart,
-                       int contextEnd,
-                       float x,
-                       float y,
-                       boolean rtl) {
-        mTextID = textID;
-        mStart = start;
-        mEnd = end;
-        mContextStart = contextStart;
-        mContextEnd = contextEnd;
-        mX = x;
-        mY = y;
-        mRtl = rtl;
-    }
-
-    @Override
-    public void write(WireBuffer buffer) {
-        COMPANION.apply(buffer, mTextID, mStart, mEnd, mContextStart, mContextEnd, mX, mY, mRtl);
-
-    }
-
-    @Override
-    public String toString() {
-        return "";
-    }
-
-    public static class Companion implements CompanionOperation {
-        private Companion() {
-        }
-
-        @Override
-        public void read(WireBuffer buffer, List<Operation> operations) {
-            int text = buffer.readInt();
-            int start = buffer.readInt();
-            int end = buffer.readInt();
-            int contextStart = buffer.readInt();
-            int contextEnd = buffer.readInt();
-            float x = buffer.readFloat();
-            float y = buffer.readFloat();
-            boolean rtl = buffer.readBoolean();
-            DrawTextRun op = new DrawTextRun(text, start, end, contextStart, contextEnd, x, y, rtl);
-
-            operations.add(op);
-        }
-
-        @Override
-        public String name() {
-            return "";
-        }
-
-        @Override
-        public int id() {
-            return 0;
-        }
-
-        public void apply(WireBuffer buffer,
-                          int textID,
-                          int start,
-                          int end,
-                          int contextStart,
-                          int contextEnd,
-                          float x,
-                          float y,
-                          boolean rtl) {
-            buffer.start(Operations.DRAW_TEXT_RUN);
-            buffer.writeInt(textID);
-            buffer.writeInt(start);
-            buffer.writeInt(end);
-            buffer.writeInt(contextStart);
-            buffer.writeInt(contextEnd);
-            buffer.writeFloat(x);
-            buffer.writeFloat(y);
-            buffer.writeBoolean(rtl);
-        }
-    }
-
-    @Override
-    public void paint(PaintContext context) {
-        context.drawTextRun(mTextID, mStart, mEnd, mContextStart, mContextEnd, mX, mY, mRtl);
-    }
-}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/IntegerExpression.java b/core/java/com/android/internal/widget/remotecompose/core/operations/IntegerExpression.java
new file mode 100644
index 0000000..d52df5d
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/IntegerExpression.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations;
+
+import com.android.internal.widget.remotecompose.core.CompanionOperation;
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.VariableSupport;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+import com.android.internal.widget.remotecompose.core.operations.utilities.IntegerExpressionEvaluator;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Operation to deal with AnimatedFloats
+ * This is designed to be an optimized calculation for things like
+ * injecting the width of the component int draw rect
+ * As well as supporting generalized animation floats.
+ * The floats represent a RPN style calculator
+ */
+public class IntegerExpression implements Operation, VariableSupport {
+    public int mId;
+    private int mMask;
+    private int mPreMask;
+    public int[] mSrcValue;
+    public int[] mPreCalcValue;
+    private float mLastChange = Float.NaN;
+    public static final Companion COMPANION = new Companion();
+    public static final int MAX_STRING_SIZE = 4000;
+    IntegerExpressionEvaluator mExp = new IntegerExpressionEvaluator();
+
+    public IntegerExpression(int id, int mask, int[] value) {
+        this.mId = id;
+        this.mMask = mask;
+        this.mSrcValue = value;
+    }
+
+    @Override
+    public void updateVariables(RemoteContext context) {
+        if (mPreCalcValue == null || mPreCalcValue.length != mSrcValue.length) {
+            mPreCalcValue = new int[mSrcValue.length];
+        }
+        mPreMask = mMask;
+        for (int i = 0; i < mSrcValue.length; i++) {
+            if (isId(mMask, i, mSrcValue[i])) {
+                mPreMask &= ~(0x1 << i);
+                mPreCalcValue[i] = context.getInteger(mSrcValue[i]);
+            } else {
+                mPreCalcValue[i] = mSrcValue[i];
+            }
+        }
+    }
+
+
+    @Override
+    public void registerListening(RemoteContext context) {
+        for (int i = 0; i < mSrcValue.length; i++) {
+            if (isId(mMask, i, mSrcValue[i])) {
+                context.listensTo(mSrcValue[i], this);
+            }
+        }
+    }
+
+    @Override
+    public void apply(RemoteContext context) {
+        updateVariables(context);
+        float t = context.getAnimationTime();
+        if (Float.isNaN(mLastChange)) {
+            mLastChange = t;
+        }
+        int v = mExp.eval(mPreMask, Arrays.copyOf(mPreCalcValue, mPreCalcValue.length));
+        context.loadInteger(mId, v);
+    }
+
+    @Override
+    public void write(WireBuffer buffer) {
+        COMPANION.apply(buffer, mId, mMask, mSrcValue);
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder s = new StringBuilder();
+        for (int i = 0; i < mPreCalcValue.length; i++) {
+            if (i != 0) {
+                s.append(" ");
+            }
+            if (IntegerExpressionEvaluator.isOperation(mMask, i)) {
+                if (isId(mMask, i, mSrcValue[i])) {
+                    s.append("[" + mSrcValue[i] + "]");
+                } else {
+                    s.append(IntegerExpressionEvaluator.toMathName(mPreCalcValue[i]));
+                }
+            } else {
+                s.append(mSrcValue[i]);
+            }
+        }
+        return "IntegerExpression[" + mId + "] = (" + s + ")";
+    }
+
+    public static class Companion implements CompanionOperation {
+        private Companion() {
+        }
+
+        @Override
+        public String name() {
+            return "FloatExpression";
+        }
+
+        @Override
+        public int id() {
+            return Operations.INTEGER_EXPRESSION;
+        }
+
+        /**
+         * Writes out the operation to the buffer
+         *
+         * @param buffer
+         * @param id
+         * @param mask
+         * @param value
+         */
+        public void apply(WireBuffer buffer, int id, int mask, int[] value) {
+            buffer.start(Operations.INTEGER_EXPRESSION);
+            buffer.writeInt(id);
+            buffer.writeInt(mask);
+            buffer.writeInt(value.length);
+            for (int i = 0; i < value.length; i++) {
+                buffer.writeInt(value[i]);
+            }
+        }
+
+        @Override
+        public void read(WireBuffer buffer, List<Operation> operations) {
+            int id = buffer.readInt();
+            int mask = buffer.readInt();
+            int len = buffer.readInt();
+
+            int[] values = new int[len];
+            for (int i = 0; i < values.length; i++) {
+                values[i] = buffer.readInt();
+            }
+
+            operations.add(new IntegerExpression(id, mask, values));
+        }
+    }
+
+    @Override
+    public String deepToString(String indent) {
+        return indent + toString();
+    }
+
+    /**
+     * given the "i" position in the mask is this an ID
+     * @param mask 32 bit mask used for defining numbers vs other
+     * @param i the bit in question
+     * @param value the value
+     * @return true if this is an ID
+     */
+    public static boolean isId(int mask, int i, int value) {
+        return ((1 << i) & mask) != 0 && value < IntegerExpressionEvaluator.OFFSET;
+    }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/Utils.java b/core/java/com/android/internal/widget/remotecompose/core/operations/Utils.java
index fcb3bfa..e9b0c3b 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/Utils.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/Utils.java
@@ -40,7 +40,7 @@
      * @param n
      * @return
      */
-    static String trimString(String str, int n) {
+    public static String trimString(String str, int n) {
         if (str.length() > n) {
             str = str.substring(0, n - 3) + "...";
         }
@@ -55,6 +55,9 @@
      */
     public static String floatToString(float idvalue, float value) {
         if (Float.isNaN(idvalue)) {
+            if (idFromNan(value) == 0) {
+                return "NaN";
+            }
             return "[" + idFromNan(idvalue) + "]" + floatToString(value);
         }
         return floatToString(value);
@@ -67,6 +70,9 @@
      */
     public static String floatToString(float value) {
         if (Float.isNaN(value)) {
+            if (idFromNan(value) == 0) {
+                return "NaN";
+            }
             return "[" + idFromNan(value) + "]";
         }
         return Float.toString(value);
@@ -107,6 +113,7 @@
     public static boolean isVariable(float v) {
         if (Float.isNaN(v)) {
             int id = idFromNan(v);
+            if (id == 0) return false;
             return id > 40 || id < 10;
         }
         return false;
@@ -222,6 +229,4 @@
         }
         return 0;
     }
-
-
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/paint/Painter.java b/core/java/com/android/internal/widget/remotecompose/core/operations/paint/Painter.java
new file mode 100644
index 0000000..ada3757
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/paint/Painter.java
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations.paint;
+
+
+/**
+ * Provides a Builder pattern for a PaintBundle
+ */
+class Painter {
+    PaintBundle mPaint;
+
+    /**
+     * Write the paint to the buffer
+     */
+    public PaintBundle commit() {
+        return mPaint;
+    }
+
+    public Painter setAntiAlias(boolean aa) {
+        mPaint.setAntiAlias(aa);
+        return this;
+    }
+
+    public Painter setColor(int color) {
+        mPaint.setColor(color);
+        return this;
+    }
+
+    public Painter setColorId(int colorId) {
+        mPaint.setColorId(colorId);
+        return this;
+    }
+
+    /**
+     * Set the paint's Join.
+     *
+     * @param join set the paint's Join, used whenever the paint's style is
+     *             Stroke or StrokeAndFill.
+     */
+    public Painter setStrokeJoin(int join) {
+        mPaint.setStrokeJoin(join);
+        return this;
+    }
+
+    /**
+     * Set the width for stroking. Pass 0 to stroke in hairline mode.
+     * Hairlines always draws a single
+     * pixel independent of the canvas's matrix.
+     *
+     * @param width set the paint's stroke width, used whenever the paint's
+     *             style is Stroke or StrokeAndFill.
+     */
+    public Painter setStrokeWidth(float width) {
+        mPaint.setStrokeWidth(width);
+        return this;
+    }
+
+    /**
+     * Set the paint's style, used for controlling how primitives' geometries
+     * are interpreted (except for drawBitmap, which always assumes Fill).
+     *
+     * @param style The new style to set in the paint
+     */
+    public Painter setStyle(int style) {
+        mPaint.setStyle(style);
+        return this;
+    }
+
+    /**
+     * Set the paint's Cap.
+     *
+     * @param cap set the paint's line cap style, used whenever the paint's
+     *           style is Stroke or StrokeAndFill.
+     */
+    public Painter setStrokeCap(int cap) {
+        mPaint.setStrokeCap(cap);
+        return this;
+    }
+
+    /**
+     * Set the paint's stroke miter value. This is used to control the behavior
+     * of miter joins when the joins angle is sharp. This value must be >= 0.
+     *
+     * @param miter set the miter limit on the paint, used whenever the paint's
+     *             style is Stroke or StrokeAndFill.
+     */
+    public Painter setStrokeMiter(float miter) {
+        mPaint.setStrokeMiter(miter);
+        return this;
+    }
+
+    /**
+     * Helper to setColor(), that only assigns the color's alpha value,
+     * leaving its r,g,b values unchanged. Results are undefined if the alpha
+     * value is outside of the range [0..1.0]
+     *
+     * @param alpha set the alpha component [0..1.0] of the paint's color.
+     */
+    public Painter setAlpha(float alpha) {
+        mPaint.setAlpha((alpha > 2) ? alpha / 255f : alpha);
+        return this;
+    }
+
+    /**
+     * Create a color filter that uses the specified color and Porter-Duff mode.
+     *
+     * @param color The ARGB source color used with the specified Porter-Duff
+     *             mode
+     * @param mode  The porter-duff mode that is applied
+     */
+    public Painter setPorterDuffColorFilter(int color, int mode) {
+        mPaint.setColorFilter(color, mode);
+        return this;
+    }
+
+    /**
+     * sets a shader that draws a linear gradient along a line.
+     *
+     * @param startX    The x-coordinate for the start of the gradient line
+     * @param startY    The y-coordinate for the start of the gradient line
+     * @param endX      The x-coordinate for the end of the gradient line
+     * @param endY      The y-coordinate for the end of the gradient line
+     * @param colors    The sRGB colors to be distributed along the gradient
+     *                  line
+     * @param positions May be null. The relative positions [0..1] of each
+     *                 corresponding color in the colors array. If this is null,
+     *                 the colors are distributed evenly along the gradient
+     *                 line.
+     * @param tileMode  The Shader tiling mode
+     */
+    public Painter setLinearGradient(
+            float startX,
+            float startY,
+            float endX,
+            float endY,
+            int[] colors,
+            float[] positions,
+            int tileMode
+    ) {
+        mPaint.setLinearGradient(colors, positions, startX,
+                startY, endX, endY, tileMode);
+        return this;
+    }
+
+    /**
+     * Sets a shader that draws a radial gradient given the center and radius.
+     *
+     * @param centerX   The x-coordinate of the center of the radius
+     * @param centerY   The y-coordinate of the center of the radius
+     * @param radius    Must be positive. The radius of the circle for this
+     *                  gradient.
+     * @param colors    The sRGB colors to be distributed between the center
+     *                  and edge of the circle
+     * @param positions May be <code>null</code>. Valid values are between
+     *                  <code>0.0f</code> and
+     *                  <code>1.0f</code>. The relative position of each
+     *                  corresponding color in the colors array. If
+     *                  <code>null</code>, colors are distributed evenly
+     *                  between the center and edge of the circle.
+     * @param tileMode  The Shader tiling mode
+     */
+    public Painter setRadialGradient(
+            float centerX,
+            float centerY,
+            float radius,
+            int[] colors,
+            float[] positions,
+            int tileMode
+    ) {
+        mPaint.setRadialGradient(colors, positions, centerX,
+                centerY, radius, tileMode);
+        return this;
+    }
+
+    /**
+     * Set a shader that draws a sweep gradient around a center point.
+     *
+     * @param centerX   The x-coordinate of the center
+     * @param centerY   The y-coordinate of the center
+     * @param colors    The sRGB colors to be distributed between around the
+     *                  center. There must be at least 2 colors in the array.
+     * @param positions May be NULL. The relative position of each corresponding
+     *                 color in the colors array, beginning with 0 and ending
+     *                 with 1.0. If the values are not monotonic, the drawing
+     *                  may produce unexpected results. If positions is NULL,
+     *                  then the colors are automatically spaced evenly.
+     */
+    public Painter setSweepGradient(
+            float centerX,
+            float centerY,
+            int[] colors,
+            float[] positions
+    ) {
+        mPaint.setSweepGradient(colors, positions, centerX, centerY);
+        return this;
+    }
+
+    /**
+     * Set the paint's text size. This value must be > 0
+     *
+     * @param size set the paint's text size in pixel units.
+     */
+    public Painter setTextSize(float size) {
+        mPaint.setTextSize(size);
+        return this;
+    }
+
+    /**
+     * sets a typeface object that best matches the specified existing
+     * typeface and the specified weight and italic style
+     *
+     * <p>Below are numerical values and corresponding common weight names.</p>
+     * <table> <thead>
+     * <tr><th>Value</th><th>Common weight name</th></tr> </thead> <tbody>
+     * <tr><td>100</td><td>Thin</td></tr>
+     * <tr><td>200</td><td>Extra Light</td></tr>
+     * <tr><td>300</td><td>Light</td></tr>
+     * <tr><td>400</td><td>Normal</td></tr>
+     * <tr><td>500</td><td>Medium</td></tr>
+     * <tr><td>600</td><td>Semi Bold</td></tr>
+     * <tr><td>700</td><td>Bold</td></tr>
+     * <tr><td>800</td><td>Extra Bold</td></tr>
+     * <tr><td>900</td><td>Black</td></tr> </tbody> </table>
+     *
+     * @param fontType 0 = default 1 = sans serif 2 = serif 3 = monospace
+     * @param weight   The desired weight to be drawn.
+     * @param italic   {@code true} if italic style is desired to be drawn.
+     *                            Otherwise, {@code false}
+     */
+    public Painter setTypeface(int fontType, int weight, boolean italic) {
+        mPaint.setTextStyle(fontType, weight, italic);
+        return this;
+    }
+
+
+    public Painter setFilterBitmap(boolean filter) {
+        mPaint.setFilterBitmap(filter);
+        return this;
+    }
+
+
+    public Painter setShader(int id) {
+        mPaint.setShader(id);
+        return this;
+    }
+
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntFloatMap.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntFloatMap.java
new file mode 100644
index 0000000..23c3ec5
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntFloatMap.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations.utilities;
+
+import java.util.Arrays;
+
+public class IntFloatMap {
+
+    private static final int DEFAULT_CAPACITY = 16;
+    private static final float LOAD_FACTOR = 0.75f;
+    private static final int NOT_PRESENT = Integer.MIN_VALUE;
+    private int[] mKeys;
+    private float[] mValues;
+    int mSize;
+
+    public IntFloatMap() {
+        mKeys = new int[DEFAULT_CAPACITY];
+        Arrays.fill(mKeys, NOT_PRESENT);
+        mValues = new float[DEFAULT_CAPACITY];
+    }
+
+    /**
+     * clear the map
+     */
+    public void clear() {
+        Arrays.fill(mKeys, NOT_PRESENT);
+        Arrays.fill(mValues, Float.NaN); // not strictly necessary but defensive
+        mSize = 0;
+    }
+
+    /**
+     * is the key contained in map
+     *
+     * @param key the key to check
+     * @return true if the map contains the key
+     */
+    public boolean contains(int key) {
+        return findKey(key) != -1;
+    }
+
+    /**
+     * Put a item in the map
+     *
+     * @param key   item'values key
+     * @param value item's value
+     * @return old value if exist
+     */
+    public float put(int key, float value) {
+        if (key == NOT_PRESENT) {
+            throw new IllegalArgumentException("Key cannot be NOT_PRESENT");
+        }
+        if (mSize > mKeys.length * LOAD_FACTOR) {
+            resize();
+        }
+        return insert(key, value);
+    }
+
+    /**
+     * get an element given the key
+     *
+     * @param key the key to fetch
+     * @return the value
+     */
+    public float get(int key) {
+        int index = findKey(key);
+        if (index == -1) {
+            return 0;
+        } else
+            return mValues[index];
+    }
+
+    /**
+     * how many elements in the map
+     *
+     * @return number of elements
+     */
+    public int size() {
+        return mSize;
+    }
+
+    private float insert(int key, float value) {
+        int index = hash(key) % mKeys.length;
+        while (mKeys[index] != NOT_PRESENT && mKeys[index] != key) {
+            index = (index + 1) % mKeys.length;
+        }
+        float oldValue = 0;
+        if (mKeys[index] == NOT_PRESENT) {
+            mSize++;
+        } else {
+            oldValue = mValues[index];
+        }
+        mKeys[index] = key;
+        mValues[index] = value;
+        return oldValue;
+    }
+
+    private int findKey(int key) {
+        int index = hash(key) % mKeys.length;
+        while (mKeys[index] != NOT_PRESENT) {
+            if (mKeys[index] == key) {
+                return index;
+            }
+            index = (index + 1) % mKeys.length;
+        }
+        return -1;
+    }
+
+    private int hash(int key) {
+        return key;
+    }
+
+    private void resize() {
+        int[] oldKeys = mKeys;
+        float[] oldValues = mValues;
+        mKeys = new int[(oldKeys.length * 2)];
+        for (int i = 0; i < mKeys.length; i++) {
+            mKeys[i] = NOT_PRESENT;
+        }
+        mValues = new float[oldKeys.length * 2];
+        mSize = 0;
+        for (int i = 0; i < oldKeys.length; i++) {
+            if (oldKeys[i] != NOT_PRESENT) {
+                put(oldKeys[i], oldValues[i]);
+            }
+        }
+    }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntIntMap.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntIntMap.java
new file mode 100644
index 0000000..221014c
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntIntMap.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations.utilities;
+
+import java.util.Arrays;
+
+public class IntIntMap {
+    private static final int DEFAULT_CAPACITY = 16;
+    private static final float LOAD_FACTOR = 0.75f;
+    private static final int NOT_PRESENT = Integer.MIN_VALUE;
+    private int[] mKeys;
+    private int[] mValues;
+    int mSize;
+
+    public IntIntMap() {
+        mKeys = new int[DEFAULT_CAPACITY];
+        Arrays.fill(mKeys, NOT_PRESENT);
+        mValues = new int[DEFAULT_CAPACITY];
+    }
+
+    /**
+     * clear the map
+     */
+    public void clear() {
+        Arrays.fill(mKeys, NOT_PRESENT);
+        Arrays.fill(mValues, 0);
+        mSize = 0;
+    }
+
+    /**
+     * is the key contained in map
+     *
+     * @param key the key to check
+     * @return true if the map contains the key
+     */
+    public boolean contains(int key) {
+        return findKey(key) != -1;
+    }
+
+    /**
+     * Put a item in the map
+     *
+     * @param key   item'values key
+     * @param value item's value
+     * @return old value if exist
+     */
+    public int put(int key, int value) {
+        if (key == NOT_PRESENT) {
+            throw new IllegalArgumentException("Key cannot be NOT_PRESENT");
+        }
+        if (mSize > mKeys.length * LOAD_FACTOR) {
+            resize();
+        }
+        return insert(key, value);
+    }
+
+    /**
+     * get an element given the key
+     *
+     * @param key the key to fetch
+     * @return the value
+     */
+    public int get(int key) {
+        int index = findKey(key);
+        if (index == -1) {
+            return 0;
+        } else
+            return mValues[index];
+    }
+
+    /**
+     * how many elements in the map
+     *
+     * @return number of elements
+     */
+    public int size() {
+        return mSize;
+    }
+
+    private int insert(int key, int value) {
+        int index = hash(key) % mKeys.length;
+        while (mKeys[index] != NOT_PRESENT && mKeys[index] != key) {
+            index = (index + 1) % mKeys.length;
+        }
+        int oldValue = 0;
+        if (mKeys[index] == NOT_PRESENT) {
+            mSize++;
+        } else {
+            oldValue = mValues[index];
+        }
+        mKeys[index] = key;
+        mValues[index] = value;
+        return oldValue;
+    }
+
+    private int findKey(int key) {
+        int index = hash(key) % mKeys.length;
+        while (mKeys[index] != NOT_PRESENT) {
+            if (mKeys[index] == key) {
+                return index;
+            }
+            index = (index + 1) % mKeys.length;
+        }
+        return -1;
+    }
+
+    private int hash(int key) {
+        return key;
+    }
+
+    private void resize() {
+        int[] oldKeys = mKeys;
+        int[] oldValues = mValues;
+        mKeys = new int[(oldKeys.length * 2)];
+        for (int i = 0; i < mKeys.length; i++) {
+            mKeys[i] = NOT_PRESENT;
+        }
+        mValues = new int[oldKeys.length * 2];
+        mSize = 0;
+        for (int i = 0; i < oldKeys.length; i++) {
+            if (oldKeys[i] != NOT_PRESENT) {
+                put(oldKeys[i], oldValues[i]);
+            }
+        }
+    }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntegerExpressionEvaluator.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntegerExpressionEvaluator.java
new file mode 100644
index 0000000..4c1389c
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntegerExpressionEvaluator.java
@@ -0,0 +1,429 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations.utilities;
+
+/**
+ * High performance Integer expression evaluator
+ */
+public class IntegerExpressionEvaluator {
+    static IntMap<String> sNames = new IntMap<>();
+    public static final int OFFSET = 0x10000;
+    // add, sub, mul,div,mod,min,max, shl, shr, ushr, OR, AND , XOR, COPY_SIGN
+    public static final int I_ADD = OFFSET + 1;
+    public static final int I_SUB = OFFSET + 2;
+    public static final int I_MUL = OFFSET + 3;
+    public static final int I_DIV = OFFSET + 4;
+    public static final int I_MOD = OFFSET + 5;
+    public static final int I_SHL = OFFSET + 6;
+    public static final int I_SHR = OFFSET + 7;
+    public static final int I_USHR = OFFSET + 8;
+    public static final int I_OR = OFFSET + 9;
+    public static final int I_AND = OFFSET + 10;
+    public static final int I_XOR = OFFSET + 11;
+    public static final int I_COPY_SIGN = OFFSET + 12;
+    public static final int I_MIN = OFFSET + 13;
+    public static final int I_MAX = OFFSET + 14;
+
+    public static final int I_NEG = OFFSET + 15;
+    public static final int I_ABS = OFFSET + 16;
+    public static final int I_INCR = OFFSET + 17;
+    public static final int I_DECR = OFFSET + 18;
+    public static final int I_NOT = OFFSET + 19;
+    public static final int I_SIGN = OFFSET + 20;
+
+    public static final int I_CLAMP = OFFSET + 21;
+    public static final int I_IFELSE = OFFSET + 22;
+    public static final int I_MAD = OFFSET + 23;
+
+    public static final float LAST_OP = 24;
+
+    public static final int I_VAR1 = OFFSET + 24;
+    public static final int I_VAR2 = OFFSET + 24;
+
+
+    int[] mStack;
+    int[] mLocalStack = new int[128];
+    int[] mVar;
+
+
+    interface Op {
+        int eval(int sp);
+    }
+
+    /**
+     * Evaluate a float expression
+     *
+     * @param exp
+     * @param var
+     * @return
+     */
+    public int eval(int mask, int[] exp, int... var) {
+        mStack = exp;
+        mVar = var;
+        int sp = -1;
+        for (int i = 0; i < mStack.length; i++) {
+            int v = mStack[i];
+            if (((1 << i) & mask) != 0) {
+                sp = mOps[v - OFFSET].eval(sp);
+            } else {
+                mStack[++sp] = v;
+            }
+        }
+        return mStack[sp];
+    }
+
+    /**
+     * Evaluate a int expression
+     *
+     * @param exp
+     * @param len
+     * @param var
+     * @return
+     */
+    public int eval(int mask, int[] exp, int len, int... var) {
+        System.arraycopy(exp, 0, mLocalStack, 0, len);
+        mStack = mLocalStack;
+        mVar = var;
+        int sp = -1;
+        for (int i = 0; i < len; i++) {
+            int v = mStack[i];
+            if (((1 << i) & mask) != 0) {
+                sp = mOps[v - OFFSET].eval(sp);
+            } else {
+                mStack[++sp] = v;
+            }
+        }
+        return mStack[sp];
+    }
+
+    /**
+     * Evaluate a int expression
+     *
+     * @param exp
+     * @param var
+     * @return
+     */
+    public int evalDB(int mask, int[] exp, int... var) {
+        mStack = exp;
+        mVar = var;
+        int sp = -1;
+        for (int i = 0; i < exp.length; i++) {
+            int v = mStack[i];
+            if (((1 << i) & mask) != 0) {
+                System.out.print(" " + sNames.get((v - OFFSET)));
+                sp = mOps[v - OFFSET].eval(sp);
+            } else {
+                System.out.print(" " + v);
+                mStack[++sp] = v;
+            }
+        }
+        return mStack[sp];
+    }
+
+    Op[] mOps = {
+            null,
+            (sp) -> { // ADD
+                mStack[sp - 1] = mStack[sp - 1] + mStack[sp];
+                return sp - 1;
+            },
+            (sp) -> { // SUB
+                mStack[sp - 1] = mStack[sp - 1] - mStack[sp];
+                return sp - 1;
+            },
+            (sp) -> { // MUL
+                mStack[sp - 1] = mStack[sp - 1] * mStack[sp];
+                return sp - 1;
+            },
+            (sp) -> {  // DIV
+                mStack[sp - 1] = mStack[sp - 1] / mStack[sp];
+                return sp - 1;
+            },
+            (sp) -> {  // MOD
+                mStack[sp - 1] = mStack[sp - 1] % mStack[sp];
+                return sp - 1;
+            },
+            (sp) -> { // SHL shift left
+                mStack[sp - 1] = mStack[sp - 1] << mStack[sp];
+                return sp - 1;
+            },
+            (sp) -> { // SHR shift right
+                mStack[sp - 1] = mStack[sp - 1] >> mStack[sp];
+                return sp - 1;
+            },
+            (sp) -> { // USHR unsigned shift right
+                mStack[sp - 1] = mStack[sp - 1] >>> mStack[sp];
+                return sp - 1;
+            },
+            (sp) -> { // OR operator
+                mStack[sp - 1] = mStack[sp - 1] | mStack[sp];
+                return sp - 1;
+            },
+            (sp) -> { // AND operator
+                mStack[sp - 1] = mStack[sp - 1] & mStack[sp];
+                return sp - 1;
+            },
+            (sp) -> { // XOR xor operator
+                mStack[sp - 1] = mStack[sp - 1] ^ mStack[sp];
+                return sp - 1;
+            },
+            (sp) -> { // COPY_SIGN copy the sing of (using bit magic)
+                mStack[sp - 1] = (mStack[sp - 1] ^ (mStack[sp] >> 31))
+                        - (mStack[sp] >> 31);
+                return sp - 1;
+            },
+            (sp) -> { // MIN
+                mStack[sp - 1] = Math.min(mStack[sp - 1], mStack[sp]);
+                return sp - 1;
+            },
+            (sp) -> { // MAX
+                mStack[sp - 1] = Math.max(mStack[sp - 1], mStack[sp]);
+                return sp - 1;
+            },
+            (sp) -> { // NEG
+                mStack[sp] = -mStack[sp];
+                return sp;
+            },
+            (sp) -> { // ABS
+                mStack[sp] = Math.abs(mStack[sp]);
+                return sp;
+            },
+            (sp) -> { // INCR increment
+                mStack[sp] = mStack[sp] + 1;
+                return sp;
+            },
+            (sp) -> { // DECR decrement
+                mStack[sp] = mStack[sp] - 1;
+                return sp;
+            },
+            (sp) -> { // NOT Bit invert
+                mStack[sp] = ~mStack[sp];
+                return sp;
+            },
+            (sp) -> { // SIGN x<0 = -1,x==0 =  0 , x>0 = 1
+                mStack[sp] = (mStack[sp] >> 31) | (-mStack[sp] >>> 31);
+                return sp;
+            },
+
+            (sp) -> { // CLAMP(min,max, val)
+                mStack[sp - 2] = Math.min(Math.max(mStack[sp - 2], mStack[sp]),
+                        mStack[sp - 1]);
+                return sp - 2;
+            },
+            (sp) -> { // Ternary conditional
+                mStack[sp - 2] = (mStack[sp] > 0)
+                        ? mStack[sp - 1] : mStack[sp - 2];
+                return sp - 2;
+            },
+            (sp) -> { // MAD
+                mStack[sp - 2] = mStack[sp] + mStack[sp - 1] * mStack[sp - 2];
+                return sp - 2;
+            },
+
+            (sp) -> { // first var =
+                mStack[sp] = mVar[0];
+                return sp;
+            },
+            (sp) -> { // second var y?
+                mStack[sp] = mVar[1];
+                return sp;
+            },
+            (sp) -> { // 3rd var z?
+                mStack[sp] = mVar[2];
+                return sp;
+            },
+    };
+
+    static {
+        int k = 0;
+        sNames.put(k++, "NOP");
+        sNames.put(k++, "+");
+        sNames.put(k++, "-");
+        sNames.put(k++, "*");
+        sNames.put(k++, "/");
+        sNames.put(k++, "%");
+        sNames.put(k++, "<<");
+        sNames.put(k++, ">>");
+        sNames.put(k++, ">>>");
+        sNames.put(k++, "|");
+        sNames.put(k++, "&");
+        sNames.put(k++, "^");
+        sNames.put(k++, "copySign");
+        sNames.put(k++, "min");
+        sNames.put(k++, "max");
+        sNames.put(k++, "neg");
+        sNames.put(k++, "abs");
+        sNames.put(k++, "incr");
+        sNames.put(k++, "decr");
+        sNames.put(k++, "not");
+        sNames.put(k++, "sign");
+        sNames.put(k++, "clamp");
+        sNames.put(k++, "ifElse");
+        sNames.put(k++, "mad");
+        sNames.put(k++, "ceil");
+        sNames.put(k++, "a[0]");
+        sNames.put(k++, "a[1]");
+        sNames.put(k++, "a[2]");
+    }
+
+    /**
+     * given a int command return its math name (e.g sin, cos etc.)
+     *
+     * @param f
+     * @return
+     */
+    public static String toMathName(int f) {
+        int id = f - OFFSET;
+        return sNames.get(id);
+    }
+
+    /**
+     * Convert an expression encoded as an array of ints int ot a string
+     *
+     * @param exp
+     * @param labels
+     * @return
+     */
+    public static String toString(int mask, int[] exp, String[] labels) {
+        StringBuilder s = new StringBuilder();
+        for (int i = 0; i < exp.length; i++) {
+            int v = exp[i];
+
+            if (((1 << i) & mask) != 0) {
+                if (v < OFFSET) {
+                    s.append(toMathName(v));
+                } else {
+                    s.append("[");
+                    s.append(v);
+                    s.append("]");
+                }
+            } else {
+                if (labels[i] != null) {
+                    s.append(labels[i]);
+                }
+                s.append(v);
+            }
+            s.append(" ");
+        }
+        return s.toString();
+    }
+
+    /**
+     * Convert an expression encoded as an array of ints int ot a string
+     *
+     * @param mask bit mask of operators vs commands
+     * @param exp
+     * @return
+     */
+    public static String toString(int mask, int[] exp) {
+        StringBuilder s = new StringBuilder();
+        s.append(Integer.toBinaryString(mask));
+        s.append(" : ");
+        for (int i = 0; i < exp.length; i++) {
+            int v = exp[i];
+
+            if (((1 << i) & mask) != 0) {
+                if (v > OFFSET) {
+                    s.append(" ");
+                    s.append(toMathName(v));
+                    s.append(" ");
+
+                } else {
+                    s.append("[");
+                    s.append(v);
+                    s.append("]");
+                }
+            }
+            s.append(" " + v);
+        }
+        return s.toString();
+    }
+
+    /**
+     * This creates an infix string expression
+     * @param mask The bits that are operators
+     * @param exp the array of expressions
+     * @return infix string
+     */
+    public static String toStringInfix(int mask, int[] exp) {
+        return toString(mask, exp, exp.length - 1);
+    }
+
+    static String toString(int mask, int[] exp, int sp) {
+        String[] str = new String[exp.length];
+        if (((1 << sp) & mask) != 0) {
+            int id = exp[sp] - OFFSET;
+            switch (NO_OF_OPS[id]) {
+                case -1:
+                    return "nop";
+                case 1:
+                    return sNames.get(id) + "(" + toString(mask, exp, sp - 1) + ") ";
+                case 2:
+                    if (infix(id)) {
+                        return "(" + toString(mask, exp, sp - 2)
+                                + " " + sNames.get(id) + " "
+                                + toString(mask, exp, sp - 1) + ") ";
+                    } else {
+                        return sNames.get(id) + "("
+                                + toString(mask, exp, sp - 2) + ", "
+                                + toString(mask, exp, sp - 1) + ")";
+                    }
+                case 3:
+                    if (infix(id)) {
+                        return "((" + toString(mask, exp, sp + 3) + ") ? "
+                                + toString(mask, exp, sp - 2) + ":"
+                                + toString(mask, exp, sp - 1) + ")";
+                    } else {
+                        return sNames.get(id)
+                                + "(" + toString(mask, exp, sp - 3)
+                                + ", " + toString(mask, exp, sp - 2)
+                                + ", " + toString(mask, exp, sp - 1) + ")";
+                    }
+            }
+        }
+        return Integer.toString(exp[sp]);
+    }
+
+    static final int[] NO_OF_OPS = {
+            -1, // no op
+            2, 2, 2, 2, 2, // + - * / %
+            2, 2, 2, 2, 2, 2, 2, 2, 2, //<<, >> , >>> , | , &, ^, min max
+            1, 1, 1, 1, 1, 1,   // neg, abs, ++, -- , not , sign
+
+            3, 3, 3, // clamp, ifElse, mad,
+            0, 0, 0 // mad, ?:,
+            // a[0],a[1],a[2]
+    };
+
+    /**
+     * to be used by parser to determine if command is infix
+     *
+     * @param n the operator (minus the offset)
+     * @return true if the operator is infix
+     */
+    static boolean infix(int n) {
+        return ((n < 12));
+    }
+
+    /**
+     * is it an id or operation
+     * @param mask the bits that mark elements as an operation
+     * @param i the bit to check
+     * @return true if the bit is 1
+     */
+    public static boolean isOperation(int mask, int i) {
+        return ((1 << i) & mask) != 0;
+    }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/types/BooleanConstant.java b/core/java/com/android/internal/widget/remotecompose/core/types/BooleanConstant.java
new file mode 100644
index 0000000..1051192
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/types/BooleanConstant.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.types;
+
+import com.android.internal.widget.remotecompose.core.CompanionOperation;
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+
+import java.util.List;
+
+/**
+ * Used to represent a boolean
+ */
+public class BooleanConstant implements Operation {
+    boolean mValue = false;
+    private int mId;
+
+    public BooleanConstant(int id, boolean value) {
+        mId = id;
+        mValue = value;
+    }
+
+    @Override
+    public void write(WireBuffer buffer) {
+        COMPANION.apply(buffer, mId, mValue);
+    }
+
+    @Override
+    public void apply(RemoteContext context) {
+
+    }
+
+    @Override
+    public String deepToString(String indent) {
+        return toString();
+    }
+
+    @Override
+    public String toString() {
+        return "BooleanConstant[" + mId + "] = " + mValue + "";
+    }
+
+    public static final Companion COMPANION = new Companion();
+
+    public static class Companion implements CompanionOperation {
+        private Companion() {
+        }
+
+        @Override
+        public String name() {
+            return "OrigamiBoolean";
+        }
+
+        @Override
+        public int id() {
+            return Operations.DATA_BOOLEAN;
+        }
+
+        /**
+         * Writes out the operation to the buffer
+         *
+         * @param buffer
+         * @param id
+         * @param value
+         */
+        public void apply(WireBuffer buffer, int id, boolean value) {
+            buffer.start(Operations.DATA_BOOLEAN);
+            buffer.writeInt(id);
+            buffer.writeBoolean(value);
+        }
+
+        @Override
+        public void read(WireBuffer buffer, List<Operation> operations) {
+            int id = buffer.readInt();
+
+            boolean value = buffer.readBoolean();
+            operations.add(new BooleanConstant(id, value));
+        }
+    }
+
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/types/IntegerConstant.java b/core/java/com/android/internal/widget/remotecompose/core/types/IntegerConstant.java
new file mode 100644
index 0000000..ceb3236
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/types/IntegerConstant.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.types;
+
+import com.android.internal.widget.remotecompose.core.CompanionOperation;
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+
+import java.util.List;
+
+/**
+ * Represents a single integer typically used for states
+ * or named for input into the system
+ */
+public class IntegerConstant implements Operation {
+    private int mValue = 0;
+    private int mId;
+
+    IntegerConstant(int id, int value) {
+        mId = id;
+        mValue = value;
+    }
+
+    @Override
+    public void write(WireBuffer buffer) {
+        COMPANION.apply(buffer, mId, mValue);
+    }
+
+    @Override
+    public void apply(RemoteContext context) {
+        context.loadInteger(mId, mValue);
+    }
+
+    @Override
+    public String deepToString(String indent) {
+        return toString();
+    }
+
+    @Override
+    public String toString() {
+        return "IntegerConstant[" + mId + "] = " + mValue + "";
+    }
+
+    public static final Companion COMPANION = new Companion();
+
+    public static class Companion implements CompanionOperation {
+        private Companion() {
+        }
+
+        @Override
+        public String name() {
+            return "IntegerConstant";
+        }
+
+        @Override
+        public int id() {
+            return Operations.DATA_INT;
+        }
+
+        /**
+         * Writes out the operation to the buffer
+         *
+         * @param buffer
+         * @param textId
+         * @param value
+         */
+        public void apply(WireBuffer buffer, int textId, int value) {
+            buffer.start(Operations.DATA_INT);
+            buffer.writeInt(textId);
+            buffer.writeInt(value);
+        }
+
+        @Override
+        public void read(WireBuffer buffer, List<Operation> operations) {
+            int id = buffer.readInt();
+
+            int value = buffer.readInt();
+            operations.add(new IntegerConstant(id, value));
+        }
+    }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/player/RemoteComposePlayer.java b/core/java/com/android/internal/widget/remotecompose/player/RemoteComposePlayer.java
index 73e94fa..b2406bf 100644
--- a/core/java/com/android/internal/widget/remotecompose/player/RemoteComposePlayer.java
+++ b/core/java/com/android/internal/widget/remotecompose/player/RemoteComposePlayer.java
@@ -410,4 +410,3 @@
         }
     }
 }
-
diff --git a/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidPaintContext.java b/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidPaintContext.java
index ecb68bb..39a770a 100644
--- a/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidPaintContext.java
+++ b/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidPaintContext.java
@@ -39,12 +39,16 @@
 import com.android.internal.widget.remotecompose.core.operations.paint.PaintBundle;
 import com.android.internal.widget.remotecompose.core.operations.paint.PaintChanges;
 
+import java.util.ArrayList;
+import java.util.List;
+
 /**
  * An implementation of PaintContext for the Android Canvas.
  * This is used to play the RemoteCompose operations on Android.
  */
 public class AndroidPaintContext extends PaintContext {
     Paint mPaint = new Paint();
+    List<Paint> mPaintList = new ArrayList<>();
     Canvas mCanvas;
     Rect mTmpRect = new Rect(); // use in calculation of bounds
 
@@ -162,6 +166,16 @@
     }
 
     @Override
+    public void savePaint() {
+        mPaintList.add(new Paint(mPaint));
+    }
+
+    @Override
+    public void restorePaint() {
+        mPaint = mPaintList.remove(mPaintList.size() - 1);
+    }
+
+    @Override
     public void drawRoundRect(float left,
                               float top,
                               float right,
@@ -335,6 +349,11 @@
         return null;
     }
 
+    /**
+     * This applies paint changes to the current paint
+     *
+     * @param mPaintData the list change to the paint
+     */
     @Override
     public void applyPaint(PaintBundle mPaintData) {
         mPaintData.applyPaintChange((PaintContext) this, new PaintChanges() {
diff --git a/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidRemoteContext.java b/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidRemoteContext.java
index dd43bd5..5a87c70 100644
--- a/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidRemoteContext.java
+++ b/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidRemoteContext.java
@@ -120,6 +120,11 @@
         mRemoteComposeState.updateFloat(id, value);
     }
 
+    @Override
+    public void loadInteger(int id, int value) {
+        mRemoteComposeState.updateInteger(id, value);
+    }
+
 
     @Override
     public void loadColor(int id, int color) {
@@ -142,6 +147,11 @@
     }
 
     @Override
+    public int getInteger(int id) {
+        return  mRemoteComposeState.getInteger(id);
+    }
+
+    @Override
     public int getColor(int id) {
         return mRemoteComposeState.getColor(id);
     }
diff --git a/core/java/com/android/internal/widget/remotecompose/player/platform/FloatsToPath.java b/core/java/com/android/internal/widget/remotecompose/player/platform/FloatsToPath.java
index 2d766f8..7a85427 100644
--- a/core/java/com/android/internal/widget/remotecompose/player/platform/FloatsToPath.java
+++ b/core/java/com/android/internal/widget/remotecompose/player/platform/FloatsToPath.java
@@ -58,7 +58,7 @@
                 break;
                 case PathData.CONIC: {
                     i += 3;
-                    if (Build.VERSION.SDK_INT >= 34) {
+                    if (Build.VERSION.SDK_INT >= 34) { // REMOVE IN PLATFORM
                         path.conicTo(
                                 floatPath[i + 0], floatPath[i + 1],
                                 floatPath[i + 2], floatPath[i + 3],
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 011ef30..2abdd57 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -117,7 +117,6 @@
     ],
 
     static_libs: [
-        "libnativehelper_lazy",
         "libziparchive_for_incfs",
         "libguiflags",
     ],
@@ -259,6 +258,7 @@
                 "com_android_internal_content_om_OverlayManagerImpl.cpp",
                 "com_android_internal_net_NetworkUtilsInternal.cpp",
                 "com_android_internal_os_ClassLoaderFactory.cpp",
+                "com_android_internal_os_DebugStore.cpp",
                 "com_android_internal_os_FuseAppLoop.cpp",
                 "com_android_internal_os_KernelAllocationStats.cpp",
                 "com_android_internal_os_KernelCpuBpfTracking.cpp",
@@ -292,6 +292,7 @@
                 "libgif",
                 "libgui_window_info_static",
                 "libkernelconfigs",
+                "libnativehelper_lazy",
                 "libseccomp_policy",
                 "libgrallocusage",
                 "libscrypt_static",
@@ -315,6 +316,7 @@
                 "libcrypto",
                 "libcutils",
                 "libdebuggerd_client",
+                "libdebugstore_cxx",
                 "libutils",
                 "libbinder",
                 "libbinderdebug",
@@ -421,6 +423,7 @@
                 "libimage_type_recognition",
                 "libinput",
                 "libjpeg",
+                "libnativehelper_jvm",
                 "libpiex",
                 "libpng",
                 "libtiff_directory",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index ed59327..03b5143a 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -202,6 +202,7 @@
 extern int register_com_android_internal_content_om_OverlayManagerImpl(JNIEnv* env);
 extern int register_com_android_internal_net_NetworkUtilsInternal(JNIEnv* env);
 extern int register_com_android_internal_os_ClassLoaderFactory(JNIEnv* env);
+extern int register_com_android_internal_os_DebugStore(JNIEnv* env);
 extern int register_com_android_internal_os_FuseAppLoop(JNIEnv* env);
 extern int register_com_android_internal_os_KernelAllocationStats(JNIEnv* env);
 extern int register_com_android_internal_os_KernelCpuBpfTracking(JNIEnv* env);
@@ -1599,6 +1600,7 @@
         REG_JNI(register_com_android_internal_content_om_OverlayManagerImpl),
         REG_JNI(register_com_android_internal_net_NetworkUtilsInternal),
         REG_JNI(register_com_android_internal_os_ClassLoaderFactory),
+        REG_JNI(register_com_android_internal_os_DebugStore),
         REG_JNI(register_com_android_internal_os_LongArrayMultiStateCounter),
         REG_JNI(register_com_android_internal_os_LongMultiStateCounter),
         REG_JNI(register_com_android_internal_os_Zygote),
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index 7c62615..638591f 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -2292,7 +2292,7 @@
                                  criteria.mValue.mUsage);
                 jMixMatchCriterion = env->NewObject(gAudioMixMatchCriterionClass,
                                                     gAudioMixMatchCriterionAttrCstor,
-                                                    jMixMatchCriterion, criteria.mRule);
+                                                    jAudioAttributes, criteria.mRule);
                 break;
             case RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET:
                 jAudioAttributes = env->NewObject(gAudioAttributesClass, gAudioAttributesCstor);
@@ -2300,7 +2300,7 @@
                                  criteria.mValue.mSource);
                 jMixMatchCriterion = env->NewObject(gAudioMixMatchCriterionClass,
                                                     gAudioMixMatchCriterionAttrCstor,
-                                                    jMixMatchCriterion, criteria.mRule);
+                                                    jAudioAttributes, criteria.mRule);
                 break;
         }
         env->CallBooleanMethod(jAudioMixMatchCriterionList, gArrayListMethods.add,
diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp
index 809ec63..e5ac0e1 100644
--- a/core/jni/android_util_Process.cpp
+++ b/core/jni/android_util_Process.cpp
@@ -33,6 +33,7 @@
 
 #include <algorithm>
 #include <array>
+#include <cstring>
 #include <limits>
 #include <memory>
 #include <string>
@@ -50,7 +51,6 @@
 #include <inttypes.h>
 #include <pwd.h>
 #include <signal.h>
-#include <string.h>
 #include <sys/epoll.h>
 #include <sys/errno.h>
 #include <sys/pidfd.h>
@@ -73,13 +73,13 @@
 // readProcFile() are reading files under this threshold, e.g.,
 // /proc/pid/stat.  /proc/pid/time_in_state ends up being about 520
 // bytes, so use 1024 for the stack to provide a bit of slack.
-static constexpr ssize_t kProcReadStackBufferSize = 1024;
+static constexpr size_t kProcReadStackBufferSize = 1024;
 
 // The other files we read from proc tend to be a bit larger (e.g.,
 // /proc/stat is about 3kB), so once we exhaust the stack buffer,
 // retry with a relatively large heap-allocated buffer.  We double
 // this size and retry until the whole file fits.
-static constexpr ssize_t kProcReadMinHeapBufferSize = 4096;
+static constexpr size_t kProcReadMinHeapBufferSize = 4096;
 
 #if GUARD_THREAD_PRIORITY
 Mutex gKeyCreateMutex;
@@ -817,7 +817,6 @@
     }
 
     DIR* dirp = opendir(file8);
-
     env->ReleaseStringUTFChars(file, file8);
 
     if(dirp == NULL) {
@@ -850,6 +849,7 @@
             jintArray newArray = env->NewIntArray(newCount);
             if (newArray == NULL) {
                 closedir(dirp);
+                if (curData) env->ReleaseIntArrayElements(lastArray, curData, 0);
                 jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
                 return NULL;
             }
@@ -1046,78 +1046,71 @@
         return JNI_FALSE;
     }
 
-    const char* file8 = env->GetStringUTFChars(file, NULL);
-    if (file8 == NULL) {
+    auto releaser = [&](const char* jniStr) { env->ReleaseStringUTFChars(file, jniStr); };
+    std::unique_ptr<const char[], decltype(releaser)> file8(env->GetStringUTFChars(file, NULL),
+                                                            releaser);
+    if (!file8) {
         jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
         return JNI_FALSE;
     }
 
-    ::android::base::unique_fd fd(open(file8, O_RDONLY | O_CLOEXEC));
+    ::android::base::unique_fd fd(open(file8.get(), O_RDONLY | O_CLOEXEC));
     if (!fd.ok()) {
         if (kDebugProc) {
-            ALOGW("Unable to open process file: %s\n", file8);
+            ALOGW("Unable to open process file: %s\n", file8.get());
         }
-        env->ReleaseStringUTFChars(file, file8);
         return JNI_FALSE;
     }
-    env->ReleaseStringUTFChars(file, file8);
 
     // Most proc files we read are small, so we go through the loop
-    // with the stack buffer firstly. We allocate a buffer big
-    // enough for the whole file.
+    // with the stack buffer first. We allocate a buffer big enough
+    // for most files.
 
-    char readBufferStack[kProcReadStackBufferSize];
-    std::unique_ptr<char[]> readBufferHeap;
-    char* readBuffer = &readBufferStack[0];
-    ssize_t readBufferSize = kProcReadStackBufferSize;
-    ssize_t numberBytesRead;
+    char stackBuf[kProcReadStackBufferSize];
+    std::vector<char> heapBuf;
+    char* buf = stackBuf;
+
+    size_t remaining = sizeof(stackBuf);
     off_t offset = 0;
-    for (;;) {
-        ssize_t requestedBufferSize = readBufferSize - offset;
-        // By using pread, we can avoid an lseek to rewind the FD
-        // before retry, saving a system call.
-        numberBytesRead =
-                TEMP_FAILURE_RETRY(pread(fd, readBuffer + offset, requestedBufferSize, offset));
-        if (numberBytesRead < 0) {
+    ssize_t numBytesRead;
+
+    do {
+        numBytesRead = TEMP_FAILURE_RETRY(pread(fd, buf + offset, remaining, offset));
+        if (numBytesRead < 0) {
             if (kDebugProc) {
                 ALOGW("Unable to read process file err: %s file: %s fd=%d\n",
-                      strerror_r(errno, &readBufferStack[0], sizeof(readBufferStack)), file8,
-                      fd.get());
+                      strerror_r(errno, stackBuf, sizeof(stackBuf)), file8.get(), fd.get());
             }
             return JNI_FALSE;
         }
-        if (numberBytesRead == 0) {
-            // End of file.
-            numberBytesRead = offset;
-            break;
-        }
-        if (numberBytesRead < requestedBufferSize) {
-            // Read less bytes than requested, it's not an error per pread(2).
-            offset += numberBytesRead;
-        } else {
-            // Buffer is fully used, try to grow it.
-            if (readBufferSize > std::numeric_limits<ssize_t>::max() / 2) {
-                if (kDebugProc) {
-                    ALOGW("Proc file too big: %s fd=%d\n", file8, fd.get());
+
+        offset += numBytesRead;
+        remaining -= numBytesRead;
+
+        if (numBytesRead && !remaining) {
+            if (buf == stackBuf) {
+                heapBuf.resize(kProcReadMinHeapBufferSize);
+                static_assert(kProcReadMinHeapBufferSize > sizeof(stackBuf));
+                std::memcpy(heapBuf.data(), stackBuf, sizeof(stackBuf));
+            } else {
+                constexpr size_t MAX_READABLE_PROCFILE_SIZE = 64 << 20;
+                if (heapBuf.size() >= MAX_READABLE_PROCFILE_SIZE) {
+                    if (kDebugProc) {
+                        ALOGW("Proc file too big: %s fd=%d size=%zu\n",
+                              file8.get(), fd.get(), heapBuf.size());
+                    }
+                    return JNI_FALSE;
                 }
-                return JNI_FALSE;
+                heapBuf.resize(2 * heapBuf.size());
             }
-            readBufferSize = std::max(readBufferSize * 2, kProcReadMinHeapBufferSize);
-            readBufferHeap.reset(); // Free address space before getting more.
-            readBufferHeap = std::make_unique<char[]>(readBufferSize);
-            if (!readBufferHeap) {
-                jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
-                return JNI_FALSE;
-            }
-            readBuffer = readBufferHeap.get();
-            offset = 0;
+            buf = heapBuf.data();
+            remaining = heapBuf.size() - offset;
         }
-    }
+    } while (numBytesRead != 0);
 
     // parseProcLineArray below modifies the buffer while parsing!
     return android_os_Process_parseProcLineArray(
-        env, clazz, readBuffer, 0, numberBytesRead,
-        format, outStrings, outLongs, outFloats);
+        env, clazz, buf, 0, offset, format, outStrings, outLongs, outFloats);
 }
 
 void android_os_Process_setApplicationObject(JNIEnv* env, jobject clazz,
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 9ce7658..0f53164 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -22,6 +22,7 @@
 #include <android/graphics/properties.h>
 #include <android/graphics/region.h>
 #include <android/gui/BnWindowInfosReportedListener.h>
+#include <android/gui/EdgeExtensionParameters.h>
 #include <android/gui/JankData.h>
 #include <android/hardware/display/IDeviceProductInfoConstants.h>
 #include <android/os/IInputConstants.h>
@@ -799,6 +800,20 @@
     transaction->setStretchEffect(ctrl, stretch);
 }
 
+static void nativeSetEdgeExtensionEffect(JNIEnv* env, jclass clazz, jlong transactionObj,
+                                         jlong nativeObj, jboolean leftEdge, jboolean rightEdge,
+                                         jboolean topEdge, jboolean bottomEdge) {
+    auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
+    auto* const ctrl = reinterpret_cast<SurfaceControl*>(nativeObj);
+
+    auto effect = gui::EdgeExtensionParameters();
+    effect.extendLeft = leftEdge;
+    effect.extendRight = rightEdge;
+    effect.extendTop = topEdge;
+    effect.extendBottom = bottomEdge;
+    transaction->setEdgeExtensionEffect(ctrl, effect);
+}
+
 static void nativeSetFlags(JNIEnv* env, jclass clazz, jlong transactionObj,
         jlong nativeObject, jint flags, jint mask) {
     auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
@@ -2340,6 +2355,8 @@
             (void*)nativeSetBlurRegions },
     {"nativeSetStretchEffect", "(JJFFFFFFFFFF)V",
             (void*) nativeSetStretchEffect },
+    {"nativeSetEdgeExtensionEffect", "(JJZZZZ)V",
+            (void*) nativeSetEdgeExtensionEffect },
     {"nativeSetShadowRadius", "(JJF)V",
             (void*)nativeSetShadowRadius },
     {"nativeSetFrameRate", "(JJFII)V",
diff --git a/core/jni/com_android_internal_content_FileSystemUtils.cpp b/core/jni/com_android_internal_content_FileSystemUtils.cpp
index d426f12..6c72544 100644
--- a/core/jni/com_android_internal_content_FileSystemUtils.cpp
+++ b/core/jni/com_android_internal_content_FileSystemUtils.cpp
@@ -87,9 +87,10 @@
     IF_ALOGD() {
         ALOGD("Total number of LOAD segments %zu", programHeaders.size());
 
-        ALOGD("Size before punching holes st_blocks: %" PRIu64
-              ", st_blksize: %d, st_size: %" PRIu64 "",
-              beforePunch.st_blocks, beforePunch.st_blksize,
+        ALOGD("Size before punching holes st_blocks: %" PRIu64 ", st_blksize: %" PRIu64
+              ", st_size: %" PRIu64 "",
+              static_cast<uint64_t>(beforePunch.st_blocks),
+              static_cast<uint64_t>(beforePunch.st_blksize),
               static_cast<uint64_t>(beforePunch.st_size));
     }
 
@@ -193,9 +194,10 @@
             ALOGD("lstat64 failed for filePath %s, error:%d", filePath, errno);
             return false;
         }
-        ALOGD("Size after punching holes st_blocks: %" PRIu64 ", st_blksize: %d, st_size: %" PRIu64
-              "",
-              afterPunch.st_blocks, afterPunch.st_blksize,
+        ALOGD("Size after punching holes st_blocks: %" PRIu64 ", st_blksize: %" PRIu64
+              ", st_size: %" PRIu64 "",
+              static_cast<uint64_t>(afterPunch.st_blocks),
+              static_cast<uint64_t>(afterPunch.st_blksize),
               static_cast<uint64_t>(afterPunch.st_size));
     }
 
@@ -271,8 +273,9 @@
     uint64_t blockSize = beforePunch.st_blksize;
     IF_ALOGD() {
         ALOGD("Extra field length: %hu,  Size before punching holes st_blocks: %" PRIu64
-              ", st_blksize: %d, st_size: %" PRIu64 "",
-              extraFieldLen, beforePunch.st_blocks, beforePunch.st_blksize,
+              ", st_blksize: %" PRIu64 ", st_size: %" PRIu64 "",
+              extraFieldLen, static_cast<uint64_t>(beforePunch.st_blocks),
+              static_cast<uint64_t>(beforePunch.st_blksize),
               static_cast<uint64_t>(beforePunch.st_size));
     }
 
@@ -346,8 +349,9 @@
             return false;
         }
         ALOGD("punchHolesInApk:: Size after punching holes st_blocks: %" PRIu64
-              ", st_blksize: %d, st_size: %" PRIu64 "",
-              afterPunch.st_blocks, afterPunch.st_blksize,
+              ", st_blksize: %" PRIu64 ", st_size: %" PRIu64 "",
+              static_cast<uint64_t>(afterPunch.st_blocks),
+              static_cast<uint64_t>(afterPunch.st_blksize),
               static_cast<uint64_t>(afterPunch.st_size));
     }
     return true;
diff --git a/core/jni/com_android_internal_os_DebugStore.cpp b/core/jni/com_android_internal_os_DebugStore.cpp
new file mode 100644
index 0000000..874d6ea
--- /dev/null
+++ b/core/jni/com_android_internal_os_DebugStore.cpp
@@ -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.
+ */
+
+#include <debugstore/debugstore_cxx_bridge.rs.h>
+#include <log/log.h>
+#include <nativehelper/JNIHelp.h>
+#include <nativehelper/ScopedLocalRef.h>
+#include <nativehelper/ScopedUtfChars.h>
+
+#include <iterator>
+#include <sstream>
+#include <vector>
+
+#include "core_jni_helpers.h"
+
+namespace android {
+
+static struct {
+    jmethodID mGet;
+    jmethodID mSize;
+} gListClassInfo;
+
+static std::vector<std::string> list_to_vector(JNIEnv* env, jobject jList) {
+    std::vector<std::string> vec;
+    jint size = env->CallIntMethod(jList, gListClassInfo.mSize);
+    if (size % 2 != 0) {
+        std::ostringstream oss;
+
+        std::copy(vec.begin(), vec.end(), std::ostream_iterator<std::string>(oss, ", "));
+        ALOGW("DebugStore list size is odd: %d, elements: %s", size, oss.str().c_str());
+
+        return vec;
+    }
+
+    vec.reserve(size);
+
+    for (jint i = 0; i < size; i++) {
+        ScopedLocalRef<jstring> jEntry(env,
+                                       reinterpret_cast<jstring>(
+                                               env->CallObjectMethod(jList, gListClassInfo.mGet,
+                                                                     i)));
+        ScopedUtfChars cEntry(env, jEntry.get());
+        vec.emplace_back(cEntry.c_str());
+    }
+    return vec;
+}
+
+static void com_android_internal_os_DebugStore_endEvent(JNIEnv* env, jclass clazz, jlong eventId,
+                                                        jobject jAttributeList) {
+    auto attributes = list_to_vector(env, jAttributeList);
+    debugstore::debug_store_end(static_cast<uint64_t>(eventId), attributes);
+}
+
+static jlong com_android_internal_os_DebugStore_beginEvent(JNIEnv* env, jclass clazz,
+                                                           jstring jeventName,
+                                                           jobject jAttributeList) {
+    ScopedUtfChars eventName(env, jeventName);
+    auto attributes = list_to_vector(env, jAttributeList);
+    jlong eventId =
+            static_cast<jlong>(debugstore::debug_store_begin(eventName.c_str(), attributes));
+    return eventId;
+}
+
+static void com_android_internal_os_DebugStore_recordEvent(JNIEnv* env, jclass clazz,
+                                                           jstring jeventName,
+                                                           jobject jAttributeList) {
+    ScopedUtfChars eventName(env, jeventName);
+    auto attributes = list_to_vector(env, jAttributeList);
+    debugstore::debug_store_record(eventName.c_str(), attributes);
+}
+
+static const JNINativeMethod gDebugStoreMethods[] = {
+        /* name, signature, funcPtr */
+        {"beginEventNative", "(Ljava/lang/String;Ljava/util/List;)J",
+         (void*)com_android_internal_os_DebugStore_beginEvent},
+        {"endEventNative", "(JLjava/util/List;)V",
+         (void*)com_android_internal_os_DebugStore_endEvent},
+        {"recordEventNative", "(Ljava/lang/String;Ljava/util/List;)V",
+         (void*)com_android_internal_os_DebugStore_recordEvent},
+};
+
+int register_com_android_internal_os_DebugStore(JNIEnv* env) {
+    int res = RegisterMethodsOrDie(env, "com/android/internal/os/DebugStore", gDebugStoreMethods,
+                                   NELEM(gDebugStoreMethods));
+    jclass listClass = FindClassOrDie(env, "java/util/List");
+    gListClassInfo.mGet = GetMethodIDOrDie(env, listClass, "get", "(I)Ljava/lang/Object;");
+    gListClassInfo.mSize = GetMethodIDOrDie(env, listClass, "size", "()I");
+
+    return res;
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/core/proto/android/app/appexitinfo.proto b/core/proto/android/app/appexitinfo.proto
index e560a94..8ee7962 100644
--- a/core/proto/android/app/appexitinfo.proto
+++ b/core/proto/android/app/appexitinfo.proto
@@ -20,7 +20,7 @@
 package android.app;
 
 import "frameworks/base/core/proto/android/privacy.proto";
-import "frameworks/proto_logging/stats/enums/app/app_enums.proto";
+import "frameworks/proto_logging/stats/enums/app_shared/app_enums.proto";
 
 /**
  * An android.app.ApplicationExitInfo object.
diff --git a/core/proto/android/app/appstartinfo.proto b/core/proto/android/app/appstartinfo.proto
index c137533..8de5458 100644
--- a/core/proto/android/app/appstartinfo.proto
+++ b/core/proto/android/app/appstartinfo.proto
@@ -20,7 +20,7 @@
 package android.app;
 
 import "frameworks/base/core/proto/android/privacy.proto";
-import "frameworks/proto_logging/stats/enums/app/app_enums.proto";
+import "frameworks/proto_logging/stats/enums/app_shared/app_enums.proto";
 
 /**
  * An android.app.ApplicationStartInfo object.
diff --git a/core/proto/android/providers/settings/system.proto b/core/proto/android/providers/settings/system.proto
index e5ced25..e795e809 100644
--- a/core/proto/android/providers/settings/system.proto
+++ b/core/proto/android/providers/settings/system.proto
@@ -69,6 +69,7 @@
         // 0 = no, 1 = yes
         optional SettingProto window_orientation_listener_log = 3 [ (android.privacy).dest = DEST_AUTOMATIC ];
         optional SettingProto show_key_presses = 4 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto touchpad_visualizer = 5 [ (android.privacy).dest = DEST_AUTOMATIC ];
     }
     optional DevOptions developer_options = 7;
 
diff --git a/core/proto/android/server/activitymanagerservice.proto b/core/proto/android/server/activitymanagerservice.proto
index 90069f1..58f39a9 100644
--- a/core/proto/android/server/activitymanagerservice.proto
+++ b/core/proto/android/server/activitymanagerservice.proto
@@ -35,7 +35,7 @@
 import "frameworks/base/core/proto/android/server/windowmanagerservice.proto";
 import "frameworks/base/core/proto/android/util/common.proto";
 import "frameworks/base/core/proto/android/privacy.proto";
-import "frameworks/proto_logging/stats/enums/app/app_enums.proto";
+import "frameworks/proto_logging/stats/enums/app_shared/app_enums.proto";
 
 option java_multiple_files = true;
 
diff --git a/core/proto/android/server/powermanagerservice.proto b/core/proto/android/server/powermanagerservice.proto
index 593bbc6..8fd5d71 100644
--- a/core/proto/android/server/powermanagerservice.proto
+++ b/core/proto/android/server/powermanagerservice.proto
@@ -26,7 +26,7 @@
 import "frameworks/base/core/proto/android/providers/settings.proto";
 import "frameworks/base/core/proto/android/server/wirelesschargerdetector.proto";
 import "frameworks/base/core/proto/android/privacy.proto";
-import "frameworks/proto_logging/stats/enums/app/app_enums.proto";
+import "frameworks/proto_logging/stats/enums/app_shared/app_enums.proto";
 import "frameworks/proto_logging/stats/enums/os/enums.proto";
 import "frameworks/proto_logging/stats/enums/view/enums.proto";
 
diff --git a/core/proto/android/widget/remoteviews.proto b/core/proto/android/widget/remoteviews.proto
index f08ea1b..37d1c5b 100644
--- a/core/proto/android/widget/remoteviews.proto
+++ b/core/proto/android/widget/remoteviews.proto
@@ -21,6 +21,7 @@
 package android.widget;
 
 import "frameworks/base/core/proto/android/privacy.proto";
+import "frameworks/base/core/proto/android/content/res/color_state_list.proto";
 
 /**
  * An android.widget.RemoteViews object. This is used by RemoteViews.createPreviewFromProto
@@ -71,6 +72,23 @@
         optional int32 view_type_count = 4;
         optional bool attached = 5;
     }
+
+    /**
+     * An android.graphics.drawable Icon.
+     */
+    message Icon {
+        option (android.msg_privacy).dest = DEST_AUTOMATIC;
+        optional int32 blend_mode = 1;
+        optional android.content.res.ColorStateListProto tint_list = 2;
+        oneof icon {
+            bytes bitmap = 3;
+            string resource = 4;
+            bytes data = 5;
+            string uri = 6;
+            string uri_adaptive_bitmap = 7;
+            bytes adaptive_bitmap = 8;
+        };
+    }
 }
 
 
diff --git a/core/res/Android.bp b/core/res/Android.bp
index a44e92c..9207aa8 100644
--- a/core/res/Android.bp
+++ b/core/res/Android.bp
@@ -163,6 +163,7 @@
         "android.net.platform.flags-aconfig",
         "com.android.window.flags.window-aconfig",
         "android.permission.flags-aconfig",
+        "android.os.flags-aconfig",
     ],
 }
 
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 9f00d5e..a00cc8b 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2611,6 +2611,14 @@
     <permission android:name="android.permission.VIBRATE_SYSTEM_CONSTANTS"
         android:protectionLevel="signature" />
 
+    <!-- @SystemApi Allows access to perform vendor effects in the vibrator.
+         <p>Protection level: signature
+         @FlaggedApi("android.os.vibrator.vendor_vibration_effects")
+         @hide
+    -->
+    <permission android:name="android.permission.VIBRATE_VENDOR_EFFECTS"
+        android:protectionLevel="signature|privileged" />
+
     <!-- @SystemApi Allows access to the vibrator state.
          <p>Protection level: signature
          @hide
@@ -6099,9 +6107,8 @@
                 android:description="@string/permdesc_deliverCompanionMessages"
                 android:protectionLevel="normal" />
 
-    <!-- @hide @FlaggedApi("android.companion.flags.companion_transport_apis")
-         Allows an application to send and receive messages via CDM transports.
-    -->
+    <!-- Allows an application to send and receive messages via CDM transports.
+         @hide -->
     <permission android:name="android.permission.USE_COMPANION_TRANSPORTS"
         android:protectionLevel="signature" />
 
@@ -8243,7 +8250,8 @@
          <p>Not for use by third-party applications. -->
     <permission
         android:name="android.permission.CAPTURE_CONSENTLESS_BUGREPORT_DELEGATED_CONSENT"
-        android:protectionLevel="signature|privileged" />
+        android:protectionLevel="signature|privileged"
+        android:featureFlag="android.os.allow_consentless_bugreport_delegated_consent" />
 
     <!-- @SystemApi Allows to call APIs that log process lifecycle events
          @hide -->
diff --git a/core/res/TEST_MAPPING b/core/res/TEST_MAPPING
index 4d09076..0e01a2a 100644
--- a/core/res/TEST_MAPPING
+++ b/core/res/TEST_MAPPING
@@ -11,5 +11,22 @@
                 }
             ]
         }
+    ],
+    // v2/sysui/suite/test-mapping-sysui-screenshot-test
+    "sysui-screenshot-test": [
+        {
+        "name": "SystemUIGoogleScreenshotTests",
+        "options": [
+            {
+            "exclude-annotation": "org.junit.Ignore"
+            },
+            {
+            "exclude-annotation": "androidx.test.filters.FlakyTest"
+            },
+            {
+            "exclude-annotation": "android.platform.test.annotations.Postsubmit"
+            }
+        ]
+        }
     ]
-}
\ No newline at end of file
+}
diff --git a/core/res/res/drawable/ic_call_answer_video.xml b/core/res/res/drawable/ic_call_answer_video.xml
index 77c8892..79af247 100644
--- a/core/res/res/drawable/ic_call_answer_video.xml
+++ b/core/res/res/drawable/ic_call_answer_video.xml
@@ -16,8 +16,8 @@
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
     android:width="24dp"
     android:height="24dp"
-    android:viewportWidth="20"
-    android:viewportHeight="20"
+    android:viewportWidth="24"
+    android:viewportHeight="24"
     android:tint="?android:attr/colorControlNormal"
     android:autoMirrored="true">
     <path
diff --git a/core/res/res/drawable/ic_zen_mode_icon_ball_sports.xml b/core/res/res/drawable/ic_zen_mode_icon_ball_sports.xml
new file mode 100644
index 0000000..fdf0bcf
--- /dev/null
+++ b/core/res/res/drawable/ic_zen_mode_icon_ball_sports.xml
@@ -0,0 +1,25 @@
+<!--
+Copyright (C) 2024 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:tint="?android:attr/colorControlNormal"
+    android:viewportHeight="960"
+    android:viewportWidth="960">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M368,956L298,916L418,708L350,668L290,772L220,732L426,376Q388,337 369,287Q350,237 350,184Q350,148 359,112.5Q368,77 388,44L388,44L456,84L456,84Q442,107 436,131.5Q430,156 430,182Q430,235 456,281.5Q482,328 530,356L620,408Q682,444 711,511.5Q740,579 740,638Q740,676 730,712Q720,748 702,780L702,780L632,740L632,740Q646,716 652,691Q658,666 658,640Q658,608 649,578Q640,548 620,522L368,956ZM640,360Q607,360 583.5,336.5Q560,313 560,280Q560,247 583.5,223.5Q607,200 640,200Q673,200 696.5,223.5Q720,247 720,280Q720,313 696.5,336.5Q673,360 640,360ZM540,160Q514,160 497,142Q480,124 480,100Q480,74 498,57Q516,40 540,40Q566,40 583,58Q600,76 600,100Q600,126 582,143Q564,160 540,160Z" />
+</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/ic_zen_mode_icon_beach.xml b/core/res/res/drawable/ic_zen_mode_icon_beach.xml
new file mode 100644
index 0000000..a42d9fd
--- /dev/null
+++ b/core/res/res/drawable/ic_zen_mode_icon_beach.xml
@@ -0,0 +1,25 @@
+<!--
+Copyright (C) 2024 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:tint="?android:attr/colorControlNormal"
+    android:viewportHeight="960"
+    android:viewportWidth="960">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M784,840L530,586L586,530L840,784L784,840ZM238,812Q178,752 149,677Q120,602 120,524Q120,446 149,372Q178,298 238,238Q298,178 372.5,148.5Q447,119 525,119Q603,119 677.5,148.5Q752,178 812,238L238,812ZM246,690L300,636Q284,615 269.5,593Q255,571 243,549Q231,527 222,505Q213,483 206,462Q195,521 204.5,580Q214,639 246,690ZM358,580L580,356Q537,323 493.5,302.5Q450,282 412,274.5Q374,267 343.5,272Q313,277 296,294Q279,312 274,342.5Q269,373 276.5,411.5Q284,450 304.5,493Q325,536 358,580ZM636,300L692,246Q639,214 580,204Q521,194 462,206Q484,213 506,222Q528,231 550,242.5Q572,254 593.5,268.5Q615,283 636,300Z" />
+</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/ic_zen_mode_icon_camping.xml b/core/res/res/drawable/ic_zen_mode_icon_camping.xml
new file mode 100644
index 0000000..1797ebd
--- /dev/null
+++ b/core/res/res/drawable/ic_zen_mode_icon_camping.xml
@@ -0,0 +1,25 @@
+<!--
+Copyright (C) 2024 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:tint="?android:attr/colorControlNormal"
+    android:viewportHeight="960"
+    android:viewportWidth="960">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M80,880L80,694L430,222L360,128L424,80L480,155L536,80L600,128L530,222L880,694L880,880L80,880ZM480,289L160,720L160,800L280,800L480,520L680,800L800,800L800,720L480,289ZM378,800L582,800L480,658L378,800ZM480,520L680,800L680,800L680,800L480,520L280,800L280,800L280,800L480,520Z" />
+</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/ic_zen_mode_icon_gaming.xml b/core/res/res/drawable/ic_zen_mode_icon_gaming.xml
new file mode 100644
index 0000000..8bea9ae
--- /dev/null
+++ b/core/res/res/drawable/ic_zen_mode_icon_gaming.xml
@@ -0,0 +1,25 @@
+<!--
+Copyright (C) 2024 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:tint="?android:attr/colorControlNormal"
+    android:viewportHeight="960"
+    android:viewportWidth="960">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M182,760Q131,760 103,724.5Q75,689 82,638L124,338Q133,278 177.5,239Q222,200 282,200L678,200Q738,200 782.5,239Q827,278 836,338L878,638Q885,689 857,724.5Q829,760 778,760Q757,760 739,752.5Q721,745 706,730L616,640L344,640L254,730Q239,745 221,752.5Q203,760 182,760ZM198,674L312,560L648,560L762,674Q764,676 778,680Q789,680 795.5,673.5Q802,667 800,656L756,348Q752,319 730,299.5Q708,280 678,280L282,280Q252,280 230,299.5Q208,319 204,348L160,656Q158,667 164.5,673.5Q171,680 182,680Q184,680 198,674ZM680,520Q697,520 708.5,508.5Q720,497 720,480Q720,463 708.5,451.5Q697,440 680,440Q663,440 651.5,451.5Q640,463 640,480Q640,497 651.5,508.5Q663,520 680,520ZM600,400Q617,400 628.5,388.5Q640,377 640,360Q640,343 628.5,331.5Q617,320 600,320Q583,320 571.5,331.5Q560,343 560,360Q560,377 571.5,388.5Q583,400 600,400ZM310,520L370,520L370,450L440,450L440,390L370,390L370,320L310,320L310,390L240,390L240,450L310,450L310,520ZM480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480L480,480Q480,480 480,480Q480,480 480,480L480,480Q480,480 480,480Q480,480 480,480L480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480L480,480L480,480Z" />
+</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/ic_zen_mode_icon_golf.xml b/core/res/res/drawable/ic_zen_mode_icon_golf.xml
new file mode 100644
index 0000000..d899c5b
--- /dev/null
+++ b/core/res/res/drawable/ic_zen_mode_icon_golf.xml
@@ -0,0 +1,25 @@
+<!--
+Copyright (C) 2024 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:tint="?android:attr/colorControlNormal"
+    android:viewportHeight="960"
+    android:viewportWidth="960">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M780,840Q755,840 737.5,822.5Q720,805 720,780Q720,755 737.5,737.5Q755,720 780,720Q805,720 822.5,737.5Q840,755 840,780Q840,805 822.5,822.5Q805,840 780,840ZM400,880Q300,880 230,856.5Q160,833 160,800Q160,777 193,759Q226,741 280,730L280,800L360,800L360,80L680,236L440,360L440,722Q526,727 583,748.5Q640,770 640,800Q640,833 570,856.5Q500,880 400,880Z" />
+</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/ic_zen_mode_icon_gym.xml b/core/res/res/drawable/ic_zen_mode_icon_gym.xml
new file mode 100644
index 0000000..585e564
--- /dev/null
+++ b/core/res/res/drawable/ic_zen_mode_icon_gym.xml
@@ -0,0 +1,25 @@
+<!--
+Copyright (C) 2024 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:tint="?android:attr/colorControlNormal"
+    android:viewportHeight="960"
+    android:viewportWidth="960">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M536,876L480,820L622,678L282,338L140,480L84,424L140,366L84,310L168,226L112,168L168,112L226,168L310,84L366,140L424,84L480,140L338,282L678,622L820,480L876,536L820,594L876,650L792,734L848,792L792,848L734,792L650,876L594,820L536,876Z" />
+</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/ic_zen_mode_icon_hiking.xml b/core/res/res/drawable/ic_zen_mode_icon_hiking.xml
new file mode 100644
index 0000000..d2b8c85
--- /dev/null
+++ b/core/res/res/drawable/ic_zen_mode_icon_hiking.xml
@@ -0,0 +1,25 @@
+<!--
+Copyright (C) 2024 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:tint="?android:attr/colorControlNormal"
+    android:viewportHeight="960"
+    android:viewportWidth="960">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M280,920L403,298Q409,269 430,254.5Q451,240 474,240Q497,240 516.5,250Q536,260 548,280L588,344Q606,373 634.5,396.5Q663,420 700,431L700,360L760,360L760,920L700,920L700,514Q652,503 611,479Q570,455 540,420L516,540L600,620L600,920L520,920L520,680L436,600L364,920L280,920ZM297,525L212,509Q196,506 187,492.5Q178,479 181,462L211,305Q217,273 245,254.5Q273,236 305,242L351,251L297,525ZM540,220Q507,220 483.5,196.5Q460,173 460,140Q460,107 483.5,83.5Q507,60 540,60Q573,60 596.5,83.5Q620,107 620,140Q620,173 596.5,196.5Q573,220 540,220Z" />
+</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/ic_zen_mode_icon_martial_arts.xml b/core/res/res/drawable/ic_zen_mode_icon_martial_arts.xml
new file mode 100644
index 0000000..7cabd86
--- /dev/null
+++ b/core/res/res/drawable/ic_zen_mode_icon_martial_arts.xml
@@ -0,0 +1,25 @@
+<!--
+Copyright (C) 2024 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:tint="?android:attr/colorControlNormal"
+    android:viewportHeight="960"
+    android:viewportWidth="960">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M400,880L380,520L253,447L239,499L320,640L251,680L152,510L200,338L430,206L320,96L376,40L560,223L416,306L464,348L792,80L840,136L500,480L480,880L400,880ZM200,280Q167,280 143.5,256.5Q120,233 120,200Q120,167 143.5,143.5Q167,120 200,120Q233,120 256.5,143.5Q280,167 280,200Q280,233 256.5,256.5Q233,280 200,280Z" />
+</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/ic_zen_mode_icon_swimming.xml b/core/res/res/drawable/ic_zen_mode_icon_swimming.xml
new file mode 100644
index 0000000..465a02a
--- /dev/null
+++ b/core/res/res/drawable/ic_zen_mode_icon_swimming.xml
@@ -0,0 +1,25 @@
+<!--
+Copyright (C) 2024 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:tint="?android:attr/colorControlNormal"
+    android:viewportHeight="960"
+    android:viewportWidth="960">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M80,840L80,760Q118,760 137,740Q156,720 212,720Q268,720 289,740Q310,760 346,760Q382,760 403,740Q424,720 480,720Q536,720 557,740Q578,760 614,760Q650,760 671,740Q692,720 748,720Q804,720 823,740Q842,760 880,760L880,840Q821,840 802.5,820Q784,800 748,800Q712,800 691,820Q670,840 614,840Q558,840 537,820Q516,800 480,800Q444,800 423,820Q402,840 346,840Q290,840 269,820Q248,800 212,800Q176,800 157.5,820Q139,840 80,840ZM80,660L80,580Q118,580 137,560Q156,540 212,540Q268,540 289.5,560Q311,580 346,580Q382,580 403,560Q424,540 480,540Q536,540 557,560Q578,580 614,580Q650,580 671,560Q692,540 748,540Q804,540 823,560Q842,580 880,580L880,660Q821,660 802.5,640Q784,620 748,620Q712,620 692.5,640Q673,660 614,660Q557,660 536.5,640Q516,620 480,620Q442,620 423.5,640Q405,660 346,660Q287,660 267.5,640Q248,620 212,620Q176,620 157.5,640Q139,660 80,660ZM276,456L409,323L369,283Q336,250 299,235Q262,220 208,220L208,120Q283,120 332,136.5Q381,153 428,200L684,456Q667,467 651,473.5Q635,480 614,480Q578,480 557,460Q536,440 480,440Q424,440 403,460Q382,480 346,480Q325,480 309,473.5Q293,467 276,456ZM668,120Q710,120 739,149.5Q768,179 768,220Q768,262 739,291Q710,320 668,320Q626,320 597,291Q568,262 568,220Q568,179 597,149.5Q626,120 668,120Z" />
+</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/ic_zen_mode_icon_work.xml b/core/res/res/drawable/ic_zen_mode_icon_work.xml
new file mode 100644
index 0000000..7820458
--- /dev/null
+++ b/core/res/res/drawable/ic_zen_mode_icon_work.xml
@@ -0,0 +1,25 @@
+<!--
+Copyright (C) 2024 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:tint="?android:attr/colorControlNormal"
+    android:viewportHeight="960"
+    android:viewportWidth="960">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M160,840Q127,840 103.5,816.5Q80,793 80,760L80,320Q80,287 103.5,263.5Q127,240 160,240L320,240L320,160Q320,127 343.5,103.5Q367,80 400,80L560,80Q593,80 616.5,103.5Q640,127 640,160L640,240L800,240Q833,240 856.5,263.5Q880,287 880,320L880,760Q880,793 856.5,816.5Q833,840 800,840L160,840ZM160,760L800,760Q800,760 800,760Q800,760 800,760L800,320Q800,320 800,320Q800,320 800,320L160,320Q160,320 160,320Q160,320 160,320L160,760Q160,760 160,760Q160,760 160,760ZM400,240L560,240L560,160Q560,160 560,160Q560,160 560,160L400,160Q400,160 400,160Q400,160 400,160L400,240ZM160,760Q160,760 160,760Q160,760 160,760L160,320Q160,320 160,320Q160,320 160,320L160,320Q160,320 160,320Q160,320 160,320L160,760Q160,760 160,760Q160,760 160,760Z" />
+</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/ic_zen_mode_icon_workshop.xml b/core/res/res/drawable/ic_zen_mode_icon_workshop.xml
new file mode 100644
index 0000000..844c7b7
--- /dev/null
+++ b/core/res/res/drawable/ic_zen_mode_icon_workshop.xml
@@ -0,0 +1,25 @@
+<!--
+Copyright (C) 2024 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:tint="?android:attr/colorControlNormal"
+    android:viewportHeight="960"
+    android:viewportWidth="960">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M756,840L537,621L621,537L840,756L756,840ZM204,840L120,756L396,480L328,412L300,440L249,389L249,471L221,499L100,378L128,350L210,350L160,300L302,158Q322,138 345,129Q368,120 392,120Q416,120 439,129Q462,138 482,158L390,250L440,300L412,328L480,396L570,306Q566,295 563.5,283Q561,271 561,259Q561,200 601.5,159.5Q642,119 701,119Q716,119 729.5,122Q743,125 757,131L658,230L730,302L829,203Q836,217 838.5,230.5Q841,244 841,259Q841,318 800.5,358.5Q760,399 701,399Q689,399 677,397Q665,395 654,390L204,840Z" />
+</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/ic_zen_mode_type_managed.xml b/core/res/res/drawable/ic_zen_mode_type_managed.xml
index 5e224eb..46fb435 100644
--- a/core/res/res/drawable/ic_zen_mode_type_managed.xml
+++ b/core/res/res/drawable/ic_zen_mode_type_managed.xml
@@ -21,5 +21,5 @@
     android:viewportWidth="960">
     <path
         android:fillColor="@android:color/white"
-        android:pathData="M234,684Q285,645 348,622.5Q411,600 480,600Q549,600 612,622.5Q675,645 726,684Q761,643 780.5,591Q800,539 800,480Q800,347 706.5,253.5Q613,160 480,160Q347,160 253.5,253.5Q160,347 160,480Q160,539 179.5,591Q199,643 234,684ZM480,520Q421,520 380.5,479.5Q340,439 340,380Q340,321 380.5,280.5Q421,240 480,240Q539,240 579.5,280.5Q620,321 620,380Q620,439 579.5,479.5Q539,520 480,520ZM480,880Q397,880 324,848.5Q251,817 197,763Q143,709 111.5,636Q80,563 80,480Q80,397 111.5,324Q143,251 197,197Q251,143 324,111.5Q397,80 480,80Q563,80 636,111.5Q709,143 763,197Q817,251 848.5,324Q880,397 880,480Q880,563 848.5,636Q817,709 763,763Q709,817 636,848.5Q563,880 480,880ZM480,800Q533,800 580,784.5Q627,769 666,740Q627,711 580,695.5Q533,680 480,680Q427,680 380,695.5Q333,711 294,740Q333,769 380,784.5Q427,800 480,800ZM480,440Q506,440 523,423Q540,406 540,380Q540,354 523,337Q506,320 480,320Q454,320 437,337Q420,354 420,380Q420,406 437,423Q454,440 480,440ZM480,380Q480,380 480,380Q480,380 480,380Q480,380 480,380Q480,380 480,380Q480,380 480,380Q480,380 480,380Q480,380 480,380Q480,380 480,380ZM480,740Q480,740 480,740Q480,740 480,740Q480,740 480,740Q480,740 480,740Q480,740 480,740Q480,740 480,740Q480,740 480,740Q480,740 480,740Z" />
+        android:pathData="M680,600Q638,600 609,571Q580,542 580,500Q580,458 609,429Q638,400 680,400Q722,400 751,429Q780,458 780,500Q780,542 751,571Q722,600 680,600ZM480,800L480,744Q480,720 492.5,699.5Q505,679 528,670Q564,655 602.5,647.5Q641,640 680,640Q719,640 757.5,647.5Q796,655 832,670Q855,679 867.5,699.5Q880,720 880,744L880,800L480,800ZM400,480Q334,480 287,433Q240,386 240,320Q240,254 287,207Q334,160 400,160Q466,160 513,207Q560,254 560,320Q560,386 513,433Q466,480 400,480ZM400,320Q400,320 400,320Q400,320 400,320Q400,320 400,320Q400,320 400,320Q400,320 400,320Q400,320 400,320Q400,320 400,320Q400,320 400,320ZM80,800L80,688Q80,654 97,625.5Q114,597 144,582Q204,552 268.5,536Q333,520 400,520Q435,520 470,526Q505,532 540,540Q523,557 506,574Q489,591 472,608Q454,603 436,601.5Q418,600 400,600Q342,600 286.5,614Q231,628 180,654Q170,659 165,668Q160,677 160,688L160,720L400,720L400,800L80,800ZM400,720Q400,720 400,720Q400,720 400,720Q400,720 400,720Q400,720 400,720L400,720L400,720Q400,720 400,720Q400,720 400,720Q400,720 400,720Q400,720 400,720ZM400,400Q433,400 456.5,376.5Q480,353 480,320Q480,287 456.5,263.5Q433,240 400,240Q367,240 343.5,263.5Q320,287 320,320Q320,353 343.5,376.5Q367,400 400,400Z" />
 </vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/tooltip_frame.xml b/core/res/res/drawable/tooltip_frame.xml
index 14130c8..e2618ca 100644
--- a/core/res/res/drawable/tooltip_frame.xml
+++ b/core/res/res/drawable/tooltip_frame.xml
@@ -17,5 +17,5 @@
 <shape xmlns:android="http://schemas.android.com/apk/res/android"
        android:shape="rectangle">
     <solid android:color="?attr/tooltipBackgroundColor" />
-    <corners android:radius="@dimen/tooltip_corner_radius" />
-</shape>
\ No newline at end of file
+    <corners android:radius="?attr/tooltipCornerRadius" />
+</shape>
diff --git a/core/res/res/layout/tooltip.xml b/core/res/res/layout/tooltip.xml
index 376c5eb..5b6799e 100644
--- a/core/res/res/layout/tooltip.xml
+++ b/core/res/res/layout/tooltip.xml
@@ -27,10 +27,10 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_margin="@dimen/tooltip_margin"
-        android:paddingStart="@dimen/tooltip_horizontal_padding"
-        android:paddingEnd="@dimen/tooltip_horizontal_padding"
-        android:paddingTop="@dimen/tooltip_vertical_padding"
-        android:paddingBottom="@dimen/tooltip_vertical_padding"
+        android:paddingStart="?attr/tooltipHorizontalPadding"
+        android:paddingEnd="?attr/tooltipHorizontalPadding"
+        android:paddingTop="?attr/tooltipVerticalPadding"
+        android:paddingBottom="?attr/tooltipVerticalPadding"
         android:maxWidth="256dp"
         android:background="?android:attr/tooltipFrameBackground"
         android:textAppearance="@style/TextAppearance.Tooltip"
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index 5b6de34..043f2b2 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -1411,10 +1411,8 @@
     <string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"Kies om draadlose ontfouting te deaktiveer."</string>
     <string name="test_harness_mode_notification_title" msgid="2282785860014142511">"Toetsraamwerkmodus is geaktiveer"</string>
     <string name="test_harness_mode_notification_message" msgid="3039123743127958420">"Voer \'n fabriekterugstelling uit om Toetsraamwerkmodus te deaktiveer."</string>
-    <!-- no translation found for wrong_hsum_configuration_notification_title (7212758829332714385) -->
-    <skip />
-    <!-- no translation found for wrong_hsum_configuration_notification_message (5353475441480684381) -->
-    <skip />
+    <string name="wrong_hsum_configuration_notification_title" msgid="7212758829332714385">"Verkeerde bou-opstelling van Stelselgebruiker sonder Koppelvlak (HSUM)"</string>
+    <string name="wrong_hsum_configuration_notification_message" msgid="5353475441480684381">"Die Stelselgebruiker sonder Koppelvlak-modustoestand van hierdie toestel verskil van sy bou-opstelling. Doen ’n fabriekterugstelling van die toestel."</string>
     <string name="console_running_notification_title" msgid="6087888939261635904">"Reekskonsole is geaktiveer"</string>
     <string name="console_running_notification_message" msgid="7892751888125174039">"Werkverrigting word beïnvloed. Gaan selflaaiprogram na om te deaktiveer."</string>
     <string name="mte_override_notification_title" msgid="4731115381962792944">"Eksperimentele MTE is geaktiveer"</string>
@@ -1430,6 +1428,7 @@
     <string name="share_remote_bugreport_action" msgid="7630880678785123682">"DEEL"</string>
     <string name="decline_remote_bugreport_action" msgid="4040894777519784346">"WEIER"</string>
     <string name="select_input_method" msgid="3971267998568587025">"Kies invoermetode"</string>
+    <string name="input_method_language_settings" msgid="8069089418056819437">"Taalinstellings"</string>
     <string name="show_ime" msgid="6406112007347443383">"Hou dit op die skerm terwyl fisieke sleutelbord aktief is"</string>
     <string name="hardware" msgid="3611039921284836033">"Gebruik skermsleutelbord"</string>
     <string name="select_keyboard_layout_notification_title" msgid="5823199895322205589">"Stel <xliff:g id="DEVICE_NAME">%s</xliff:g> op"</string>
@@ -1759,12 +1758,9 @@
     <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>
-    <!-- no translation found for accessibility_button_prompt_text (6105393217162198616) -->
-    <skip />
-    <!-- no translation found for accessibility_gesture_prompt_text (6452246951969541792) -->
-    <skip />
-    <!-- no translation found for accessibility_gesture_3finger_prompt_text (77745752309056152) -->
-    <skip />
+    <string name="accessibility_button_prompt_text" msgid="6105393217162198616">"Kies ’n kenmerk"</string>
+    <string name="accessibility_gesture_prompt_text" msgid="6452246951969541792">"Kies ’n kenmerk"</string>
+    <string name="accessibility_gesture_3finger_prompt_text" msgid="77745752309056152">"Kies ’n kenmerk"</string>
     <string name="accessibility_button_instructional_text" msgid="6831154884557881996">"Die kenmerk sal oopmaak wanneer jy weer op die toeganklikheidknoppie tik"</string>
     <string name="accessibility_gesture_instructional_text" msgid="4133877896011098550">"Die kenmerk sal oopmaak wanneer jy weer hierdie kortpad gebruik. Swiep vanaf die onderkant van jou skerm met 2 vingers op en los vinnig."</string>
     <string name="accessibility_gesture_3finger_instructional_text" msgid="1124458279366968154">"Die kenmerk sal oopmaak wanneer jy weer hierdie kortpad gebruik. Swiep vanaf die onderkant van jou skerm met 3 vingers op en los vinnig."</string>
@@ -1890,8 +1886,7 @@
     <string name="restr_pin_error_too_short" msgid="1547007808237941065">"PIN is te kort. Moet ten minste 4 syfers wees."</string>
     <string name="restr_pin_try_later" msgid="5897719962541636727">"Probeer later weer"</string>
     <string name="immersive_cling_title" msgid="2307034298721541791">"Bekyk tans volskerm"</string>
-    <!-- no translation found for immersive_cling_description (2896205051090870978) -->
-    <skip />
+    <string name="immersive_cling_description" msgid="2896205051090870978">"Swiep van die bokant van jou skerm af ondertoe om uit te gaan"</string>
     <string name="immersive_cling_positive" msgid="7047498036346489883">"Het dit"</string>
     <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Draai vir ’n beter aansig"</string>
     <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Maak <xliff:g id="NAME">%s</xliff:g> in volskerm oop vir ’n beter aansig"</string>
@@ -2443,22 +2438,13 @@
     <string name="bg_user_sound_notification_button_switch_user" msgid="3091969648572788946">"Wissel gebruiker"</string>
     <string name="bg_user_sound_notification_button_mute" msgid="4942158515665615243">"Demp"</string>
     <string name="bg_user_sound_notification_message" msgid="8613881975316976673">"Tik om klank te demp"</string>
-    <!-- no translation found for keyboard_shortcut_group_applications_browser (6535007304687100909) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_contacts (2750702518068326356) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_email (4229037666415353683) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_sms (3523799286376321137) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_music (2051507523525651067) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calendar (3571770335653387606) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calculator (6753209559716091507) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_maps (7950000659522589471) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications (3010389163951364798) -->
-    <skip />
+    <string name="keyboard_shortcut_group_applications_browser" msgid="6535007304687100909">"Blaaier"</string>
+    <string name="keyboard_shortcut_group_applications_contacts" msgid="2750702518068326356">"Kontakte"</string>
+    <string name="keyboard_shortcut_group_applications_email" msgid="4229037666415353683">"E-pos"</string>
+    <string name="keyboard_shortcut_group_applications_sms" msgid="3523799286376321137">"SMS"</string>
+    <string name="keyboard_shortcut_group_applications_music" msgid="2051507523525651067">"Musiek"</string>
+    <string name="keyboard_shortcut_group_applications_calendar" msgid="3571770335653387606">"Kalender"</string>
+    <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Sakrekenaar"</string>
+    <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Maps"</string>
+    <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Apps"</string>
 </resources>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index 7b40f30..c01cb69 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -312,7 +312,7 @@
     <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"በባትሪ እና ውሂብ አጠቃቀም ላይ ዝርዝሮችን ለማግኘት መታ ያድርጉ"</string>
     <string name="foreground_service_multiple_separator" msgid="5002287361849863168">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>፣ <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
     <string name="safeMode" msgid="8974401416068943888">"የሚያስተማምን ሁነታ"</string>
-    <string name="android_system_label" msgid="5974767339591067210">"Android ስርዓት"</string>
+    <string name="android_system_label" msgid="5974767339591067210">"Android ሥርዓት"</string>
     <string name="user_owner_label" msgid="8628726904184471211">"ወደ የግል መገለጫ ቀይር"</string>
     <string name="managed_profile_label" msgid="7316778766973512382">"ወደ የስራ መገለጫ ቀይር"</string>
     <string name="user_owner_app_label" msgid="1553595155465750298">"ወደ የግል <xliff:g id="APP_NAME">%1$s</xliff:g> ቀይር"</string>
@@ -1428,6 +1428,7 @@
     <string name="share_remote_bugreport_action" msgid="7630880678785123682">"አጋራ"</string>
     <string name="decline_remote_bugreport_action" msgid="4040894777519784346">"አትቀበል"</string>
     <string name="select_input_method" msgid="3971267998568587025">"የግቤት ስልት ምረጥ"</string>
+    <string name="input_method_language_settings" msgid="8069089418056819437">"የቋንቋ ቅንብሮች"</string>
     <string name="show_ime" msgid="6406112007347443383">"አካላዊ የቁልፍ ሰሌዳ ገቢር ሆኖ ሳለ በማያ ገፅ ላይ አቆየው"</string>
     <string name="hardware" msgid="3611039921284836033">"የማያ ገጽ ላይ የቁልፍ ሰሌዳ ይጠቀሙ"</string>
     <string name="select_keyboard_layout_notification_title" msgid="5823199895322205589">"<xliff:g id="DEVICE_NAME">%s</xliff:g>ን ያዋቅሩ"</string>
@@ -2437,22 +2438,13 @@
     <string name="bg_user_sound_notification_button_switch_user" msgid="3091969648572788946">"ተጠቃሚ ቀይር"</string>
     <string name="bg_user_sound_notification_button_mute" msgid="4942158515665615243">"ድምፀ-ከል አድርግ"</string>
     <string name="bg_user_sound_notification_message" msgid="8613881975316976673">"ድምፀ-ከል አድርግ ለማድረግ መታ ያድርጉ"</string>
-    <!-- no translation found for keyboard_shortcut_group_applications_browser (6535007304687100909) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_contacts (2750702518068326356) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_email (4229037666415353683) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_sms (3523799286376321137) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_music (2051507523525651067) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calendar (3571770335653387606) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calculator (6753209559716091507) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_maps (7950000659522589471) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications (3010389163951364798) -->
-    <skip />
+    <string name="keyboard_shortcut_group_applications_browser" msgid="6535007304687100909">"አሳሽ"</string>
+    <string name="keyboard_shortcut_group_applications_contacts" msgid="2750702518068326356">"ዕውቂያዎች"</string>
+    <string name="keyboard_shortcut_group_applications_email" msgid="4229037666415353683">"ኢሜይል"</string>
+    <string name="keyboard_shortcut_group_applications_sms" msgid="3523799286376321137">"ኤስኤምኤስ"</string>
+    <string name="keyboard_shortcut_group_applications_music" msgid="2051507523525651067">"ሙዚቃ"</string>
+    <string name="keyboard_shortcut_group_applications_calendar" msgid="3571770335653387606">"ቀን መቁጠሪያ"</string>
+    <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"ሒሳብ ማስያ"</string>
+    <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"ካርታዎች"</string>
+    <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"መተግበሪያዎች"</string>
 </resources>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index e17e0b6..b353893 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -202,7 +202,7 @@
     <string name="ssl_ca_cert_noti_managed" msgid="217337232273211674">"بواسطة <xliff:g id="MANAGING_DOMAIN">%s</xliff:g>"</string>
     <string name="work_profile_deleted" msgid="5891181538182009328">"تم حذف ملف العمل."</string>
     <string name="work_profile_deleted_details" msgid="3773706828364418016">"تطبيق المشرف لملف العمل مفقود أو تالف لذا تم حذف ملف العمل والبيانات ذات الصلة. اتصل بالمشرف للحصول على المساعدة."</string>
-    <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"لم يعد ملفك الشخصي للعمل متاحًا على هذا الجهاز"</string>
+    <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"لم يعد ملف العمل الخاص بك متاحًا على هذا الجهاز"</string>
     <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"تم إجراء محاولات كثيرة جدًا لإدخال كلمة المرور"</string>
     <string name="device_ownership_relinquished" msgid="4080886992183195724">"تنازل المشرف عن الجهاز للاستخدام الشخصي"</string>
     <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"تمت إزالة المساحة الخاصّة"</string>
@@ -224,7 +224,7 @@
     <string name="factory_reset_message" msgid="2657049595153992213">"تعذّر استخدام تطبيق المشرف. سيتم محو بيانات جهازك الآن.\n\nإذا كانت لديك أسئلة، اتصل بمشرف مؤسستك."</string>
     <string name="printing_disabled_by" msgid="3517499806528864633">"تم إيقاف الطباعة بواسطة <xliff:g id="OWNER_APP">%s</xliff:g>."</string>
     <string name="personal_apps_suspension_title" msgid="7561416677884286600">"تفعيل ملف العمل"</string>
-    <string name="personal_apps_suspension_text" msgid="6115455688932935597">"تم حظر تطبيقاتك الشخصية إلى أن تفعِّل ملفك الشخصي للعمل."</string>
+    <string name="personal_apps_suspension_text" msgid="6115455688932935597">"تم حظر تطبيقاتك الشخصية إلى أن تفعِّل ملف العمل الخاص بك."</string>
     <string name="personal_apps_suspension_soon_text" msgid="8123898693479590">"سيتم حظر التطبيقات الشخصية في <xliff:g id="DATE">%1$s</xliff:g> في <xliff:g id="TIME">%2$s</xliff:g>. لا يسمح مشرف تكنولوجيا المعلومات في مؤسستك بإيقاف ملف العمل أكثر من <xliff:g id="NUMBER">%3$d</xliff:g> يوم."</string>
     <string name="personal_apps_suspended_turn_profile_on" msgid="2758012869627513689">"تفعيل"</string>
     <string name="work_profile_telephony_paused_title" msgid="7690804479291839519">"المكالمات والرسائل غير مفعّلة"</string>
@@ -701,12 +701,12 @@
     <string name="fingerprint_icon_content_description" msgid="4741068463175388817">"رمز بصمة الإصبع"</string>
     <string name="device_unlock_notification_name" msgid="2632928999862915709">"فتح قفل الجهاز"</string>
     <string name="alternative_unlock_setup_notification_title" msgid="6241508547901933544">"تجربة طريقة أخرى لفتح القفل"</string>
-    <string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"استخدِم ميزة \"فتح الجهاز بالتعرف على الوجه\" عندما لا يتم التعرف على بصمة إصبعك، مثلاً عندما تكون أصابعك مبتلة."</string>
+    <string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"استخدِم ميزة \"فتح الجهاز ببصمة الوجه\" عندما لا يتم التعرف على بصمة إصبعك، مثلاً عندما تكون أصابعك مبتلة."</string>
     <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"استخدِم ميزة \"فتح الجهاز ببصمة الإصبع\" عندما لا يتم التعرف على وجهك، مثلاً عند عدم وجود ضوء كافٍ."</string>
-    <string name="face_recalibrate_notification_name" msgid="7311163114750748686">"فتح الجهاز بالتعرف على الوجه"</string>
-    <string name="face_recalibrate_notification_title" msgid="2524791952735579082">"مشكلة متعلّقة بميزة \"فتح الجهاز بالتعرف على الوجه\""</string>
+    <string name="face_recalibrate_notification_name" msgid="7311163114750748686">"فتح الجهاز ببصمة الوجه"</string>
+    <string name="face_recalibrate_notification_title" msgid="2524791952735579082">"مشكلة متعلّقة بميزة \"فتح الجهاز ببصمة الوجه\""</string>
     <string name="face_recalibrate_notification_content" msgid="3064513770251355594">"انقر لحذف نموذج الوجه ثم أضِف نموذجًا لوجهك مرة أخرى."</string>
-    <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"‏لاستخدام ميزة \"فتح الجهاز بالتعرف على الوجه\"، عليك منح إذن "<b>"الوصول إلى الكاميرا"</b>" في الإعدادات &gt; الخصوصية."</string>
+    <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"‏لاستخدام ميزة \"فتح الجهاز ببصمة الوجه\"، عليك منح إذن "<b>"الوصول إلى الكاميرا"</b>" في الإعدادات &gt; الخصوصية."</string>
     <string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"فتح الجهاز ببصمة الإصبع"</string>
     <string name="fingerprint_recalibrate_notification_title" msgid="2406561052064558497">"لا يمكن استخدام مستشعر بصمات الإصبع"</string>
     <string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"يُرجى التواصل مع مقدِّم خدمات إصلاح."</string>
@@ -740,19 +740,19 @@
   <string-array name="face_acquired_vendor">
   </string-array>
     <string name="face_error_hw_not_available" msgid="5085202213036026288">"يتعذّر التحقُّق من الوجه. الجهاز غير مُتاح."</string>
-    <string name="face_error_timeout" msgid="2598544068593889762">"جرِّب \"فتح الجهاز بالتعرف على الوجه\" مرة أخرى."</string>
+    <string name="face_error_timeout" msgid="2598544068593889762">"جرِّب \"فتح الجهاز ببصمة الوجه\" مرة أخرى."</string>
     <string name="face_error_no_space" msgid="5649264057026021723">"يتعذَّر تخزين بيانات الوجه الجديد. احذف الوجه القديم أولاً."</string>
     <string name="face_error_canceled" msgid="2164434737103802131">"تمّ إلغاء عملية مصادقة الوجه."</string>
-    <string name="face_error_user_canceled" msgid="5766472033202928373">"ألغى المستخدم ميزة \"فتح الجهاز بالتعرف على الوجه\"."</string>
+    <string name="face_error_user_canceled" msgid="5766472033202928373">"ألغى المستخدم ميزة \"فتح الجهاز ببصمة الوجه\"."</string>
     <string name="face_error_lockout" msgid="7864408714994529437">"تمّ إجراء محاولات كثيرة. أعِد المحاولة لاحقًا."</string>
-    <string name="face_error_lockout_permanent" msgid="8533257333130473422">"أجريت محاولات كثيرة جدًا. ميزة \"فتح الجهاز بالتعرف على الوجه\" غير متاحة."</string>
+    <string name="face_error_lockout_permanent" msgid="8533257333130473422">"أجريت محاولات كثيرة جدًا. ميزة \"فتح الجهاز ببصمة الوجه\" غير متاحة."</string>
     <string name="face_error_lockout_screen_lock" msgid="5062609811636860928">"تم إجراء عدد كبير جدًا من المحاولات. أدخِل قفل الشاشة بدلاً من ذلك."</string>
     <string name="face_error_unable_to_process" msgid="5723292697366130070">"يتعذّر التحقق من الوجه. حاول مرة أخرى."</string>
-    <string name="face_error_not_enrolled" msgid="1134739108536328412">"لم يسبق لك إعداد ميزة \"فتح الجهاز بالتعرف على الوجه\"."</string>
-    <string name="face_error_hw_not_present" msgid="7940978724978763011">"ميزة \"فتح الجهاز بالتعرف على الوجه\" غير متوافقة على هذا الجهاز."</string>
+    <string name="face_error_not_enrolled" msgid="1134739108536328412">"لم يسبق لك إعداد ميزة \"فتح الجهاز ببصمة الوجه\"."</string>
+    <string name="face_error_hw_not_present" msgid="7940978724978763011">"ميزة \"فتح الجهاز ببصمة الوجه\" غير متوافقة على هذا الجهاز."</string>
     <string name="face_error_security_update_required" msgid="5076017208528750161">"تم إيقاف جهاز الاستشعار مؤقتًا."</string>
     <string name="face_name_template" msgid="3877037340223318119">"الوجه <xliff:g id="FACEID">%d</xliff:g>"</string>
-    <string name="face_app_setting_name" msgid="5854024256907828015">"فتح الجهاز بالتعرف على الوجه"</string>
+    <string name="face_app_setting_name" msgid="5854024256907828015">"فتح الجهاز ببصمة الوجه"</string>
     <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"استخدام ميزة \"فتح الجهاز بالتعرف على الوجه\" أو ميزة \"قفل الشاشة\""</string>
     <string name="face_dialog_default_subtitle" msgid="6620492813371195429">"استخدِم الوجه للمتابعة"</string>
     <string name="face_or_screen_lock_dialog_default_subtitle" msgid="5006381531158341844">"استخدام ميزة \"فتح القفل بالوجه\" أو ميزة \"قفل الشاشة\" للمتابعة"</string>
@@ -1007,7 +1007,7 @@
     <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"أعد المحاولة"</string>
     <string name="lockscreen_password_wrong" msgid="8605355913868947490">"أعد المحاولة"</string>
     <string name="lockscreen_storage_locked" msgid="634993789186443380">"فتح القفل للوصول إلى جميع الميزات والبيانات"</string>
-    <string name="faceunlock_multiple_failures" msgid="681991538434031708">"تم تجاوز الحد الأقصى لعدد محاولات فتح الجهاز بالتعرف على الوجه"</string>
+    <string name="faceunlock_multiple_failures" msgid="681991538434031708">"تم تجاوز الحد الأقصى لعدد محاولات فتح الجهاز ببصمة الوجه"</string>
     <string name="lockscreen_missing_sim_message_short" msgid="1229301273156907613">"‏لا تتوفر شريحة SIM."</string>
     <string name="lockscreen_missing_sim_message" product="tablet" msgid="3986843848305639161">"‏لا تتوفر شريحة SIM في الجهاز اللوحي."</string>
     <string name="lockscreen_missing_sim_message" product="tv" msgid="3903140876952198273">"‏لا تتوفر شريحة SIM في جهاز Android TV."</string>
@@ -1077,7 +1077,7 @@
     <string name="keyguard_accessibility_expand_lock_area" msgid="4215280881346033434">"توسيع منطقة فتح القفل."</string>
     <string name="keyguard_accessibility_slide_unlock" msgid="2968195219692413046">"فتح القفل باستخدام التمرير."</string>
     <string name="keyguard_accessibility_pattern_unlock" msgid="8669128146589233293">"فتح القفل باستخدام النقش."</string>
-    <string name="keyguard_accessibility_face_unlock" msgid="4533832120787386728">"فتح الجهاز بالتعرف على الوجه"</string>
+    <string name="keyguard_accessibility_face_unlock" msgid="4533832120787386728">"فتح الجهاز ببصمة الوجه"</string>
     <string name="keyguard_accessibility_pin_unlock" msgid="4020864007967340068">"‏فتح القفل باستخدام رمز PIN."</string>
     <string name="keyguard_accessibility_sim_pin_unlock" msgid="4895939120871890557">"‏فتح قفل رقم التعريف الشخصي لشريحة SIM."</string>
     <string name="keyguard_accessibility_sim_puk_unlock" msgid="3459003464041899101">"‏فتح قفل مفتاح PUK لشريحة SIM."</string>
@@ -1432,6 +1432,7 @@
     <string name="share_remote_bugreport_action" msgid="7630880678785123682">"مشاركة"</string>
     <string name="decline_remote_bugreport_action" msgid="4040894777519784346">"رفض"</string>
     <string name="select_input_method" msgid="3971267998568587025">"اختيار أسلوب الإدخال"</string>
+    <string name="input_method_language_settings" msgid="8069089418056819437">"إعدادات اللغة"</string>
     <string name="show_ime" msgid="6406112007347443383">"استمرار عرضها على الشاشة عندما تكون لوحة المفاتيح الخارجية متصلة"</string>
     <string name="hardware" msgid="3611039921284836033">"استخدام لوحة المفاتيح على الشاشة"</string>
     <string name="select_keyboard_layout_notification_title" msgid="5823199895322205589">"إعداد <xliff:g id="DEVICE_NAME">%s</xliff:g>"</string>
@@ -1525,8 +1526,8 @@
     <string name="permission_request_notification_title" msgid="1810025922441048273">"الإذن مطلوب"</string>
     <string name="permission_request_notification_with_subtitle" msgid="3743417870360129298">"الإذن مطلوب\nللحساب <xliff:g id="ACCOUNT">%s</xliff:g>."</string>
     <string name="permission_request_notification_for_app_with_subtitle" msgid="1298704005732851350">"طلب تطبيق <xliff:g id="APP">%1$s</xliff:g> الإذن بالدخول\nإلى حساب <xliff:g id="ACCOUNT">%2$s</xliff:g>."</string>
-    <string name="forward_intent_to_owner" msgid="4620359037192871015">"أنت تستخدم هذا التطبيق خارج ملفك الشخصي للعمل"</string>
-    <string name="forward_intent_to_work" msgid="3620262405636021151">"أنت تستخدم هذا التطبيق في ملفك الشخصي للعمل"</string>
+    <string name="forward_intent_to_owner" msgid="4620359037192871015">"أنت تستخدم هذا التطبيق خارج ملف العمل الخاص بك"</string>
+    <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>
@@ -1655,7 +1656,7 @@
     <string name="SetupCallDefault" msgid="5581740063237175247">"هل تريد قبول المكالمة؟"</string>
     <string name="activity_resolver_use_always" msgid="5575222334666843269">"دائمًا"</string>
     <string name="activity_resolver_use_once" msgid="948462794469672658">"مرة واحدة فقط"</string>
-    <string name="activity_resolver_work_profiles_support" msgid="4071345609235361269">"‏لا يدعم %1$s الملفات الشخصية للعمل"</string>
+    <string name="activity_resolver_work_profiles_support" msgid="4071345609235361269">"‏لا يدعم %1$s ملفات العمل"</string>
     <string name="default_audio_route_name" product="tablet" msgid="367936735632195517">"الجهاز اللوحي"</string>
     <string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"التلفزيون"</string>
     <string name="default_audio_route_name" product="default" msgid="9213546147739983977">"الهاتف"</string>
@@ -2227,7 +2228,7 @@
     <string name="resolver_switch_on_work" msgid="4527096360772311894">"إلغاء الإيقاف المؤقت"</string>
     <string name="resolver_no_work_apps_available" msgid="3298291360133337270">"ما مِن تطبيقات عمل."</string>
     <string name="resolver_no_personal_apps_available" msgid="6284837227019594881">"ما مِن تطبيقات شخصية."</string>
-    <string name="miniresolver_open_work" msgid="6286176185835401931">"هل تريد فتح تطبيق \"<xliff:g id="APP">%s</xliff:g>\" في ملفك الشخصي للعمل؟"</string>
+    <string name="miniresolver_open_work" msgid="6286176185835401931">"هل تريد فتح تطبيق \"<xliff:g id="APP">%s</xliff:g>\" في ملف العمل الخاص بك؟"</string>
     <string name="miniresolver_open_in_personal" msgid="807427577794490375">"هل تريد فتح المحتوى في تطبيق \"<xliff:g id="APP">%s</xliff:g>\" في الملف الشخصي؟"</string>
     <string name="miniresolver_open_in_work" msgid="941341494673509916">"هل تريد فتح المحتوى في تطبيق \"<xliff:g id="APP">%s</xliff:g>\" في ملف العمل؟"</string>
     <string name="miniresolver_call_in_work" msgid="528779988307529039">"هل تريد الاتصال من تطبيق العمل؟"</string>
@@ -2433,30 +2434,21 @@
     <string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"لا يمكن بعد الآن التعرّف على \"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g>\" و\"<xliff:g id="FINGERPRINT_1">%2$s</xliff:g>\"."</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="1824812666549916586">"لا يمكن بعد الآن التعرّف على \"<xliff:g id="FINGERPRINT">%s</xliff:g>\". يجب ضبط ميزة \"فتح الجهاز ببصمة الإصبع\" مجددًا."</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="5974657382960155099">"لا يمكن بعد الآن التعرّف على \"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g>\" و\"<xliff:g id="FINGERPRINT_1">%2$s</xliff:g>\". يجب ضبط ميزة \"فتح الجهاز ببصمة الإصبع\" مجددًا."</string>
-    <string name="face_dangling_notification_title" msgid="947852541060975473">"إعادة إعداد ميزة \"فتح الجهاز بالتعرّف على الوجه\""</string>
-    <string name="face_dangling_notification_msg" msgid="746235263598985384">"لا يمكن بعد الآن التعرّف على نموذج الوجه الخاص بك. يجب ضبط ميزة \"فتح الجهاز بالتعرّف على الوجه\" مجددًا."</string>
+    <string name="face_dangling_notification_title" msgid="947852541060975473">"إعادة إعداد ميزة \"فتح الجهاز ببصمة الوجه\""</string>
+    <string name="face_dangling_notification_msg" msgid="746235263598985384">"لا يمكن بعد الآن التعرّف على نموذج الوجه الخاص بك. يجب ضبط ميزة \"فتح الجهاز ببصمة الوجه\" مجددًا."</string>
     <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"إعداد"</string>
     <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"لاحقًا"</string>
     <string name="bg_user_sound_notification_title_alarm" msgid="5251678483393143527">"تنبيه لـ \"<xliff:g id="USER_NAME">%s</xliff:g>\""</string>
     <string name="bg_user_sound_notification_button_switch_user" msgid="3091969648572788946">"تبديل المستخدم"</string>
     <string name="bg_user_sound_notification_button_mute" msgid="4942158515665615243">"كتم الصوت"</string>
     <string name="bg_user_sound_notification_message" msgid="8613881975316976673">"انقر لكتم الصوت"</string>
-    <!-- no translation found for keyboard_shortcut_group_applications_browser (6535007304687100909) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_contacts (2750702518068326356) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_email (4229037666415353683) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_sms (3523799286376321137) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_music (2051507523525651067) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calendar (3571770335653387606) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calculator (6753209559716091507) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_maps (7950000659522589471) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications (3010389163951364798) -->
-    <skip />
+    <string name="keyboard_shortcut_group_applications_browser" msgid="6535007304687100909">"المتصفّح"</string>
+    <string name="keyboard_shortcut_group_applications_contacts" msgid="2750702518068326356">"جهات الاتصال"</string>
+    <string name="keyboard_shortcut_group_applications_email" msgid="4229037666415353683">"البريد الإلكتروني"</string>
+    <string name="keyboard_shortcut_group_applications_sms" msgid="3523799286376321137">"‏رسائل SMS"</string>
+    <string name="keyboard_shortcut_group_applications_music" msgid="2051507523525651067">"الموسيقى"</string>
+    <string name="keyboard_shortcut_group_applications_calendar" msgid="3571770335653387606">"التقويم"</string>
+    <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"الآلة الحاسبة"</string>
+    <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"‏خرائط Google"</string>
+    <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"التطبيقات"</string>
 </resources>
diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml
index 2642a32..4ddef5f 100644
--- a/core/res/res/values-as/strings.xml
+++ b/core/res/res/values-as/strings.xml
@@ -1428,6 +1428,7 @@
     <string name="share_remote_bugreport_action" msgid="7630880678785123682">"শ্বেয়াৰ কৰক"</string>
     <string name="decline_remote_bugreport_action" msgid="4040894777519784346">"প্ৰত্যাখ্যান কৰক"</string>
     <string name="select_input_method" msgid="3971267998568587025">"ইনপুট পদ্ধতি বাছনি কৰক"</string>
+    <string name="input_method_language_settings" msgid="8069089418056819437">"ভাষাৰ ছেটিং"</string>
     <string name="show_ime" msgid="6406112007347443383">"কায়িক কীব’ৰ্ড সক্ৰিয় হৈ থাকোঁতে ইয়াক স্ক্ৰীনত ৰাখক"</string>
     <string name="hardware" msgid="3611039921284836033">"অন-স্ক্ৰীন কীব’ৰ্ড ব্যৱহাৰ কৰক"</string>
     <string name="select_keyboard_layout_notification_title" msgid="5823199895322205589">"<xliff:g id="DEVICE_NAME">%s</xliff:g> কনফিগাৰ কৰক"</string>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index 90db2d4..86c4c35 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -1428,6 +1428,7 @@
     <string name="share_remote_bugreport_action" msgid="7630880678785123682">"PAYLAŞIN"</string>
     <string name="decline_remote_bugreport_action" msgid="4040894777519784346">"RƏDD EDİN"</string>
     <string name="select_input_method" msgid="3971267998568587025">"Daxiletmə metodunu seçin"</string>
+    <string name="input_method_language_settings" msgid="8069089418056819437">"Dil ayarları"</string>
     <string name="show_ime" msgid="6406112007347443383">"Fiziki klaviatura aktiv olanda görünsün"</string>
     <string name="hardware" msgid="3611039921284836033">"Ekran klaviaturası işlədin"</string>
     <string name="select_keyboard_layout_notification_title" msgid="5823199895322205589">"<xliff:g id="DEVICE_NAME">%s</xliff:g> cihazını konfiqurasiya edin"</string>
@@ -2433,26 +2434,17 @@
     <string name="face_dangling_notification_msg" msgid="746235263598985384">"Üz modeliniz artıq tanınmır. Üzlə Kilidaçmanı yenidən ayarlayın."</string>
     <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"Ayarlayın"</string>
     <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"İndi yox"</string>
-    <string name="bg_user_sound_notification_title_alarm" msgid="5251678483393143527">"<xliff:g id="USER_NAME">%s</xliff:g> üçün alarm"</string>
+    <string name="bg_user_sound_notification_title_alarm" msgid="5251678483393143527">"<xliff:g id="USER_NAME">%s</xliff:g> üçün zəngli saat"</string>
     <string name="bg_user_sound_notification_button_switch_user" msgid="3091969648572788946">"İstifadəçini dəyişin"</string>
     <string name="bg_user_sound_notification_button_mute" msgid="4942158515665615243">"Susdurun"</string>
     <string name="bg_user_sound_notification_message" msgid="8613881975316976673">"Susdurmaq üçün toxunun"</string>
-    <!-- no translation found for keyboard_shortcut_group_applications_browser (6535007304687100909) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_contacts (2750702518068326356) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_email (4229037666415353683) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_sms (3523799286376321137) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_music (2051507523525651067) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calendar (3571770335653387606) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calculator (6753209559716091507) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_maps (7950000659522589471) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications (3010389163951364798) -->
-    <skip />
+    <string name="keyboard_shortcut_group_applications_browser" msgid="6535007304687100909">"Brauzer"</string>
+    <string name="keyboard_shortcut_group_applications_contacts" msgid="2750702518068326356">"Kontaktlar"</string>
+    <string name="keyboard_shortcut_group_applications_email" msgid="4229037666415353683">"E-poçt"</string>
+    <string name="keyboard_shortcut_group_applications_sms" msgid="3523799286376321137">"SMS"</string>
+    <string name="keyboard_shortcut_group_applications_music" msgid="2051507523525651067">"Musiqi"</string>
+    <string name="keyboard_shortcut_group_applications_calendar" msgid="3571770335653387606">"Təqvim"</string>
+    <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Kalkulyator"</string>
+    <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Xəritə"</string>
+    <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Tətbiqlər"</string>
 </resources>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index a2a885b..33a92e0 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -1429,6 +1429,7 @@
     <string name="share_remote_bugreport_action" msgid="7630880678785123682">"DELI"</string>
     <string name="decline_remote_bugreport_action" msgid="4040894777519784346">"ODBIJ"</string>
     <string name="select_input_method" msgid="3971267998568587025">"Izbor metoda unosa"</string>
+    <string name="input_method_language_settings" msgid="8069089418056819437">"Podešavanja jezika"</string>
     <string name="show_ime" msgid="6406112007347443383">"Zadržava se na ekranu dok je fizička tastatura aktivna"</string>
     <string name="hardware" msgid="3611039921284836033">"Koristi tastaturu na ekranu"</string>
     <string name="select_keyboard_layout_notification_title" msgid="5823199895322205589">"Konfigurišite uređaj <xliff:g id="DEVICE_NAME">%s</xliff:g>"</string>
@@ -2438,22 +2439,13 @@
     <string name="bg_user_sound_notification_button_switch_user" msgid="3091969648572788946">"Promeni korisnika"</string>
     <string name="bg_user_sound_notification_button_mute" msgid="4942158515665615243">"Isključi zvuk"</string>
     <string name="bg_user_sound_notification_message" msgid="8613881975316976673">"Dodirnite da biste isključili zvuk"</string>
-    <!-- no translation found for keyboard_shortcut_group_applications_browser (6535007304687100909) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_contacts (2750702518068326356) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_email (4229037666415353683) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_sms (3523799286376321137) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_music (2051507523525651067) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calendar (3571770335653387606) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calculator (6753209559716091507) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_maps (7950000659522589471) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications (3010389163951364798) -->
-    <skip />
+    <string name="keyboard_shortcut_group_applications_browser" msgid="6535007304687100909">"Pregledač"</string>
+    <string name="keyboard_shortcut_group_applications_contacts" msgid="2750702518068326356">"Kontakti"</string>
+    <string name="keyboard_shortcut_group_applications_email" msgid="4229037666415353683">"Imejl"</string>
+    <string name="keyboard_shortcut_group_applications_sms" msgid="3523799286376321137">"SMS"</string>
+    <string name="keyboard_shortcut_group_applications_music" msgid="2051507523525651067">"Muzika"</string>
+    <string name="keyboard_shortcut_group_applications_calendar" msgid="3571770335653387606">"Kalendar"</string>
+    <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Kalkulator"</string>
+    <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Mape"</string>
+    <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Aplikacije"</string>
 </resources>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index 18d5698a..cd040c8 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -1430,6 +1430,7 @@
     <string name="share_remote_bugreport_action" msgid="7630880678785123682">"АБАГУЛІЦЬ"</string>
     <string name="decline_remote_bugreport_action" msgid="4040894777519784346">"АДХІЛІЦЬ"</string>
     <string name="select_input_method" msgid="3971267998568587025">"Выберыце метад уводу"</string>
+    <string name="input_method_language_settings" msgid="8069089418056819437">"Налады мовы"</string>
     <string name="show_ime" msgid="6406112007347443383">"Захоўваць яе на экране ў той час, калі фізічная клавіятура актыўная"</string>
     <string name="hardware" msgid="3611039921284836033">"Экранная клавіятура"</string>
     <string name="select_keyboard_layout_notification_title" msgid="5823199895322205589">"Наладзьце прыладу \"<xliff:g id="DEVICE_NAME">%s</xliff:g>\""</string>
@@ -2439,22 +2440,13 @@
     <string name="bg_user_sound_notification_button_switch_user" msgid="3091969648572788946">"Змяніць карыстальніка"</string>
     <string name="bg_user_sound_notification_button_mute" msgid="4942158515665615243">"Выключыць гук"</string>
     <string name="bg_user_sound_notification_message" msgid="8613881975316976673">"Націсніце, каб выключыць гук"</string>
-    <!-- no translation found for keyboard_shortcut_group_applications_browser (6535007304687100909) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_contacts (2750702518068326356) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_email (4229037666415353683) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_sms (3523799286376321137) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_music (2051507523525651067) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calendar (3571770335653387606) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calculator (6753209559716091507) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_maps (7950000659522589471) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications (3010389163951364798) -->
-    <skip />
+    <string name="keyboard_shortcut_group_applications_browser" msgid="6535007304687100909">"Браўзер"</string>
+    <string name="keyboard_shortcut_group_applications_contacts" msgid="2750702518068326356">"Кантакты"</string>
+    <string name="keyboard_shortcut_group_applications_email" msgid="4229037666415353683">"Электронная пошта"</string>
+    <string name="keyboard_shortcut_group_applications_sms" msgid="3523799286376321137">"SMS"</string>
+    <string name="keyboard_shortcut_group_applications_music" msgid="2051507523525651067">"Музыка"</string>
+    <string name="keyboard_shortcut_group_applications_calendar" msgid="3571770335653387606">"Каляндар"</string>
+    <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Калькулятар"</string>
+    <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Карты"</string>
+    <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Праграмы"</string>
 </resources>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index ea49f52..0fe1067 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -1428,6 +1428,7 @@
     <string name="share_remote_bugreport_action" msgid="7630880678785123682">"СПОДЕЛЯНЕ"</string>
     <string name="decline_remote_bugreport_action" msgid="4040894777519784346">"ОТХВЪРЛЯНЕ"</string>
     <string name="select_input_method" msgid="3971267998568587025">"Избор на метод на въвеждане"</string>
+    <string name="input_method_language_settings" msgid="8069089418056819437">"Езикови настройки"</string>
     <string name="show_ime" msgid="6406112007347443383">"Показване на екрана, докато физическата клавиатура е активна"</string>
     <string name="hardware" msgid="3611039921284836033">"Ползв. на екранната клавиатура"</string>
     <string name="select_keyboard_layout_notification_title" msgid="5823199895322205589">"Конфигуриране на <xliff:g id="DEVICE_NAME">%s</xliff:g>"</string>
@@ -2433,26 +2434,17 @@
     <string name="face_dangling_notification_msg" msgid="746235263598985384">"Моделът на лицето ви вече не може да бъде разпознат. Настройте отново „Отключване с лице“."</string>
     <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"Настройване"</string>
     <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"Не сега"</string>
-    <string name="bg_user_sound_notification_title_alarm" msgid="5251678483393143527">"Будилник за <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="bg_user_sound_notification_title_alarm" msgid="5251678483393143527">"Будилник за: <xliff:g id="USER_NAME">%s</xliff:g>"</string>
     <string name="bg_user_sound_notification_button_switch_user" msgid="3091969648572788946">"Смяна на потребителя"</string>
     <string name="bg_user_sound_notification_button_mute" msgid="4942158515665615243">"Спиране на звука"</string>
     <string name="bg_user_sound_notification_message" msgid="8613881975316976673">"Докоснете, за да спрете звука"</string>
-    <!-- no translation found for keyboard_shortcut_group_applications_browser (6535007304687100909) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_contacts (2750702518068326356) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_email (4229037666415353683) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_sms (3523799286376321137) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_music (2051507523525651067) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calendar (3571770335653387606) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calculator (6753209559716091507) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_maps (7950000659522589471) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications (3010389163951364798) -->
-    <skip />
+    <string name="keyboard_shortcut_group_applications_browser" msgid="6535007304687100909">"Браузър"</string>
+    <string name="keyboard_shortcut_group_applications_contacts" msgid="2750702518068326356">"Контакти"</string>
+    <string name="keyboard_shortcut_group_applications_email" msgid="4229037666415353683">"Имейл"</string>
+    <string name="keyboard_shortcut_group_applications_sms" msgid="3523799286376321137">"SMS"</string>
+    <string name="keyboard_shortcut_group_applications_music" msgid="2051507523525651067">"Музика"</string>
+    <string name="keyboard_shortcut_group_applications_calendar" msgid="3571770335653387606">"Календар"</string>
+    <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Калкулатор"</string>
+    <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Карти"</string>
+    <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Приложения"</string>
 </resources>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index 3548993..b32cf2d 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -1428,6 +1428,7 @@
     <string name="share_remote_bugreport_action" msgid="7630880678785123682">"শেয়ার করুন"</string>
     <string name="decline_remote_bugreport_action" msgid="4040894777519784346">"অস্বীকার করুন"</string>
     <string name="select_input_method" msgid="3971267998568587025">"ইনপুট পদ্ধতি বেছে নিন"</string>
+    <string name="input_method_language_settings" msgid="8069089418056819437">"ভাষা সেটিংস"</string>
     <string name="show_ime" msgid="6406112007347443383">"ফিজিক্যাল কীবোর্ড সক্রিয় থাকার সময় এটিকে স্ক্রীনে রাখুন"</string>
     <string name="hardware" msgid="3611039921284836033">"স্ক্রিনের কীবোর্ড ব্যবহার করুন"</string>
     <string name="select_keyboard_layout_notification_title" msgid="5823199895322205589">"<xliff:g id="DEVICE_NAME">%s</xliff:g> কনফিগার করুন"</string>
@@ -2437,22 +2438,13 @@
     <string name="bg_user_sound_notification_button_switch_user" msgid="3091969648572788946">"ব্যবহারকারী পাল্টান"</string>
     <string name="bg_user_sound_notification_button_mute" msgid="4942158515665615243">"মিউট করুন"</string>
     <string name="bg_user_sound_notification_message" msgid="8613881975316976673">"সাউন্ড মিউট করতে ট্যাপ করুন"</string>
-    <!-- no translation found for keyboard_shortcut_group_applications_browser (6535007304687100909) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_contacts (2750702518068326356) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_email (4229037666415353683) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_sms (3523799286376321137) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_music (2051507523525651067) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calendar (3571770335653387606) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calculator (6753209559716091507) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_maps (7950000659522589471) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications (3010389163951364798) -->
-    <skip />
+    <string name="keyboard_shortcut_group_applications_browser" msgid="6535007304687100909">"ব্রাউজার"</string>
+    <string name="keyboard_shortcut_group_applications_contacts" msgid="2750702518068326356">"পরিচিতি"</string>
+    <string name="keyboard_shortcut_group_applications_email" msgid="4229037666415353683">"ইমেল"</string>
+    <string name="keyboard_shortcut_group_applications_sms" msgid="3523799286376321137">"এসএমএস"</string>
+    <string name="keyboard_shortcut_group_applications_music" msgid="2051507523525651067">"মিউজিক"</string>
+    <string name="keyboard_shortcut_group_applications_calendar" msgid="3571770335653387606">"ক্যালেন্ডার"</string>
+    <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"ক্যালকুলেটর"</string>
+    <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"ম্যাপ"</string>
+    <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"অ্যাপ্লিকেশন"</string>
 </resources>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index eb66410..c950d7c 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -1067,7 +1067,7 @@
     <string name="keyguard_accessibility_user_selector" msgid="1466067610235696600">"Biranje korisnika"</string>
     <string name="keyguard_accessibility_status" msgid="6792745049712397237">"Status"</string>
     <string name="keyguard_accessibility_camera" msgid="7862557559464986528">"Kamera"</string>
-    <string name="keygaurd_accessibility_media_controls" msgid="2267379779900620614">"Upravljanje medijima"</string>
+    <string name="keygaurd_accessibility_media_controls" msgid="2267379779900620614">"Kontrole medija"</string>
     <string name="keyguard_accessibility_widget_reorder_start" msgid="7066213328912939191">"Promjena rasporeda widgeta je počela."</string>
     <string name="keyguard_accessibility_widget_reorder_end" msgid="1083806817600593490">"Promjena rasporeda widgeta je završena."</string>
     <string name="keyguard_accessibility_widget_deleted" msgid="1509738950119878705">"Widget <xliff:g id="WIDGET_INDEX">%1$s</xliff:g> je izbrisan."</string>
@@ -1429,6 +1429,7 @@
     <string name="share_remote_bugreport_action" msgid="7630880678785123682">"PODIJELI"</string>
     <string name="decline_remote_bugreport_action" msgid="4040894777519784346">"ODBACI"</string>
     <string name="select_input_method" msgid="3971267998568587025">"Odaberite način unosa"</string>
+    <string name="input_method_language_settings" msgid="8069089418056819437">"Postavke jezika"</string>
     <string name="show_ime" msgid="6406112007347443383">"Prikaži na ekranu dok je fizička tastatura aktivna"</string>
     <string name="hardware" msgid="3611039921284836033">"Koristi tastaturu na ekranu"</string>
     <string name="select_keyboard_layout_notification_title" msgid="5823199895322205589">"Konfigurirajte uređaj <xliff:g id="DEVICE_NAME">%s</xliff:g>"</string>
@@ -2438,22 +2439,13 @@
     <string name="bg_user_sound_notification_button_switch_user" msgid="3091969648572788946">"Prebaci na drugog korisnika"</string>
     <string name="bg_user_sound_notification_button_mute" msgid="4942158515665615243">"Isključi zvuk"</string>
     <string name="bg_user_sound_notification_message" msgid="8613881975316976673">"Dodirnite da isključite zvuk"</string>
-    <!-- no translation found for keyboard_shortcut_group_applications_browser (6535007304687100909) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_contacts (2750702518068326356) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_email (4229037666415353683) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_sms (3523799286376321137) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_music (2051507523525651067) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calendar (3571770335653387606) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calculator (6753209559716091507) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_maps (7950000659522589471) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications (3010389163951364798) -->
-    <skip />
+    <string name="keyboard_shortcut_group_applications_browser" msgid="6535007304687100909">"Preglednik"</string>
+    <string name="keyboard_shortcut_group_applications_contacts" msgid="2750702518068326356">"Kontakti"</string>
+    <string name="keyboard_shortcut_group_applications_email" msgid="4229037666415353683">"E-pošta"</string>
+    <string name="keyboard_shortcut_group_applications_sms" msgid="3523799286376321137">"SMS"</string>
+    <string name="keyboard_shortcut_group_applications_music" msgid="2051507523525651067">"Muzika"</string>
+    <string name="keyboard_shortcut_group_applications_calendar" msgid="3571770335653387606">"Kalendar"</string>
+    <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Kalkulator"</string>
+    <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Mape"</string>
+    <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Aplikacije"</string>
 </resources>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 9e5c336..bf9540c 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -1429,6 +1429,7 @@
     <string name="share_remote_bugreport_action" msgid="7630880678785123682">"COMPARTEIX"</string>
     <string name="decline_remote_bugreport_action" msgid="4040894777519784346">"REBUTJA"</string>
     <string name="select_input_method" msgid="3971267998568587025">"Selecciona un mètode d\'introducció"</string>
+    <string name="input_method_language_settings" msgid="8069089418056819437">"Configuració d\'idioma"</string>
     <string name="show_ime" msgid="6406112007347443383">"Mantén-lo en pantalla mentre el teclat físic està actiu"</string>
     <string name="hardware" msgid="3611039921284836033">"Utilitza el teclat en pantalla"</string>
     <string name="select_keyboard_layout_notification_title" msgid="5823199895322205589">"Configura <xliff:g id="DEVICE_NAME">%s</xliff:g>"</string>
@@ -2438,22 +2439,13 @@
     <string name="bg_user_sound_notification_button_switch_user" msgid="3091969648572788946">"Canvia d\'usuari"</string>
     <string name="bg_user_sound_notification_button_mute" msgid="4942158515665615243">"Silencia"</string>
     <string name="bg_user_sound_notification_message" msgid="8613881975316976673">"Toca per silenciar el so"</string>
-    <!-- no translation found for keyboard_shortcut_group_applications_browser (6535007304687100909) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_contacts (2750702518068326356) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_email (4229037666415353683) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_sms (3523799286376321137) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_music (2051507523525651067) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calendar (3571770335653387606) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calculator (6753209559716091507) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_maps (7950000659522589471) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications (3010389163951364798) -->
-    <skip />
+    <string name="keyboard_shortcut_group_applications_browser" msgid="6535007304687100909">"Navegador"</string>
+    <string name="keyboard_shortcut_group_applications_contacts" msgid="2750702518068326356">"Contactes"</string>
+    <string name="keyboard_shortcut_group_applications_email" msgid="4229037666415353683">"Correu electrònic"</string>
+    <string name="keyboard_shortcut_group_applications_sms" msgid="3523799286376321137">"SMS"</string>
+    <string name="keyboard_shortcut_group_applications_music" msgid="2051507523525651067">"Música"</string>
+    <string name="keyboard_shortcut_group_applications_calendar" msgid="3571770335653387606">"Calendar"</string>
+    <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Calculadora"</string>
+    <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Maps"</string>
+    <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Aplicacions"</string>
 </resources>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 78b79cb..139058f 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -1430,6 +1430,7 @@
     <string name="share_remote_bugreport_action" msgid="7630880678785123682">"SDÍLET"</string>
     <string name="decline_remote_bugreport_action" msgid="4040894777519784346">"ODMÍTNOUT"</string>
     <string name="select_input_method" msgid="3971267998568587025">"Vybrat metodu zadávání"</string>
+    <string name="input_method_language_settings" msgid="8069089418056819437">"Nastavení jazyka"</string>
     <string name="show_ime" msgid="6406112007347443383">"Ponechat na obrazovce, když je aktivní fyzická klávesnice"</string>
     <string name="hardware" msgid="3611039921284836033">"Použít softwarovou klávesnici"</string>
     <string name="select_keyboard_layout_notification_title" msgid="5823199895322205589">"Nakonfigurujte zařízení <xliff:g id="DEVICE_NAME">%s</xliff:g>"</string>
@@ -2439,22 +2440,13 @@
     <string name="bg_user_sound_notification_button_switch_user" msgid="3091969648572788946">"Přepnout uživatele"</string>
     <string name="bg_user_sound_notification_button_mute" msgid="4942158515665615243">"Ztlumit"</string>
     <string name="bg_user_sound_notification_message" msgid="8613881975316976673">"Klepnutím ztlumíte zvuk"</string>
-    <!-- no translation found for keyboard_shortcut_group_applications_browser (6535007304687100909) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_contacts (2750702518068326356) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_email (4229037666415353683) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_sms (3523799286376321137) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_music (2051507523525651067) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calendar (3571770335653387606) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calculator (6753209559716091507) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_maps (7950000659522589471) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications (3010389163951364798) -->
-    <skip />
+    <string name="keyboard_shortcut_group_applications_browser" msgid="6535007304687100909">"Prohlížeč"</string>
+    <string name="keyboard_shortcut_group_applications_contacts" msgid="2750702518068326356">"Kontakty"</string>
+    <string name="keyboard_shortcut_group_applications_email" msgid="4229037666415353683">"E‑mail"</string>
+    <string name="keyboard_shortcut_group_applications_sms" msgid="3523799286376321137">"SMS"</string>
+    <string name="keyboard_shortcut_group_applications_music" msgid="2051507523525651067">"Hudba"</string>
+    <string name="keyboard_shortcut_group_applications_calendar" msgid="3571770335653387606">"Kalendář"</string>
+    <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Kalkulačka"</string>
+    <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Mapy"</string>
+    <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Aplikace"</string>
 </resources>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index b7e70ef..8ec7e22 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -1428,6 +1428,7 @@
     <string name="share_remote_bugreport_action" msgid="7630880678785123682">"DEL"</string>
     <string name="decline_remote_bugreport_action" msgid="4040894777519784346">"AFVIS"</string>
     <string name="select_input_method" msgid="3971267998568587025">"Vælg inputmetode"</string>
+    <string name="input_method_language_settings" msgid="8069089418056819437">"Sprogindstillinger"</string>
     <string name="show_ime" msgid="6406112007347443383">"Behold den på skærmen, mens det fysiske tastatur er aktivt"</string>
     <string name="hardware" msgid="3611039921284836033">"Brug skærmtastaturet"</string>
     <string name="select_keyboard_layout_notification_title" msgid="5823199895322205589">"Konfigurer <xliff:g id="DEVICE_NAME">%s</xliff:g>"</string>
@@ -2437,22 +2438,13 @@
     <string name="bg_user_sound_notification_button_switch_user" msgid="3091969648572788946">"Skift bruger"</string>
     <string name="bg_user_sound_notification_button_mute" msgid="4942158515665615243">"Slå lyden fra"</string>
     <string name="bg_user_sound_notification_message" msgid="8613881975316976673">"Tryk for at slå lyden fra"</string>
-    <!-- no translation found for keyboard_shortcut_group_applications_browser (6535007304687100909) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_contacts (2750702518068326356) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_email (4229037666415353683) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_sms (3523799286376321137) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_music (2051507523525651067) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calendar (3571770335653387606) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calculator (6753209559716091507) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_maps (7950000659522589471) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications (3010389163951364798) -->
-    <skip />
+    <string name="keyboard_shortcut_group_applications_browser" msgid="6535007304687100909">"Browser"</string>
+    <string name="keyboard_shortcut_group_applications_contacts" msgid="2750702518068326356">"Kontakter"</string>
+    <string name="keyboard_shortcut_group_applications_email" msgid="4229037666415353683">"Mail"</string>
+    <string name="keyboard_shortcut_group_applications_sms" msgid="3523799286376321137">"Sms"</string>
+    <string name="keyboard_shortcut_group_applications_music" msgid="2051507523525651067">"Musik"</string>
+    <string name="keyboard_shortcut_group_applications_calendar" msgid="3571770335653387606">"Kalender"</string>
+    <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Lommeregner"</string>
+    <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Kort"</string>
+    <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Apps"</string>
 </resources>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index e30f92d..a7ae278 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -1428,6 +1428,7 @@
     <string name="share_remote_bugreport_action" msgid="7630880678785123682">"TEILEN"</string>
     <string name="decline_remote_bugreport_action" msgid="4040894777519784346">"ABLEHNEN"</string>
     <string name="select_input_method" msgid="3971267998568587025">"Eingabemethode wählen"</string>
+    <string name="input_method_language_settings" msgid="8069089418056819437">"Spracheinstellungen"</string>
     <string name="show_ime" msgid="6406112007347443383">"Bildschirmtastatur auch dann anzeigen, wenn physische Tastatur aktiv ist"</string>
     <string name="hardware" msgid="3611039921284836033">"Bildschirmtastatur verwenden"</string>
     <string name="select_keyboard_layout_notification_title" msgid="5823199895322205589">"<xliff:g id="DEVICE_NAME">%s</xliff:g> konfigurieren"</string>
@@ -2437,22 +2438,13 @@
     <string name="bg_user_sound_notification_button_switch_user" msgid="3091969648572788946">"Nutzer wechseln"</string>
     <string name="bg_user_sound_notification_button_mute" msgid="4942158515665615243">"Stummschalten"</string>
     <string name="bg_user_sound_notification_message" msgid="8613881975316976673">"Zum Stummschalten tippen"</string>
-    <!-- no translation found for keyboard_shortcut_group_applications_browser (6535007304687100909) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_contacts (2750702518068326356) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_email (4229037666415353683) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_sms (3523799286376321137) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_music (2051507523525651067) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calendar (3571770335653387606) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calculator (6753209559716091507) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_maps (7950000659522589471) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications (3010389163951364798) -->
-    <skip />
+    <string name="keyboard_shortcut_group_applications_browser" msgid="6535007304687100909">"Browser"</string>
+    <string name="keyboard_shortcut_group_applications_contacts" msgid="2750702518068326356">"Kontakte"</string>
+    <string name="keyboard_shortcut_group_applications_email" msgid="4229037666415353683">"E‑Mail"</string>
+    <string name="keyboard_shortcut_group_applications_sms" msgid="3523799286376321137">"SMS"</string>
+    <string name="keyboard_shortcut_group_applications_music" msgid="2051507523525651067">"Musik"</string>
+    <string name="keyboard_shortcut_group_applications_calendar" msgid="3571770335653387606">"Kalender"</string>
+    <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Rechner"</string>
+    <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Maps"</string>
+    <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Anwendungen"</string>
 </resources>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index c7a5563..a6a3f6f 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -1428,6 +1428,7 @@
     <string name="share_remote_bugreport_action" msgid="7630880678785123682">"ΚΟΙΝΟΠΟΙΗΣΗ"</string>
     <string name="decline_remote_bugreport_action" msgid="4040894777519784346">"ΑΠΟΡΡΙΨΗ"</string>
     <string name="select_input_method" msgid="3971267998568587025">"Επιλογή μεθόδου εισόδου"</string>
+    <string name="input_method_language_settings" msgid="8069089418056819437">"Ρυθμίσεις γλώσσας"</string>
     <string name="show_ime" msgid="6406112007347443383">"Να παραμένει στην οθόνη όταν είναι ενεργό το κανονικό πληκτρολόγιο"</string>
     <string name="hardware" msgid="3611039921284836033">"Χρήση πληκτρολογίου οθόνης"</string>
     <string name="select_keyboard_layout_notification_title" msgid="5823199895322205589">"Διαμόρφωση <xliff:g id="DEVICE_NAME">%s</xliff:g>"</string>
@@ -2437,22 +2438,13 @@
     <string name="bg_user_sound_notification_button_switch_user" msgid="3091969648572788946">"Εναλλαγή χρήστη"</string>
     <string name="bg_user_sound_notification_button_mute" msgid="4942158515665615243">"Σίγαση"</string>
     <string name="bg_user_sound_notification_message" msgid="8613881975316976673">"Πατήστε για σίγαση του ήχου"</string>
-    <!-- no translation found for keyboard_shortcut_group_applications_browser (6535007304687100909) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_contacts (2750702518068326356) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_email (4229037666415353683) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_sms (3523799286376321137) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_music (2051507523525651067) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calendar (3571770335653387606) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calculator (6753209559716091507) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_maps (7950000659522589471) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications (3010389163951364798) -->
-    <skip />
+    <string name="keyboard_shortcut_group_applications_browser" msgid="6535007304687100909">"Πρόγραμμα περιήγησης"</string>
+    <string name="keyboard_shortcut_group_applications_contacts" msgid="2750702518068326356">"Επαφές"</string>
+    <string name="keyboard_shortcut_group_applications_email" msgid="4229037666415353683">"Ηλεκτρονικό ταχυδρομείο"</string>
+    <string name="keyboard_shortcut_group_applications_sms" msgid="3523799286376321137">"SMS"</string>
+    <string name="keyboard_shortcut_group_applications_music" msgid="2051507523525651067">"Μουσική"</string>
+    <string name="keyboard_shortcut_group_applications_calendar" msgid="3571770335653387606">"Ημερολόγιο"</string>
+    <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Αριθμομηχανή"</string>
+    <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Χάρτες"</string>
+    <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Εφαρμογές"</string>
 </resources>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index 5dfd2ac..a58b262 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -1428,6 +1428,7 @@
     <string name="share_remote_bugreport_action" msgid="7630880678785123682">"SHARE"</string>
     <string name="decline_remote_bugreport_action" msgid="4040894777519784346">"DECLINE"</string>
     <string name="select_input_method" msgid="3971267998568587025">"Choose input method"</string>
+    <string name="input_method_language_settings" msgid="8069089418056819437">"Language settings"</string>
     <string name="show_ime" msgid="6406112007347443383">"Keep it on screen while physical keyboard is active"</string>
     <string name="hardware" msgid="3611039921284836033">"Use on-screen keyboard"</string>
     <string name="select_keyboard_layout_notification_title" msgid="5823199895322205589">"Configure <xliff:g id="DEVICE_NAME">%s</xliff:g>"</string>
@@ -2433,26 +2434,17 @@
     <string name="face_dangling_notification_msg" msgid="746235263598985384">"Your face model can no longer be recognised. Set up Face Unlock again."</string>
     <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"Set up"</string>
     <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"Not now"</string>
-    <string name="bg_user_sound_notification_title_alarm" msgid="5251678483393143527">"Alarm for <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="bg_user_sound_notification_title_alarm" msgid="5251678483393143527">"Alarm for: <xliff:g id="USER_NAME">%s</xliff:g>"</string>
     <string name="bg_user_sound_notification_button_switch_user" msgid="3091969648572788946">"Switch user"</string>
     <string name="bg_user_sound_notification_button_mute" msgid="4942158515665615243">"Mute"</string>
     <string name="bg_user_sound_notification_message" msgid="8613881975316976673">"Tap to mute sound"</string>
-    <!-- no translation found for keyboard_shortcut_group_applications_browser (6535007304687100909) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_contacts (2750702518068326356) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_email (4229037666415353683) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_sms (3523799286376321137) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_music (2051507523525651067) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calendar (3571770335653387606) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calculator (6753209559716091507) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_maps (7950000659522589471) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications (3010389163951364798) -->
-    <skip />
+    <string name="keyboard_shortcut_group_applications_browser" msgid="6535007304687100909">"Browser"</string>
+    <string name="keyboard_shortcut_group_applications_contacts" msgid="2750702518068326356">"Contacts"</string>
+    <string name="keyboard_shortcut_group_applications_email" msgid="4229037666415353683">"Email"</string>
+    <string name="keyboard_shortcut_group_applications_sms" msgid="3523799286376321137">"SMS"</string>
+    <string name="keyboard_shortcut_group_applications_music" msgid="2051507523525651067">"Music"</string>
+    <string name="keyboard_shortcut_group_applications_calendar" msgid="3571770335653387606">"Calendar"</string>
+    <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Calculator"</string>
+    <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Maps"</string>
+    <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Applications"</string>
 </resources>
diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml
index 55c6376..656d177 100644
--- a/core/res/res/values-en-rCA/strings.xml
+++ b/core/res/res/values-en-rCA/strings.xml
@@ -1428,6 +1428,7 @@
     <string name="share_remote_bugreport_action" msgid="7630880678785123682">"SHARE"</string>
     <string name="decline_remote_bugreport_action" msgid="4040894777519784346">"DECLINE"</string>
     <string name="select_input_method" msgid="3971267998568587025">"Choose input method"</string>
+    <string name="input_method_language_settings" msgid="8069089418056819437">"Language Settings"</string>
     <string name="show_ime" msgid="6406112007347443383">"Keep it on screen while physical keyboard is active"</string>
     <string name="hardware" msgid="3611039921284836033">"Use on-screen keyboard"</string>
     <string name="select_keyboard_layout_notification_title" msgid="5823199895322205589">"Configure <xliff:g id="DEVICE_NAME">%s</xliff:g>"</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index bb55bad..234eb7b 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -1428,6 +1428,7 @@
     <string name="share_remote_bugreport_action" msgid="7630880678785123682">"SHARE"</string>
     <string name="decline_remote_bugreport_action" msgid="4040894777519784346">"DECLINE"</string>
     <string name="select_input_method" msgid="3971267998568587025">"Choose input method"</string>
+    <string name="input_method_language_settings" msgid="8069089418056819437">"Language settings"</string>
     <string name="show_ime" msgid="6406112007347443383">"Keep it on screen while physical keyboard is active"</string>
     <string name="hardware" msgid="3611039921284836033">"Use on-screen keyboard"</string>
     <string name="select_keyboard_layout_notification_title" msgid="5823199895322205589">"Configure <xliff:g id="DEVICE_NAME">%s</xliff:g>"</string>
@@ -2433,26 +2434,17 @@
     <string name="face_dangling_notification_msg" msgid="746235263598985384">"Your face model can no longer be recognised. Set up Face Unlock again."</string>
     <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"Set up"</string>
     <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"Not now"</string>
-    <string name="bg_user_sound_notification_title_alarm" msgid="5251678483393143527">"Alarm for <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="bg_user_sound_notification_title_alarm" msgid="5251678483393143527">"Alarm for: <xliff:g id="USER_NAME">%s</xliff:g>"</string>
     <string name="bg_user_sound_notification_button_switch_user" msgid="3091969648572788946">"Switch user"</string>
     <string name="bg_user_sound_notification_button_mute" msgid="4942158515665615243">"Mute"</string>
     <string name="bg_user_sound_notification_message" msgid="8613881975316976673">"Tap to mute sound"</string>
-    <!-- no translation found for keyboard_shortcut_group_applications_browser (6535007304687100909) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_contacts (2750702518068326356) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_email (4229037666415353683) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_sms (3523799286376321137) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_music (2051507523525651067) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calendar (3571770335653387606) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calculator (6753209559716091507) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_maps (7950000659522589471) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications (3010389163951364798) -->
-    <skip />
+    <string name="keyboard_shortcut_group_applications_browser" msgid="6535007304687100909">"Browser"</string>
+    <string name="keyboard_shortcut_group_applications_contacts" msgid="2750702518068326356">"Contacts"</string>
+    <string name="keyboard_shortcut_group_applications_email" msgid="4229037666415353683">"Email"</string>
+    <string name="keyboard_shortcut_group_applications_sms" msgid="3523799286376321137">"SMS"</string>
+    <string name="keyboard_shortcut_group_applications_music" msgid="2051507523525651067">"Music"</string>
+    <string name="keyboard_shortcut_group_applications_calendar" msgid="3571770335653387606">"Calendar"</string>
+    <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Calculator"</string>
+    <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Maps"</string>
+    <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Applications"</string>
 </resources>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index 1ab0a16..8c2a92e 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -1428,6 +1428,7 @@
     <string name="share_remote_bugreport_action" msgid="7630880678785123682">"SHARE"</string>
     <string name="decline_remote_bugreport_action" msgid="4040894777519784346">"DECLINE"</string>
     <string name="select_input_method" msgid="3971267998568587025">"Choose input method"</string>
+    <string name="input_method_language_settings" msgid="8069089418056819437">"Language settings"</string>
     <string name="show_ime" msgid="6406112007347443383">"Keep it on screen while physical keyboard is active"</string>
     <string name="hardware" msgid="3611039921284836033">"Use on-screen keyboard"</string>
     <string name="select_keyboard_layout_notification_title" msgid="5823199895322205589">"Configure <xliff:g id="DEVICE_NAME">%s</xliff:g>"</string>
@@ -2433,26 +2434,17 @@
     <string name="face_dangling_notification_msg" msgid="746235263598985384">"Your face model can no longer be recognised. Set up Face Unlock again."</string>
     <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"Set up"</string>
     <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"Not now"</string>
-    <string name="bg_user_sound_notification_title_alarm" msgid="5251678483393143527">"Alarm for <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="bg_user_sound_notification_title_alarm" msgid="5251678483393143527">"Alarm for: <xliff:g id="USER_NAME">%s</xliff:g>"</string>
     <string name="bg_user_sound_notification_button_switch_user" msgid="3091969648572788946">"Switch user"</string>
     <string name="bg_user_sound_notification_button_mute" msgid="4942158515665615243">"Mute"</string>
     <string name="bg_user_sound_notification_message" msgid="8613881975316976673">"Tap to mute sound"</string>
-    <!-- no translation found for keyboard_shortcut_group_applications_browser (6535007304687100909) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_contacts (2750702518068326356) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_email (4229037666415353683) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_sms (3523799286376321137) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_music (2051507523525651067) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calendar (3571770335653387606) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calculator (6753209559716091507) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_maps (7950000659522589471) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications (3010389163951364798) -->
-    <skip />
+    <string name="keyboard_shortcut_group_applications_browser" msgid="6535007304687100909">"Browser"</string>
+    <string name="keyboard_shortcut_group_applications_contacts" msgid="2750702518068326356">"Contacts"</string>
+    <string name="keyboard_shortcut_group_applications_email" msgid="4229037666415353683">"Email"</string>
+    <string name="keyboard_shortcut_group_applications_sms" msgid="3523799286376321137">"SMS"</string>
+    <string name="keyboard_shortcut_group_applications_music" msgid="2051507523525651067">"Music"</string>
+    <string name="keyboard_shortcut_group_applications_calendar" msgid="3571770335653387606">"Calendar"</string>
+    <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Calculator"</string>
+    <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Maps"</string>
+    <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Applications"</string>
 </resources>
diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml
index cac4a51..c59c194 100644
--- a/core/res/res/values-en-rXC/strings.xml
+++ b/core/res/res/values-en-rXC/strings.xml
@@ -1428,6 +1428,7 @@
     <string name="share_remote_bugreport_action" msgid="7630880678785123682">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‎‏‏‏‏‎‎‏‏‎‎‏‎‏‎‏‎‏‏‎‎‎‏‏‎‎‎‎‎‏‎‏‏‎‎‏‏‎‎‎‎‏‎‎‎‎‎‏‎‏‎‏‏‎‎‎‏‎‎SHARE‎‏‎‎‏‎"</string>
     <string name="decline_remote_bugreport_action" msgid="4040894777519784346">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‎‎‎‎‎‏‎‏‎‎‎‎‏‎‎‏‎‎‎‏‏‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‎‎‏‏‎‎‎‏‎‎‎‎‎‏‏‎‎‏‏‎‏‎‎DECLINE‎‏‎‎‏‎"</string>
     <string name="select_input_method" msgid="3971267998568587025">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‏‏‎‎‎‏‏‏‎‎‏‏‎‎‎‏‏‏‎‎‏‏‎‏‎‎‎‎‏‏‏‎‏‎‏‎‎‎‏‏‏‎‏‎‎‏‏‏‏‏‎‎‎‏‎‎‎‏‎Choose input method‎‏‎‎‏‎"</string>
+    <string name="input_method_language_settings" msgid="8069089418056819437">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‏‏‏‏‏‏‏‎‏‏‎‎‏‎‏‎‏‎‎‎‎‏‎‎‏‎‏‏‏‎‏‎‎‎‎‎‏‎‎‎‏‏‏‏‏‎‎‎‏‎‏‏‏‎‏‏‎‏‎Language Settings‎‏‎‎‏‎"</string>
     <string name="show_ime" msgid="6406112007347443383">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‎‎‏‏‏‎‎‏‏‏‎‎‎‏‎‏‎‎‏‏‏‎‏‏‎‏‏‏‏‏‏‏‎‏‎‏‎‎‏‏‏‏‎‎‎‏‎‏‏‎‏‎‏‏‎‏‏‏‎Keep it on screen while physical keyboard is active‎‏‎‎‏‎"</string>
     <string name="hardware" msgid="3611039921284836033">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‏‎‎‎‎‏‏‏‎‎‏‏‏‏‏‏‎‏‏‎‏‎‏‏‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‏‏‏‏‎‏‎‏‎‏‏‎‎‎‎‎‏‎Use on-screen keyboard‎‏‎‎‏‎"</string>
     <string name="select_keyboard_layout_notification_title" msgid="5823199895322205589">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‎‎‏‏‎‏‎‎‎‎‎‎‏‎‏‎‎‏‎‏‏‏‎‎‏‏‏‏‎‎‎‎‎‎‏‏‎‎‎‏‎‏‏‎‎‏‏‏‎‏‏‎‎‏‎‏‎‏‎Configure ‎‏‎‎‏‏‎<xliff:g id="DEVICE_NAME">%s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index b1f56b7..3235b0e 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -272,7 +272,7 @@
     <string name="bugreport_option_interactive_summary" msgid="8493795476325339542">"Usa esta opción en la mayoría de los casos. Te permite realizar un seguimiento del progreso del informe, ingresar más detalles acerca del problema y tomar capturas de pantalla. Es posible que se omitan secciones menos usadas cuyos informes demoran más en completarse."</string>
     <string name="bugreport_option_full_title" msgid="7681035745950045690">"Informe completo"</string>
     <string name="bugreport_option_full_summary" msgid="1975130009258435885">"Usa esta opción para reducir al mínimo la interferencia del sistema cuando tu dispositivo no responde o funciona muy lento, o cuando necesitas todas las secciones del informe. No permite ingresar más detalles ni tomar capturas de pantalla adicionales."</string>
-    <string name="bugreport_countdown" msgid="6418620521782120755">"{count,plural, =1{Se tomará una captura de pantalla para el informe de errores en # segundo.}many{Se tomará una captura de pantalla para el informe de errores en # segundos.}other{Se tomará una captura de pantalla para el informe de errores en # segundos.}}"</string>
+    <string name="bugreport_countdown" msgid="6418620521782120755">"{count,plural, =1{Se tomará una captura de pantalla para el informe de errores en # segundo.}many{Se tomará una captura de pantalla para el informe de errores en # de segundos.}other{Se tomará una captura de pantalla para el informe de errores en # segundos.}}"</string>
     <string name="bugreport_screenshot_success_toast" msgid="7986095104151473745">"Se tomó la captura de pantalla con el informe de errores"</string>
     <string name="bugreport_screenshot_failure_toast" msgid="6736320861311294294">"No se pudo tomar la captura de pantalla con el informe de errores"</string>
     <string name="global_action_toggle_silent_mode" msgid="8464352592860372188">"Modo silencioso"</string>
@@ -292,7 +292,7 @@
     <string name="notification_channel_security" msgid="8516754650348238057">"Seguridad"</string>
     <string name="notification_channel_car_mode" msgid="2123919247040988436">"Modo auto"</string>
     <string name="notification_channel_account" msgid="6436294521740148173">"Estado de la cuenta"</string>
-    <string name="notification_channel_developer" msgid="1691059964407549150">"Mensajes de programadores"</string>
+    <string name="notification_channel_developer" msgid="1691059964407549150">"Mensajes de desarrolladores"</string>
     <string name="notification_channel_developer_important" msgid="7197281908918789589">"Mensajes importantes de desarrolladores"</string>
     <string name="notification_channel_updates" msgid="7907863984825495278">"Actualizaciones"</string>
     <string name="notification_channel_network_status" msgid="2127687368725272809">"Estado de la red"</string>
@@ -966,7 +966,7 @@
     <string name="relationTypeCustom" msgid="282938315217441351">"Personalizado"</string>
     <string name="relationTypeAssistant" msgid="4057605157116589315">"Asistente"</string>
     <string name="relationTypeBrother" msgid="7141662427379247820">"Hermano"</string>
-    <string name="relationTypeChild" msgid="9076258911292693601">"Hijo"</string>
+    <string name="relationTypeChild" msgid="9076258911292693601">"Hijo o hija"</string>
     <string name="relationTypeDomesticPartner" msgid="7825306887697559238">"Pareja de hecho"</string>
     <string name="relationTypeFather" msgid="3856225062864790596">"Padre"</string>
     <string name="relationTypeFriend" msgid="3192092625893980574">"Amigo"</string>
@@ -1127,7 +1127,7 @@
     <string name="enable_explore_by_touch_warning_message" product="default" msgid="4312979647356179250">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> desea activar la exploración táctil. Cuando esta función esté activada, podrás escuchar o ver descripciones del contenido seleccionado o usar gestos para interactuar con el dispositivo."</string>
     <string name="oneMonthDurationPast" msgid="4538030857114635777">"Hace 1 mes."</string>
     <string name="beforeOneMonthDurationPast" msgid="8315149541372065392">"Anterior a 1 mes atrás"</string>
-    <string name="last_num_days" msgid="2393660431490280537">"{count,plural, =1{Último # día}many{Últimos # días}other{Últimos # días}}"</string>
+    <string name="last_num_days" msgid="2393660431490280537">"{count,plural, =1{Último # día}many{Últimos # de días}other{Últimos # días}}"</string>
     <string name="last_month" msgid="1528906781083518683">"Último mes"</string>
     <string name="older" msgid="1645159827884647400">"Antiguos"</string>
     <string name="preposition_for_date" msgid="2780767868832729599">"activado <xliff:g id="DATE">%s</xliff:g>"</string>
@@ -1155,11 +1155,11 @@
     <string name="duration_days_shortest_future" msgid="3392722163935571543">"en <xliff:g id="COUNT">%d</xliff:g> d"</string>
     <string name="duration_years_shortest_future" msgid="5537464088352970388">"en <xliff:g id="COUNT">%d</xliff:g> años"</string>
     <string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{Hace # minuto}many{Hace # minutos}other{Hace # minutos}}"</string>
-    <string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{Hace # hora}many{Hace # horas}other{Hace # horas}}"</string>
-    <string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{Hace # día}many{Hace # días}other{Hace # días}}"</string>
+    <string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{Hace # hora}many{Hace # de horas}other{Hace # horas}}"</string>
+    <string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{Hace # día}many{Hace # de días}other{Hace # días}}"</string>
     <string name="duration_years_relative" msgid="8731202348869424370">"{count,plural, =1{Hace # año}many{Hace # años}other{Hace # años}}"</string>
-    <string name="duration_minutes_relative_future" msgid="5259574171747708115">"{count,plural, =1{# minuto}many{# minutos}other{# minutos}}"</string>
-    <string name="duration_hours_relative_future" msgid="6670440478481140565">"{count,plural, =1{# hora}many{# horas}other{# horas}}"</string>
+    <string name="duration_minutes_relative_future" msgid="5259574171747708115">"{count,plural, =1{# minuto}many{# de minutos}other{# minutos}}"</string>
+    <string name="duration_hours_relative_future" msgid="6670440478481140565">"{count,plural, =1{# hora}many{# horas}other{# horas}}"</string>
     <string name="duration_days_relative_future" msgid="8870658635774250746">"{count,plural, =1{# día}many{# días}other{# días}}"</string>
     <string name="duration_years_relative_future" msgid="8855853883925918380">"{count,plural, =1{# año}many{# años}other{# años}}"</string>
     <string name="VideoView_error_title" msgid="5750686717225068016">"Problemas de video"</string>
@@ -1429,6 +1429,7 @@
     <string name="share_remote_bugreport_action" msgid="7630880678785123682">"COMPARTIR"</string>
     <string name="decline_remote_bugreport_action" msgid="4040894777519784346">"RECHAZAR"</string>
     <string name="select_input_method" msgid="3971267998568587025">"Selecciona el método de entrada"</string>
+    <string name="input_method_language_settings" msgid="8069089418056819437">"Configuración del idioma"</string>
     <string name="show_ime" msgid="6406112007347443383">"Mientras el teclado físico está activo"</string>
     <string name="hardware" msgid="3611039921284836033">"Usar teclado en pantalla"</string>
     <string name="select_keyboard_layout_notification_title" msgid="5823199895322205589">"Configura <xliff:g id="DEVICE_NAME">%s</xliff:g>"</string>
@@ -1473,7 +1474,7 @@
     <string name="ext_media_seamless_action" msgid="8837030226009268080">"Cambiar salida"</string>
     <string name="ext_media_missing_title" msgid="3209472091220515046">"Falta <xliff:g id="NAME">%s</xliff:g>"</string>
     <string name="ext_media_missing_message" msgid="4408988706227922909">"Vuelve a insertar dispositivo"</string>
-    <string name="ext_media_move_specific_title" msgid="8492118544775964250">"Transfiriendo la aplicación <xliff:g id="NAME">%s</xliff:g>"</string>
+    <string name="ext_media_move_specific_title" msgid="8492118544775964250">"Moviendo <xliff:g id="NAME">%s</xliff:g>"</string>
     <string name="ext_media_move_title" msgid="2682741525619033637">"Transfiriendo los datos"</string>
     <string name="ext_media_move_success_title" msgid="4901763082647316767">"Se transfirió el contenido"</string>
     <string name="ext_media_move_success_message" msgid="9159542002276982979">"Se transfirió el contenido a <xliff:g id="NAME">%s</xliff:g>"</string>
@@ -1527,7 +1528,7 @@
     <string name="input_method_binding_label" msgid="1166731601721983656">"Método de entrada"</string>
     <string name="sync_binding_label" msgid="469249309424662147">"Sincronización"</string>
     <string name="accessibility_binding_label" msgid="1974602776545801715">"Accesibilidad"</string>
-    <string name="wallpaper_binding_label" msgid="1197440498000786738">"Papel tapiz"</string>
+    <string name="wallpaper_binding_label" msgid="1197440498000786738">"Fondo de pantalla"</string>
     <string name="chooser_wallpaper" msgid="3082405680079923708">"Cambiar fondo de pantalla"</string>
     <string name="notification_listener_binding_label" msgid="2702165274471499713">"Agente de escucha de notificaciones"</string>
     <string name="vr_listener_binding_label" msgid="8013112996671206429">"Procesador de realidad virtual"</string>
@@ -1536,7 +1537,7 @@
     <string name="vpn_title" msgid="5906991595291514182">"VPN activada"</string>
     <string name="vpn_title_long" msgid="6834144390504619998">"VPN está activado por <xliff:g id="APP">%s</xliff:g>"</string>
     <string name="vpn_text" msgid="2275388920267251078">"Pulsa para gestionar la red."</string>
-    <string name="vpn_text_long" msgid="278540576806169831">"Conectado a <xliff:g id="SESSION">%s</xliff:g>. Pulsa para gestionar la red."</string>
+    <string name="vpn_text_long" msgid="278540576806169831">"Se estableció conexión con <xliff:g id="SESSION">%s</xliff:g>. Presiona para administrar la red."</string>
     <string name="vpn_lockdown_connecting" msgid="6096725311950342607">"Estableciendo conexión con la VPN siempre activada..."</string>
     <string name="vpn_lockdown_connected" msgid="2853127976590658469">"Se estableció conexión con la VPN siempre activada."</string>
     <string name="vpn_lockdown_disconnected" msgid="5573611651300764955">"Desconectado de la VPN siempre activa"</string>
@@ -1915,14 +1916,14 @@
     <string name="data_saver_description" msgid="4995164271550590517">"Para reducir el uso de datos, el modo Ahorro de datos evita que algunas apps envíen y reciban datos en segundo plano. La app que estés usando podrá acceder a los datos, pero con menor frecuencia. De esta forma, por ejemplo, las imágenes no se mostrarán hasta que las presiones."</string>
     <string name="data_saver_enable_title" msgid="7080620065745260137">"¿Deseas activar Ahorro de datos?"</string>
     <string name="data_saver_enable_button" msgid="4399405762586419726">"Activar"</string>
-    <string name="zen_mode_duration_minutes_summary" msgid="4555514757230849789">"{count,plural, =1{Por un minuto (hasta {formattedTime})}many{Por # minutos (hasta {formattedTime})}other{Por # minutos (hasta {formattedTime})}}"</string>
+    <string name="zen_mode_duration_minutes_summary" msgid="4555514757230849789">"{count,plural, =1{Por un minuto (hasta la(s) {formattedTime})}many{Por # de minutos (hasta la(s) {formattedTime})}other{Por # minutos (hasta la(s) {formattedTime})}}"</string>
     <string name="zen_mode_duration_minutes_summary_short" msgid="1187553788355486950">"{count,plural, =1{Durante 1 min (hasta {formattedTime})}many{Durante # min (hasta {formattedTime})}other{Durante # min (hasta {formattedTime})}}"</string>
     <string name="zen_mode_duration_hours_summary" msgid="3866333100793277211">"{count,plural, =1{Durante 1 hora (hasta {formattedTime})}many{Durante # horas (hasta {formattedTime})}other{Durante # horas (hasta {formattedTime})}}"</string>
     <string name="zen_mode_duration_hours_summary_short" msgid="687919813833347945">"{count,plural, =1{Durante 1 h (hasta {formattedTime})}many{Durante # h (hasta {formattedTime})}other{Durante # h (hasta {formattedTime})}}"</string>
-    <string name="zen_mode_duration_minutes" msgid="2340007982276569054">"{count,plural, =1{Durante un minuto}many{Durante # minutos}other{Durante # minutos}}"</string>
-    <string name="zen_mode_duration_minutes_short" msgid="2435756450204526554">"{count,plural, =1{Durante 1 min}many{Durante # min}other{Durante # min}}"</string>
-    <string name="zen_mode_duration_hours" msgid="7841806065034711849">"{count,plural, =1{Durante 1 hora}many{Durante # horas}other{Durante # horas}}"</string>
-    <string name="zen_mode_duration_hours_short" msgid="3666949653933099065">"{count,plural, =1{Durante 1 h}many{Durante # h}other{Durante # h}}"</string>
+    <string name="zen_mode_duration_minutes" msgid="2340007982276569054">"{count,plural, =1{Durante un minuto}many{Durante # de minutos}other{Durante # minutos}}"</string>
+    <string name="zen_mode_duration_minutes_short" msgid="2435756450204526554">"{count,plural, =1{Durante 1 min}many{Durante # de min}other{Durante # min}}"</string>
+    <string name="zen_mode_duration_hours" msgid="7841806065034711849">"{count,plural, =1{Durante 1 hora}many{Durante # de horas}other{Durante # horas}}"</string>
+    <string name="zen_mode_duration_hours_short" msgid="3666949653933099065">"{count,plural, =1{Durante 1 h}many{Durante # de h}other{Durante # h}}"</string>
     <string name="zen_mode_until_next_day" msgid="1403042784161725038">"Hasta las <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
     <string name="zen_mode_until" msgid="2250286190237669079">"Hasta la(s) <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
     <string name="zen_mode_alarm" msgid="7046911727540499275">"Hasta la hora <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (próxima alarma)"</string>
@@ -2434,26 +2435,17 @@
     <string name="face_dangling_notification_msg" msgid="746235263598985384">"Ya no se puede reconocer tu modelo de rostro. Vuelve a configurar el Desbloqueo facial."</string>
     <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"Configurar"</string>
     <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"Ahora no"</string>
-    <string name="bg_user_sound_notification_title_alarm" msgid="5251678483393143527">"Alarma para <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="bg_user_sound_notification_title_alarm" msgid="5251678483393143527">"Alarma para: <xliff:g id="USER_NAME">%s</xliff:g>"</string>
     <string name="bg_user_sound_notification_button_switch_user" msgid="3091969648572788946">"Cambiar de usuario"</string>
     <string name="bg_user_sound_notification_button_mute" msgid="4942158515665615243">"Silenciar"</string>
     <string name="bg_user_sound_notification_message" msgid="8613881975316976673">"Presiona para silenciar el sonido"</string>
-    <!-- no translation found for keyboard_shortcut_group_applications_browser (6535007304687100909) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_contacts (2750702518068326356) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_email (4229037666415353683) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_sms (3523799286376321137) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_music (2051507523525651067) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calendar (3571770335653387606) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calculator (6753209559716091507) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_maps (7950000659522589471) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications (3010389163951364798) -->
-    <skip />
+    <string name="keyboard_shortcut_group_applications_browser" msgid="6535007304687100909">"Navegador"</string>
+    <string name="keyboard_shortcut_group_applications_contacts" msgid="2750702518068326356">"Contactos"</string>
+    <string name="keyboard_shortcut_group_applications_email" msgid="4229037666415353683">"Correo electrónico"</string>
+    <string name="keyboard_shortcut_group_applications_sms" msgid="3523799286376321137">"SMS"</string>
+    <string name="keyboard_shortcut_group_applications_music" msgid="2051507523525651067">"Música"</string>
+    <string name="keyboard_shortcut_group_applications_calendar" msgid="3571770335653387606">"Calendario"</string>
+    <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Calculadora"</string>
+    <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Maps"</string>
+    <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Aplicaciones"</string>
 </resources>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index f187cfd..4fa7d72 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -123,7 +123,7 @@
     <string name="roamingTextSearching" msgid="5323235489657753486">"Buscando servicio"</string>
     <string name="wfcRegErrorTitle" msgid="3193072971584858020">"No se ha podido configurar la llamada por Wi‑Fi"</string>
   <string-array name="wfcOperatorErrorAlertMessages">
-    <item msgid="468830943567116703">"Para hacer llamadas y enviar mensajes por Wi-Fi, pide antes a tu operador que configure este servicio. Una vez hecho esto, vuelva a activar la llamada por Wi-Fi en Ajustes. (Código de error: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
+    <item msgid="468830943567116703">"Para hacer llamadas y enviar mensajes por Wi-Fi, pide antes a tu operador que configure este servicio. Una vez hecho esto, vuelve a activar la llamada por Wi-Fi en Ajustes. (Código de error: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
   </string-array>
   <string-array name="wfcOperatorErrorNotificationMessages">
     <item msgid="4795145070505729156">"No se ha podido registrar la llamada por Wi‑Fi con tu operador: <xliff:g id="CODE">%1$s</xliff:g>"</item>
@@ -131,7 +131,7 @@
     <!-- no translation found for wfcSpnFormat_spn (2982505428519096311) -->
     <skip />
     <string name="wfcSpnFormat_spn_wifi_calling" msgid="3165949348000906194">"Llamada por Wi‑Fi de <xliff:g id="SPN">%s</xliff:g>"</string>
-    <string name="wfcSpnFormat_spn_wifi_calling_vo_hyphen" msgid="3836827895369365298">"<xliff:g id="SPN">%s</xliff:g> Llamada por Wi‑Fi"</string>
+    <string name="wfcSpnFormat_spn_wifi_calling_vo_hyphen" msgid="3836827895369365298">"Llamada por Wi‑Fi de <xliff:g id="SPN">%s</xliff:g>"</string>
     <string name="wfcSpnFormat_wlan_call" msgid="4895315549916165700">"Llamada por WLAN"</string>
     <string name="wfcSpnFormat_spn_wlan_call" msgid="255919245825481510">"Llamada por WLAN de <xliff:g id="SPN">%s</xliff:g>"</string>
     <string name="wfcSpnFormat_spn_wifi" msgid="7232899594327126970">"Wi‑Fi de <xliff:g id="SPN">%s</xliff:g>"</string>
@@ -159,7 +159,7 @@
     <string name="scIdentifierDisclosureIssueTitle" msgid="2898888825129970328">"Se ha accedido al ID del dispositivo"</string>
     <string name="scIdentifierDisclosureIssueSummaryNotification" msgid="3699930821270580416">"A las <xliff:g id="DISCLOSURE_TIME">%1$s</xliff:g>, una red cercana registró el ID único de tu dispositivo (IMSI o IMEI) mientras usabas la SIM de <xliff:g id="DISCLOSURE_NETWORK">%2$s</xliff:g>"</string>
     <string name="scIdentifierDisclosureIssueSummary" msgid="7283387338827749276">"A las <xliff:g id="DISCLOSURE_TIME">%1$s</xliff:g>, una red cercana registró el ID único de tu dispositivo (IMSI o IMEI) mientras usabas la SIM de <xliff:g id="DISCLOSURE_NETWORK">%2$s</xliff:g>.\n\nEsto significa que se ha registrado tu ubicación, actividad o identidad. Se trata de una práctica habitual, pero puede ser un problema para quienes les preocupa su privacidad."</string>
-    <string name="scNullCipherIssueEncryptedTitle" msgid="234717016411824969">"Conectado a la red cifrada <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</string>
+    <string name="scNullCipherIssueEncryptedTitle" msgid="234717016411824969">"Conexión a la red cifrada <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</string>
     <string name="scNullCipherIssueEncryptedSummary" msgid="8577510708842150475">"Ahora, la conexión con la SIM de <xliff:g id="NETWORK_NAME">%1$s</xliff:g> es más segura"</string>
     <string name="scNullCipherIssueNonEncryptedTitle" msgid="3978071464929453915">"Conectado a una red no cifrada"</string>
     <string name="scNullCipherIssueNonEncryptedSummaryNotification" msgid="7386936934128110388">"Tus llamadas, mensajes y datos son más vulnerables mientras uses la SIM de <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</string>
@@ -228,7 +228,7 @@
     <string name="work_profile_telephony_paused_text" msgid="8065762301100978221">"Has pausado las aplicaciones de trabajo. No recibirás llamadas ni mensajes de texto."</string>
     <string name="work_profile_telephony_paused_turn_on_button" msgid="7542632318337068821">"Reanudar aplicaciones de trabajo"</string>
     <string name="me" msgid="6207584824693813140">"Yo"</string>
-    <string name="power_dialog" product="tablet" msgid="8333207765671417261">"Opciones del tablet"</string>
+    <string name="power_dialog" product="tablet" msgid="8333207765671417261">"Opciones de la tablet"</string>
     <string name="power_dialog" product="tv" msgid="7792839006640933763">"Opciones de Android TV"</string>
     <string name="power_dialog" product="default" msgid="1107775420270203046">"Opciones del teléfono"</string>
     <string name="silent_mode" msgid="8796112363642579333">"Modo Silencio"</string>
@@ -255,7 +255,7 @@
     <string name="reboot_safemode_confirm" msgid="1658357874737219624">"¿Quieres reiniciar el sistema en modo Seguro? Se inhabilitarán todas las aplicaciones externas que hayas instalado. Esas aplicaciones se restaurarán la próxima vez que reinicies el sistema."</string>
     <string name="recent_tasks_title" msgid="8183172372995396653">"Reciente"</string>
     <string name="no_recent_tasks" msgid="9063946524312275906">"No hay aplicaciones recientes."</string>
-    <string name="global_actions" product="tablet" msgid="4412132498517933867">"Opciones del tablet"</string>
+    <string name="global_actions" product="tablet" msgid="4412132498517933867">"Opciones de la tablet"</string>
     <string name="global_actions" product="tv" msgid="3871763739487450369">"Opciones de Android TV"</string>
     <string name="global_actions" product="default" msgid="6410072189971495460">"Opciones del teléfono"</string>
     <string name="global_action_lock" msgid="6949357274257655383">"Bloqueo de pantalla"</string>
@@ -381,9 +381,9 @@
     <string name="permlab_answerPhoneCalls" msgid="4131324833663725855">"responder llamadas"</string>
     <string name="permdesc_answerPhoneCalls" msgid="894386681983116838">"Permite que la aplicación responda una llamada."</string>
     <string name="permlab_receiveSms" msgid="505961632050451881">"recibir mensajes de texto (SMS)"</string>
-    <string name="permdesc_receiveSms" msgid="1797345626687832285">"Permite que la aplicación reciba y procese mensajes MMS, lo que significa que podría utilizar este permiso para controlar o eliminar mensajes enviados al dispositivo sin mostrárselos al usuario."</string>
+    <string name="permdesc_receiveSms" msgid="1797345626687832285">"Permite que la aplicación reciba y procese mensajes SMS, lo que significa que podría utilizar este permiso para monitorizar o eliminar mensajes enviados al dispositivo sin mostrárselos al usuario."</string>
     <string name="permlab_receiveMms" msgid="4000650116674380275">"recibir mensajes de texto (MMS)"</string>
-    <string name="permdesc_receiveMms" msgid="958102423732219710">"Permite que la aplicación reciba y procese mensajes MMS, lo que significa que podría utilizar este permiso para controlar o eliminar mensajes enviados al dispositivo sin mostrárselos al usuario."</string>
+    <string name="permdesc_receiveMms" msgid="958102423732219710">"Permite que la aplicación reciba y procese mensajes MMS, lo que significa que podría usar este permiso para controlar o eliminar mensajes enviados al dispositivo sin mostrártelos."</string>
     <string name="permlab_bindCellBroadcastService" msgid="586746677002040651">"Reenviar mensajes de difusión móvil"</string>
     <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"Permite que la aplicación se vincule con el módulo de difusión móvil para reenviar los mensajes de ese tipo en cuanto se reciben. En ciertas ubicaciones se envían alertas de difusión móvil para avisar de situaciones de emergencia. Cuando se recibe una alerta de difusión móvil de emergencia, ciertas aplicaciones malintencionadas podrían interferir en el rendimiento o en el funcionamiento del dispositivo."</string>
     <string name="permlab_manageOngoingCalls" msgid="281244770664231782">"Gestionar llamadas en curso"</string>
@@ -401,13 +401,13 @@
     <string name="permdesc_readSms" product="tv" msgid="3054753345758011986">"Esta aplicación puede leer todos los SMS almacenados en tu dispositivo Android TV."</string>
     <string name="permdesc_readSms" product="default" msgid="774753371111699782">"Esta aplicación puede leer todos los SMS (mensajes de texto) almacenados en tu teléfono."</string>
     <string name="permlab_receiveWapPush" msgid="4223747702856929056">"recibir mensajes de texto (WAP)"</string>
-    <string name="permdesc_receiveWapPush" msgid="1638677888301778457">"Permite que la aplicación reciba y procese mensajes WAP, lo que significa que podría utilizar este permiso para controlar o eliminar mensajes enviados al usuario sin mostrárselos."</string>
+    <string name="permdesc_receiveWapPush" msgid="1638677888301778457">"Permite que la aplicación reciba y procese mensajes WAP, lo que significa que podría utilizar este permiso para monitorizar o eliminar mensajes enviados al usuario sin mostrárselos."</string>
     <string name="permlab_getTasks" msgid="7460048811831750262">"recuperar aplicaciones en ejecución"</string>
     <string name="permdesc_getTasks" msgid="7388138607018233726">"Permite que aplicación recupere información sobre tareas que se están ejecutando en ese momento o que se han ejecutado recientemente. La aplicación puede utilizar este permiso para descubrir cuáles son las aplicaciones que se utilizan en el dispositivo."</string>
     <string name="permlab_manageProfileAndDeviceOwners" msgid="639849495253987493">"administrar propietarios del perfil y del dispositivo"</string>
     <string name="permdesc_manageProfileAndDeviceOwners" msgid="7304240671781989283">"Permite que las aplicaciones establezcan los propietarios del perfil y del dispositivo."</string>
     <string name="permlab_reorderTasks" msgid="7598562301992923804">"reorganizar aplicaciones en ejecución"</string>
-    <string name="permdesc_reorderTasks" msgid="8796089937352344183">"Permite que la aplicación mueva tareas a segundo o a primer plano. La aplicación puede utilizar este permiso para realizar estos movimientos sin que se lo indique el usuario."</string>
+    <string name="permdesc_reorderTasks" msgid="8796089937352344183">"Permite que la aplicación mueva tareas a segundo o a primer plano. La aplicación podrá hacerlo sin que se lo indiques."</string>
     <string name="permlab_enableCarMode" msgid="893019409519325311">"habilitar modo coche"</string>
     <string name="permdesc_enableCarMode" msgid="56419168820473508">"Permite que la aplicación habilite el modo coche."</string>
     <string name="permlab_killBackgroundProcesses" msgid="6559320515561928348">"cerrar otras aplicaciones"</string>
@@ -548,7 +548,7 @@
     <string name="permlab_readPhoneNumbers" msgid="5668704794723365628">"leer números de teléfono"</string>
     <string name="permdesc_readPhoneNumbers" msgid="7368652482818338871">"Permite que la aplicación acceda a los números de teléfono del dispositivo."</string>
     <string name="permlab_wakeLock" product="automotive" msgid="1904736682319375676">"mantener la pantalla del coche encendida"</string>
-    <string name="permlab_wakeLock" product="tablet" msgid="1527660973931694000">"impedir que el tablet entre en modo de suspensión"</string>
+    <string name="permlab_wakeLock" product="tablet" msgid="1527660973931694000">"impedir que la tablet entre en modo de suspensión"</string>
     <string name="permlab_wakeLock" product="tv" msgid="2856941418123343518">"evitar que tu dispositivo Android TV entre en modo de suspensión"</string>
     <string name="permlab_wakeLock" product="default" msgid="569409726861695115">"impedir que el teléfono entre en modo de suspensión"</string>
     <string name="permdesc_wakeLock" product="automotive" msgid="5995045369683254571">"Permite que la aplicación deje la pantalla del coche encendida."</string>
@@ -556,7 +556,7 @@
     <string name="permdesc_wakeLock" product="tv" msgid="2329298966735118796">"Permite que la aplicación impida que tu dispositivo Android TV entre en modo de suspensión."</string>
     <string name="permdesc_wakeLock" product="default" msgid="3689523792074007163">"Permite que la aplicación impida que el teléfono entre en modo de suspensión."</string>
     <string name="permlab_transmitIr" msgid="8077196086358004010">"transmitir infrarrojos"</string>
-    <string name="permdesc_transmitIr" product="tablet" msgid="5884738958581810253">"Permite que la aplicación utilice el transmisor de infrarrojos del tablet."</string>
+    <string name="permdesc_transmitIr" product="tablet" msgid="5884738958581810253">"Permite que la aplicación use el transmisor de infrarrojos de la tablet."</string>
     <string name="permdesc_transmitIr" product="tv" msgid="3278506969529173281">"Permite que la aplicación utilice el transmisor de infrarrojos de tu dispositivo Android TV."</string>
     <string name="permdesc_transmitIr" product="default" msgid="8484193849295581808">"Permite que la aplicación utilice el transmisor de infrarrojos del teléfono."</string>
     <string name="permlab_setWallpaper" msgid="6959514622698794511">"establecer fondo de pantalla"</string>
@@ -586,7 +586,7 @@
     <string name="permlab_changeWifiState" msgid="7947824109713181554">"conectarse a redes Wi-Fi y desconectarse"</string>
     <string name="permdesc_changeWifiState" msgid="7170350070554505384">"Permite que la aplicación se conecte a puntos de acceso Wi-Fi y se desconecte de ellos y que realice cambios en la configuración de redes Wi-Fi del dispositivo."</string>
     <string name="permlab_changeWifiMulticastState" msgid="285626875870754696">"permitir recepción multidifusión Wi-Fi"</string>
-    <string name="permdesc_changeWifiMulticastState" product="tablet" msgid="191079868596433554">"Permite que la aplicación reciba paquetes enviados a todos los dispositivos de una red Wi-Fi que utilicen direcciones de multidifusión, no solo al tablet. Utiliza más batería que el modo de no multidifusión."</string>
+    <string name="permdesc_changeWifiMulticastState" product="tablet" msgid="191079868596433554">"Permite que la aplicación reciba paquetes enviados a todos los dispositivos de una red Wi-Fi que usen direcciones de multicast, no solo a la tablet. Usa más batería que el modo de no multicast."</string>
     <string name="permdesc_changeWifiMulticastState" product="tv" msgid="1336952358450652595">"Permite que la aplicación reciba paquetes enviados a través de una red Wi-Fi y mediante direcciones de multidifusión a todos los dispositivos, no solo a tu dispositivo Android TV. Consume más batería que el modo sin multidifusión."</string>
     <string name="permdesc_changeWifiMulticastState" product="default" msgid="8296627590220222740">"Permite que la aplicación reciba paquetes enviados a todos los dispositivos de una red Wi-Fi que utilicen direcciones de multidifusión, no solo al teléfono. Utiliza más batería que el modo de no multidifusión."</string>
     <string name="permlab_bluetoothAdmin" msgid="6490373569441946064">"acceder a los ajustes de Bluetooth"</string>
@@ -622,17 +622,17 @@
     <string name="permlab_disableKeyguard" msgid="3605253559020928505">"inhabilitar el bloqueo de pantalla"</string>
     <string name="permdesc_disableKeyguard" msgid="3223710003098573038">"Permite que la aplicación inhabilite el bloqueo del teclado y cualquier protección con contraseña asociada. Por ejemplo, el teléfono puede inhabilitar el bloqueo del teclado cuando se recibe una llamada telefónica y volver a habilitarlo cuando finaliza la llamada."</string>
     <string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"solicitar complejidad del bloqueo de pantalla"</string>
-    <string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"Permite que la aplicación entienda el nivel de complejidad del bloqueo de pantalla (alto, medio, bajo o ninguno) que indica la longitud y el tipo del bloqueo de pantalla posibles. La aplicación también puede sugerir a los usuarios que actualicen el bloqueo de pantalla para que tenga un nivel concreto de complejidad, pero los usuarios pueden ignorar la advertencia libremente. El bloqueo de pantalla no se almacena en texto sin formato, así que la aplicación no puede saber cuál es la contraseña exacta."</string>
+    <string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"Permite que la aplicación entienda el nivel de complejidad del bloqueo de pantalla (alto, medio, bajo o ninguno), que indica la longitud y el tipo del bloqueo de pantalla posibles. La aplicación también puede sugerir a los usuarios que actualicen el bloqueo de pantalla para que tenga un nivel concreto de complejidad, pero los usuarios pueden ignorar la advertencia libremente. El bloqueo de pantalla no se almacena en texto sin formato, así que la aplicación no puede saber cuál es la contraseña exacta."</string>
     <string name="permlab_postNotification" msgid="4875401198597803658">"mostrar notificaciones"</string>
     <string name="permdesc_postNotification" msgid="5974977162462877075">"Permite que la aplicación muestre notificaciones"</string>
     <string name="permlab_turnScreenOn" msgid="219344053664171492">"encender la pantalla"</string>
     <string name="permdesc_turnScreenOn" msgid="4394606875897601559">"Permite que la aplicación active la pantalla."</string>
     <string name="permlab_useBiometric" msgid="6314741124749633786">"usar hardware biométrico"</string>
     <string name="permdesc_useBiometric" msgid="7502858732677143410">"Permite que la aplicación utilice el hardware biométrico para realizar la autenticación"</string>
-    <string name="permlab_manageFingerprint" msgid="7432667156322821178">"administrar hardware de huellas digitales"</string>
+    <string name="permlab_manageFingerprint" msgid="7432667156322821178">"gestionar lector de huellas digitales"</string>
     <string name="permdesc_manageFingerprint" msgid="2025616816437339865">"Permite que la aplicación invoque métodos para añadir y eliminar plantillas de huellas digitales y utilizarlas."</string>
-    <string name="permlab_useFingerprint" msgid="1001421069766751922">"utilizar hardware de huellas digitales"</string>
-    <string name="permdesc_useFingerprint" msgid="412463055059323742">"Permite que la aplicación utilice el hardware de huellas digitales para realizar la autenticación"</string>
+    <string name="permlab_useFingerprint" msgid="1001421069766751922">"usar lectores de huellas digitales"</string>
+    <string name="permdesc_useFingerprint" msgid="412463055059323742">"Permite que la aplicación use el hardware de huellas digitales para realizar la autenticación"</string>
     <string name="permlab_audioWrite" msgid="8501705294265669405">"modificar tu colección de música"</string>
     <string name="permdesc_audioWrite" msgid="8057399517013412431">"Permite que la aplicación modifique tu colección de música."</string>
     <string name="permlab_videoWrite" msgid="5940738769586451318">"modificar tu colección de vídeos"</string>
@@ -814,10 +814,10 @@
     <string name="permlab_handoverStatus" msgid="7620438488137057281">"recibir estado de transferencias de Android Beam"</string>
     <string name="permdesc_handoverStatus" msgid="3842269451732571070">"Permite que esta aplicación reciba información sobre las transferencias actuales de Android Beam"</string>
     <string name="permlab_removeDrmCertificates" msgid="710576248717404416">"quitar certificados DRM"</string>
-    <string name="permdesc_removeDrmCertificates" msgid="4068445390318355716">"Permite a una aplicación eliminar los certificados DRM. Las aplicaciones normales no deberí­an necesitar este permiso."</string>
+    <string name="permdesc_removeDrmCertificates" msgid="4068445390318355716">"Permite que una aplicación elimine certificados DRM. Las aplicaciones normales no deberían necesitar nunca este permiso."</string>
     <string name="permlab_bindCarrierMessagingService" msgid="3363450860593096967">"vincular con el servicio de mensajería de un operador"</string>
     <string name="permdesc_bindCarrierMessagingService" msgid="6316457028173478345">"Permite vincular con la interfaz de nivel superior del servicio de mensajería de un operador. Las aplicaciones normales no deberían necesitar este permiso."</string>
-    <string name="permlab_bindCarrierServices" msgid="2395596978626237474">"vincular con servicios de operador"</string>
+    <string name="permlab_bindCarrierServices" msgid="2395596978626237474">"vincular con servicios del operador"</string>
     <string name="permdesc_bindCarrierServices" msgid="9185614481967262900">"Permite vincular con servicios de operador. Las aplicaciones normales no deberían necesitar este permiso."</string>
     <string name="permlab_access_notification_policy" msgid="5524112842876975537">"acceso a No molestar"</string>
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Permite que la aplicación lea y modifique la configuración de No molestar."</string>
@@ -856,11 +856,11 @@
     <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Borrar datos del perfil"</string>
     <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Borrar datos del usuario"</string>
     <string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Borra los datos del usuario en esta tablet sin avisar."</string>
-    <string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Eliminar los datos de este usuario del dispositivo Android TV sin previo aviso."</string>
+    <string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Eliminar los datos de este usuario que hay en este dispositivo Android TV sin previo aviso."</string>
     <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Borra los datos del perfil de este sistema de infoentretenimiento sin avisar."</string>
-    <string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Borra los datos del usuario en este teléfono sin avisar."</string>
+    <string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Eliminar los datos de este usuario que hay en este teléfono sin previo aviso."</string>
     <string name="policylab_setGlobalProxy" msgid="215332221188670221">"Definir el servidor proxy global"</string>
-    <string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Define el servidor proxy global que se debe utilizar mientras la política esté habilitada. Solo el propietario del dispositivo puede definir el proxy global."</string>
+    <string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Define el servidor proxy global que se debe usar mientras la política esté habilitada. Solo el propietario del dispositivo puede definir el proxy global."</string>
     <string name="policylab_expirePassword" msgid="6015404400532459169">"Definir caducidad contraseña"</string>
     <string name="policydesc_expirePassword" msgid="9136524319325960675">"Cambia la frecuencia con la que se debe cambiar el patrón, el PIN o la contraseña del bloqueo de pantalla."</string>
     <string name="policylab_encryptedStorage" msgid="9012936958126670110">"Cifrado del almacenamiento"</string>
@@ -966,11 +966,11 @@
     <string name="relationTypeCustom" msgid="282938315217441351">"Personalizado"</string>
     <string name="relationTypeAssistant" msgid="4057605157116589315">"Asistente"</string>
     <string name="relationTypeBrother" msgid="7141662427379247820">"Hermano"</string>
-    <string name="relationTypeChild" msgid="9076258911292693601">"Hijo"</string>
+    <string name="relationTypeChild" msgid="9076258911292693601">"Hijo/a"</string>
     <string name="relationTypeDomesticPartner" msgid="7825306887697559238">"Pareja de hecho"</string>
     <string name="relationTypeFather" msgid="3856225062864790596">"Padre"</string>
     <string name="relationTypeFriend" msgid="3192092625893980574">"Amigo"</string>
-    <string name="relationTypeManager" msgid="2272860813153171857">"Jefe"</string>
+    <string name="relationTypeManager" msgid="2272860813153171857">"Superior"</string>
     <string name="relationTypeMother" msgid="2331762740982699460">"Madre"</string>
     <string name="relationTypeParent" msgid="4177920938333039882">"Padre/madre"</string>
     <string name="relationTypePartner" msgid="4018017075116766194">"Pareja"</string>
@@ -1046,7 +1046,7 @@
     <string name="lockscreen_glogin_username_hint" msgid="6916101478673157045">"Nombre de usuario (correo electrónico)"</string>
     <string name="lockscreen_glogin_password_hint" msgid="3031027901286812848">"Contraseña"</string>
     <string name="lockscreen_glogin_submit_button" msgid="3590556636347843733">"Iniciar sesión"</string>
-    <string name="lockscreen_glogin_invalid_input" msgid="4369219936865697679">"Nombre de usuario o contraseña no válido"</string>
+    <string name="lockscreen_glogin_invalid_input" msgid="4369219936865697679">"El nombre de usuario o la contraseña no son válidos."</string>
     <string name="lockscreen_glogin_account_recovery_hint" msgid="1683405808525090649">"Si has olvidado tu nombre de usuario o tu contraseña,\naccede a la página "<b>"google.com/accounts/recovery"</b>"."</string>
     <string name="lockscreen_glogin_checking_password" msgid="2607271802803381645">"Comprobando..."</string>
     <string name="lockscreen_unlock_label" msgid="4648257878373307582">"Desbloquear"</string>
@@ -1212,7 +1212,7 @@
     <string name="not_checked" msgid="7972320087569023342">"no seleccionado"</string>
     <string name="selected" msgid="6614607926197755875">"seleccionado"</string>
     <string name="not_selected" msgid="410652016565864475">"no seleccionado"</string>
-    <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{Una estrella de {max}}many{# estrellas de {max}}other{# estrellas de {max}}}"</string>
+    <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{1 estrella de {max}}many{# estrellas de {max}}other{# estrellas de {max}}}"</string>
     <string name="in_progress" msgid="2149208189184319441">"en curso"</string>
     <string name="whichApplication" msgid="5432266899591255759">"Completar acción utilizando"</string>
     <string name="whichApplicationNamed" msgid="6969946041713975681">"Completar acción con %1$s"</string>
@@ -1279,11 +1279,11 @@
     <string name="smv_application" msgid="3775183542777792638">"La aplicación <xliff:g id="APPLICATION">%1$s</xliff:g> (proceso <xliff:g id="PROCESS">%2$s</xliff:g>) ha infringido su política StrictMode autoaplicable."</string>
     <string name="smv_process" msgid="1398801497130695446">"El proceso <xliff:g id="PROCESS">%1$s</xliff:g> ha infringido su política StrictMode autoaplicable."</string>
     <string name="android_upgrading_title" product="default" msgid="7279077384220829683">"El teléfono se está actualizando…"</string>
-    <string name="android_upgrading_title" product="tablet" msgid="4268417249079938805">"El tablet se está actualizando…"</string>
+    <string name="android_upgrading_title" product="tablet" msgid="4268417249079938805">"La tablet se está actualizando…"</string>
     <string name="android_upgrading_title" product="device" msgid="6774767702998149762">"El dispositivo se está actualizando…"</string>
     <string name="android_start_title" product="default" msgid="4036708252778757652">"El teléfono se está iniciando…"</string>
     <string name="android_start_title" product="automotive" msgid="7917984412828168079">"Android se está iniciando…"</string>
-    <string name="android_start_title" product="tablet" msgid="4429767260263190344">"El tablet se está iniciando…"</string>
+    <string name="android_start_title" product="tablet" msgid="4429767260263190344">"La tablet se está iniciando…"</string>
     <string name="android_start_title" product="device" msgid="6967413819673299309">"El dispositivo se está iniciando…"</string>
     <string name="android_upgrading_notification_title" product="default" msgid="3509927005342279257">"Finalizando actualización del sistema…"</string>
     <string name="app_upgrading_toast" msgid="1016267296049455585">"<xliff:g id="APPLICATION">%1$s</xliff:g> se está actualizando…"</string>
@@ -1429,6 +1429,7 @@
     <string name="share_remote_bugreport_action" msgid="7630880678785123682">"COMPARTIR"</string>
     <string name="decline_remote_bugreport_action" msgid="4040894777519784346">"RECHAZAR"</string>
     <string name="select_input_method" msgid="3971267998568587025">"Selecciona un método de entrada"</string>
+    <string name="input_method_language_settings" msgid="8069089418056819437">"Ajustes de idioma"</string>
     <string name="show_ime" msgid="6406112007347443383">"Mantenlo en pantalla mientras el teclado físico está activo"</string>
     <string name="hardware" msgid="3611039921284836033">"Usar teclado en pantalla"</string>
     <string name="select_keyboard_layout_notification_title" msgid="5823199895322205589">"Configura <xliff:g id="DEVICE_NAME">%s</xliff:g>"</string>
@@ -1539,7 +1540,7 @@
     <string name="vpn_text_long" msgid="278540576806169831">"Conectado a <xliff:g id="SESSION">%s</xliff:g>. Toca para administrar la red."</string>
     <string name="vpn_lockdown_connecting" msgid="6096725311950342607">"Conectando VPN siempre activada…"</string>
     <string name="vpn_lockdown_connected" msgid="2853127976590658469">"VPN siempre activada conectada"</string>
-    <string name="vpn_lockdown_disconnected" msgid="5573611651300764955">"Desconectado de VPN siempre activada"</string>
+    <string name="vpn_lockdown_disconnected" msgid="5573611651300764955">"Desconectado de VPN siempre activa"</string>
     <string name="vpn_lockdown_error" msgid="4453048646854247947">"No se ha podido establecer la conexión con la VPN siempre activada"</string>
     <string name="vpn_lockdown_config" msgid="8331697329868252169">"Cambiar ajustes de red o VPN"</string>
     <string name="upload_file" msgid="8651942222301634271">"Seleccionar archivo"</string>
@@ -1875,7 +1876,7 @@
     <string name="reason_service_unavailable" msgid="5288405248063804713">"Servicio de impresión no habilitado"</string>
     <string name="print_service_installed_title" msgid="6134880817336942482">"El servicio <xliff:g id="NAME">%s</xliff:g> se ha instalado"</string>
     <string name="print_service_installed_message" msgid="7005672469916968131">"Tocar para habilitar"</string>
-    <string name="restr_pin_enter_admin_pin" msgid="1199419462726962697">"Introducir el PIN del administrador"</string>
+    <string name="restr_pin_enter_admin_pin" msgid="1199419462726962697">"Introduce el PIN del administrador"</string>
     <string name="restr_pin_enter_pin" msgid="373139384161304555">"Introducir PIN"</string>
     <string name="restr_pin_incorrect" msgid="3861383632940852496">"Incorrecto"</string>
     <string name="restr_pin_enter_old_pin" msgid="7537079094090650967">"PIN actual"</string>
@@ -1937,7 +1938,7 @@
     <string name="zen_mode_default_events_name" msgid="2280682960128512257">"Evento"</string>
     <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Durmiendo"</string>
     <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Gestionado por <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
-    <string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Activado"</string>
+    <string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Activada"</string>
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Desactivado"</string>
     <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
     <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g>-<xliff:g id="END">%2$s</xliff:g>"</string>
@@ -1956,7 +1957,7 @@
     <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"Alerta de phishing"</string>
     <string name="notification_work_profile_content_description" msgid="5296477955677725799">"Perfil de trabajo"</string>
     <string name="notification_alerted_content_description" msgid="6139691253611265992">"Con sonido"</string>
-    <string name="notification_verified_content_description" msgid="6401483602782359391">"Verificado"</string>
+    <string name="notification_verified_content_description" msgid="6401483602782359391">"Verificada"</string>
     <string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Mostrar"</string>
     <string name="expand_button_content_description_expanded" msgid="7484217944948667489">"Ocultar"</string>
     <string name="content_description_collapsed" msgid="2827748787566489401">"Contraída"</string>
@@ -2438,22 +2439,13 @@
     <string name="bg_user_sound_notification_button_switch_user" msgid="3091969648572788946">"Cambiar de usuario"</string>
     <string name="bg_user_sound_notification_button_mute" msgid="4942158515665615243">"Silenciar"</string>
     <string name="bg_user_sound_notification_message" msgid="8613881975316976673">"Toca para silenciar el sonido"</string>
-    <!-- no translation found for keyboard_shortcut_group_applications_browser (6535007304687100909) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_contacts (2750702518068326356) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_email (4229037666415353683) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_sms (3523799286376321137) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_music (2051507523525651067) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calendar (3571770335653387606) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calculator (6753209559716091507) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_maps (7950000659522589471) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications (3010389163951364798) -->
-    <skip />
+    <string name="keyboard_shortcut_group_applications_browser" msgid="6535007304687100909">"Navegador"</string>
+    <string name="keyboard_shortcut_group_applications_contacts" msgid="2750702518068326356">"Contactos"</string>
+    <string name="keyboard_shortcut_group_applications_email" msgid="4229037666415353683">"Correo"</string>
+    <string name="keyboard_shortcut_group_applications_sms" msgid="3523799286376321137">"SMS"</string>
+    <string name="keyboard_shortcut_group_applications_music" msgid="2051507523525651067">"Música"</string>
+    <string name="keyboard_shortcut_group_applications_calendar" msgid="3571770335653387606">"Calendar"</string>
+    <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Calculadora"</string>
+    <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Maps"</string>
+    <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Aplicaciones"</string>
 </resources>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index 5a618d6..cfcab8c 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -1428,6 +1428,7 @@
     <string name="share_remote_bugreport_action" msgid="7630880678785123682">"JAGA"</string>
     <string name="decline_remote_bugreport_action" msgid="4040894777519784346">"KEELDU"</string>
     <string name="select_input_method" msgid="3971267998568587025">"Valige sisestusmeetod"</string>
+    <string name="input_method_language_settings" msgid="8069089418056819437">"Keeleseaded"</string>
     <string name="show_ime" msgid="6406112007347443383">"Hoia seda ekraanil, kui füüsiline klaviatuur on aktiivne"</string>
     <string name="hardware" msgid="3611039921284836033">"Kasuta ekraaniklaviatuuri"</string>
     <string name="select_keyboard_layout_notification_title" msgid="5823199895322205589">"Seadistage <xliff:g id="DEVICE_NAME">%s</xliff:g>"</string>
@@ -2437,22 +2438,13 @@
     <string name="bg_user_sound_notification_button_switch_user" msgid="3091969648572788946">"Vaheta kasutajat"</string>
     <string name="bg_user_sound_notification_button_mute" msgid="4942158515665615243">"Vaigista"</string>
     <string name="bg_user_sound_notification_message" msgid="8613881975316976673">"Puudutage heli vaigistamiseks"</string>
-    <!-- no translation found for keyboard_shortcut_group_applications_browser (6535007304687100909) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_contacts (2750702518068326356) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_email (4229037666415353683) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_sms (3523799286376321137) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_music (2051507523525651067) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calendar (3571770335653387606) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calculator (6753209559716091507) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_maps (7950000659522589471) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications (3010389163951364798) -->
-    <skip />
+    <string name="keyboard_shortcut_group_applications_browser" msgid="6535007304687100909">"Brauser"</string>
+    <string name="keyboard_shortcut_group_applications_contacts" msgid="2750702518068326356">"Kontaktid"</string>
+    <string name="keyboard_shortcut_group_applications_email" msgid="4229037666415353683">"E-post"</string>
+    <string name="keyboard_shortcut_group_applications_sms" msgid="3523799286376321137">"SMS"</string>
+    <string name="keyboard_shortcut_group_applications_music" msgid="2051507523525651067">"Muusika"</string>
+    <string name="keyboard_shortcut_group_applications_calendar" msgid="3571770335653387606">"Kalender"</string>
+    <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Kalkulaator"</string>
+    <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Kaardid"</string>
+    <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Rakendused"</string>
 </resources>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index 5819289..43ab92c 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -1428,6 +1428,7 @@
     <string name="share_remote_bugreport_action" msgid="7630880678785123682">"PARTEKATU"</string>
     <string name="decline_remote_bugreport_action" msgid="4040894777519784346">"BAZTERTU"</string>
     <string name="select_input_method" msgid="3971267998568587025">"Aukeratu idazketa-metodoa"</string>
+    <string name="input_method_language_settings" msgid="8069089418056819437">"Hizkuntza-ezarpenak"</string>
     <string name="show_ime" msgid="6406112007347443383">"Erakutsi pantailan teklatu fisikoa aktibo dagoen bitartean"</string>
     <string name="hardware" msgid="3611039921284836033">"Erabili pantailako teklatua"</string>
     <string name="select_keyboard_layout_notification_title" msgid="5823199895322205589">"Konfiguratu <xliff:g id="DEVICE_NAME">%s</xliff:g>"</string>
@@ -2437,22 +2438,13 @@
     <string name="bg_user_sound_notification_button_switch_user" msgid="3091969648572788946">"Aldatu erabiltzailea"</string>
     <string name="bg_user_sound_notification_button_mute" msgid="4942158515665615243">"Desaktibatu audioa"</string>
     <string name="bg_user_sound_notification_message" msgid="8613881975316976673">"Sakatu audioa desaktibatzeko"</string>
-    <!-- no translation found for keyboard_shortcut_group_applications_browser (6535007304687100909) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_contacts (2750702518068326356) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_email (4229037666415353683) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_sms (3523799286376321137) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_music (2051507523525651067) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calendar (3571770335653387606) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calculator (6753209559716091507) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_maps (7950000659522589471) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications (3010389163951364798) -->
-    <skip />
+    <string name="keyboard_shortcut_group_applications_browser" msgid="6535007304687100909">"Arakatzailea"</string>
+    <string name="keyboard_shortcut_group_applications_contacts" msgid="2750702518068326356">"Kontaktuak"</string>
+    <string name="keyboard_shortcut_group_applications_email" msgid="4229037666415353683">"Posta elektronikoa"</string>
+    <string name="keyboard_shortcut_group_applications_sms" msgid="3523799286376321137">"SMSak"</string>
+    <string name="keyboard_shortcut_group_applications_music" msgid="2051507523525651067">"Musika"</string>
+    <string name="keyboard_shortcut_group_applications_calendar" msgid="3571770335653387606">"Calendar"</string>
+    <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Kalkulagailua"</string>
+    <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Maps"</string>
+    <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Aplikazioak"</string>
 </resources>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 7a08af6..5d3d42b 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -1428,6 +1428,7 @@
     <string name="share_remote_bugreport_action" msgid="7630880678785123682">"اشتراک‌گذاری"</string>
     <string name="decline_remote_bugreport_action" msgid="4040894777519784346">"نپذیرفتن"</string>
     <string name="select_input_method" msgid="3971267998568587025">"انتخاب روش ورودی"</string>
+    <string name="input_method_language_settings" msgid="8069089418056819437">"تنظیمات زبان"</string>
     <string name="show_ime" msgid="6406112007347443383">"وقتی صفحه‌کلید فیزیکی فعال است این ویرایشگر را روی صفحه نگه‌می‌دارد"</string>
     <string name="hardware" msgid="3611039921284836033">"استفاده از صفحه‌کلید مجازی"</string>
     <string name="select_keyboard_layout_notification_title" msgid="5823199895322205589">"پیکربندی <xliff:g id="DEVICE_NAME">%s</xliff:g>"</string>
@@ -2433,26 +2434,17 @@
     <string name="face_dangling_notification_msg" msgid="746235263598985384">"مدل چهره‌تان دیگر قابل‌شناسایی نیست. «قفل‌گشایی با چهره» را دوباره راه‌اندازی کنید."</string>
     <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"راه‌اندازی"</string>
     <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"حالا نه"</string>
-    <string name="bg_user_sound_notification_title_alarm" msgid="5251678483393143527">"زنگ ساعت <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="bg_user_sound_notification_title_alarm" msgid="5251678483393143527">"زنگ هشدار برای <xliff:g id="USER_NAME">%s</xliff:g>"</string>
     <string name="bg_user_sound_notification_button_switch_user" msgid="3091969648572788946">"تغییر کاربر"</string>
     <string name="bg_user_sound_notification_button_mute" msgid="4942158515665615243">"بی‌صدا کردن"</string>
-    <string name="bg_user_sound_notification_message" msgid="8613881975316976673">"برای بی‌صدا کردن تک‌ضرب بزنید"</string>
-    <!-- no translation found for keyboard_shortcut_group_applications_browser (6535007304687100909) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_contacts (2750702518068326356) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_email (4229037666415353683) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_sms (3523799286376321137) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_music (2051507523525651067) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calendar (3571770335653387606) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calculator (6753209559716091507) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_maps (7950000659522589471) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications (3010389163951364798) -->
-    <skip />
+    <string name="bg_user_sound_notification_message" msgid="8613881975316976673">"برای بی‌صدا کردن، تک‌ضرب بزنید"</string>
+    <string name="keyboard_shortcut_group_applications_browser" msgid="6535007304687100909">"مرورگر"</string>
+    <string name="keyboard_shortcut_group_applications_contacts" msgid="2750702518068326356">"مخاطبین"</string>
+    <string name="keyboard_shortcut_group_applications_email" msgid="4229037666415353683">"ایمیل"</string>
+    <string name="keyboard_shortcut_group_applications_sms" msgid="3523799286376321137">"پیامک"</string>
+    <string name="keyboard_shortcut_group_applications_music" msgid="2051507523525651067">"موسیقی"</string>
+    <string name="keyboard_shortcut_group_applications_calendar" msgid="3571770335653387606">"تقویم"</string>
+    <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"ماشین‌حساب"</string>
+    <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"نقشه"</string>
+    <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"برنامه‌ها"</string>
 </resources>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 8393e9a..67dd02b 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -1428,6 +1428,7 @@
     <string name="share_remote_bugreport_action" msgid="7630880678785123682">"JAA"</string>
     <string name="decline_remote_bugreport_action" msgid="4040894777519784346">"HYLKÄÄ"</string>
     <string name="select_input_method" msgid="3971267998568587025">"Valitse syöttötapa"</string>
+    <string name="input_method_language_settings" msgid="8069089418056819437">"Kieliasetukset"</string>
     <string name="show_ime" msgid="6406112007347443383">"Pidä näytöllä, kun fyysinen näppäimistö on aktiivinen"</string>
     <string name="hardware" msgid="3611039921284836033">"Käytä näyttönäppäimistöä"</string>
     <string name="select_keyboard_layout_notification_title" msgid="5823199895322205589">"Määritä <xliff:g id="DEVICE_NAME">%s</xliff:g>"</string>
@@ -2437,22 +2438,13 @@
     <string name="bg_user_sound_notification_button_switch_user" msgid="3091969648572788946">"Vaihda käyttäjää"</string>
     <string name="bg_user_sound_notification_button_mute" msgid="4942158515665615243">"Mykistä"</string>
     <string name="bg_user_sound_notification_message" msgid="8613881975316976673">"Mykistä äänet napauttamalla"</string>
-    <!-- no translation found for keyboard_shortcut_group_applications_browser (6535007304687100909) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_contacts (2750702518068326356) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_email (4229037666415353683) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_sms (3523799286376321137) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_music (2051507523525651067) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calendar (3571770335653387606) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calculator (6753209559716091507) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_maps (7950000659522589471) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications (3010389163951364798) -->
-    <skip />
+    <string name="keyboard_shortcut_group_applications_browser" msgid="6535007304687100909">"Selain"</string>
+    <string name="keyboard_shortcut_group_applications_contacts" msgid="2750702518068326356">"Yhteystiedot"</string>
+    <string name="keyboard_shortcut_group_applications_email" msgid="4229037666415353683">"Sähköposti"</string>
+    <string name="keyboard_shortcut_group_applications_sms" msgid="3523799286376321137">"Tekstiviesti"</string>
+    <string name="keyboard_shortcut_group_applications_music" msgid="2051507523525651067">"Musiikki"</string>
+    <string name="keyboard_shortcut_group_applications_calendar" msgid="3571770335653387606">"Kalenteri"</string>
+    <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Laskin"</string>
+    <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Maps"</string>
+    <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Sovellukset"</string>
 </resources>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index a0f8ec0..7f99750 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -373,7 +373,7 @@
     <string name="permlab_fullScreenIntent" msgid="4310888199502509104">"afficher les notifications en mode plein écran sur un appareil verrouillé"</string>
     <string name="permdesc_fullScreenIntent" msgid="1100721419406643997">"Permet à l\'appli d\'afficher les notifications en mode plein écran sur un appareil verrouillé."</string>
     <string name="permlab_install_shortcut" msgid="7451554307502256221">"Installer des raccourcis"</string>
-    <string name="permdesc_install_shortcut" msgid="4476328467240212503">"Permet à une appli d\'ajouter des raccourcis sans l\'intervention de l\'utilisateur."</string>
+    <string name="permdesc_install_shortcut" msgid="4476328467240212503">"Permet à une application d\'ajouter des raccourcis à l\'écran d\'accueil sans l\'intervention de l\'utilisateur."</string>
     <string name="permlab_uninstall_shortcut" msgid="295263654781900390">"désinstaller des raccourcis"</string>
     <string name="permdesc_uninstall_shortcut" msgid="1924735350988629188">"Permet à l\'appli de supprimer des raccourcis de la page d\'accueil sans intervention de l\'utilisateur."</string>
     <string name="permlab_processOutgoingCalls" msgid="4075056020714266558">"transférer les appels sortants"</string>
@@ -622,7 +622,7 @@
     <string name="permlab_disableKeyguard" msgid="3605253559020928505">"désactiver le verrouillage de l\'écran"</string>
     <string name="permdesc_disableKeyguard" msgid="3223710003098573038">"Permet à l\'appli de désactiver le verrouillage des touches et toute mesure de sécurité par mot de passe associée. Par exemple, votre téléphone désactive le verrouillage des touches lorsque vous recevez un appel, puis le réactive lorsque vous raccrochez."</string>
     <string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"demander la complexité du verrouillage d\'écran"</string>
-    <string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"Autorise l\'appli à apprendre le niveau de complexité de l\'écran de verrouillage (élevé, moyen, faible ou aucun), qui indique la gamme possible de longueur et de type de verrouillage d\'écran. L\'appli peut aussi suggérer aux utilisateurs de mettre à jour l\'écran de verrouillage afin d\'utiliser un certain niveau de complexité, mais ils peuvent ignorer la suggestion. Notez que le verrouillage d\'écran n\'est pas stocké en texte brut pour de manière à ce que l\'appli n\'ait pas accès au mot de passe exact."</string>
+    <string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"Autorise l\'appli à apprendre le niveau de complexité du Verrouillage de l\'écran (élevé, moyen, faible ou aucun), ce qui indique les différentes possibilités de longueur et de type de verrouillage d\'écran. L\'appli peut aussi suggérer aux utilisateurs de mettre à jour le Verrouillage de l\'écran afin d\'atteindre un certain niveau de complexité, mais ces derniers peuvent ignorer la suggestion. Notez que le Verrouillage de l\'écran n\'est pas stocké en texte brut pour que l\'appli n\'ait pas accès au mot de passe exact."</string>
     <string name="permlab_postNotification" msgid="4875401198597803658">"afficher les notifications"</string>
     <string name="permdesc_postNotification" msgid="5974977162462877075">"Permet à l\'appli d\'afficher les notifications"</string>
     <string name="permlab_turnScreenOn" msgid="219344053664171492">"allumer l\'écran"</string>
@@ -740,7 +740,7 @@
     <string name="face_error_timeout" msgid="2598544068593889762">"Réessayez déverrouillage reconnaissance faciale"</string>
     <string name="face_error_no_space" msgid="5649264057026021723">"Impossible de stocker de nouveaux visages. Supprimez-en un."</string>
     <string name="face_error_canceled" msgid="2164434737103802131">"Opération de reconnaissance du visage annulée."</string>
-    <string name="face_error_user_canceled" msgid="5766472033202928373">"Le déverrouillage par reconnaissance faciale a été annulé"</string>
+    <string name="face_error_user_canceled" msgid="5766472033202928373">"Déverrouillage par reconnaissance faciale annulé par l\'utilisateur"</string>
     <string name="face_error_lockout" msgid="7864408714994529437">"Trop de tentatives. Veuillez réessayer plus tard."</string>
     <string name="face_error_lockout_permanent" msgid="8533257333130473422">"Trop de tentatives. Le déverrouillage par reconnaissance faciale est inaccessible."</string>
     <string name="face_error_lockout_screen_lock" msgid="5062609811636860928">"Trop de tentatives. Entrez plutôt le verrouillage de l\'écran."</string>
@@ -829,7 +829,7 @@
     <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Permet au détenteur de commencer à afficher les renseignements sur les fonctionnalités d\'une appli."</string>
     <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"accéder aux données des capteurs à un taux d’échantillonnage élevé"</string>
     <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Permet à l’appli d’échantillonner les données des capteurs à une fréquence supérieure à 200 Hz"</string>
-    <string name="permlab_updatePackagesWithoutUserAction" msgid="3363272609642618551">"Mettre à jour l\'appli sans intervention de l\'utilisateur"</string>
+    <string name="permlab_updatePackagesWithoutUserAction" msgid="3363272609642618551">"mettre à jour l\'appli sans intervention de l\'utilisateur"</string>
     <string name="permdesc_updatePackagesWithoutUserAction" msgid="4567739631260526366">"Permet à une appli précédemment installée de se mettre à jour sans intervention de l\'utilisateur"</string>
     <string name="permlab_writeVerificationStateE2eeContactKeys" msgid="3990742344778360457">"mettre à jour les états des vérifications des clés de contact chiffrées de bout en bout qui appartiennent à d\'autres applis"</string>
     <string name="permdesc_writeVerificationStateE2eeContactKeys" msgid="8453156829747427041">"Autorise l\'appli à mettre à jour les états des vérifications des clés de contact chiffrées de bout en bout qui appartiennent à d\'autres applis"</string>
@@ -977,7 +977,7 @@
     <string name="relationTypeReferredBy" msgid="5285082289602849400">"Recommandé par"</string>
     <string name="relationTypeRelative" msgid="3396498519818009134">"Proche"</string>
     <string name="relationTypeSister" msgid="3721676005094140671">"Sœur"</string>
-    <string name="relationTypeSpouse" msgid="6916682664436031703">"Conjoint(e)"</string>
+    <string name="relationTypeSpouse" msgid="6916682664436031703">"Conjoint/Conjointe"</string>
     <string name="sipAddressTypeCustom" msgid="6283889809842649336">"Personnaliser"</string>
     <string name="sipAddressTypeHome" msgid="5918441930656878367">"Domicile"</string>
     <string name="sipAddressTypeWork" msgid="7873967986701216770">"Travail"</string>
@@ -987,7 +987,7 @@
     <string name="keyguard_password_enter_puk_code" msgid="3112256684547584093">"Saisissez la clé PUK et le nouveau NIP."</string>
     <string name="keyguard_password_enter_puk_prompt" msgid="2825313071899938305">"Clé PUK"</string>
     <string name="keyguard_password_enter_pin_prompt" msgid="5505434724229581207">"Nouveau NIP"</string>
-    <string name="keyguard_password_entry_touch_hint" msgid="4032288032993261520"><font size="17">"Touchez pour entrer le m. de p."</font></string>
+    <string name="keyguard_password_entry_touch_hint" msgid="4032288032993261520"><font size="17">"Touch. pour taper mot de passe"</font></string>
     <string name="keyguard_password_enter_password_code" msgid="2751130557661643482">"Saisissez le mot de passe pour déverrouiller le clavier."</string>
     <string name="keyguard_password_enter_pin_password_code" msgid="7792964196473964340">"Saisissez le NIP pour déverrouiller le clavier."</string>
     <string name="keyguard_password_wrong_pin_code" msgid="8583732939138432793">"NIP erroné."</string>
@@ -1023,7 +1023,7 @@
     <string name="emergency_calls_only" msgid="3057351206678279851">"Appels d\'urgence uniquement"</string>
     <string name="lockscreen_network_locked_message" msgid="2814046965899249635">"Réseau verrouillé"</string>
     <string name="lockscreen_sim_puk_locked_message" msgid="2867953953604224166">"La carte SIM est verrouillée par clé PUK."</string>
-    <string name="lockscreen_sim_puk_locked_instructions" msgid="5307979043730860995">"Veuillez consulter le guide d\'utilisation ou contacter le service à la clientèle."</string>
+    <string name="lockscreen_sim_puk_locked_instructions" msgid="5307979043730860995">"Veuillez consulter le guide d\'utilisation ou contacter l\'assistance à la clientèle."</string>
     <string name="lockscreen_sim_locked_message" msgid="5911944931911850164">"La carte SIM est verrouillée."</string>
     <string name="lockscreen_sim_unlock_progress_dialog_message" msgid="8381565919325410939">"Déverrouillage de la carte SIM en cours…"</string>
     <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6458790975898594240">"Vous avez dessiné un schéma de déverrouillage incorrect à <xliff:g id="NUMBER_0">%1$d</xliff:g> reprises.\n\nVeuillez réessayer dans <xliff:g id="NUMBER_1">%2$d</xliff:g> secondes."</string>
@@ -1160,7 +1160,7 @@
     <string name="duration_years_relative" msgid="8731202348869424370">"{count,plural, =1{Il y a # an}one{Il y a # an}many{Il y a # ans}other{Il y a # ans}}"</string>
     <string name="duration_minutes_relative_future" msgid="5259574171747708115">"{count,plural, =1{# minute}one{# minute}many{# minutes}other{# minutes}}"</string>
     <string name="duration_hours_relative_future" msgid="6670440478481140565">"{count,plural, =1{# heure}one{# heure}many{# heures}other{# heures}}"</string>
-    <string name="duration_days_relative_future" msgid="8870658635774250746">"{count,plural, =1{# jour}one{# jour}many{# jours}other{# jours}}"</string>
+    <string name="duration_days_relative_future" msgid="8870658635774250746">"{count,plural, =1{# jour}one{# jour}many{# de jours}other{# jours}}"</string>
     <string name="duration_years_relative_future" msgid="8855853883925918380">"{count,plural, =1{# an}one{# an}many{# ans}other{# ans}}"</string>
     <string name="VideoView_error_title" msgid="5750686717225068016">"Problème vidéo"</string>
     <string name="VideoView_error_text_invalid_progressive_playback" msgid="3782449246085134720">"Impossible de lire cette vidéo en continu sur cet appareil."</string>
@@ -1208,8 +1208,8 @@
     <string name="loading" msgid="3138021523725055037">"Chargement en cours..."</string>
     <string name="capital_on" msgid="2770685323900821829">"ACTIVÉ"</string>
     <string name="capital_off" msgid="7443704171014626777">"DÉSACTIVÉ"</string>
-    <string name="checked" msgid="9179896827054513119">"coché"</string>
-    <string name="not_checked" msgid="7972320087569023342">"non coché"</string>
+    <string name="checked" msgid="9179896827054513119">"Coché"</string>
+    <string name="not_checked" msgid="7972320087569023342">"Non coché"</string>
     <string name="selected" msgid="6614607926197755875">"sélectionné"</string>
     <string name="not_selected" msgid="410652016565864475">"non sélectionné"</string>
     <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{Une étoile sur {max}}one{# étoile sur {max}}many{# d\'étoiles sur {max}}other{# étoiles sur {max}}}"</string>
@@ -1328,7 +1328,7 @@
     <string name="volume_icon_description_notification" msgid="579091344110747279">"Volume des notifications"</string>
     <string name="ringtone_default" msgid="9118299121288174597">"Sonnerie par défaut"</string>
     <string name="ringtone_default_with_actual" msgid="2709686194556159773">"Défaut (<xliff:g id="ACTUAL_RINGTONE">%1$s</xliff:g>)"</string>
-    <string name="ringtone_silent" msgid="397111123930141876">"Aucun(e)"</string>
+    <string name="ringtone_silent" msgid="397111123930141876">"Aucune"</string>
     <string name="ringtone_picker_title" msgid="667342618626068253">"Sonneries"</string>
     <string name="ringtone_picker_title_alarm" msgid="7438934548339024767">"Sons d\'alarme"</string>
     <string name="ringtone_picker_title_notification" msgid="6387191794719608122">"Sons de notification"</string>
@@ -1417,7 +1417,7 @@
     <string name="console_running_notification_title" msgid="6087888939261635904">"La console série est activée"</string>
     <string name="console_running_notification_message" msgid="7892751888125174039">"La performance est réduite. Pour désactiver cette fonction, vérifier le programme d\'amorçage."</string>
     <string name="mte_override_notification_title" msgid="4731115381962792944">"Extension MTE expérimentale activée"</string>
-    <string name="mte_override_notification_message" msgid="2441170442725738942">"La performance et la stabilité pourraient être affectées. Redémarrez l\'appareil pour la désactiver. Si vous l\'activez au moyen de la propriété arm64.memtag.bootctl, définissez d\'abord celle-ci à « none »."</string>
+    <string name="mte_override_notification_message" msgid="2441170442725738942">"La performance et la stabilité pourraient être affectées. Redémarrez pour désactiver. Si vous l\'activez au moyen de la propriété arm64.memtag.bootctl, définissez d\'abord celle-ci à « none »."</string>
     <string name="usb_contaminant_detected_title" msgid="4359048603069159678">"Liquide ou débris dans le port USB"</string>
     <string name="usb_contaminant_detected_message" msgid="7346100585390795743">"Le port USB est désactivé automatiquement. Touchez ici pour en savoir plus."</string>
     <string name="usb_contaminant_not_detected_title" msgid="2651167729563264053">"Autorisation d\'utiliser le port USB"</string>
@@ -1429,6 +1429,7 @@
     <string name="share_remote_bugreport_action" msgid="7630880678785123682">"PARTAGER"</string>
     <string name="decline_remote_bugreport_action" msgid="4040894777519784346">"REFUSER"</string>
     <string name="select_input_method" msgid="3971267998568587025">"Sélectionnez le mode de saisie"</string>
+    <string name="input_method_language_settings" msgid="8069089418056819437">"Paramètres de langue"</string>
     <string name="show_ime" msgid="6406112007347443383">"Afficher lorsque le clavier physique est activé"</string>
     <string name="hardware" msgid="3611039921284836033">"Utiliser le clavier à l\'écran"</string>
     <string name="select_keyboard_layout_notification_title" msgid="5823199895322205589">"Configurer <xliff:g id="DEVICE_NAME">%s</xliff:g>"</string>
@@ -1473,23 +1474,23 @@
     <string name="ext_media_seamless_action" msgid="8837030226009268080">"Changer de sortie"</string>
     <string name="ext_media_missing_title" msgid="3209472091220515046">"Mémoire de stockage <xliff:g id="NAME">%s</xliff:g> manquante"</string>
     <string name="ext_media_missing_message" msgid="4408988706227922909">"Insérez l\'appareil de nouveau"</string>
-    <string name="ext_media_move_specific_title" msgid="8492118544775964250">"Déplacement de <xliff:g id="NAME">%s</xliff:g> en cours..."</string>
+    <string name="ext_media_move_specific_title" msgid="8492118544775964250">"Déplacement de <xliff:g id="NAME">%s</xliff:g> en cours…"</string>
     <string name="ext_media_move_title" msgid="2682741525619033637">"Déplacement des données..."</string>
     <string name="ext_media_move_success_title" msgid="4901763082647316767">"Transfert de contenu terminé"</string>
     <string name="ext_media_move_success_message" msgid="9159542002276982979">"Contenu déplacé vers <xliff:g id="NAME">%s</xliff:g>"</string>
     <string name="ext_media_move_failure_title" msgid="3184577479181333665">"Impossible de déplacer le contenu"</string>
     <string name="ext_media_move_failure_message" msgid="4197306718121869335">"Essayez de déplacer le contenu de nouveau"</string>
-    <string name="ext_media_status_removed" msgid="241223931135751691">"Supprimée"</string>
+    <string name="ext_media_status_removed" msgid="241223931135751691">"Supprimé"</string>
     <string name="ext_media_status_unmounted" msgid="8145812017295835941">"Éjectée"</string>
     <string name="ext_media_status_checking" msgid="159013362442090347">"Vérification en cours…"</string>
-    <string name="ext_media_status_mounted" msgid="3459448555811203459">"Prête"</string>
+    <string name="ext_media_status_mounted" msgid="3459448555811203459">"Prêt"</string>
     <string name="ext_media_status_mounted_ro" msgid="1974809199760086956">"En lecture seule"</string>
     <string name="ext_media_status_bad_removal" msgid="508448566481406245">"Retrait risqué"</string>
     <string name="ext_media_status_unmountable" msgid="7043574843541087748">"Corrompue"</string>
     <string name="ext_media_status_unsupported" msgid="5460509911660539317">"Non compatible"</string>
     <string name="ext_media_status_ejecting" msgid="7532403368044013797">"Éjection en cours..."</string>
     <string name="ext_media_status_formatting" msgid="774148701503179906">"Formatage en cours..."</string>
-    <string name="ext_media_status_missing" msgid="6520746443048867314">"Non insérée"</string>
+    <string name="ext_media_status_missing" msgid="6520746443048867314">"Non inséré"</string>
     <string name="activity_list_empty" msgid="4219430010716034252">"Aucune activité correspondante trouvée."</string>
     <string name="permlab_route_media_output" msgid="8048124531439513118">"diriger la sortie multimédia"</string>
     <string name="permdesc_route_media_output" msgid="1759683269387729675">"Permet à une appli de diriger la sortie multimédia vers d\'autres appareils externes."</string>
@@ -1508,7 +1509,7 @@
     <string name="ime_action_go" msgid="5536744546326495436">"Aller"</string>
     <string name="ime_action_search" msgid="4501435960587287668">"Rechercher"</string>
     <string name="ime_action_send" msgid="8456843745664334138">"Envoyer"</string>
-    <string name="ime_action_next" msgid="4169702997635728543">"Suivante"</string>
+    <string name="ime_action_next" msgid="4169702997635728543">"Suivant"</string>
     <string name="ime_action_done" msgid="6299921014822891569">"Terminé"</string>
     <string name="ime_action_previous" msgid="6548799326860401611">"Précédente"</string>
     <string name="ime_action_default" msgid="8265027027659800121">"Exécuter"</string>
@@ -1536,7 +1537,7 @@
     <string name="vpn_title" msgid="5906991595291514182">"RPV activé"</string>
     <string name="vpn_title_long" msgid="6834144390504619998">"RPV activé par <xliff:g id="APP">%s</xliff:g>"</string>
     <string name="vpn_text" msgid="2275388920267251078">"Appuyez ici pour gérer le réseau."</string>
-    <string name="vpn_text_long" msgid="278540576806169831">"Connecté à <xliff:g id="SESSION">%s</xliff:g>. Appuyez ici pour gérer le réseau."</string>
+    <string name="vpn_text_long" msgid="278540576806169831">"Connecté à <xliff:g id="SESSION">%s</xliff:g>. Touchez ici pour gérer le réseau."</string>
     <string name="vpn_lockdown_connecting" msgid="6096725311950342607">"RPV permanent en cours de connexion…"</string>
     <string name="vpn_lockdown_connected" msgid="2853127976590658469">"RPV permanent connecté"</string>
     <string name="vpn_lockdown_disconnected" msgid="5573611651300764955">"Déconnecté du RPV permanent"</string>
@@ -1549,7 +1550,7 @@
     <string name="car_mode_disable_notification_title" msgid="8450693275833142896">"L\'appli de conduite est en cours d\'exécution"</string>
     <string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Touchez pour quitter l\'appli de conduite."</string>
     <string name="back_button_label" msgid="4078224038025043387">"Précédent"</string>
-    <string name="next_button_label" msgid="6040209156399907780">"Suivante"</string>
+    <string name="next_button_label" msgid="6040209156399907780">"Suivant"</string>
     <string name="skip_button_label" msgid="3566599811326688389">"Ignorer"</string>
     <string name="no_matches" msgid="6472699895759164599">"Aucune partie"</string>
     <string name="find_on_page" msgid="5400537367077438198">"Rechercher sur la page"</string>
@@ -1938,7 +1939,7 @@
     <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Sommeil"</string>
     <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Géré par <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Activé"</string>
-    <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Désactivé"</string>
+    <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Désactivée"</string>
     <string name="zen_mode_trigger_summary_divider_text" msgid="7461583466043698862">", "</string>
     <string name="zen_mode_trigger_summary_range_symbol_combination" msgid="1804900738798069619">"<xliff:g id="START">%1$s</xliff:g> – <xliff:g id="END">%2$s</xliff:g>"</string>
     <string name="zen_mode_trigger_event_calendar_any" msgid="2086784607921121803">"N\'importe quel agenda"</string>
@@ -2150,7 +2151,7 @@
     <string name="nas_upgrade_notification_enable_action" msgid="3046406808378726874">"OK"</string>
     <string name="nas_upgrade_notification_disable_action" msgid="3794833210043497982">"Désactiver"</string>
     <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"En savoir plus"</string>
-    <string name="nas_upgrade_notification_learn_more_content" msgid="3735480566983530650">"Les notifications améliorées ont remplacé les notifications adaptatives Android sous Android 12. Cette fonctionnalité vous présente des suggestions d\'actions et de réponses, et organise vos notifications.\n\nLes notifications améliorées peuvent accéder au contenu de toutes les notifications, y compris les renseignements personnels comme le nom des contacts et les messages. Cette fonctionnalité peut aussi fermer des notifications ou interagir avec elles, comme répondre aux appels téléphoniques et gérer le mode Ne pas déranger."</string>
+    <string name="nas_upgrade_notification_learn_more_content" msgid="3735480566983530650">"Les notifications améliorées ont remplacé les notifications adaptatives Android sous Android 12. Cette fonctionnalité vous présente des suggestions d\'actions et de réponse, et organise vos notifications.\n\nLes notifications améliorées peuvent accéder au contenu de toutes les notifications, y compris les renseignements personnels comme le nom des contacts et les messages. Cette fonctionnalité peut aussi fermer des notifications ou interagir avec elles, comme répondre aux appels téléphoniques et gérer le mode Ne pas déranger."</string>
     <string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Notification d\'information du mode Routine"</string>
     <string name="dynamic_mode_notification_title" msgid="1388718452788985481">"Économiseur de pile activé"</string>
     <string name="dynamic_mode_notification_summary" msgid="1639031262484979689">"Réduction de l\'utilisation de la pile pour en prolonger l\'autonomie"</string>
@@ -2437,23 +2438,14 @@
     <string name="bg_user_sound_notification_title_alarm" msgid="5251678483393143527">"Alarme pour <xliff:g id="USER_NAME">%s</xliff:g>"</string>
     <string name="bg_user_sound_notification_button_switch_user" msgid="3091969648572788946">"Changer d\'utilisateur"</string>
     <string name="bg_user_sound_notification_button_mute" msgid="4942158515665615243">"Désactiver le son"</string>
-    <string name="bg_user_sound_notification_message" msgid="8613881975316976673">"Toucher pour désactiver le son"</string>
-    <!-- no translation found for keyboard_shortcut_group_applications_browser (6535007304687100909) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_contacts (2750702518068326356) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_email (4229037666415353683) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_sms (3523799286376321137) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_music (2051507523525651067) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calendar (3571770335653387606) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calculator (6753209559716091507) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_maps (7950000659522589471) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications (3010389163951364798) -->
-    <skip />
+    <string name="bg_user_sound_notification_message" msgid="8613881975316976673">"Touchez pour désactiver le son"</string>
+    <string name="keyboard_shortcut_group_applications_browser" msgid="6535007304687100909">"Navigateur"</string>
+    <string name="keyboard_shortcut_group_applications_contacts" msgid="2750702518068326356">"Contacts"</string>
+    <string name="keyboard_shortcut_group_applications_email" msgid="4229037666415353683">"Courriel"</string>
+    <string name="keyboard_shortcut_group_applications_sms" msgid="3523799286376321137">"Messages texte"</string>
+    <string name="keyboard_shortcut_group_applications_music" msgid="2051507523525651067">"Musique"</string>
+    <string name="keyboard_shortcut_group_applications_calendar" msgid="3571770335653387606">"Agenda"</string>
+    <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Calculatrice"</string>
+    <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Maps"</string>
+    <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Applications"</string>
 </resources>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 0f58c47..1711b286 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -1429,6 +1429,7 @@
     <string name="share_remote_bugreport_action" msgid="7630880678785123682">"PARTAGER"</string>
     <string name="decline_remote_bugreport_action" msgid="4040894777519784346">"REFUSER"</string>
     <string name="select_input_method" msgid="3971267998568587025">"Sélectionnez le mode de saisie"</string>
+    <string name="input_method_language_settings" msgid="8069089418056819437">"Paramètres de langue"</string>
     <string name="show_ime" msgid="6406112007347443383">"Afficher le clavier virtuel même lorsque le clavier physique est actif"</string>
     <string name="hardware" msgid="3611039921284836033">"Utiliser le clavier à l\'écran"</string>
     <string name="select_keyboard_layout_notification_title" msgid="5823199895322205589">"Configurer <xliff:g id="DEVICE_NAME">%s</xliff:g>"</string>
@@ -2437,23 +2438,14 @@
     <string name="bg_user_sound_notification_title_alarm" msgid="5251678483393143527">"Alarme pour <xliff:g id="USER_NAME">%s</xliff:g>"</string>
     <string name="bg_user_sound_notification_button_switch_user" msgid="3091969648572788946">"Changer d\'utilisateur"</string>
     <string name="bg_user_sound_notification_button_mute" msgid="4942158515665615243">"Couper le son"</string>
-    <string name="bg_user_sound_notification_message" msgid="8613881975316976673">"Appuyer pour couper le son"</string>
-    <!-- no translation found for keyboard_shortcut_group_applications_browser (6535007304687100909) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_contacts (2750702518068326356) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_email (4229037666415353683) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_sms (3523799286376321137) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_music (2051507523525651067) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calendar (3571770335653387606) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calculator (6753209559716091507) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_maps (7950000659522589471) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications (3010389163951364798) -->
-    <skip />
+    <string name="bg_user_sound_notification_message" msgid="8613881975316976673">"Appuyez pour couper le son"</string>
+    <string name="keyboard_shortcut_group_applications_browser" msgid="6535007304687100909">"Navigateur"</string>
+    <string name="keyboard_shortcut_group_applications_contacts" msgid="2750702518068326356">"Contacts"</string>
+    <string name="keyboard_shortcut_group_applications_email" msgid="4229037666415353683">"Messagerie"</string>
+    <string name="keyboard_shortcut_group_applications_sms" msgid="3523799286376321137">"SMS"</string>
+    <string name="keyboard_shortcut_group_applications_music" msgid="2051507523525651067">"Musique"</string>
+    <string name="keyboard_shortcut_group_applications_calendar" msgid="3571770335653387606">"Agenda"</string>
+    <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Calculatrice"</string>
+    <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Maps"</string>
+    <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Applications"</string>
 </resources>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index d016747..7152818 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -1411,10 +1411,8 @@
     <string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"Selecciona para desactivar a depuración sen fíos."</string>
     <string name="test_harness_mode_notification_title" msgid="2282785860014142511">"Activouse o modo de axente de proba"</string>
     <string name="test_harness_mode_notification_message" msgid="3039123743127958420">"Restablece a configuración de fábrica para desactivar o modo de axente de proba."</string>
-    <!-- no translation found for wrong_hsum_configuration_notification_title (7212758829332714385) -->
-    <skip />
-    <!-- no translation found for wrong_hsum_configuration_notification_message (5353475441480684381) -->
-    <skip />
+    <string name="wrong_hsum_configuration_notification_title" msgid="7212758829332714385">"Configuración incorrecta de compilación do modo de usuario do sistema sen interface"</string>
+    <string name="wrong_hsum_configuration_notification_message" msgid="5353475441480684381">"O estado do modo de usuario do sistema sen interface deste dispositivo é distinto ao da súa configuración de compilación. Restablece a configuración de fábrica do dispositivo."</string>
     <string name="console_running_notification_title" msgid="6087888939261635904">"A consola de serie está activada"</string>
     <string name="console_running_notification_message" msgid="7892751888125174039">"O rendemento vese afectado. Para desactivar a consola, comproba o cargador de arranque."</string>
     <string name="mte_override_notification_title" msgid="4731115381962792944">"A MTE experimental está activada"</string>
@@ -1430,6 +1428,7 @@
     <string name="share_remote_bugreport_action" msgid="7630880678785123682">"COMPARTIR"</string>
     <string name="decline_remote_bugreport_action" msgid="4040894777519784346">"ANULAR"</string>
     <string name="select_input_method" msgid="3971267998568587025">"Escoller método de introdución de texto"</string>
+    <string name="input_method_language_settings" msgid="8069089418056819437">"Configuración de idioma"</string>
     <string name="show_ime" msgid="6406112007347443383">"Móstrase na pantalla mentres o teclado físico estea activo"</string>
     <string name="hardware" msgid="3611039921284836033">"Utilizar o teclado en pantalla"</string>
     <string name="select_keyboard_layout_notification_title" msgid="5823199895322205589">"Configura o teclado (<xliff:g id="DEVICE_NAME">%s</xliff:g>)"</string>
@@ -1759,12 +1758,9 @@
     <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>
-    <!-- no translation found for accessibility_button_prompt_text (6105393217162198616) -->
-    <skip />
-    <!-- no translation found for accessibility_gesture_prompt_text (6452246951969541792) -->
-    <skip />
-    <!-- no translation found for accessibility_gesture_3finger_prompt_text (77745752309056152) -->
-    <skip />
+    <string name="accessibility_button_prompt_text" msgid="6105393217162198616">"Escolle unha función"</string>
+    <string name="accessibility_gesture_prompt_text" msgid="6452246951969541792">"Escolle unha función"</string>
+    <string name="accessibility_gesture_3finger_prompt_text" msgid="77745752309056152">"Escolle unha función"</string>
     <string name="accessibility_button_instructional_text" msgid="6831154884557881996">"A función abrirase cando volvas tocar o botón Accesibilidade"</string>
     <string name="accessibility_gesture_instructional_text" msgid="4133877896011098550">"A función abrirase cando volvas usar este atallo. Pasa dous dedos desde a parte inferior da pantalla e solta rapidamente."</string>
     <string name="accessibility_gesture_3finger_instructional_text" msgid="1124458279366968154">"A función abrirase cando volvas usar este atallo. Pasa tres dedos desde a parte inferior da pantalla e solta rapidamente."</string>
@@ -1890,8 +1886,7 @@
     <string name="restr_pin_error_too_short" msgid="1547007808237941065">"O PIN é demasiado curto. Debe conter polo menos 4 díxitos."</string>
     <string name="restr_pin_try_later" msgid="5897719962541636727">"Téntao de novo máis tarde"</string>
     <string name="immersive_cling_title" msgid="2307034298721541791">"Vendo pantalla completa"</string>
-    <!-- no translation found for immersive_cling_description (2896205051090870978) -->
-    <skip />
+    <string name="immersive_cling_description" msgid="2896205051090870978">"Para saír, pasa o dedo cara abaixo desde a parte superior da pantalla"</string>
     <string name="immersive_cling_positive" msgid="7047498036346489883">"Entendido"</string>
     <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Xira a pantalla para que se vexa mellor"</string>
     <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Abre <xliff:g id="NAME">%s</xliff:g> en pantalla completa para unha mellor visualización"</string>
@@ -2439,26 +2434,17 @@
     <string name="face_dangling_notification_msg" msgid="746235263598985384">"Xa non se recoñece o teu modelo facial. Configura de novo o desbloqueo facial."</string>
     <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"Configurar"</string>
     <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"Agora non"</string>
-    <string name="bg_user_sound_notification_title_alarm" msgid="5251678483393143527">"Alarma para <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="bg_user_sound_notification_title_alarm" msgid="5251678483393143527">"Alarma para: <xliff:g id="USER_NAME">%s</xliff:g>"</string>
     <string name="bg_user_sound_notification_button_switch_user" msgid="3091969648572788946">"Cambiar de usuario"</string>
     <string name="bg_user_sound_notification_button_mute" msgid="4942158515665615243">"Silenciar"</string>
-    <string name="bg_user_sound_notification_message" msgid="8613881975316976673">"Tocar para silenciar o son"</string>
-    <!-- no translation found for keyboard_shortcut_group_applications_browser (6535007304687100909) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_contacts (2750702518068326356) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_email (4229037666415353683) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_sms (3523799286376321137) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_music (2051507523525651067) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calendar (3571770335653387606) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calculator (6753209559716091507) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_maps (7950000659522589471) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications (3010389163951364798) -->
-    <skip />
+    <string name="bg_user_sound_notification_message" msgid="8613881975316976673">"Toca para silenciar o son"</string>
+    <string name="keyboard_shortcut_group_applications_browser" msgid="6535007304687100909">"Navegador"</string>
+    <string name="keyboard_shortcut_group_applications_contacts" msgid="2750702518068326356">"Contactos"</string>
+    <string name="keyboard_shortcut_group_applications_email" msgid="4229037666415353683">"Correo electrónico"</string>
+    <string name="keyboard_shortcut_group_applications_sms" msgid="3523799286376321137">"SMS"</string>
+    <string name="keyboard_shortcut_group_applications_music" msgid="2051507523525651067">"Música"</string>
+    <string name="keyboard_shortcut_group_applications_calendar" msgid="3571770335653387606">"Calendario"</string>
+    <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Calculadora"</string>
+    <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Mapas"</string>
+    <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Aplicacións"</string>
 </resources>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index 7204703..a9ad4b7 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -1428,6 +1428,7 @@
     <string name="share_remote_bugreport_action" msgid="7630880678785123682">"શેર કરો"</string>
     <string name="decline_remote_bugreport_action" msgid="4040894777519784346">"નકારો"</string>
     <string name="select_input_method" msgid="3971267998568587025">"ઇનપુટ પદ્ધતિ પસંદ કરો"</string>
+    <string name="input_method_language_settings" msgid="8069089418056819437">"ભાષા સંબંધિત સેટિંગ"</string>
     <string name="show_ime" msgid="6406112007347443383">"જ્યારે ભૌતિક કીબોર્ડ સક્રિય હોય ત્યારે તેને સ્ક્રીન પર રાખો"</string>
     <string name="hardware" msgid="3611039921284836033">"ઑન-સ્ક્રીન કીબોર્ડનો ઉપયોગ કરો"</string>
     <string name="select_keyboard_layout_notification_title" msgid="5823199895322205589">"<xliff:g id="DEVICE_NAME">%s</xliff:g>ની ગોઠવણી કરો"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index c4d6f4a..428e9af 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -1428,6 +1428,7 @@
     <string name="share_remote_bugreport_action" msgid="7630880678785123682">"शेयर करें"</string>
     <string name="decline_remote_bugreport_action" msgid="4040894777519784346">"अस्वीकार करें"</string>
     <string name="select_input_method" msgid="3971267998568587025">"इनपुट का तरीका चुनें"</string>
+    <string name="input_method_language_settings" msgid="8069089418056819437">"भाषा की सेटिंग"</string>
     <string name="show_ime" msgid="6406112007347443383">"सामान्य कीबोर्ड के सक्रिय होने के दौरान इसे स्‍क्रीन पर बनाए रखें"</string>
     <string name="hardware" msgid="3611039921284836033">"ऑन-स्क्रीन कीबोर्ड इस्तेमाल करें"</string>
     <string name="select_keyboard_layout_notification_title" msgid="5823199895322205589">"<xliff:g id="DEVICE_NAME">%s</xliff:g> को कॉन्फ़िगर करें"</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index c3a6ec2..5c56ec4 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -1429,6 +1429,7 @@
     <string name="share_remote_bugreport_action" msgid="7630880678785123682">"DIJELI"</string>
     <string name="decline_remote_bugreport_action" msgid="4040894777519784346">"ODBIJ"</string>
     <string name="select_input_method" msgid="3971267998568587025">"Odabir načina unosa"</string>
+    <string name="input_method_language_settings" msgid="8069089418056819437">"Postavke jezika"</string>
     <string name="show_ime" msgid="6406112007347443383">"Zadržava se na zaslonu dok je fizička tipkovnica aktivna"</string>
     <string name="hardware" msgid="3611039921284836033">"Upotreba zaslonske tipkovnice"</string>
     <string name="select_keyboard_layout_notification_title" msgid="5823199895322205589">"Konfigurirajte uređaj <xliff:g id="DEVICE_NAME">%s</xliff:g>"</string>
@@ -2438,22 +2439,13 @@
     <string name="bg_user_sound_notification_button_switch_user" msgid="3091969648572788946">"Promijeni korisnika"</string>
     <string name="bg_user_sound_notification_button_mute" msgid="4942158515665615243">"Isključi zvuk"</string>
     <string name="bg_user_sound_notification_message" msgid="8613881975316976673">"Dodirnite za isključivanje zvuka"</string>
-    <!-- no translation found for keyboard_shortcut_group_applications_browser (6535007304687100909) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_contacts (2750702518068326356) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_email (4229037666415353683) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_sms (3523799286376321137) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_music (2051507523525651067) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calendar (3571770335653387606) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calculator (6753209559716091507) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_maps (7950000659522589471) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications (3010389163951364798) -->
-    <skip />
+    <string name="keyboard_shortcut_group_applications_browser" msgid="6535007304687100909">"Preglednik"</string>
+    <string name="keyboard_shortcut_group_applications_contacts" msgid="2750702518068326356">"Kontakti"</string>
+    <string name="keyboard_shortcut_group_applications_email" msgid="4229037666415353683">"E-pošta"</string>
+    <string name="keyboard_shortcut_group_applications_sms" msgid="3523799286376321137">"SMS"</string>
+    <string name="keyboard_shortcut_group_applications_music" msgid="2051507523525651067">"Glazba"</string>
+    <string name="keyboard_shortcut_group_applications_calendar" msgid="3571770335653387606">"Kalendar"</string>
+    <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Kalkulator"</string>
+    <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Karte"</string>
+    <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Aplikacije"</string>
 </resources>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 396a482d..3da7722 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -1428,6 +1428,7 @@
     <string name="share_remote_bugreport_action" msgid="7630880678785123682">"MEGOSZTÁS"</string>
     <string name="decline_remote_bugreport_action" msgid="4040894777519784346">"ELUTASÍTÁS"</string>
     <string name="select_input_method" msgid="3971267998568587025">"Beviteli mód kiválasztása"</string>
+    <string name="input_method_language_settings" msgid="8069089418056819437">"Nyelvi beállítások"</string>
     <string name="show_ime" msgid="6406112007347443383">"Maradjon a képernyőn, amíg a billentyűzet aktív"</string>
     <string name="hardware" msgid="3611039921284836033">"Képernyő-billentyűzet"</string>
     <string name="select_keyboard_layout_notification_title" msgid="5823199895322205589">"A(z) <xliff:g id="DEVICE_NAME">%s</xliff:g> beállítása"</string>
@@ -2437,22 +2438,13 @@
     <string name="bg_user_sound_notification_button_switch_user" msgid="3091969648572788946">"Felhasználóváltás"</string>
     <string name="bg_user_sound_notification_button_mute" msgid="4942158515665615243">"Némítás"</string>
     <string name="bg_user_sound_notification_message" msgid="8613881975316976673">"Koppintson a hang elnémításához"</string>
-    <!-- no translation found for keyboard_shortcut_group_applications_browser (6535007304687100909) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_contacts (2750702518068326356) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_email (4229037666415353683) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_sms (3523799286376321137) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_music (2051507523525651067) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calendar (3571770335653387606) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calculator (6753209559716091507) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_maps (7950000659522589471) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications (3010389163951364798) -->
-    <skip />
+    <string name="keyboard_shortcut_group_applications_browser" msgid="6535007304687100909">"Böngésző"</string>
+    <string name="keyboard_shortcut_group_applications_contacts" msgid="2750702518068326356">"Névjegyek"</string>
+    <string name="keyboard_shortcut_group_applications_email" msgid="4229037666415353683">"E-mail"</string>
+    <string name="keyboard_shortcut_group_applications_sms" msgid="3523799286376321137">"SMS"</string>
+    <string name="keyboard_shortcut_group_applications_music" msgid="2051507523525651067">"Zene"</string>
+    <string name="keyboard_shortcut_group_applications_calendar" msgid="3571770335653387606">"Naptár"</string>
+    <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Számológép"</string>
+    <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Térkép"</string>
+    <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Alkalmazások"</string>
 </resources>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index aad7760..dba6014 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -1428,6 +1428,7 @@
     <string name="share_remote_bugreport_action" msgid="7630880678785123682">"ՏՐԱՄԱԴՐԵԼ"</string>
     <string name="decline_remote_bugreport_action" msgid="4040894777519784346">"ՄԵՐԺԵԼ"</string>
     <string name="select_input_method" msgid="3971267998568587025">"Ընտրեք ներածման եղանակը"</string>
+    <string name="input_method_language_settings" msgid="8069089418056819437">"Լեզվի կարգավորումներ"</string>
     <string name="show_ime" msgid="6406112007347443383">"Պահել էկրանին, երբ ֆիզիկական ստեղնաշարն ակտիվ է"</string>
     <string name="hardware" msgid="3611039921284836033">"Օգտագործել էկրանային ստեղնաշար"</string>
     <string name="select_keyboard_layout_notification_title" msgid="5823199895322205589">"Կարգավորեք <xliff:g id="DEVICE_NAME">%s</xliff:g> սարքը"</string>
@@ -2433,26 +2434,17 @@
     <string name="face_dangling_notification_msg" msgid="746235263598985384">"Ձեր դեմքի նմուշն այլևս չի կարող ճանաչվել։ Նորից կարգավորեք դեմքով ապակողպումը։"</string>
     <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"Կարգավորել"</string>
     <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"Ոչ հիմա"</string>
-    <string name="bg_user_sound_notification_title_alarm" msgid="5251678483393143527">"<xliff:g id="USER_NAME">%s</xliff:g>-ի զարթուցիչ"</string>
+    <string name="bg_user_sound_notification_title_alarm" msgid="5251678483393143527">"«<xliff:g id="USER_NAME">%s</xliff:g>» օգտատիրոջ զարթուցիչ"</string>
     <string name="bg_user_sound_notification_button_switch_user" msgid="3091969648572788946">"Անցնել մյուս հաշիվ"</string>
     <string name="bg_user_sound_notification_button_mute" msgid="4942158515665615243">"Անտեսել"</string>
     <string name="bg_user_sound_notification_message" msgid="8613881975316976673">"Հպեք՝ ձայնն անջատելու համար"</string>
-    <!-- no translation found for keyboard_shortcut_group_applications_browser (6535007304687100909) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_contacts (2750702518068326356) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_email (4229037666415353683) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_sms (3523799286376321137) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_music (2051507523525651067) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calendar (3571770335653387606) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calculator (6753209559716091507) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_maps (7950000659522589471) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications (3010389163951364798) -->
-    <skip />
+    <string name="keyboard_shortcut_group_applications_browser" msgid="6535007304687100909">"Դիտարկիչ"</string>
+    <string name="keyboard_shortcut_group_applications_contacts" msgid="2750702518068326356">"Կոնտակտներ"</string>
+    <string name="keyboard_shortcut_group_applications_email" msgid="4229037666415353683">"Էլփոստ"</string>
+    <string name="keyboard_shortcut_group_applications_sms" msgid="3523799286376321137">"SMS"</string>
+    <string name="keyboard_shortcut_group_applications_music" msgid="2051507523525651067">"Երաժշտություն"</string>
+    <string name="keyboard_shortcut_group_applications_calendar" msgid="3571770335653387606">"Օրացույց"</string>
+    <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Հաշվիչ"</string>
+    <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Քարտեզներ"</string>
+    <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Հավելվածներ"</string>
 </resources>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index 9668f0e..0e431f4 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -1428,6 +1428,7 @@
     <string name="share_remote_bugreport_action" msgid="7630880678785123682">"BAGIKAN"</string>
     <string name="decline_remote_bugreport_action" msgid="4040894777519784346">"TOLAK"</string>
     <string name="select_input_method" msgid="3971267998568587025">"Pilih metode masukan"</string>
+    <string name="input_method_language_settings" msgid="8069089418056819437">"Setelan Bahasa"</string>
     <string name="show_ime" msgid="6406112007347443383">"Biarkan di layar meski keyboard fisik aktif"</string>
     <string name="hardware" msgid="3611039921284836033">"Gunakan keyboard virtual"</string>
     <string name="select_keyboard_layout_notification_title" msgid="5823199895322205589">"Konfigurasi <xliff:g id="DEVICE_NAME">%s</xliff:g>"</string>
@@ -2437,22 +2438,13 @@
     <string name="bg_user_sound_notification_button_switch_user" msgid="3091969648572788946">"Ganti pengguna"</string>
     <string name="bg_user_sound_notification_button_mute" msgid="4942158515665615243">"Bisukan"</string>
     <string name="bg_user_sound_notification_message" msgid="8613881975316976673">"Ketuk untuk membisukan suara"</string>
-    <!-- no translation found for keyboard_shortcut_group_applications_browser (6535007304687100909) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_contacts (2750702518068326356) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_email (4229037666415353683) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_sms (3523799286376321137) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_music (2051507523525651067) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calendar (3571770335653387606) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calculator (6753209559716091507) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_maps (7950000659522589471) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications (3010389163951364798) -->
-    <skip />
+    <string name="keyboard_shortcut_group_applications_browser" msgid="6535007304687100909">"Browser"</string>
+    <string name="keyboard_shortcut_group_applications_contacts" msgid="2750702518068326356">"Kontak"</string>
+    <string name="keyboard_shortcut_group_applications_email" msgid="4229037666415353683">"Email"</string>
+    <string name="keyboard_shortcut_group_applications_sms" msgid="3523799286376321137">"SMS"</string>
+    <string name="keyboard_shortcut_group_applications_music" msgid="2051507523525651067">"Musik"</string>
+    <string name="keyboard_shortcut_group_applications_calendar" msgid="3571770335653387606">"Kalender"</string>
+    <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Kalkulator"</string>
+    <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Maps"</string>
+    <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Aplikasi"</string>
 </resources>
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index b179c15..b2e03f3 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -1428,6 +1428,7 @@
     <string name="share_remote_bugreport_action" msgid="7630880678785123682">"DEILA"</string>
     <string name="decline_remote_bugreport_action" msgid="4040894777519784346">"HAFNA"</string>
     <string name="select_input_method" msgid="3971267998568587025">"Veldu innsláttaraðferð"</string>
+    <string name="input_method_language_settings" msgid="8069089418056819437">"Tungumálastillingar"</string>
     <string name="show_ime" msgid="6406112007347443383">"Halda því á skjánum meðan vélbúnaðarlyklaborðið er virkt"</string>
     <string name="hardware" msgid="3611039921284836033">"Nota skjályklaborð"</string>
     <string name="select_keyboard_layout_notification_title" msgid="5823199895322205589">"Stilla <xliff:g id="DEVICE_NAME">%s</xliff:g>"</string>
@@ -2433,26 +2434,17 @@
     <string name="face_dangling_notification_msg" msgid="746235263598985384">"Ekki er hægt að bera kennsl á andlitslíkanið þitt lengur. Settu upp andlitskenni aftur."</string>
     <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"Setja upp"</string>
     <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"Ekki núna"</string>
-    <string name="bg_user_sound_notification_title_alarm" msgid="5251678483393143527">"Viðvörun fyrir <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="bg_user_sound_notification_title_alarm" msgid="5251678483393143527">"Viðvörun fyrir: <xliff:g id="USER_NAME">%s</xliff:g>"</string>
     <string name="bg_user_sound_notification_button_switch_user" msgid="3091969648572788946">"Skipta um notanda"</string>
     <string name="bg_user_sound_notification_button_mute" msgid="4942158515665615243">"Þagga"</string>
     <string name="bg_user_sound_notification_message" msgid="8613881975316976673">"Ýttu til að þagga hljóð"</string>
-    <!-- no translation found for keyboard_shortcut_group_applications_browser (6535007304687100909) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_contacts (2750702518068326356) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_email (4229037666415353683) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_sms (3523799286376321137) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_music (2051507523525651067) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calendar (3571770335653387606) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calculator (6753209559716091507) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_maps (7950000659522589471) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications (3010389163951364798) -->
-    <skip />
+    <string name="keyboard_shortcut_group_applications_browser" msgid="6535007304687100909">"Vafri"</string>
+    <string name="keyboard_shortcut_group_applications_contacts" msgid="2750702518068326356">"Tengiliðir"</string>
+    <string name="keyboard_shortcut_group_applications_email" msgid="4229037666415353683">"Tölvupóstur"</string>
+    <string name="keyboard_shortcut_group_applications_sms" msgid="3523799286376321137">"SMS"</string>
+    <string name="keyboard_shortcut_group_applications_music" msgid="2051507523525651067">"Tónlist"</string>
+    <string name="keyboard_shortcut_group_applications_calendar" msgid="3571770335653387606">"Dagatal"</string>
+    <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Reiknivél"</string>
+    <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Kort"</string>
+    <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Forrit"</string>
 </resources>
diff --git a/core/res/res/values-it-feminine/strings.xml b/core/res/res/values-it-feminine/strings.xml
new file mode 100644
index 0000000..141b467
--- /dev/null
+++ b/core/res/res/values-it-feminine/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/* //device/apps/common/assets/res/any/strings.xml
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="relationTypeFriend" msgid="3192092625893980574">"Amica"</string>
+    <string name="relationTypeSpouse" msgid="6916682664436031703">"Moglie"</string>
+</resources>
diff --git a/core/res/res/values-it-masculine/strings.xml b/core/res/res/values-it-masculine/strings.xml
new file mode 100644
index 0000000..7310eb8
--- /dev/null
+++ b/core/res/res/values-it-masculine/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/* //device/apps/common/assets/res/any/strings.xml
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="relationTypeFriend" msgid="3192092625893980574">"Amico"</string>
+    <string name="relationTypeSpouse" msgid="6916682664436031703">"Marito"</string>
+</resources>
diff --git a/core/res/res/values-it-neuter/strings.xml b/core/res/res/values-it-neuter/strings.xml
new file mode 100644
index 0000000..ce433d7
--- /dev/null
+++ b/core/res/res/values-it-neuter/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/* //device/apps/common/assets/res/any/strings.xml
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="relationTypeFriend" msgid="3192092625893980574">"Amicə"</string>
+    <string name="relationTypeSpouse" msgid="6916682664436031703">"Coniuge"</string>
+</resources>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 06aa308..52fd5b1 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -47,7 +47,7 @@
     <string name="needPuk2" msgid="3910763547447344963">"Digita il codice PUK2 per sbloccare la SIM."</string>
     <string name="enablePin" msgid="2543771964137091212">"Operazione non riuscita; attiva blocco SIM/RUIM."</string>
     <plurals name="pinpuk_attempts" formatted="false" msgid="1619867269012213584">
-      <item quantity="many">Hai ancora <xliff:g id="NUMBER_1">%d</xliff:g> tentativi a disposizione prima che la SIM venga bloccata.</item>
+      <item quantity="many">Hai ancora <xliff:g id="NUMBER_1">%d</xliff:g> di tentativi a disposizione prima che la SIM venga bloccata.</item>
       <item quantity="other">Hai ancora <xliff:g id="NUMBER_1">%d</xliff:g> tentativi a disposizione prima che la SIM venga bloccata.</item>
       <item quantity="one">Hai ancora <xliff:g id="NUMBER_0">%d</xliff:g> tentativo a disposizione prima che la SIM venga bloccata.</item>
     </plurals>
@@ -521,7 +521,7 @@
     <string name="permlab_backgroundCamera" msgid="7549917926079731681">"Acquisizione di foto e video in background"</string>
     <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"Questa app può scattare foto e registrare video tramite la fotocamera in qualsiasi momento."</string>
     <string name="permlab_systemCamera" msgid="3642917457796210580">"Consenti a un\'applicazione o a un servizio di accedere alle videocamere del sistema per fare foto e video"</string>
-    <string name="permdesc_systemCamera" msgid="5938360914419175986">"Questa app di sistema o con privilegi può scattare foto e registrare video tramite una videocamera di sistema in qualsiasi momento. Richiede che anche l\'app disponga dell\'autorizzazione android.permission.CAMERA"</string>
+    <string name="permdesc_systemCamera" msgid="5938360914419175986">"Questa app di sistema o con privilegi può scattare foto e registrare video tramite una fotocamera di sistema in qualsiasi momento. Richiede che anche l\'app disponga dell\'autorizzazione android.permission.CAMERA"</string>
     <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Consenti a un\'applicazione o a un servizio di ricevere callback relativi all\'apertura o alla chiusura di videocamere."</string>
     <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"Questa app può ricevere callback quando una videocamera viene aperta (da una specifica applicazione) o chiusa."</string>
     <string name="permlab_cameraHeadlessSystemUser" msgid="680194666834500050">"Consenti a un\'applicazione o a un servizio di accedere alla fotocamera come utente di sistema senza testa."</string>
@@ -622,7 +622,7 @@
     <string name="permlab_disableKeyguard" msgid="3605253559020928505">"disattivazione blocco schermo"</string>
     <string name="permdesc_disableKeyguard" msgid="3223710003098573038">"Consente all\'applicazione di disattivare il blocco tastiera ed eventuali protezioni tramite password associate. Ad esempio, il telefono disattiva il blocco tastiera quando riceve una telefonata in arrivo e lo riattiva al termine della chiamata."</string>
     <string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"richiesta di complessità del blocco schermo"</string>
-    <string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"Consente all\'app di conoscere il livello di complessità del blocco schermo (alto, medio, basso o nessuno), che indica l\'intervallo di caratteri possibile e il tipo di blocco schermo. L\'app può inoltre suggerire agli utenti di aggiornare il blocco schermo a un livello specifico di complessità, ma gli utenti possono ignorare liberamente il suggerimento e uscire. Tieni presente che il blocco schermo non viene memorizzato come testo non crittografato, quindi l\'app non conosce la password esatta."</string>
+    <string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"Consente all\'app di conoscere il livello di complessità del blocco schermo (alto, medio, basso o nessuno), che indica l\'intervallo di lunghezza di caratteri possibile e il tipo di blocco schermo. L\'app può inoltre suggerire agli utenti di aggiornare il blocco schermo a un livello specifico di complessità, ma gli utenti possono ignorare liberamente il suggerimento e uscire. Tieni presente che il blocco schermo non viene memorizzato come testo non crittografato, quindi l\'app non conosce la password esatta."</string>
     <string name="permlab_postNotification" msgid="4875401198597803658">"Visualizzazione di notifiche"</string>
     <string name="permdesc_postNotification" msgid="5974977162462877075">"Consente all\'app di mostrare notifiche"</string>
     <string name="permlab_turnScreenOn" msgid="219344053664171492">"attiva lo schermo"</string>
@@ -738,7 +738,7 @@
   </string-array>
     <string name="face_error_hw_not_available" msgid="5085202213036026288">"Imposs. verificare volto. Hardware non disponibile."</string>
     <string name="face_error_timeout" msgid="2598544068593889762">"Riprova lo Sblocco con il Volto"</string>
-    <string name="face_error_no_space" msgid="5649264057026021723">"Imposs. salvare dati nuovi volti. Elimina un volto vecchio."</string>
+    <string name="face_error_no_space" msgid="5649264057026021723">"Impossibile salvare nuovi dati del volto. Elimina un volto vecchio."</string>
     <string name="face_error_canceled" msgid="2164434737103802131">"Operazione associata al volto annullata."</string>
     <string name="face_error_user_canceled" msgid="5766472033202928373">"Sblocco con il Volto annullato dall\'utente"</string>
     <string name="face_error_lockout" msgid="7864408714994529437">"Troppi tentativi. Riprova più tardi."</string>
@@ -969,7 +969,7 @@
     <string name="relationTypeChild" msgid="9076258911292693601">"Figlio"</string>
     <string name="relationTypeDomesticPartner" msgid="7825306887697559238">"Convivente"</string>
     <string name="relationTypeFather" msgid="3856225062864790596">"Padre"</string>
-    <string name="relationTypeFriend" msgid="3192092625893980574">"Amico"</string>
+    <string name="relationTypeFriend" msgid="3192092625893980574">"Persona amica"</string>
     <string name="relationTypeManager" msgid="2272860813153171857">"Dirigente"</string>
     <string name="relationTypeMother" msgid="2331762740982699460">"Madre"</string>
     <string name="relationTypeParent" msgid="4177920938333039882">"Genitore"</string>
@@ -1127,7 +1127,7 @@
     <string name="enable_explore_by_touch_warning_message" product="default" msgid="4312979647356179250">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> vuole attivare la funzione Esplora al tocco. Quando la funzione Esplora al tocco è attiva, puoi ascoltare o visualizzare le descrizioni di ciò che stai toccando oppure interagire con il telefono tramite gesti."</string>
     <string name="oneMonthDurationPast" msgid="4538030857114635777">"1 mese fa"</string>
     <string name="beforeOneMonthDurationPast" msgid="8315149541372065392">"Oltre 1 mese fa"</string>
-    <string name="last_num_days" msgid="2393660431490280537">"{count,plural, =1{Ultimo giorno}many{Ultimi # giorni}other{Ultimi # giorni}}"</string>
+    <string name="last_num_days" msgid="2393660431490280537">"{count,plural, =1{Ultimo giorno}many{Ultimi # di giorni}other{Ultimi # giorni}}"</string>
     <string name="last_month" msgid="1528906781083518683">"Ultimo mese"</string>
     <string name="older" msgid="1645159827884647400">"Precedente"</string>
     <string name="preposition_for_date" msgid="2780767868832729599">"<xliff:g id="DATE">%s</xliff:g>"</string>
@@ -1154,14 +1154,14 @@
     <string name="duration_hours_shortest_future" msgid="2979276794547981674">"tra <xliff:g id="COUNT">%d</xliff:g> h"</string>
     <string name="duration_days_shortest_future" msgid="3392722163935571543">"tra <xliff:g id="COUNT">%d</xliff:g> g"</string>
     <string name="duration_years_shortest_future" msgid="5537464088352970388">"tra <xliff:g id="COUNT">%d</xliff:g> a"</string>
-    <string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# minuto fa}many{# minuti fa}other{# minuti fa}}"</string>
-    <string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# ora fa}many{# ore fa}other{# ore fa}}"</string>
-    <string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# giorno fa}many{# giorni fa}other{# giorni fa}}"</string>
-    <string name="duration_years_relative" msgid="8731202348869424370">"{count,plural, =1{# anno fa}many{# anni fa}other{# anni fa}}"</string>
-    <string name="duration_minutes_relative_future" msgid="5259574171747708115">"{count,plural, =1{# minuto}many{# minuti}other{# minuti}}"</string>
-    <string name="duration_hours_relative_future" msgid="6670440478481140565">"{count,plural, =1{# ora}many{# ore}other{# ore}}"</string>
-    <string name="duration_days_relative_future" msgid="8870658635774250746">"{count,plural, =1{# giorno}many{# giorni}other{# giorni}}"</string>
-    <string name="duration_years_relative_future" msgid="8855853883925918380">"{count,plural, =1{# anno}many{# anni}other{# anni}}"</string>
+    <string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# minuto fa}many{# di minuti fa}other{# minuti fa}}"</string>
+    <string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# ora fa}many{# di ore fa}other{# ore fa}}"</string>
+    <string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# giorno fa}many{# di giorni fa}other{# giorni fa}}"</string>
+    <string name="duration_years_relative" msgid="8731202348869424370">"{count,plural, =1{# anno fa}many{# di anni fa}other{# anni fa}}"</string>
+    <string name="duration_minutes_relative_future" msgid="5259574171747708115">"{count,plural, =1{# minuto}many{# di minuti}other{# minuti}}"</string>
+    <string name="duration_hours_relative_future" msgid="6670440478481140565">"{count,plural, =1{# ora}many{# di ore}other{# ore}}"</string>
+    <string name="duration_days_relative_future" msgid="8870658635774250746">"{count,plural, =1{# giorno}many{# di giorni}other{# giorni}}"</string>
+    <string name="duration_years_relative_future" msgid="8855853883925918380">"{count,plural, =1{# anno}many{# di anni}other{# anni}}"</string>
     <string name="VideoView_error_title" msgid="5750686717225068016">"Problemi video"</string>
     <string name="VideoView_error_text_invalid_progressive_playback" msgid="3782449246085134720">"Questo video non è valido per lo streaming su questo dispositivo."</string>
     <string name="VideoView_error_text_unknown" msgid="7658683339707607138">"Impossibile riprodurre il video."</string>
@@ -1286,7 +1286,7 @@
     <string name="android_start_title" product="tablet" msgid="4429767260263190344">"Avvio del tablet…"</string>
     <string name="android_start_title" product="device" msgid="6967413819673299309">"Avvio del dispositivo…"</string>
     <string name="android_upgrading_notification_title" product="default" msgid="3509927005342279257">"Completamento aggiornamento di sistema…"</string>
-    <string name="app_upgrading_toast" msgid="1016267296049455585">"Upgrade dell\'app <xliff:g id="APPLICATION">%1$s</xliff:g>…"</string>
+    <string name="app_upgrading_toast" msgid="1016267296049455585">"Upgrade dell\'app <xliff:g id="APPLICATION">%1$s</xliff:g> in corso…"</string>
     <string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g> in preparazione."</string>
     <string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Avvio app."</string>
     <string name="android_upgrading_complete" msgid="409800058018374746">"Conclusione dell\'avvio."</string>
@@ -1429,6 +1429,7 @@
     <string name="share_remote_bugreport_action" msgid="7630880678785123682">"CONDIVIDI"</string>
     <string name="decline_remote_bugreport_action" msgid="4040894777519784346">"RIFIUTO"</string>
     <string name="select_input_method" msgid="3971267998568587025">"Scegli il metodo di immissione"</string>
+    <string name="input_method_language_settings" msgid="8069089418056819437">"Impostazioni della lingua"</string>
     <string name="show_ime" msgid="6406112007347443383">"Tieni sullo schermo quando è attiva la tastiera fisica"</string>
     <string name="hardware" msgid="3611039921284836033">"Usa tastiera sullo schermo"</string>
     <string name="select_keyboard_layout_notification_title" msgid="5823199895322205589">"Configura <xliff:g id="DEVICE_NAME">%s</xliff:g>"</string>
@@ -1767,7 +1768,7 @@
     <string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"Ingrandimento"</string>
     <string name="user_switched" msgid="7249833311585228097">"Utente corrente <xliff:g id="NAME">%1$s</xliff:g>."</string>
     <string name="user_switching_message" msgid="1912993630661332336">"Passaggio a <xliff:g id="NAME">%1$s</xliff:g>…"</string>
-    <string name="user_logging_out_message" msgid="7216437629179710359">"Disconnessione di <xliff:g id="NAME">%1$s</xliff:g>…"</string>
+    <string name="user_logging_out_message" msgid="7216437629179710359">"Disconnessione di <xliff:g id="NAME">%1$s</xliff:g> in corso…"</string>
     <string name="owner_name" msgid="8713560351570795743">"Proprietario"</string>
     <string name="guest_name" msgid="8502103277839834324">"Ospite"</string>
     <string name="error_message_title" msgid="4082495589294631966">"Errore"</string>
@@ -1915,14 +1916,14 @@
     <string name="data_saver_description" msgid="4995164271550590517">"Per contribuire a ridurre l\'utilizzo dei dati, la funzionalità Risparmio dati impedisce ad alcune app di inviare o ricevere dati in background. Un\'app in uso può accedere ai dati, ma potrebbe farlo con meno frequenza. Per esempio, è possibile che le immagini non vengano visualizzate finché non le tocchi."</string>
     <string name="data_saver_enable_title" msgid="7080620065745260137">"Attivare Risparmio dati?"</string>
     <string name="data_saver_enable_button" msgid="4399405762586419726">"Attiva"</string>
-    <string name="zen_mode_duration_minutes_summary" msgid="4555514757230849789">"{count,plural, =1{Per un minuto (fino alle ore {formattedTime})}many{Per # minuti (fino alle ore {formattedTime})}other{Per # minuti (fino alle ore {formattedTime})}}"</string>
-    <string name="zen_mode_duration_minutes_summary_short" msgid="1187553788355486950">"{count,plural, =1{Per 1 min (fino alle ore {formattedTime})}many{Per # min (fino alle ore {formattedTime})}other{Per # min (fino alle ore {formattedTime})}}"</string>
-    <string name="zen_mode_duration_hours_summary" msgid="3866333100793277211">"{count,plural, =1{Per 1 ora (fino alle ore {formattedTime})}many{Per # ore (fino alle ore {formattedTime})}other{Per # ore (fino alle ore {formattedTime})}}"</string>
-    <string name="zen_mode_duration_hours_summary_short" msgid="687919813833347945">"{count,plural, =1{Per 1 h (fino alle ore {formattedTime})}many{Per # h (fino alle ore {formattedTime})}other{Per # h (fino alle ore {formattedTime})}}"</string>
-    <string name="zen_mode_duration_minutes" msgid="2340007982276569054">"{count,plural, =1{Per un minuto}many{Per # minuti}other{Per # minuti}}"</string>
-    <string name="zen_mode_duration_minutes_short" msgid="2435756450204526554">"{count,plural, =1{Per 1 min}many{Per # min}other{Per # min}}"</string>
-    <string name="zen_mode_duration_hours" msgid="7841806065034711849">"{count,plural, =1{Per 1 ora}many{Per # ore}other{Per # ore}}"</string>
-    <string name="zen_mode_duration_hours_short" msgid="3666949653933099065">"{count,plural, =1{Per 1 h}many{Per # h}other{Per # h}}"</string>
+    <string name="zen_mode_duration_minutes_summary" msgid="4555514757230849789">"{count,plural, =1{Per un minuto (fino alle ore {formattedTime})}many{Per # di minuti (fino alle ore {formattedTime})}other{Per # minuti (fino alle ore {formattedTime})}}"</string>
+    <string name="zen_mode_duration_minutes_summary_short" msgid="1187553788355486950">"{count,plural, =1{Per 1 min (fino alle ore {formattedTime})}many{Per # di min (fino alle ore {formattedTime})}other{Per # min (fino alle ore {formattedTime})}}"</string>
+    <string name="zen_mode_duration_hours_summary" msgid="3866333100793277211">"{count,plural, =1{Per 1 ora (fino alle ore {formattedTime})}many{Per # di ore (fino alle ore {formattedTime})}other{Per # ore (fino alle ore {formattedTime})}}"</string>
+    <string name="zen_mode_duration_hours_summary_short" msgid="687919813833347945">"{count,plural, =1{Per 1 h (fino alle ore {formattedTime})}many{Per # di h (fino alle ore {formattedTime})}other{Per # h (fino alle ore {formattedTime})}}"</string>
+    <string name="zen_mode_duration_minutes" msgid="2340007982276569054">"{count,plural, =1{Per un minuto}many{Per # di minuti}other{Per # minuti}}"</string>
+    <string name="zen_mode_duration_minutes_short" msgid="2435756450204526554">"{count,plural, =1{Per 1 min}many{Per # di min}other{Per # min}}"</string>
+    <string name="zen_mode_duration_hours" msgid="7841806065034711849">"{count,plural, =1{Per 1 ora}many{Per # di ore}other{Per # ore}}"</string>
+    <string name="zen_mode_duration_hours_short" msgid="3666949653933099065">"{count,plural, =1{Per 1 h}many{Per # di h}other{Per # h}}"</string>
     <string name="zen_mode_until_next_day" msgid="1403042784161725038">"Fino a: <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
     <string name="zen_mode_until" msgid="2250286190237669079">"Fino a <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
     <string name="zen_mode_alarm" msgid="7046911727540499275">"Fino a <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (prossima sveglia)"</string>
@@ -2438,22 +2439,13 @@
     <string name="bg_user_sound_notification_button_switch_user" msgid="3091969648572788946">"Cambia utente"</string>
     <string name="bg_user_sound_notification_button_mute" msgid="4942158515665615243">"Disattiva audio"</string>
     <string name="bg_user_sound_notification_message" msgid="8613881975316976673">"Tocca per disattivare l\'audio"</string>
-    <!-- no translation found for keyboard_shortcut_group_applications_browser (6535007304687100909) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_contacts (2750702518068326356) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_email (4229037666415353683) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_sms (3523799286376321137) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_music (2051507523525651067) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calendar (3571770335653387606) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calculator (6753209559716091507) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_maps (7950000659522589471) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications (3010389163951364798) -->
-    <skip />
+    <string name="keyboard_shortcut_group_applications_browser" msgid="6535007304687100909">"Browser"</string>
+    <string name="keyboard_shortcut_group_applications_contacts" msgid="2750702518068326356">"Contatti"</string>
+    <string name="keyboard_shortcut_group_applications_email" msgid="4229037666415353683">"Email"</string>
+    <string name="keyboard_shortcut_group_applications_sms" msgid="3523799286376321137">"SMS"</string>
+    <string name="keyboard_shortcut_group_applications_music" msgid="2051507523525651067">"Musica"</string>
+    <string name="keyboard_shortcut_group_applications_calendar" msgid="3571770335653387606">"Calendario"</string>
+    <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Calcolatrice"</string>
+    <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Maps"</string>
+    <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Applicazioni"</string>
 </resources>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index dd9f6b8..76e4e83 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -1275,7 +1275,7 @@
     <string name="unsupported_display_size_show" msgid="980129850974919375">"להציג תמיד"</string>
     <string name="unsupported_compile_sdk_message" msgid="7326293500707890537">"‏<xliff:g id="APP_NAME">%1$s</xliff:g> נבנתה לגרסה לא תואמת של מערכת ההפעלה של Android ועלולה להתנהג באופן לא צפוי. ייתכן שקיימת גרסה מעודכנת של האפליקציה."</string>
     <string name="unsupported_compile_sdk_show" msgid="1601210057960312248">"להציג תמיד"</string>
-    <string name="unsupported_compile_sdk_check_update" msgid="1103639989147664456">"האם יש עדכון חדש?"</string>
+    <string name="unsupported_compile_sdk_check_update" msgid="1103639989147664456">"יש עדכון חדש?"</string>
     <string name="smv_application" msgid="3775183542777792638">"‏האפליקציה <xliff:g id="APPLICATION">%1$s</xliff:g> (תהליך <xliff:g id="PROCESS">%2$s</xliff:g>) הפרה את מדיניות StrictMode באכיפה עצמית שלה."</string>
     <string name="smv_process" msgid="1398801497130695446">"‏התהליך <xliff:g id="PROCESS">%1$s</xliff:g> הפר את מדיניות StrictMode באכיפה עצמית."</string>
     <string name="android_upgrading_title" product="default" msgid="7279077384220829683">"הטלפון מתעדכן…"</string>
@@ -1429,6 +1429,7 @@
     <string name="share_remote_bugreport_action" msgid="7630880678785123682">"שיתוף"</string>
     <string name="decline_remote_bugreport_action" msgid="4040894777519784346">"עדיף שלא"</string>
     <string name="select_input_method" msgid="3971267998568587025">"בחירה של שיטת הזנה"</string>
+    <string name="input_method_language_settings" msgid="8069089418056819437">"הגדרות שפה"</string>
     <string name="show_ime" msgid="6406112007347443383">"להשאיר במסך בזמן שהמקלדת הפיזית פעילה"</string>
     <string name="hardware" msgid="3611039921284836033">"שימוש במקלדת שמופיעה במסך"</string>
     <string name="select_keyboard_layout_notification_title" msgid="5823199895322205589">"הגדרה של <xliff:g id="DEVICE_NAME">%s</xliff:g>"</string>
@@ -2437,23 +2438,14 @@
     <string name="bg_user_sound_notification_title_alarm" msgid="5251678483393143527">"שעון מעורר של <xliff:g id="USER_NAME">%s</xliff:g>"</string>
     <string name="bg_user_sound_notification_button_switch_user" msgid="3091969648572788946">"החלפת משתמש"</string>
     <string name="bg_user_sound_notification_button_mute" msgid="4942158515665615243">"השתקה"</string>
-    <string name="bg_user_sound_notification_message" msgid="8613881975316976673">"צריך להקיש כדי להשתיק את הצליל"</string>
-    <!-- no translation found for keyboard_shortcut_group_applications_browser (6535007304687100909) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_contacts (2750702518068326356) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_email (4229037666415353683) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_sms (3523799286376321137) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_music (2051507523525651067) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calendar (3571770335653387606) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calculator (6753209559716091507) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_maps (7950000659522589471) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications (3010389163951364798) -->
-    <skip />
+    <string name="bg_user_sound_notification_message" msgid="8613881975316976673">"כדי להשתיק את הצליל, צריך להקיש כאן"</string>
+    <string name="keyboard_shortcut_group_applications_browser" msgid="6535007304687100909">"דפדפן"</string>
+    <string name="keyboard_shortcut_group_applications_contacts" msgid="2750702518068326356">"אנשי קשר"</string>
+    <string name="keyboard_shortcut_group_applications_email" msgid="4229037666415353683">"אימייל"</string>
+    <string name="keyboard_shortcut_group_applications_sms" msgid="3523799286376321137">"SMS"</string>
+    <string name="keyboard_shortcut_group_applications_music" msgid="2051507523525651067">"מוזיקה"</string>
+    <string name="keyboard_shortcut_group_applications_calendar" msgid="3571770335653387606">"יומן"</string>
+    <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"מחשבון"</string>
+    <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"מפות"</string>
+    <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"אפליקציות"</string>
 </resources>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 2964486..4364b28 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -1144,7 +1144,7 @@
     <string name="weeks" msgid="3516247214269821391">"週間"</string>
     <string name="year" msgid="5182610307741238982">"年"</string>
     <string name="years" msgid="5797714729103773425">"年"</string>
-    <string name="now_string_shortest" msgid="3684914126941650330">"現在"</string>
+    <string name="now_string_shortest" msgid="3684914126941650330">"今"</string>
     <string name="duration_minutes_shortest" msgid="5744379079540806690">"<xliff:g id="COUNT">%d</xliff:g> 分"</string>
     <string name="duration_hours_shortest" msgid="1477752094141971675">"<xliff:g id="COUNT">%d</xliff:g> 時間"</string>
     <string name="duration_days_shortest" msgid="4083124701676227233">"<xliff:g id="COUNT">%d</xliff:g> 日"</string>
@@ -1428,6 +1428,7 @@
     <string name="share_remote_bugreport_action" msgid="7630880678785123682">"共有する"</string>
     <string name="decline_remote_bugreport_action" msgid="4040894777519784346">"共有しない"</string>
     <string name="select_input_method" msgid="3971267998568587025">"入力方法の選択"</string>
+    <string name="input_method_language_settings" msgid="8069089418056819437">"言語設定"</string>
     <string name="show_ime" msgid="6406112007347443383">"物理キーボードが有効になっていても画面に表示させます"</string>
     <string name="hardware" msgid="3611039921284836033">"画面キーボードを使用"</string>
     <string name="select_keyboard_layout_notification_title" msgid="5823199895322205589">"<xliff:g id="DEVICE_NAME">%s</xliff:g>の設定"</string>
@@ -2436,23 +2437,14 @@
     <string name="bg_user_sound_notification_title_alarm" msgid="5251678483393143527">"<xliff:g id="USER_NAME">%s</xliff:g> さんのアラーム"</string>
     <string name="bg_user_sound_notification_button_switch_user" msgid="3091969648572788946">"ユーザーを切り替え"</string>
     <string name="bg_user_sound_notification_button_mute" msgid="4942158515665615243">"ミュート"</string>
-    <string name="bg_user_sound_notification_message" msgid="8613881975316976673">"音声をミュートするにはタップします"</string>
-    <!-- no translation found for keyboard_shortcut_group_applications_browser (6535007304687100909) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_contacts (2750702518068326356) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_email (4229037666415353683) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_sms (3523799286376321137) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_music (2051507523525651067) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calendar (3571770335653387606) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calculator (6753209559716091507) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_maps (7950000659522589471) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications (3010389163951364798) -->
-    <skip />
+    <string name="bg_user_sound_notification_message" msgid="8613881975316976673">"通知音をミュートするにはタップしてください"</string>
+    <string name="keyboard_shortcut_group_applications_browser" msgid="6535007304687100909">"ブラウザ"</string>
+    <string name="keyboard_shortcut_group_applications_contacts" msgid="2750702518068326356">"連絡先"</string>
+    <string name="keyboard_shortcut_group_applications_email" msgid="4229037666415353683">"メール"</string>
+    <string name="keyboard_shortcut_group_applications_sms" msgid="3523799286376321137">"SMS"</string>
+    <string name="keyboard_shortcut_group_applications_music" msgid="2051507523525651067">"音楽"</string>
+    <string name="keyboard_shortcut_group_applications_calendar" msgid="3571770335653387606">"カレンダー"</string>
+    <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"電卓"</string>
+    <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"マップ"</string>
+    <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"アプリ"</string>
 </resources>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index a45176f..1e5cf93 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -1428,6 +1428,7 @@
     <string name="share_remote_bugreport_action" msgid="7630880678785123682">"გაზიარება"</string>
     <string name="decline_remote_bugreport_action" msgid="4040894777519784346">"უარყოფა"</string>
     <string name="select_input_method" msgid="3971267998568587025">"აირჩიეთ შეყვანის მეთოდი"</string>
+    <string name="input_method_language_settings" msgid="8069089418056819437">"ენის პარამეტრები"</string>
     <string name="show_ime" msgid="6406112007347443383">"აქტიური ფიზიკური კლავიატურისას ეკრანზე შენარჩუნება"</string>
     <string name="hardware" msgid="3611039921284836033">"ეკრანული კლავიატურის გამოყენება"</string>
     <string name="select_keyboard_layout_notification_title" msgid="5823199895322205589">"მოახდინეთ <xliff:g id="DEVICE_NAME">%s</xliff:g>-ის კონფიგურირება"</string>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index c526870..2acf35d 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -1428,6 +1428,7 @@
     <string name="share_remote_bugreport_action" msgid="7630880678785123682">"БӨЛІСУ"</string>
     <string name="decline_remote_bugreport_action" msgid="4040894777519784346">"ҚАБЫЛДАМАУ"</string>
     <string name="select_input_method" msgid="3971267998568587025">"Енгізу әдісін таңдау"</string>
+    <string name="input_method_language_settings" msgid="8069089418056819437">"Тіл параметрлері"</string>
     <string name="show_ime" msgid="6406112007347443383">"Физикалық пернетақта қосулы кезде оны экранға шығару"</string>
     <string name="hardware" msgid="3611039921284836033">"Экрандағы пернетақтаны пайдалану"</string>
     <string name="select_keyboard_layout_notification_title" msgid="5823199895322205589">"<xliff:g id="DEVICE_NAME">%s</xliff:g> конфигурациялау"</string>
@@ -2433,26 +2434,17 @@
     <string name="face_dangling_notification_msg" msgid="746235263598985384">"Бет үлгіңіз бұдан былай танылмайды. Бет тану функциясын қайта реттеңіз."</string>
     <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"Реттеу"</string>
     <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"Қазір емес"</string>
-    <string name="bg_user_sound_notification_title_alarm" msgid="5251678483393143527">"<xliff:g id="USER_NAME">%s</xliff:g> атына қойылған дабыл"</string>
+    <string name="bg_user_sound_notification_title_alarm" msgid="5251678483393143527">"<xliff:g id="USER_NAME">%s</xliff:g> оятқышы"</string>
     <string name="bg_user_sound_notification_button_switch_user" msgid="3091969648572788946">"Пайдаланушыны ауыстыру"</string>
     <string name="bg_user_sound_notification_button_mute" msgid="4942158515665615243">"Дыбысын өшіру"</string>
     <string name="bg_user_sound_notification_message" msgid="8613881975316976673">"Дыбысын өшіру үшін түртіңіз."</string>
-    <!-- no translation found for keyboard_shortcut_group_applications_browser (6535007304687100909) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_contacts (2750702518068326356) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_email (4229037666415353683) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_sms (3523799286376321137) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_music (2051507523525651067) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calendar (3571770335653387606) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calculator (6753209559716091507) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_maps (7950000659522589471) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications (3010389163951364798) -->
-    <skip />
+    <string name="keyboard_shortcut_group_applications_browser" msgid="6535007304687100909">"Браузер"</string>
+    <string name="keyboard_shortcut_group_applications_contacts" msgid="2750702518068326356">"Контактілер"</string>
+    <string name="keyboard_shortcut_group_applications_email" msgid="4229037666415353683">"Электрондық пошта"</string>
+    <string name="keyboard_shortcut_group_applications_sms" msgid="3523799286376321137">"SMS"</string>
+    <string name="keyboard_shortcut_group_applications_music" msgid="2051507523525651067">"Музыка"</string>
+    <string name="keyboard_shortcut_group_applications_calendar" msgid="3571770335653387606">"Күнтізбе"</string>
+    <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Калькулятор"</string>
+    <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Maps"</string>
+    <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Қолданбалар"</string>
 </resources>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index 2bf799c..7f3c09e 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -1428,6 +1428,7 @@
     <string name="share_remote_bugreport_action" msgid="7630880678785123682">"ចែករំលែក"</string>
     <string name="decline_remote_bugreport_action" msgid="4040894777519784346">"បដិសេធ"</string>
     <string name="select_input_method" msgid="3971267998568587025">"ជ្រើស​វិធីសាស្ត្រ​បញ្ចូល"</string>
+    <string name="input_method_language_settings" msgid="8069089418056819437">"ការកំណត់​ភាសា"</string>
     <string name="show_ime" msgid="6406112007347443383">"ទុកវានៅលើអេក្រង់ខណៈពេលក្តារចុចពិតប្រាកដកំពុងសកម្ម"</string>
     <string name="hardware" msgid="3611039921284836033">"ប្រើក្ដារចុច​លើអេក្រង់"</string>
     <string name="select_keyboard_layout_notification_title" msgid="5823199895322205589">"កំណត់រចនាសម្ព័ន្ធ <xliff:g id="DEVICE_NAME">%s</xliff:g>"</string>
@@ -2437,22 +2438,13 @@
     <string name="bg_user_sound_notification_button_switch_user" msgid="3091969648572788946">"ប្ដូរអ្នកប្រើប្រាស់"</string>
     <string name="bg_user_sound_notification_button_mute" msgid="4942158515665615243">"បិទសំឡេង"</string>
     <string name="bg_user_sound_notification_message" msgid="8613881975316976673">"ចុចដើម្បីបិទសំឡេង"</string>
-    <!-- no translation found for keyboard_shortcut_group_applications_browser (6535007304687100909) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_contacts (2750702518068326356) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_email (4229037666415353683) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_sms (3523799286376321137) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_music (2051507523525651067) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calendar (3571770335653387606) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calculator (6753209559716091507) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_maps (7950000659522589471) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications (3010389163951364798) -->
-    <skip />
+    <string name="keyboard_shortcut_group_applications_browser" msgid="6535007304687100909">"កម្មវិធី​រុករក​តាម​អ៊ីនធឺណិត"</string>
+    <string name="keyboard_shortcut_group_applications_contacts" msgid="2750702518068326356">"ទំនាក់ទំនង"</string>
+    <string name="keyboard_shortcut_group_applications_email" msgid="4229037666415353683">"អ៊ីមែល"</string>
+    <string name="keyboard_shortcut_group_applications_sms" msgid="3523799286376321137">"SMS"</string>
+    <string name="keyboard_shortcut_group_applications_music" msgid="2051507523525651067">"តន្ត្រី"</string>
+    <string name="keyboard_shortcut_group_applications_calendar" msgid="3571770335653387606">"ប្រតិទិន"</string>
+    <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"ម៉ាស៊ីនគិតលេខ"</string>
+    <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"ផែនទី"</string>
+    <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"កម្មវិធី"</string>
 </resources>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index 413a35a..4ef70df 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -1144,7 +1144,7 @@
     <string name="weeks" msgid="3516247214269821391">"ವಾರಗಳು"</string>
     <string name="year" msgid="5182610307741238982">"ವರ್ಷ"</string>
     <string name="years" msgid="5797714729103773425">"ವರ್ಷಗಳು"</string>
-    <string name="now_string_shortest" msgid="3684914126941650330">"ಇದೀಗ"</string>
+    <string name="now_string_shortest" msgid="3684914126941650330">"ಈಗ"</string>
     <string name="duration_minutes_shortest" msgid="5744379079540806690">"<xliff:g id="COUNT">%d</xliff:g>ನಿ"</string>
     <string name="duration_hours_shortest" msgid="1477752094141971675">"<xliff:g id="COUNT">%d</xliff:g>ಗಂ"</string>
     <string name="duration_days_shortest" msgid="4083124701676227233">"<xliff:g id="COUNT">%d</xliff:g>ದಿ"</string>
@@ -1428,6 +1428,7 @@
     <string name="share_remote_bugreport_action" msgid="7630880678785123682">"ಹಂಚಿಕೊಳ್ಳಿ"</string>
     <string name="decline_remote_bugreport_action" msgid="4040894777519784346">"ನಿರಾಕರಿಸಿ"</string>
     <string name="select_input_method" msgid="3971267998568587025">"ಇನ್‌ಪುಟ್‌‌ ವಿಧಾನವನ್ನು ಆರಿಸಿ"</string>
+    <string name="input_method_language_settings" msgid="8069089418056819437">"ಭಾಷೆ ಸೆಟ್ಟಿಂಗ್‌ಗಳು"</string>
     <string name="show_ime" msgid="6406112007347443383">"ಭೌತಿಕ ಕೀಬೋರ್ಡ್ ಸಕ್ರಿಯವಾಗಿರುವಾಗ ಅದನ್ನು ಸ್ಕ್ರೀನ್ ಮೇಲಿರಿಸಿ"</string>
     <string name="hardware" msgid="3611039921284836033">"ಆನ್-ಸ್ಕ್ರೀನ್ ಕೀಬೋರ್ಡ್ ಬಳಸಿ"</string>
     <string name="select_keyboard_layout_notification_title" msgid="5823199895322205589">"<xliff:g id="DEVICE_NAME">%s</xliff:g> ಕಾನ್ಫಿಗರ್ ಮಾಡಿ"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index e0d3fb7..2cdc59c 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -1428,6 +1428,7 @@
     <string name="share_remote_bugreport_action" msgid="7630880678785123682">"공유"</string>
     <string name="decline_remote_bugreport_action" msgid="4040894777519784346">"거부"</string>
     <string name="select_input_method" msgid="3971267998568587025">"입력 방법 선택"</string>
+    <string name="input_method_language_settings" msgid="8069089418056819437">"언어 설정"</string>
     <string name="show_ime" msgid="6406112007347443383">"물리적 키보드가 활성 상태인 경우 화면에 켜 둠"</string>
     <string name="hardware" msgid="3611039921284836033">"터치 키보드 사용"</string>
     <string name="select_keyboard_layout_notification_title" msgid="5823199895322205589">"<xliff:g id="DEVICE_NAME">%s</xliff:g> 설정"</string>
@@ -2436,23 +2437,14 @@
     <string name="bg_user_sound_notification_title_alarm" msgid="5251678483393143527">"<xliff:g id="USER_NAME">%s</xliff:g>님의 알람"</string>
     <string name="bg_user_sound_notification_button_switch_user" msgid="3091969648572788946">"사용자 전환"</string>
     <string name="bg_user_sound_notification_button_mute" msgid="4942158515665615243">"음소거"</string>
-    <string name="bg_user_sound_notification_message" msgid="8613881975316976673">"탭하여 소리 음소거"</string>
-    <!-- no translation found for keyboard_shortcut_group_applications_browser (6535007304687100909) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_contacts (2750702518068326356) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_email (4229037666415353683) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_sms (3523799286376321137) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_music (2051507523525651067) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calendar (3571770335653387606) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calculator (6753209559716091507) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_maps (7950000659522589471) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications (3010389163951364798) -->
-    <skip />
+    <string name="bg_user_sound_notification_message" msgid="8613881975316976673">"탭하여 음소거"</string>
+    <string name="keyboard_shortcut_group_applications_browser" msgid="6535007304687100909">"브라우저"</string>
+    <string name="keyboard_shortcut_group_applications_contacts" msgid="2750702518068326356">"연락처"</string>
+    <string name="keyboard_shortcut_group_applications_email" msgid="4229037666415353683">"이메일"</string>
+    <string name="keyboard_shortcut_group_applications_sms" msgid="3523799286376321137">"SMS"</string>
+    <string name="keyboard_shortcut_group_applications_music" msgid="2051507523525651067">"음악"</string>
+    <string name="keyboard_shortcut_group_applications_calendar" msgid="3571770335653387606">"캘린더"</string>
+    <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"계산기"</string>
+    <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"지도"</string>
+    <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"애플리케이션"</string>
 </resources>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index e4c4603..82cb944 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -1428,6 +1428,7 @@
     <string name="share_remote_bugreport_action" msgid="7630880678785123682">"БӨЛҮШҮҮ"</string>
     <string name="decline_remote_bugreport_action" msgid="4040894777519784346">"ЧЕТКЕ КАГУУ"</string>
     <string name="select_input_method" msgid="3971267998568587025">"Дайын киргизүү ыкмасын тандаңыз"</string>
+    <string name="input_method_language_settings" msgid="8069089418056819437">"Тил параметрлери"</string>
     <string name="show_ime" msgid="6406112007347443383">"Баскычтоп иштетилгенде экранда көрүнүп турат"</string>
     <string name="hardware" msgid="3611039921284836033">"Экрандагы баскычтопту колдонуу"</string>
     <string name="select_keyboard_layout_notification_title" msgid="5823199895322205589">"<xliff:g id="DEVICE_NAME">%s</xliff:g> түзмөгүн конфигурациялоо"</string>
@@ -2434,25 +2435,16 @@
     <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"Тууралоо"</string>
     <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"Азыр эмес"</string>
     <string name="bg_user_sound_notification_title_alarm" msgid="5251678483393143527">"<xliff:g id="USER_NAME">%s</xliff:g> үчүн ойготкуч"</string>
-    <string name="bg_user_sound_notification_button_switch_user" msgid="3091969648572788946">"Колдонуучуну которуштуруу"</string>
+    <string name="bg_user_sound_notification_button_switch_user" msgid="3091969648572788946">"Башка колдонуучуга которулуу"</string>
     <string name="bg_user_sound_notification_button_mute" msgid="4942158515665615243">"Үнүн басуу"</string>
     <string name="bg_user_sound_notification_message" msgid="8613881975316976673">"Үнүн басуу үчүн таптап коюңуз"</string>
-    <!-- no translation found for keyboard_shortcut_group_applications_browser (6535007304687100909) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_contacts (2750702518068326356) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_email (4229037666415353683) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_sms (3523799286376321137) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_music (2051507523525651067) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calendar (3571770335653387606) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calculator (6753209559716091507) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_maps (7950000659522589471) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications (3010389163951364798) -->
-    <skip />
+    <string name="keyboard_shortcut_group_applications_browser" msgid="6535007304687100909">"Серепчи"</string>
+    <string name="keyboard_shortcut_group_applications_contacts" msgid="2750702518068326356">"Байланыштар"</string>
+    <string name="keyboard_shortcut_group_applications_email" msgid="4229037666415353683">"Электрондук почта"</string>
+    <string name="keyboard_shortcut_group_applications_sms" msgid="3523799286376321137">"SMS"</string>
+    <string name="keyboard_shortcut_group_applications_music" msgid="2051507523525651067">"Музыка"</string>
+    <string name="keyboard_shortcut_group_applications_calendar" msgid="3571770335653387606">"Жылнаама"</string>
+    <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Эсептегич"</string>
+    <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Карталар"</string>
+    <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Колдонмолор"</string>
 </resources>
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index ef5e51d..1f4bb88d 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -1428,6 +1428,7 @@
     <string name="share_remote_bugreport_action" msgid="7630880678785123682">"ແບ່ງປັນ"</string>
     <string name="decline_remote_bugreport_action" msgid="4040894777519784346">"ປະຕິເສດ"</string>
     <string name="select_input_method" msgid="3971267998568587025">"ເລືອກຮູບແບບການປ້ອນ"</string>
+    <string name="input_method_language_settings" msgid="8069089418056819437">"ການຕັ້ງຄ່າພາສາ"</string>
     <string name="show_ime" msgid="6406112007347443383">"ເປີດໃຊ້ໃຫ້ມັນຢູ່ໃນໜ້າຈໍໃນຂະນະທີ່ໃຊ້ແປ້ນພິມພາຍນອກຢູ່"</string>
     <string name="hardware" msgid="3611039921284836033">"ໃຊ້ແປ້ນພິມໃນໜ້າຈໍ"</string>
     <string name="select_keyboard_layout_notification_title" msgid="5823199895322205589">"ຕັ້ງຄ່າ <xliff:g id="DEVICE_NAME">%s</xliff:g>"</string>
@@ -2437,22 +2438,13 @@
     <string name="bg_user_sound_notification_button_switch_user" msgid="3091969648572788946">"ສະຫຼັບຜູ້ໃຊ້"</string>
     <string name="bg_user_sound_notification_button_mute" msgid="4942158515665615243">"ປິດສຽງ"</string>
     <string name="bg_user_sound_notification_message" msgid="8613881975316976673">"ແຕະເພື່ອປິດສຽງ"</string>
-    <!-- no translation found for keyboard_shortcut_group_applications_browser (6535007304687100909) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_contacts (2750702518068326356) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_email (4229037666415353683) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_sms (3523799286376321137) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_music (2051507523525651067) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calendar (3571770335653387606) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calculator (6753209559716091507) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_maps (7950000659522589471) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications (3010389163951364798) -->
-    <skip />
+    <string name="keyboard_shortcut_group_applications_browser" msgid="6535007304687100909">"ໂປຣແກຣມທ່ອງເວັບ"</string>
+    <string name="keyboard_shortcut_group_applications_contacts" msgid="2750702518068326356">"ລາຍຊື່ຜູ້ຕິດຕໍ່"</string>
+    <string name="keyboard_shortcut_group_applications_email" msgid="4229037666415353683">"ອີເມວ"</string>
+    <string name="keyboard_shortcut_group_applications_sms" msgid="3523799286376321137">"SMS"</string>
+    <string name="keyboard_shortcut_group_applications_music" msgid="2051507523525651067">"ເພງ"</string>
+    <string name="keyboard_shortcut_group_applications_calendar" msgid="3571770335653387606">"ປະຕິທິນ"</string>
+    <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"ຈັກຄິດໄລ່"</string>
+    <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"ແຜນທີ່"</string>
+    <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"ແອັບພລິເຄຊັນ"</string>
 </resources>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index e2d59fd..04ac536 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -1430,6 +1430,7 @@
     <string name="share_remote_bugreport_action" msgid="7630880678785123682">"BENDRINTI"</string>
     <string name="decline_remote_bugreport_action" msgid="4040894777519784346">"ATMESTI"</string>
     <string name="select_input_method" msgid="3971267998568587025">"Pasirinkite įvesties metodą"</string>
+    <string name="input_method_language_settings" msgid="8069089418056819437">"Kalbos nustatymai"</string>
     <string name="show_ime" msgid="6406112007347443383">"Palikti ekrane, kol fizinė klaviatūra aktyvi"</string>
     <string name="hardware" msgid="3611039921284836033">"Ekrano klaviatūros naudojimas"</string>
     <string name="select_keyboard_layout_notification_title" msgid="5823199895322205589">"„<xliff:g id="DEVICE_NAME">%s</xliff:g>“ konfigūravimas"</string>
@@ -2439,22 +2440,13 @@
     <string name="bg_user_sound_notification_button_switch_user" msgid="3091969648572788946">"Perjungti naudotoją"</string>
     <string name="bg_user_sound_notification_button_mute" msgid="4942158515665615243">"Nutildyti"</string>
     <string name="bg_user_sound_notification_message" msgid="8613881975316976673">"Palieskite, kad nutildytumėte garsą"</string>
-    <!-- no translation found for keyboard_shortcut_group_applications_browser (6535007304687100909) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_contacts (2750702518068326356) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_email (4229037666415353683) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_sms (3523799286376321137) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_music (2051507523525651067) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calendar (3571770335653387606) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calculator (6753209559716091507) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_maps (7950000659522589471) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications (3010389163951364798) -->
-    <skip />
+    <string name="keyboard_shortcut_group_applications_browser" msgid="6535007304687100909">"Naršyklė"</string>
+    <string name="keyboard_shortcut_group_applications_contacts" msgid="2750702518068326356">"Kontaktai"</string>
+    <string name="keyboard_shortcut_group_applications_email" msgid="4229037666415353683">"El. paštas"</string>
+    <string name="keyboard_shortcut_group_applications_sms" msgid="3523799286376321137">"SMS"</string>
+    <string name="keyboard_shortcut_group_applications_music" msgid="2051507523525651067">"Muzika"</string>
+    <string name="keyboard_shortcut_group_applications_calendar" msgid="3571770335653387606">"Kalendorius"</string>
+    <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Skaičiuotuvas"</string>
+    <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Žemėlapiai"</string>
+    <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Programos"</string>
 </resources>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index 42fb763..6d362e7 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -1412,10 +1412,8 @@
     <string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"Atlasiet, lai atspējotu bezvadu atkļūdošanu."</string>
     <string name="test_harness_mode_notification_title" msgid="2282785860014142511">"Drošības pārbaudes režīms ir iespējots"</string>
     <string name="test_harness_mode_notification_message" msgid="3039123743127958420">"Lai atspējotu drošības pārbaudes režīmu, veiciet rūpnīcas datu atiestatīšanu."</string>
-    <!-- no translation found for wrong_hsum_configuration_notification_title (7212758829332714385) -->
-    <skip />
-    <!-- no translation found for wrong_hsum_configuration_notification_message (5353475441480684381) -->
-    <skip />
+    <string name="wrong_hsum_configuration_notification_title" msgid="7212758829332714385">"Nepareiza bezgalvas sistēmas lietotāja režīma būvējuma konfigurācija"</string>
+    <string name="wrong_hsum_configuration_notification_message" msgid="5353475441480684381">"Šīs ierīces bezgalvas sistēmas lietotāja režīma stāvoklis atšķiras no tā būvējuma konfigurācijas. Lūdzu, atiestatiet ierīcē rūpnīcas datus."</string>
     <string name="console_running_notification_title" msgid="6087888939261635904">"Seriālā konsole ir iespējota"</string>
     <string name="console_running_notification_message" msgid="7892751888125174039">"Tiek ietekmēta veiktspēja. Lai atspējotu, pārbaudiet operētājsistēmu ielādes rīku."</string>
     <string name="mte_override_notification_title" msgid="4731115381962792944">"Eksperimentālais paplašinājums MTE iespējots"</string>
@@ -1431,6 +1429,7 @@
     <string name="share_remote_bugreport_action" msgid="7630880678785123682">"KOPĪGOT"</string>
     <string name="decline_remote_bugreport_action" msgid="4040894777519784346">"NORAIDĪT"</string>
     <string name="select_input_method" msgid="3971267998568587025">"Ievades metodes izvēle"</string>
+    <string name="input_method_language_settings" msgid="8069089418056819437">"Valodas iestatījumi"</string>
     <string name="show_ime" msgid="6406112007347443383">"Paturēt ekrānā, kamēr ir aktīva fiziskā tastatūra"</string>
     <string name="hardware" msgid="3611039921284836033">"Izmantojiet ekrāna tastatūru"</string>
     <string name="select_keyboard_layout_notification_title" msgid="5823199895322205589">"Jākonfigurē <xliff:g id="DEVICE_NAME">%s</xliff:g>"</string>
@@ -1760,12 +1759,9 @@
     <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>
-    <!-- no translation found for accessibility_button_prompt_text (6105393217162198616) -->
-    <skip />
-    <!-- no translation found for accessibility_gesture_prompt_text (6452246951969541792) -->
-    <skip />
-    <!-- no translation found for accessibility_gesture_3finger_prompt_text (77745752309056152) -->
-    <skip />
+    <string name="accessibility_button_prompt_text" msgid="6105393217162198616">"Izvēlieties funkciju"</string>
+    <string name="accessibility_gesture_prompt_text" msgid="6452246951969541792">"Izvēlieties funkciju"</string>
+    <string name="accessibility_gesture_3finger_prompt_text" msgid="77745752309056152">"Izvēlieties funkciju"</string>
     <string name="accessibility_button_instructional_text" msgid="6831154884557881996">"Funkcija tiks atvērta, kad nākamreiz pieskarsieties pieejamības pogai."</string>
     <string name="accessibility_gesture_instructional_text" msgid="4133877896011098550">"Funkcija tiks atvērta, kad nākamreiz izmantosiet šo saīsni. Velciet augšup ar diviem pirkstiem no ekrāna apakšdaļas un ātri atlaidiet."</string>
     <string name="accessibility_gesture_3finger_instructional_text" msgid="1124458279366968154">"Funkcija tiks atvērta, kad nākamreiz izmantosiet šo saīsni. Velciet augšup ar trim pirkstiem no ekrāna apakšdaļas un ātri atlaidiet."</string>
@@ -1891,8 +1887,7 @@
     <string name="restr_pin_error_too_short" msgid="1547007808237941065">"PIN ir pārāk īss. Tam ir jābūt vismaz 4 ciparus garam."</string>
     <string name="restr_pin_try_later" msgid="5897719962541636727">"Vēlāk mēģiniet vēlreiz."</string>
     <string name="immersive_cling_title" msgid="2307034298721541791">"Skatīšanās pilnekrāna režīmā"</string>
-    <!-- no translation found for immersive_cling_description (2896205051090870978) -->
-    <skip />
+    <string name="immersive_cling_description" msgid="2896205051090870978">"Lai izietu, velciet lejup no ekrāna augšdaļas."</string>
     <string name="immersive_cling_positive" msgid="7047498036346489883">"Labi"</string>
     <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Lai uzlabotu skatu, pagrieziet ekrānu."</string>
     <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Labākam skatam atveriet lietotni <xliff:g id="NAME">%s</xliff:g> pilnekrāna režīmā."</string>
@@ -2444,22 +2439,13 @@
     <string name="bg_user_sound_notification_button_switch_user" msgid="3091969648572788946">"Mainīt lietotāju"</string>
     <string name="bg_user_sound_notification_button_mute" msgid="4942158515665615243">"Izslēgt skaņu"</string>
     <string name="bg_user_sound_notification_message" msgid="8613881975316976673">"Pieskarieties, lai izslēgtu skaņu"</string>
-    <!-- no translation found for keyboard_shortcut_group_applications_browser (6535007304687100909) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_contacts (2750702518068326356) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_email (4229037666415353683) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_sms (3523799286376321137) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_music (2051507523525651067) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calendar (3571770335653387606) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calculator (6753209559716091507) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_maps (7950000659522589471) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications (3010389163951364798) -->
-    <skip />
+    <string name="keyboard_shortcut_group_applications_browser" msgid="6535007304687100909">"Pārlūkprogramma"</string>
+    <string name="keyboard_shortcut_group_applications_contacts" msgid="2750702518068326356">"Kontaktpersonas"</string>
+    <string name="keyboard_shortcut_group_applications_email" msgid="4229037666415353683">"E-pasts"</string>
+    <string name="keyboard_shortcut_group_applications_sms" msgid="3523799286376321137">"Īsziņas"</string>
+    <string name="keyboard_shortcut_group_applications_music" msgid="2051507523525651067">"Mūzika"</string>
+    <string name="keyboard_shortcut_group_applications_calendar" msgid="3571770335653387606">"Kalendārs"</string>
+    <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Kalkulators"</string>
+    <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Maps"</string>
+    <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Lietojumprogrammas"</string>
 </resources>
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index b3b3e37..c764026 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -1411,10 +1411,8 @@
     <string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"Изберете за да се оневозможи безжично отстранување грешки."</string>
     <string name="test_harness_mode_notification_title" msgid="2282785860014142511">"Овозможен е режимот на рамка за тестирање"</string>
     <string name="test_harness_mode_notification_message" msgid="3039123743127958420">"Извршете фабричко ресетирање за да го оневозможите режимот на рамка за тестирање."</string>
-    <!-- no translation found for wrong_hsum_configuration_notification_title (7212758829332714385) -->
-    <skip />
-    <!-- no translation found for wrong_hsum_configuration_notification_message (5353475441480684381) -->
-    <skip />
+    <string name="wrong_hsum_configuration_notification_title" msgid="7212758829332714385">"Погрешна конфигурација на верзија на HSUM"</string>
+    <string name="wrong_hsum_configuration_notification_message" msgid="5353475441480684381">"Состојбата на „Режимот на системскиот корисник без кориснички интерфејс“ на уредов се разликува од неговата конфигурација на верзија. Ресетирајте го уредот на фабрички поставки."</string>
     <string name="console_running_notification_title" msgid="6087888939261635904">"Сериската конзола е овозможена"</string>
     <string name="console_running_notification_message" msgid="7892751888125174039">"Перформансите се засегнати. За да оневозможите, проверете го подигнувачот."</string>
     <string name="mte_override_notification_title" msgid="4731115381962792944">"Овозможена е експериментална MTE"</string>
@@ -1430,6 +1428,7 @@
     <string name="share_remote_bugreport_action" msgid="7630880678785123682">"СПОДЕЛИ"</string>
     <string name="decline_remote_bugreport_action" msgid="4040894777519784346">"ОДБИЈ"</string>
     <string name="select_input_method" msgid="3971267998568587025">"Одбери метод на внес"</string>
+    <string name="input_method_language_settings" msgid="8069089418056819437">"Поставки за јазик"</string>
     <string name="show_ime" msgid="6406112007347443383">"Прикажувај ја на екранот додека е активна физичката тастатура"</string>
     <string name="hardware" msgid="3611039921284836033">"Користете тастатура на екран"</string>
     <string name="select_keyboard_layout_notification_title" msgid="5823199895322205589">"Конфигурирање на <xliff:g id="DEVICE_NAME">%s</xliff:g>"</string>
@@ -1759,12 +1758,9 @@
     <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>
-    <!-- no translation found for accessibility_button_prompt_text (6105393217162198616) -->
-    <skip />
-    <!-- no translation found for accessibility_gesture_prompt_text (6452246951969541792) -->
-    <skip />
-    <!-- no translation found for accessibility_gesture_3finger_prompt_text (77745752309056152) -->
-    <skip />
+    <string name="accessibility_button_prompt_text" msgid="6105393217162198616">"Изберете функција"</string>
+    <string name="accessibility_gesture_prompt_text" msgid="6452246951969541792">"Изберете функција"</string>
+    <string name="accessibility_gesture_3finger_prompt_text" msgid="77745752309056152">"Изберете функција"</string>
     <string name="accessibility_button_instructional_text" msgid="6831154884557881996">"Функцијата ќе се отвори следниот пат кога ќе го допрете копчето за пристапност"</string>
     <string name="accessibility_gesture_instructional_text" msgid="4133877896011098550">"Функцијата ќе се отвори следниот пат кога ќе ја користите кратенкава. Повлечете нагоре со 2 прста од долниот дел на екранот и брзо отпуштете."</string>
     <string name="accessibility_gesture_3finger_instructional_text" msgid="1124458279366968154">"Функцијата ќе се отвори следниот пат кога ќе ја користите кратенкава. Повлечете нагоре со 3 прста од долниот дел на екранот и брзо отпуштете."</string>
@@ -1890,8 +1886,7 @@
     <string name="restr_pin_error_too_short" msgid="1547007808237941065">"PIN кодот е премногу краток. Мора да има најмалку 4 цифри."</string>
     <string name="restr_pin_try_later" msgid="5897719962541636727">"Обиди се повторно подоцна"</string>
     <string name="immersive_cling_title" msgid="2307034298721541791">"Се прикажува на цел екран"</string>
-    <!-- no translation found for immersive_cling_description (2896205051090870978) -->
-    <skip />
+    <string name="immersive_cling_description" msgid="2896205051090870978">"За да излезете, повлечете надолу од горниот дел на екранот"</string>
     <string name="immersive_cling_positive" msgid="7047498036346489883">"Сфатив"</string>
     <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Ротирајте за подобар приказ"</string>
     <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"За подобар приказ, отворете ја апликацијата <xliff:g id="NAME">%s</xliff:g> на цел екран"</string>
@@ -2439,26 +2434,17 @@
     <string name="face_dangling_notification_msg" msgid="746235263598985384">"Вашиот модел на лик веќе не може да се препознае. Поставете „Отклучување со лик“ повторно."</string>
     <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"Поставете"</string>
     <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"Не сега"</string>
-    <string name="bg_user_sound_notification_title_alarm" msgid="5251678483393143527">"Аларм за <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="bg_user_sound_notification_title_alarm" msgid="5251678483393143527">"Аларм за: <xliff:g id="USER_NAME">%s</xliff:g>"</string>
     <string name="bg_user_sound_notification_button_switch_user" msgid="3091969648572788946">"Сменете го корисникот"</string>
     <string name="bg_user_sound_notification_button_mute" msgid="4942158515665615243">"Исклучи звук"</string>
     <string name="bg_user_sound_notification_message" msgid="8613881975316976673">"Допрете за да го исклучите звукот"</string>
-    <!-- no translation found for keyboard_shortcut_group_applications_browser (6535007304687100909) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_contacts (2750702518068326356) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_email (4229037666415353683) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_sms (3523799286376321137) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_music (2051507523525651067) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calendar (3571770335653387606) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calculator (6753209559716091507) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_maps (7950000659522589471) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications (3010389163951364798) -->
-    <skip />
+    <string name="keyboard_shortcut_group_applications_browser" msgid="6535007304687100909">"Прелистувач"</string>
+    <string name="keyboard_shortcut_group_applications_contacts" msgid="2750702518068326356">"Контакти"</string>
+    <string name="keyboard_shortcut_group_applications_email" msgid="4229037666415353683">"Е-пошта"</string>
+    <string name="keyboard_shortcut_group_applications_sms" msgid="3523799286376321137">"SMS"</string>
+    <string name="keyboard_shortcut_group_applications_music" msgid="2051507523525651067">"Музика"</string>
+    <string name="keyboard_shortcut_group_applications_calendar" msgid="3571770335653387606">"Календар"</string>
+    <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Калкулатор"</string>
+    <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Карти"</string>
+    <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Апликации"</string>
 </resources>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index f9a74e7..e1cff81 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -1428,6 +1428,7 @@
     <string name="share_remote_bugreport_action" msgid="7630880678785123682">"പങ്കിടുക"</string>
     <string name="decline_remote_bugreport_action" msgid="4040894777519784346">"നിരസിക്കുക"</string>
     <string name="select_input_method" msgid="3971267998568587025">"ഇൻപുട്ട് രീതി തിരഞ്ഞെടുക്കുക"</string>
+    <string name="input_method_language_settings" msgid="8069089418056819437">"ഭാഷാ ക്രമീകരണം"</string>
     <string name="show_ime" msgid="6406112007347443383">"ഫിസിക്കൽ കീബോർഡ് സജീവമായിരിക്കുമ്പോൾ സ്ക്രീനിൽ നിലനിർത്തുക"</string>
     <string name="hardware" msgid="3611039921284836033">"ഓൺ-സ്ക്രീൻ കീബോർഡ് ഉപയോഗിക്കൂ"</string>
     <string name="select_keyboard_layout_notification_title" msgid="5823199895322205589">"<xliff:g id="DEVICE_NAME">%s</xliff:g> കോൺഫിഗർ ചെയ്യുക"</string>
@@ -2437,22 +2438,13 @@
     <string name="bg_user_sound_notification_button_switch_user" msgid="3091969648572788946">"ഉപയോക്താവിനെ മാറുക"</string>
     <string name="bg_user_sound_notification_button_mute" msgid="4942158515665615243">"മ്യൂട്ടുചെയ്യുക"</string>
     <string name="bg_user_sound_notification_message" msgid="8613881975316976673">"ശബ്ദം മ്യൂട്ട് ചെയ്യാൻ ടാപ്പ് ചെയ്യുക"</string>
-    <!-- no translation found for keyboard_shortcut_group_applications_browser (6535007304687100909) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_contacts (2750702518068326356) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_email (4229037666415353683) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_sms (3523799286376321137) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_music (2051507523525651067) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calendar (3571770335653387606) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calculator (6753209559716091507) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_maps (7950000659522589471) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications (3010389163951364798) -->
-    <skip />
+    <string name="keyboard_shortcut_group_applications_browser" msgid="6535007304687100909">"ബ്രൗസർ"</string>
+    <string name="keyboard_shortcut_group_applications_contacts" msgid="2750702518068326356">"കോൺടാക്റ്റുകൾ"</string>
+    <string name="keyboard_shortcut_group_applications_email" msgid="4229037666415353683">"ഇമെയിൽ"</string>
+    <string name="keyboard_shortcut_group_applications_sms" msgid="3523799286376321137">"SMS"</string>
+    <string name="keyboard_shortcut_group_applications_music" msgid="2051507523525651067">"സംഗീതം"</string>
+    <string name="keyboard_shortcut_group_applications_calendar" msgid="3571770335653387606">"കലണ്ടർ"</string>
+    <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"കാൽക്കുലേറ്റർ"</string>
+    <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"മാപ്പുകൾ"</string>
+    <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"ആപ്പുകൾ"</string>
 </resources>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index dc92f63..90aee05 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -312,7 +312,7 @@
     <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Батарей, дата ашиглалтын талаар дэлгэрэнгүйг харахын тулд товшино уу"</string>
     <string name="foreground_service_multiple_separator" msgid="5002287361849863168">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
     <string name="safeMode" msgid="8974401416068943888">"Аюулгүй горим"</string>
-    <string name="android_system_label" msgid="5974767339591067210">"Андройд систем"</string>
+    <string name="android_system_label" msgid="5974767339591067210">"Android систем"</string>
     <string name="user_owner_label" msgid="8628726904184471211">"Хувийн профайл руу сэлгэх"</string>
     <string name="managed_profile_label" msgid="7316778766973512382">"Ажлын профайл руу сэлгэх"</string>
     <string name="user_owner_app_label" msgid="1553595155465750298">"Хувийн <xliff:g id="APP_NAME">%1$s</xliff:g> руу сэлгэх"</string>
@@ -1428,6 +1428,7 @@
     <string name="share_remote_bugreport_action" msgid="7630880678785123682">"ХУВААЛЦАХ"</string>
     <string name="decline_remote_bugreport_action" msgid="4040894777519784346">"ТАТГАЛЗАХ"</string>
     <string name="select_input_method" msgid="3971267998568587025">"Оруулах аргыг сонгоно уу"</string>
+    <string name="input_method_language_settings" msgid="8069089418056819437">"Хэлний тохиргоо"</string>
     <string name="show_ime" msgid="6406112007347443383">"Биет гар идэвхтэй үед үүнийг дэлгэцэд харуулна уу"</string>
     <string name="hardware" msgid="3611039921284836033">"Дэлгэц дээрх гарыг ашиглах"</string>
     <string name="select_keyboard_layout_notification_title" msgid="5823199895322205589">"<xliff:g id="DEVICE_NAME">%s</xliff:g>-г тохируулна уу"</string>
@@ -2437,22 +2438,13 @@
     <string name="bg_user_sound_notification_button_switch_user" msgid="3091969648572788946">"Хэрэглэгч сэлгэх"</string>
     <string name="bg_user_sound_notification_button_mute" msgid="4942158515665615243">"Дууг хаах"</string>
     <string name="bg_user_sound_notification_message" msgid="8613881975316976673">"Дууг хаахын тулд товшино уу"</string>
-    <!-- no translation found for keyboard_shortcut_group_applications_browser (6535007304687100909) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_contacts (2750702518068326356) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_email (4229037666415353683) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_sms (3523799286376321137) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_music (2051507523525651067) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calendar (3571770335653387606) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calculator (6753209559716091507) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_maps (7950000659522589471) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications (3010389163951364798) -->
-    <skip />
+    <string name="keyboard_shortcut_group_applications_browser" msgid="6535007304687100909">"Хөтөч"</string>
+    <string name="keyboard_shortcut_group_applications_contacts" msgid="2750702518068326356">"Харилцагчид"</string>
+    <string name="keyboard_shortcut_group_applications_email" msgid="4229037666415353683">"Имэйл"</string>
+    <string name="keyboard_shortcut_group_applications_sms" msgid="3523799286376321137">"SMS"</string>
+    <string name="keyboard_shortcut_group_applications_music" msgid="2051507523525651067">"Хөгжим"</string>
+    <string name="keyboard_shortcut_group_applications_calendar" msgid="3571770335653387606">"Календарь"</string>
+    <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Тооны машин"</string>
+    <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Газрын зураг"</string>
+    <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Аппликэйшн"</string>
 </resources>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index 3874200..9f88b1c 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -1428,6 +1428,7 @@
     <string name="share_remote_bugreport_action" msgid="7630880678785123682">"शेअर करा"</string>
     <string name="decline_remote_bugreport_action" msgid="4040894777519784346">"नकार द्या"</string>
     <string name="select_input_method" msgid="3971267998568587025">"इनपुट पद्धत निवडा"</string>
+    <string name="input_method_language_settings" msgid="8069089418056819437">"भाषा सेटिंग्ज"</string>
     <string name="show_ime" msgid="6406112007347443383">"भौतिक कीबोर्ड सक्रिय असताना त्यास स्क्रीनवर ठेवा"</string>
     <string name="hardware" msgid="3611039921284836033">"ऑन-स्क्रीन कीबोर्ड वापरा"</string>
     <string name="select_keyboard_layout_notification_title" msgid="5823199895322205589">"<xliff:g id="DEVICE_NAME">%s</xliff:g> कॉन्फिगर करा"</string>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index cc866b90..85a984d 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -1428,6 +1428,7 @@
     <string name="share_remote_bugreport_action" msgid="7630880678785123682">"KONGSI"</string>
     <string name="decline_remote_bugreport_action" msgid="4040894777519784346">"TOLAK"</string>
     <string name="select_input_method" msgid="3971267998568587025">"Pilih kaedah input"</string>
+    <string name="input_method_language_settings" msgid="8069089418056819437">"Tetapan Bahasa"</string>
     <string name="show_ime" msgid="6406112007347443383">"Pastikannya pada skrin, semasa papan kekunci fizikal aktif"</string>
     <string name="hardware" msgid="3611039921284836033">"Guna papan kekunci pada skrin"</string>
     <string name="select_keyboard_layout_notification_title" msgid="5823199895322205589">"Konfigurasikan <xliff:g id="DEVICE_NAME">%s</xliff:g>"</string>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index fca79cb..2ed315c 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -1428,6 +1428,7 @@
     <string name="share_remote_bugreport_action" msgid="7630880678785123682">"မျှဝေပါ"</string>
     <string name="decline_remote_bugreport_action" msgid="4040894777519784346">"ငြင်းပယ်ပါ"</string>
     <string name="select_input_method" msgid="3971267998568587025">"ထည့်သွင်းရေး နည်းကို ရွေးရန်"</string>
+    <string name="input_method_language_settings" msgid="8069089418056819437">"ဘာသာစကား ဆက်တင်များ"</string>
     <string name="show_ime" msgid="6406112007347443383">"စက်၏ကီးဘုတ် ဖွင့်ထားစဉ်တွင် ၎င်းကို ဖန်သားပြင်ပေါ်တွင် ဆက်ထားပါ"</string>
     <string name="hardware" msgid="3611039921284836033">"မျက်နှာပြင် လက်ကွက် သုံးခြင်း"</string>
     <string name="select_keyboard_layout_notification_title" msgid="5823199895322205589">"<xliff:g id="DEVICE_NAME">%s</xliff:g> ကို စီစဉ်သတ်မှတ်ရန်"</string>
@@ -2437,22 +2438,13 @@
     <string name="bg_user_sound_notification_button_switch_user" msgid="3091969648572788946">"အသုံးပြုသူ ပြောင်းရန်"</string>
     <string name="bg_user_sound_notification_button_mute" msgid="4942158515665615243">"အသံပိတ်ရန်"</string>
     <string name="bg_user_sound_notification_message" msgid="8613881975316976673">"အသံပိတ်ရန် တို့ပါ"</string>
-    <!-- no translation found for keyboard_shortcut_group_applications_browser (6535007304687100909) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_contacts (2750702518068326356) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_email (4229037666415353683) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_sms (3523799286376321137) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_music (2051507523525651067) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calendar (3571770335653387606) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calculator (6753209559716091507) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_maps (7950000659522589471) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications (3010389163951364798) -->
-    <skip />
+    <string name="keyboard_shortcut_group_applications_browser" msgid="6535007304687100909">"ဘရောင်ဇာ"</string>
+    <string name="keyboard_shortcut_group_applications_contacts" msgid="2750702518068326356">"အဆက်အသွယ်များ"</string>
+    <string name="keyboard_shortcut_group_applications_email" msgid="4229037666415353683">"အီးမေးလ်"</string>
+    <string name="keyboard_shortcut_group_applications_sms" msgid="3523799286376321137">"SMS စာတိုစနစ်"</string>
+    <string name="keyboard_shortcut_group_applications_music" msgid="2051507523525651067">"တေးဂီတ"</string>
+    <string name="keyboard_shortcut_group_applications_calendar" msgid="3571770335653387606">"ပြက္ခဒိန်"</string>
+    <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"ဂဏန်းတွက်စက်"</string>
+    <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Maps"</string>
+    <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"အပလီကေးရှင်းများ"</string>
 </resources>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 03db9c5..7918aa7 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -1428,6 +1428,7 @@
     <string name="share_remote_bugreport_action" msgid="7630880678785123682">"DEL"</string>
     <string name="decline_remote_bugreport_action" msgid="4040894777519784346">"AVSLÅ"</string>
     <string name="select_input_method" msgid="3971267998568587025">"Velg inndatametode"</string>
+    <string name="input_method_language_settings" msgid="8069089418056819437">"Språkinnstillinger"</string>
     <string name="show_ime" msgid="6406112007347443383">"Ha den på skjermen mens det fysiske tastaturet er aktivt"</string>
     <string name="hardware" msgid="3611039921284836033">"Bruk skjermtastaturet"</string>
     <string name="select_keyboard_layout_notification_title" msgid="5823199895322205589">"Konfigurer <xliff:g id="DEVICE_NAME">%s</xliff:g>"</string>
@@ -2437,22 +2438,13 @@
     <string name="bg_user_sound_notification_button_switch_user" msgid="3091969648572788946">"Bytt bruker"</string>
     <string name="bg_user_sound_notification_button_mute" msgid="4942158515665615243">"Kutt lyden"</string>
     <string name="bg_user_sound_notification_message" msgid="8613881975316976673">"Trykk for å kutte lyden"</string>
-    <!-- no translation found for keyboard_shortcut_group_applications_browser (6535007304687100909) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_contacts (2750702518068326356) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_email (4229037666415353683) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_sms (3523799286376321137) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_music (2051507523525651067) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calendar (3571770335653387606) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calculator (6753209559716091507) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_maps (7950000659522589471) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications (3010389163951364798) -->
-    <skip />
+    <string name="keyboard_shortcut_group_applications_browser" msgid="6535007304687100909">"Nettleser"</string>
+    <string name="keyboard_shortcut_group_applications_contacts" msgid="2750702518068326356">"Kontakter"</string>
+    <string name="keyboard_shortcut_group_applications_email" msgid="4229037666415353683">"E-post"</string>
+    <string name="keyboard_shortcut_group_applications_sms" msgid="3523799286376321137">"SMS"</string>
+    <string name="keyboard_shortcut_group_applications_music" msgid="2051507523525651067">"Musikk"</string>
+    <string name="keyboard_shortcut_group_applications_calendar" msgid="3571770335653387606">"Kalender"</string>
+    <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Kalkulator"</string>
+    <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Maps"</string>
+    <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Apper"</string>
 </resources>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index 6c58d16..340358d 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -1428,6 +1428,7 @@
     <string name="share_remote_bugreport_action" msgid="7630880678785123682">"सेयर गर्नुहोस्"</string>
     <string name="decline_remote_bugreport_action" msgid="4040894777519784346">"अस्वीकार गर्नुहोस्"</string>
     <string name="select_input_method" msgid="3971267998568587025">"निवेश विधि छान्नुहोस्"</string>
+    <string name="input_method_language_settings" msgid="8069089418056819437">"भाषासम्बन्धी सेटिङ"</string>
     <string name="show_ime" msgid="6406112007347443383">"फिजिकल किबोर्ड सक्रिय हुँदा यसलाई स्क्रिनमा राख्नुहोस्"</string>
     <string name="hardware" msgid="3611039921284836033">"अनस्क्रिन किबोर्ड प्रयोग गर्नुहोस्"</string>
     <string name="select_keyboard_layout_notification_title" msgid="5823199895322205589">"<xliff:g id="DEVICE_NAME">%s</xliff:g> कन्फिगर गर्नुहोस्"</string>
@@ -2437,22 +2438,13 @@
     <string name="bg_user_sound_notification_button_switch_user" msgid="3091969648572788946">"प्रयोगकर्ता बदल्नुहोस्"</string>
     <string name="bg_user_sound_notification_button_mute" msgid="4942158515665615243">"म्युट गर्नुहोस्"</string>
     <string name="bg_user_sound_notification_message" msgid="8613881975316976673">"साउन्ड म्युट गर्न ट्याप गर्नुहोस्"</string>
-    <!-- no translation found for keyboard_shortcut_group_applications_browser (6535007304687100909) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_contacts (2750702518068326356) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_email (4229037666415353683) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_sms (3523799286376321137) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_music (2051507523525651067) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calendar (3571770335653387606) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calculator (6753209559716091507) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_maps (7950000659522589471) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications (3010389163951364798) -->
-    <skip />
+    <string name="keyboard_shortcut_group_applications_browser" msgid="6535007304687100909">"ब्राउजर"</string>
+    <string name="keyboard_shortcut_group_applications_contacts" msgid="2750702518068326356">"कन्ट्याक्टहरू"</string>
+    <string name="keyboard_shortcut_group_applications_email" msgid="4229037666415353683">"इमेल"</string>
+    <string name="keyboard_shortcut_group_applications_sms" msgid="3523799286376321137">"SMS"</string>
+    <string name="keyboard_shortcut_group_applications_music" msgid="2051507523525651067">"सङ्गीत"</string>
+    <string name="keyboard_shortcut_group_applications_calendar" msgid="3571770335653387606">"पात्रो"</string>
+    <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"क्याल्कुलेटर"</string>
+    <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"नक्सा"</string>
+    <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"एपहरू"</string>
 </resources>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index 5fd6d42..45f00cd 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -1428,6 +1428,7 @@
     <string name="share_remote_bugreport_action" msgid="7630880678785123682">"DELEN"</string>
     <string name="decline_remote_bugreport_action" msgid="4040894777519784346">"WEIGEREN"</string>
     <string name="select_input_method" msgid="3971267998568587025">"Invoermethode selecteren"</string>
+    <string name="input_method_language_settings" msgid="8069089418056819437">"Taalinstellingen"</string>
     <string name="show_ime" msgid="6406112007347443383">"Toon op het scherm terwijl het fysieke toetsenbord actief is"</string>
     <string name="hardware" msgid="3611039921284836033">"Schermtoetsenbord gebruiken"</string>
     <string name="select_keyboard_layout_notification_title" msgid="5823199895322205589">"<xliff:g id="DEVICE_NAME">%s</xliff:g> instellen"</string>
@@ -2437,22 +2438,13 @@
     <string name="bg_user_sound_notification_button_switch_user" msgid="3091969648572788946">"Gebruiker wijzigen"</string>
     <string name="bg_user_sound_notification_button_mute" msgid="4942158515665615243">"Geluid uitzetten"</string>
     <string name="bg_user_sound_notification_message" msgid="8613881975316976673">"Tik om het geluid uit te zetten"</string>
-    <!-- no translation found for keyboard_shortcut_group_applications_browser (6535007304687100909) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_contacts (2750702518068326356) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_email (4229037666415353683) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_sms (3523799286376321137) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_music (2051507523525651067) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calendar (3571770335653387606) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calculator (6753209559716091507) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_maps (7950000659522589471) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications (3010389163951364798) -->
-    <skip />
+    <string name="keyboard_shortcut_group_applications_browser" msgid="6535007304687100909">"Browser"</string>
+    <string name="keyboard_shortcut_group_applications_contacts" msgid="2750702518068326356">"Contacten"</string>
+    <string name="keyboard_shortcut_group_applications_email" msgid="4229037666415353683">"E-mail"</string>
+    <string name="keyboard_shortcut_group_applications_sms" msgid="3523799286376321137">"Sms"</string>
+    <string name="keyboard_shortcut_group_applications_music" msgid="2051507523525651067">"Muziek"</string>
+    <string name="keyboard_shortcut_group_applications_calendar" msgid="3571770335653387606">"Agenda"</string>
+    <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Rekenmachine"</string>
+    <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Kaarten"</string>
+    <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Apps"</string>
 </resources>
diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml
index 883ef1f..97cd1da 100644
--- a/core/res/res/values-or/strings.xml
+++ b/core/res/res/values-or/strings.xml
@@ -1428,6 +1428,7 @@
     <string name="share_remote_bugreport_action" msgid="7630880678785123682">"ସେୟାର୍‌ କରନ୍ତୁ"</string>
     <string name="decline_remote_bugreport_action" msgid="4040894777519784346">"ଅଗ୍ରାହ୍ୟ କରନ୍ତୁ"</string>
     <string name="select_input_method" msgid="3971267998568587025">"ଇନପୁଟ୍ ପଦ୍ଧତି ବାଛନ୍ତୁ"</string>
+    <string name="input_method_language_settings" msgid="8069089418056819437">"ଭାଷା ସେଟିଂସ"</string>
     <string name="show_ime" msgid="6406112007347443383">"ଫିଜିକାଲ୍‌ କୀବୋର୍ଡ ସକ୍ରିୟ ଥିବାବେଳେ ଏହାକୁ ସ୍କ୍ରିନ୍‌ ଉପରେ ରଖନ୍ତୁ"</string>
     <string name="hardware" msgid="3611039921284836033">"ଅନ-ସ୍କ୍ରିନ କୀବୋର୍ଡ ବ୍ୟବହାର କର"</string>
     <string name="select_keyboard_layout_notification_title" msgid="5823199895322205589">"<xliff:g id="DEVICE_NAME">%s</xliff:g>କୁ କନଫିଗର କରନ୍ତୁ"</string>
@@ -2434,25 +2435,16 @@
     <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"ସେଟ ଅପ କରନ୍ତୁ"</string>
     <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"ବର୍ତ୍ତମାନ ନୁହେଁ"</string>
     <string name="bg_user_sound_notification_title_alarm" msgid="5251678483393143527">"<xliff:g id="USER_NAME">%s</xliff:g>ଙ୍କ ପାଇଁ ଆଲାରାମ"</string>
-    <string name="bg_user_sound_notification_button_switch_user" msgid="3091969648572788946">"ୟୁଜରଙ୍କୁ ସ୍ୱିଚ କରନ୍ତୁ"</string>
+    <string name="bg_user_sound_notification_button_switch_user" msgid="3091969648572788946">"ୟୁଜରଙ୍କୁ ସୁଇଚ କରନ୍ତୁ"</string>
     <string name="bg_user_sound_notification_button_mute" msgid="4942158515665615243">"ମ୍ୟୁଟ କରନ୍ତୁ"</string>
     <string name="bg_user_sound_notification_message" msgid="8613881975316976673">"ସାଉଣ୍ଡ ମ୍ୟୁଟ କରିବାକୁ ଟାପ କରନ୍ତୁ"</string>
-    <!-- no translation found for keyboard_shortcut_group_applications_browser (6535007304687100909) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_contacts (2750702518068326356) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_email (4229037666415353683) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_sms (3523799286376321137) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_music (2051507523525651067) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calendar (3571770335653387606) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calculator (6753209559716091507) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_maps (7950000659522589471) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications (3010389163951364798) -->
-    <skip />
+    <string name="keyboard_shortcut_group_applications_browser" msgid="6535007304687100909">"ବ୍ରାଉଜର"</string>
+    <string name="keyboard_shortcut_group_applications_contacts" msgid="2750702518068326356">"Contacts"</string>
+    <string name="keyboard_shortcut_group_applications_email" msgid="4229037666415353683">"ଇମେଲ"</string>
+    <string name="keyboard_shortcut_group_applications_sms" msgid="3523799286376321137">"SMS"</string>
+    <string name="keyboard_shortcut_group_applications_music" msgid="2051507523525651067">"ମ୍ୟୁଜିକ"</string>
+    <string name="keyboard_shortcut_group_applications_calendar" msgid="3571770335653387606">"Calendar"</string>
+    <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"କାଲକୁଲେଟର"</string>
+    <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Maps"</string>
+    <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"ଆପ୍ଲିକେସନଗୁଡ଼ିକ"</string>
 </resources>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index fda4943..5cd397e 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -1428,6 +1428,7 @@
     <string name="share_remote_bugreport_action" msgid="7630880678785123682">"ਸਾਂਝਾ ਕਰੋ"</string>
     <string name="decline_remote_bugreport_action" msgid="4040894777519784346">"ਅਸਵੀਕਾਰ ਕਰੋ"</string>
     <string name="select_input_method" msgid="3971267998568587025">"ਇਨਪੁਟ ਵਿਧੀ ਚੁਣੋ"</string>
+    <string name="input_method_language_settings" msgid="8069089418056819437">"ਭਾਸ਼ਾ ਸੈਟਿੰਗਾਂ"</string>
     <string name="show_ime" msgid="6406112007347443383">"ਭੌਤਿਕ ਕੀ-ਬੋਰਡ ਸਰਗਰਮ ਹੋਣ ਦੌਰਾਨ ਇਸ ਨੂੰ ਸਕ੍ਰੀਨ \'ਤੇ ਬਣਾਈ ਰੱਖੋ"</string>
     <string name="hardware" msgid="3611039921284836033">"ਆਨ-ਸਕ੍ਰੀਨ ਕੀ-ਬੋਰਡ ਨੂੰ ਵਰਤੋ"</string>
     <string name="select_keyboard_layout_notification_title" msgid="5823199895322205589">"<xliff:g id="DEVICE_NAME">%s</xliff:g> ਦਾ ਸੰਰੂਪਣ ਕਰੋ"</string>
@@ -2437,22 +2438,13 @@
     <string name="bg_user_sound_notification_button_switch_user" msgid="3091969648572788946">"ਵਰਤੋਂਕਾਰ ਬਦਲੋ"</string>
     <string name="bg_user_sound_notification_button_mute" msgid="4942158515665615243">"ਮਿਊਟ ਕਰੋ"</string>
     <string name="bg_user_sound_notification_message" msgid="8613881975316976673">"ਧੁਨੀ ਮਿਊਟ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ"</string>
-    <!-- no translation found for keyboard_shortcut_group_applications_browser (6535007304687100909) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_contacts (2750702518068326356) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_email (4229037666415353683) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_sms (3523799286376321137) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_music (2051507523525651067) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calendar (3571770335653387606) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calculator (6753209559716091507) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_maps (7950000659522589471) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications (3010389163951364798) -->
-    <skip />
+    <string name="keyboard_shortcut_group_applications_browser" msgid="6535007304687100909">"ਬ੍ਰਾਊਜ਼ਰ"</string>
+    <string name="keyboard_shortcut_group_applications_contacts" msgid="2750702518068326356">"Contacts"</string>
+    <string name="keyboard_shortcut_group_applications_email" msgid="4229037666415353683">"Email"</string>
+    <string name="keyboard_shortcut_group_applications_sms" msgid="3523799286376321137">"SMS"</string>
+    <string name="keyboard_shortcut_group_applications_music" msgid="2051507523525651067">"Music"</string>
+    <string name="keyboard_shortcut_group_applications_calendar" msgid="3571770335653387606">"Calendar"</string>
+    <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Calculator"</string>
+    <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Maps"</string>
+    <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"ਐਪਲੀਕੇਸ਼ਨਾਂ"</string>
 </resources>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index bfaf21c..474eb01 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -1430,6 +1430,7 @@
     <string name="share_remote_bugreport_action" msgid="7630880678785123682">"UDOSTĘPNIJ"</string>
     <string name="decline_remote_bugreport_action" msgid="4040894777519784346">"ODRZUĆ"</string>
     <string name="select_input_method" msgid="3971267998568587025">"Wybierz metodę wprowadzania"</string>
+    <string name="input_method_language_settings" msgid="8069089418056819437">"Ustawienia języka"</string>
     <string name="show_ime" msgid="6406112007347443383">"Pozostaw na ekranie, gdy aktywna jest klawiatura fizyczna"</string>
     <string name="hardware" msgid="3611039921284836033">"Używaj klawiatury ekranowej"</string>
     <string name="select_keyboard_layout_notification_title" msgid="5823199895322205589">"Skonfiguruj urządzenie <xliff:g id="DEVICE_NAME">%s</xliff:g>"</string>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index a6c07ef..0b9cbf3 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -1429,6 +1429,7 @@
     <string name="share_remote_bugreport_action" msgid="7630880678785123682">"COMPARTILHAR"</string>
     <string name="decline_remote_bugreport_action" msgid="4040894777519784346">"RECUSAR"</string>
     <string name="select_input_method" msgid="3971267998568587025">"Selecione o método de entrada"</string>
+    <string name="input_method_language_settings" msgid="8069089418056819437">"Configurações de idioma"</string>
     <string name="show_ime" msgid="6406112007347443383">"Mantém o teclado virtual na tela enquanto o teclado físico está ativo"</string>
     <string name="hardware" msgid="3611039921284836033">"Usar teclado na tela"</string>
     <string name="select_keyboard_layout_notification_title" msgid="5823199895322205589">"Configure o dispositivo <xliff:g id="DEVICE_NAME">%s</xliff:g>"</string>
@@ -2438,22 +2439,13 @@
     <string name="bg_user_sound_notification_button_switch_user" msgid="3091969648572788946">"Trocar usuário"</string>
     <string name="bg_user_sound_notification_button_mute" msgid="4942158515665615243">"Desativar som"</string>
     <string name="bg_user_sound_notification_message" msgid="8613881975316976673">"Toque para silenciar"</string>
-    <!-- no translation found for keyboard_shortcut_group_applications_browser (6535007304687100909) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_contacts (2750702518068326356) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_email (4229037666415353683) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_sms (3523799286376321137) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_music (2051507523525651067) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calendar (3571770335653387606) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calculator (6753209559716091507) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_maps (7950000659522589471) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications (3010389163951364798) -->
-    <skip />
+    <string name="keyboard_shortcut_group_applications_browser" msgid="6535007304687100909">"Navegador"</string>
+    <string name="keyboard_shortcut_group_applications_contacts" msgid="2750702518068326356">"Contatos"</string>
+    <string name="keyboard_shortcut_group_applications_email" msgid="4229037666415353683">"E-mail"</string>
+    <string name="keyboard_shortcut_group_applications_sms" msgid="3523799286376321137">"SMS"</string>
+    <string name="keyboard_shortcut_group_applications_music" msgid="2051507523525651067">"Música"</string>
+    <string name="keyboard_shortcut_group_applications_calendar" msgid="3571770335653387606">"Agenda"</string>
+    <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Calculadora"</string>
+    <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Mapas"</string>
+    <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Apps"</string>
 </resources>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 6c88408..94f446a 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -1429,6 +1429,7 @@
     <string name="share_remote_bugreport_action" msgid="7630880678785123682">"PARTILHAR"</string>
     <string name="decline_remote_bugreport_action" msgid="4040894777519784346">"RECUSAR"</string>
     <string name="select_input_method" msgid="3971267998568587025">"Escolher o método de entrada"</string>
+    <string name="input_method_language_settings" msgid="8069089418056819437">"Definições de idioma"</string>
     <string name="show_ime" msgid="6406112007347443383">"Mantê-lo no ecrã enquanto o teclado físico estiver ativo"</string>
     <string name="hardware" msgid="3611039921284836033">"Usar o teclado no ecrã"</string>
     <string name="select_keyboard_layout_notification_title" msgid="5823199895322205589">"Configure o dispositivo <xliff:g id="DEVICE_NAME">%s</xliff:g>"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index a6c07ef..0b9cbf3 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -1429,6 +1429,7 @@
     <string name="share_remote_bugreport_action" msgid="7630880678785123682">"COMPARTILHAR"</string>
     <string name="decline_remote_bugreport_action" msgid="4040894777519784346">"RECUSAR"</string>
     <string name="select_input_method" msgid="3971267998568587025">"Selecione o método de entrada"</string>
+    <string name="input_method_language_settings" msgid="8069089418056819437">"Configurações de idioma"</string>
     <string name="show_ime" msgid="6406112007347443383">"Mantém o teclado virtual na tela enquanto o teclado físico está ativo"</string>
     <string name="hardware" msgid="3611039921284836033">"Usar teclado na tela"</string>
     <string name="select_keyboard_layout_notification_title" msgid="5823199895322205589">"Configure o dispositivo <xliff:g id="DEVICE_NAME">%s</xliff:g>"</string>
@@ -2438,22 +2439,13 @@
     <string name="bg_user_sound_notification_button_switch_user" msgid="3091969648572788946">"Trocar usuário"</string>
     <string name="bg_user_sound_notification_button_mute" msgid="4942158515665615243">"Desativar som"</string>
     <string name="bg_user_sound_notification_message" msgid="8613881975316976673">"Toque para silenciar"</string>
-    <!-- no translation found for keyboard_shortcut_group_applications_browser (6535007304687100909) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_contacts (2750702518068326356) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_email (4229037666415353683) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_sms (3523799286376321137) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_music (2051507523525651067) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calendar (3571770335653387606) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calculator (6753209559716091507) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_maps (7950000659522589471) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications (3010389163951364798) -->
-    <skip />
+    <string name="keyboard_shortcut_group_applications_browser" msgid="6535007304687100909">"Navegador"</string>
+    <string name="keyboard_shortcut_group_applications_contacts" msgid="2750702518068326356">"Contatos"</string>
+    <string name="keyboard_shortcut_group_applications_email" msgid="4229037666415353683">"E-mail"</string>
+    <string name="keyboard_shortcut_group_applications_sms" msgid="3523799286376321137">"SMS"</string>
+    <string name="keyboard_shortcut_group_applications_music" msgid="2051507523525651067">"Música"</string>
+    <string name="keyboard_shortcut_group_applications_calendar" msgid="3571770335653387606">"Agenda"</string>
+    <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Calculadora"</string>
+    <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Mapas"</string>
+    <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Apps"</string>
 </resources>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index 4a94715..e7f4902 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -1429,6 +1429,7 @@
     <string name="share_remote_bugreport_action" msgid="7630880678785123682">"TRIMITE"</string>
     <string name="decline_remote_bugreport_action" msgid="4040894777519784346">"REFUZ"</string>
     <string name="select_input_method" msgid="3971267998568587025">"Alege metoda de introducere de text"</string>
+    <string name="input_method_language_settings" msgid="8069089418056819437">"Setări de limbă"</string>
     <string name="show_ime" msgid="6406112007347443383">"Se păstrează pe ecran cât timp este activată tastatura fizică"</string>
     <string name="hardware" msgid="3611039921284836033">"Folosește tastatura pe ecran"</string>
     <string name="select_keyboard_layout_notification_title" msgid="5823199895322205589">"Configurează <xliff:g id="DEVICE_NAME">%s</xliff:g>"</string>
@@ -2438,22 +2439,13 @@
     <string name="bg_user_sound_notification_button_switch_user" msgid="3091969648572788946">"Schimbă utilizatorul"</string>
     <string name="bg_user_sound_notification_button_mute" msgid="4942158515665615243">"Dezactivează sunetul"</string>
     <string name="bg_user_sound_notification_message" msgid="8613881975316976673">"Atinge pentru a dezactiva sunetul"</string>
-    <!-- no translation found for keyboard_shortcut_group_applications_browser (6535007304687100909) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_contacts (2750702518068326356) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_email (4229037666415353683) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_sms (3523799286376321137) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_music (2051507523525651067) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calendar (3571770335653387606) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calculator (6753209559716091507) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_maps (7950000659522589471) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications (3010389163951364798) -->
-    <skip />
+    <string name="keyboard_shortcut_group_applications_browser" msgid="6535007304687100909">"Browser"</string>
+    <string name="keyboard_shortcut_group_applications_contacts" msgid="2750702518068326356">"Agendă"</string>
+    <string name="keyboard_shortcut_group_applications_email" msgid="4229037666415353683">"E-mail"</string>
+    <string name="keyboard_shortcut_group_applications_sms" msgid="3523799286376321137">"SMS"</string>
+    <string name="keyboard_shortcut_group_applications_music" msgid="2051507523525651067">"Muzică"</string>
+    <string name="keyboard_shortcut_group_applications_calendar" msgid="3571770335653387606">"Calendar"</string>
+    <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Calculator"</string>
+    <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Maps"</string>
+    <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Aplicații"</string>
 </resources>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 96da732..370d015 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -1430,6 +1430,7 @@
     <string name="share_remote_bugreport_action" msgid="7630880678785123682">"ПРЕДОСТАВИТЬ ДОСТУП"</string>
     <string name="decline_remote_bugreport_action" msgid="4040894777519784346">"ОТКЛОНИТЬ"</string>
     <string name="select_input_method" msgid="3971267998568587025">"Выберите способ ввода"</string>
+    <string name="input_method_language_settings" msgid="8069089418056819437">"Языковые настройки"</string>
     <string name="show_ime" msgid="6406112007347443383">"Не скрывать экранную клавиатуру, когда включена физическая"</string>
     <string name="hardware" msgid="3611039921284836033">"Использовать экранную клавиатуру"</string>
     <string name="select_keyboard_layout_notification_title" msgid="5823199895322205589">"Настройте устройство \"<xliff:g id="DEVICE_NAME">%s</xliff:g>\""</string>
@@ -2439,22 +2440,13 @@
     <string name="bg_user_sound_notification_button_switch_user" msgid="3091969648572788946">"Сменить пользователя"</string>
     <string name="bg_user_sound_notification_button_mute" msgid="4942158515665615243">"Отключить звук"</string>
     <string name="bg_user_sound_notification_message" msgid="8613881975316976673">"Нажмите, чтобы отключить звук."</string>
-    <!-- no translation found for keyboard_shortcut_group_applications_browser (6535007304687100909) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_contacts (2750702518068326356) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_email (4229037666415353683) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_sms (3523799286376321137) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_music (2051507523525651067) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calendar (3571770335653387606) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calculator (6753209559716091507) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_maps (7950000659522589471) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications (3010389163951364798) -->
-    <skip />
+    <string name="keyboard_shortcut_group_applications_browser" msgid="6535007304687100909">"Браузер"</string>
+    <string name="keyboard_shortcut_group_applications_contacts" msgid="2750702518068326356">"Контакты"</string>
+    <string name="keyboard_shortcut_group_applications_email" msgid="4229037666415353683">"Электронная почта"</string>
+    <string name="keyboard_shortcut_group_applications_sms" msgid="3523799286376321137">"SMS"</string>
+    <string name="keyboard_shortcut_group_applications_music" msgid="2051507523525651067">"Музыка"</string>
+    <string name="keyboard_shortcut_group_applications_calendar" msgid="3571770335653387606">"Календарь"</string>
+    <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Калькулятор"</string>
+    <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Карты"</string>
+    <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Приложения"</string>
 </resources>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index 6549a7e..f3f5a700 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -1428,6 +1428,7 @@
     <string name="share_remote_bugreport_action" msgid="7630880678785123682">"බෙදා ගන්න"</string>
     <string name="decline_remote_bugreport_action" msgid="4040894777519784346">"ප්‍රතික්ෂේප කරන්න"</string>
     <string name="select_input_method" msgid="3971267998568587025">"ආදාන ක්‍රමයක් තෝරන්න"</string>
+    <string name="input_method_language_settings" msgid="8069089418056819437">"භාෂා සැකසීම්"</string>
     <string name="show_ime" msgid="6406112007347443383">"භෞතික යතුරු පුවරුව සක්‍රිය අතරතුර එය තිරය මත තබා ගන්න"</string>
     <string name="hardware" msgid="3611039921284836033">"තිරය මත යතුරු පුවරුව භාවිතය"</string>
     <string name="select_keyboard_layout_notification_title" msgid="5823199895322205589">"<xliff:g id="DEVICE_NAME">%s</xliff:g> වින්‍යාස කරන්න"</string>
@@ -2437,22 +2438,13 @@
     <string name="bg_user_sound_notification_button_switch_user" msgid="3091969648572788946">"පරිශීලක මාරු කරන්න"</string>
     <string name="bg_user_sound_notification_button_mute" msgid="4942158515665615243">"නිහඬ කරන්න"</string>
     <string name="bg_user_sound_notification_message" msgid="8613881975316976673">"ශබ්දය නිශ්ශබ්ද කිරීමට තට්ටු කරන්න"</string>
-    <!-- no translation found for keyboard_shortcut_group_applications_browser (6535007304687100909) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_contacts (2750702518068326356) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_email (4229037666415353683) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_sms (3523799286376321137) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_music (2051507523525651067) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calendar (3571770335653387606) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calculator (6753209559716091507) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_maps (7950000659522589471) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications (3010389163951364798) -->
-    <skip />
+    <string name="keyboard_shortcut_group_applications_browser" msgid="6535007304687100909">"බ්‍රවුසරය"</string>
+    <string name="keyboard_shortcut_group_applications_contacts" msgid="2750702518068326356">"සම්බන්ධතා"</string>
+    <string name="keyboard_shortcut_group_applications_email" msgid="4229037666415353683">"ඉ-තැපෑල"</string>
+    <string name="keyboard_shortcut_group_applications_sms" msgid="3523799286376321137">"SMS"</string>
+    <string name="keyboard_shortcut_group_applications_music" msgid="2051507523525651067">"සංගීතය"</string>
+    <string name="keyboard_shortcut_group_applications_calendar" msgid="3571770335653387606">"දින දර්ශනය"</string>
+    <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"ගණක යන්ත්‍රය"</string>
+    <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"සිතියම්"</string>
+    <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"යෙදුම්"</string>
 </resources>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index 27500c4..cd89ebb 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -1430,6 +1430,7 @@
     <string name="share_remote_bugreport_action" msgid="7630880678785123682">"ZDIEĽAŤ"</string>
     <string name="decline_remote_bugreport_action" msgid="4040894777519784346">"ODMIETNUŤ"</string>
     <string name="select_input_method" msgid="3971267998568587025">"Zvoliť metódu vstupu"</string>
+    <string name="input_method_language_settings" msgid="8069089418056819437">"Nastavenia jazyka"</string>
     <string name="show_ime" msgid="6406112007347443383">"Ponechať na obrazovke, keď je aktívna fyzická klávesnica"</string>
     <string name="hardware" msgid="3611039921284836033">"Použiť klávesnicu na obrazovke"</string>
     <string name="select_keyboard_layout_notification_title" msgid="5823199895322205589">"Nakonfigurujte <xliff:g id="DEVICE_NAME">%s</xliff:g>"</string>
@@ -2439,22 +2440,13 @@
     <string name="bg_user_sound_notification_button_switch_user" msgid="3091969648572788946">"Prepnúť používateľa"</string>
     <string name="bg_user_sound_notification_button_mute" msgid="4942158515665615243">"Ignorovať"</string>
     <string name="bg_user_sound_notification_message" msgid="8613881975316976673">"Klepnutím vypnite zvuk"</string>
-    <!-- no translation found for keyboard_shortcut_group_applications_browser (6535007304687100909) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_contacts (2750702518068326356) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_email (4229037666415353683) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_sms (3523799286376321137) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_music (2051507523525651067) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calendar (3571770335653387606) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calculator (6753209559716091507) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_maps (7950000659522589471) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications (3010389163951364798) -->
-    <skip />
+    <string name="keyboard_shortcut_group_applications_browser" msgid="6535007304687100909">"Prehliadač"</string>
+    <string name="keyboard_shortcut_group_applications_contacts" msgid="2750702518068326356">"Kontakty"</string>
+    <string name="keyboard_shortcut_group_applications_email" msgid="4229037666415353683">"E‑mail"</string>
+    <string name="keyboard_shortcut_group_applications_sms" msgid="3523799286376321137">"SMS"</string>
+    <string name="keyboard_shortcut_group_applications_music" msgid="2051507523525651067">"Hudba"</string>
+    <string name="keyboard_shortcut_group_applications_calendar" msgid="3571770335653387606">"Kalendár"</string>
+    <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Kalkulačka"</string>
+    <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Mapy"</string>
+    <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Aplikácie"</string>
 </resources>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 08043ec..568718d 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -1430,6 +1430,7 @@
     <string name="share_remote_bugreport_action" msgid="7630880678785123682">"DELJENJE"</string>
     <string name="decline_remote_bugreport_action" msgid="4040894777519784346">"NE SPREJMEM"</string>
     <string name="select_input_method" msgid="3971267998568587025">"Izberite način vnosa"</string>
+    <string name="input_method_language_settings" msgid="8069089418056819437">"Jezikovne nastavitve"</string>
     <string name="show_ime" msgid="6406112007347443383">"Ohrani na zaslonu, dokler je aktivna fizična tipkovnica"</string>
     <string name="hardware" msgid="3611039921284836033">"Uporaba zaslonske tipkovnice"</string>
     <string name="select_keyboard_layout_notification_title" msgid="5823199895322205589">"Konfiguriranje naprave <xliff:g id="DEVICE_NAME">%s</xliff:g>"</string>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index 9adae67..998c960 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -1428,6 +1428,7 @@
     <string name="share_remote_bugreport_action" msgid="7630880678785123682">"SHPËRNDAJ"</string>
     <string name="decline_remote_bugreport_action" msgid="4040894777519784346">"REFUZO"</string>
     <string name="select_input_method" msgid="3971267998568587025">"Zgjidh metodën e hyrjes"</string>
+    <string name="input_method_language_settings" msgid="8069089418056819437">"Cilësimet e gjuhës"</string>
     <string name="show_ime" msgid="6406112007347443383">"Mbaje në ekran ndërsa tastiera fizike është aktive"</string>
     <string name="hardware" msgid="3611039921284836033">"Përdor tastierën në ekran"</string>
     <string name="select_keyboard_layout_notification_title" msgid="5823199895322205589">"Konfiguro <xliff:g id="DEVICE_NAME">%s</xliff:g>"</string>
@@ -2437,22 +2438,13 @@
     <string name="bg_user_sound_notification_button_switch_user" msgid="3091969648572788946">"Ndërro përdoruesin"</string>
     <string name="bg_user_sound_notification_button_mute" msgid="4942158515665615243">"Hiqi zërin"</string>
     <string name="bg_user_sound_notification_message" msgid="8613881975316976673">"Trokit për t\'i hequr zërin"</string>
-    <!-- no translation found for keyboard_shortcut_group_applications_browser (6535007304687100909) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_contacts (2750702518068326356) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_email (4229037666415353683) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_sms (3523799286376321137) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_music (2051507523525651067) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calendar (3571770335653387606) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calculator (6753209559716091507) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_maps (7950000659522589471) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications (3010389163951364798) -->
-    <skip />
+    <string name="keyboard_shortcut_group_applications_browser" msgid="6535007304687100909">"Shfletuesi"</string>
+    <string name="keyboard_shortcut_group_applications_contacts" msgid="2750702518068326356">"Kontaktet"</string>
+    <string name="keyboard_shortcut_group_applications_email" msgid="4229037666415353683">"Email-i"</string>
+    <string name="keyboard_shortcut_group_applications_sms" msgid="3523799286376321137">"SMS-të"</string>
+    <string name="keyboard_shortcut_group_applications_music" msgid="2051507523525651067">"Muzika"</string>
+    <string name="keyboard_shortcut_group_applications_calendar" msgid="3571770335653387606">"Kalendari"</string>
+    <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Makina llogaritëse"</string>
+    <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Maps"</string>
+    <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Aplikacionet"</string>
 </resources>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 6cea89f..a88045c 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -1429,6 +1429,7 @@
     <string name="share_remote_bugreport_action" msgid="7630880678785123682">"ДЕЛИ"</string>
     <string name="decline_remote_bugreport_action" msgid="4040894777519784346">"ОДБИЈ"</string>
     <string name="select_input_method" msgid="3971267998568587025">"Избор метода уноса"</string>
+    <string name="input_method_language_settings" msgid="8069089418056819437">"Подешавања језика"</string>
     <string name="show_ime" msgid="6406112007347443383">"Задржава се на екрану док је физичка тастатура активна"</string>
     <string name="hardware" msgid="3611039921284836033">"Користи тастатуру на екрану"</string>
     <string name="select_keyboard_layout_notification_title" msgid="5823199895322205589">"Конфигуришите уређај <xliff:g id="DEVICE_NAME">%s</xliff:g>"</string>
@@ -2438,22 +2439,13 @@
     <string name="bg_user_sound_notification_button_switch_user" msgid="3091969648572788946">"Промени корисника"</string>
     <string name="bg_user_sound_notification_button_mute" msgid="4942158515665615243">"Искључи звук"</string>
     <string name="bg_user_sound_notification_message" msgid="8613881975316976673">"Додирните да бисте искључили звук"</string>
-    <!-- no translation found for keyboard_shortcut_group_applications_browser (6535007304687100909) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_contacts (2750702518068326356) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_email (4229037666415353683) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_sms (3523799286376321137) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_music (2051507523525651067) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calendar (3571770335653387606) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calculator (6753209559716091507) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_maps (7950000659522589471) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications (3010389163951364798) -->
-    <skip />
+    <string name="keyboard_shortcut_group_applications_browser" msgid="6535007304687100909">"Прегледач"</string>
+    <string name="keyboard_shortcut_group_applications_contacts" msgid="2750702518068326356">"Контакти"</string>
+    <string name="keyboard_shortcut_group_applications_email" msgid="4229037666415353683">"Имејл"</string>
+    <string name="keyboard_shortcut_group_applications_sms" msgid="3523799286376321137">"SMS"</string>
+    <string name="keyboard_shortcut_group_applications_music" msgid="2051507523525651067">"Музика"</string>
+    <string name="keyboard_shortcut_group_applications_calendar" msgid="3571770335653387606">"Календар"</string>
+    <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Калкулатор"</string>
+    <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Мапе"</string>
+    <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Апликације"</string>
 </resources>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 53f1408..8a8c3f3 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -1428,6 +1428,7 @@
     <string name="share_remote_bugreport_action" msgid="7630880678785123682">"DELA"</string>
     <string name="decline_remote_bugreport_action" msgid="4040894777519784346">"AVVISA"</string>
     <string name="select_input_method" msgid="3971267998568587025">"Välj inmatningsmetod"</string>
+    <string name="input_method_language_settings" msgid="8069089418056819437">"Språkinställningar"</string>
     <string name="show_ime" msgid="6406112007347443383">"Ha kvar det på skärmen när det fysiska tangentbordet används"</string>
     <string name="hardware" msgid="3611039921284836033">"Använd skärmtangentbord"</string>
     <string name="select_keyboard_layout_notification_title" msgid="5823199895322205589">"Konfigurera <xliff:g id="DEVICE_NAME">%s</xliff:g>"</string>
@@ -2437,22 +2438,13 @@
     <string name="bg_user_sound_notification_button_switch_user" msgid="3091969648572788946">"Byt användare"</string>
     <string name="bg_user_sound_notification_button_mute" msgid="4942158515665615243">"Ljud av"</string>
     <string name="bg_user_sound_notification_message" msgid="8613881975316976673">"Tryck för att stänga av ljudet"</string>
-    <!-- no translation found for keyboard_shortcut_group_applications_browser (6535007304687100909) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_contacts (2750702518068326356) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_email (4229037666415353683) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_sms (3523799286376321137) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_music (2051507523525651067) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calendar (3571770335653387606) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calculator (6753209559716091507) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_maps (7950000659522589471) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications (3010389163951364798) -->
-    <skip />
+    <string name="keyboard_shortcut_group_applications_browser" msgid="6535007304687100909">"Webbläsare"</string>
+    <string name="keyboard_shortcut_group_applications_contacts" msgid="2750702518068326356">"Kontakter"</string>
+    <string name="keyboard_shortcut_group_applications_email" msgid="4229037666415353683">"E-post"</string>
+    <string name="keyboard_shortcut_group_applications_sms" msgid="3523799286376321137">"Sms"</string>
+    <string name="keyboard_shortcut_group_applications_music" msgid="2051507523525651067">"Musik"</string>
+    <string name="keyboard_shortcut_group_applications_calendar" msgid="3571770335653387606">"Kalender"</string>
+    <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Kalkylator"</string>
+    <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Maps"</string>
+    <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Appar"</string>
 </resources>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index c096d4b..06a4b20 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -1428,6 +1428,7 @@
     <string name="share_remote_bugreport_action" msgid="7630880678785123682">"SHIRIKI"</string>
     <string name="decline_remote_bugreport_action" msgid="4040894777519784346">"KATAA"</string>
     <string name="select_input_method" msgid="3971267998568587025">"Chagua njia ya ingizo"</string>
+    <string name="input_method_language_settings" msgid="8069089418056819437">"Mipangilio ya Lugha"</string>
     <string name="show_ime" msgid="6406112007347443383">"Ionyeshe kwenye skrini wakati kibodi halisi inatumika"</string>
     <string name="hardware" msgid="3611039921284836033">"Tumia kibodi ya skrini"</string>
     <string name="select_keyboard_layout_notification_title" msgid="5823199895322205589">"Wekea mipangilio <xliff:g id="DEVICE_NAME">%s</xliff:g>"</string>
@@ -2433,26 +2434,17 @@
     <string name="face_dangling_notification_msg" msgid="746235263598985384">"Muundo wa uso wako hautambuliki tena. Weka tena mipangilio ya Kufungua kwa Uso."</string>
     <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"Weka mipangilio"</string>
     <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"Si sasa"</string>
-    <string name="bg_user_sound_notification_title_alarm" msgid="5251678483393143527">"King\'ora cha <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="bg_user_sound_notification_title_alarm" msgid="5251678483393143527">"Kengele ya <xliff:g id="USER_NAME">%s</xliff:g>"</string>
     <string name="bg_user_sound_notification_button_switch_user" msgid="3091969648572788946">"Badilisha mtumiaji"</string>
     <string name="bg_user_sound_notification_button_mute" msgid="4942158515665615243">"Zima sauti"</string>
     <string name="bg_user_sound_notification_message" msgid="8613881975316976673">"Gusa ili uzime sauti"</string>
-    <!-- no translation found for keyboard_shortcut_group_applications_browser (6535007304687100909) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_contacts (2750702518068326356) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_email (4229037666415353683) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_sms (3523799286376321137) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_music (2051507523525651067) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calendar (3571770335653387606) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calculator (6753209559716091507) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_maps (7950000659522589471) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications (3010389163951364798) -->
-    <skip />
+    <string name="keyboard_shortcut_group_applications_browser" msgid="6535007304687100909">"Kivinjari"</string>
+    <string name="keyboard_shortcut_group_applications_contacts" msgid="2750702518068326356">"Anwani"</string>
+    <string name="keyboard_shortcut_group_applications_email" msgid="4229037666415353683">"Barua pepe"</string>
+    <string name="keyboard_shortcut_group_applications_sms" msgid="3523799286376321137">"SMS"</string>
+    <string name="keyboard_shortcut_group_applications_music" msgid="2051507523525651067">"Muziki"</string>
+    <string name="keyboard_shortcut_group_applications_calendar" msgid="3571770335653387606">"Kalenda"</string>
+    <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Kikokotoo"</string>
+    <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Ramani"</string>
+    <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Programu"</string>
 </resources>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index 429862b..4f2dee2 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -1428,6 +1428,7 @@
     <string name="share_remote_bugreport_action" msgid="7630880678785123682">"பகிர்"</string>
     <string name="decline_remote_bugreport_action" msgid="4040894777519784346">"வேண்டாம்"</string>
     <string name="select_input_method" msgid="3971267998568587025">"உள்ளீட்டு முறையைத் தேர்வுசெய்க"</string>
+    <string name="input_method_language_settings" msgid="8069089418056819437">"மொழி அமைப்புகள்"</string>
     <string name="show_ime" msgid="6406112007347443383">"கைமுறை கீபோர்டு இயக்கத்தில் இருக்கும் போது IMEஐ திரையில் வைத்திரு"</string>
     <string name="hardware" msgid="3611039921284836033">"ஸ்கிரீன் கீபோர்டை உபயோகி"</string>
     <string name="select_keyboard_layout_notification_title" msgid="5823199895322205589">"<xliff:g id="DEVICE_NAME">%s</xliff:g> சாதனத்தை உள்ளமைத்தல்"</string>
@@ -2437,22 +2438,13 @@
     <string name="bg_user_sound_notification_button_switch_user" msgid="3091969648572788946">"பயனரை மாற்றுங்கள்"</string>
     <string name="bg_user_sound_notification_button_mute" msgid="4942158515665615243">"ஒலியடக்கு"</string>
     <string name="bg_user_sound_notification_message" msgid="8613881975316976673">"ஒலியடக்க தட்டவும்"</string>
-    <!-- no translation found for keyboard_shortcut_group_applications_browser (6535007304687100909) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_contacts (2750702518068326356) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_email (4229037666415353683) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_sms (3523799286376321137) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_music (2051507523525651067) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calendar (3571770335653387606) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calculator (6753209559716091507) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_maps (7950000659522589471) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications (3010389163951364798) -->
-    <skip />
+    <string name="keyboard_shortcut_group_applications_browser" msgid="6535007304687100909">"உலாவி"</string>
+    <string name="keyboard_shortcut_group_applications_contacts" msgid="2750702518068326356">"தொடர்புகள்"</string>
+    <string name="keyboard_shortcut_group_applications_email" msgid="4229037666415353683">"மின்னஞ்சல்"</string>
+    <string name="keyboard_shortcut_group_applications_sms" msgid="3523799286376321137">"மெசேஜ்"</string>
+    <string name="keyboard_shortcut_group_applications_music" msgid="2051507523525651067">"இசை"</string>
+    <string name="keyboard_shortcut_group_applications_calendar" msgid="3571770335653387606">"கேலெண்டர்"</string>
+    <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"கால்குலேட்டர்"</string>
+    <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Maps"</string>
+    <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"ஆப்ஸ்"</string>
 </resources>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index 830c4f6..5c1f8b8 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -1428,6 +1428,7 @@
     <string name="share_remote_bugreport_action" msgid="7630880678785123682">"షేర్ చేయండి"</string>
     <string name="decline_remote_bugreport_action" msgid="4040894777519784346">"తిరస్కరిస్తున్నాను"</string>
     <string name="select_input_method" msgid="3971267998568587025">"ఇన్‌పుట్ పద్ధతిని ఎంచుకోండి"</string>
+    <string name="input_method_language_settings" msgid="8069089418056819437">"భాషా సెట్టింగ్‌లు"</string>
     <string name="show_ime" msgid="6406112007347443383">"దీన్ని భౌతిక కీబోర్డ్ యాక్టివ్‌గా ఉన్నప్పుడు స్క్రీన్‌పై ఉంచుతుంది"</string>
     <string name="hardware" msgid="3611039921284836033">"స్క్రీన్‌పై కీబోర్డ్ ఉపయోగించు"</string>
     <string name="select_keyboard_layout_notification_title" msgid="5823199895322205589">"<xliff:g id="DEVICE_NAME">%s</xliff:g>‌ను కాన్ఫిగర్ చేయండి"</string>
@@ -2437,22 +2438,13 @@
     <string name="bg_user_sound_notification_button_switch_user" msgid="3091969648572788946">"యూజర్‌ను మార్చండి"</string>
     <string name="bg_user_sound_notification_button_mute" msgid="4942158515665615243">"మ్యూట్ చేయండి"</string>
     <string name="bg_user_sound_notification_message" msgid="8613881975316976673">"సౌండ్‌ను మ్యూట్ చేయడానికి ట్యాప్ చేయండి"</string>
-    <!-- no translation found for keyboard_shortcut_group_applications_browser (6535007304687100909) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_contacts (2750702518068326356) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_email (4229037666415353683) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_sms (3523799286376321137) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_music (2051507523525651067) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calendar (3571770335653387606) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calculator (6753209559716091507) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_maps (7950000659522589471) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications (3010389163951364798) -->
-    <skip />
+    <string name="keyboard_shortcut_group_applications_browser" msgid="6535007304687100909">"బ్రౌజర్"</string>
+    <string name="keyboard_shortcut_group_applications_contacts" msgid="2750702518068326356">"కాంటాక్ట్‌లు"</string>
+    <string name="keyboard_shortcut_group_applications_email" msgid="4229037666415353683">"ఈమెయిల్"</string>
+    <string name="keyboard_shortcut_group_applications_sms" msgid="3523799286376321137">"SMS"</string>
+    <string name="keyboard_shortcut_group_applications_music" msgid="2051507523525651067">"మ్యూజిక్"</string>
+    <string name="keyboard_shortcut_group_applications_calendar" msgid="3571770335653387606">"క్యాలెండర్"</string>
+    <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"క్యాలిక్యులేటర్"</string>
+    <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Maps"</string>
+    <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"అప్లికేషన్‌లు"</string>
 </resources>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index b4f3979..9096a03 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -1428,6 +1428,7 @@
     <string name="share_remote_bugreport_action" msgid="7630880678785123682">"แชร์"</string>
     <string name="decline_remote_bugreport_action" msgid="4040894777519784346">"ปฏิเสธ"</string>
     <string name="select_input_method" msgid="3971267998568587025">"เลือกวิธีการป้อนข้อมูล"</string>
+    <string name="input_method_language_settings" msgid="8069089418056819437">"การตั้งค่าภาษา"</string>
     <string name="show_ime" msgid="6406112007347443383">"เปิดทิ้งไว้บนหน้าจอในระหว่างใช้งานแป้นพิมพ์จริง"</string>
     <string name="hardware" msgid="3611039921284836033">"ใช้แป้นพิมพ์บนหน้าจอ"</string>
     <string name="select_keyboard_layout_notification_title" msgid="5823199895322205589">"กำหนดค่า <xliff:g id="DEVICE_NAME">%s</xliff:g>"</string>
@@ -2433,7 +2434,7 @@
     <string name="face_dangling_notification_msg" msgid="746235263598985384">"ระบบไม่จดจำรูปแบบใบหน้าของคุณอีกต่อไป ตั้งค่าการปลดล็อกด้วยใบหน้าอีกครั้ง"</string>
     <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"ตั้งค่า"</string>
     <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"ไว้ทีหลัง"</string>
-    <string name="bg_user_sound_notification_title_alarm" msgid="5251678483393143527">"นาฬิกาปลุกสำหรับ <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="bg_user_sound_notification_title_alarm" msgid="5251678483393143527">"นาฬิกาปลุกสำหรับ \"<xliff:g id="USER_NAME">%s</xliff:g>\""</string>
     <string name="bg_user_sound_notification_button_switch_user" msgid="3091969648572788946">"สลับผู้ใช้"</string>
     <string name="bg_user_sound_notification_button_mute" msgid="4942158515665615243">"ปิดเสียง"</string>
     <string name="bg_user_sound_notification_message" msgid="8613881975316976673">"แตะเพื่อปิดเสียง"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index bebc2b4..9febadb 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -1428,6 +1428,7 @@
     <string name="share_remote_bugreport_action" msgid="7630880678785123682">"IBAHAGI"</string>
     <string name="decline_remote_bugreport_action" msgid="4040894777519784346">"TANGGIHAN"</string>
     <string name="select_input_method" msgid="3971267998568587025">"Pumili ng pamamaraan ng pag-input"</string>
+    <string name="input_method_language_settings" msgid="8069089418056819437">"Mga Setting ng Wika"</string>
     <string name="show_ime" msgid="6406112007347443383">"Panatilihin ito sa screen habang aktibo ang pisikal na keyboard"</string>
     <string name="hardware" msgid="3611039921284836033">"Gumamit ng on-screen keyboard"</string>
     <string name="select_keyboard_layout_notification_title" msgid="5823199895322205589">"I-configure ang <xliff:g id="DEVICE_NAME">%s</xliff:g>"</string>
@@ -2433,7 +2434,7 @@
     <string name="face_dangling_notification_msg" msgid="746235263598985384">"Hindi na makilala ang iyong face model. I-set up ulit ang Pag-unlock Gamit ang Mukha."</string>
     <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"I-set up"</string>
     <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"Huwag muna"</string>
-    <string name="bg_user_sound_notification_title_alarm" msgid="5251678483393143527">"Alarm para kay <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="bg_user_sound_notification_title_alarm" msgid="5251678483393143527">"Alarm para kay/sa <xliff:g id="USER_NAME">%s</xliff:g>"</string>
     <string name="bg_user_sound_notification_button_switch_user" msgid="3091969648572788946">"Magpalit ng user"</string>
     <string name="bg_user_sound_notification_button_mute" msgid="4942158515665615243">"I-mute"</string>
     <string name="bg_user_sound_notification_message" msgid="8613881975316976673">"I-tap para i-mute ang tunog"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 72404af..9e73054 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -1428,6 +1428,7 @@
     <string name="share_remote_bugreport_action" msgid="7630880678785123682">"PAYLAŞ"</string>
     <string name="decline_remote_bugreport_action" msgid="4040894777519784346">"REDDET"</string>
     <string name="select_input_method" msgid="3971267998568587025">"Giriş yöntemini seçin"</string>
+    <string name="input_method_language_settings" msgid="8069089418056819437">"Dil Ayarları"</string>
     <string name="show_ime" msgid="6406112007347443383">"Fiziksel klavye etkin durumdayken ekranda tut"</string>
     <string name="hardware" msgid="3611039921284836033">"Ekran klavyesi kullanın"</string>
     <string name="select_keyboard_layout_notification_title" msgid="5823199895322205589">"<xliff:g id="DEVICE_NAME">%s</xliff:g> ayarlarını yapılandır"</string>
@@ -2437,22 +2438,13 @@
     <string name="bg_user_sound_notification_button_switch_user" msgid="3091969648572788946">"Kullanıcı değiştir"</string>
     <string name="bg_user_sound_notification_button_mute" msgid="4942158515665615243">"Sesi kapat"</string>
     <string name="bg_user_sound_notification_message" msgid="8613881975316976673">"Sesi kapat düğmesine dokunun"</string>
-    <!-- no translation found for keyboard_shortcut_group_applications_browser (6535007304687100909) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_contacts (2750702518068326356) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_email (4229037666415353683) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_sms (3523799286376321137) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_music (2051507523525651067) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calendar (3571770335653387606) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calculator (6753209559716091507) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_maps (7950000659522589471) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications (3010389163951364798) -->
-    <skip />
+    <string name="keyboard_shortcut_group_applications_browser" msgid="6535007304687100909">"Tarayıcı"</string>
+    <string name="keyboard_shortcut_group_applications_contacts" msgid="2750702518068326356">"Kişiler"</string>
+    <string name="keyboard_shortcut_group_applications_email" msgid="4229037666415353683">"E-posta"</string>
+    <string name="keyboard_shortcut_group_applications_sms" msgid="3523799286376321137">"SMS"</string>
+    <string name="keyboard_shortcut_group_applications_music" msgid="2051507523525651067">"Müzik"</string>
+    <string name="keyboard_shortcut_group_applications_calendar" msgid="3571770335653387606">"Takvim"</string>
+    <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Hesap Makinesi"</string>
+    <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Haritalar"</string>
+    <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Uygulamalar"</string>
 </resources>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 246cce9..e7f9ddf 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -1430,6 +1430,7 @@
     <string name="share_remote_bugreport_action" msgid="7630880678785123682">"ПОДІЛИТИСЯ"</string>
     <string name="decline_remote_bugreport_action" msgid="4040894777519784346">"ВІДХИЛИТИ"</string>
     <string name="select_input_method" msgid="3971267998568587025">"Вибрати метод введення"</string>
+    <string name="input_method_language_settings" msgid="8069089418056819437">"Налаштування мови"</string>
     <string name="show_ime" msgid="6406112007347443383">"Утримуйте на екрані, коли активна фізична клавіатура"</string>
     <string name="hardware" msgid="3611039921284836033">"Екранна клавіатура"</string>
     <string name="select_keyboard_layout_notification_title" msgid="5823199895322205589">"Налаштуйте клавіатуру \"<xliff:g id="DEVICE_NAME">%s</xliff:g>\""</string>
@@ -2439,22 +2440,13 @@
     <string name="bg_user_sound_notification_button_switch_user" msgid="3091969648572788946">"Змінити користувача"</string>
     <string name="bg_user_sound_notification_button_mute" msgid="4942158515665615243">"Вимкнути звук"</string>
     <string name="bg_user_sound_notification_message" msgid="8613881975316976673">"Натисніть, щоб вимкнути звук"</string>
-    <!-- no translation found for keyboard_shortcut_group_applications_browser (6535007304687100909) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_contacts (2750702518068326356) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_email (4229037666415353683) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_sms (3523799286376321137) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_music (2051507523525651067) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calendar (3571770335653387606) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calculator (6753209559716091507) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_maps (7950000659522589471) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications (3010389163951364798) -->
-    <skip />
+    <string name="keyboard_shortcut_group_applications_browser" msgid="6535007304687100909">"Вебпереглядач"</string>
+    <string name="keyboard_shortcut_group_applications_contacts" msgid="2750702518068326356">"Контакти"</string>
+    <string name="keyboard_shortcut_group_applications_email" msgid="4229037666415353683">"Електронна пошта"</string>
+    <string name="keyboard_shortcut_group_applications_sms" msgid="3523799286376321137">"SMS"</string>
+    <string name="keyboard_shortcut_group_applications_music" msgid="2051507523525651067">"Музика"</string>
+    <string name="keyboard_shortcut_group_applications_calendar" msgid="3571770335653387606">"Календар"</string>
+    <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Калькулятор"</string>
+    <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Карти"</string>
+    <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Додатки"</string>
 </resources>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index e613dd7..26f8f63 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -1428,6 +1428,7 @@
     <string name="share_remote_bugreport_action" msgid="7630880678785123682">"اشتراک کریں"</string>
     <string name="decline_remote_bugreport_action" msgid="4040894777519784346">"مسترد کریں"</string>
     <string name="select_input_method" msgid="3971267998568587025">"ان پٹ کا طریقہ منتخب کریں"</string>
+    <string name="input_method_language_settings" msgid="8069089418056819437">"زبان کی ترتیبات"</string>
     <string name="show_ime" msgid="6406112007347443383">"‏جب فزیکل کی بورڈ فعال ہو تو IME کو اسکرین پر رکھیں"</string>
     <string name="hardware" msgid="3611039921284836033">"آن اسکرین کی بورڈ استعمال کریں"</string>
     <string name="select_keyboard_layout_notification_title" msgid="5823199895322205589">"<xliff:g id="DEVICE_NAME">%s</xliff:g> کنفیگر کریں"</string>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index 994950c..1670050 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -1428,6 +1428,7 @@
     <string name="share_remote_bugreport_action" msgid="7630880678785123682">"BAHAM KO‘RISH"</string>
     <string name="decline_remote_bugreport_action" msgid="4040894777519784346">"RAD ETISH"</string>
     <string name="select_input_method" msgid="3971267998568587025">"Matn kiritish usulini tanlang"</string>
+    <string name="input_method_language_settings" msgid="8069089418056819437">"Til sozlamalari"</string>
     <string name="show_ime" msgid="6406112007347443383">"Tashqi klaviatura ulanganida ekranda chiqib turadi"</string>
     <string name="hardware" msgid="3611039921284836033">"Ekrandagi klaviatura"</string>
     <string name="select_keyboard_layout_notification_title" msgid="5823199895322205589">"Sozlang: <xliff:g id="DEVICE_NAME">%s</xliff:g>"</string>
@@ -2437,22 +2438,13 @@
     <string name="bg_user_sound_notification_button_switch_user" msgid="3091969648572788946">"Foydalanuvchini almashtirish"</string>
     <string name="bg_user_sound_notification_button_mute" msgid="4942158515665615243">"Ovozsiz qilish"</string>
     <string name="bg_user_sound_notification_message" msgid="8613881975316976673">"Tovushsiz qilish uchun bosing"</string>
-    <!-- no translation found for keyboard_shortcut_group_applications_browser (6535007304687100909) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_contacts (2750702518068326356) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_email (4229037666415353683) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_sms (3523799286376321137) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_music (2051507523525651067) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calendar (3571770335653387606) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calculator (6753209559716091507) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_maps (7950000659522589471) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications (3010389163951364798) -->
-    <skip />
+    <string name="keyboard_shortcut_group_applications_browser" msgid="6535007304687100909">"Brauzer"</string>
+    <string name="keyboard_shortcut_group_applications_contacts" msgid="2750702518068326356">"Kontaktlar"</string>
+    <string name="keyboard_shortcut_group_applications_email" msgid="4229037666415353683">"Email"</string>
+    <string name="keyboard_shortcut_group_applications_sms" msgid="3523799286376321137">"SMS"</string>
+    <string name="keyboard_shortcut_group_applications_music" msgid="2051507523525651067">"Musiqa"</string>
+    <string name="keyboard_shortcut_group_applications_calendar" msgid="3571770335653387606">"Taqvim"</string>
+    <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Kalkulyator"</string>
+    <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Xaritalar"</string>
+    <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Ilovalar"</string>
 </resources>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index f825f7b..6e976a9 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -1428,6 +1428,7 @@
     <string name="share_remote_bugreport_action" msgid="7630880678785123682">"CHIA SẺ"</string>
     <string name="decline_remote_bugreport_action" msgid="4040894777519784346">"TỪ CHỐI"</string>
     <string name="select_input_method" msgid="3971267998568587025">"Chọn phương thức nhập"</string>
+    <string name="input_method_language_settings" msgid="8069089418056819437">"Cài đặt ngôn ngữ"</string>
     <string name="show_ime" msgid="6406112007347443383">"Hiện bàn phím ảo trên màn hình trong khi bàn phím vật lý đang hoạt động"</string>
     <string name="hardware" msgid="3611039921284836033">"Sử dụng bàn phím ảo"</string>
     <string name="select_keyboard_layout_notification_title" msgid="5823199895322205589">"Định cấu hình <xliff:g id="DEVICE_NAME">%s</xliff:g>"</string>
@@ -2437,22 +2438,13 @@
     <string name="bg_user_sound_notification_button_switch_user" msgid="3091969648572788946">"Chuyển đổi người dùng"</string>
     <string name="bg_user_sound_notification_button_mute" msgid="4942158515665615243">"Tắt tiếng"</string>
     <string name="bg_user_sound_notification_message" msgid="8613881975316976673">"Nhấn để tắt tiếng"</string>
-    <!-- no translation found for keyboard_shortcut_group_applications_browser (6535007304687100909) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_contacts (2750702518068326356) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_email (4229037666415353683) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_sms (3523799286376321137) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_music (2051507523525651067) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calendar (3571770335653387606) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calculator (6753209559716091507) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_maps (7950000659522589471) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications (3010389163951364798) -->
-    <skip />
+    <string name="keyboard_shortcut_group_applications_browser" msgid="6535007304687100909">"Trình duyệt"</string>
+    <string name="keyboard_shortcut_group_applications_contacts" msgid="2750702518068326356">"Danh bạ"</string>
+    <string name="keyboard_shortcut_group_applications_email" msgid="4229037666415353683">"Email"</string>
+    <string name="keyboard_shortcut_group_applications_sms" msgid="3523799286376321137">"SMS"</string>
+    <string name="keyboard_shortcut_group_applications_music" msgid="2051507523525651067">"Âm nhạc"</string>
+    <string name="keyboard_shortcut_group_applications_calendar" msgid="3571770335653387606">"Lịch"</string>
+    <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Máy tính"</string>
+    <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Bản đồ"</string>
+    <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Ứng dụng"</string>
 </resources>
diff --git a/core/res/res/values-watch/themes_device_defaults.xml b/core/res/res/values-watch/themes_device_defaults.xml
index 85d34e2..4d2085bb 100644
--- a/core/res/res/values-watch/themes_device_defaults.xml
+++ b/core/res/res/values-watch/themes_device_defaults.xml
@@ -548,4 +548,7 @@
         <item name="primaryContentAlpha">@dimen/primary_content_alpha_device_default</item>
         <item name="secondaryContentAlpha">@dimen/secondary_content_alpha_device_default</item>
     </style>
+
+    <!--  Device default theme for the Input Method Switcher dialog. Override to make it dark.  -->
+    <style name="Theme.DeviceDefault.InputMethodSwitcherDialog" parent="Theme.DeviceDefault.Dialog.Alert"/>
 </resources>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 2d1873d..1cc0bfe 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -1428,6 +1428,7 @@
     <string name="share_remote_bugreport_action" msgid="7630880678785123682">"分享"</string>
     <string name="decline_remote_bugreport_action" msgid="4040894777519784346">"拒绝"</string>
     <string name="select_input_method" msgid="3971267998568587025">"选择输入法"</string>
+    <string name="input_method_language_settings" msgid="8069089418056819437">"语言设置"</string>
     <string name="show_ime" msgid="6406112007347443383">"连接到实体键盘时,在屏幕上显示一个虚拟键盘"</string>
     <string name="hardware" msgid="3611039921284836033">"使用屏幕键盘"</string>
     <string name="select_keyboard_layout_notification_title" msgid="5823199895322205589">"配置“<xliff:g id="DEVICE_NAME">%s</xliff:g>”"</string>
@@ -2437,22 +2438,13 @@
     <string name="bg_user_sound_notification_button_switch_user" msgid="3091969648572788946">"切换用户"</string>
     <string name="bg_user_sound_notification_button_mute" msgid="4942158515665615243">"静音"</string>
     <string name="bg_user_sound_notification_message" msgid="8613881975316976673">"点按即可设为静音"</string>
-    <!-- no translation found for keyboard_shortcut_group_applications_browser (6535007304687100909) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_contacts (2750702518068326356) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_email (4229037666415353683) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_sms (3523799286376321137) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_music (2051507523525651067) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calendar (3571770335653387606) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calculator (6753209559716091507) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_maps (7950000659522589471) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications (3010389163951364798) -->
-    <skip />
+    <string name="keyboard_shortcut_group_applications_browser" msgid="6535007304687100909">"浏览器"</string>
+    <string name="keyboard_shortcut_group_applications_contacts" msgid="2750702518068326356">"通讯录"</string>
+    <string name="keyboard_shortcut_group_applications_email" msgid="4229037666415353683">"电子邮件"</string>
+    <string name="keyboard_shortcut_group_applications_sms" msgid="3523799286376321137">"短信"</string>
+    <string name="keyboard_shortcut_group_applications_music" msgid="2051507523525651067">"音乐"</string>
+    <string name="keyboard_shortcut_group_applications_calendar" msgid="3571770335653387606">"日历"</string>
+    <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"计算器"</string>
+    <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"地图"</string>
+    <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"应用"</string>
 </resources>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index ca88600..b0c4cfb 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -1428,6 +1428,7 @@
     <string name="share_remote_bugreport_action" msgid="7630880678785123682">"分享"</string>
     <string name="decline_remote_bugreport_action" msgid="4040894777519784346">"拒絕"</string>
     <string name="select_input_method" msgid="3971267998568587025">"選擇輸入法"</string>
+    <string name="input_method_language_settings" msgid="8069089418056819437">"語言設定"</string>
     <string name="show_ime" msgid="6406112007347443383">"在實體鍵盤處於連接狀態時保持顯示"</string>
     <string name="hardware" msgid="3611039921284836033">"使用屏幕鍵盤"</string>
     <string name="select_keyboard_layout_notification_title" msgid="5823199895322205589">"設定「<xliff:g id="DEVICE_NAME">%s</xliff:g>」"</string>
@@ -2437,22 +2438,13 @@
     <string name="bg_user_sound_notification_button_switch_user" msgid="3091969648572788946">"切換使用者"</string>
     <string name="bg_user_sound_notification_button_mute" msgid="4942158515665615243">"靜音"</string>
     <string name="bg_user_sound_notification_message" msgid="8613881975316976673">"輕按即可靜音"</string>
-    <!-- no translation found for keyboard_shortcut_group_applications_browser (6535007304687100909) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_contacts (2750702518068326356) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_email (4229037666415353683) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_sms (3523799286376321137) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_music (2051507523525651067) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calendar (3571770335653387606) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calculator (6753209559716091507) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_maps (7950000659522589471) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications (3010389163951364798) -->
-    <skip />
+    <string name="keyboard_shortcut_group_applications_browser" msgid="6535007304687100909">"瀏覽器"</string>
+    <string name="keyboard_shortcut_group_applications_contacts" msgid="2750702518068326356">"通訊錄"</string>
+    <string name="keyboard_shortcut_group_applications_email" msgid="4229037666415353683">"電郵"</string>
+    <string name="keyboard_shortcut_group_applications_sms" msgid="3523799286376321137">"短訊"</string>
+    <string name="keyboard_shortcut_group_applications_music" msgid="2051507523525651067">"音樂"</string>
+    <string name="keyboard_shortcut_group_applications_calendar" msgid="3571770335653387606">"日曆"</string>
+    <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"計算機"</string>
+    <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"地圖"</string>
+    <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"應用程式"</string>
 </resources>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index b22ad0a..731905f 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -1428,6 +1428,7 @@
     <string name="share_remote_bugreport_action" msgid="7630880678785123682">"分享"</string>
     <string name="decline_remote_bugreport_action" msgid="4040894777519784346">"拒絕"</string>
     <string name="select_input_method" msgid="3971267998568587025">"選擇輸入法"</string>
+    <string name="input_method_language_settings" msgid="8069089418056819437">"語言設定"</string>
     <string name="show_ime" msgid="6406112007347443383">"使用實體鍵盤時仍繼續顯示螢幕小鍵盤"</string>
     <string name="hardware" msgid="3611039921284836033">"使用螢幕小鍵盤"</string>
     <string name="select_keyboard_layout_notification_title" msgid="5823199895322205589">"設定「<xliff:g id="DEVICE_NAME">%s</xliff:g>」"</string>
@@ -2437,22 +2438,13 @@
     <string name="bg_user_sound_notification_button_switch_user" msgid="3091969648572788946">"切換使用者"</string>
     <string name="bg_user_sound_notification_button_mute" msgid="4942158515665615243">"靜音"</string>
     <string name="bg_user_sound_notification_message" msgid="8613881975316976673">"輕觸即可設為靜音"</string>
-    <!-- no translation found for keyboard_shortcut_group_applications_browser (6535007304687100909) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_contacts (2750702518068326356) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_email (4229037666415353683) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_sms (3523799286376321137) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_music (2051507523525651067) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calendar (3571770335653387606) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calculator (6753209559716091507) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_maps (7950000659522589471) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications (3010389163951364798) -->
-    <skip />
+    <string name="keyboard_shortcut_group_applications_browser" msgid="6535007304687100909">"瀏覽器"</string>
+    <string name="keyboard_shortcut_group_applications_contacts" msgid="2750702518068326356">"聯絡人"</string>
+    <string name="keyboard_shortcut_group_applications_email" msgid="4229037666415353683">"電子郵件"</string>
+    <string name="keyboard_shortcut_group_applications_sms" msgid="3523799286376321137">"簡訊"</string>
+    <string name="keyboard_shortcut_group_applications_music" msgid="2051507523525651067">"音樂"</string>
+    <string name="keyboard_shortcut_group_applications_calendar" msgid="3571770335653387606">"日曆"</string>
+    <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"計算機"</string>
+    <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"地圖"</string>
+    <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"應用程式"</string>
 </resources>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index f9aa925..2ce2947 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -1428,6 +1428,7 @@
     <string name="share_remote_bugreport_action" msgid="7630880678785123682">"YABELANA"</string>
     <string name="decline_remote_bugreport_action" msgid="4040894777519784346">"YENQABA"</string>
     <string name="select_input_method" msgid="3971267998568587025">"Khetha indlela yokufaka"</string>
+    <string name="input_method_language_settings" msgid="8069089418056819437">"Amasethingi Olimi"</string>
     <string name="show_ime" msgid="6406112007347443383">"Yigcine kusikrini ngenkathi kusebenza ikhibhodi ephathekayo"</string>
     <string name="hardware" msgid="3611039921284836033">"Sebenzisa ikhibhodi ekuskrini"</string>
     <string name="select_keyboard_layout_notification_title" msgid="5823199895322205589">"Lungisa i-<xliff:g id="DEVICE_NAME">%s</xliff:g>"</string>
@@ -2437,22 +2438,13 @@
     <string name="bg_user_sound_notification_button_switch_user" msgid="3091969648572788946">"Shintsha umsebenzisi"</string>
     <string name="bg_user_sound_notification_button_mute" msgid="4942158515665615243">"Thulisa"</string>
     <string name="bg_user_sound_notification_message" msgid="8613881975316976673">"Thepha ukuze uthulise umsindo"</string>
-    <!-- no translation found for keyboard_shortcut_group_applications_browser (6535007304687100909) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_contacts (2750702518068326356) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_email (4229037666415353683) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_sms (3523799286376321137) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_music (2051507523525651067) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calendar (3571770335653387606) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calculator (6753209559716091507) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_maps (7950000659522589471) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications (3010389163951364798) -->
-    <skip />
+    <string name="keyboard_shortcut_group_applications_browser" msgid="6535007304687100909">"Ibhrawuza"</string>
+    <string name="keyboard_shortcut_group_applications_contacts" msgid="2750702518068326356">"Oxhumana nabo"</string>
+    <string name="keyboard_shortcut_group_applications_email" msgid="4229037666415353683">"I-imeyili"</string>
+    <string name="keyboard_shortcut_group_applications_sms" msgid="3523799286376321137">"I-SMS"</string>
+    <string name="keyboard_shortcut_group_applications_music" msgid="2051507523525651067">"Umculo"</string>
+    <string name="keyboard_shortcut_group_applications_calendar" msgid="3571770335653387606">"Ikhalenda"</string>
+    <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Isibali"</string>
+    <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Amamephu"</string>
+    <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Ama-application"</string>
 </resources>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 7cc9e13..440219d 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -1078,6 +1078,11 @@
         <!-- Background color to use for tooltip popups. -->
         <attr name="tooltipBackgroundColor" format="reference|color" />
 
+        <attr name="tooltipCornerRadius" format="dimension" />
+        <attr name="tooltipHorizontalPadding" format="dimension" />
+        <attr name="tooltipVerticalPadding" format="dimension" />
+        <attr name="tooltipFontSize" format="dimension" />
+
         <!-- Theme to use for Search Dialogs. -->
         <attr name="searchDialogTheme" format="reference" />
 
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 2e3dbda..0be33c2 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -3296,8 +3296,8 @@
              usually TVs.
              <p>Requires permission {@code android.permission.DISABLE_SYSTEM_SOUND_EFFECTS}. -->
         <attr name="playHomeTransitionSound" format="boolean"/>
-        <!-- Indicates whether the activity can be displayed on a remote device which may or
-             may not be running Android. -->
+        <!-- Indicates whether the activity can be displayed on a display that may belong to a
+             remote device which may or may not be running Android. -->
         <attr name="canDisplayOnRemoteDevices" format="boolean"/>
         <attr name="allowUntrustedActivityEmbedding" />
         <attr name="knownActivityEmbeddingCerts" />
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 236e7c5..495af5b 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1399,6 +1399,10 @@
          Settings.System.RING_VIBRATION_INTENSITY more details on the constant values and
          meanings. -->
     <integer name="config_defaultRingVibrationIntensity">2</integer>
+    <!-- The default intensity level for keyboard vibrations. Note that this will only be applied
+         on devices where config_keyboardVibrationSettingsSupported is true, otherwise the
+         keyboard vibration will follow config_defaultHapticFeedbackIntensity -->
+    <integer name="config_defaultKeyboardVibrationIntensity">2</integer>
 
     <!-- Whether to use the strict phone number matcher by default. -->
     <bool name="config_use_strict_phone_number_comparation">false</bool>
@@ -6154,10 +6158,6 @@
         is enabled and activity is connected to the camera in fullscreen. -->
     <bool name="config_isWindowManagerCameraCompatSplitScreenAspectRatioEnabled">false</bool>
 
-    <!-- Whether a camera compat controller is enabled to allow the user to apply or revert
-         treatment for stretched issues in camera viewfinder. -->
-    <bool name="config_isCameraCompatControlForStretchedIssuesEnabled">false</bool>
-
     <!-- Docking is a uiMode configuration change and will cause activities to relaunch if it's not
          handled. If true, the configuration change will be sent but activities will not be
          relaunched upon docking. Apps with desk resources will behave like normal, since they may
@@ -7069,6 +7069,9 @@
     <!-- Whether desktop mode is supported on the current device  -->
     <bool name="config_isDesktopModeSupported">false</bool>
 
+    <!-- Maximum number of active tasks on a given Desktop Windowing session. Set to 0 for unlimited. -->
+    <integer name="config_maxDesktopWindowingActiveTasks">0</integer>
+
     <!-- Frame rate compatibility value for Wallpaper
          FRAME_RATE_COMPATIBILITY_MIN (102) is used by default for lower power consumption -->
     <integer name="config_wallpaperFrameRateCompatibility">102</integer>
@@ -7093,4 +7096,12 @@
 
     <!-- Whether to enable usb state update via udc sysfs. -->
     <bool name="config_enableUdcSysfsUsbStateUpdate">false</bool>
+
+    <!-- Whether to enable the private space search illustration and search tile content in "Hide Private Space" settings page.
+         OEM/Partner can explicitly opt to hide the illustration and search tile content. -->
+    <bool name="config_enableSearchTileHideIllustrationInPrivateSpace">true</bool>
+
+    <!-- The maximum number of call log entries for each sim card that can be stored in the call log
+         provider on the device. -->
+    <integer name="config_maximumCallLogEntriesPerSim">500</integer>
 </resources>
diff --git a/core/res/res/values/config_device_idle.xml b/core/res/res/values/config_device_idle.xml
index 7a707c0..cc7b891 100644
--- a/core/res/res/values/config_device_idle.xml
+++ b/core/res/res/values/config_device_idle.xml
@@ -28,7 +28,7 @@
     <integer name="device_idle_flex_time_short_ms">60000</integer>
 
     <!-- Default for DeviceIdleController.Constants.LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT -->
-    <integer name="device_idle_light_after_inactive_to_ms">60000</integer>
+    <integer name="device_idle_light_after_inactive_to_ms">240000</integer>
 
     <!-- Default for DeviceIdleController.Constants.LIGHT_IDLE_TIMEOUT -->
     <integer name="device_idle_light_idle_to_ms">300000</integer>
@@ -67,10 +67,10 @@
     <integer name="device_idle_min_deep_maintenance_time_ms">30000</integer>
 
     <!-- Default for DeviceIdleController.Constants.INACTIVE_TIMEOUT -->
-    <integer name="device_idle_inactive_to_ms">60000</integer>
+    <integer name="device_idle_inactive_to_ms">15000</integer>
 
     <!-- Default for DeviceIdleController.Constants.SENSING_TIMEOUT -->
-    <integer name="device_idle_sensing_to_ms">30000</integer>
+    <integer name="device_idle_sensing_to_ms">15000</integer>
 
     <!-- Default for DeviceIdleController.Constants.LOCATING_TIMEOUT -->
     <integer name="device_idle_locating_to_ms">15000</integer>
@@ -79,13 +79,13 @@
     <item name="device_idle_location_accuracy" format="float" type="integer">20.0</item>
 
     <!-- Default for DeviceIdleController.Constants.MOTION_INACTIVE_TIMEOUT -->
-    <integer name="device_idle_motion_inactive_to_ms">600000</integer>
+    <integer name="device_idle_motion_inactive_to_ms">30000</integer>
 
     <!-- Default for DeviceIdleController.Constants.MOTION_INACTIVE_TIMEOUT_FLEX -->
     <integer name="device_idle_motion_inactive_to_flex_ms">60000</integer>
 
     <!-- Default for DeviceIdleController.Constants.IDLE_AFTER_INACTIVE_TIMEOUT -->
-    <integer name="device_idle_idle_after_inactive_to_ms">60000</integer>
+    <integer name="device_idle_idle_after_inactive_to_ms">15000</integer>
 
     <!-- Default for DeviceIdleController.Constants.IDLE_PENDING_TIMEOUT -->
     <integer name="device_idle_idle_pending_to_ms">300000</integer>
@@ -100,7 +100,7 @@
     <integer name="device_idle_quick_doze_delay_to_ms">60000</integer>
 
     <!-- Default for DeviceIdleController.Constants.IDLE_TIMEOUT -->
-    <integer name="device_idle_idle_to_ms">900000</integer>
+    <integer name="device_idle_idle_to_ms">3600000</integer>
 
     <!-- Default for DeviceIdleController.Constants.MAX_IDLE_TIMEOUT -->
     <integer name="device_idle_max_idle_to_ms">21600000</integer>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 6cba84b..77b5587 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -771,6 +771,7 @@
     <dimen name="tooltip_precise_anchor_threshold">96dp</dimen>
     <!-- Extra tooltip offset used when anchoring to the mouse/touch position -->
     <dimen name="tooltip_precise_anchor_extra_offset">8dp</dimen>
+    <dimen name="tooltip_font_size">14sp</dimen>
 
     <!-- The max amount of scroll ItemTouchHelper will trigger if dragged view is out of
          RecyclerView's bounds.-->
diff --git a/core/res/res/values/dimens_material.xml b/core/res/res/values/dimens_material.xml
index 972fe7e..35f35fb 100644
--- a/core/res/res/values/dimens_material.xml
+++ b/core/res/res/values/dimens_material.xml
@@ -204,4 +204,9 @@
     <dimen name="progress_bar_size_small">16dip</dimen>
     <dimen name="progress_bar_size_medium">48dp</dimen>
     <dimen name="progress_bar_size_large">76dp</dimen>
+
+    <dimen name="tooltip_corner_radius_material">4dp</dimen>
+    <dimen name="tooltip_horizontal_padding_material">8dp</dimen>
+    <dimen name="tooltip_vertical_padding_material">4dp</dimen>
+    <dimen name="tooltip_font_size_material">12sp</dimen>
 </resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index ec865f6..9104379 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -6496,10 +6496,14 @@
     <string name="screen_not_shared_sensitive_content">App content hidden from screen share for security</string>
 
     <!-- Satellite related messages -->
-    <!-- Notification title when satellite service is connected. -->
+    <!-- Notification title when satellite service is auto connected. -->
     <string name="satellite_notification_title">Auto connected to satellite</string>
-    <!-- Notification summary when satellite service is connected. [CHAR LIMIT=NONE] -->
+    <!-- Notification summary when satellite service is auto connected. [CHAR LIMIT=NONE] -->
     <string name="satellite_notification_summary">You can send and receive messages without a mobile or Wi-Fi network</string>
+    <!-- Notification title when satellite service can be manually enabled. -->
+    <string name="satellite_notification_manual_title">Use satellite messaging?</string>
+    <!-- Notification summary when satellite service can be manually enabled. [CHAR LIMIT=NONE] -->
+    <string name="satellite_notification_manual_summary">Send and receive messages without a mobile or Wi-Fi network</string>
     <!-- Invoke "What to expect" dialog of messaging application -->
     <string name="satellite_notification_open_message">Open Messages</string>
     <!-- Invoke Satellite setting activity of Settings -->
@@ -6555,4 +6559,7 @@
     <string name="keyboard_shortcut_group_applications_maps">Maps</string>
     <!-- User visible title for the keyboard shortcut group containing system-wide application launch shortcuts. [CHAR-LIMIT=70] -->
     <string name="keyboard_shortcut_group_applications">Applications</string>
+
+    <!-- Fingerprint loe notification string -->
+    <string name="fingerprint_loe_notification_msg">Your fingerprints can no longer be recognized. Set up Fingerprint Unlock again.</string>
 </resources>
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index aabc8ca..c084b4c 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -998,7 +998,7 @@
 
     <style name="TextAppearance.Tooltip">
         <item name="fontFamily">sans-serif</item>
-        <item name="textSize">14sp</item>
+        <item name="textSize">?android:attr/tooltipFontSize</item>
     </style>
 
     <style name="Widget.ActivityChooserView">
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 09688f2..b158e0f 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -524,6 +524,7 @@
   <java-symbol type="bool" name="config_notificationCloseButtonSupported"/>
   <java-symbol type="bool" name="config_enableGaiaEducationInPrivateSpace"/>
   <java-symbol type="bool" name="config_enableUdcSysfsUsbStateUpdate"/>
+  <java-symbol type="bool" name="config_enableSearchTileHideIllustrationInPrivateSpace"/>
 
   <java-symbol type="color" name="tab_indicator_text_v4" />
 
@@ -4222,6 +4223,7 @@
   <java-symbol type="integer" name="config_defaultMediaVibrationIntensity" />
   <java-symbol type="integer" name="config_defaultNotificationVibrationIntensity" />
   <java-symbol type="integer" name="config_defaultRingVibrationIntensity" />
+  <java-symbol type="integer" name="config_defaultKeyboardVibrationIntensity" />
 
   <java-symbol type="bool" name="config_maskMainBuiltInDisplayCutout" />
 
@@ -4765,7 +4767,6 @@
   <java-symbol type="bool" name="config_isCompatFakeFocusEnabled" />
   <java-symbol type="bool" name="config_isWindowManagerCameraCompatTreatmentEnabled" />
   <java-symbol type="bool" name="config_isWindowManagerCameraCompatSplitScreenAspectRatioEnabled" />
-  <java-symbol type="bool" name="config_isCameraCompatControlForStretchedIssuesEnabled" />
   <java-symbol type="bool" name="config_skipActivityRelaunchWhenDocking" />
 
   <java-symbol type="bool" name="config_hideDisplayCutoutWithDisplayArea" />
@@ -5498,6 +5499,8 @@
   <!-- System notification for satellite service -->
   <java-symbol type="string" name="satellite_notification_title" />
   <java-symbol type="string" name="satellite_notification_summary" />
+  <java-symbol type="string" name="satellite_notification_manual_title" />
+  <java-symbol type="string" name="satellite_notification_manual_summary" />
   <java-symbol type="string" name="satellite_notification_open_message" />
   <java-symbol type="string" name="satellite_notification_how_it_works" />
   <java-symbol type="drawable" name="ic_satellite_alt_24px" />
@@ -5522,10 +5525,15 @@
   <!-- Whether desktop mode is supported on the current device  -->
   <java-symbol type="bool" name="config_isDesktopModeSupported" />
 
+  <!-- Maximum number of active tasks on a given Desktop Windowing session. Set to 0 for unlimited. -->
+  <java-symbol type="integer" name="config_maxDesktopWindowingActiveTasks"/>
+
   <!-- Frame rate compatibility value for Wallpaper -->
   <java-symbol type="integer" name="config_wallpaperFrameRateCompatibility" />
 
   <java-symbol type="integer" name="config_defaultMinEmergencyGestureTapDurationMillis" />
+  <java-symbol type="integer" name="config_maximumCallLogEntriesPerSim" />
+
   <!-- Back swipe thresholds -->
   <java-symbol type="dimen" name="navigation_edge_action_progress_threshold" />
   <java-symbol type="dimen" name="back_progress_non_linear_factor" />
@@ -5577,4 +5585,7 @@
   <java-symbol type="string" name="keyboard_shortcut_group_applications_music" />
   <java-symbol type="string" name="keyboard_shortcut_group_applications_sms" />
   <java-symbol type="string" name="keyboard_shortcut_group_applications" />
+
+  <!-- Fingerprint loe notification string -->
+  <java-symbol type="string" name="fingerprint_loe_notification_msg" />
 </resources>
diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml
index c3d304d..3b3bb8d 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -461,6 +461,10 @@
         <item name="tooltipFrameBackground">@drawable/tooltip_frame</item>
         <item name="tooltipForegroundColor">@color/bright_foreground_light</item>
         <item name="tooltipBackgroundColor">@color/tooltip_background_light</item>
+        <item name="tooltipCornerRadius">@dimen/tooltip_corner_radius</item>
+        <item name="tooltipHorizontalPadding">@dimen/tooltip_horizontal_padding</item>
+        <item name="tooltipVerticalPadding">@dimen/tooltip_vertical_padding</item>
+        <item name="tooltipFontSize">@dimen/tooltip_font_size</item>
 
         <!-- Autofill: max width/height of the dataset picker as a fraction of screen size -->
         <item name="autofillDatasetPickerMaxWidth">@dimen/autofill_dataset_picker_max_width</item>
@@ -582,9 +586,10 @@
         <item name="floatingToolbarOpenDrawable">@drawable/ic_menu_moreoverflow_material_light</item>
         <item name="floatingToolbarDividerColor">@color/floating_popup_divider_light</item>
 
-        <!-- Tooltip popup colors -->
+        <!-- Tooltip popup styles -->
         <item name="tooltipForegroundColor">@color/bright_foreground_dark</item>
         <item name="tooltipBackgroundColor">@color/tooltip_background_dark</item>
+
     </style>
 
     <!-- Variant of {@link #Theme_Light} with no title bar -->
diff --git a/core/res/res/values/themes_material.xml b/core/res/res/values/themes_material.xml
index 8e2fb34..9f11208 100644
--- a/core/res/res/values/themes_material.xml
+++ b/core/res/res/values/themes_material.xml
@@ -408,8 +408,12 @@
         <item name="colorProgressBackgroundNormal">?attr/colorControlNormal</item>
 
         <!-- Tooltip popup properties -->
-        <item name="tooltipForegroundColor">@color/foreground_material_light</item>
-        <item name="tooltipBackgroundColor">@color/tooltip_background_light</item>
+        <item name="tooltipForegroundColor">@color/system_on_surface_light</item>
+        <item name="tooltipBackgroundColor">@color/system_surface_light</item>
+        <item name="tooltipCornerRadius">@dimen/tooltip_corner_radius_material</item>
+        <item name="tooltipHorizontalPadding">@dimen/tooltip_horizontal_padding_material</item>
+        <item name="tooltipVerticalPadding">@dimen/tooltip_vertical_padding_material</item>
+        <item name="tooltipFontSize">@dimen/tooltip_font_size_material</item>
     </style>
 
     <!-- Material theme (light version). -->
@@ -785,8 +789,13 @@
         <item name="colorProgressBackgroundNormal">?attr/colorControlNormal</item>
 
         <!-- Tooltip popup properties -->
-        <item name="tooltipForegroundColor">@color/foreground_material_dark</item>
-        <item name="tooltipBackgroundColor">@color/tooltip_background_dark</item>
+        <item name="tooltipForegroundColor">@color/system_on_surface_dark</item>
+        <item name="tooltipBackgroundColor">@color/system_surface_dark</item>
+        <item name="tooltipCornerRadius">@dimen/tooltip_corner_radius_material</item>
+        <item name="tooltipHorizontalPadding">@dimen/tooltip_horizontal_padding_material</item>
+        <item name="tooltipVerticalPadding">@dimen/tooltip_vertical_padding_material</item>
+        <item name="tooltipFontSize">@dimen/tooltip_font_size_material</item>
+
     </style>
 
     <!-- Variant of the material (light) theme that has a solid (opaque) action bar
diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp
index 41696df..5793bbe 100644
--- a/core/tests/coretests/Android.bp
+++ b/core/tests/coretests/Android.bp
@@ -284,3 +284,510 @@
     ],
     auto_gen_config: true,
 }
+
+FLAKY_OR_IGNORED = [
+    "androidx.test.filters.FlakyTest",
+    "org.junit.Ignore",
+]
+
+test_module_config {
+    name: "FrameworksCoreTests_Presubmit",
+    base: "FrameworksCoreTests",
+    test_suites: [
+        "device-tests",
+        "device-platinum-tests",
+    ],
+    include_annotations: ["android.platform.test.annotations.Presubmit"],
+    exclude_annotations: FLAKY_OR_IGNORED,
+}
+
+test_module_config {
+    name: "FrameworksCoreTests_inputmethod",
+    base: "FrameworksCoreTests",
+    test_suites: [
+        "device-tests",
+        "device-platinum-tests",
+    ],
+    include_filters: [
+        "com.android.internal.inputmethod",
+        "android.view.inputmethod",
+    ],
+    exclude_annotations: ["androidx.test.filters.FlakyTest"],
+}
+
+test_module_config {
+    name: "FrameworksCoreTests_context",
+    base: "FrameworksCoreTests",
+    test_suites: [
+        "device-tests",
+        "device-platinum-tests",
+    ],
+    include_filters: ["android.content.ContextTest"],
+    exclude_annotations: FLAKY_OR_IGNORED,
+}
+
+test_module_config {
+    name: "FrameworksCoreTests_keyguard_manager",
+    base: "FrameworksCoreTests",
+    test_suites: [
+        "device-tests",
+        "device-platinum-tests",
+    ],
+    include_filters: ["android.app.KeyguardManagerTest"],
+    exclude_annotations: FLAKY_OR_IGNORED,
+}
+
+test_module_config {
+    name: "FrameworksCoreTests_property_invalidated_cache",
+    base: "FrameworksCoreTests",
+    test_suites: [
+        "device-tests",
+        "device-platinum-tests",
+    ],
+    include_filters: ["android.app.PropertyInvalidatedCacheTests"],
+    exclude_annotations: FLAKY_OR_IGNORED,
+}
+
+test_module_config {
+    name: "FrameworksCoreTests_android_content",
+    base: "FrameworksCoreTests",
+    test_suites: [
+        "device-tests",
+        "device-platinum-tests",
+    ],
+    include_filters: [
+        "android.content.ContextTest",
+        "android.content.ComponentCallbacksControllerTest",
+        "android.content.ContextWrapperTest",
+    ],
+    exclude_annotations: FLAKY_OR_IGNORED,
+}
+
+test_module_config {
+    name: "FrameworksCoreTests_sqlite",
+    base: "FrameworksCoreTests",
+    test_suites: [
+        "device-tests",
+        "device-platinum-tests",
+    ],
+    include_filters: ["android.database.sqlite.SQLiteRawStatementTest"],
+    exclude_annotations: FLAKY_OR_IGNORED,
+}
+
+test_module_config {
+    name: "FrameworksCoreTests_android_net",
+    base: "FrameworksCoreTests",
+    test_suites: [
+        "device-tests",
+        "device-platinum-tests",
+    ],
+    include_filters: ["android.net"],
+    include_annotations: ["android.platform.test.annotations.Presubmit"],
+    exclude_annotations: FLAKY_OR_IGNORED,
+}
+
+test_module_config {
+    name: "FrameworksCoreTests_battery_stats",
+    base: "FrameworksCoreTests",
+    test_suites: [
+        "device-tests",
+        "device-platinum-tests",
+    ],
+    include_filters: ["com.android.internal.os.BatteryStatsTests"],
+    exclude_annotations: ["com.android.internal.os.SkipPresubmit"],
+}
+
+test_module_config {
+    name: "FrameworksCoreTests_environment",
+    base: "FrameworksCoreTests",
+    test_suites: [
+        "device-tests",
+        "device-platinum-tests",
+    ],
+    include_filters: ["android.os.EnvironmentTest"],
+}
+
+test_module_config {
+    name: "FrameworksCoreTests_util_data_charset",
+    base: "FrameworksCoreTests",
+    test_suites: [
+        "device-tests",
+        "device-platinum-tests",
+    ],
+    include_filters: [
+        "com.android.internal.util.FastDataTest",
+        "android.util.CharsetUtilsTest",
+    ],
+}
+
+test_module_config {
+    name: "FrameworksCoreTests_xml",
+    base: "FrameworksCoreTests",
+    test_suites: [
+        "device-tests",
+        "device-platinum-tests",
+    ],
+    include_filters: [
+        "android.util.XmlTest",
+        "android.util.BinaryXmlTest",
+    ],
+}
+
+test_module_config {
+    name: "FrameworksCoreTests_util_apk",
+    base: "FrameworksCoreTests",
+    test_suites: [
+        "device-tests",
+        "device-platinum-tests",
+    ],
+    include_filters: ["android.util.apk.SourceStampVerifierTest"],
+}
+
+test_module_config {
+    name: "FrameworksCoreTests_textclassifier",
+    base: "FrameworksCoreTests",
+    test_suites: [
+        "device-tests",
+        "device-platinum-tests",
+    ],
+    include_filters: ["android.view.textclassifier"],
+    exclude_annotations: ["androidx.test.filters.FlakyTest"],
+}
+
+test_module_config {
+    name: "FrameworksCoreTests_internal_app",
+    base: "FrameworksCoreTests",
+    test_suites: [
+        "device-tests",
+        "device-platinum-tests",
+    ],
+    include_filters: ["com.android.internal.app."],
+    exclude_filters: [
+        "com.android.internal.app.WindowDecorActionBarTest",
+        "com.android.internal.app.IntentForwarderActivityTest",
+    ],
+}
+
+test_module_config {
+    name: "FrameworksCoreTests_internal_content",
+    base: "FrameworksCoreTests",
+    test_suites: [
+        "device-tests",
+        "device-platinum-tests",
+    ],
+    include_filters: ["com.android.internal.content."],
+}
+
+test_module_config {
+    name: "FrameworksCoreTests_internal_infra",
+    base: "FrameworksCoreTests",
+    test_suites: [
+        "device-tests",
+        "device-platinum-tests",
+    ],
+    include_filters: ["com.android.internal.infra."],
+}
+
+test_module_config {
+    name: "FrameworksCoreTests_internal_jank",
+    base: "FrameworksCoreTests",
+    test_suites: [
+        "device-tests",
+        "device-platinum-tests",
+    ],
+    include_filters: ["com.android.internal.jank"],
+    exclude_annotations: FLAKY_OR_IGNORED,
+}
+
+test_module_config {
+    name: "FrameworksCoreTests_internal_os_binder",
+    base: "FrameworksCoreTests",
+    test_suites: [
+        "device-tests",
+        "device-platinum-tests",
+    ],
+    include_filters: ["com.android.internal.os.BinderDeathDispatcherTest"],
+    exclude_annotations: ["com.android.internal.os.SkipPresubmit"],
+}
+
+test_module_config {
+    name: "FrameworksCoreTests_internal_os_kernel",
+    base: "FrameworksCoreTests",
+    test_suites: [
+        "device-tests",
+        "device-platinum-tests",
+    ],
+    include_filters: [
+        "com.android.internal.os.KernelCpuUidClusterTimeReaderTest",
+        "com.android.internal.os.KernelCpuUidBpfMapReaderTest",
+        "com.android.internal.os.KernelCpuUidActiveTimeReaderTest",
+        "com.android.internal.os.KernelCpuUidFreqTimeReaderTest",
+        "com.android.internal.os.KernelSingleUidTimeReaderTest",
+    ],
+}
+
+test_module_config {
+    name: "FrameworksCoreTests_server_power",
+    base: "FrameworksCoreTests",
+    test_suites: [
+        "device-tests",
+        "device-platinum-tests",
+    ],
+    include_filters: ["com.android.server.power.stats.BstatsCpuTimesValidationTest"],
+}
+
+test_module_config {
+    name: "FrameworksCoreTests_internal_security",
+    base: "FrameworksCoreTests",
+    test_suites: [
+        "device-tests",
+        "device-platinum-tests",
+    ],
+    include_filters: ["com.android.internal.security."],
+    include_annotations: ["android.platform.test.annotations.Presubmit"],
+}
+
+test_module_config {
+    name: "FrameworksCoreTests_internal_util_latency_tracker",
+    base: "FrameworksCoreTests",
+    test_suites: [
+        "device-tests",
+        "device-platinum-tests",
+    ],
+    include_filters: ["com.android.internal.util.LatencyTrackerTest"],
+    exclude_annotations: FLAKY_OR_IGNORED,
+}
+
+test_module_config {
+    name: "FrameworksCoreTests_content_capture_options",
+    base: "FrameworksCoreTests",
+    test_suites: [
+        "device-tests",
+        "device-platinum-tests",
+    ],
+    include_filters: ["android.content.ContentCaptureOptionsTest"],
+    exclude_annotations: FLAKY_OR_IGNORED,
+}
+
+test_module_config {
+    name: "FrameworksCoreTests_android_content_integrity",
+    base: "FrameworksCoreTests",
+    test_suites: [
+        "device-tests",
+        "device-platinum-tests",
+    ],
+    include_filters: ["android.content.integrity."],
+}
+
+test_module_config {
+    name: "FrameworksCoreTests_android_content_pm_PreSubmit",
+    base: "FrameworksCoreTests",
+    test_suites: [
+        "device-tests",
+        "device-platinum-tests",
+    ],
+    include_filters: ["android.content.pm."],
+    include_annotations: ["android.platform.test.annotations.Presubmit"],
+    exclude_annotations: FLAKY_OR_IGNORED,
+}
+
+test_module_config {
+    name: "FrameworksCoreTests_android_content_pm_PostSubmit",
+    base: "FrameworksCoreTests",
+    test_suites: [
+        "device-tests",
+        "device-platinum-tests",
+    ],
+    include_filters: ["android.content.pm."],
+    include_annotations: ["android.platform.test.annotations.Postsubmit"],
+    exclude_annotations: FLAKY_OR_IGNORED,
+}
+
+test_module_config {
+    name: "FrameworksCoreTests_android_content_res",
+    base: "FrameworksCoreTests",
+    test_suites: [
+        "device-tests",
+        "device-platinum-tests",
+    ],
+    include_filters: ["android.content.res."],
+    include_annotations: ["android.platform.test.annotations.Presubmit"],
+    exclude_annotations: [
+        "androidx.test.filters.FlakyTest",
+        "android.platform.test.annotations.Postsubmit",
+        "org.junit.Ignore",
+    ],
+}
+
+test_module_config {
+    name: "FrameworksCoreTests_android_content_res_PostSubmit",
+    base: "FrameworksCoreTests",
+    test_suites: [
+        "device-tests",
+        "device-platinum-tests",
+    ],
+    include_filters: ["android.content.res."],
+    include_annotations: ["android.platform.test.annotations.Postsubmit"],
+    exclude_annotations: FLAKY_OR_IGNORED,
+}
+
+test_module_config {
+    name: "FrameworksCoreTests_android_service",
+    base: "FrameworksCoreTests",
+    test_suites: [
+        "device-tests",
+        "device-platinum-tests",
+    ],
+    include_filters: [
+        "android.service.euicc",
+        "android.service.notification",
+        "android.service.quicksettings",
+        "android.service.settings.suggestions",
+        "android.service.controls.templates",
+        "android.service.controls.actions",
+        "android.service.controls",
+    ],
+    exclude_annotations: ["org.junit.Ignore"],
+}
+
+test_module_config {
+    name: "FrameworksCoreTests_android_view_contentcapture",
+    base: "FrameworksCoreTests",
+    test_suites: [
+        "device-tests",
+        "device-platinum-tests",
+    ],
+    include_filters: ["android.view.contentcapture"],
+    exclude_annotations: FLAKY_OR_IGNORED,
+}
+
+test_module_config {
+    name: "FrameworksCoreTests_android_view_contentprotection",
+    base: "FrameworksCoreTests",
+    test_suites: [
+        "device-tests",
+        "device-platinum-tests",
+    ],
+    include_filters: ["android.view.contentprotection"],
+    exclude_annotations: FLAKY_OR_IGNORED,
+}
+
+test_module_config {
+    name: "FrameworksCoreTests_com_android_internal_content_Presubmit",
+    base: "FrameworksCoreTests",
+    test_suites: [
+        "device-tests",
+        "device-platinum-tests",
+    ],
+    include_filters: ["com.android.internal.content."],
+    include_annotations: ["android.platform.test.annotations.Presubmit"],
+    exclude_annotations: FLAKY_OR_IGNORED,
+}
+
+test_module_config {
+    name: "FrameworksCoreTests_drawable",
+    base: "FrameworksCoreTests",
+    test_suites: [
+        "device-tests",
+        "device-platinum-tests",
+    ],
+    include_filters: ["android.graphics.drawable.IconTest"],
+}
+
+test_module_config {
+    name: "FrameworksCoreTests_accessibility_NO_FLAKES",
+    base: "FrameworksCoreTests",
+    test_suites: [
+        "device-tests",
+        "device-platinum-tests",
+    ],
+    include_filters: [
+        "com.android.internal.accessibility",
+        "android.accessibilityservice",
+        "android.view.accessibility",
+    ],
+    exclude_annotations: ["androidx.test.filters.FlakyTest"],
+}
+
+test_module_config {
+    name: "FrameworksCoreTests_accessibility",
+    base: "FrameworksCoreTests",
+    test_suites: [
+        "device-tests",
+        "device-platinum-tests",
+    ],
+    include_filters: [
+        "com.android.internal.accessibility",
+        "android.accessibilityservice",
+        "android.view.accessibility",
+    ],
+}
+
+test_module_config {
+    name: "FrameworksCoreTests_usage",
+    base: "FrameworksCoreTests",
+    test_suites: [
+        "device-tests",
+        "device-platinum-tests",
+    ],
+    include_filters: ["android.app.usage"],
+}
+
+test_module_config {
+    name: "FrameworksCoreTests_fastdata",
+    base: "FrameworksCoreTests",
+    test_suites: [
+        "device-tests",
+        "device-platinum-tests",
+    ],
+    include_filters: ["com.android.internal.util.FastDataTest"],
+}
+
+test_module_config {
+    name: "FrameworksCoreTests_hardware_input",
+    base: "FrameworksCoreTests",
+    test_suites: [
+        "device-tests",
+        "device-platinum-tests",
+    ],
+    include_filters: ["android.hardware.input"],
+}
+
+test_module_config {
+    name: "FrameworksCoreTests_view_verified",
+    base: "FrameworksCoreTests",
+    test_suites: [
+        "device-tests",
+        "device-platinum-tests",
+    ],
+    include_filters: [
+        "android.view.VerifiedMotionEventTest",
+        "android.view.VerifiedKeyEventTest",
+    ],
+}
+
+test_module_config {
+    name: "FrameworksCoreTests_jank",
+    base: "FrameworksCoreTests",
+    test_suites: [
+        "device-tests",
+        "device-platinum-tests",
+    ],
+    include_filters: [
+        "com.android.internal.jank.FrameTrackerTest",
+        "com.android.internal.jank.InteractionJankMonitorTest",
+        "com.android.internal.util.LatencyTrackerTest",
+    ],
+    exclude_annotations: FLAKY_OR_IGNORED,
+}
+
+test_module_config {
+    name: "FrameworksCoreTests_Platinum",
+    base: "FrameworksCoreTests",
+    test_suites: [
+        "device-tests",
+        "device-platinum-tests",
+    ],
+    include_annotations: ["android.platform.test.annotations.PlatinumTest"],
+    exclude_annotations: FLAKY_OR_IGNORED,
+}
diff --git a/core/tests/coretests/src/android/accessibilityservice/AccessibilityShortcutInfoTest.java b/core/tests/coretests/src/android/accessibilityservice/AccessibilityShortcutInfoTest.java
index eebc578..ef7df59 100644
--- a/core/tests/coretests/src/android/accessibilityservice/AccessibilityShortcutInfoTest.java
+++ b/core/tests/coretests/src/android/accessibilityservice/AccessibilityShortcutInfoTest.java
@@ -29,9 +29,9 @@
 import android.view.accessibility.AccessibilityManager;
 import android.view.accessibility.AccessibilityTestActivity;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 import androidx.test.platform.app.InstrumentationRegistry;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.android.frameworks.coretests.R;
 
diff --git a/core/tests/coretests/src/android/animation/ValueAnimatorTests.java b/core/tests/coretests/src/android/animation/ValueAnimatorTests.java
index a102b3e..eb463fd 100644
--- a/core/tests/coretests/src/android/animation/ValueAnimatorTests.java
+++ b/core/tests/coretests/src/android/animation/ValueAnimatorTests.java
@@ -30,9 +30,9 @@
 import android.view.Choreographer;
 import android.view.animation.LinearInterpolator;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.MediumTest;
 import androidx.test.rule.ActivityTestRule;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.After;
 import org.junit.Before;
diff --git a/core/tests/coretests/src/android/app/ActivityManagerTest.java b/core/tests/coretests/src/android/app/ActivityManagerTest.java
index 3c042ba..d850f86 100644
--- a/core/tests/coretests/src/android/app/ActivityManagerTest.java
+++ b/core/tests/coretests/src/android/app/ActivityManagerTest.java
@@ -26,7 +26,7 @@
 import android.os.UserHandle;
 import android.platform.test.ravenwood.RavenwoodRule;
 
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import org.junit.Rule;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/app/ApplicationErrorReportTest.java b/core/tests/coretests/src/android/app/ApplicationErrorReportTest.java
index 8dc5ad6..c4240a2 100644
--- a/core/tests/coretests/src/android/app/ApplicationErrorReportTest.java
+++ b/core/tests/coretests/src/android/app/ApplicationErrorReportTest.java
@@ -21,8 +21,8 @@
 
 import android.app.ApplicationErrorReport.CrashInfo;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/app/ApplicationLoadersTest.java b/core/tests/coretests/src/android/app/ApplicationLoadersTest.java
index 565e21d..dfb8a23 100644
--- a/core/tests/coretests/src/android/app/ApplicationLoadersTest.java
+++ b/core/tests/coretests/src/android/app/ApplicationLoadersTest.java
@@ -22,8 +22,8 @@
 
 import android.content.pm.SharedLibraryInfo;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.google.android.collect.Lists;
 
diff --git a/core/tests/coretests/src/android/app/BackgroundStartPrivilegesTest.java b/core/tests/coretests/src/android/app/BackgroundStartPrivilegesTest.java
index 982ad63..cf6266c 100644
--- a/core/tests/coretests/src/android/app/BackgroundStartPrivilegesTest.java
+++ b/core/tests/coretests/src/android/app/BackgroundStartPrivilegesTest.java
@@ -26,8 +26,8 @@
 import android.os.Binder;
 import android.os.IBinder;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/app/LoaderLifecycleTest.java b/core/tests/coretests/src/android/app/LoaderLifecycleTest.java
index e343383..2eb0bbe 100644
--- a/core/tests/coretests/src/android/app/LoaderLifecycleTest.java
+++ b/core/tests/coretests/src/android/app/LoaderLifecycleTest.java
@@ -28,9 +28,9 @@
 import android.os.Parcelable;
 import android.util.ArrayMap;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.MediumTest;
 import androidx.test.rule.ActivityTestRule;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Rule;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/app/NotificationChannelGroupTest.java b/core/tests/coretests/src/android/app/NotificationChannelGroupTest.java
index 046f5ac..322893c 100644
--- a/core/tests/coretests/src/android/app/NotificationChannelGroupTest.java
+++ b/core/tests/coretests/src/android/app/NotificationChannelGroupTest.java
@@ -23,8 +23,8 @@
 import android.platform.test.annotations.Presubmit;
 import android.text.TextUtils;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.google.common.base.Strings;
 
diff --git a/core/tests/coretests/src/android/app/NotificationChannelTest.java b/core/tests/coretests/src/android/app/NotificationChannelTest.java
index 504f98f..c08e42b 100644
--- a/core/tests/coretests/src/android/app/NotificationChannelTest.java
+++ b/core/tests/coretests/src/android/app/NotificationChannelTest.java
@@ -23,7 +23,6 @@
 import static junit.framework.TestCase.assertNull;
 import static junit.framework.TestCase.assertTrue;
 
-import static org.junit.Assert.assertEquals;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.ArgumentMatchers.eq;
@@ -31,7 +30,6 @@
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
-import android.annotation.FlaggedApi;
 import android.content.AttributionSource;
 import android.content.ContentProvider;
 import android.content.ContentResolver;
@@ -54,8 +52,8 @@
 import android.test.mock.MockContentResolver;
 import android.util.Xml;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.android.modules.utils.TypedXmlPullParser;
 import com.android.modules.utils.TypedXmlSerializer;
diff --git a/core/tests/coretests/src/android/app/NotificationHistoryTest.java b/core/tests/coretests/src/android/app/NotificationHistoryTest.java
index c44c1eb..38581e8 100644
--- a/core/tests/coretests/src/android/app/NotificationHistoryTest.java
+++ b/core/tests/coretests/src/android/app/NotificationHistoryTest.java
@@ -24,7 +24,7 @@
 import android.platform.test.annotations.Presubmit;
 
 import androidx.test.InstrumentationRegistry;
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java b/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java
index cd6abdd..b5ee130 100644
--- a/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java
+++ b/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java
@@ -375,20 +375,4 @@
             PropertyInvalidatedCache.MODULE_BLUETOOTH, "getState");
         assertEquals(n1, "cache_key.bluetooth.get_state");
     }
-
-    @Test
-    public void testOnTrimMemory() {
-        TestCache cache = new TestCache(MODULE, "trimMemoryTest");
-        // The cache is not active until it has been invalidated once.
-        cache.invalidateCache();
-        // Populate the cache with six entries.
-        for (int i = 0; i < 6; i++) {
-            cache.query(i);
-        }
-        // The maximum number of entries in TestCache is 4, so even though six entries were
-        // created, only four are retained.
-        assertEquals(4, cache.size());
-        PropertyInvalidatedCache.onTrimMemory();
-        assertEquals(0, cache.size());
-    }
 }
diff --git a/core/tests/coretests/src/android/app/QueuedWorkTest.java b/core/tests/coretests/src/android/app/QueuedWorkTest.java
index 7021187..230c9e8 100644
--- a/core/tests/coretests/src/android/app/QueuedWorkTest.java
+++ b/core/tests/coretests/src/android/app/QueuedWorkTest.java
@@ -20,18 +20,18 @@
 
 import android.platform.test.annotations.Presubmit;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.Semaphore;
 
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+
 @RunWith(AndroidJUnit4.class)
 @Presubmit
 @SmallTest
diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
index 294352e..24f6cea 100644
--- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
+++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
@@ -78,10 +78,10 @@
 import android.window.WindowContextInfo;
 import android.window.WindowTokenClientController;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.MediumTest;
 import androidx.test.platform.app.InstrumentationRegistry;
 import androidx.test.rule.ActivityTestRule;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.android.internal.content.ReferrerIntent;
 
@@ -254,8 +254,8 @@
         try {
             // Send process level config change.
             ClientTransaction transaction = newTransaction(activityThread);
-            transaction.addTransactionItem(ConfigurationChangeItem.obtain(
-                    newConfig, DEVICE_ID_INVALID));
+            transaction.addTransactionItem(
+                    new ConfigurationChangeItem(newConfig, DEVICE_ID_INVALID));
             appThread.scheduleTransaction(transaction);
             InstrumentationRegistry.getInstrumentation().waitForIdleSync();
 
@@ -271,7 +271,7 @@
             newConfig.seq++;
             newConfig.smallestScreenWidthDp++;
             transaction = newTransaction(activityThread);
-            transaction.addTransactionItem(ActivityConfigurationChangeItem.obtain(
+            transaction.addTransactionItem(new ActivityConfigurationChangeItem(
                     activity.getActivityToken(), newConfig, new ActivityWindowInfo()));
             appThread.scheduleTransaction(transaction);
             InstrumentationRegistry.getInstrumentation().waitForIdleSync();
@@ -474,16 +474,16 @@
         activity.mTestLatch = new CountDownLatch(1);
 
         ClientTransaction transaction = newTransaction(activityThread);
-        transaction.addTransactionItem(ConfigurationChangeItem.obtain(
-                processConfigLandscape, DEVICE_ID_INVALID));
+        transaction.addTransactionItem(
+                new ConfigurationChangeItem(processConfigLandscape, DEVICE_ID_INVALID));
         appThread.scheduleTransaction(transaction);
 
         transaction = newTransaction(activityThread);
-        transaction.addTransactionItem(ActivityConfigurationChangeItem.obtain(
+        transaction.addTransactionItem(new ActivityConfigurationChangeItem(
                 activity.getActivityToken(), activityConfigLandscape, new ActivityWindowInfo()));
-        transaction.addTransactionItem(ConfigurationChangeItem.obtain(
-                processConfigPortrait, DEVICE_ID_INVALID));
-        transaction.addTransactionItem(ActivityConfigurationChangeItem.obtain(
+        transaction.addTransactionItem(
+                new ConfigurationChangeItem(processConfigPortrait, DEVICE_ID_INVALID));
+        transaction.addTransactionItem(new ActivityConfigurationChangeItem(
                 activity.getActivityToken(), activityConfigPortrait, new ActivityWindowInfo()));
         appThread.scheduleTransaction(transaction);
 
@@ -847,7 +847,7 @@
             final ActivityWindowInfo newInfo = new ActivityWindowInfo();
             newInfo.set(true /* isEmbedded */, new Rect(0, 0, 1000, 2000),
                     new Rect(0, 0, 1000, 1000));
-            final ActivityRelaunchItem relaunchItem = ActivityRelaunchItem.obtain(
+            final ActivityRelaunchItem relaunchItem = new ActivityRelaunchItem(
                     activity.getActivityToken(), null, null, 0,
                     new MergedConfiguration(currentConfig, currentConfig),
                     false /* preserveWindow */, newInfo);
@@ -881,7 +881,7 @@
             final ActivityWindowInfo activityWindowInfo = new ActivityWindowInfo();
             activityWindowInfo.set(true /* isEmbedded */, taskBounds, taskFragmentBounds);
             final ActivityConfigurationChangeItem activityConfigurationChangeItem =
-                    ActivityConfigurationChangeItem.obtain(
+                    new ActivityConfigurationChangeItem(
                             activity.getActivityToken(), config, activityWindowInfo);
             final ClientTransaction transaction = newTransaction(activity);
             transaction.addTransactionItem(activityConfigurationChangeItem);
@@ -898,7 +898,7 @@
                     new ActivityWindowInfo(activityWindowInfo);
             config.seq++;
             final ActivityConfigurationChangeItem activityConfigurationChangeItem2 =
-                    ActivityConfigurationChangeItem.obtain(
+                    new ActivityConfigurationChangeItem(
                             activity.getActivityToken(), config, activityWindowInfo2);
             final ClientTransaction transaction2 = newTransaction(activity);
             transaction2.addTransactionItem(activityConfigurationChangeItem2);
@@ -978,12 +978,12 @@
         } else {
             activityWindowInfo = record.getActivityWindowInfo();
         }
-        final ClientTransactionItem callbackItem = ActivityRelaunchItem.obtain(
+        final ClientTransactionItem callbackItem = new ActivityRelaunchItem(
                 activity.getActivityToken(), null, null, 0,
                 new MergedConfiguration(currentConfig, currentConfig),
                 false /* preserveWindow */, activityWindowInfo);
         final ResumeActivityItem resumeStateRequest =
-                ResumeActivityItem.obtain(activity.getActivityToken(), true /* isForward */,
+                new ResumeActivityItem(activity.getActivityToken(), true /* isForward */,
                         false /* shouldSendCompatFakeFocus*/);
 
         final ClientTransaction transaction = newTransaction(activity);
@@ -996,7 +996,7 @@
     @NonNull
     private static ClientTransaction newResumeTransaction(@NonNull Activity activity) {
         final ResumeActivityItem resumeStateRequest =
-                ResumeActivityItem.obtain(activity.getActivityToken(), true /* isForward */,
+                new ResumeActivityItem(activity.getActivityToken(), true /* isForward */,
                         false /* shouldSendCompatFakeFocus */);
 
         final ClientTransaction transaction = newTransaction(activity);
@@ -1007,8 +1007,7 @@
 
     @NonNull
     private static ClientTransaction newStopTransaction(@NonNull Activity activity) {
-        final StopActivityItem stopStateRequest = StopActivityItem.obtain(
-                activity.getActivityToken());
+        final StopActivityItem stopStateRequest = new StopActivityItem(activity.getActivityToken());
 
         final ClientTransaction transaction = newTransaction(activity);
         transaction.addTransactionItem(stopStateRequest);
@@ -1019,7 +1018,7 @@
     @NonNull
     private static ClientTransaction newActivityConfigTransaction(@NonNull Activity activity,
             @NonNull Configuration config) {
-        final ActivityConfigurationChangeItem item = ActivityConfigurationChangeItem.obtain(
+        final ActivityConfigurationChangeItem item = new ActivityConfigurationChangeItem(
                 activity.getActivityToken(), config, new ActivityWindowInfo());
 
         final ClientTransaction transaction = newTransaction(activity);
@@ -1031,8 +1030,7 @@
     @NonNull
     private static ClientTransaction newNewIntentTransaction(@NonNull Activity activity,
             @NonNull List<ReferrerIntent> intents, boolean resume) {
-        final NewIntentItem item = NewIntentItem.obtain(activity.getActivityToken(), intents,
-                resume);
+        final NewIntentItem item = new NewIntentItem(activity.getActivityToken(), intents, resume);
 
         final ClientTransaction transaction = newTransaction(activity);
         transaction.addTransactionItem(item);
@@ -1047,7 +1045,7 @@
 
     @NonNull
     private static ClientTransaction newTransaction(@NonNull ActivityThread activityThread) {
-        return ClientTransaction.obtain(activityThread.getApplicationThread());
+        return new ClientTransaction(activityThread.getApplicationThread());
     }
 
     // Test activity
diff --git a/core/tests/coretests/src/android/app/activity/ActivityTransitionDrawableTest.java b/core/tests/coretests/src/android/app/activity/ActivityTransitionDrawableTest.java
index 2c4e443..a50f524 100644
--- a/core/tests/coretests/src/android/app/activity/ActivityTransitionDrawableTest.java
+++ b/core/tests/coretests/src/android/app/activity/ActivityTransitionDrawableTest.java
@@ -27,9 +27,9 @@
 import android.view.View;
 import android.view.Window;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.MediumTest;
 import androidx.test.rule.ActivityTestRule;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Rule;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/app/activity/RegisterComponentCallbacksTest.java b/core/tests/coretests/src/android/app/activity/RegisterComponentCallbacksTest.java
index fd1add9..5958d15 100644
--- a/core/tests/coretests/src/android/app/activity/RegisterComponentCallbacksTest.java
+++ b/core/tests/coretests/src/android/app/activity/RegisterComponentCallbacksTest.java
@@ -36,8 +36,8 @@
 
 import androidx.test.core.app.ActivityScenario;
 import androidx.test.ext.junit.rules.ActivityScenarioRule;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Rule;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/app/activity/ServiceTest.java b/core/tests/coretests/src/android/app/activity/ServiceTest.java
index 3f3d6a3..4e3b2af 100644
--- a/core/tests/coretests/src/android/app/activity/ServiceTest.java
+++ b/core/tests/coretests/src/android/app/activity/ServiceTest.java
@@ -157,6 +157,21 @@
         assertThat(mCurrentConnection.takePid(), is(NOT_STARTED));
     }
 
+    @Test
+    public void testRestart_stickyStartedService_unbindHappenedAfterRestart_restarted() {
+        final int servicePid = startService(Service.START_STICKY);
+        assertThat(servicePid, not(NOT_STARTED));
+        assertThat(bindService(0 /* flags */), is(servicePid));
+
+        final int restartedServicePid = waitForServiceStarted(
+                () -> {
+                    Process.killProcess(servicePid);
+                    mContext.unbindService(mCurrentConnection);
+                    mCurrentConnection = null;
+                });
+        assertThat(restartedServicePid, not(NOT_STARTED));
+    }
+
     /** @return The pid of the started service. */
     private int startService(int code) {
         return waitForServiceStarted(
diff --git a/core/tests/coretests/src/android/app/admin/PackagePolicyTest.java b/core/tests/coretests/src/android/app/admin/PackagePolicyTest.java
index d8298fd..c648fd2 100644
--- a/core/tests/coretests/src/android/app/admin/PackagePolicyTest.java
+++ b/core/tests/coretests/src/android/app/admin/PackagePolicyTest.java
@@ -29,8 +29,8 @@
 import android.platform.test.annotations.Presubmit;
 import android.util.ArraySet;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/app/admin/PasswordPolicyTest.java b/core/tests/coretests/src/android/app/admin/PasswordPolicyTest.java
index f1be173..2d0dd9f 100644
--- a/core/tests/coretests/src/android/app/admin/PasswordPolicyTest.java
+++ b/core/tests/coretests/src/android/app/admin/PasswordPolicyTest.java
@@ -36,8 +36,8 @@
 import android.app.admin.PasswordPolicy;
 import android.platform.test.annotations.Presubmit;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/app/assist/AssistStructureTest.java b/core/tests/coretests/src/android/app/assist/AssistStructureTest.java
index 1f2788c..a28b2f6 100644
--- a/core/tests/coretests/src/android/app/assist/AssistStructureTest.java
+++ b/core/tests/coretests/src/android/app/assist/AssistStructureTest.java
@@ -49,8 +49,8 @@
 
 import androidx.annotation.NonNull;
 import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.rule.ActivityTestRule;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/app/backup/BackupAgentTest.java b/core/tests/coretests/src/android/app/backup/BackupAgentTest.java
index cd5deb6..93bbb3a 100644
--- a/core/tests/coretests/src/android/app/backup/BackupAgentTest.java
+++ b/core/tests/coretests/src/android/app/backup/BackupAgentTest.java
@@ -32,7 +32,7 @@
 import android.platform.test.flag.junit.SetFlagsRule;
 import android.util.ArraySet;
 
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import com.android.server.backup.Flags;
 
diff --git a/core/tests/coretests/src/android/app/backup/BackupManagerMonitorWrapperTest.java b/core/tests/coretests/src/android/app/backup/BackupManagerMonitorWrapperTest.java
index 1f5e0cf..3620a0c 100644
--- a/core/tests/coretests/src/android/app/backup/BackupManagerMonitorWrapperTest.java
+++ b/core/tests/coretests/src/android/app/backup/BackupManagerMonitorWrapperTest.java
@@ -25,7 +25,7 @@
 import android.os.Bundle;
 import android.platform.test.annotations.Presubmit;
 
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/app/backup/BackupManagerTest.java b/core/tests/coretests/src/android/app/backup/BackupManagerTest.java
index 567ca01..88994c9 100644
--- a/core/tests/coretests/src/android/app/backup/BackupManagerTest.java
+++ b/core/tests/coretests/src/android/app/backup/BackupManagerTest.java
@@ -30,7 +30,7 @@
 import android.os.UserHandle;
 import android.platform.test.annotations.Presubmit;
 
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/app/backup/BackupRestoreEventLoggerTest.java b/core/tests/coretests/src/android/app/backup/BackupRestoreEventLoggerTest.java
index 0aefef2..7b41217 100644
--- a/core/tests/coretests/src/android/app/backup/BackupRestoreEventLoggerTest.java
+++ b/core/tests/coretests/src/android/app/backup/BackupRestoreEventLoggerTest.java
@@ -28,7 +28,7 @@
 import android.platform.test.annotations.Presubmit;
 import android.platform.test.flag.junit.SetFlagsRule;
 
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import com.android.server.backup.Flags;
 
diff --git a/core/tests/coretests/src/android/app/compat/CompatChangesTest.java b/core/tests/coretests/src/android/app/compat/CompatChangesTest.java
index fbd02ed..8fd21c8 100644
--- a/core/tests/coretests/src/android/app/compat/CompatChangesTest.java
+++ b/core/tests/coretests/src/android/app/compat/CompatChangesTest.java
@@ -25,8 +25,8 @@
 import android.platform.test.annotations.Presubmit;
 
 import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges;
 import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
diff --git a/core/tests/coretests/src/android/app/people/PeopleManagerTest.java b/core/tests/coretests/src/android/app/people/PeopleManagerTest.java
index a2afc77..1296d32 100644
--- a/core/tests/coretests/src/android/app/people/PeopleManagerTest.java
+++ b/core/tests/coretests/src/android/app/people/PeopleManagerTest.java
@@ -31,7 +31,7 @@
 import android.util.Pair;
 
 import androidx.test.InstrumentationRegistry;
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/app/people/PeopleSpaceTileTest.java b/core/tests/coretests/src/android/app/people/PeopleSpaceTileTest.java
index 5cee2c1..a9d1d35 100644
--- a/core/tests/coretests/src/android/app/people/PeopleSpaceTileTest.java
+++ b/core/tests/coretests/src/android/app/people/PeopleSpaceTileTest.java
@@ -43,8 +43,8 @@
 import android.os.UserHandle;
 
 import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.google.common.collect.ImmutableList;
 
diff --git a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionItemTest.java b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionItemTest.java
index 72c4639..f023196 100644
--- a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionItemTest.java
+++ b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionItemTest.java
@@ -21,13 +21,17 @@
 import static org.junit.Assert.assertEquals;
 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.eq;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 
 import android.app.Activity;
+import android.app.ActivityManager;
 import android.app.ActivityThread;
 import android.app.ClientTransactionHandler;
 import android.content.res.Configuration;
@@ -43,8 +47,8 @@
 import android.window.ClientWindowFrames;
 import android.window.WindowContextInfo;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Rule;
@@ -87,10 +91,6 @@
     private ActivityThread.ActivityClientRecord mActivityClientRecord;
     private ArrayMap<IBinder, DestroyActivityItem> mActivitiesToBeDestroyed;
     private InsetsState mInsetsState;
-    private ClientWindowFrames mFrames;
-    private MergedConfiguration mMergedConfiguration;
-    private ActivityWindowInfo mActivityWindowInfo;
-    private InsetsSourceControl.Array mActiveControls;
 
     @Before
     public void setup() {
@@ -99,10 +99,6 @@
         mActivitiesToBeDestroyed = new ArrayMap<>();
         mActivityClientRecord = new ActivityThread.ActivityClientRecord();
         mInsetsState = new InsetsState();
-        mFrames = new ClientWindowFrames();
-        mMergedConfiguration = new MergedConfiguration(mGlobalConfig, mConfiguration);
-        mActivityWindowInfo = new ActivityWindowInfo();
-        mActiveControls = new InsetsSourceControl.Array();
 
         doReturn(mActivity).when(mHandler).getActivity(mActivityToken);
         doReturn(mActivitiesToBeDestroyed).when(mHandler).getActivitiesToBeDestroyed();
@@ -110,8 +106,9 @@
 
     @Test
     public void testDestroyActivityItem_preExecute() {
-        final DestroyActivityItem item = DestroyActivityItem
-                .obtain(mActivityToken, false /* finished */);
+        final DestroyActivityItem item =
+                new DestroyActivityItem(mActivityToken, false /* finished */);
+
         item.preExecute(mHandler);
 
         assertEquals(1, mActivitiesToBeDestroyed.size());
@@ -120,9 +117,10 @@
 
     @Test
     public void testDestroyActivityItem_postExecute() {
-        final DestroyActivityItem item = DestroyActivityItem
-                .obtain(mActivityToken, false /* finished */);
+        final DestroyActivityItem item =
+                new DestroyActivityItem(mActivityToken, false /* finished */);
         item.preExecute(mHandler);
+
         item.postExecute(mHandler, mPendingActions);
 
         assertTrue(mActivitiesToBeDestroyed.isEmpty());
@@ -130,8 +128,9 @@
 
     @Test
     public void testDestroyActivityItem_execute() {
-        final DestroyActivityItem item = DestroyActivityItem
-                .obtain(mActivityToken, false /* finished */);
+        final DestroyActivityItem item =
+                new DestroyActivityItem(mActivityToken, false /* finished */);
+
         item.execute(mHandler, mActivityClientRecord, mPendingActions);
 
         verify(mHandler).handleDestroyActivity(eq(mActivityClientRecord), eq(false) /* finishing */,
@@ -139,9 +138,45 @@
     }
 
     @Test
+    public void testResumeActivityItem_preExecute_withProcState_updatesProcessState() {
+        final ResumeActivityItem item = new ResumeActivityItem(mActivityToken,
+                ActivityManager.PROCESS_STATE_TOP /* procState */,
+                true /* isForward */,
+                false /* shouldSendCompatFakeFocus*/);
+
+        item.preExecute(mHandler);
+
+        verify(mHandler).updateProcessState(ActivityManager.PROCESS_STATE_TOP, false);
+    }
+
+    @Test
+    public void testResumeActivityItem_preExecute_withUnknownProcState_skipsProcessStateUpdate() {
+        final ResumeActivityItem item = new ResumeActivityItem(mActivityToken,
+                ActivityManager.PROCESS_STATE_UNKNOWN /* procState */,
+                true /* isForward */,
+                false /* shouldSendCompatFakeFocus*/);
+
+        item.preExecute(mHandler);
+
+        verify(mHandler, never()).updateProcessState(anyInt(), anyBoolean());
+    }
+
+    @Test
+    public void testResumeActivityItem_preExecute_withoutProcState_skipsProcessStateUpdate() {
+        final ResumeActivityItem item = new ResumeActivityItem(mActivityToken,
+                true /* isForward */,
+                false /* shouldSendCompatFakeFocus*/);
+
+        item.preExecute(mHandler);
+
+        verify(mHandler, never()).updateProcessState(anyInt(), anyBoolean());
+    }
+
+    @Test
     public void testWindowContextInfoChangeItem_execute() {
-        final WindowContextInfoChangeItem item = WindowContextInfoChangeItem
-                .obtain(mWindowClientToken, mConfiguration, DEFAULT_DISPLAY);
+        final WindowContextInfoChangeItem item = new WindowContextInfoChangeItem(mWindowClientToken,
+                mConfiguration, DEFAULT_DISPLAY);
+
         item.execute(mHandler, mPendingActions);
 
         verify(mHandler).handleWindowContextInfoChanged(mWindowClientToken,
@@ -150,8 +185,9 @@
 
     @Test
     public void testWindowContextWindowRemovalItem_execute() {
-        final WindowContextWindowRemovalItem item = WindowContextWindowRemovalItem.obtain(
-                mWindowClientToken);
+        final WindowContextWindowRemovalItem item =
+                new WindowContextWindowRemovalItem(mWindowClientToken);
+
         item.execute(mHandler, mPendingActions);
 
         verify(mHandler).handleWindowContextWindowRemoval(mWindowClientToken);
@@ -159,37 +195,43 @@
 
     @Test
     public void testWindowStateResizeItem_execute() throws RemoteException {
-        final WindowStateResizeItem item = WindowStateResizeItem.obtain(mWindow, mFrames,
-                true /* reportDraw */, mMergedConfiguration, mInsetsState, true /* forceLayout */,
+        final MergedConfiguration mergedConfiguration = new MergedConfiguration(mGlobalConfig,
+                mConfiguration);
+        final ActivityWindowInfo activityWindowInfo = new ActivityWindowInfo();
+        final ClientWindowFrames frames = new ClientWindowFrames();
+        final WindowStateResizeItem item = new WindowStateResizeItem(mWindow, frames,
+                true /* reportDraw */, mergedConfiguration, mInsetsState, true /* forceLayout */,
                 true /* alwaysConsumeSystemBars */, 123 /* displayId */, 321 /* syncSeqId */,
-                true /* dragResizing */, mActivityWindowInfo);
+                true /* dragResizing */, activityWindowInfo);
+
         item.execute(mHandler, mPendingActions);
 
-        verify(mWindow).resized(mFrames,
-                true /* reportDraw */, mMergedConfiguration, mInsetsState, true /* forceLayout */,
+        verify(mWindow).resized(frames,
+                true /* reportDraw */, mergedConfiguration, mInsetsState, true /* forceLayout */,
                 true /* alwaysConsumeSystemBars */, 123 /* displayId */, 321 /* syncSeqId */,
-                true /* dragResizing */, mActivityWindowInfo);
+                true /* dragResizing */, activityWindowInfo);
     }
 
     @Test
     public void testWindowStateInsetsControlChangeItem_execute() throws RemoteException {
-        final WindowStateInsetsControlChangeItem item = WindowStateInsetsControlChangeItem.obtain(
-                mWindow, mInsetsState, mActiveControls);
+        final InsetsSourceControl.Array activeControls = new InsetsSourceControl.Array();
+        final WindowStateInsetsControlChangeItem item = new WindowStateInsetsControlChangeItem(
+                mWindow, mInsetsState, activeControls);
+
         item.execute(mHandler, mPendingActions);
 
-        verify(mWindow).insetsControlChanged(mInsetsState, mActiveControls);
+        verify(mWindow).insetsControlChanged(mInsetsState, activeControls);
     }
 
     @Test
     public void testWindowStateInsetsControlChangeItem_executeError() throws RemoteException {
+        final InsetsSourceControl.Array spiedActiveControls = spy(new InsetsSourceControl.Array());
+        final WindowStateInsetsControlChangeItem item = new WindowStateInsetsControlChangeItem(
+                mWindow, mInsetsState, spiedActiveControls, false /* copyActiveControls */);
         doThrow(new RemoteException()).when(mWindow).insetsControlChanged(any(), any());
 
-        mActiveControls = spy(mActiveControls);
-        final WindowStateInsetsControlChangeItem item = WindowStateInsetsControlChangeItem.obtain(
-                mWindow, mInsetsState, mActiveControls);
-        item.mActiveControls = mActiveControls;
         item.execute(mHandler, mPendingActions);
 
-        verify(mActiveControls).release();
+        verify(spiedActiveControls).release();
     }
 }
diff --git a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionListenerControllerTest.java b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionListenerControllerTest.java
index f9609fc..31a4f16 100644
--- a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionListenerControllerTest.java
+++ b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionListenerControllerTest.java
@@ -50,8 +50,8 @@
 import android.window.ActivityWindowInfo;
 import android.window.WindowTokenClient;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Rule;
diff --git a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionTests.java b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionTests.java
index 79659ca..05392eb 100644
--- a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionTests.java
@@ -23,8 +23,8 @@
 import android.app.ClientTransactionHandler;
 import android.platform.test.annotations.Presubmit;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -52,7 +52,7 @@
         final ClientTransactionHandler clientTransactionHandler =
                 mock(ClientTransactionHandler.class);
 
-        final ClientTransaction transaction = ClientTransaction.obtain(null /* client */);
+        final ClientTransaction transaction = new ClientTransaction();
         transaction.addTransactionItem(callback1);
         transaction.addTransactionItem(callback2);
         transaction.addTransactionItem(stateRequest);
diff --git a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
deleted file mode 100644
index 918235b..0000000
--- a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
+++ /dev/null
@@ -1,248 +0,0 @@
-/*
- * Copyright 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.app.servertransaction;
-
-import static android.app.servertransaction.TestUtils.config;
-import static android.app.servertransaction.TestUtils.mergedConfig;
-import static android.app.servertransaction.TestUtils.referrerIntentList;
-import static android.app.servertransaction.TestUtils.resultInfoList;
-
-import static com.android.window.flags.Flags.FLAG_DISABLE_OBJECT_POOL;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertNotSame;
-import static org.junit.Assert.assertSame;
-
-import android.annotation.NonNull;
-import android.app.ActivityOptions;
-import android.app.IApplicationThread;
-import android.app.servertransaction.TestUtils.LaunchActivityItemBuilder;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.content.pm.ApplicationInfo;
-import android.content.res.Configuration;
-import android.os.Binder;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.os.PersistableBundle;
-import android.platform.test.annotations.Presubmit;
-import android.platform.test.flag.junit.FlagsParameterization;
-import android.platform.test.flag.junit.SetFlagsRule;
-import android.window.ActivityWindowInfo;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.window.flags.Flags;
-
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.junit.MockitoJUnit;
-import org.mockito.junit.MockitoRule;
-
-import java.util.List;
-import java.util.function.Supplier;
-
-import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
-import platform.test.runner.parameterized.Parameters;
-
-/**
- * Tests for {@link ObjectPool}.
- *
- * <p>Build/Install/Run:
- *  atest FrameworksCoreTests:ObjectPoolTests
- *
- * <p>This test class is a part of Window Manager Service tests and specified in
- * {@link com.android.server.wm.test.filters.FrameworksTestsFilter}.
- */
-@RunWith(ParameterizedAndroidJunit4.class)
-@SmallTest
-@Presubmit
-public class ObjectPoolTests {
-
-    @Parameters(name = "{0}")
-    public static List<FlagsParameterization> getParams() {
-        return FlagsParameterization.allCombinationsOf(FLAG_DISABLE_OBJECT_POOL);
-    }
-
-    @Rule
-    public final MockitoRule mocks = MockitoJUnit.rule();
-
-    @Rule
-    public SetFlagsRule mSetFlagsRule;
-
-    @Mock
-    private IApplicationThread mApplicationThread;
-    @Mock
-    private IBinder mActivityToken;
-
-    public ObjectPoolTests(FlagsParameterization flags) {
-        mSetFlagsRule = new SetFlagsRule(flags);
-    }
-
-    // 1. Check if two obtained objects from pool are not the same.
-    // 2. Check if the state of the object is cleared after recycling.
-    // 3. Check if the same object is obtained from pool after recycling.
-
-    @Test
-    public void testRecycleActivityConfigurationChangeItem() {
-        testRecycle(() -> ActivityConfigurationChangeItem.obtain(mActivityToken, config(),
-                new ActivityWindowInfo()));
-    }
-
-    @Test
-    public void testRecycleActivityResultItem() {
-        testRecycle(() -> ActivityResultItem.obtain(mActivityToken, resultInfoList()));
-    }
-
-    @Test
-    public void testRecycleConfigurationChangeItem() {
-        testRecycle(() -> ConfigurationChangeItem.obtain(config(), 1));
-    }
-
-    @Test
-    public void testRecycleDestroyActivityItem() {
-        testRecycle(() -> DestroyActivityItem.obtain(mActivityToken, true));
-    }
-
-    @Test
-    public void testRecycleLaunchActivityItem() {
-        final IBinder activityToken = new Binder();
-        final Intent intent = new Intent("action");
-        final int ident = 57;
-        final ActivityInfo activityInfo = new ActivityInfo();
-        activityInfo.flags = 42;
-        activityInfo.setMaxAspectRatio(2.4f);
-        activityInfo.launchToken = "token";
-        activityInfo.applicationInfo = new ApplicationInfo();
-        activityInfo.packageName = "packageName";
-        activityInfo.name = "name";
-        final Configuration overrideConfig = new Configuration();
-        overrideConfig.assetsSeq = 5;
-        final String referrer = "referrer";
-        final int procState = 4;
-        final Bundle bundle = new Bundle();
-        bundle.putString("key", "value");
-        final PersistableBundle persistableBundle = new PersistableBundle();
-        persistableBundle.putInt("k", 4);
-        final IBinder assistToken = new Binder();
-        final IBinder shareableActivityToken = new Binder();
-        final int deviceId = 3;
-        final IBinder taskFragmentToken = new Binder();
-        final IBinder initialCallerInfoAccessToken = new Binder();
-        final ActivityWindowInfo activityWindowInfo = new ActivityWindowInfo();
-
-        testRecycle(() -> new LaunchActivityItemBuilder(
-                activityToken, intent, activityInfo)
-                .setIdent(ident)
-                .setCurConfig(config())
-                .setOverrideConfig(overrideConfig)
-                .setReferrer(referrer)
-                .setProcState(procState)
-                .setState(bundle)
-                .setPersistentState(persistableBundle)
-                .setPendingResults(resultInfoList())
-                .setPendingNewIntents(referrerIntentList())
-                .setIsForward(true)
-                .setAssistToken(assistToken)
-                .setShareableActivityToken(shareableActivityToken)
-                .setTaskFragmentToken(taskFragmentToken)
-                .setDeviceId(deviceId)
-                .setInitialCallerInfoAccessToken(initialCallerInfoAccessToken)
-                .setActivityWindowInfo(activityWindowInfo)
-                .build());
-    }
-
-    @Test
-    public void testRecycleActivityRelaunchItem() {
-        testRecycle(() -> ActivityRelaunchItem.obtain(mActivityToken,
-                resultInfoList(), referrerIntentList(), 42, mergedConfig(), true,
-                new ActivityWindowInfo()));
-    }
-
-    @Test
-    public void testRecycleMoveToDisplayItem() {
-        testRecycle(() -> MoveToDisplayItem.obtain(mActivityToken, 4, config(),
-                new ActivityWindowInfo()));
-    }
-
-    @Test
-    public void testRecycleNewIntentItem() {
-        testRecycle(() -> NewIntentItem.obtain(mActivityToken, referrerIntentList(), false));
-    }
-
-    @Test
-    public void testRecyclePauseActivityItemItem() {
-        testRecycle(() -> PauseActivityItem.obtain(mActivityToken, true, true, true, true));
-    }
-
-    @Test
-    public void testRecycleResumeActivityItem() {
-        testRecycle(() -> ResumeActivityItem.obtain(mActivityToken, 3, true, false));
-    }
-
-    @Test
-    public void testRecycleStartActivityItem() {
-        testRecycle(() -> StartActivityItem.obtain(mActivityToken,
-                new ActivityOptions.SceneTransitionInfo()));
-    }
-
-    @Test
-    public void testRecycleStopItem() {
-        testRecycle(() -> StopActivityItem.obtain(mActivityToken));
-    }
-
-    @Test
-    public void testRecycleClientTransaction() {
-        testRecycle(() -> ClientTransaction.obtain(mApplicationThread));
-    }
-
-    private void testRecycle(@NonNull Supplier<? extends ObjectPoolItem> obtain) {
-        // Reuse the same object after recycle.
-        final ObjectPoolItem item = obtain.get();
-        item.recycle();
-        final ObjectPoolItem item2 = obtain.get();
-
-        if (Flags.disableObjectPool()) {
-            assertNotSame(item, item2);  // Different instance.
-        } else {
-            assertSame(item, item2);
-        }
-
-        // Create new object when the pool is empty.
-        final ObjectPoolItem item3 = obtain.get();
-
-        assertNotSame(item, item3);
-        if (Flags.disableObjectPool()) {
-            // Skip recycle if flag enabled, compare unnecessary.
-            return;
-        }
-        assertEquals(item, item3);
-
-        // Reset fields after recycle.
-        item.recycle();
-
-        assertNotEquals(item, item3);
-
-        // Recycled objects are equal.
-        item3.recycle();
-
-        assertEquals(item, item3);
-    }
-}
diff --git a/core/tests/coretests/src/android/app/servertransaction/TestUtils.java b/core/tests/coretests/src/android/app/servertransaction/TestUtils.java
index c1b9efd..24bc547 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TestUtils.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TestUtils.java
@@ -271,7 +271,7 @@
 
         @NonNull
         LaunchActivityItem build() {
-            return LaunchActivityItem.obtain(mActivityToken, mIntent, mIdent, mInfo,
+            return new LaunchActivityItem(mActivityToken, mIntent, mIdent, mInfo,
                     mCurConfig, mOverrideConfig, mDeviceId, mReferrer, mVoiceInteractor,
                     mProcState, mState, mPersistentState, mPendingResults, mPendingNewIntents,
                     mActivityOptions != null ? mActivityOptions.getSceneTransitionInfo() : null,
diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java
index eb69b9c..76a53d4 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java
@@ -53,8 +53,8 @@
 import android.platform.test.annotations.Presubmit;
 import android.util.ArrayMap;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Rule;
@@ -248,7 +248,7 @@
         ClientTransactionItem callback2 = mock(ClientTransactionItem.class);
         when(callback2.getPostExecutionState()).thenReturn(UNDEFINED);
 
-        ClientTransaction transaction = ClientTransaction.obtain(null /* client */);
+        final ClientTransaction transaction = new ClientTransaction();
         transaction.addTransactionItem(callback1);
         transaction.addTransactionItem(callback2);
         transaction.addTransactionItem(mActivityLifecycleItem);
@@ -273,15 +273,15 @@
 
         // An incoming destroy transaction enters binder thread (preExecute).
         final IBinder token = mock(IBinder.class);
-        final ClientTransaction destroyTransaction = ClientTransaction.obtain(null /* client */);
+        final ClientTransaction destroyTransaction = new ClientTransaction();
         destroyTransaction.addTransactionItem(
-                DestroyActivityItem.obtain(token, false /* finished */));
+                new DestroyActivityItem(token, false /* finished */));
         destroyTransaction.preExecute(mTransactionHandler);
         // The activity should be added to to-be-destroyed container.
         assertEquals(1, activitiesToBeDestroyed.size());
 
         // A previous queued launch transaction runs on main thread (execute).
-        final ClientTransaction launchTransaction = ClientTransaction.obtain(null /* client */);
+        final ClientTransaction launchTransaction = new ClientTransaction();
         final LaunchActivityItem launchItem =
                 spy(new LaunchActivityItemBuilder(token, new Intent(), new ActivityInfo()).build());
         launchTransaction.addTransactionItem(launchItem);
@@ -302,7 +302,7 @@
 
         PostExecItem postExecItem = new PostExecItem(ON_RESUME);
 
-        ClientTransaction transaction = ClientTransaction.obtain(null /* client */);
+        final ClientTransaction transaction = new ClientTransaction();
         transaction.addTransactionItem(postExecItem);
 
         // Verify resolution that should get to onPause
@@ -454,7 +454,7 @@
         final ActivityTransactionItem activityItem = mock(ActivityTransactionItem.class);
         when(activityItem.getPostExecutionState()).thenReturn(UNDEFINED);
         final IBinder token = mock(IBinder.class);
-        final ClientTransaction transaction = ClientTransaction.obtain(null /* client */);
+        final ClientTransaction transaction = new ClientTransaction();
         transaction.addTransactionItem(activityItem);
         when(mTransactionHandler.getActivityClient(token)).thenReturn(null);
 
@@ -463,7 +463,7 @@
 
     @Test
     public void testActivityItemExecute() {
-        final ClientTransaction transaction = ClientTransaction.obtain(null /* client */);
+        final ClientTransaction transaction = new ClientTransaction();
         final ActivityTransactionItem activityItem = mock(ActivityTransactionItem.class);
         when(activityItem.getPostExecutionState()).thenReturn(UNDEFINED);
         when(activityItem.getActivityToken()).thenReturn(mActivityToken);
@@ -498,7 +498,7 @@
     private static class PostExecItem extends StubItem {
 
         @LifecycleState
-        private int mPostExecutionState;
+        private final int mPostExecutionState;
 
         PostExecItem(@LifecycleState int state) {
             mPostExecutionState = state;
@@ -525,10 +525,6 @@
         }
 
         @Override
-        public void recycle() {
-        }
-
-        @Override
         public void writeToParcel(@NonNull Parcel dest, int flags) {
         }
 
diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
index a4d7661..59d8c8d 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
@@ -39,8 +39,8 @@
 import android.platform.test.annotations.Presubmit;
 import android.window.ActivityWindowInfo;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -74,11 +74,13 @@
     @Test
     public void testConfigurationChange() {
         // Write to parcel
-        ConfigurationChangeItem item = ConfigurationChangeItem.obtain(config(), 1 /* deviceId */);
+        final ConfigurationChangeItem item =
+                new ConfigurationChangeItem(config(), 1 /* deviceId */);
         writeAndPrepareForReading(item);
 
         // Read from parcel and assert
-        ConfigurationChangeItem result = ConfigurationChangeItem.CREATOR.createFromParcel(mParcel);
+        final ConfigurationChangeItem result =
+                ConfigurationChangeItem.CREATOR.createFromParcel(mParcel);
 
         assertEquals(item.hashCode(), result.hashCode());
         assertEquals(item, result);
@@ -90,7 +92,7 @@
         final ActivityWindowInfo activityWindowInfo = new ActivityWindowInfo();
         activityWindowInfo.set(true /* isEmbedded */, new Rect(0, 0, 500, 1000),
                 new Rect(0, 0, 500, 500));
-        ActivityConfigurationChangeItem item = ActivityConfigurationChangeItem.obtain(
+        final ActivityConfigurationChangeItem item = new ActivityConfigurationChangeItem(
                 mActivityToken, config(), activityWindowInfo);
         writeAndPrepareForReading(item);
 
@@ -108,12 +110,12 @@
         final ActivityWindowInfo activityWindowInfo = new ActivityWindowInfo();
         activityWindowInfo.set(true /* isEmbedded */, new Rect(0, 0, 500, 1000),
                 new Rect(0, 0, 500, 500));
-        MoveToDisplayItem item = MoveToDisplayItem.obtain(mActivityToken, 4 /* targetDisplayId */,
-                config(), activityWindowInfo);
+        final MoveToDisplayItem item = new MoveToDisplayItem(mActivityToken,
+                4 /* targetDisplayId */, config(), activityWindowInfo);
         writeAndPrepareForReading(item);
 
         // Read from parcel and assert
-        MoveToDisplayItem result = MoveToDisplayItem.CREATOR.createFromParcel(mParcel);
+        final MoveToDisplayItem result = MoveToDisplayItem.CREATOR.createFromParcel(mParcel);
 
         assertEquals(item.hashCode(), result.hashCode());
         assertEquals(item, result);
@@ -122,11 +124,12 @@
     @Test
     public void testNewIntent() {
         // Write to parcel
-        NewIntentItem item = NewIntentItem.obtain(mActivityToken, referrerIntentList(), false);
+        final NewIntentItem item =
+                new NewIntentItem(mActivityToken, referrerIntentList(), false /* resume */);
         writeAndPrepareForReading(item);
 
         // Read from parcel and assert
-        NewIntentItem result = NewIntentItem.CREATOR.createFromParcel(mParcel);
+        final NewIntentItem result = NewIntentItem.CREATOR.createFromParcel(mParcel);
 
         assertEquals(item.hashCode(), result.hashCode());
         assertEquals(item, result);
@@ -135,11 +138,11 @@
     @Test
     public void testActivityResult() {
         // Write to parcel
-        ActivityResultItem item = ActivityResultItem.obtain(mActivityToken, resultInfoList());
+        final ActivityResultItem item = new ActivityResultItem(mActivityToken, resultInfoList());
         writeAndPrepareForReading(item);
 
         // Read from parcel and assert
-        ActivityResultItem result = ActivityResultItem.CREATOR.createFromParcel(mParcel);
+        final ActivityResultItem result = ActivityResultItem.CREATOR.createFromParcel(mParcel);
 
         assertEquals(item.hashCode(), result.hashCode());
         assertEquals(item, result);
@@ -147,11 +150,12 @@
 
     @Test
     public void testDestroy() {
-        DestroyActivityItem item = DestroyActivityItem.obtain(mActivityToken, true /* finished */);
+        final DestroyActivityItem item =
+                new DestroyActivityItem(mActivityToken, true /* finished */);
         writeAndPrepareForReading(item);
 
         // Read from parcel and assert
-        DestroyActivityItem result = DestroyActivityItem.CREATOR.createFromParcel(mParcel);
+        final DestroyActivityItem result = DestroyActivityItem.CREATOR.createFromParcel(mParcel);
 
         assertEquals(item.hashCode(), result.hashCode());
         assertEquals(item, result);
@@ -162,7 +166,7 @@
         // Write to parcel
         final IBinder activityToken = new Binder();
         final Intent intent = new Intent("action");
-        int ident = 57;
+        final int ident = 57;
         final ActivityInfo activityInfo = new ActivityInfo();
         activityInfo.flags = 42;
         activityInfo.setMaxAspectRatio(2.4f);
@@ -173,7 +177,7 @@
         final Configuration overrideConfig = new Configuration();
         overrideConfig.assetsSeq = 5;
         final String referrer = "referrer";
-        int procState = 4;
+        final int procState = 4;
         final Bundle bundle = new Bundle();
         bundle.putString("key", "value");
         bundle.putParcelable("data", new ParcelableData(1));
@@ -215,17 +219,17 @@
     @Test
     public void testRelaunch() {
         // Write to parcel
-        Configuration overrideConfig = new Configuration();
+        final Configuration overrideConfig = new Configuration();
         overrideConfig.assetsSeq = 5;
         final ActivityWindowInfo activityWindowInfo = new ActivityWindowInfo();
         activityWindowInfo.set(true /* isEmbedded */, new Rect(0, 0, 500, 1000),
                 new Rect(0, 0, 500, 500));
-        ActivityRelaunchItem item = ActivityRelaunchItem.obtain(mActivityToken, resultInfoList(),
+        final ActivityRelaunchItem item = new ActivityRelaunchItem(mActivityToken, resultInfoList(),
                 referrerIntentList(), 35, mergedConfig(), true, activityWindowInfo);
         writeAndPrepareForReading(item);
 
         // Read from parcel and assert
-        ActivityRelaunchItem result = ActivityRelaunchItem.CREATOR.createFromParcel(mParcel);
+        final ActivityRelaunchItem result = ActivityRelaunchItem.CREATOR.createFromParcel(mParcel);
 
         assertEquals(item.hashCode(), result.hashCode());
         assertEquals(item, result);
@@ -234,12 +238,12 @@
     @Test
     public void testPause() {
         // Write to parcel
-        PauseActivityItem item = PauseActivityItem.obtain(mActivityToken, true /* finished */,
+        final PauseActivityItem item = new PauseActivityItem(mActivityToken, true /* finished */,
                 true /* userLeaving */, true /* dontReport */, true /* autoEnteringPip */);
         writeAndPrepareForReading(item);
 
         // Read from parcel and assert
-        PauseActivityItem result = PauseActivityItem.CREATOR.createFromParcel(mParcel);
+        final PauseActivityItem result = PauseActivityItem.CREATOR.createFromParcel(mParcel);
 
         assertEquals(item.hashCode(), result.hashCode());
         assertEquals(item, result);
@@ -248,12 +252,12 @@
     @Test
     public void testResume() {
         // Write to parcel
-        ResumeActivityItem item = ResumeActivityItem.obtain(mActivityToken, 27 /* procState */,
+        final ResumeActivityItem item = new ResumeActivityItem(mActivityToken, 27 /* procState */,
                 true /* isForward */, false /* shouldSendCompatFakeFocus */);
         writeAndPrepareForReading(item);
 
         // Read from parcel and assert
-        ResumeActivityItem result = ResumeActivityItem.CREATOR.createFromParcel(mParcel);
+        final ResumeActivityItem result = ResumeActivityItem.CREATOR.createFromParcel(mParcel);
 
         assertEquals(item.hashCode(), result.hashCode());
         assertEquals(item, result);
@@ -262,11 +266,11 @@
     @Test
     public void testStop() {
         // Write to parcel
-        StopActivityItem item = StopActivityItem.obtain(mActivityToken);
+        final StopActivityItem item = new StopActivityItem(mActivityToken);
         writeAndPrepareForReading(item);
 
         // Read from parcel and assert
-        StopActivityItem result = StopActivityItem.CREATOR.createFromParcel(mParcel);
+        final StopActivityItem result = StopActivityItem.CREATOR.createFromParcel(mParcel);
 
         assertEquals(item.hashCode(), result.hashCode());
         assertEquals(item, result);
@@ -275,12 +279,12 @@
     @Test
     public void testStart() {
         // Write to parcel
-        StartActivityItem item = StartActivityItem.obtain(mActivityToken,
+        final StartActivityItem item = new StartActivityItem(mActivityToken,
                 new ActivityOptions.SceneTransitionInfo());
         writeAndPrepareForReading(item);
 
         // Read from parcel and assert
-        StartActivityItem result = StartActivityItem.CREATOR.createFromParcel(mParcel);
+        final StartActivityItem result = StartActivityItem.CREATOR.createFromParcel(mParcel);
 
         assertEquals(item.hashCode(), result.hashCode());
         assertEquals(item, result);
@@ -289,13 +293,14 @@
     @Test
     public void testClientTransaction() {
         // Write to parcel
-        NewIntentItem callback1 = NewIntentItem.obtain(mActivityToken, new ArrayList<>(), true);
-        ActivityConfigurationChangeItem callback2 = ActivityConfigurationChangeItem.obtain(
+        final NewIntentItem callback1 =
+                new NewIntentItem(mActivityToken, new ArrayList<>(), true /* resume */);
+        final ActivityConfigurationChangeItem callback2 = new ActivityConfigurationChangeItem(
                 mActivityToken, config(), new ActivityWindowInfo());
 
-        StopActivityItem lifecycleRequest = StopActivityItem.obtain(mActivityToken);
+        final StopActivityItem lifecycleRequest = new StopActivityItem(mActivityToken);
 
-        ClientTransaction transaction = ClientTransaction.obtain(null /* client */);
+        final ClientTransaction transaction = new ClientTransaction();
         transaction.addTransactionItem(callback1);
         transaction.addTransactionItem(callback2);
         transaction.addTransactionItem(lifecycleRequest);
@@ -303,7 +308,7 @@
         writeAndPrepareForReading(transaction);
 
         // Read from parcel and assert
-        ClientTransaction result = ClientTransaction.CREATOR.createFromParcel(mParcel);
+        final ClientTransaction result = ClientTransaction.CREATOR.createFromParcel(mParcel);
 
         assertEquals(transaction.hashCode(), result.hashCode());
         assertEquals(transaction, result);
@@ -324,9 +329,7 @@
      *   android.app.servertransaction.TransactionParcelTests$ParcelableData".
      */
     public static class ParcelableData implements Parcelable {
-        int mValue;
-
-        ParcelableData() {}
+        private final int mValue;
 
         ParcelableData(int value) {
             mValue = value;
@@ -342,12 +345,10 @@
             dest.writeInt(mValue);
         }
 
-        public static final Creator<ParcelableData> CREATOR = new Creator<ParcelableData>() {
+        public static final Creator<ParcelableData> CREATOR = new Creator<>() {
             @Override
             public ParcelableData createFromParcel(Parcel source) {
-                final ParcelableData data = new ParcelableData();
-                data.mValue = source.readInt();
-                return data;
+                return new ParcelableData(source.readInt());
             }
 
             @Override
diff --git a/core/tests/coretests/src/android/app/usage/EventListTest.java b/core/tests/coretests/src/android/app/usage/EventListTest.java
index 685fcae..d5a6858 100644
--- a/core/tests/coretests/src/android/app/usage/EventListTest.java
+++ b/core/tests/coretests/src/android/app/usage/EventListTest.java
@@ -22,8 +22,8 @@
 
 import android.util.Log;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/app/usage/ParcelableUsageEventListTest.java b/core/tests/coretests/src/android/app/usage/ParcelableUsageEventListTest.java
index da40f2a..1c35a64 100644
--- a/core/tests/coretests/src/android/app/usage/ParcelableUsageEventListTest.java
+++ b/core/tests/coretests/src/android/app/usage/ParcelableUsageEventListTest.java
@@ -30,8 +30,8 @@
 import android.os.Parcel;
 import android.os.PersistableBundle;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.LargeTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/app/usage/UsageEventsQueryTest.java b/core/tests/coretests/src/android/app/usage/UsageEventsQueryTest.java
index 5516845..97d2819 100644
--- a/core/tests/coretests/src/android/app/usage/UsageEventsQueryTest.java
+++ b/core/tests/coretests/src/android/app/usage/UsageEventsQueryTest.java
@@ -25,8 +25,8 @@
 import android.platform.test.flag.junit.CheckFlagsRule;
 import android.platform.test.flag.junit.DeviceFlagsValueProvider;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Rule;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/app/usage/UsageStatsPersistenceTest.java b/core/tests/coretests/src/android/app/usage/UsageStatsPersistenceTest.java
index 3618543..ad6b1b8 100644
--- a/core/tests/coretests/src/android/app/usage/UsageStatsPersistenceTest.java
+++ b/core/tests/coretests/src/android/app/usage/UsageStatsPersistenceTest.java
@@ -18,8 +18,8 @@
 
 import static junit.framework.Assert.fail;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.android.internal.util.ArrayUtils;
 
diff --git a/core/tests/coretests/src/android/app/usage/UsageStatsTest.java b/core/tests/coretests/src/android/app/usage/UsageStatsTest.java
index f728080..69a7a68 100644
--- a/core/tests/coretests/src/android/app/usage/UsageStatsTest.java
+++ b/core/tests/coretests/src/android/app/usage/UsageStatsTest.java
@@ -38,8 +38,8 @@
 import android.os.UserHandle;
 
 import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 import androidx.test.uiautomator.UiDevice;
 
 import org.junit.Before;
diff --git a/core/tests/coretests/src/android/companion/virtual/audio/VirtualAudioSessionTest.java b/core/tests/coretests/src/android/companion/virtual/audio/VirtualAudioSessionTest.java
index e025fae..b91263e 100644
--- a/core/tests/coretests/src/android/companion/virtual/audio/VirtualAudioSessionTest.java
+++ b/core/tests/coretests/src/android/companion/virtual/audio/VirtualAudioSessionTest.java
@@ -35,7 +35,7 @@
 import android.platform.test.annotations.Presubmit;
 
 import androidx.test.InstrumentationRegistry;
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Rule;
diff --git a/core/tests/coretests/src/android/content/AbstractCrossUserContentResolverTest.java b/core/tests/coretests/src/android/content/AbstractCrossUserContentResolverTest.java
index 92b1c04..1d360cc 100644
--- a/core/tests/coretests/src/android/content/AbstractCrossUserContentResolverTest.java
+++ b/core/tests/coretests/src/android/content/AbstractCrossUserContentResolverTest.java
@@ -18,7 +18,6 @@
 
 import static org.junit.Assert.fail;
 import static org.junit.Assume.assumeTrue;
-import org.junit.Ignore;
 
 import android.app.ActivityManager;
 import android.app.activity.LocalProvider;
@@ -33,13 +32,14 @@
 import android.os.UserManager;
 
 import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.LargeTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.android.compatibility.common.util.SystemUtil;
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
diff --git a/core/tests/coretests/src/android/content/ApexEnvironmentTest.java b/core/tests/coretests/src/android/content/ApexEnvironmentTest.java
index 438c5ae..f3803d2 100644
--- a/core/tests/coretests/src/android/content/ApexEnvironmentTest.java
+++ b/core/tests/coretests/src/android/content/ApexEnvironmentTest.java
@@ -20,8 +20,8 @@
 
 import android.os.UserHandle;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/content/BroadcastReceiverTests.java b/core/tests/coretests/src/android/content/BroadcastReceiverTests.java
index 407c6c3..a9e781c 100644
--- a/core/tests/coretests/src/android/content/BroadcastReceiverTests.java
+++ b/core/tests/coretests/src/android/content/BroadcastReceiverTests.java
@@ -19,8 +19,8 @@
 import static org.junit.Assert.fail;
 
 import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/content/ContentProviderTest.java b/core/tests/coretests/src/android/content/ContentProviderTest.java
index c9a6d22..4ecd2da 100644
--- a/core/tests/coretests/src/android/content/ContentProviderTest.java
+++ b/core/tests/coretests/src/android/content/ContentProviderTest.java
@@ -26,7 +26,7 @@
 import android.os.UserHandle;
 import android.platform.test.annotations.Presubmit;
 
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/content/ContentResolverTest.java b/core/tests/coretests/src/android/content/ContentResolverTest.java
index c8015d4..dfde0bc 100644
--- a/core/tests/coretests/src/android/content/ContentResolverTest.java
+++ b/core/tests/coretests/src/android/content/ContentResolverTest.java
@@ -39,7 +39,7 @@
 import android.util.Size;
 
 import androidx.test.InstrumentationRegistry;
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import org.junit.After;
 import org.junit.Before;
diff --git a/core/tests/coretests/src/android/content/PermissionCheckerTest.java b/core/tests/coretests/src/android/content/PermissionCheckerTest.java
index cb04a74..65d8e2b 100644
--- a/core/tests/coretests/src/android/content/PermissionCheckerTest.java
+++ b/core/tests/coretests/src/android/content/PermissionCheckerTest.java
@@ -23,7 +23,7 @@
 import android.os.Binder;
 
 import androidx.test.InstrumentationRegistry;
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import org.junit.After;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/content/pm/ModuleInfoTest.java b/core/tests/coretests/src/android/content/pm/ModuleInfoTest.java
index 4366e02c..7799185 100644
--- a/core/tests/coretests/src/android/content/pm/ModuleInfoTest.java
+++ b/core/tests/coretests/src/android/content/pm/ModuleInfoTest.java
@@ -22,7 +22,7 @@
 import android.platform.test.annotations.AppModeFull;
 import android.text.TextUtils;
 
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/content/pm/PackageManagerPropertyTests.java b/core/tests/coretests/src/android/content/pm/PackageManagerPropertyTests.java
index 86e95832..ad3a16b 100644
--- a/core/tests/coretests/src/android/content/pm/PackageManagerPropertyTests.java
+++ b/core/tests/coretests/src/android/content/pm/PackageManagerPropertyTests.java
@@ -19,15 +19,14 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.assertThrows;
-import static org.junit.Assert.fail;
+import static org.junit.Assert.assertTrue;
 
 import android.content.pm.PackageManager.Property;
 import android.os.Bundle;
 import android.platform.test.annotations.Presubmit;
 
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/content/pm/PackageManagerTest.java b/core/tests/coretests/src/android/content/pm/PackageManagerTest.java
index 20421d1..b60d614 100644
--- a/core/tests/coretests/src/android/content/pm/PackageManagerTest.java
+++ b/core/tests/coretests/src/android/content/pm/PackageManagerTest.java
@@ -18,8 +18,8 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/content/pm/PackageParserCacheHelperTest.java b/core/tests/coretests/src/android/content/pm/PackageParserCacheHelperTest.java
index 61a3a11..dbd6c2b 100644
--- a/core/tests/coretests/src/android/content/pm/PackageParserCacheHelperTest.java
+++ b/core/tests/coretests/src/android/content/pm/PackageParserCacheHelperTest.java
@@ -24,8 +24,8 @@
 import android.os.Parcel;
 import android.platform.test.annotations.Presubmit;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/content/pm/PackagePartitionsTest.java b/core/tests/coretests/src/android/content/pm/PackagePartitionsTest.java
index 2986d61..6c23ea3 100644
--- a/core/tests/coretests/src/android/content/pm/PackagePartitionsTest.java
+++ b/core/tests/coretests/src/android/content/pm/PackagePartitionsTest.java
@@ -24,7 +24,7 @@
 import android.os.SystemProperties;
 import android.platform.test.annotations.Presubmit;
 
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/content/pm/ShortcutQueryWrapperTest.java b/core/tests/coretests/src/android/content/pm/ShortcutQueryWrapperTest.java
index 8f8488f..ec5e205 100644
--- a/core/tests/coretests/src/android/content/pm/ShortcutQueryWrapperTest.java
+++ b/core/tests/coretests/src/android/content/pm/ShortcutQueryWrapperTest.java
@@ -23,7 +23,7 @@
 import android.os.Parcel;
 import android.platform.test.annotations.Presubmit;
 
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import com.google.android.collect.Lists;
 
diff --git a/core/tests/coretests/src/android/content/pm/UserInfoTest.java b/core/tests/coretests/src/android/content/pm/UserInfoTest.java
index af36dbb..edeea6d 100644
--- a/core/tests/coretests/src/android/content/pm/UserInfoTest.java
+++ b/core/tests/coretests/src/android/content/pm/UserInfoTest.java
@@ -20,8 +20,8 @@
 
 import android.os.UserHandle;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/content/res/FontResourcesParserTest.java b/core/tests/coretests/src/android/content/res/FontResourcesParserTest.java
index 9aef2ca..85f5d69 100644
--- a/core/tests/coretests/src/android/content/res/FontResourcesParserTest.java
+++ b/core/tests/coretests/src/android/content/res/FontResourcesParserTest.java
@@ -30,8 +30,8 @@
 import android.platform.test.annotations.Presubmit;
 
 import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.android.frameworks.coretests.R;
 
diff --git a/core/tests/coretests/src/android/content/res/ResourcesDrawableTest.java b/core/tests/coretests/src/android/content/res/ResourcesDrawableTest.java
index f7f9569..ac69a0f 100644
--- a/core/tests/coretests/src/android/content/res/ResourcesDrawableTest.java
+++ b/core/tests/coretests/src/android/content/res/ResourcesDrawableTest.java
@@ -27,8 +27,8 @@
 import android.platform.test.annotations.Presubmit;
 
 import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.android.frameworks.coretests.R;
 
diff --git a/core/tests/coretests/src/android/database/CursorWindowTest.java b/core/tests/coretests/src/android/database/CursorWindowTest.java
index 255020a..64e4d3b 100644
--- a/core/tests/coretests/src/android/database/CursorWindowTest.java
+++ b/core/tests/coretests/src/android/database/CursorWindowTest.java
@@ -23,8 +23,8 @@
 import android.database.sqlite.SQLiteException;
 import android.platform.test.ravenwood.RavenwoodRule;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Rule;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/database/DatabaseUtilsTest.java b/core/tests/coretests/src/android/database/DatabaseUtilsTest.java
index e25fdf9..c00c171 100644
--- a/core/tests/coretests/src/android/database/DatabaseUtilsTest.java
+++ b/core/tests/coretests/src/android/database/DatabaseUtilsTest.java
@@ -16,20 +16,19 @@
 
 package android.database;
 
-import static android.database.DatabaseUtils.bindSelection;
-import static android.database.DatabaseUtils.getSqlStatementType;
-import static android.database.DatabaseUtils.getSqlStatementTypeExtended;
-import static android.database.DatabaseUtils.STATEMENT_COMMENT;
 import static android.database.DatabaseUtils.STATEMENT_CREATE;
 import static android.database.DatabaseUtils.STATEMENT_DDL;
 import static android.database.DatabaseUtils.STATEMENT_OTHER;
 import static android.database.DatabaseUtils.STATEMENT_SELECT;
 import static android.database.DatabaseUtils.STATEMENT_UPDATE;
 import static android.database.DatabaseUtils.STATEMENT_WITH;
+import static android.database.DatabaseUtils.bindSelection;
+import static android.database.DatabaseUtils.getSqlStatementType;
+import static android.database.DatabaseUtils.getSqlStatementTypeExtended;
 
 import static org.junit.Assert.assertEquals;
 
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/database/RedactingCursorTest.java b/core/tests/coretests/src/android/database/RedactingCursorTest.java
index e2d2bae..470d4a9 100644
--- a/core/tests/coretests/src/android/database/RedactingCursorTest.java
+++ b/core/tests/coretests/src/android/database/RedactingCursorTest.java
@@ -24,7 +24,7 @@
 import android.net.Uri;
 
 import androidx.test.InstrumentationRegistry;
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/database/SQLiteOpenHelperTest.java b/core/tests/coretests/src/android/database/SQLiteOpenHelperTest.java
index 9bad6d2..efa9b7a 100644
--- a/core/tests/coretests/src/android/database/SQLiteOpenHelperTest.java
+++ b/core/tests/coretests/src/android/database/SQLiteOpenHelperTest.java
@@ -29,8 +29,8 @@
 import android.util.Log;
 
 import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.After;
 import org.junit.Before;
diff --git a/core/tests/coretests/src/android/database/sqlite/SQLiteCantOpenDatabaseExceptionTest.java b/core/tests/coretests/src/android/database/sqlite/SQLiteCantOpenDatabaseExceptionTest.java
index 09c380a..e20a806 100644
--- a/core/tests/coretests/src/android/database/sqlite/SQLiteCantOpenDatabaseExceptionTest.java
+++ b/core/tests/coretests/src/android/database/sqlite/SQLiteCantOpenDatabaseExceptionTest.java
@@ -20,14 +20,12 @@
 import static org.junit.Assert.fail;
 
 import android.content.Context;
-import android.database.sqlite.SQLiteCantOpenDatabaseException;
-import android.database.sqlite.SQLiteDatabase;
 import android.database.sqlite.SQLiteDatabase.OpenParams;
 import android.util.Log;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 import androidx.test.platform.app.InstrumentationRegistry;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/database/sqlite/SQLiteCompatibilityWalFlagsTest.java b/core/tests/coretests/src/android/database/sqlite/SQLiteCompatibilityWalFlagsTest.java
index 82bd588..a4dedc5 100644
--- a/core/tests/coretests/src/android/database/sqlite/SQLiteCompatibilityWalFlagsTest.java
+++ b/core/tests/coretests/src/android/database/sqlite/SQLiteCompatibilityWalFlagsTest.java
@@ -25,8 +25,8 @@
 import android.database.DatabaseUtils;
 
 import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.After;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/database/sqlite/SQLiteConnectionPoolTest.java b/core/tests/coretests/src/android/database/sqlite/SQLiteConnectionPoolTest.java
index 2b663bd..dbe7a9a 100644
--- a/core/tests/coretests/src/android/database/sqlite/SQLiteConnectionPoolTest.java
+++ b/core/tests/coretests/src/android/database/sqlite/SQLiteConnectionPoolTest.java
@@ -24,8 +24,8 @@
 import android.util.Log;
 
 import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.After;
 import org.junit.Before;
diff --git a/core/tests/coretests/src/android/database/sqlite/SQLiteDatabaseTest.java b/core/tests/coretests/src/android/database/sqlite/SQLiteDatabaseTest.java
index c4695d9..519f23b 100644
--- a/core/tests/coretests/src/android/database/sqlite/SQLiteDatabaseTest.java
+++ b/core/tests/coretests/src/android/database/sqlite/SQLiteDatabaseTest.java
@@ -25,16 +25,14 @@
 import android.content.Context;
 import android.database.Cursor;
 import android.database.DatabaseUtils;
-import android.os.SystemClock;
 import android.platform.test.annotations.RequiresFlagsEnabled;
 import android.platform.test.flag.junit.CheckFlagsRule;
 import android.platform.test.flag.junit.DeviceFlagsValueProvider;
-import android.test.AndroidTestCase;
-import android.util.Log;
+import android.util.Printer;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 import androidx.test.platform.app.InstrumentationRegistry;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.After;
 import org.junit.Before;
@@ -46,12 +44,13 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
-import java.util.concurrent.Executor;
+import java.util.Vector;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.concurrent.Phaser;
-import java.util.concurrent.Semaphore;
 import java.util.concurrent.TimeUnit;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 @RunWith(AndroidJUnit4.class)
 @SmallTest
@@ -405,4 +404,138 @@
         }
         assertFalse(allowed);
     }
+
+    /** Dumpsys information about a single database. */
+
+    /**
+     * Collect and parse dumpsys output.  This is not a full parser.  It is only enough to support
+     * the unit tests.
+     */
+    private static class Dumpsys {
+        // Regular expressions for parsing the output.  Reportedly, regular expressions are
+        // expensive, so these are created only if a dumpsys object is created.
+        private static final Object sLock = new Object();
+        static Pattern mPool;
+        static Pattern mConnection;
+        static Pattern mEntry;
+        static Pattern mSingleWord;
+        static Pattern mNone;
+
+        // The raw strings read from dumpsys.  Once loaded, this list never changes.
+        final ArrayList<String> mRaw = new ArrayList<>();
+
+        // Parsed dumpsys.  This contains only the bits that are being tested.
+        static class Connection {
+            ArrayList<String> mRecent = new ArrayList<>();
+            ArrayList<String> mLong = new ArrayList<>();
+        }
+        static class Database {
+            String mPath;
+            ArrayList<Connection> mConnection = new ArrayList<>();
+        }
+        ArrayList<Database> mDatabase;
+        ArrayList<String> mConcurrent;
+
+        Dumpsys() {
+            SQLiteDebug.dump(
+                new Printer() { public void println(String x) { mRaw.add(x); } },
+                new String[0]);
+            parse();
+        }
+
+        /** Parse the raw text. Return true if no errors were detected. */
+        boolean parse() {
+            initialize();
+
+            // Reset the parsed information.  This method may be called repeatedly.
+            mDatabase = new ArrayList<>();
+            mConcurrent = new ArrayList<>();
+
+            Database current = null;
+            Connection connection = null;
+            Matcher matcher;
+            for (int i = 0; i < mRaw.size(); i++) {
+                final String line = mRaw.get(i);
+                matcher = mPool.matcher(line);
+                if (matcher.lookingAt()) {
+                    current = new Database();
+                    mDatabase.add(current);
+                    current.mPath = matcher.group(1);
+                    continue;
+                }
+                matcher = mConnection.matcher(line);
+                if (matcher.lookingAt()) {
+                    connection = new Connection();
+                    current.mConnection.add(connection);
+                    continue;
+                }
+
+                if (line.contains("Most recently executed operations")) {
+                    i += readTable(connection.mRecent, i, mEntry);
+                    continue;
+                }
+
+                if (line.contains("Operations exceeding 2000ms")) {
+                    i += readTable(connection.mLong, i, mEntry);
+                    continue;
+                }
+                if (line.contains("Concurrently opened database files")) {
+                    i += readTable(mConcurrent, i, mSingleWord);
+                    continue;
+                }
+            }
+            return true;
+        }
+
+        /**
+         * Read a series of lines following a header.  Return the number of lines read.  The input
+         * line number is the number of the header.
+         */
+        private int readTable(List<String> s, int header, Pattern p) {
+            // Special case: if the first line is "<none>" then there are no more lines to the
+            // table.
+            if (lookingAt(header+1, mNone)) return 1;
+
+            int i;
+            for (i = header + 1; i < mRaw.size() && lookingAt(i, p); i++) {
+                s.add(mRaw.get(i).trim());
+            }
+            return i - header;
+        }
+
+        /** Return true if the n'th raw line matches the pattern. */
+        boolean lookingAt(int n, Pattern p) {
+            return p.matcher(mRaw.get(n)).lookingAt();
+        }
+
+        /** Compile the regular expressions the first time. */
+        private static void initialize() {
+            synchronized (sLock) {
+                if (mPool != null) return;
+                mPool = Pattern.compile("Connection pool for (\\S+):");
+                mConnection = Pattern.compile("\\s+Connection #(\\d+):");
+                mEntry = Pattern.compile("\\s+(\\d+): ");
+                mSingleWord = Pattern.compile("  (\\S+)$");
+                mNone = Pattern.compile("\\s+<none>$");
+            }
+        }
+    }
+
+    @Test
+    public void testDumpsys() throws Exception {
+        Dumpsys dumpsys = new Dumpsys();
+
+        assertEquals(1, dumpsys.mDatabase.size());
+        // Note: cannot test mConcurrent because that attribute is not hermitic with respect to
+        // the tests.
+
+        Dumpsys.Database db = dumpsys.mDatabase.get(0);
+
+        // Work with normalized paths.
+        String wantPath = mDatabaseFile.toPath().toRealPath().toString();
+        String realPath = new File(db.mPath).toPath().toRealPath().toString();
+        assertEquals(wantPath, realPath);
+
+        assertEquals(1, db.mConnection.size());
+    }
 }
diff --git a/core/tests/coretests/src/android/database/sqlite/SQLiteRawStatementTest.java b/core/tests/coretests/src/android/database/sqlite/SQLiteRawStatementTest.java
index 24d27c4..832ebe5 100644
--- a/core/tests/coretests/src/android/database/sqlite/SQLiteRawStatementTest.java
+++ b/core/tests/coretests/src/android/database/sqlite/SQLiteRawStatementTest.java
@@ -29,9 +29,9 @@
 import android.os.SystemClock;
 import android.util.Log;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 import androidx.test.platform.app.InstrumentationRegistry;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.After;
 import org.junit.Before;
diff --git a/core/tests/coretests/src/android/database/sqlite/SQLiteTokenizerTest.java b/core/tests/coretests/src/android/database/sqlite/SQLiteTokenizerTest.java
index a9d1482..ced1846 100644
--- a/core/tests/coretests/src/android/database/sqlite/SQLiteTokenizerTest.java
+++ b/core/tests/coretests/src/android/database/sqlite/SQLiteTokenizerTest.java
@@ -20,7 +20,7 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/debug/AdbNotificationsTest.java b/core/tests/coretests/src/android/debug/AdbNotificationsTest.java
index 3496e2c..10eeb35 100644
--- a/core/tests/coretests/src/android/debug/AdbNotificationsTest.java
+++ b/core/tests/coretests/src/android/debug/AdbNotificationsTest.java
@@ -25,8 +25,8 @@
 import android.text.TextUtils;
 
 import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/graphics/FontListParserTest.java b/core/tests/coretests/src/android/graphics/FontListParserTest.java
index 5f96c17..52f53dd 100644
--- a/core/tests/coretests/src/android/graphics/FontListParserTest.java
+++ b/core/tests/coretests/src/android/graphics/FontListParserTest.java
@@ -16,16 +16,16 @@
 
 package android.graphics;
 
+import static android.graphics.fonts.FontFamily.Builder.VARIABLE_FONT_FAMILY_TYPE_NONE;
+import static android.graphics.fonts.FontFamily.Builder.VARIABLE_FONT_FAMILY_TYPE_SINGLE_FONT_WGHT_ITAL;
+import static android.graphics.fonts.FontFamily.Builder.VARIABLE_FONT_FAMILY_TYPE_SINGLE_FONT_WGHT_ONLY;
+import static android.graphics.fonts.FontFamily.Builder.VARIABLE_FONT_FAMILY_TYPE_TWO_FONTS_WGHT;
 import static android.graphics.fonts.FontStyle.FONT_SLANT_ITALIC;
 import static android.graphics.fonts.FontStyle.FONT_SLANT_UPRIGHT;
 import static android.graphics.fonts.FontStyle.FONT_WEIGHT_NORMAL;
 import static android.text.FontConfig.FontFamily.VARIANT_COMPACT;
 import static android.text.FontConfig.FontFamily.VARIANT_DEFAULT;
 import static android.text.FontConfig.FontFamily.VARIANT_ELEGANT;
-import static android.graphics.fonts.FontFamily.Builder.VARIABLE_FONT_FAMILY_TYPE_NONE;
-import static android.graphics.fonts.FontFamily.Builder.VARIABLE_FONT_FAMILY_TYPE_SINGLE_FONT_WGHT_ONLY;
-import static android.graphics.fonts.FontFamily.Builder.VARIABLE_FONT_FAMILY_TYPE_SINGLE_FONT_WGHT_ITAL;
-import static android.graphics.fonts.FontFamily.Builder.VARIABLE_FONT_FAMILY_TYPE_TWO_FONTS_WGHT;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -38,8 +38,8 @@
 import android.text.FontConfig;
 import android.util.Xml;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/graphics/RectTest.java b/core/tests/coretests/src/android/graphics/RectTest.java
index 2918f44..d0cb5d5 100644
--- a/core/tests/coretests/src/android/graphics/RectTest.java
+++ b/core/tests/coretests/src/android/graphics/RectTest.java
@@ -24,8 +24,8 @@
 
 import android.platform.test.annotations.Presubmit;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/graphics/TypefaceEqualsTest.java b/core/tests/coretests/src/android/graphics/TypefaceEqualsTest.java
index 6ae7eb7..a94f412 100644
--- a/core/tests/coretests/src/android/graphics/TypefaceEqualsTest.java
+++ b/core/tests/coretests/src/android/graphics/TypefaceEqualsTest.java
@@ -23,8 +23,8 @@
 import android.graphics.fonts.Font;
 
 import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java b/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java
index 0d687b2..10aed8d 100644
--- a/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java
+++ b/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java
@@ -39,8 +39,8 @@
 import android.util.ArrayMap;
 
 import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.After;
 import org.junit.Before;
diff --git a/core/tests/coretests/src/android/graphics/TypefaceTest.java b/core/tests/coretests/src/android/graphics/TypefaceTest.java
index 6bf8f56..80efa51 100644
--- a/core/tests/coretests/src/android/graphics/TypefaceTest.java
+++ b/core/tests/coretests/src/android/graphics/TypefaceTest.java
@@ -30,10 +30,10 @@
 import android.util.ArrayMap;
 
 import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.LargeTest;
 import androidx.test.filters.MediumTest;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.android.frameworks.coretests.R;
 
diff --git a/core/tests/coretests/src/android/graphics/drawable/DrawableWrapperTest.java b/core/tests/coretests/src/android/graphics/drawable/DrawableWrapperTest.java
index d0a6ff9..4991cd0 100644
--- a/core/tests/coretests/src/android/graphics/drawable/DrawableWrapperTest.java
+++ b/core/tests/coretests/src/android/graphics/drawable/DrawableWrapperTest.java
@@ -25,8 +25,8 @@
 import android.graphics.PorterDuffXfermode;
 import android.graphics.Xfermode;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/hardware/biometrics/BiometricPromptTest.java b/core/tests/coretests/src/android/hardware/biometrics/BiometricPromptTest.java
index 5464ea3..d6c0e99 100644
--- a/core/tests/coretests/src/android/hardware/biometrics/BiometricPromptTest.java
+++ b/core/tests/coretests/src/android/hardware/biometrics/BiometricPromptTest.java
@@ -16,7 +16,6 @@
 
 package android.hardware.biometrics;
 
-import static android.hardware.biometrics.BiometricPrompt.MAX_LOGO_DESCRIPTION_CHARACTER_NUMBER;
 import static android.hardware.biometrics.PromptContentViewWithMoreOptionsButton.MAX_DESCRIPTION_CHARACTER_NUMBER;
 import static android.hardware.biometrics.PromptVerticalListContentView.MAX_EACH_ITEM_CHARACTER_NUMBER;
 import static android.hardware.biometrics.PromptVerticalListContentView.MAX_ITEM_NUMBER;
@@ -117,19 +116,7 @@
                 () -> new BiometricPrompt.Builder(mContext).setLogoDescription(null)
         );
 
-        assertThat(e).hasMessageThat().contains(
-                "Logo description passed in can not be null or exceed");
-    }
-
-    @Test
-    public void testLogoDescription_charLimit() {
-        IllegalArgumentException e = assertThrows(IllegalArgumentException.class,
-                () -> new BiometricPrompt.Builder(mContext).setLogoDescription(
-                        generateRandomString(MAX_LOGO_DESCRIPTION_CHARACTER_NUMBER + 1))
-        );
-
-        assertThat(e).hasMessageThat().contains(
-                "Logo description passed in can not be null or exceed");
+        assertThat(e).hasMessageThat().isEqualTo("Logo description passed in can not be null");
     }
 
     @Test
diff --git a/core/tests/coretests/src/android/hardware/display/AmbientBrightnessDayStatsTest.java b/core/tests/coretests/src/android/hardware/display/AmbientBrightnessDayStatsTest.java
index 0a25bd7..244024d 100644
--- a/core/tests/coretests/src/android/hardware/display/AmbientBrightnessDayStatsTest.java
+++ b/core/tests/coretests/src/android/hardware/display/AmbientBrightnessDayStatsTest.java
@@ -24,8 +24,8 @@
 
 import android.os.Parcel;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/hardware/display/BrightnessConfigurationTest.java b/core/tests/coretests/src/android/hardware/display/BrightnessConfigurationTest.java
index 1c7ab74..9f12e51 100644
--- a/core/tests/coretests/src/android/hardware/display/BrightnessConfigurationTest.java
+++ b/core/tests/coretests/src/android/hardware/display/BrightnessConfigurationTest.java
@@ -25,8 +25,8 @@
 import android.util.Pair;
 import android.util.Xml;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.android.modules.utils.TypedXmlPullParser;
 import com.android.modules.utils.TypedXmlSerializer;
diff --git a/core/tests/coretests/src/android/hardware/display/DisplayManagerGlobalTest.java b/core/tests/coretests/src/android/hardware/display/DisplayManagerGlobalTest.java
index 969ae8e..5a0dacb 100644
--- a/core/tests/coretests/src/android/hardware/display/DisplayManagerGlobalTest.java
+++ b/core/tests/coretests/src/android/hardware/display/DisplayManagerGlobalTest.java
@@ -31,8 +31,8 @@
 import android.view.DisplayInfo;
 
 import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/hardware/input/InputFlagsTest.java b/core/tests/coretests/src/android/hardware/input/InputFlagsTest.java
index 5aeab42..b4f1dee 100644
--- a/core/tests/coretests/src/android/hardware/input/InputFlagsTest.java
+++ b/core/tests/coretests/src/android/hardware/input/InputFlagsTest.java
@@ -21,8 +21,8 @@
 
 import android.platform.test.annotations.Presubmit;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/net/NetworkKeyTest.java b/core/tests/coretests/src/android/net/NetworkKeyTest.java
index b13bcd1..444ed51 100644
--- a/core/tests/coretests/src/android/net/NetworkKeyTest.java
+++ b/core/tests/coretests/src/android/net/NetworkKeyTest.java
@@ -25,7 +25,7 @@
 import android.net.wifi.WifiInfo;
 import android.net.wifi.WifiManager;
 
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/net/NetworkRecommendationProviderTest.java b/core/tests/coretests/src/android/net/NetworkRecommendationProviderTest.java
index 3e45a79..46f22ce 100644
--- a/core/tests/coretests/src/android/net/NetworkRecommendationProviderTest.java
+++ b/core/tests/coretests/src/android/net/NetworkRecommendationProviderTest.java
@@ -26,7 +26,7 @@
 import android.Manifest.permission;
 import android.content.Context;
 
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/net/SSLCertificateSocketFactoryTest.java b/core/tests/coretests/src/android/net/SSLCertificateSocketFactoryTest.java
index bc12e72..7413ede 100644
--- a/core/tests/coretests/src/android/net/SSLCertificateSocketFactoryTest.java
+++ b/core/tests/coretests/src/android/net/SSLCertificateSocketFactoryTest.java
@@ -19,7 +19,7 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/net/ScoredNetworkTest.java b/core/tests/coretests/src/android/net/ScoredNetworkTest.java
index d984d86..63eeaa1 100644
--- a/core/tests/coretests/src/android/net/ScoredNetworkTest.java
+++ b/core/tests/coretests/src/android/net/ScoredNetworkTest.java
@@ -26,7 +26,7 @@
 import android.os.Bundle;
 import android.os.Parcel;
 
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/net/SntpClientTest.java b/core/tests/coretests/src/android/net/SntpClientTest.java
index 267fc2b..024d614 100644
--- a/core/tests/coretests/src/android/net/SntpClientTest.java
+++ b/core/tests/coretests/src/android/net/SntpClientTest.java
@@ -29,7 +29,7 @@
 import android.platform.test.annotations.Presubmit;
 import android.util.Log;
 
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import libcore.util.HexEncoding;
 
diff --git a/core/tests/coretests/src/android/net/sntp/Duration64Test.java b/core/tests/coretests/src/android/net/sntp/Duration64Test.java
index b228596..b177e18 100644
--- a/core/tests/coretests/src/android/net/sntp/Duration64Test.java
+++ b/core/tests/coretests/src/android/net/sntp/Duration64Test.java
@@ -23,7 +23,7 @@
 
 import android.platform.test.annotations.Presubmit;
 
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/net/sntp/Timestamp64Test.java b/core/tests/coretests/src/android/net/sntp/Timestamp64Test.java
index 200c80e..9f95132 100644
--- a/core/tests/coretests/src/android/net/sntp/Timestamp64Test.java
+++ b/core/tests/coretests/src/android/net/sntp/Timestamp64Test.java
@@ -23,7 +23,7 @@
 
 import android.platform.test.annotations.Presubmit;
 
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/os/BinderDeathRecipientTest.java b/core/tests/coretests/src/android/os/BinderDeathRecipientTest.java
index eff52f0..5ef1460 100644
--- a/core/tests/coretests/src/android/os/BinderDeathRecipientTest.java
+++ b/core/tests/coretests/src/android/os/BinderDeathRecipientTest.java
@@ -32,7 +32,7 @@
 import android.util.Pair;
 
 import androidx.test.InstrumentationRegistry;
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import com.android.frameworks.coretests.bdr_helper_app.TestCommsReceiver;
 
diff --git a/core/tests/coretests/src/android/os/BinderProxyCountingTest.java b/core/tests/coretests/src/android/os/BinderProxyCountingTest.java
index 84d2995..4dfe2e2 100644
--- a/core/tests/coretests/src/android/os/BinderProxyCountingTest.java
+++ b/core/tests/coretests/src/android/os/BinderProxyCountingTest.java
@@ -29,8 +29,8 @@
 import android.util.Log;
 
 import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.LargeTest;
-import androidx.test.runner.AndroidJUnit4;
 import androidx.test.uiautomator.UiDevice;
 
 import com.android.frameworks.coretests.aidl.IBpcCallbackObserver;
diff --git a/core/tests/coretests/src/android/os/BinderWorkSourceTest.java b/core/tests/coretests/src/android/os/BinderWorkSourceTest.java
index 552066c..98e96c2 100644
--- a/core/tests/coretests/src/android/os/BinderWorkSourceTest.java
+++ b/core/tests/coretests/src/android/os/BinderWorkSourceTest.java
@@ -29,13 +29,12 @@
 import android.platform.test.ravenwood.RavenwoodRule;
 
 import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.LargeTest;
 import androidx.test.filters.Suppress;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.After;
 import org.junit.Before;
-import org.junit.BeforeClass;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/os/BluetoothBatteryStatsTest.java b/core/tests/coretests/src/android/os/BluetoothBatteryStatsTest.java
index e12ca24a..3af35f7 100644
--- a/core/tests/coretests/src/android/os/BluetoothBatteryStatsTest.java
+++ b/core/tests/coretests/src/android/os/BluetoothBatteryStatsTest.java
@@ -18,7 +18,7 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/os/BroadcasterTest.java b/core/tests/coretests/src/android/os/BroadcasterTest.java
index 7829457..31a7063 100644
--- a/core/tests/coretests/src/android/os/BroadcasterTest.java
+++ b/core/tests/coretests/src/android/os/BroadcasterTest.java
@@ -18,8 +18,8 @@
 
 import android.platform.test.ravenwood.RavenwoodRule;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.MediumTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Rule;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/os/BuildTest.java b/core/tests/coretests/src/android/os/BuildTest.java
index 2a718ff..e8c701e 100644
--- a/core/tests/coretests/src/android/os/BuildTest.java
+++ b/core/tests/coretests/src/android/os/BuildTest.java
@@ -23,8 +23,8 @@
 import android.platform.test.flag.junit.SetFlagsRule;
 import android.platform.test.ravenwood.RavenwoodRule;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import junit.framework.Assert;
 
diff --git a/core/tests/coretests/src/android/os/BundleTest.java b/core/tests/coretests/src/android/os/BundleTest.java
index 40e79ad..ded6fc5 100644
--- a/core/tests/coretests/src/android/os/BundleTest.java
+++ b/core/tests/coretests/src/android/os/BundleTest.java
@@ -30,8 +30,8 @@
 import android.platform.test.ravenwood.RavenwoodRule;
 import android.util.Log;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.After;
 import org.junit.Rule;
diff --git a/core/tests/coretests/src/android/os/CancellationSignalBeamerTest.java b/core/tests/coretests/src/android/os/CancellationSignalBeamerTest.java
index c2cea0a..2117e74 100644
--- a/core/tests/coretests/src/android/os/CancellationSignalBeamerTest.java
+++ b/core/tests/coretests/src/android/os/CancellationSignalBeamerTest.java
@@ -27,9 +27,9 @@
 import android.util.PollingCheck;
 import android.util.PollingCheck.PollingCheckCondition;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 import androidx.test.platform.app.InstrumentationRegistry;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Rule;
diff --git a/core/tests/coretests/src/android/os/CancellationSignalTest.java b/core/tests/coretests/src/android/os/CancellationSignalTest.java
index 5cd2873..8e11df5 100644
--- a/core/tests/coretests/src/android/os/CancellationSignalTest.java
+++ b/core/tests/coretests/src/android/os/CancellationSignalTest.java
@@ -21,7 +21,7 @@
 
 import android.platform.test.ravenwood.RavenwoodRule;
 
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import org.junit.Rule;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/os/EnvironmentTest.java b/core/tests/coretests/src/android/os/EnvironmentTest.java
index 1aa263f..1b49624 100644
--- a/core/tests/coretests/src/android/os/EnvironmentTest.java
+++ b/core/tests/coretests/src/android/os/EnvironmentTest.java
@@ -32,7 +32,7 @@
 import android.platform.test.ravenwood.RavenwoodRule;
 
 import androidx.test.InstrumentationRegistry;
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import org.junit.After;
 import org.junit.Before;
diff --git a/core/tests/coretests/src/android/os/FileUtilsTest.java b/core/tests/coretests/src/android/os/FileUtilsTest.java
index 78a2c1c..774878a 100644
--- a/core/tests/coretests/src/android/os/FileUtilsTest.java
+++ b/core/tests/coretests/src/android/os/FileUtilsTest.java
@@ -54,13 +54,13 @@
 import static org.junit.Assert.fail;
 
 import android.os.FileUtils.MemoryPipe;
-import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.annotations.DisabledOnRavenwood;
 import android.platform.test.ravenwood.RavenwoodRule;
 import android.provider.DocumentsContract.Document;
 import android.system.Os;
 import android.util.DataUnit;
 
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import org.junit.After;
 import org.junit.Before;
@@ -70,8 +70,6 @@
 
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
-import java.io.DataInputStream;
-import java.io.DataOutputStream;
 import java.io.File;
 import java.io.FileDescriptor;
 import java.io.FileInputStream;
@@ -79,11 +77,11 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.net.InetSocketAddress;
 import java.nio.file.Files;
 import java.util.Arrays;
 import java.util.HashSet;
 import java.util.Random;
-import java.net.InetSocketAddress;
 
 @RunWith(AndroidJUnit4.class)
 public class FileUtilsTest {
@@ -158,7 +156,7 @@
     }
 
     @Test
-    @IgnoreUnderRavenwood(blockedBy = MemoryPipe.class)
+    @DisabledOnRavenwood(blockedBy = MemoryPipe.class)
     public void testCopy_FileToPipe() throws Exception {
         for (int size : DATA_SIZES) {
             final File src = new File(mTarget, "src");
@@ -179,7 +177,7 @@
     }
 
     @Test
-    @IgnoreUnderRavenwood(blockedBy = MemoryPipe.class)
+    @DisabledOnRavenwood(blockedBy = MemoryPipe.class)
     public void testCopy_PipeToFile() throws Exception {
         for (int size : DATA_SIZES) {
             final File dest = new File(mTarget, "dest");
@@ -199,7 +197,7 @@
     }
 
     @Test
-    @IgnoreUnderRavenwood(blockedBy = MemoryPipe.class)
+    @DisabledOnRavenwood(blockedBy = MemoryPipe.class)
     public void testCopy_PipeToPipe() throws Exception {
         for (int size : DATA_SIZES) {
             byte[] expected = new byte[size];
@@ -217,7 +215,7 @@
     }
 
     @Test
-    @IgnoreUnderRavenwood(blockedBy = MemoryPipe.class)
+    @DisabledOnRavenwood(blockedBy = MemoryPipe.class)
     public void testCopy_ShortPipeToFile() throws Exception {
         byte[] source = new byte[33_000_000];
         new Random().nextBytes(source);
@@ -259,9 +257,9 @@
         assertArrayEquals(expected, actual);
     }
 
-    //TODO(ravenwood) Remove the _$noRavenwood suffix and add @RavenwoodIgnore instead
     @Test
-    public void testCopy_SocketToFile_FileToSocket$noRavenwood() throws Exception {
+    @DisabledOnRavenwood(reason = "Missing Os methods in Ravenwood")
+    public void testCopy_SocketToFile_FileToSocket() throws Exception {
         for (int size : DATA_SIZES ) {
             final File src = new File(mTarget, "src");
             final File dest = new File(mTarget, "dest");
@@ -512,7 +510,7 @@
     }
 
     @Test
-    @IgnoreUnderRavenwood(blockedBy = android.webkit.MimeTypeMap.class)
+    @DisabledOnRavenwood(blockedBy = android.webkit.MimeTypeMap.class)
     public void testBuildUniqueFile_normal() throws Exception {
         assertNameEquals("test.jpg", FileUtils.buildUniqueFile(mTarget, "image/jpeg", "test"));
         assertNameEquals("test.jpg", FileUtils.buildUniqueFile(mTarget, "image/jpeg", "test.jpg"));
@@ -532,7 +530,7 @@
     }
 
     @Test
-    @IgnoreUnderRavenwood(blockedBy = android.webkit.MimeTypeMap.class)
+    @DisabledOnRavenwood(blockedBy = android.webkit.MimeTypeMap.class)
     public void testBuildUniqueFile_unknown() throws Exception {
         assertNameEquals("test",
                 FileUtils.buildUniqueFile(mTarget, "application/octet-stream", "test"));
@@ -546,7 +544,7 @@
     }
 
     @Test
-    @IgnoreUnderRavenwood(blockedBy = android.webkit.MimeTypeMap.class)
+    @DisabledOnRavenwood(blockedBy = android.webkit.MimeTypeMap.class)
     public void testBuildUniqueFile_dir() throws Exception {
         assertNameEquals("test", FileUtils.buildUniqueFile(mTarget, Document.MIME_TYPE_DIR, "test"));
         new File(mTarget, "test").mkdir();
@@ -561,7 +559,7 @@
     }
 
     @Test
-    @IgnoreUnderRavenwood(blockedBy = android.webkit.MimeTypeMap.class)
+    @DisabledOnRavenwood(blockedBy = android.webkit.MimeTypeMap.class)
     public void testBuildUniqueFile_increment() throws Exception {
         assertNameEquals("test.jpg", FileUtils.buildUniqueFile(mTarget, "image/jpeg", "test.jpg"));
         new File(mTarget, "test.jpg").createNewFile();
@@ -581,7 +579,7 @@
     }
 
     @Test
-    @IgnoreUnderRavenwood(blockedBy = android.webkit.MimeTypeMap.class)
+    @DisabledOnRavenwood(blockedBy = android.webkit.MimeTypeMap.class)
     public void testBuildUniqueFile_mimeless() throws Exception {
         assertNameEquals("test.jpg", FileUtils.buildUniqueFile(mTarget, "test.jpg"));
         new File(mTarget, "test.jpg").createNewFile();
@@ -677,8 +675,7 @@
     }
 
     @Test
-    @IgnoreUnderRavenwood(reason = "Requires kernel support")
-    public void testTranslateMode() throws Exception {
+    public void testTranslateMode() {
         assertTranslate("r", O_RDONLY, MODE_READ_ONLY);
 
         assertTranslate("rw", O_RDWR | O_CREAT,
@@ -697,8 +694,7 @@
     }
 
     @Test
-    @IgnoreUnderRavenwood(reason = "Requires kernel support")
-    public void testMalformedTransate_int() throws Exception {
+    public void testMalformedTransate_int() {
         try {
             // The non-standard Linux access mode 3 should throw
             // an IllegalArgumentException.
@@ -709,8 +705,7 @@
     }
 
     @Test
-    @IgnoreUnderRavenwood(reason = "Requires kernel support")
-    public void testMalformedTransate_string() throws Exception {
+    public void testMalformedTransate_string() {
         try {
             // The non-standard Linux access mode 3 should throw
             // an IllegalArgumentException.
@@ -721,8 +716,7 @@
     }
 
     @Test
-    @IgnoreUnderRavenwood(reason = "Requires kernel support")
-    public void testTranslateMode_Invalid() throws Exception {
+    public void testTranslateMode_Invalid() {
         try {
             translateModeStringToPosix("rwx");
             fail();
@@ -736,8 +730,7 @@
     }
 
     @Test
-    @IgnoreUnderRavenwood(reason = "Requires kernel support")
-    public void testTranslateMode_Access() throws Exception {
+    public void testTranslateMode_Access() {
         assertEquals(O_RDONLY, translateModeAccessToPosix(F_OK));
         assertEquals(O_RDONLY, translateModeAccessToPosix(R_OK));
         assertEquals(O_WRONLY, translateModeAccessToPosix(W_OK));
@@ -746,7 +739,7 @@
     }
 
     @Test
-    @IgnoreUnderRavenwood(reason = "Requires kernel support")
+    @DisabledOnRavenwood(reason = "Requires kernel support")
     public void testConvertToModernFd() throws Exception {
         final String nonce = String.valueOf(System.nanoTime());
 
diff --git a/core/tests/coretests/src/android/os/HandlerThreadTest.java b/core/tests/coretests/src/android/os/HandlerThreadTest.java
index 3237812..a4eda06 100644
--- a/core/tests/coretests/src/android/os/HandlerThreadTest.java
+++ b/core/tests/coretests/src/android/os/HandlerThreadTest.java
@@ -25,8 +25,8 @@
 
 import android.platform.test.ravenwood.RavenwoodRule;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.MediumTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Assume;
 import org.junit.Rule;
diff --git a/core/tests/coretests/src/android/os/IdleHandlerTest.java b/core/tests/coretests/src/android/os/IdleHandlerTest.java
index 8644663..7accb3b 100644
--- a/core/tests/coretests/src/android/os/IdleHandlerTest.java
+++ b/core/tests/coretests/src/android/os/IdleHandlerTest.java
@@ -19,8 +19,8 @@
 import android.os.MessageQueue.IdleHandler;
 import android.platform.test.ravenwood.RavenwoodRule;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.MediumTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Rule;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/os/LocaleListTest.java b/core/tests/coretests/src/android/os/LocaleListTest.java
index 0025e3a..0b8aff0 100644
--- a/core/tests/coretests/src/android/os/LocaleListTest.java
+++ b/core/tests/coretests/src/android/os/LocaleListTest.java
@@ -20,11 +20,10 @@
 import static org.junit.Assert.assertSame;
 import static org.junit.Assert.fail;
 
-import android.platform.test.annotations.IgnoreUnderRavenwood;
 import android.platform.test.ravenwood.RavenwoodRule;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Rule;
 import org.junit.Test;
@@ -33,7 +32,6 @@
 import java.util.Locale;
 
 @RunWith(AndroidJUnit4.class)
-@IgnoreUnderRavenwood(blockedBy = LocaleList.class)
 public class LocaleListTest {
     @Rule
     public final RavenwoodRule mRavenwood = new RavenwoodRule();
diff --git a/core/tests/coretests/src/android/os/MessageQueueTest.java b/core/tests/coretests/src/android/os/MessageQueueTest.java
index 8cd6773..549e666 100644
--- a/core/tests/coretests/src/android/os/MessageQueueTest.java
+++ b/core/tests/coretests/src/android/os/MessageQueueTest.java
@@ -18,9 +18,9 @@
 
 import android.platform.test.ravenwood.RavenwoodRule;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.MediumTest;
 import androidx.test.filters.Suppress;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Rule;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/os/PackageTagsListTest.java b/core/tests/coretests/src/android/os/PackageTagsListTest.java
index 9034a5c..6c9d7eb 100644
--- a/core/tests/coretests/src/android/os/PackageTagsListTest.java
+++ b/core/tests/coretests/src/android/os/PackageTagsListTest.java
@@ -24,7 +24,7 @@
 import android.util.ArrayMap;
 import android.util.ArraySet;
 
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/os/ParcelNullabilityTest.java b/core/tests/coretests/src/android/os/ParcelNullabilityTest.java
index ffeab29..09395f1 100644
--- a/core/tests/coretests/src/android/os/ParcelNullabilityTest.java
+++ b/core/tests/coretests/src/android/os/ParcelNullabilityTest.java
@@ -24,8 +24,8 @@
 import android.platform.test.ravenwood.RavenwoodRule;
 import android.util.ArrayMap;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Rule;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/os/ParcelTest.java b/core/tests/coretests/src/android/os/ParcelTest.java
index 0697c96..0373231 100644
--- a/core/tests/coretests/src/android/os/ParcelTest.java
+++ b/core/tests/coretests/src/android/os/ParcelTest.java
@@ -28,7 +28,7 @@
 import android.platform.test.ravenwood.RavenwoodRule;
 import android.util.Log;
 
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import org.junit.Rule;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/os/PerformanceCollectorTest.java b/core/tests/coretests/src/android/os/PerformanceCollectorTest.java
index 46f1706..436720e 100644
--- a/core/tests/coretests/src/android/os/PerformanceCollectorTest.java
+++ b/core/tests/coretests/src/android/os/PerformanceCollectorTest.java
@@ -25,9 +25,9 @@
 import android.platform.test.annotations.IgnoreUnderRavenwood;
 import android.platform.test.ravenwood.RavenwoodRule;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.MediumTest;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.After;
 import org.junit.Before;
diff --git a/core/tests/coretests/src/android/os/PerformanceHintManagerTest.java b/core/tests/coretests/src/android/os/PerformanceHintManagerTest.java
index 93a5642..4b49fde 100644
--- a/core/tests/coretests/src/android/os/PerformanceHintManagerTest.java
+++ b/core/tests/coretests/src/android/os/PerformanceHintManagerTest.java
@@ -28,7 +28,7 @@
 import android.platform.test.ravenwood.RavenwoodRule;
 
 import androidx.test.InstrumentationRegistry;
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Rule;
diff --git a/core/tests/coretests/src/android/os/PowerManagerTest.java b/core/tests/coretests/src/android/os/PowerManagerTest.java
index b9a12ad..3b27fc0 100644
--- a/core/tests/coretests/src/android/os/PowerManagerTest.java
+++ b/core/tests/coretests/src/android/os/PowerManagerTest.java
@@ -36,8 +36,8 @@
 import android.platform.test.ravenwood.RavenwoodRule;
 
 import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 import androidx.test.uiautomator.UiDevice;
 
 import org.junit.After;
diff --git a/core/tests/coretests/src/android/os/RedactingFileDescriptorTest.java b/core/tests/coretests/src/android/os/RedactingFileDescriptorTest.java
index 25ce240..e22c862 100644
--- a/core/tests/coretests/src/android/os/RedactingFileDescriptorTest.java
+++ b/core/tests/coretests/src/android/os/RedactingFileDescriptorTest.java
@@ -29,7 +29,7 @@
 import android.system.Os;
 
 import androidx.test.InstrumentationRegistry;
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import org.junit.After;
 import org.junit.Before;
diff --git a/core/tests/coretests/src/android/os/RemoteCallbackListTest.java b/core/tests/coretests/src/android/os/RemoteCallbackListTest.java
index cc342cf..7d694a0 100644
--- a/core/tests/coretests/src/android/os/RemoteCallbackListTest.java
+++ b/core/tests/coretests/src/android/os/RemoteCallbackListTest.java
@@ -19,7 +19,7 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/os/RemoteCallbackTest.java b/core/tests/coretests/src/android/os/RemoteCallbackTest.java
index 192c3d01..ddcc380 100644
--- a/core/tests/coretests/src/android/os/RemoteCallbackTest.java
+++ b/core/tests/coretests/src/android/os/RemoteCallbackTest.java
@@ -21,7 +21,7 @@
 
 import android.platform.test.ravenwood.RavenwoodRule;
 
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import org.junit.Rule;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/os/ResultReceiverTest.java b/core/tests/coretests/src/android/os/ResultReceiverTest.java
index 8ee873b..be67825 100644
--- a/core/tests/coretests/src/android/os/ResultReceiverTest.java
+++ b/core/tests/coretests/src/android/os/ResultReceiverTest.java
@@ -21,7 +21,7 @@
 
 import android.platform.test.ravenwood.RavenwoodRule;
 
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import org.junit.Rule;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/os/StrictModeTest.java b/core/tests/coretests/src/android/os/StrictModeTest.java
index 9050583..7f8af83 100644
--- a/core/tests/coretests/src/android/os/StrictModeTest.java
+++ b/core/tests/coretests/src/android/os/StrictModeTest.java
@@ -18,7 +18,7 @@
 
 import static org.junit.Assert.assertEquals;
 
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/os/TestLooperManagerTest.java b/core/tests/coretests/src/android/os/TestLooperManagerTest.java
index 5959444..4d64a3a 100644
--- a/core/tests/coretests/src/android/os/TestLooperManagerTest.java
+++ b/core/tests/coretests/src/android/os/TestLooperManagerTest.java
@@ -24,8 +24,8 @@
 
 import android.platform.test.ravenwood.RavenwoodRule;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.platform.app.InstrumentationRegistry;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Rule;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/os/TimestampedValueTest.java b/core/tests/coretests/src/android/os/TimestampedValueTest.java
index f36d9e6..5bb7eb5 100644
--- a/core/tests/coretests/src/android/os/TimestampedValueTest.java
+++ b/core/tests/coretests/src/android/os/TimestampedValueTest.java
@@ -20,7 +20,7 @@
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.fail;
 
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/os/TraceTest.java b/core/tests/coretests/src/android/os/TraceTest.java
index b2c005f..5462f32 100644
--- a/core/tests/coretests/src/android/os/TraceTest.java
+++ b/core/tests/coretests/src/android/os/TraceTest.java
@@ -20,10 +20,10 @@
 import android.platform.test.ravenwood.RavenwoodRule;
 import android.util.Log;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.LargeTest;
 import androidx.test.filters.SmallTest;
 import androidx.test.filters.Suppress;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Rule;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/os/UserHandleTest.java b/core/tests/coretests/src/android/os/UserHandleTest.java
index 160b20d..168f2a6 100644
--- a/core/tests/coretests/src/android/os/UserHandleTest.java
+++ b/core/tests/coretests/src/android/os/UserHandleTest.java
@@ -26,7 +26,7 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertSame;
 
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/os/VibrationAttributesTest.java b/core/tests/coretests/src/android/os/VibrationAttributesTest.java
index f5a81c5..d8142c8 100644
--- a/core/tests/coretests/src/android/os/VibrationAttributesTest.java
+++ b/core/tests/coretests/src/android/os/VibrationAttributesTest.java
@@ -18,7 +18,7 @@
 
 import static org.junit.Assert.assertEquals;
 
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/os/WakeLockStatsTest.java b/core/tests/coretests/src/android/os/WakeLockStatsTest.java
index f3b18c8..2e842b2 100644
--- a/core/tests/coretests/src/android/os/WakeLockStatsTest.java
+++ b/core/tests/coretests/src/android/os/WakeLockStatsTest.java
@@ -18,7 +18,7 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/os/WorkDurationUnitTest.java b/core/tests/coretests/src/android/os/WorkDurationUnitTest.java
index fcdc590..58a434a 100644
--- a/core/tests/coretests/src/android/os/WorkDurationUnitTest.java
+++ b/core/tests/coretests/src/android/os/WorkDurationUnitTest.java
@@ -25,7 +25,7 @@
 import android.platform.test.flag.junit.RavenwoodFlagsValueProvider;
 import android.platform.test.ravenwood.RavenwoodRule;
 
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Rule;
diff --git a/core/tests/coretests/src/android/os/WorkSourceTest.java b/core/tests/coretests/src/android/os/WorkSourceTest.java
index 85dc127..d3f4f04 100644
--- a/core/tests/coretests/src/android/os/WorkSourceTest.java
+++ b/core/tests/coretests/src/android/os/WorkSourceTest.java
@@ -25,7 +25,7 @@
 
 import android.os.WorkSource.WorkChain;
 
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/preference/PreferenceIconSpaceTest.java b/core/tests/coretests/src/android/preference/PreferenceIconSpaceTest.java
index 0deb77e..55a347e 100644
--- a/core/tests/coretests/src/android/preference/PreferenceIconSpaceTest.java
+++ b/core/tests/coretests/src/android/preference/PreferenceIconSpaceTest.java
@@ -27,8 +27,8 @@
 import android.widget.ImageView;
 
 import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/print/IPrintManagerParametersTest.java b/core/tests/coretests/src/android/print/IPrintManagerParametersTest.java
index c25aa51..746c8ca 100644
--- a/core/tests/coretests/src/android/print/IPrintManagerParametersTest.java
+++ b/core/tests/coretests/src/android/print/IPrintManagerParametersTest.java
@@ -42,9 +42,9 @@
 import android.print.test.services.StubbablePrinterDiscoverySession;
 import android.printservice.recommendation.IRecommendationsChangeListener;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.LargeTest;
 import androidx.test.filters.MediumTest;
-import androidx.test.runner.AndroidJUnit4;
 import androidx.test.uiautomator.UiDevice;
 
 import org.junit.Before;
diff --git a/core/tests/coretests/src/android/provider/DeviceConfigServiceManagerTest.java b/core/tests/coretests/src/android/provider/DeviceConfigServiceManagerTest.java
index e20258a..a60746f 100644
--- a/core/tests/coretests/src/android/provider/DeviceConfigServiceManagerTest.java
+++ b/core/tests/coretests/src/android/provider/DeviceConfigServiceManagerTest.java
@@ -23,8 +23,8 @@
 
 import android.platform.test.annotations.Presubmit;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/provider/DeviceConfigTest.java b/core/tests/coretests/src/android/provider/DeviceConfigTest.java
index 9300d1e..681396e 100644
--- a/core/tests/coretests/src/android/provider/DeviceConfigTest.java
+++ b/core/tests/coretests/src/android/provider/DeviceConfigTest.java
@@ -29,9 +29,9 @@
 import android.platform.test.annotations.Presubmit;
 
 import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.FlakyTest;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.After;
 import org.junit.Assert;
diff --git a/core/tests/coretests/src/android/provider/FontsContractE2ETest.java b/core/tests/coretests/src/android/provider/FontsContractE2ETest.java
index 7e02be8..4010171 100644
--- a/core/tests/coretests/src/android/provider/FontsContractE2ETest.java
+++ b/core/tests/coretests/src/android/provider/FontsContractE2ETest.java
@@ -33,8 +33,8 @@
 import android.provider.FontsContract.FontFamilyResult;
 
 import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.After;
 import org.junit.Before;
diff --git a/core/tests/coretests/src/android/security/CredentialManagementAppTest.java b/core/tests/coretests/src/android/security/CredentialManagementAppTest.java
index fa824b1..1690741 100644
--- a/core/tests/coretests/src/android/security/CredentialManagementAppTest.java
+++ b/core/tests/coretests/src/android/security/CredentialManagementAppTest.java
@@ -23,8 +23,8 @@
 import android.net.Uri;
 import android.util.Xml;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/security/keystore/recovery/KeyChainProtectionParamsTest.java b/core/tests/coretests/src/android/security/keystore/recovery/KeyChainProtectionParamsTest.java
index ce0bf30..938147c 100644
--- a/core/tests/coretests/src/android/security/keystore/recovery/KeyChainProtectionParamsTest.java
+++ b/core/tests/coretests/src/android/security/keystore/recovery/KeyChainProtectionParamsTest.java
@@ -21,8 +21,8 @@
 
 import android.os.Parcel;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/security/keystore/recovery/KeyChainSnapshotTest.java b/core/tests/coretests/src/android/security/keystore/recovery/KeyChainSnapshotTest.java
index 37fe22f..242a273 100644
--- a/core/tests/coretests/src/android/security/keystore/recovery/KeyChainSnapshotTest.java
+++ b/core/tests/coretests/src/android/security/keystore/recovery/KeyChainSnapshotTest.java
@@ -21,8 +21,8 @@
 
 import android.os.Parcel;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.google.common.collect.Lists;
 
diff --git a/core/tests/coretests/src/android/security/keystore/recovery/KeyDerivationParamsTest.java b/core/tests/coretests/src/android/security/keystore/recovery/KeyDerivationParamsTest.java
index 2b37b52..d310b76 100644
--- a/core/tests/coretests/src/android/security/keystore/recovery/KeyDerivationParamsTest.java
+++ b/core/tests/coretests/src/android/security/keystore/recovery/KeyDerivationParamsTest.java
@@ -21,8 +21,8 @@
 
 import android.os.Parcel;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/security/keystore/recovery/RecoveryCertPathTest.java b/core/tests/coretests/src/android/security/keystore/recovery/RecoveryCertPathTest.java
index 3b8f715..29001ae 100644
--- a/core/tests/coretests/src/android/security/keystore/recovery/RecoveryCertPathTest.java
+++ b/core/tests/coretests/src/android/security/keystore/recovery/RecoveryCertPathTest.java
@@ -21,8 +21,8 @@
 
 import android.os.Parcel;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/security/keystore/recovery/TrustedRootCertificatesTest.java b/core/tests/coretests/src/android/security/keystore/recovery/TrustedRootCertificatesTest.java
index 2b15d73..7677c3c 100644
--- a/core/tests/coretests/src/android/security/keystore/recovery/TrustedRootCertificatesTest.java
+++ b/core/tests/coretests/src/android/security/keystore/recovery/TrustedRootCertificatesTest.java
@@ -20,8 +20,8 @@
 
 import static org.junit.Assert.assertTrue;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/security/keystore/recovery/WrappedApplicationKeyTest.java b/core/tests/coretests/src/android/security/keystore/recovery/WrappedApplicationKeyTest.java
index aec54e1..07353f8 100644
--- a/core/tests/coretests/src/android/security/keystore/recovery/WrappedApplicationKeyTest.java
+++ b/core/tests/coretests/src/android/security/keystore/recovery/WrappedApplicationKeyTest.java
@@ -21,8 +21,8 @@
 
 import android.os.Parcel;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.After;
 import org.junit.Before;
diff --git a/core/tests/coretests/src/android/security/keystore/recovery/X509CertificateParsingUtilsTest.java b/core/tests/coretests/src/android/security/keystore/recovery/X509CertificateParsingUtilsTest.java
index ba5e74a..7f91d03 100644
--- a/core/tests/coretests/src/android/security/keystore/recovery/X509CertificateParsingUtilsTest.java
+++ b/core/tests/coretests/src/android/security/keystore/recovery/X509CertificateParsingUtilsTest.java
@@ -21,8 +21,8 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.fail;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/service/controls/ControlProviderServiceTest.java b/core/tests/coretests/src/android/service/controls/ControlProviderServiceTest.java
index 4d446901..6eaf2e4 100644
--- a/core/tests/coretests/src/android/service/controls/ControlProviderServiceTest.java
+++ b/core/tests/coretests/src/android/service/controls/ControlProviderServiceTest.java
@@ -45,9 +45,9 @@
 import android.service.controls.actions.ControlActionWrapper;
 import android.service.controls.templates.ThumbnailTemplate;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 import androidx.test.platform.app.InstrumentationRegistry;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.android.internal.R;
 
diff --git a/core/tests/coretests/src/android/service/controls/actions/ControlActionTest.java b/core/tests/coretests/src/android/service/controls/actions/ControlActionTest.java
index d8088b7..44bdc53 100644
--- a/core/tests/coretests/src/android/service/controls/actions/ControlActionTest.java
+++ b/core/tests/coretests/src/android/service/controls/actions/ControlActionTest.java
@@ -23,8 +23,8 @@
 
 import android.os.Parcel;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/service/controls/templates/ControlTemplateTest.java b/core/tests/coretests/src/android/service/controls/templates/ControlTemplateTest.java
index 91a3ba7..73b6f648 100644
--- a/core/tests/coretests/src/android/service/controls/templates/ControlTemplateTest.java
+++ b/core/tests/coretests/src/android/service/controls/templates/ControlTemplateTest.java
@@ -25,8 +25,8 @@
 import android.graphics.drawable.Icon;
 import android.os.Parcel;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.android.frameworks.coretests.R;
 
diff --git a/core/tests/coretests/src/android/service/euicc/EuiccProfileInfoTest.java b/core/tests/coretests/src/android/service/euicc/EuiccProfileInfoTest.java
index 6792d0b..f4206c8 100644
--- a/core/tests/coretests/src/android/service/euicc/EuiccProfileInfoTest.java
+++ b/core/tests/coretests/src/android/service/euicc/EuiccProfileInfoTest.java
@@ -26,8 +26,8 @@
 import android.service.carrier.CarrierIdentifier;
 import android.telephony.UiccAccessRule;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/service/notification/NotificationListenerFilterTest.java b/core/tests/coretests/src/android/service/notification/NotificationListenerFilterTest.java
index a121941..44456e9 100644
--- a/core/tests/coretests/src/android/service/notification/NotificationListenerFilterTest.java
+++ b/core/tests/coretests/src/android/service/notification/NotificationListenerFilterTest.java
@@ -27,8 +27,8 @@
 import android.os.Parcel;
 import android.util.ArraySet;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/service/notification/StatusBarNotificationTest.java b/core/tests/coretests/src/android/service/notification/StatusBarNotificationTest.java
index 76c9f88..5042408 100644
--- a/core/tests/coretests/src/android/service/notification/StatusBarNotificationTest.java
+++ b/core/tests/coretests/src/android/service/notification/StatusBarNotificationTest.java
@@ -37,8 +37,8 @@
 import android.os.UserHandle;
 
 import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 
diff --git a/core/tests/coretests/src/android/service/quicksettings/TileTest.java b/core/tests/coretests/src/android/service/quicksettings/TileTest.java
index ca6c3b4..43f9122 100644
--- a/core/tests/coretests/src/android/service/quicksettings/TileTest.java
+++ b/core/tests/coretests/src/android/service/quicksettings/TileTest.java
@@ -18,8 +18,8 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/service/settings/suggestions/SuggestionServiceTest.java b/core/tests/coretests/src/android/service/settings/suggestions/SuggestionServiceTest.java
index 64edda5..85659d6 100644
--- a/core/tests/coretests/src/android/service/settings/suggestions/SuggestionServiceTest.java
+++ b/core/tests/coretests/src/android/service/settings/suggestions/SuggestionServiceTest.java
@@ -23,9 +23,9 @@
 import android.os.RemoteException;
 
 import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 import androidx.test.rule.ServiceTestRule;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.After;
 import org.junit.Before;
diff --git a/core/tests/coretests/src/android/service/settings/suggestions/SuggestionTest.java b/core/tests/coretests/src/android/service/settings/suggestions/SuggestionTest.java
index e0eb197..03096de 100644
--- a/core/tests/coretests/src/android/service/settings/suggestions/SuggestionTest.java
+++ b/core/tests/coretests/src/android/service/settings/suggestions/SuggestionTest.java
@@ -26,8 +26,8 @@
 import android.os.Parcel;
 
 import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/telephony/PinResultTest.java b/core/tests/coretests/src/android/telephony/PinResultTest.java
index c260807..f5432ee 100644
--- a/core/tests/coretests/src/android/telephony/PinResultTest.java
+++ b/core/tests/coretests/src/android/telephony/PinResultTest.java
@@ -18,7 +18,7 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/text/BidiFormatterTest.java b/core/tests/coretests/src/android/text/BidiFormatterTest.java
index 312fb68..307c95b 100644
--- a/core/tests/coretests/src/android/text/BidiFormatterTest.java
+++ b/core/tests/coretests/src/android/text/BidiFormatterTest.java
@@ -20,8 +20,8 @@
 
 import android.platform.test.annotations.Presubmit;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/text/DynamicLayoutBlocksTest.java b/core/tests/coretests/src/android/text/DynamicLayoutBlocksTest.java
index cca1ad3..81c4982 100644
--- a/core/tests/coretests/src/android/text/DynamicLayoutBlocksTest.java
+++ b/core/tests/coretests/src/android/text/DynamicLayoutBlocksTest.java
@@ -23,8 +23,8 @@
 
 import android.platform.test.annotations.Presubmit;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/text/DynamicLayoutOffsetMappingTest.java b/core/tests/coretests/src/android/text/DynamicLayoutOffsetMappingTest.java
index 5939c06..8a41678 100644
--- a/core/tests/coretests/src/android/text/DynamicLayoutOffsetMappingTest.java
+++ b/core/tests/coretests/src/android/text/DynamicLayoutOffsetMappingTest.java
@@ -21,11 +21,18 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import android.platform.test.annotations.Presubmit;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
 import android.text.method.OffsetMapping;
+import android.text.style.UpdateLayout;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
+import com.android.text.flags.Flags;
+
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -36,6 +43,9 @@
     private static final int WIDTH = 10000;
     private static final TextPaint sTextPaint = new TextPaint();
 
+    @Rule
+    public CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
     @Test
     public void textWithOffsetMapping() {
         final String text = "abcde";
@@ -120,6 +130,84 @@
     }
 
     @Test
+    @RequiresFlagsEnabled(Flags.FLAG_INSERT_MODE_CRASH_UPDATE_LAYOUT_SPAN)
+    public void textWithOffsetMapping_deletion_withUpdateLayoutSpan() {
+        final String text = "abcdef";
+        final SpannableStringBuilder spannable = new SpannableStringBuilder(text);
+        // UpdateLayout span covers the letter 'd'.
+        spannable.setSpan(new UpdateLayout() {}, 3, 4, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
+
+        final CharSequence transformedText =
+                new TestOffsetMapping(spannable, 3, "\n\n");
+
+        final DynamicLayout layout = DynamicLayout.Builder.obtain(spannable, sTextPaint, WIDTH)
+                .setAlignment(ALIGN_NORMAL)
+                .setIncludePad(false)
+                .setDisplayText(transformedText)
+                .build();
+
+        // delete character 'c', original text becomes "abdef"
+        spannable.delete(2, 3);
+        assertThat(transformedText.toString()).isEqualTo("ab\n\ndef");
+        assertLineRange(layout, /* lineBreaks */ 0, 3, 4, 7);
+
+        // delete character 'd', original text becomes "abef"
+        spannable.delete(2, 3);
+        assertThat(transformedText.toString()).isEqualTo("ab\n\nef");
+        assertLineRange(layout, /* lineBreaks */ 0, 3, 4, 6);
+
+        // delete "be", original text becomes "af"
+        spannable.delete(1, 3);
+        assertThat(transformedText.toString()).isEqualTo("a\n\nf");
+        assertLineRange(layout, /* lineBreaks */ 0, 2, 3, 4);
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_INSERT_MODE_CRASH_UPDATE_LAYOUT_SPAN)
+    public void textWithOffsetMapping_insert_withUpdateLayoutSpan() {
+        final String text = "abcdef";
+        final SpannableStringBuilder spannable = new SpannableStringBuilder(text);
+        final CharSequence transformedText = new TestOffsetMapping(spannable, 3, "\n\n");
+
+        // UpdateLayout span covers the letter 'de'.
+        spannable.setSpan(new UpdateLayout() {}, 3, 5, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
+
+        final DynamicLayout layout = DynamicLayout.Builder.obtain(spannable, sTextPaint, WIDTH)
+                .setAlignment(ALIGN_NORMAL)
+                .setIncludePad(false)
+                .setDisplayText(transformedText)
+                .build();
+
+        spannable.insert(3, "x");
+        assertThat(transformedText.toString()).isEqualTo("abcx\n\ndef");
+        assertLineRange(layout, /* lineBreaks */ 0, 5, 6, 9);
+
+        spannable.insert(5, "x");
+        assertThat(transformedText.toString()).isEqualTo("abcx\n\ndxef");
+        assertLineRange(layout, /* lineBreaks */ 0, 5, 6, 10);
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_INSERT_MODE_CRASH_UPDATE_LAYOUT_SPAN)
+    public void textWithOffsetMapping_replace_withUpdateLayoutSpan() {
+        final String text = "abcdef";
+        final SpannableStringBuilder spannable = new SpannableStringBuilder(text);
+        final CharSequence transformedText = new TestOffsetMapping(spannable, 3, "\n\n");
+        // UpdateLayout span covers the letter 'de'.
+        spannable.setSpan(new UpdateLayout() {}, 3, 5, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
+
+        final DynamicLayout layout = DynamicLayout.Builder.obtain(spannable, sTextPaint, WIDTH)
+                .setAlignment(ALIGN_NORMAL)
+                .setIncludePad(false)
+                .setDisplayText(transformedText)
+                .build();
+
+        spannable.replace(2, 4, "xx");
+        assertThat(transformedText.toString()).isEqualTo("abxx\n\nef");
+        assertLineRange(layout, /* lineBreaks */ 0, 5, 6, 8);
+    }
+
+    @Test
     public void textWithOffsetMapping_blockBeforeTextChanged_deletion() {
         final String text = "abcdef";
         final SpannableStringBuilder spannable = new TestNoBeforeTextChangeSpannableString(text);
diff --git a/core/tests/coretests/src/android/text/DynamicLayoutTest.java b/core/tests/coretests/src/android/text/DynamicLayoutTest.java
index 699243b..1036928 100644
--- a/core/tests/coretests/src/android/text/DynamicLayoutTest.java
+++ b/core/tests/coretests/src/android/text/DynamicLayoutTest.java
@@ -30,8 +30,8 @@
 import android.text.style.ReplacementSpan;
 import android.util.ArraySet;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/text/EmojiConsistencyTest.java b/core/tests/coretests/src/android/text/EmojiConsistencyTest.java
index c6e9e9c..72d09c8 100644
--- a/core/tests/coretests/src/android/text/EmojiConsistencyTest.java
+++ b/core/tests/coretests/src/android/text/EmojiConsistencyTest.java
@@ -18,8 +18,8 @@
 
 import static junit.framework.Assert.assertEquals;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/text/EmojiTest.java b/core/tests/coretests/src/android/text/EmojiTest.java
index 0aeeb74..21f346e 100644
--- a/core/tests/coretests/src/android/text/EmojiTest.java
+++ b/core/tests/coretests/src/android/text/EmojiTest.java
@@ -22,8 +22,8 @@
 import android.icu.lang.UCharacterDirection;
 import android.icu.text.Bidi;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/text/LayoutBidiCursorPathTest.java b/core/tests/coretests/src/android/text/LayoutBidiCursorPathTest.java
index 96e7fb9..7728866 100644
--- a/core/tests/coretests/src/android/text/LayoutBidiCursorPathTest.java
+++ b/core/tests/coretests/src/android/text/LayoutBidiCursorPathTest.java
@@ -26,8 +26,8 @@
 import android.view.KeyEvent;
 
 import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/text/LayoutTest.java b/core/tests/coretests/src/android/text/LayoutTest.java
index 98f8b7f..25f9cb7 100644
--- a/core/tests/coretests/src/android/text/LayoutTest.java
+++ b/core/tests/coretests/src/android/text/LayoutTest.java
@@ -42,8 +42,8 @@
 import android.text.style.ForegroundColorSpan;
 import android.text.style.StrikethroughSpan;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.google.common.truth.Expect;
 
diff --git a/core/tests/coretests/src/android/text/MeasuredParagraphTest.java b/core/tests/coretests/src/android/text/MeasuredParagraphTest.java
index 02b67e2..921a6bd 100644
--- a/core/tests/coretests/src/android/text/MeasuredParagraphTest.java
+++ b/core/tests/coretests/src/android/text/MeasuredParagraphTest.java
@@ -26,8 +26,8 @@
 import android.graphics.text.MeasuredText;
 
 import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/text/PackedIntVectorTest.java b/core/tests/coretests/src/android/text/PackedIntVectorTest.java
index ba15b92..e8d706d 100644
--- a/core/tests/coretests/src/android/text/PackedIntVectorTest.java
+++ b/core/tests/coretests/src/android/text/PackedIntVectorTest.java
@@ -20,8 +20,8 @@
 
 import android.platform.test.annotations.Presubmit;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/text/SpanColorsTest.java b/core/tests/coretests/src/android/text/SpanColorsTest.java
index 3d8d8f9..d2cb8c1 100644
--- a/core/tests/coretests/src/android/text/SpanColorsTest.java
+++ b/core/tests/coretests/src/android/text/SpanColorsTest.java
@@ -25,8 +25,8 @@
 import android.text.style.ImageSpan;
 import android.text.style.UnderlineSpan;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/text/SpannableStringBuilderTest.java b/core/tests/coretests/src/android/text/SpannableStringBuilderTest.java
index 91b8c6a..b725133 100644
--- a/core/tests/coretests/src/android/text/SpannableStringBuilderTest.java
+++ b/core/tests/coretests/src/android/text/SpannableStringBuilderTest.java
@@ -25,8 +25,8 @@
 import android.text.style.SubscriptSpan;
 import android.text.style.UnderlineSpan;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/text/SpannableStringNoCopyTest.java b/core/tests/coretests/src/android/text/SpannableStringNoCopyTest.java
index 9149f7b..a2952f6 100644
--- a/core/tests/coretests/src/android/text/SpannableStringNoCopyTest.java
+++ b/core/tests/coretests/src/android/text/SpannableStringNoCopyTest.java
@@ -24,8 +24,8 @@
 import android.text.style.QuoteSpan;
 import android.text.style.UnderlineSpan;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/text/SpannableTest.java b/core/tests/coretests/src/android/text/SpannableTest.java
index d248a1f..a3e6a78 100644
--- a/core/tests/coretests/src/android/text/SpannableTest.java
+++ b/core/tests/coretests/src/android/text/SpannableTest.java
@@ -21,8 +21,8 @@
 import android.platform.test.annotations.Presubmit;
 import android.test.MoreAsserts;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/text/SpannedStringNoCopyTest.java b/core/tests/coretests/src/android/text/SpannedStringNoCopyTest.java
index ca43733..3e2516f 100644
--- a/core/tests/coretests/src/android/text/SpannedStringNoCopyTest.java
+++ b/core/tests/coretests/src/android/text/SpannedStringNoCopyTest.java
@@ -24,8 +24,8 @@
 import android.text.style.QuoteSpan;
 import android.text.style.UnderlineSpan;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/text/SpannedTest.java b/core/tests/coretests/src/android/text/SpannedTest.java
index 3ab0755..e9a357c 100644
--- a/core/tests/coretests/src/android/text/SpannedTest.java
+++ b/core/tests/coretests/src/android/text/SpannedTest.java
@@ -26,8 +26,8 @@
 import android.text.style.TextAppearanceSpan;
 import android.text.style.TypefaceSpan;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/text/StaticLayoutBidiTest.java b/core/tests/coretests/src/android/text/StaticLayoutBidiTest.java
index 32370b3e..3deda8c 100644
--- a/core/tests/coretests/src/android/text/StaticLayoutBidiTest.java
+++ b/core/tests/coretests/src/android/text/StaticLayoutBidiTest.java
@@ -21,8 +21,8 @@
 import android.platform.test.annotations.Presubmit;
 import android.util.Log;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/text/StaticLayoutDirectionsTest.java b/core/tests/coretests/src/android/text/StaticLayoutDirectionsTest.java
index 4221ac2..bc7efe4 100644
--- a/core/tests/coretests/src/android/text/StaticLayoutDirectionsTest.java
+++ b/core/tests/coretests/src/android/text/StaticLayoutDirectionsTest.java
@@ -22,8 +22,8 @@
 import android.text.Layout.Directions;
 import android.text.StaticLayoutTest.LayoutBuilder;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/text/StaticLayoutTest.java b/core/tests/coretests/src/android/text/StaticLayoutTest.java
index 0ebf03f..3541900 100644
--- a/core/tests/coretests/src/android/text/StaticLayoutTest.java
+++ b/core/tests/coretests/src/android/text/StaticLayoutTest.java
@@ -31,8 +31,8 @@
 import android.text.style.LocaleSpan;
 import android.util.Log;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/text/StaticLayoutTextMeasuringTest.java b/core/tests/coretests/src/android/text/StaticLayoutTextMeasuringTest.java
index 0d42326..b32e94a 100644
--- a/core/tests/coretests/src/android/text/StaticLayoutTextMeasuringTest.java
+++ b/core/tests/coretests/src/android/text/StaticLayoutTextMeasuringTest.java
@@ -21,8 +21,8 @@
 
 import android.text.Layout.Alignment;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/text/TextLayoutTest.java b/core/tests/coretests/src/android/text/TextLayoutTest.java
index 15fbc9e..1584bc3 100644
--- a/core/tests/coretests/src/android/text/TextLayoutTest.java
+++ b/core/tests/coretests/src/android/text/TextLayoutTest.java
@@ -18,8 +18,8 @@
 
 import android.platform.test.annotations.Presubmit;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/text/TextLineTest.java b/core/tests/coretests/src/android/text/TextLineTest.java
index 8ae5669..2997853 100644
--- a/core/tests/coretests/src/android/text/TextLineTest.java
+++ b/core/tests/coretests/src/android/text/TextLineTest.java
@@ -30,10 +30,10 @@
 import android.text.style.ReplacementSpan;
 import android.text.style.TabStopSpan;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 import androidx.test.filters.Suppress;
 import androidx.test.platform.app.InstrumentationRegistry;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/text/TextShaperTest.java b/core/tests/coretests/src/android/text/TextShaperTest.java
index 77b14e6..84112ae 100644
--- a/core/tests/coretests/src/android/text/TextShaperTest.java
+++ b/core/tests/coretests/src/android/text/TextShaperTest.java
@@ -21,8 +21,8 @@
 import android.graphics.fonts.Font;
 import android.graphics.fonts.FontFileUtil;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/text/TextUtilsTest.java b/core/tests/coretests/src/android/text/TextUtilsTest.java
index c4bcfd4..f552265 100644
--- a/core/tests/coretests/src/android/text/TextUtilsTest.java
+++ b/core/tests/coretests/src/android/text/TextUtilsTest.java
@@ -34,9 +34,9 @@
 import android.text.util.Rfc822Tokenizer;
 import android.view.View;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.LargeTest;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.google.android.collect.Lists;
 
diff --git a/core/tests/coretests/src/android/text/VariationParserTest.java b/core/tests/coretests/src/android/text/VariationParserTest.java
index 0afe811..8e93dd4 100644
--- a/core/tests/coretests/src/android/text/VariationParserTest.java
+++ b/core/tests/coretests/src/android/text/VariationParserTest.java
@@ -22,8 +22,8 @@
 import android.graphics.fonts.FontVariationAxis;
 import android.platform.test.annotations.Presubmit;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/text/format/DateFormatTest.java b/core/tests/coretests/src/android/text/format/DateFormatTest.java
index 212cc44..59af6dd 100644
--- a/core/tests/coretests/src/android/text/format/DateFormatTest.java
+++ b/core/tests/coretests/src/android/text/format/DateFormatTest.java
@@ -25,8 +25,8 @@
 import android.icu.text.DateFormatSymbols;
 import android.platform.test.annotations.Presubmit;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges;
 import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
diff --git a/core/tests/coretests/src/android/text/format/DateIntervalFormatTest.java b/core/tests/coretests/src/android/text/format/DateIntervalFormatTest.java
index 9750de3..a07d399 100644
--- a/core/tests/coretests/src/android/text/format/DateIntervalFormatTest.java
+++ b/core/tests/coretests/src/android/text/format/DateIntervalFormatTest.java
@@ -42,8 +42,8 @@
 import android.icu.util.ULocale;
 import android.platform.test.annotations.Presubmit;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/text/format/DateUtilsTest.java b/core/tests/coretests/src/android/text/format/DateUtilsTest.java
index 381c051..47be893 100644
--- a/core/tests/coretests/src/android/text/format/DateUtilsTest.java
+++ b/core/tests/coretests/src/android/text/format/DateUtilsTest.java
@@ -23,8 +23,8 @@
 import android.os.LocaleList;
 import android.platform.test.annotations.Presubmit;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.After;
 import org.junit.Before;
diff --git a/core/tests/coretests/src/android/text/format/FormatterTest.java b/core/tests/coretests/src/android/text/format/FormatterTest.java
index 986cee5..555292e 100644
--- a/core/tests/coretests/src/android/text/format/FormatterTest.java
+++ b/core/tests/coretests/src/android/text/format/FormatterTest.java
@@ -28,8 +28,8 @@
 import android.text.format.Formatter.BytesResult;
 
 import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.After;
 import org.junit.Before;
diff --git a/core/tests/coretests/src/android/text/format/RelativeDateTimeFormatterTest.java b/core/tests/coretests/src/android/text/format/RelativeDateTimeFormatterTest.java
index 2337802..cd31950 100644
--- a/core/tests/coretests/src/android/text/format/RelativeDateTimeFormatterTest.java
+++ b/core/tests/coretests/src/android/text/format/RelativeDateTimeFormatterTest.java
@@ -36,8 +36,8 @@
 
 import android.platform.test.annotations.Presubmit;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/text/format/TimeMigrationUtilsTest.java b/core/tests/coretests/src/android/text/format/TimeMigrationUtilsTest.java
index b605520..c8cb5f3 100644
--- a/core/tests/coretests/src/android/text/format/TimeMigrationUtilsTest.java
+++ b/core/tests/coretests/src/android/text/format/TimeMigrationUtilsTest.java
@@ -20,8 +20,8 @@
 
 import android.platform.test.annotations.Presubmit;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.After;
 import org.junit.Before;
diff --git a/core/tests/coretests/src/android/text/format/TimeTest.java b/core/tests/coretests/src/android/text/format/TimeTest.java
index ac00411..6138ea1 100644
--- a/core/tests/coretests/src/android/text/format/TimeTest.java
+++ b/core/tests/coretests/src/android/text/format/TimeTest.java
@@ -24,9 +24,9 @@
 import android.util.Log;
 import android.util.TimeFormatException;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 import androidx.test.filters.Suppress;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/text/method/BackspaceTest.java b/core/tests/coretests/src/android/text/method/BackspaceTest.java
index 19c2c61..a7ff244 100644
--- a/core/tests/coretests/src/android/text/method/BackspaceTest.java
+++ b/core/tests/coretests/src/android/text/method/BackspaceTest.java
@@ -24,8 +24,8 @@
 import android.widget.TextView.BufferType;
 
 import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/text/method/ForwardDeleteTest.java b/core/tests/coretests/src/android/text/method/ForwardDeleteTest.java
index 652622d..1e4024d 100644
--- a/core/tests/coretests/src/android/text/method/ForwardDeleteTest.java
+++ b/core/tests/coretests/src/android/text/method/ForwardDeleteTest.java
@@ -24,8 +24,8 @@
 import android.widget.TextView.BufferType;
 
 import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/text/method/InsertModeTransformationMethodTest.java b/core/tests/coretests/src/android/text/method/InsertModeTransformationMethodTest.java
index 9ef137b..2f336ab 100644
--- a/core/tests/coretests/src/android/text/method/InsertModeTransformationMethodTest.java
+++ b/core/tests/coretests/src/android/text/method/InsertModeTransformationMethodTest.java
@@ -27,9 +27,8 @@
 import android.view.View;
 
 import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
 
 import org.junit.BeforeClass;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/text/method/WordIteratorTest.java b/core/tests/coretests/src/android/text/method/WordIteratorTest.java
index cc345f5..046496a 100644
--- a/core/tests/coretests/src/android/text/method/WordIteratorTest.java
+++ b/core/tests/coretests/src/android/text/method/WordIteratorTest.java
@@ -23,8 +23,8 @@
 
 import android.platform.test.annotations.Presubmit;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/text/style/UnderlineSpanTest.java b/core/tests/coretests/src/android/text/style/UnderlineSpanTest.java
index a0d2f85..043960d0 100644
--- a/core/tests/coretests/src/android/text/style/UnderlineSpanTest.java
+++ b/core/tests/coretests/src/android/text/style/UnderlineSpanTest.java
@@ -24,8 +24,8 @@
 import android.text.StaticLayout;
 import android.text.TextPaint;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/text/util/LinkifyTest.java b/core/tests/coretests/src/android/text/util/LinkifyTest.java
index 107ecd7..52f3b2e 100644
--- a/core/tests/coretests/src/android/text/util/LinkifyTest.java
+++ b/core/tests/coretests/src/android/text/util/LinkifyTest.java
@@ -31,8 +31,8 @@
 import android.widget.TextView;
 
 import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.After;
 import org.junit.Before;
diff --git a/core/tests/coretests/src/android/tracing/perfetto/DataSourceTest.java b/core/tests/coretests/src/android/tracing/perfetto/DataSourceTest.java
index df9a89e..bbeb18d 100644
--- a/core/tests/coretests/src/android/tracing/perfetto/DataSourceTest.java
+++ b/core/tests/coretests/src/android/tracing/perfetto/DataSourceTest.java
@@ -37,7 +37,7 @@
 import android.util.proto.ProtoInputStream;
 import android.util.proto.ProtoOutputStream;
 
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import com.google.common.truth.Truth;
 import com.google.protobuf.InvalidProtocolBufferException;
diff --git a/core/tests/coretests/src/android/transition/AutoTransitionTest.java b/core/tests/coretests/src/android/transition/AutoTransitionTest.java
index deae967..5d58fead 100644
--- a/core/tests/coretests/src/android/transition/AutoTransitionTest.java
+++ b/core/tests/coretests/src/android/transition/AutoTransitionTest.java
@@ -20,8 +20,8 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/util/ArrayMapTest.java b/core/tests/coretests/src/android/util/ArrayMapTest.java
index 1e444ad..711ff94 100644
--- a/core/tests/coretests/src/android/util/ArrayMapTest.java
+++ b/core/tests/coretests/src/android/util/ArrayMapTest.java
@@ -19,8 +19,8 @@
 import android.platform.test.annotations.IgnoreUnderRavenwood;
 import android.platform.test.ravenwood.RavenwoodRule;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.LargeTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Rule;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/util/ArraySetTest.java b/core/tests/coretests/src/android/util/ArraySetTest.java
index 51de634..8888991 100644
--- a/core/tests/coretests/src/android/util/ArraySetTest.java
+++ b/core/tests/coretests/src/android/util/ArraySetTest.java
@@ -18,8 +18,8 @@
 
 import static org.junit.Assert.fail;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.LargeTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.After;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/util/Base64Test.java b/core/tests/coretests/src/android/util/Base64Test.java
index b648266..3b322c2 100644
--- a/core/tests/coretests/src/android/util/Base64Test.java
+++ b/core/tests/coretests/src/android/util/Base64Test.java
@@ -20,8 +20,8 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.fail;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.LargeTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Ignore;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/util/BinaryXmlTest.java b/core/tests/coretests/src/android/util/BinaryXmlTest.java
index da29828..96c79013 100644
--- a/core/tests/coretests/src/android/util/BinaryXmlTest.java
+++ b/core/tests/coretests/src/android/util/BinaryXmlTest.java
@@ -30,7 +30,7 @@
 
 import android.os.PersistableBundle;
 
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import com.android.modules.utils.TypedXmlPullParser;
 import com.android.modules.utils.TypedXmlSerializer;
diff --git a/core/tests/coretests/src/android/util/CharsetUtilsTest.java b/core/tests/coretests/src/android/util/CharsetUtilsTest.java
index fbbe311..33936e9 100644
--- a/core/tests/coretests/src/android/util/CharsetUtilsTest.java
+++ b/core/tests/coretests/src/android/util/CharsetUtilsTest.java
@@ -21,7 +21,7 @@
 import android.platform.test.annotations.IgnoreUnderRavenwood;
 import android.platform.test.ravenwood.RavenwoodRule;
 
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import com.android.internal.util.HexDump;
 
diff --git a/core/tests/coretests/src/android/util/DayOfMonthCursorTest.java b/core/tests/coretests/src/android/util/DayOfMonthCursorTest.java
index 72bd578..4587f5b 100644
--- a/core/tests/coretests/src/android/util/DayOfMonthCursorTest.java
+++ b/core/tests/coretests/src/android/util/DayOfMonthCursorTest.java
@@ -21,8 +21,8 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/util/FloatMathTest.java b/core/tests/coretests/src/android/util/FloatMathTest.java
index f748acd..a52c9ac 100644
--- a/core/tests/coretests/src/android/util/FloatMathTest.java
+++ b/core/tests/coretests/src/android/util/FloatMathTest.java
@@ -18,7 +18,7 @@
 
 import static org.junit.Assert.assertEquals;
 
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/util/KeyValueListParserTest.java b/core/tests/coretests/src/android/util/KeyValueListParserTest.java
index f65c4c7..eaf8d19 100644
--- a/core/tests/coretests/src/android/util/KeyValueListParserTest.java
+++ b/core/tests/coretests/src/android/util/KeyValueListParserTest.java
@@ -20,8 +20,8 @@
 
 import android.platform.test.annotations.Presubmit;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/util/LogNullabilityTest.java b/core/tests/coretests/src/android/util/LogNullabilityTest.java
index 475e347..5aa2626 100644
--- a/core/tests/coretests/src/android/util/LogNullabilityTest.java
+++ b/core/tests/coretests/src/android/util/LogNullabilityTest.java
@@ -21,8 +21,8 @@
 
 import android.platform.test.ravenwood.RavenwoodRule;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/util/LogTest.java b/core/tests/coretests/src/android/util/LogTest.java
index 15caac9..0e44e68 100644
--- a/core/tests/coretests/src/android/util/LogTest.java
+++ b/core/tests/coretests/src/android/util/LogTest.java
@@ -19,8 +19,8 @@
 import android.os.SystemProperties;
 import android.test.PerformanceTestCase;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.Suppress;
-import androidx.test.runner.AndroidJUnit4;
 
 import junit.framework.TestCase;
 
diff --git a/core/tests/coretests/src/android/util/LogWriterTest.java b/core/tests/coretests/src/android/util/LogWriterTest.java
index 890a401..739fbcd 100644
--- a/core/tests/coretests/src/android/util/LogWriterTest.java
+++ b/core/tests/coretests/src/android/util/LogWriterTest.java
@@ -16,7 +16,7 @@
 
 package android.util;
 
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/util/LongArrayQueueTest.java b/core/tests/coretests/src/android/util/LongArrayQueueTest.java
index 77e8d60..f696f70 100644
--- a/core/tests/coretests/src/android/util/LongArrayQueueTest.java
+++ b/core/tests/coretests/src/android/util/LongArrayQueueTest.java
@@ -20,8 +20,8 @@
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.fail;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/util/LongSparseLongArrayTest.java b/core/tests/coretests/src/android/util/LongSparseLongArrayTest.java
index 9dbaae0d..fb0b2e3 100644
--- a/core/tests/coretests/src/android/util/LongSparseLongArrayTest.java
+++ b/core/tests/coretests/src/android/util/LongSparseLongArrayTest.java
@@ -18,8 +18,8 @@
 
 import static org.junit.Assert.assertEquals;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.LargeTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/util/LruCacheTest.java b/core/tests/coretests/src/android/util/LruCacheTest.java
index 10e8308..1c6dcdf 100644
--- a/core/tests/coretests/src/android/util/LruCacheTest.java
+++ b/core/tests/coretests/src/android/util/LruCacheTest.java
@@ -20,7 +20,7 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.fail;
 
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/util/MonthDisplayHelperTest.java b/core/tests/coretests/src/android/util/MonthDisplayHelperTest.java
index 06f970f..cb34c98 100644
--- a/core/tests/coretests/src/android/util/MonthDisplayHelperTest.java
+++ b/core/tests/coretests/src/android/util/MonthDisplayHelperTest.java
@@ -20,8 +20,8 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/util/MutableTest.java b/core/tests/coretests/src/android/util/MutableTest.java
index dfdff4d..1f73c16 100644
--- a/core/tests/coretests/src/android/util/MutableTest.java
+++ b/core/tests/coretests/src/android/util/MutableTest.java
@@ -20,7 +20,7 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/util/PatternsTest.java b/core/tests/coretests/src/android/util/PatternsTest.java
index a180ec3..8d0785f 100644
--- a/core/tests/coretests/src/android/util/PatternsTest.java
+++ b/core/tests/coretests/src/android/util/PatternsTest.java
@@ -20,8 +20,8 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/util/PoolsTest.java b/core/tests/coretests/src/android/util/PoolsTest.java
index bdbc9b1..e31ab78 100644
--- a/core/tests/coretests/src/android/util/PoolsTest.java
+++ b/core/tests/coretests/src/android/util/PoolsTest.java
@@ -19,7 +19,7 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNull;
 
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/util/PrefixPrinterTest.java b/core/tests/coretests/src/android/util/PrefixPrinterTest.java
index a8d48ee..8199155 100644
--- a/core/tests/coretests/src/android/util/PrefixPrinterTest.java
+++ b/core/tests/coretests/src/android/util/PrefixPrinterTest.java
@@ -18,7 +18,7 @@
 
 import static org.junit.Assert.assertEquals;
 
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/util/RecurrenceRuleTest.java b/core/tests/coretests/src/android/util/RecurrenceRuleTest.java
index 32548b4..8b2068c 100644
--- a/core/tests/coretests/src/android/util/RecurrenceRuleTest.java
+++ b/core/tests/coretests/src/android/util/RecurrenceRuleTest.java
@@ -20,8 +20,8 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.After;
 import org.junit.Before;
diff --git a/core/tests/coretests/src/android/util/SequenceUtilsTest.java b/core/tests/coretests/src/android/util/SequenceUtilsTest.java
index 6ca1751..e5ee04c 100644
--- a/core/tests/coretests/src/android/util/SequenceUtilsTest.java
+++ b/core/tests/coretests/src/android/util/SequenceUtilsTest.java
@@ -29,8 +29,8 @@
 import android.platform.test.annotations.Presubmit;
 import android.platform.test.ravenwood.RavenwoodRule;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Rule;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/util/SingletonTest.java b/core/tests/coretests/src/android/util/SingletonTest.java
index 8c5a963..31ae650 100644
--- a/core/tests/coretests/src/android/util/SingletonTest.java
+++ b/core/tests/coretests/src/android/util/SingletonTest.java
@@ -18,7 +18,7 @@
 
 import static org.junit.Assert.assertTrue;
 
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/util/SparseDoubleArrayTest.java b/core/tests/coretests/src/android/util/SparseDoubleArrayTest.java
index ba9c8d9..cc5ffbe 100644
--- a/core/tests/coretests/src/android/util/SparseDoubleArrayTest.java
+++ b/core/tests/coretests/src/android/util/SparseDoubleArrayTest.java
@@ -18,8 +18,8 @@
 
 import static org.junit.Assert.assertEquals;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/util/SparseLongArrayTest.java b/core/tests/coretests/src/android/util/SparseLongArrayTest.java
index b29b6f1..4038d88 100644
--- a/core/tests/coretests/src/android/util/SparseLongArrayTest.java
+++ b/core/tests/coretests/src/android/util/SparseLongArrayTest.java
@@ -21,8 +21,8 @@
 
 import android.annotation.NonNull;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/util/SparseSetArrayTest.java b/core/tests/coretests/src/android/util/SparseSetArrayTest.java
index a8dce70..48ea3a1 100644
--- a/core/tests/coretests/src/android/util/SparseSetArrayTest.java
+++ b/core/tests/coretests/src/android/util/SparseSetArrayTest.java
@@ -19,8 +19,8 @@
 
 import android.platform.test.ravenwood.RavenwoodRule;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Rule;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/util/StateSetTest.java b/core/tests/coretests/src/android/util/StateSetTest.java
index dfd1523..14e4e20 100644
--- a/core/tests/coretests/src/android/util/StateSetTest.java
+++ b/core/tests/coretests/src/android/util/StateSetTest.java
@@ -22,8 +22,8 @@
 import android.platform.test.annotations.IgnoreUnderRavenwood;
 import android.platform.test.ravenwood.RavenwoodRule;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Rule;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/util/TeeWriterTest.java b/core/tests/coretests/src/android/util/TeeWriterTest.java
index c78376a..cc1c091 100644
--- a/core/tests/coretests/src/android/util/TeeWriterTest.java
+++ b/core/tests/coretests/src/android/util/TeeWriterTest.java
@@ -18,7 +18,7 @@
 
 import static org.junit.Assert.assertEquals;
 
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/util/XmlTest.java b/core/tests/coretests/src/android/util/XmlTest.java
index 91ebc2a..540f118 100644
--- a/core/tests/coretests/src/android/util/XmlTest.java
+++ b/core/tests/coretests/src/android/util/XmlTest.java
@@ -26,7 +26,7 @@
 
 import android.os.PersistableBundle;
 
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import com.android.internal.util.XmlUtils;
 import com.android.modules.utils.TypedXmlPullParser;
diff --git a/core/tests/coretests/src/android/view/CompositionSamplingListenerTest.java b/core/tests/coretests/src/android/view/CompositionSamplingListenerTest.java
index 729a555..e74027e 100644
--- a/core/tests/coretests/src/android/view/CompositionSamplingListenerTest.java
+++ b/core/tests/coretests/src/android/view/CompositionSamplingListenerTest.java
@@ -21,8 +21,8 @@
 import android.graphics.Rect;
 import android.platform.test.annotations.Presubmit;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/view/CutoutSpecificationTest.java b/core/tests/coretests/src/android/view/CutoutSpecificationTest.java
index 7872810..0fdc239 100644
--- a/core/tests/coretests/src/android/view/CutoutSpecificationTest.java
+++ b/core/tests/coretests/src/android/view/CutoutSpecificationTest.java
@@ -23,8 +23,8 @@
 import android.graphics.Rect;
 import android.platform.test.annotations.Presubmit;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/view/DisplayCutoutTest.java b/core/tests/coretests/src/android/view/DisplayCutoutTest.java
index 0d1dde3..2c66330 100644
--- a/core/tests/coretests/src/android/view/DisplayCutoutTest.java
+++ b/core/tests/coretests/src/android/view/DisplayCutoutTest.java
@@ -44,8 +44,8 @@
 import android.platform.test.annotations.Presubmit;
 import android.view.DisplayCutout.ParcelableWrapper;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/view/DisplayShapeTest.java b/core/tests/coretests/src/android/view/DisplayShapeTest.java
index 77dd8bd..7778ba1 100644
--- a/core/tests/coretests/src/android/view/DisplayShapeTest.java
+++ b/core/tests/coretests/src/android/view/DisplayShapeTest.java
@@ -27,8 +27,8 @@
 import android.graphics.RectF;
 import android.platform.test.annotations.Presubmit;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
index 1682135..668487d 100644
--- a/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
+++ b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
@@ -42,7 +42,7 @@
 import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams;
 import android.view.animation.LinearInterpolator;
 
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java
index ae7f465..7bc0d2f 100644
--- a/core/tests/coretests/src/android/view/InsetsControllerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java
@@ -56,6 +56,7 @@
 import static java.util.concurrent.TimeUnit.SECONDS;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.Context;
 import android.graphics.Insets;
 import android.graphics.Point;
@@ -1095,9 +1096,10 @@
         }
 
         @Override
-        public void updateRequestedVisibleTypes(@InsetsType int requestedVisibleTypes) {
+        public void updateRequestedVisibleTypes(@InsetsType int requestedVisibleTypes,
+                @Nullable ImeTracker.Token statsToken) {
             mRequestedVisibleTypes = requestedVisibleTypes;
-            super.updateRequestedVisibleTypes(requestedVisibleTypes);
+            super.updateRequestedVisibleTypes(requestedVisibleTypes, statsToken);
         }
 
         public boolean isRequestedVisible(@InsetsType int types) {
diff --git a/core/tests/coretests/src/android/view/InsetsSourceTest.java b/core/tests/coretests/src/android/view/InsetsSourceTest.java
index 61e05da..c3bd065 100644
--- a/core/tests/coretests/src/android/view/InsetsSourceTest.java
+++ b/core/tests/coretests/src/android/view/InsetsSourceTest.java
@@ -32,7 +32,7 @@
 import android.platform.test.annotations.Presubmit;
 import android.util.SparseArray;
 
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/view/InsetsStateTest.java b/core/tests/coretests/src/android/view/InsetsStateTest.java
index 16bd20a..1144ee1 100644
--- a/core/tests/coretests/src/android/view/InsetsStateTest.java
+++ b/core/tests/coretests/src/android/view/InsetsStateTest.java
@@ -19,6 +19,7 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.view.InsetsSource.FLAG_FORCE_CONSUMING;
+import static android.view.InsetsSource.FLAG_FORCE_CONSUMING_OPAQUE_CAPTION_BAR;
 import static android.view.InsetsSource.ID_IME;
 import static android.view.InsetsSource.SIDE_BOTTOM;
 import static android.view.InsetsSource.SIDE_TOP;
@@ -54,12 +55,17 @@
 import android.graphics.Insets;
 import android.graphics.Rect;
 import android.os.Parcel;
+import android.platform.test.annotations.EnableFlags;
 import android.platform.test.annotations.Presubmit;
+import android.platform.test.flag.junit.SetFlagsRule;
 import android.util.SparseIntArray;
 import android.view.WindowInsets.Type;
 
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
+import com.android.window.flags.Flags;
+
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -78,6 +84,9 @@
 @RunWith(AndroidJUnit4.class)
 public class InsetsStateTest {
 
+    @Rule
+    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
     private static final int ID_STATUS_BAR = InsetsSource.createId(
             null /* owner */, 0 /* index */, statusBars());
     private static final int ID_NAVIGATION_BAR = InsetsSource.createId(
@@ -854,4 +863,19 @@
         );
 
     }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION_ALWAYS)
+    public void testCalculateInsets_forceConsumingCaptionBar() {
+        mState.getOrCreateSource(ID_CAPTION_BAR, captionBar())
+                .setFrame(new Rect(0, 0, 100, 100))
+                .setVisible(true)
+                .setFlags(FLAG_FORCE_CONSUMING_OPAQUE_CAPTION_BAR);
+
+        final WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 1000, 1000), null, false,
+                SOFT_INPUT_ADJUST_RESIZE, 0, 0, TYPE_APPLICATION, ACTIVITY_TYPE_UNDEFINED,
+                new SparseIntArray());
+
+        assertTrue(insets.isForceConsumingOpaqueCaptionBar());
+    }
 }
diff --git a/core/tests/coretests/src/android/view/MotionEventTest.java b/core/tests/coretests/src/android/view/MotionEventTest.java
index bad0485..d0f9a38 100644
--- a/core/tests/coretests/src/android/view/MotionEventTest.java
+++ b/core/tests/coretests/src/android/view/MotionEventTest.java
@@ -33,8 +33,8 @@
 import android.view.MotionEvent.PointerCoords;
 import android.view.MotionEvent.PointerProperties;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/view/PendingInsetsControllerTest.java b/core/tests/coretests/src/android/view/PendingInsetsControllerTest.java
index b5b2d0c..8ac9292 100644
--- a/core/tests/coretests/src/android/view/PendingInsetsControllerTest.java
+++ b/core/tests/coretests/src/android/view/PendingInsetsControllerTest.java
@@ -37,7 +37,7 @@
 import android.view.WindowInsetsController.OnControllableInsetsChangedListener;
 import android.view.animation.LinearInterpolator;
 
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/view/RoundedCornerTest.java b/core/tests/coretests/src/android/view/RoundedCornerTest.java
index 4349021..3992aa1 100644
--- a/core/tests/coretests/src/android/view/RoundedCornerTest.java
+++ b/core/tests/coretests/src/android/view/RoundedCornerTest.java
@@ -23,8 +23,8 @@
 import android.graphics.Point;
 import android.platform.test.annotations.Presubmit;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/view/RoundedCornersTest.java b/core/tests/coretests/src/android/view/RoundedCornersTest.java
index ec665ad..c26d945 100644
--- a/core/tests/coretests/src/android/view/RoundedCornersTest.java
+++ b/core/tests/coretests/src/android/view/RoundedCornersTest.java
@@ -42,8 +42,8 @@
 import android.util.DisplayMetrics;
 import android.util.Pair;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.android.internal.R;
 
diff --git a/core/tests/coretests/src/android/view/ScrollCaptureConnectionTest.java b/core/tests/coretests/src/android/view/ScrollCaptureConnectionTest.java
index 5f258949..bee5dc4 100644
--- a/core/tests/coretests/src/android/view/ScrollCaptureConnectionTest.java
+++ b/core/tests/coretests/src/android/view/ScrollCaptureConnectionTest.java
@@ -38,8 +38,8 @@
 import android.os.RemoteException;
 import android.platform.test.annotations.Presubmit;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/view/ScrollCaptureSearchResultsTest.java b/core/tests/coretests/src/android/view/ScrollCaptureSearchResultsTest.java
index dc43204..726ee85 100644
--- a/core/tests/coretests/src/android/view/ScrollCaptureSearchResultsTest.java
+++ b/core/tests/coretests/src/android/view/ScrollCaptureSearchResultsTest.java
@@ -35,8 +35,8 @@
 import android.platform.test.annotations.Presubmit;
 
 import androidx.annotation.NonNull;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/view/SurfaceControlRegistryTests.java b/core/tests/coretests/src/android/view/SurfaceControlRegistryTests.java
index 71bdce4..5a5510c 100644
--- a/core/tests/coretests/src/android/view/SurfaceControlRegistryTests.java
+++ b/core/tests/coretests/src/android/view/SurfaceControlRegistryTests.java
@@ -22,8 +22,8 @@
 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
 
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 import static org.mockito.ArgumentMatchers.eq;
@@ -33,7 +33,7 @@
 import android.content.Context;
 import android.platform.test.annotations.Presubmit;
 
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import org.junit.AfterClass;
 import org.junit.BeforeClass;
diff --git a/core/tests/coretests/src/android/view/TunnelModeEnabledListenerTest.java b/core/tests/coretests/src/android/view/TunnelModeEnabledListenerTest.java
index 65dd34f..9ab14af 100644
--- a/core/tests/coretests/src/android/view/TunnelModeEnabledListenerTest.java
+++ b/core/tests/coretests/src/android/view/TunnelModeEnabledListenerTest.java
@@ -21,16 +21,16 @@
 
 import android.platform.test.annotations.Presubmit;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.MediumTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicBoolean;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+
 @RunWith(AndroidJUnit4.class)
 @MediumTest
 @Presubmit
diff --git a/core/tests/coretests/src/android/view/ViewCaptureTest.java b/core/tests/coretests/src/android/view/ViewCaptureTest.java
index 218047c..9144cd6 100644
--- a/core/tests/coretests/src/android/view/ViewCaptureTest.java
+++ b/core/tests/coretests/src/android/view/ViewCaptureTest.java
@@ -26,9 +26,9 @@
 import android.view.ViewDebug.HardwareCanvasProvider;
 import android.view.ViewDebug.SoftwareCanvasProvider;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 import androidx.test.rule.ActivityTestRule;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.android.frameworks.coretests.R;
 
diff --git a/core/tests/coretests/src/android/view/ViewFrameRateTest.java b/core/tests/coretests/src/android/view/ViewFrameRateTest.java
index 62291d4..b8ff595 100644
--- a/core/tests/coretests/src/android/view/ViewFrameRateTest.java
+++ b/core/tests/coretests/src/android/view/ViewFrameRateTest.java
@@ -50,10 +50,10 @@
 import android.widget.ProgressBar;
 
 import androidx.test.annotation.UiThreadTest;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 import androidx.test.platform.app.InstrumentationRegistry;
 import androidx.test.rule.ActivityTestRule;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.android.frameworks.coretests.R;
 
@@ -278,7 +278,7 @@
     @RequiresFlagsEnabled({FLAG_VIEW_VELOCITY_API,
             FLAG_TOOLKIT_FRAME_RATE_VELOCITY_MAPPING_READ_ONLY,
             FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY})
-    public void lowVelocity80() throws Throwable {
+    public void lowVelocity60() throws Throwable {
         if (!ViewProperties.vrr_enabled().orElse(true)) {
             return;
         }
@@ -292,6 +292,31 @@
         mActivityRule.runOnUiThread(() -> {
             mMovingView.setFrameContentVelocity(1f);
             mMovingView.invalidate();
+            runAfterDraw(() -> assertEquals(60f, mViewRoot.getLastPreferredFrameRate(), 0f));
+        });
+        waitForAfterDraw();
+    }
+
+    @Test
+    @RequiresFlagsEnabled({FLAG_VIEW_VELOCITY_API,
+            FLAG_TOOLKIT_FRAME_RATE_VELOCITY_MAPPING_READ_ONLY,
+            FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY})
+    public void midVelocity80() throws Throwable {
+        if (!ViewProperties.vrr_enabled().orElse(true)) {
+            return;
+        }
+        mActivityRule.runOnUiThread(() -> {
+            ViewGroup.LayoutParams layoutParams = mMovingView.getLayoutParams();
+            layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT;
+            layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT;
+            mMovingView.setLayoutParams(layoutParams);
+        });
+        waitForFrameRateCategoryToSettle();
+        mActivityRule.runOnUiThread(() -> {
+            float midSpeed =
+                    200f * mMovingView.getContext().getResources().getDisplayMetrics().density;
+            mMovingView.setFrameContentVelocity(midSpeed);
+            mMovingView.invalidate();
             runAfterDraw(() -> assertEquals(80f, mViewRoot.getLastPreferredFrameRate(), 0f));
         });
         waitForAfterDraw();
@@ -321,7 +346,7 @@
             frameLayout.setFrameContentVelocity(1f);
             mMovingView.offsetTopAndBottom(100);
             frameLayout.invalidate();
-            runAfterDraw(() -> assertEquals(80f, mViewRoot.getLastPreferredFrameRate(), 0f));
+            runAfterDraw(() -> assertEquals(60f, mViewRoot.getLastPreferredFrameRate(), 0f));
         });
         waitForAfterDraw();
     }
@@ -590,7 +615,7 @@
             runAfterDraw(() -> {
                 assertEquals(FRAME_RATE_CATEGORY_LOW,
                         mViewRoot.getLastPreferredFrameRateCategory());
-                assertEquals(80f, mViewRoot.getLastPreferredFrameRate());
+                assertEquals(60f, mViewRoot.getLastPreferredFrameRate());
             });
         });
         waitForAfterDraw();
diff --git a/core/tests/coretests/src/android/view/ViewGroupTest.java b/core/tests/coretests/src/android/view/ViewGroupTest.java
index bce3f3e..ae3ad36 100644
--- a/core/tests/coretests/src/android/view/ViewGroupTest.java
+++ b/core/tests/coretests/src/android/view/ViewGroupTest.java
@@ -19,6 +19,7 @@
 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.doReturn;
@@ -29,11 +30,16 @@
 import android.content.Context;
 import android.graphics.Region;
 import android.platform.test.annotations.Presubmit;
+import android.view.autofill.AutofillId;
 
 import androidx.test.filters.SmallTest;
 
 import org.junit.Test;
 
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
 
 /**
  * Test basic functions of ViewGroup.
@@ -182,6 +188,31 @@
         assertRegionContainPoint(3 /* x */, r, false /* contain */); // Outside of bounds
     }
 
+    @Test
+    public void testfindAutofillableViewsByTraversal() {
+        final Context context = getInstrumentation().getContext();
+        final TestView viewGroup = new TestView(context, 200 /* right */);
+
+        // viewA and viewC are autofillable. ViewB isn't.
+        final TestView viewA = spy(new AutofillableTestView(context, 100 /* right */));
+        final TestView viewB = spy(new NonAutofillableTestView(context, 200 /* right */));
+        final TestView viewC = spy(new AutofillableTestView(context, 300 /* right */));
+
+        viewGroup.addView(viewA);
+        viewGroup.addView(viewB);
+        viewGroup.addView(viewC);
+
+        List<View> autofillableViews = new ArrayList<>();
+        viewGroup.findAutofillableViewsByTraversal(autofillableViews);
+
+        verify(viewA).findAutofillableViewsByTraversal(autofillableViews);
+        verify(viewB).findAutofillableViewsByTraversal(autofillableViews);
+        verify(viewC).findAutofillableViewsByTraversal(autofillableViews);
+
+        assertEquals("Size of autofillable views", 2, autofillableViews.size());
+        assertTrue(autofillableViews.containsAll(Arrays.asList(viewA, viewC)));
+    }
+
     private static void getUnobscuredTouchableRegion(Region outRegion, View view) {
         outRegion.set(view.getLeft(), view.getTop(), view.getRight(), view.getBottom());
         final ViewParent parent = view.getParent();
@@ -210,4 +241,29 @@
             // We don't layout this view.
         }
     }
+
+    public static class AutofillableTestView extends TestView {
+        AutofillableTestView(Context context, int right) {
+            super(context, right);
+            setImportantForAutofill(IMPORTANT_FOR_AUTOFILL_YES);
+            // Need to set autofill id in such a way that the view is considered part of activity.
+            setAutofillId(new AutofillId(LAST_APP_AUTOFILL_ID + 5));
+        }
+
+        @Override
+        public @AutofillType int getAutofillType() {
+            return AUTOFILL_TYPE_TEXT;
+        }
+    }
+
+    public static class NonAutofillableTestView extends TestView {
+        NonAutofillableTestView(Context context, int right) {
+            super(context, right);
+        }
+
+        @Override
+        public @AutofillType int getAutofillType() {
+            return AUTOFILL_TYPE_NONE;
+        }
+    }
 }
diff --git a/core/tests/coretests/src/android/view/ViewGroupTransientViewTest.java b/core/tests/coretests/src/android/view/ViewGroupTransientViewTest.java
index 54524b2..f5c71f8 100644
--- a/core/tests/coretests/src/android/view/ViewGroupTransientViewTest.java
+++ b/core/tests/coretests/src/android/view/ViewGroupTransientViewTest.java
@@ -25,9 +25,9 @@
 import android.widget.FrameLayout;
 
 import androidx.test.annotation.UiThreadTest;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.MediumTest;
 import androidx.test.rule.ActivityTestRule;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Rule;
diff --git a/core/tests/coretests/src/android/view/ViewInvalidateTest.java b/core/tests/coretests/src/android/view/ViewInvalidateTest.java
index c25a2deb..d4181d3 100644
--- a/core/tests/coretests/src/android/view/ViewInvalidateTest.java
+++ b/core/tests/coretests/src/android/view/ViewInvalidateTest.java
@@ -31,9 +31,9 @@
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.annotation.UiThreadTest;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.LargeTest;
 import androidx.test.rule.ActivityTestRule;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.After;
 import org.junit.Assert;
diff --git a/core/tests/coretests/src/android/view/WindowInsetsTest.java b/core/tests/coretests/src/android/view/WindowInsetsTest.java
index ba1204b..c86045d 100644
--- a/core/tests/coretests/src/android/view/WindowInsetsTest.java
+++ b/core/tests/coretests/src/android/view/WindowInsetsTest.java
@@ -43,14 +43,14 @@
     @Test
     public void systemWindowInsets_afterConsuming_isConsumed() {
         assertTrue(new WindowInsets(WindowInsets.createCompatTypeMap(new Rect(1, 2, 3, 4)), null,
-                null, false, 0, 0, null, null, null, null,
+                null, false, 0, false, 0, null, null, null, null,
                 WindowInsets.Type.systemBars(), false, null, null, 0, 0)
                 .consumeSystemWindowInsets().isConsumed());
     }
 
     @Test
     public void multiNullConstructor_isConsumed() {
-        assertTrue(new WindowInsets(null, null, null, false, 0, 0, null, null, null, null,
+        assertTrue(new WindowInsets(null, null, null, false, 0, false, 0, null, null, null, null,
                 WindowInsets.Type.systemBars(), false, null, null, 0, 0).isConsumed());
     }
 
@@ -67,7 +67,7 @@
         WindowInsets.assignCompatInsets(maxInsets, new Rect(0, 10, 0, 0));
         WindowInsets.assignCompatInsets(insets, new Rect(0, 0, 0, 0));
         WindowInsets windowInsets = new WindowInsets(insets, maxInsets, visible, false, 0,
-                0, null, null, null, DisplayShape.NONE, systemBars(),
+                false, 0, null, null, null, DisplayShape.NONE, systemBars(),
                 true /* compatIgnoreVisibility */, null, null, 0, 0);
         assertEquals(Insets.of(0, 10, 0, 0), windowInsets.getSystemWindowInsets());
     }
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java
index dd8cc6e..e5ad561 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java
@@ -42,8 +42,8 @@
 import android.view.Display;
 import android.view.View;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.LargeTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.google.common.base.Throwables;
 
@@ -1053,6 +1053,28 @@
         assertFalse(mAccessibilityCache.isNodeInCache(childInfo));
     }
 
+    @Test
+    public void getEventSourceClassName_windowStateChangedThenRemoved() {
+        final String sourceActivityClassName = "com.example.SomeActivity";
+        final AccessibilityEvent windowStateChangedEvent = new AccessibilityEvent(
+                AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
+        final View mockView = getMockViewWithA11yAndWindowIds(PARENT_VIEW_ID, WINDOW_ID_1);
+        windowStateChangedEvent.setSource(mockView);
+        windowStateChangedEvent.setClassName(sourceActivityClassName);
+
+        mAccessibilityCache.onAccessibilityEvent(windowStateChangedEvent);
+        assertEquals(mAccessibilityCache.getEventSourceClassName(WINDOW_ID_1),
+                sourceActivityClassName);
+
+        final AccessibilityEvent windowRemovedEvent = new AccessibilityEvent(
+                AccessibilityEvent.TYPE_WINDOWS_CHANGED);
+        windowRemovedEvent.setWindowChanges(AccessibilityEvent.WINDOWS_CHANGE_REMOVED);
+        windowRemovedEvent.setSource(mockView);
+
+        mAccessibilityCache.onAccessibilityEvent(windowRemovedEvent);
+        assertNull(mAccessibilityCache.getEventSourceClassName(WINDOW_ID_1));
+    }
+
     private AccessibilityWindowInfo obtainAccessibilityWindowInfo(int windowId, int layer) {
         AccessibilityWindowInfo windowInfo = AccessibilityWindowInfo.obtain();
         windowInfo.setId(windowId);
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityEventTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityEventTest.java
index ddc27aa..3b8f66a 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityEventTest.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityEventTest.java
@@ -23,7 +23,7 @@
 import android.os.Parcel;
 import android.view.Display;
 
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityInteractionClientTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityInteractionClientTest.java
index 3e061d2..eb482f2e 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityInteractionClientTest.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityInteractionClientTest.java
@@ -26,8 +26,8 @@
 import android.os.Bundle;
 import android.os.RemoteException;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.LargeTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import libcore.util.EmptyArray;
 
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityManagerTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityManagerTest.java
index ce36ee0..82e3427 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityManagerTest.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityManagerTest.java
@@ -55,7 +55,7 @@
 
 import androidx.annotation.NonNull;
 import androidx.test.InstrumentationRegistry;
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import com.android.internal.R;
 import com.android.internal.accessibility.common.ShortcutConstants;
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java
index 2d82d23..a5137bdf 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java
@@ -28,8 +28,8 @@
 import android.util.ArraySet;
 import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.LargeTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.android.internal.util.CollectionUtils;
 
@@ -46,7 +46,7 @@
     // The number of fields tested in the corresponding CTS AccessibilityNodeInfoTest:
     // See fullyPopulateAccessibilityNodeInfo, assertEqualsAccessibilityNodeInfo,
     // and assertAccessibilityNodeInfoCleared in that class.
-    private static final int NUM_MARSHALLED_PROPERTIES = 44;
+    private static final int NUM_MARSHALLED_PROPERTIES = 43;
 
     /**
      * The number of properties that are purposely not marshalled
diff --git a/core/tests/coretests/src/android/view/autofill/AutofillStateFingerprintTest.java b/core/tests/coretests/src/android/view/autofill/AutofillStateFingerprintTest.java
new file mode 100644
index 0000000..7cbfc40
--- /dev/null
+++ b/core/tests/coretests/src/android/view/autofill/AutofillStateFingerprintTest.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.autofill;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import android.content.Context;
+import android.text.InputType;
+import android.view.View;
+import android.view.inputmethod.EditorInfo;
+import android.widget.TextView;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class AutofillStateFingerprintTest {
+
+    private static final Context sContext = ApplicationProvider.getApplicationContext();
+
+    private static final int MAGIC_AUTOFILL_NUMBER = 1000;
+
+    private AutofillStateFingerprint mAutofillStateFingerprint =
+            AutofillStateFingerprint.createInstance();
+
+    @Test
+    public void testSameFingerprintsForTextView() throws Exception {
+        TextView tv = new TextView(sContext);
+        tv.setHint("Password");
+        tv.setSingleLine(true);
+        tv.setInputType(InputType.TYPE_TEXT_VARIATION_PASSWORD);
+        tv.setImeOptions(EditorInfo.IME_FLAG_NAVIGATE_NEXT);
+        fillViewProperties(tv);
+
+        // Create a copy Text View, and compare both id's
+        View tvCopy = copySelectiveViewAttributes(tv);
+        assertIdsEqual(tv, tvCopy);
+    }
+
+    @Test
+    public void testDifferentFingerprintsForTextViewWithDifferentHint() throws Exception {
+        TextView tv = new TextView(sContext);
+        tv.setHint("Password");
+        tv.setSingleLine(true);
+        tv.setInputType(InputType.TYPE_TEXT_VARIATION_PASSWORD);
+        tv.setImeOptions(EditorInfo.IME_FLAG_NAVIGATE_NEXT);
+        fillViewProperties(tv);
+
+        TextView tvCopy = (TextView) copySelectiveViewAttributes(tv);
+        tvCopy.setHint("what a useless different hint");
+        assertIdsNotEqual(tv, tvCopy);
+    }
+
+    @Test
+    public void testSameFingerprintsForNonTextView() throws Exception {
+        View v = new View(sContext);
+        fillViewProperties(v);
+
+        // Create a copy Text View, and compare both id's
+        View copy = copySelectiveViewAttributes(v);
+        assertIdsEqual(v, copy);
+    }
+
+    @Test
+    public void testDifferentFingerprintsForNonTextViewWithDifferentVisibility() throws Exception {
+        View v = new View(sContext);
+        fillViewProperties(v);
+
+        View copy = copySelectiveViewAttributes(v);
+        copy.setVisibility(View.GONE);
+        assertIdsNotEqual(v, copy);
+    }
+
+    private void assertIdsEqual(View v1, View v2) {
+        assertEquals(mAutofillStateFingerprint.getEphemeralFingerprintId(v1, 0),
+                mAutofillStateFingerprint.getEphemeralFingerprintId(v2, 0));
+    }
+
+    private void assertIdsNotEqual(View v1, View v2) {
+        assertNotEquals(mAutofillStateFingerprint.getEphemeralFingerprintId(v1, 0),
+                mAutofillStateFingerprint.getEphemeralFingerprintId(v2, 0));
+    }
+
+    private void fillViewProperties(View view) {
+        // Fill in relevant view properties
+        view.setContentDescription("ContentDesc");
+        view.setTooltipText("TooltipText");
+        view.setAutofillHints(new String[] {"password"});
+        view.setVisibility(View.VISIBLE);
+        view.setLeft(20);
+        view.setRight(200);
+        view.setTop(20);
+        view.setBottom(200);
+        view.setPadding(0, 1, 2, 3);
+    }
+
+    // Only copy interesting view attributes, particularly the view attributes that are critical
+    // for calculating fingerprint. Keep Autofill Id different.
+    private View copySelectiveViewAttributes(View view) {
+        View copy;
+        if (view instanceof TextView) {
+            copy = new TextView(sContext);
+            copySelectiveTextViewAttributes((TextView) view, (TextView) copy);
+        } else {
+            copy = new View(sContext) {
+                public @AutofillType int getAutofillType() {
+                    return view.getAutofillType();
+                }
+            };
+        }
+        // Copy over interested view properties.
+        // Keep the order same as with the tested code for easier clarity.
+        copy.setVisibility(view.getVisibility());
+        copy.setAutofillHints(view.getAutofillHints());
+        copy.setContentDescription(view.getContentDescription());
+        copy.setTooltip(view.getTooltipText());
+
+        copy.setRight(view.getRight());
+        copy.setLeft(view.getLeft());
+        copy.setTop(view.getTop());
+        copy.setBottom(view.getBottom());
+        copy.setPadding(view.getPaddingLeft(), view.getPaddingTop(),
+                view.getPaddingRight(), view.getPaddingBottom());
+
+        // DO not copy over autofill id
+        AutofillId newId = new AutofillId(view.getAutofillId().getViewId() + MAGIC_AUTOFILL_NUMBER);
+        copy.setAutofillId(newId);
+        return copy;
+    }
+
+    private void copySelectiveTextViewAttributes(TextView fromView, TextView toView) {
+        toView.setInputType(fromView.getInputType());
+        toView.setHint(fromView.getHint());
+        toView.setSingleLine(fromView.isSingleLine());
+        toView.setImeOptions(fromView.getImeOptions());
+    }
+}
diff --git a/core/tests/coretests/src/android/view/textclassifier/ConversationActionTest.java b/core/tests/coretests/src/android/view/textclassifier/ConversationActionTest.java
index e1b403f..c5e0ebd 100644
--- a/core/tests/coretests/src/android/view/textclassifier/ConversationActionTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/ConversationActionTest.java
@@ -26,8 +26,8 @@
 import android.os.Bundle;
 
 import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/view/textclassifier/SelectionEventTest.java b/core/tests/coretests/src/android/view/textclassifier/SelectionEventTest.java
index 46e3a4c..1c26da7 100644
--- a/core/tests/coretests/src/android/view/textclassifier/SelectionEventTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/SelectionEventTest.java
@@ -20,8 +20,8 @@
 
 import android.os.Parcel;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/view/textclassifier/SystemTextClassifierMetadataTest.java b/core/tests/coretests/src/android/view/textclassifier/SystemTextClassifierMetadataTest.java
index e4cfc53..4635e9b 100644
--- a/core/tests/coretests/src/android/view/textclassifier/SystemTextClassifierMetadataTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/SystemTextClassifierMetadataTest.java
@@ -20,11 +20,10 @@
 
 import static org.testng.Assert.assertThrows;
 
-
 import android.os.Parcel;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassificationConstantsTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassificationConstantsTest.java
index 20a8768..07fe12f 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassificationConstantsTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassificationConstantsTest.java
@@ -20,8 +20,8 @@
 
 import android.provider.DeviceConfig;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassificationTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassificationTest.java
index 8225afc..2ed016c 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassificationTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassificationTest.java
@@ -38,8 +38,8 @@
 import android.view.View;
 
 import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassifierEventTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassifierEventTest.java
index 11eb567..d92da6e 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassifierEventTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassifierEventTest.java
@@ -19,8 +19,8 @@
 
 import android.annotation.Nullable;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassifierUtilsTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassifierUtilsTest.java
index 011866d..ec46426 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassifierUtilsTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassifierUtilsTest.java
@@ -20,8 +20,8 @@
 
 import static org.testng.Assert.assertThrows;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextLanguageTest.java b/core/tests/coretests/src/android/view/textclassifier/TextLanguageTest.java
index 31f8029..de6f1d2 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextLanguageTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextLanguageTest.java
@@ -24,8 +24,8 @@
 import android.os.Bundle;
 import android.os.Parcel;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextLinksTest.java b/core/tests/coretests/src/android/view/textclassifier/TextLinksTest.java
index 4f0b44b..bd1f7e1 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextLinksTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextLinksTest.java
@@ -25,8 +25,8 @@
 import android.os.Parcel;
 import android.util.ArrayMap;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextSelectionTest.java b/core/tests/coretests/src/android/view/textclassifier/TextSelectionTest.java
index 14c077c..61e6738 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextSelectionTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextSelectionTest.java
@@ -36,8 +36,8 @@
 import android.os.Parcel;
 
 import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/widget/AbsListViewFunctionalTest.java b/core/tests/coretests/src/android/widget/AbsListViewFunctionalTest.java
index ceea6ca..e36a7f1 100644
--- a/core/tests/coretests/src/android/widget/AbsListViewFunctionalTest.java
+++ b/core/tests/coretests/src/android/widget/AbsListViewFunctionalTest.java
@@ -28,9 +28,9 @@
 import android.util.AttributeSet;
 import android.util.PollingCheck;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.MediumTest;
 import androidx.test.rule.ActivityTestRule;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.android.compatibility.common.util.WidgetTestUtils;
 import com.android.frameworks.coretests.R;
diff --git a/core/tests/coretests/src/android/widget/AbsSeekBarTest.java b/core/tests/coretests/src/android/widget/AbsSeekBarTest.java
index ccd873d..451fcf4 100644
--- a/core/tests/coretests/src/android/widget/AbsSeekBarTest.java
+++ b/core/tests/coretests/src/android/widget/AbsSeekBarTest.java
@@ -31,9 +31,9 @@
 import android.graphics.drawable.shapes.RectShape;
 import android.platform.test.annotations.Presubmit;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 import androidx.test.platform.app.InstrumentationRegistry;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/widget/AppWidgetHostViewTest.java b/core/tests/coretests/src/android/widget/AppWidgetHostViewTest.java
index 6edc162..6a8b426 100644
--- a/core/tests/coretests/src/android/widget/AppWidgetHostViewTest.java
+++ b/core/tests/coretests/src/android/widget/AppWidgetHostViewTest.java
@@ -28,8 +28,8 @@
 import android.view.ViewGroup.OnHierarchyChangeListener;
 
 import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.android.frameworks.coretests.R;
 
diff --git a/core/tests/coretests/src/android/widget/DateTimeViewTest.java b/core/tests/coretests/src/android/widget/DateTimeViewTest.java
index 14b48ed..a8fd913 100644
--- a/core/tests/coretests/src/android/widget/DateTimeViewTest.java
+++ b/core/tests/coretests/src/android/widget/DateTimeViewTest.java
@@ -21,8 +21,8 @@
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.annotation.UiThreadTest;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Assert;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/widget/EditorCursorDragTest.java b/core/tests/coretests/src/android/widget/EditorCursorDragTest.java
index c37a34a..8f52d23 100644
--- a/core/tests/coretests/src/android/widget/EditorCursorDragTest.java
+++ b/core/tests/coretests/src/android/widget/EditorCursorDragTest.java
@@ -48,10 +48,10 @@
 import android.view.View;
 
 import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.MediumTest;
 import androidx.test.filters.Suppress;
 import androidx.test.rule.ActivityTestRule;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.android.frameworks.coretests.R;
 
diff --git a/core/tests/coretests/src/android/widget/EditorCursorTest.java b/core/tests/coretests/src/android/widget/EditorCursorTest.java
index 585c601..9513aa5 100644
--- a/core/tests/coretests/src/android/widget/EditorCursorTest.java
+++ b/core/tests/coretests/src/android/widget/EditorCursorTest.java
@@ -34,9 +34,9 @@
 import android.app.Instrumentation;
 
 import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.MediumTest;
 import androidx.test.rule.ActivityTestRule;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.android.frameworks.coretests.R;
 
diff --git a/core/tests/coretests/src/android/widget/HorizontalScrollViewFunctionalTest.java b/core/tests/coretests/src/android/widget/HorizontalScrollViewFunctionalTest.java
index 5d62f1c..981f1db 100644
--- a/core/tests/coretests/src/android/widget/HorizontalScrollViewFunctionalTest.java
+++ b/core/tests/coretests/src/android/widget/HorizontalScrollViewFunctionalTest.java
@@ -29,9 +29,9 @@
 import android.util.AttributeSet;
 import android.util.PollingCheck;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.MediumTest;
 import androidx.test.rule.ActivityTestRule;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.android.frameworks.coretests.R;
 
diff --git a/core/tests/coretests/src/android/widget/NumberPickerTest.java b/core/tests/coretests/src/android/widget/NumberPickerTest.java
index cab7c89..386972a 100644
--- a/core/tests/coretests/src/android/widget/NumberPickerTest.java
+++ b/core/tests/coretests/src/android/widget/NumberPickerTest.java
@@ -24,9 +24,9 @@
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.accessibility.AccessibilityNodeProvider;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 import androidx.test.platform.app.InstrumentationRegistry;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/widget/ProgressBarTest.java b/core/tests/coretests/src/android/widget/ProgressBarTest.java
index 5b92471..fa6dd31 100644
--- a/core/tests/coretests/src/android/widget/ProgressBarTest.java
+++ b/core/tests/coretests/src/android/widget/ProgressBarTest.java
@@ -26,9 +26,9 @@
 import android.view.View;
 import android.view.accessibility.AccessibilityNodeInfo;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 import androidx.test.platform.app.InstrumentationRegistry;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.After;
 import org.junit.Before;
diff --git a/core/tests/coretests/src/android/widget/RemoteViewsAdapterTest.java b/core/tests/coretests/src/android/widget/RemoteViewsAdapterTest.java
index 534420e..b021b62 100644
--- a/core/tests/coretests/src/android/widget/RemoteViewsAdapterTest.java
+++ b/core/tests/coretests/src/android/widget/RemoteViewsAdapterTest.java
@@ -41,8 +41,8 @@
 import android.view.View;
 
 import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.android.frameworks.coretests.R;
 import com.android.internal.widget.IRemoteViewsFactory;
diff --git a/core/tests/coretests/src/android/widget/RemoteViewsProtoTest.java b/core/tests/coretests/src/android/widget/RemoteViewsProtoTest.java
index 8e9ba7b..7c14032 100644
--- a/core/tests/coretests/src/android/widget/RemoteViewsProtoTest.java
+++ b/core/tests/coretests/src/android/widget/RemoteViewsProtoTest.java
@@ -26,8 +26,8 @@
 import android.view.View;
 
 import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.android.frameworks.coretests.R;
 
diff --git a/core/tests/coretests/src/android/widget/RemoteViewsSerializersTest.kt b/core/tests/coretests/src/android/widget/RemoteViewsSerializersTest.kt
new file mode 100644
index 0000000..44d10d3
--- /dev/null
+++ b/core/tests/coretests/src/android/widget/RemoteViewsSerializersTest.kt
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.widget
+
+import android.content.Context
+import android.content.res.ColorStateList
+import android.graphics.Bitmap
+import android.graphics.BlendMode
+import android.graphics.Color
+import android.graphics.drawable.BitmapDrawable
+import android.graphics.drawable.Icon
+import android.util.proto.ProtoInputStream
+import android.util.proto.ProtoOutputStream
+import android.widget.RemoteViewsSerializers.createIconFromProto
+import android.widget.RemoteViewsSerializers.writeIconToProto
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.frameworks.coretests.R
+import com.google.common.truth.Truth.assertThat
+import java.io.ByteArrayOutputStream
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class RemoteViewsSerializersTest {
+    private val context = ApplicationProvider.getApplicationContext<Context>()
+
+    /**
+     * Based on android.graphics.drawable.IconTest#testParcel
+     */
+    @Test
+    fun testWriteIconToProto() {
+        val bitmap = (context.getDrawable(R.drawable.landscape) as BitmapDrawable).bitmap
+        val bitmapData = ByteArrayOutputStream().let {
+            bitmap.compress(Bitmap.CompressFormat.PNG, 100, it)
+            it.toByteArray()
+        }
+
+        for (icon in listOf(
+            Icon.createWithBitmap(bitmap),
+            Icon.createWithAdaptiveBitmap(bitmap),
+            Icon.createWithData(bitmapData, 0, bitmapData.size),
+            Icon.createWithResource(context, R.drawable.landscape),
+            Icon.createWithContentUri("content://com.example.myapp/my_icon"),
+            Icon.createWithAdaptiveBitmapContentUri("content://com.example.myapp/my_icon"),
+        )) {
+            icon.tintList = ColorStateList.valueOf(Color.RED)
+            icon.tintBlendMode = BlendMode.SRC_OVER
+            val bytes = ProtoOutputStream().let {
+                writeIconToProto(it, context.resources, icon)
+                it.bytes
+            }
+
+            val copy = ProtoInputStream(bytes).let {
+                createIconFromProto(it).apply(context.resources)
+            }
+            assertThat(copy.type).isEqualTo(icon.type)
+            assertThat(copy.tintBlendMode).isEqualTo(icon.tintBlendMode)
+            assertThat(equalColorStateLists(copy.tintList, icon.tintList)).isTrue()
+
+            when (icon.type) {
+                Icon.TYPE_DATA, Icon.TYPE_URI, Icon.TYPE_URI_ADAPTIVE_BITMAP,
+                Icon.TYPE_RESOURCE -> {
+                    assertThat(copy.sameAs(icon)).isTrue()
+                }
+
+                Icon.TYPE_BITMAP, Icon.TYPE_ADAPTIVE_BITMAP -> {
+                    assertThat(copy.bitmap.sameAs(icon.bitmap)).isTrue()
+                }
+            }
+        }
+    }
+}
+
+fun equalColorStateLists(a: ColorStateList?, b: ColorStateList?): Boolean {
+    if (a == null && b == null) return true
+    return a != null && b != null &&
+            a.colors.contentEquals(b.colors) &&
+            a.states.foldIndexed(true) { i, acc, it -> acc && it.contentEquals(b.states[i])}
+}
diff --git a/core/tests/coretests/src/android/widget/RemoteViewsTest.java b/core/tests/coretests/src/android/widget/RemoteViewsTest.java
index c8ea374..0621d82 100644
--- a/core/tests/coretests/src/android/widget/RemoteViewsTest.java
+++ b/core/tests/coretests/src/android/widget/RemoteViewsTest.java
@@ -34,9 +34,6 @@
 import android.appwidget.AppWidgetHostView;
 import android.content.Context;
 import android.content.Intent;
-import android.graphics.Bitmap;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
 import android.graphics.drawable.Icon;
 import android.net.Uri;
 import android.os.AsyncTask;
@@ -53,8 +50,8 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.android.frameworks.coretests.R;
 
diff --git a/core/tests/coretests/src/android/widget/ScrollViewFunctionalTest.java b/core/tests/coretests/src/android/widget/ScrollViewFunctionalTest.java
index 6d0bab5..3dfb3f5 100644
--- a/core/tests/coretests/src/android/widget/ScrollViewFunctionalTest.java
+++ b/core/tests/coretests/src/android/widget/ScrollViewFunctionalTest.java
@@ -30,9 +30,9 @@
 import android.util.AttributeSet;
 import android.util.PollingCheck;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.MediumTest;
 import androidx.test.rule.ActivityTestRule;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.android.frameworks.coretests.R;
 
diff --git a/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java b/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java
index a0cfb31..3de4893 100644
--- a/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java
@@ -47,9 +47,9 @@
 import android.view.textclassifier.TextClassificationManager;
 import android.view.textclassifier.TextClassifier;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.MediumTest;
 import androidx.test.rule.ActivityTestRule;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.android.frameworks.coretests.R;
 
diff --git a/core/tests/coretests/src/android/widget/TextViewActivityTest.java b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
index 9cf2e42..3eb7d9a 100644
--- a/core/tests/coretests/src/android/widget/TextViewActivityTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
@@ -88,10 +88,10 @@
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.espresso.action.EspressoKey;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.MediumTest;
 import androidx.test.filters.Suppress;
 import androidx.test.rule.ActivityTestRule;
-import androidx.test.runner.AndroidJUnit4;
 import androidx.test.uiautomator.By;
 import androidx.test.uiautomator.UiDevice;
 import androidx.test.uiautomator.Until;
diff --git a/core/tests/coretests/src/android/widget/TextViewContextMenuTest.java b/core/tests/coretests/src/android/widget/TextViewContextMenuTest.java
index 777246b..12f8c9c 100644
--- a/core/tests/coretests/src/android/widget/TextViewContextMenuTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewContextMenuTest.java
@@ -41,9 +41,9 @@
 import android.view.textclassifier.TextClassification;
 
 import androidx.test.annotation.UiThreadTest;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.MediumTest;
 import androidx.test.rule.ActivityTestRule;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.android.frameworks.coretests.R;
 
diff --git a/core/tests/coretests/src/android/widget/TextViewPerformanceTest.java b/core/tests/coretests/src/android/widget/TextViewPerformanceTest.java
index a769ea4..ff99db3 100644
--- a/core/tests/coretests/src/android/widget/TextViewPerformanceTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewPerformanceTest.java
@@ -25,9 +25,9 @@
 import android.view.ViewGroup;
 
 import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.MediumTest;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/widget/TextViewProcessTextTest.java b/core/tests/coretests/src/android/widget/TextViewProcessTextTest.java
index 91266f0..8043a66 100644
--- a/core/tests/coretests/src/android/widget/TextViewProcessTextTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewProcessTextTest.java
@@ -26,9 +26,9 @@
 import android.widget.TextView.BufferType;
 
 import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.MediumTest;
 import androidx.test.rule.ActivityTestRule;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Rule;
diff --git a/core/tests/coretests/src/android/widget/TextViewReceiveContentTest.java b/core/tests/coretests/src/android/widget/TextViewReceiveContentTest.java
index 98bf87b..b61d868 100644
--- a/core/tests/coretests/src/android/widget/TextViewReceiveContentTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewReceiveContentTest.java
@@ -47,9 +47,9 @@
 import android.view.inputmethod.InputContentInfo;
 
 import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.MediumTest;
 import androidx.test.rule.ActivityTestRule;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.android.frameworks.coretests.R;
 
diff --git a/core/tests/coretests/src/android/widget/TextViewTest.java b/core/tests/coretests/src/android/widget/TextViewTest.java
index f5582d0..769a440 100644
--- a/core/tests/coretests/src/android/widget/TextViewTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewTest.java
@@ -49,9 +49,9 @@
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.annotation.UiThreadTest;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.MediumTest;
 import androidx.test.rule.ActivityTestRule;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Rule;
diff --git a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutChooserActivityTest.java b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutChooserActivityTest.java
index 9f5ed29..37625e2 100644
--- a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutChooserActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutChooserActivityTest.java
@@ -67,8 +67,8 @@
 
 import androidx.lifecycle.Lifecycle;
 import androidx.test.core.app.ActivityScenario;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.platform.app.InstrumentationRegistry;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.android.internal.R;
 import com.android.internal.accessibility.dialog.AccessibilityShortcutChooserActivity;
diff --git a/core/tests/coretests/src/com/android/internal/accessibility/dialog/AccessibilityTargetTest.java b/core/tests/coretests/src/com/android/internal/accessibility/dialog/AccessibilityTargetTest.java
index f01ac6f..8608f6c 100644
--- a/core/tests/coretests/src/com/android/internal/accessibility/dialog/AccessibilityTargetTest.java
+++ b/core/tests/coretests/src/com/android/internal/accessibility/dialog/AccessibilityTargetTest.java
@@ -27,7 +27,7 @@
 import android.platform.test.flag.junit.SetFlagsRule;
 import android.provider.Flags;
 
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import com.android.internal.accessibility.common.ShortcutConstants;
 
diff --git a/core/tests/coretests/src/com/android/internal/accessibility/dialog/InvisibleToggleAccessibilityServiceTargetTest.java b/core/tests/coretests/src/com/android/internal/accessibility/dialog/InvisibleToggleAccessibilityServiceTargetTest.java
index 9cac312..5339d91 100644
--- a/core/tests/coretests/src/com/android/internal/accessibility/dialog/InvisibleToggleAccessibilityServiceTargetTest.java
+++ b/core/tests/coretests/src/com/android/internal/accessibility/dialog/InvisibleToggleAccessibilityServiceTargetTest.java
@@ -41,8 +41,8 @@
 import android.view.accessibility.Flags;
 import android.view.accessibility.IAccessibilityManager;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.platform.app.InstrumentationRegistry;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.android.internal.accessibility.TestUtils;
 import com.android.internal.accessibility.common.ShortcutConstants;
diff --git a/core/tests/coretests/src/com/android/internal/accessibility/util/AccessibilityUtilsTest.java b/core/tests/coretests/src/com/android/internal/accessibility/util/AccessibilityUtilsTest.java
index 58ab92a..f37ec9b 100644
--- a/core/tests/coretests/src/com/android/internal/accessibility/util/AccessibilityUtilsTest.java
+++ b/core/tests/coretests/src/com/android/internal/accessibility/util/AccessibilityUtilsTest.java
@@ -33,7 +33,7 @@
 import android.text.SpannableString;
 import android.text.style.LocaleSpan;
 
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/com/android/internal/app/BilingualSuggestedLocaleAdapterTest.java b/core/tests/coretests/src/com/android/internal/app/BilingualSuggestedLocaleAdapterTest.java
index e8aac11..d577e0f 100644
--- a/core/tests/coretests/src/com/android/internal/app/BilingualSuggestedLocaleAdapterTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/BilingualSuggestedLocaleAdapterTest.java
@@ -18,7 +18,7 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import com.android.internal.app.LocaleHelper.LocaleInfoComparator;
 import com.android.internal.app.LocaleStore.LocaleInfo;
diff --git a/core/tests/coretests/src/com/android/internal/app/IntentForwarderActivityTest.java b/core/tests/coretests/src/com/android/internal/app/IntentForwarderActivityTest.java
index dbabcea..73a64fc 100644
--- a/core/tests/coretests/src/com/android/internal/app/IntentForwarderActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/IntentForwarderActivityTest.java
@@ -57,8 +57,8 @@
 import android.provider.Settings;
 
 import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.rule.ActivityTestRule;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.android.internal.R;
 import com.android.internal.logging.MetricsLogger;
diff --git a/core/tests/coretests/src/com/android/internal/app/NoOpResolverComparatorTest.java b/core/tests/coretests/src/com/android/internal/app/NoOpResolverComparatorTest.java
index 22c319c..0ed0a81 100644
--- a/core/tests/coretests/src/com/android/internal/app/NoOpResolverComparatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/NoOpResolverComparatorTest.java
@@ -22,7 +22,7 @@
 import android.os.UserHandle;
 
 import androidx.test.InstrumentationRegistry;
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import com.android.internal.app.ResolverActivity.ResolvedComponentInfo;
 
diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
index ff28055..d21ab44 100644
--- a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
@@ -57,8 +57,8 @@
 import androidx.test.InstrumentationRegistry;
 import androidx.test.espresso.Espresso;
 import androidx.test.espresso.NoMatchingViewException;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.rule.ActivityTestRule;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.android.internal.R;
 import com.android.internal.app.ResolverActivity.ResolvedComponentInfo;
diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverAppPredictorCallbackTest.java b/core/tests/coretests/src/com/android/internal/app/ResolverAppPredictorCallbackTest.java
index 4aca854..bde9bdb 100644
--- a/core/tests/coretests/src/com/android/internal/app/ResolverAppPredictorCallbackTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ResolverAppPredictorCallbackTest.java
@@ -21,7 +21,7 @@
 import android.app.prediction.AppTargetId;
 import android.os.UserHandle;
 
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverListControllerTest.java b/core/tests/coretests/src/com/android/internal/app/ResolverListControllerTest.java
index 8f6cee3..90f5c24 100644
--- a/core/tests/coretests/src/com/android/internal/app/ResolverListControllerTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ResolverListControllerTest.java
@@ -45,7 +45,7 @@
 import android.util.ArrayMap;
 
 import androidx.test.InstrumentationRegistry;
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import com.android.internal.app.ResolverActivity.ResolvedComponentInfo;
 
diff --git a/core/tests/coretests/src/com/android/internal/content/res/OverlayConfigParserTest.java b/core/tests/coretests/src/com/android/internal/content/res/OverlayConfigParserTest.java
index 4eccbe5..a466caf 100644
--- a/core/tests/coretests/src/com/android/internal/content/res/OverlayConfigParserTest.java
+++ b/core/tests/coretests/src/com/android/internal/content/res/OverlayConfigParserTest.java
@@ -22,7 +22,7 @@
 
 import android.platform.test.annotations.Presubmit;
 
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import com.android.internal.content.om.OverlayConfigParser;
 
diff --git a/core/tests/coretests/src/com/android/internal/content/res/OverlayConfigTest.java b/core/tests/coretests/src/com/android/internal/content/res/OverlayConfigTest.java
index a0e9947..43cff8d 100644
--- a/core/tests/coretests/src/com/android/internal/content/res/OverlayConfigTest.java
+++ b/core/tests/coretests/src/com/android/internal/content/res/OverlayConfigTest.java
@@ -28,7 +28,7 @@
 import android.platform.test.annotations.Presubmit;
 
 import androidx.test.InstrumentationRegistry;
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import com.android.frameworks.coretests.R;
 import com.android.internal.content.om.OverlayConfig;
diff --git a/core/tests/coretests/src/com/android/internal/infra/AndroidFutureTest.java b/core/tests/coretests/src/com/android/internal/infra/AndroidFutureTest.java
index 3a27225..178e93a 100644
--- a/core/tests/coretests/src/com/android/internal/infra/AndroidFutureTest.java
+++ b/core/tests/coretests/src/com/android/internal/infra/AndroidFutureTest.java
@@ -22,7 +22,7 @@
 
 import android.os.Parcel;
 
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/com/android/internal/infra/ServiceConnectorTest.java b/core/tests/coretests/src/com/android/internal/infra/ServiceConnectorTest.java
index 725dcf3..3d1b565 100644
--- a/core/tests/coretests/src/com/android/internal/infra/ServiceConnectorTest.java
+++ b/core/tests/coretests/src/com/android/internal/infra/ServiceConnectorTest.java
@@ -29,8 +29,8 @@
 import android.os.UserHandle;
 
 import androidx.annotation.NonNull;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.platform.app.InstrumentationRegistry;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.android.frameworks.coretests.aidl.ITestServiceConnectorService;
 import com.android.internal.infra.ServiceConnectorTest.CapturingServiceLifecycleCallbacks.ServiceLifeCycleEvent;
diff --git a/core/tests/coretests/src/com/android/internal/logging/MetricsLoggerTest.java b/core/tests/coretests/src/com/android/internal/logging/MetricsLoggerTest.java
index 7054cc0..b86cb4a 100644
--- a/core/tests/coretests/src/com/android/internal/logging/MetricsLoggerTest.java
+++ b/core/tests/coretests/src/com/android/internal/logging/MetricsLoggerTest.java
@@ -20,8 +20,8 @@
 
 import android.metrics.LogMaker;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.android.internal.logging.nano.MetricsProto;
 import com.android.internal.logging.testing.FakeMetricsLogger;
diff --git a/core/tests/coretests/src/com/android/internal/logging/UiEventLoggerTest.java b/core/tests/coretests/src/com/android/internal/logging/UiEventLoggerTest.java
index 7840f71..fc28627 100644
--- a/core/tests/coretests/src/com/android/internal/logging/UiEventLoggerTest.java
+++ b/core/tests/coretests/src/com/android/internal/logging/UiEventLoggerTest.java
@@ -18,8 +18,8 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.android.internal.logging.testing.UiEventLoggerFake;
 
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryInputSuspendTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryInputSuspendTest.java
index 8d825e4..7928e6a 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryInputSuspendTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryInputSuspendTest.java
@@ -26,8 +26,8 @@
 import android.os.Build;
 import android.os.ConditionVariable;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.platform.app.InstrumentationRegistry;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.android.compatibility.common.util.SystemUtil;
 
diff --git a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
index b70f290..5f8ab28 100644
--- a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
@@ -35,8 +35,8 @@
 import android.util.ArrayMap;
 import android.util.SparseArray;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.android.internal.os.BinderInternal.CallSession;
 
diff --git a/core/tests/coretests/src/com/android/internal/os/BinderDeathDispatcherTest.java b/core/tests/coretests/src/com/android/internal/os/BinderDeathDispatcherTest.java
index 27398ea..66de3d7 100644
--- a/core/tests/coretests/src/com/android/internal/os/BinderDeathDispatcherTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BinderDeathDispatcherTest.java
@@ -34,8 +34,8 @@
 import android.platform.test.annotations.DisabledOnRavenwood;
 import android.platform.test.ravenwood.RavenwoodRule;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Rule;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/com/android/internal/os/BinderHeavyHitterTest.java b/core/tests/coretests/src/com/android/internal/os/BinderHeavyHitterTest.java
index a1b80d2..13d72137 100644
--- a/core/tests/coretests/src/com/android/internal/os/BinderHeavyHitterTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BinderHeavyHitterTest.java
@@ -23,7 +23,7 @@
 
 import android.os.Binder;
 
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import com.android.internal.os.BinderCallHeavyHitterWatcher.HeavyHitterContainer;
 
diff --git a/core/tests/coretests/src/com/android/internal/os/BinderLatencyBucketsTest.java b/core/tests/coretests/src/com/android/internal/os/BinderLatencyBucketsTest.java
index b2054f1..c6baea3 100644
--- a/core/tests/coretests/src/com/android/internal/os/BinderLatencyBucketsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BinderLatencyBucketsTest.java
@@ -22,8 +22,8 @@
 
 import android.platform.test.annotations.Presubmit;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/com/android/internal/os/BinderLatencyObserverTest.java b/core/tests/coretests/src/com/android/internal/os/BinderLatencyObserverTest.java
index 31b55e6..3355cc3 100644
--- a/core/tests/coretests/src/com/android/internal/os/BinderLatencyObserverTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BinderLatencyObserverTest.java
@@ -29,8 +29,8 @@
 import android.util.ArrayMap;
 import android.util.proto.ProtoOutputStream;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.android.internal.os.BinderInternal.CallSession;
 import com.android.internal.os.BinderLatencyObserver.LatencyDims;
diff --git a/core/tests/coretests/src/com/android/internal/os/BinderfsStatsReaderTest.java b/core/tests/coretests/src/com/android/internal/os/BinderfsStatsReaderTest.java
index 5f02f04..9acd991 100644
--- a/core/tests/coretests/src/com/android/internal/os/BinderfsStatsReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BinderfsStatsReaderTest.java
@@ -23,8 +23,8 @@
 import android.os.FileUtils;
 import android.util.IntArray;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.After;
 import org.junit.Before;
diff --git a/core/tests/coretests/src/com/android/internal/os/CpuScalingPolicyReaderTest.java b/core/tests/coretests/src/com/android/internal/os/CpuScalingPolicyReaderTest.java
index 2da3873..ca97472 100644
--- a/core/tests/coretests/src/com/android/internal/os/CpuScalingPolicyReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/CpuScalingPolicyReaderTest.java
@@ -20,7 +20,7 @@
 
 import android.os.FileUtils;
 
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/com/android/internal/os/DebugStoreTest.java b/core/tests/coretests/src/com/android/internal/os/DebugStoreTest.java
new file mode 100644
index 0000000..786c2fc
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/os/DebugStoreTest.java
@@ -0,0 +1,311 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.os;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyList;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.ServiceInfo;
+import android.platform.test.annotations.DisabledOnRavenwood;
+import android.platform.test.ravenwood.RavenwoodRule;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.List;
+
+/**
+ * Test class for {@link DebugStore}.
+ *
+ * To run it:
+ * atest FrameworksCoreTests:com.android.internal.os.DebugStoreTest
+ */
+@RunWith(AndroidJUnit4.class)
+@DisabledOnRavenwood(blockedBy = DebugStore.class)
+@SmallTest
+public class DebugStoreTest {
+    @Rule
+    public final RavenwoodRule mRavenwood = new RavenwoodRule();
+
+    @Mock
+    private DebugStore.DebugStoreNative mDebugStoreNativeMock;
+
+    @Captor
+    private ArgumentCaptor<List<String>> mListCaptor;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        DebugStore.setDebugStoreNative(mDebugStoreNativeMock);
+    }
+
+    @Test
+    public void testRecordServiceOnStart() {
+        Intent intent = new Intent();
+        intent.setAction("com.android.ACTION");
+        intent.setComponent(new ComponentName("com.android", "androidService"));
+        intent.setPackage("com.android");
+
+        when(mDebugStoreNativeMock.beginEvent(anyString(), anyList())).thenReturn(1L);
+
+        long eventId = DebugStore.recordServiceOnStart(1, 0, intent);
+
+        verify(mDebugStoreNativeMock).beginEvent(eq("SvcStart"), mListCaptor.capture());
+        List<String> capturedList = mListCaptor.getValue();
+        assertThat(capturedList).containsExactly(
+                "stId", "1",
+                "flg", "0",
+                "act", "com.android.ACTION",
+                "comp", "ComponentInfo{com.android/androidService}",
+                "pkg", "com.android"
+        ).inOrder();
+        assertThat(eventId).isEqualTo(1L);
+    }
+
+    @Test
+    public void testRecordServiceCreate() {
+        ServiceInfo serviceInfo = new ServiceInfo();
+        serviceInfo.name = "androidService";
+        serviceInfo.packageName = "com.android";
+
+        when(mDebugStoreNativeMock.beginEvent(anyString(), anyList())).thenReturn(2L);
+
+        long eventId = DebugStore.recordServiceCreate(serviceInfo);
+
+        verify(mDebugStoreNativeMock).beginEvent(eq("SvcCreate"), mListCaptor.capture());
+        List<String> capturedList = mListCaptor.getValue();
+        assertThat(capturedList).containsExactly(
+                "name", "androidService",
+                "pkg", "com.android"
+        ).inOrder();
+        assertThat(eventId).isEqualTo(2L);
+    }
+
+    @Test
+    public void testRecordServiceBind() {
+        Intent intent = new Intent();
+        intent.setAction("com.android.ACTION");
+        intent.setComponent(new ComponentName("com.android", "androidService"));
+        intent.setPackage("com.android");
+
+        when(mDebugStoreNativeMock.beginEvent(anyString(), anyList())).thenReturn(3L);
+
+        long eventId = DebugStore.recordServiceBind(true, intent);
+
+        verify(mDebugStoreNativeMock).beginEvent(eq("SvcBind"), mListCaptor.capture());
+        List<String> capturedList = mListCaptor.getValue();
+        assertThat(capturedList).containsExactly(
+                "rebind", "true",
+                "act", "com.android.ACTION",
+                "cmp", "ComponentInfo{com.android/androidService}",
+                "pkg", "com.android"
+        ).inOrder();
+        assertThat(eventId).isEqualTo(3L);
+    }
+
+    @Test
+    public void testRecordGoAsync() {
+        DebugStore.recordGoAsync("androidReceiver");
+
+        verify(mDebugStoreNativeMock).recordEvent(eq("GoAsync"), mListCaptor.capture());
+        List<String> capturedList = mListCaptor.getValue();
+        assertThat(capturedList).containsExactly(
+                "tname", Thread.currentThread().getName(),
+                "tid", String.valueOf(Thread.currentThread().getId()),
+                "rcv", "androidReceiver"
+        ).inOrder();
+    }
+
+    @Test
+    public void testRecordFinish() {
+        DebugStore.recordFinish("androidReceiver");
+
+        verify(mDebugStoreNativeMock).recordEvent(eq("Finish"), mListCaptor.capture());
+        List<String> capturedList = mListCaptor.getValue();
+        assertThat(capturedList).containsExactly(
+                "tname", Thread.currentThread().getName(),
+                "tid", String.valueOf(Thread.currentThread().getId()),
+                "rcv", "androidReceiver"
+        ).inOrder();
+    }
+
+    @Test
+    public void testRecordLongLooperMessage() {
+        DebugStore.recordLongLooperMessage(100, "androidHandler", 500L);
+
+        verify(mDebugStoreNativeMock).recordEvent(eq("LooperMsg"), mListCaptor.capture());
+        List<String> capturedList = mListCaptor.getValue();
+        assertThat(capturedList).containsExactly(
+                "code", "100",
+                "trgt", "androidHandler",
+                "elapsed", "500"
+        ).inOrder();
+    }
+
+    @Test
+    public void testRecordBroadcastHandleReceiver() {
+        Intent intent = new Intent();
+        intent.setAction("com.android.ACTION");
+        intent.setComponent(new ComponentName("com.android", "androidReceiver"));
+        intent.setPackage("com.android");
+
+        when(mDebugStoreNativeMock.beginEvent(anyString(), anyList())).thenReturn(4L);
+
+        long eventId = DebugStore.recordBroadcastHandleReceiver(intent);
+
+        verify(mDebugStoreNativeMock).beginEvent(eq("HandleReceiver"), mListCaptor.capture());
+        List<String> capturedList = mListCaptor.getValue();
+        assertThat(capturedList).containsExactly(
+                "tname", Thread.currentThread().getName(),
+                "tid", String.valueOf(Thread.currentThread().getId()),
+                "act", "com.android.ACTION",
+                "cmp", "ComponentInfo{com.android/androidReceiver}",
+                "pkg", "com.android"
+        ).inOrder();
+        assertThat(eventId).isEqualTo(4L);
+    }
+
+    @Test
+    public void testRecordEventEnd() {
+        DebugStore.recordEventEnd(1L);
+
+        verify(mDebugStoreNativeMock).endEvent(eq(1L), anyList());
+    }
+
+    @Test
+    public void testRecordServiceOnStartWithNullIntent() {
+        when(mDebugStoreNativeMock.beginEvent(anyString(), anyList())).thenReturn(5L);
+
+        long eventId = DebugStore.recordServiceOnStart(1, 0, null);
+
+        verify(mDebugStoreNativeMock).beginEvent(eq("SvcStart"), mListCaptor.capture());
+        List<String> capturedList = mListCaptor.getValue();
+        assertThat(capturedList).containsExactly(
+                "stId", "1",
+                "flg", "0",
+                "act", "null",
+                "comp", "null",
+                "pkg", "null"
+        ).inOrder();
+        assertThat(eventId).isEqualTo(5L);
+    }
+
+    @Test
+    public void testRecordServiceCreateWithNullServiceInfo() {
+        when(mDebugStoreNativeMock.beginEvent(anyString(), anyList())).thenReturn(6L);
+
+        long eventId = DebugStore.recordServiceCreate(null);
+
+        verify(mDebugStoreNativeMock).beginEvent(eq("SvcCreate"), mListCaptor.capture());
+        List<String> capturedList = mListCaptor.getValue();
+        assertThat(capturedList).containsExactly(
+                "name", "null",
+                "pkg", "null"
+        ).inOrder();
+        assertThat(eventId).isEqualTo(6L);
+    }
+
+    @Test
+    public void testRecordServiceBindWithNullIntent() {
+        when(mDebugStoreNativeMock.beginEvent(anyString(), anyList())).thenReturn(7L);
+
+        long eventId = DebugStore.recordServiceBind(false, null);
+
+        verify(mDebugStoreNativeMock).beginEvent(eq("SvcBind"), mListCaptor.capture());
+        List<String> capturedList = mListCaptor.getValue();
+        assertThat(capturedList).containsExactly(
+                "rebind", "false",
+                "act", "null",
+                "cmp", "null",
+                "pkg", "null"
+        ).inOrder();
+        assertThat(eventId).isEqualTo(7L);
+    }
+
+    @Test
+    public void testRecordBroadcastHandleReceiverWithNullIntent() {
+        when(mDebugStoreNativeMock.beginEvent(anyString(), anyList())).thenReturn(8L);
+
+        long eventId = DebugStore.recordBroadcastHandleReceiver(null);
+
+        verify(mDebugStoreNativeMock).beginEvent(eq("HandleReceiver"), mListCaptor.capture());
+        List<String> capturedList = mListCaptor.getValue();
+        assertThat(capturedList).containsExactly(
+                "tname", Thread.currentThread().getName(),
+                "tid", String.valueOf(Thread.currentThread().getId()),
+                "act", "null",
+                "cmp", "null",
+                "pkg", "null"
+        ).inOrder();
+        assertThat(eventId).isEqualTo(8L);
+    }
+
+    @Test
+    public void testRecordGoAsyncWithNullReceiverClassName() {
+        DebugStore.recordGoAsync(null);
+
+        verify(mDebugStoreNativeMock).recordEvent(eq("GoAsync"), mListCaptor.capture());
+        List<String> capturedList = mListCaptor.getValue();
+        assertThat(capturedList).containsExactly(
+                "tname", Thread.currentThread().getName(),
+                "tid", String.valueOf(Thread.currentThread().getId()),
+                "rcv", "null"
+        ).inOrder();
+    }
+
+    @Test
+    public void testRecordFinishWithNullReceiverClassName() {
+        DebugStore.recordFinish(null);
+
+        verify(mDebugStoreNativeMock).recordEvent(eq("Finish"), mListCaptor.capture());
+        List<String> capturedList = mListCaptor.getValue();
+        assertThat(capturedList).containsExactly(
+                "tname", Thread.currentThread().getName(),
+                "tid", String.valueOf(Thread.currentThread().getId()),
+                "rcv", "null"
+        ).inOrder();
+    }
+
+    @Test
+    public void testRecordLongLooperMessageWithNullTargetClass() {
+        DebugStore.recordLongLooperMessage(200, null, 1000L);
+
+        verify(mDebugStoreNativeMock).recordEvent(eq("LooperMsg"), mListCaptor.capture());
+        List<String> capturedList = mListCaptor.getValue();
+        assertThat(capturedList).containsExactly(
+                "code", "200",
+                "trgt", "null",
+                "elapsed", "1000"
+        ).inOrder();
+    }
+}
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuProcStringReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuProcStringReaderTest.java
index 1d8628d..8fd87c0 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelCpuProcStringReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuProcStringReaderTest.java
@@ -28,8 +28,8 @@
 import android.platform.test.ravenwood.RavenwoodRule;
 
 import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.After;
 import org.junit.Before;
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderDiffTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderDiffTest.java
index a57a400..78cf65c 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderDiffTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderDiffTest.java
@@ -28,8 +28,8 @@
 import android.platform.test.annotations.Presubmit;
 import android.platform.test.ravenwood.RavenwoodRule;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.After;
 import org.junit.Before;
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java
index 7eac2a3..c3d4b83 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java
@@ -30,8 +30,8 @@
 import android.platform.test.ravenwood.RavenwoodRule;
 
 import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.After;
 import org.junit.Before;
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuUidBpfMapReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidBpfMapReaderTest.java
index 610e6ae..b75ad7f 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelCpuUidBpfMapReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidBpfMapReaderTest.java
@@ -28,16 +28,15 @@
 import android.util.SparseArray;
 
 import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Rule;
-import org.junit.runner.RunWith;
 
 import com.android.internal.os.KernelCpuUidBpfMapReader.BpfMapIterator;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 
 import java.io.PrintWriter;
 import java.io.StringWriter;
@@ -50,7 +49,6 @@
 import java.util.concurrent.Executors;
 import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.TimeUnit;
-import java.util.stream.IntStream;
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuUidUserSysTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidUserSysTimeReaderTest.java
index 6507226..864e198 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelCpuUidUserSysTimeReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidUserSysTimeReaderTest.java
@@ -28,8 +28,8 @@
 import android.util.SparseArray;
 
 import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidUserSysTimeReader;
 
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelMemoryBandwidthStatsTest.java b/core/tests/coretests/src/com/android/internal/os/KernelMemoryBandwidthStatsTest.java
index ad5186e..a74f339 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelMemoryBandwidthStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelMemoryBandwidthStatsTest.java
@@ -22,8 +22,8 @@
 import android.platform.test.ravenwood.RavenwoodRule;
 import android.util.LongSparseLongArray;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Rule;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelSingleProcessCpuThreadReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelSingleProcessCpuThreadReaderTest.java
index f42d26d..cdfef25 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelSingleProcessCpuThreadReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelSingleProcessCpuThreadReaderTest.java
@@ -22,8 +22,8 @@
 import android.platform.test.annotations.IgnoreUnderRavenwood;
 import android.platform.test.ravenwood.RavenwoodRule;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Rule;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/com/android/internal/os/LoggingPrintStreamTest.java b/core/tests/coretests/src/com/android/internal/os/LoggingPrintStreamTest.java
index 632dce0..0a0ad39 100644
--- a/core/tests/coretests/src/com/android/internal/os/LoggingPrintStreamTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/LoggingPrintStreamTest.java
@@ -19,7 +19,7 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/com/android/internal/os/LongArrayMultiStateCounterTest.java b/core/tests/coretests/src/com/android/internal/os/LongArrayMultiStateCounterTest.java
index fa5d72a..b86dc58 100644
--- a/core/tests/coretests/src/com/android/internal/os/LongArrayMultiStateCounterTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/LongArrayMultiStateCounterTest.java
@@ -24,8 +24,8 @@
 import android.os.Parcel;
 import android.platform.test.ravenwood.RavenwoodRule;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Rule;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/com/android/internal/os/LongMultiStateCounterTest.java b/core/tests/coretests/src/com/android/internal/os/LongMultiStateCounterTest.java
index 78ef92b..b8f7903 100644
--- a/core/tests/coretests/src/com/android/internal/os/LongMultiStateCounterTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/LongMultiStateCounterTest.java
@@ -24,8 +24,8 @@
 import android.os.Parcel;
 import android.platform.test.ravenwood.RavenwoodRule;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Rule;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java b/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java
index dfb5cc3..8bb0ee7 100644
--- a/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java
@@ -25,8 +25,8 @@
 import android.platform.test.annotations.Presubmit;
 import android.platform.test.ravenwood.RavenwoodRule;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.After;
 import org.junit.Assert;
diff --git a/core/tests/coretests/src/com/android/internal/os/MonotonicClockTest.java b/core/tests/coretests/src/com/android/internal/os/MonotonicClockTest.java
index 7ffc7b2..cff57f4 100644
--- a/core/tests/coretests/src/com/android/internal/os/MonotonicClockTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/MonotonicClockTest.java
@@ -20,8 +20,8 @@
 
 import android.platform.test.ravenwood.RavenwoodRule;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Rule;
diff --git a/core/tests/coretests/src/com/android/internal/os/PowerProfileTest.java b/core/tests/coretests/src/com/android/internal/os/PowerProfileTest.java
index 951fa98..30dd50e 100644
--- a/core/tests/coretests/src/com/android/internal/os/PowerProfileTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/PowerProfileTest.java
@@ -33,8 +33,8 @@
 import android.platform.test.ravenwood.RavenwoodRule;
 import android.util.Xml;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.android.internal.power.ModemPowerProfile;
 import com.android.internal.util.XmlUtils;
diff --git a/core/tests/coretests/src/com/android/internal/os/PowerStatsTest.java b/core/tests/coretests/src/com/android/internal/os/PowerStatsTest.java
index 4846ed27..046f747 100644
--- a/core/tests/coretests/src/com/android/internal/os/PowerStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/PowerStatsTest.java
@@ -26,8 +26,8 @@
 import android.util.SparseArray;
 import android.util.Xml;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.android.modules.utils.TypedXmlPullParser;
 import com.android.modules.utils.TypedXmlSerializer;
diff --git a/core/tests/coretests/src/com/android/internal/os/ProcLocksReaderTest.java b/core/tests/coretests/src/com/android/internal/os/ProcLocksReaderTest.java
index f61fc7c..a05a44c5 100644
--- a/core/tests/coretests/src/com/android/internal/os/ProcLocksReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/ProcLocksReaderTest.java
@@ -21,8 +21,8 @@
 import android.os.FileUtils;
 import android.util.IntArray;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.After;
 import org.junit.Before;
diff --git a/core/tests/coretests/src/com/android/internal/os/ProcStatsUtilTest.java b/core/tests/coretests/src/com/android/internal/os/ProcStatsUtilTest.java
index 3e4f34d..7c7e2ed 100644
--- a/core/tests/coretests/src/com/android/internal/os/ProcStatsUtilTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/ProcStatsUtilTest.java
@@ -20,8 +20,8 @@
 
 import android.os.FileUtils;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.After;
 import org.junit.Before;
diff --git a/core/tests/coretests/src/com/android/internal/os/ProcTimeInStateReaderTest.java b/core/tests/coretests/src/com/android/internal/os/ProcTimeInStateReaderTest.java
index a706350..93dd09d 100644
--- a/core/tests/coretests/src/com/android/internal/os/ProcTimeInStateReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/ProcTimeInStateReaderTest.java
@@ -24,8 +24,8 @@
 import android.platform.test.annotations.IgnoreUnderRavenwood;
 import android.platform.test.ravenwood.RavenwoodRule;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.After;
 import org.junit.Before;
diff --git a/core/tests/coretests/src/com/android/internal/os/StoragedUidIoStatsReaderTest.java b/core/tests/coretests/src/com/android/internal/os/StoragedUidIoStatsReaderTest.java
index cc6c4e8..26f5955 100644
--- a/core/tests/coretests/src/com/android/internal/os/StoragedUidIoStatsReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/StoragedUidIoStatsReaderTest.java
@@ -23,8 +23,8 @@
 import android.platform.test.annotations.DisabledOnRavenwood;
 import android.platform.test.ravenwood.RavenwoodRule;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.After;
 import org.junit.Before;
diff --git a/core/tests/coretests/src/com/android/internal/policy/DecorContextTest.java b/core/tests/coretests/src/com/android/internal/policy/DecorContextTest.java
index 7f4e9ad..2f3b7f9 100644
--- a/core/tests/coretests/src/com/android/internal/policy/DecorContextTest.java
+++ b/core/tests/coretests/src/com/android/internal/policy/DecorContextTest.java
@@ -36,9 +36,9 @@
 import android.view.WindowManagerImpl;
 
 import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 import androidx.test.rule.ActivityTestRule;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Rule;
diff --git a/core/tests/coretests/src/com/android/internal/policy/PhoneWindowTest.java b/core/tests/coretests/src/com/android/internal/policy/PhoneWindowTest.java
index 4921e4a..e037f2a 100644
--- a/core/tests/coretests/src/com/android/internal/policy/PhoneWindowTest.java
+++ b/core/tests/coretests/src/com/android/internal/policy/PhoneWindowTest.java
@@ -44,8 +44,8 @@
 import android.view.WindowManager;
 
 import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.android.frameworks.coretests.R;
 
diff --git a/core/tests/coretests/src/com/android/internal/ravenwood/RavenwoodEnvironmentTest.java b/core/tests/coretests/src/com/android/internal/ravenwood/RavenwoodEnvironmentTest.java
index d1ef61b..d1c0668 100644
--- a/core/tests/coretests/src/com/android/internal/ravenwood/RavenwoodEnvironmentTest.java
+++ b/core/tests/coretests/src/com/android/internal/ravenwood/RavenwoodEnvironmentTest.java
@@ -19,7 +19,7 @@
 
 import android.platform.test.ravenwood.RavenwoodRule;
 
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import org.junit.Rule;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/com/android/internal/security/VerityUtilsTest.java b/core/tests/coretests/src/com/android/internal/security/VerityUtilsTest.java
index a978e3b..7b9ea55 100644
--- a/core/tests/coretests/src/com/android/internal/security/VerityUtilsTest.java
+++ b/core/tests/coretests/src/com/android/internal/security/VerityUtilsTest.java
@@ -21,9 +21,9 @@
 
 import android.platform.test.annotations.Presubmit;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 import androidx.test.platform.app.InstrumentationRegistry;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.android.frameworks.coretests.R;
 
diff --git a/core/tests/coretests/src/com/android/internal/statusbar/RegisterStatusBarResultTest.java b/core/tests/coretests/src/com/android/internal/statusbar/RegisterStatusBarResultTest.java
index af2a2bb..c733bae 100644
--- a/core/tests/coretests/src/com/android/internal/statusbar/RegisterStatusBarResultTest.java
+++ b/core/tests/coretests/src/com/android/internal/statusbar/RegisterStatusBarResultTest.java
@@ -21,7 +21,6 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import android.graphics.Rect;
-import android.os.Binder;
 import android.os.Parcel;
 import android.os.UserHandle;
 import android.util.ArrayMap;
@@ -63,7 +62,6 @@
                 0x10 /* imeBackDisposition */,
                 false /* showImeSwitcher */,
                 0x20 /* disabledFlags2 */,
-                new Binder() /* imeToken */,
                 true /* navbarColorManagedByIme */,
                 BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE,
                 WindowInsets.Type.defaultVisible(),
@@ -85,7 +83,6 @@
         assertThat(copy.mImeBackDisposition).isEqualTo(original.mImeBackDisposition);
         assertThat(copy.mShowImeSwitcher).isEqualTo(original.mShowImeSwitcher);
         assertThat(copy.mDisabledFlags2).isEqualTo(original.mDisabledFlags2);
-        assertThat(copy.mImeToken).isSameInstanceAs(original.mImeToken);
         assertThat(copy.mNavbarColorManagedByIme).isEqualTo(original.mNavbarColorManagedByIme);
         assertThat(copy.mBehavior).isEqualTo(original.mBehavior);
         assertThat(copy.mRequestedVisibleTypes).isEqualTo(original.mRequestedVisibleTypes);
diff --git a/core/tests/coretests/src/com/android/internal/util/BitUtilsTest.java b/core/tests/coretests/src/com/android/internal/util/BitUtilsTest.java
index fdde36a..fdba811 100644
--- a/core/tests/coretests/src/com/android/internal/util/BitUtilsTest.java
+++ b/core/tests/coretests/src/com/android/internal/util/BitUtilsTest.java
@@ -30,8 +30,8 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/com/android/internal/util/CollectionUtilsTest.java b/core/tests/coretests/src/com/android/internal/util/CollectionUtilsTest.java
index ac954d6a..32c2d63 100644
--- a/core/tests/coretests/src/com/android/internal/util/CollectionUtilsTest.java
+++ b/core/tests/coretests/src/com/android/internal/util/CollectionUtilsTest.java
@@ -22,7 +22,7 @@
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/com/android/internal/util/ContrastColorUtilTest.java b/core/tests/coretests/src/com/android/internal/util/ContrastColorUtilTest.java
index e6ebfef..aa59afe 100644
--- a/core/tests/coretests/src/com/android/internal/util/ContrastColorUtilTest.java
+++ b/core/tests/coretests/src/com/android/internal/util/ContrastColorUtilTest.java
@@ -33,8 +33,8 @@
 import android.text.style.TextAppearanceSpan;
 
 import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.android.internal.R;
 
diff --git a/core/tests/coretests/src/com/android/internal/util/DumpUtilsTest.java b/core/tests/coretests/src/com/android/internal/util/DumpUtilsTest.java
index d2d3c13..7bd062a 100644
--- a/core/tests/coretests/src/com/android/internal/util/DumpUtilsTest.java
+++ b/core/tests/coretests/src/com/android/internal/util/DumpUtilsTest.java
@@ -31,7 +31,7 @@
 import android.content.ComponentName;
 import android.util.SparseArray;
 
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/com/android/internal/util/DumpableContainerImplTest.java b/core/tests/coretests/src/com/android/internal/util/DumpableContainerImplTest.java
index 61d4e3d..9259181f 100644
--- a/core/tests/coretests/src/com/android/internal/util/DumpableContainerImplTest.java
+++ b/core/tests/coretests/src/com/android/internal/util/DumpableContainerImplTest.java
@@ -19,7 +19,7 @@
 
 import android.util.Dumpable;
 
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import com.android.internal.util.dump.DumpableContainerImpl;
 
diff --git a/core/tests/coretests/src/com/android/internal/util/FakeLatencyTrackerTest.java b/core/tests/coretests/src/com/android/internal/util/FakeLatencyTrackerTest.java
index 6bd67ea..aee352b 100644
--- a/core/tests/coretests/src/com/android/internal/util/FakeLatencyTrackerTest.java
+++ b/core/tests/coretests/src/com/android/internal/util/FakeLatencyTrackerTest.java
@@ -28,7 +28,7 @@
 import android.platform.test.ravenwood.RavenwoodRule;
 import android.provider.DeviceConfig;
 
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import org.junit.After;
 import org.junit.Before;
diff --git a/core/tests/coretests/src/com/android/internal/util/FastMathTest.java b/core/tests/coretests/src/com/android/internal/util/FastMathTest.java
index dd26334..bedcf4c 100644
--- a/core/tests/coretests/src/com/android/internal/util/FastMathTest.java
+++ b/core/tests/coretests/src/com/android/internal/util/FastMathTest.java
@@ -18,7 +18,7 @@
 
 import static org.junit.Assert.assertEquals;
 
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/com/android/internal/util/GrowingArrayUtilsTest.java b/core/tests/coretests/src/com/android/internal/util/GrowingArrayUtilsTest.java
index 8456161..a0eb058 100644
--- a/core/tests/coretests/src/com/android/internal/util/GrowingArrayUtilsTest.java
+++ b/core/tests/coretests/src/com/android/internal/util/GrowingArrayUtilsTest.java
@@ -23,7 +23,7 @@
 
 import android.util.EmptyArray;
 
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/com/android/internal/util/HexDumpTest.java b/core/tests/coretests/src/com/android/internal/util/HexDumpTest.java
index dcffa1c..9adf607 100644
--- a/core/tests/coretests/src/com/android/internal/util/HexDumpTest.java
+++ b/core/tests/coretests/src/com/android/internal/util/HexDumpTest.java
@@ -22,7 +22,7 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.fail;
 
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/com/android/internal/util/IntPairTest.java b/core/tests/coretests/src/com/android/internal/util/IntPairTest.java
index af6503f..527be8f 100644
--- a/core/tests/coretests/src/com/android/internal/util/IntPairTest.java
+++ b/core/tests/coretests/src/com/android/internal/util/IntPairTest.java
@@ -18,7 +18,7 @@
 
 import static org.junit.Assert.assertEquals;
 
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/com/android/internal/util/LatencyTrackerTest.java b/core/tests/coretests/src/com/android/internal/util/LatencyTrackerTest.java
index 010f724..ce265a3 100644
--- a/core/tests/coretests/src/com/android/internal/util/LatencyTrackerTest.java
+++ b/core/tests/coretests/src/com/android/internal/util/LatencyTrackerTest.java
@@ -29,7 +29,7 @@
 import android.platform.test.ravenwood.RavenwoodRule;
 import android.provider.DeviceConfig;
 
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import com.android.internal.util.LatencyTracker.ActionProperties;
 
diff --git a/core/tests/coretests/src/com/android/internal/util/LineBreakBufferedWriterTest.java b/core/tests/coretests/src/com/android/internal/util/LineBreakBufferedWriterTest.java
index e6418fa..93262f0 100644
--- a/core/tests/coretests/src/com/android/internal/util/LineBreakBufferedWriterTest.java
+++ b/core/tests/coretests/src/com/android/internal/util/LineBreakBufferedWriterTest.java
@@ -18,7 +18,7 @@
 
 import static org.junit.Assert.assertEquals;
 
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/com/android/internal/util/ParseUtilsTest.java b/core/tests/coretests/src/com/android/internal/util/ParseUtilsTest.java
index d24cbfe..b22014e 100644
--- a/core/tests/coretests/src/com/android/internal/util/ParseUtilsTest.java
+++ b/core/tests/coretests/src/com/android/internal/util/ParseUtilsTest.java
@@ -18,7 +18,7 @@
 
 import static org.junit.Assert.assertEquals;
 
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/com/android/internal/util/ProgressReporterTest.java b/core/tests/coretests/src/com/android/internal/util/ProgressReporterTest.java
index 0d21335..e0d5499 100644
--- a/core/tests/coretests/src/com/android/internal/util/ProgressReporterTest.java
+++ b/core/tests/coretests/src/com/android/internal/util/ProgressReporterTest.java
@@ -21,7 +21,7 @@
 import android.platform.test.annotations.IgnoreUnderRavenwood;
 import android.platform.test.ravenwood.RavenwoodRule;
 
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Rule;
diff --git a/core/tests/coretests/src/com/android/internal/util/RingBufferTest.java b/core/tests/coretests/src/com/android/internal/util/RingBufferTest.java
index d7a100a..4497770 100644
--- a/core/tests/coretests/src/com/android/internal/util/RingBufferTest.java
+++ b/core/tests/coretests/src/com/android/internal/util/RingBufferTest.java
@@ -20,8 +20,8 @@
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.fail;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/com/android/internal/util/SizedInputStreamTest.java b/core/tests/coretests/src/com/android/internal/util/SizedInputStreamTest.java
index efef7ff..dbd6fc1 100644
--- a/core/tests/coretests/src/com/android/internal/util/SizedInputStreamTest.java
+++ b/core/tests/coretests/src/com/android/internal/util/SizedInputStreamTest.java
@@ -18,7 +18,7 @@
 
 import static org.junit.Assert.assertEquals;
 
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/com/android/internal/util/TokenBucketTest.java b/core/tests/coretests/src/com/android/internal/util/TokenBucketTest.java
index ef579fe..43ee3c5 100644
--- a/core/tests/coretests/src/com/android/internal/util/TokenBucketTest.java
+++ b/core/tests/coretests/src/com/android/internal/util/TokenBucketTest.java
@@ -24,7 +24,7 @@
 import android.os.SystemClock;
 import android.text.format.DateUtils;
 
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/com/android/internal/widget/ActionBarOverlayLayoutTest.java b/core/tests/coretests/src/com/android/internal/widget/ActionBarOverlayLayoutTest.java
index 2b8adcb..ad6fe8d 100644
--- a/core/tests/coretests/src/com/android/internal/widget/ActionBarOverlayLayoutTest.java
+++ b/core/tests/coretests/src/com/android/internal/widget/ActionBarOverlayLayoutTest.java
@@ -40,8 +40,8 @@
 import android.widget.Toolbar;
 
 import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/com/android/internal/widget/LockPatternUtilsTest.java b/core/tests/coretests/src/com/android/internal/widget/LockPatternUtilsTest.java
index 92a7d8e..00b4f46 100644
--- a/core/tests/coretests/src/com/android/internal/widget/LockPatternUtilsTest.java
+++ b/core/tests/coretests/src/com/android/internal/widget/LockPatternUtilsTest.java
@@ -19,10 +19,10 @@
 import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_MANAGED;
 import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
 
+import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_TRUSTAGENT_EXPIRED;
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED;
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT;
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN;
-import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_TRUSTAGENT_EXPIRED;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -54,8 +54,8 @@
 import android.test.mock.MockContentResolver;
 
 import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.android.internal.util.test.FakeSettingsProvider;
 
diff --git a/core/tests/coretests/src/com/android/internal/widget/NotificationOptimizedLinearLayoutComparisonTest.java b/core/tests/coretests/src/com/android/internal/widget/NotificationOptimizedLinearLayoutComparisonTest.java
index bf9221a..548911b 100644
--- a/core/tests/coretests/src/com/android/internal/widget/NotificationOptimizedLinearLayoutComparisonTest.java
+++ b/core/tests/coretests/src/com/android/internal/widget/NotificationOptimizedLinearLayoutComparisonTest.java
@@ -35,8 +35,8 @@
 import android.widget.flags.Flags;
 
 import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.LargeTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.google.common.truth.Expect;
 
diff --git a/core/tests/resourceflaggingtests/Android.bp b/core/tests/resourceflaggingtests/Android.bp
index e8bb710..dd86094 100644
--- a/core/tests/resourceflaggingtests/Android.bp
+++ b/core/tests/resourceflaggingtests/Android.bp
@@ -34,12 +34,24 @@
 }
 
 genrule {
+    name: "resource-flagging-test-app-resources-compile2",
+    tools: ["aapt2"],
+    srcs: [
+        "flagged_resources_res/values/bools2.xml",
+    ],
+    out: ["values_bools2.arsc.flat"],
+    cmd: "$(location aapt2) compile $(in) -o $(genDir) " +
+        "--feature-flags test.package.falseFlag:ro=false,test.package.trueFlag:ro=true",
+}
+
+genrule {
     name: "resource-flagging-test-app-apk",
     tools: ["aapt2"],
     // The first input file in the list must be the manifest
     srcs: [
         "TestAppAndroidManifest.xml",
         ":resource-flagging-test-app-resources-compile",
+        ":resource-flagging-test-app-resources-compile2",
     ],
     out: ["resapp.apk"],
     cmd: "$(location aapt2) link -o $(out) --manifest $(in)",
diff --git a/core/tests/resourceflaggingtests/flagged_resources_res/values/bools.xml b/core/tests/resourceflaggingtests/flagged_resources_res/values/bools.xml
index f4defd9..8d01465 100644
--- a/core/tests/resourceflaggingtests/flagged_resources_res/values/bools.xml
+++ b/core/tests/resourceflaggingtests/flagged_resources_res/values/bools.xml
@@ -5,4 +5,6 @@
 
     <bool name="res2">false</bool>
     <bool name="res2" android:featureFlag="test.package.trueFlag">true</bool>
+
+    <bool name="res3">false</bool>
 </resources>
\ No newline at end of file
diff --git a/core/tests/resourceflaggingtests/flagged_resources_res/values/bools2.xml b/core/tests/resourceflaggingtests/flagged_resources_res/values/bools2.xml
new file mode 100644
index 0000000..e7563aa
--- /dev/null
+++ b/core/tests/resourceflaggingtests/flagged_resources_res/values/bools2.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+    <bool name="res3" android:featureFlag="test.package.trueFlag">true</bool>
+</resources>
\ No newline at end of file
diff --git a/core/tests/resourceflaggingtests/src/com/android/resourceflaggingtests/ResourceFlaggingTest.java b/core/tests/resourceflaggingtests/src/com/android/resourceflaggingtests/ResourceFlaggingTest.java
index a0cbe3c..ad8542e 100644
--- a/core/tests/resourceflaggingtests/src/com/android/resourceflaggingtests/ResourceFlaggingTest.java
+++ b/core/tests/resourceflaggingtests/src/com/android/resourceflaggingtests/ResourceFlaggingTest.java
@@ -63,6 +63,11 @@
         assertThat(getBoolean("res2")).isTrue();
     }
 
+    @Test
+    public void testFlagEnabledDifferentCompilationUnit() {
+        assertThat(getBoolean("res3")).isTrue();
+    }
+
     private boolean getBoolean(String name) {
         int resId = mResources.getIdentifier(name, "bool", "com.android.intenal.flaggedresources");
         assertThat(resId).isNotEqualTo(0);
diff --git a/core/tests/vibrator/Android.bp b/core/tests/vibrator/Android.bp
index 3ebe150..920ab59 100644
--- a/core/tests/vibrator/Android.bp
+++ b/core/tests/vibrator/Android.bp
@@ -18,6 +18,7 @@
         "androidx.test.ext.junit",
         "androidx.test.runner",
         "androidx.test.rules",
+        "flag-junit",
         "mockito-target-minus-junit4",
         "truth",
         "testng",
diff --git a/core/tests/vibrator/src/android/os/VibrationEffectTest.java b/core/tests/vibrator/src/android/os/VibrationEffectTest.java
index e875875..bd3d944 100644
--- a/core/tests/vibrator/src/android/os/VibrationEffectTest.java
+++ b/core/tests/vibrator/src/android/os/VibrationEffectTest.java
@@ -20,6 +20,8 @@
 import static android.os.VibrationEffect.VibrationParameter.targetAmplitude;
 import static android.os.VibrationEffect.VibrationParameter.targetFrequency;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertNotNull;
@@ -29,6 +31,7 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
+import static org.testng.Assert.assertNotEquals;
 import static org.testng.Assert.assertThrows;
 
 import android.content.ContentInterface;
@@ -38,8 +41,12 @@
 import android.hardware.vibrator.IVibrator;
 import android.net.Uri;
 import android.os.VibrationEffect.Composition.UnreachableAfterRepeatingIndefinitelyException;
+import android.os.vibrator.Flags;
+import android.os.vibrator.PrebakedSegment;
 import android.os.vibrator.PrimitiveSegment;
 import android.os.vibrator.StepSegment;
+import android.os.vibrator.VibrationEffectSegment;
+import android.platform.test.annotations.RequiresFlagsEnabled;
 
 import com.android.internal.R;
 
@@ -284,10 +291,13 @@
     }
 
     @Test
-    public void computeLegacyPattern_notPatternPased() {
-        VibrationEffect effect = VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK);
-
-        assertNull(effect.computeCreateWaveformOffOnTimingsOrNull());
+    public void computeLegacyPattern_notPatternBased() {
+        assertNull(VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK)
+                .computeCreateWaveformOffOnTimingsOrNull());
+        if (Flags.vendorVibrationEffects()) {
+            assertNull(VibrationEffect.createVendorEffect(createNonEmptyBundle())
+                    .computeCreateWaveformOffOnTimingsOrNull());
+        }
     }
 
     @Test
@@ -472,6 +482,18 @@
     }
 
     @Test
+    @RequiresFlagsEnabled(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
+    public void testValidateVendorEffect() {
+        PersistableBundle vendorData = new PersistableBundle();
+        vendorData.putInt("key", 1);
+        VibrationEffect.createVendorEffect(vendorData).validate();
+
+        PersistableBundle emptyData = new PersistableBundle();
+        assertThrows(IllegalArgumentException.class,
+                () -> VibrationEffect.createVendorEffect(emptyData).validate());
+    }
+
+    @Test
     public void testValidateWaveform() {
         VibrationEffect.createWaveform(TEST_TIMINGS, TEST_AMPLITUDES, -1).validate();
         VibrationEffect.createWaveform(new long[]{10, 10}, new int[] {0, 0}, -1).validate();
@@ -634,16 +656,16 @@
 
     @Test
     public void testResolveOneShot() {
-        VibrationEffect.Composed resolved = DEFAULT_ONE_SHOT.resolve(51);
-        assertEquals(0.2f, ((StepSegment) resolved.getSegments().get(0)).getAmplitude());
+        VibrationEffect resolved = DEFAULT_ONE_SHOT.resolve(51);
+        assertEquals(0.2f, getStepSegment(resolved, 0).getAmplitude());
 
         assertThrows(IllegalArgumentException.class, () -> DEFAULT_ONE_SHOT.resolve(1000));
     }
 
     @Test
     public void testResolveWaveform() {
-        VibrationEffect.Composed resolved = TEST_WAVEFORM.resolve(102);
-        assertEquals(0.4f, ((StepSegment) resolved.getSegments().get(2)).getAmplitude());
+        VibrationEffect resolved = TEST_WAVEFORM.resolve(102);
+        assertEquals(0.4f, getStepSegment(resolved, 2).getAmplitude());
 
         assertThrows(IllegalArgumentException.class, () -> TEST_WAVEFORM.resolve(1000));
     }
@@ -655,63 +677,127 @@
     }
 
     @Test
+    @RequiresFlagsEnabled(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
+    public void testResolveVendorEffect() {
+        VibrationEffect effect = VibrationEffect.createVendorEffect(createNonEmptyBundle());
+        assertEquals(effect, effect.resolve(51));
+    }
+
+    @Test
     public void testResolveComposed() {
         VibrationEffect effect = VibrationEffect.startComposition()
                 .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f, 1)
                 .compose();
         assertEquals(effect, effect.resolve(51));
 
-        VibrationEffect.Composed resolved = VibrationEffect.startComposition()
+        VibrationEffect resolved = VibrationEffect.startComposition()
                 .addEffect(DEFAULT_ONE_SHOT)
                 .compose()
                 .resolve(51);
-        assertEquals(0.2f, ((StepSegment) resolved.getSegments().get(0)).getAmplitude());
+        assertEquals(0.2f, getStepSegment(resolved, 0).getAmplitude());
     }
 
     @Test
     public void testScaleOneShot() {
-        VibrationEffect.Composed scaledUp = TEST_ONE_SHOT.scale(1.5f);
-        assertTrue(100 / 255f < ((StepSegment) scaledUp.getSegments().get(0)).getAmplitude());
+        VibrationEffect scaledUp = TEST_ONE_SHOT.scale(1.5f);
+        assertTrue(100 / 255f < getStepSegment(scaledUp, 0).getAmplitude());
 
-        VibrationEffect.Composed scaledDown = TEST_ONE_SHOT.scale(0.5f);
-        assertTrue(100 / 255f > ((StepSegment) scaledDown.getSegments().get(0)).getAmplitude());
+        VibrationEffect scaledDown = TEST_ONE_SHOT.scale(0.5f);
+        assertTrue(100 / 255f > getStepSegment(scaledDown, 0).getAmplitude());
     }
 
     @Test
     public void testScaleWaveform() {
-        VibrationEffect.Composed scaledUp = TEST_WAVEFORM.scale(1.5f);
-        assertEquals(1f, ((StepSegment) scaledUp.getSegments().get(0)).getAmplitude(), 1e-5f);
+        VibrationEffect scaledUp = TEST_WAVEFORM.scale(1.5f);
+        assertEquals(1f, getStepSegment(scaledUp, 0).getAmplitude(), 1e-5f);
 
-        VibrationEffect.Composed scaledDown = TEST_WAVEFORM.scale(0.5f);
-        assertTrue(1f > ((StepSegment) scaledDown.getSegments().get(0)).getAmplitude());
+        VibrationEffect scaledDown = TEST_WAVEFORM.scale(0.5f);
+        assertTrue(1f > getStepSegment(scaledDown, 0).getAmplitude());
     }
 
     @Test
     public void testScalePrebaked() {
         VibrationEffect effect = VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
 
-        VibrationEffect.Composed scaledUp = effect.scale(1.5f);
+        VibrationEffect scaledUp = effect.scale(1.5f);
         assertEquals(effect, scaledUp);
 
-        VibrationEffect.Composed scaledDown = effect.scale(0.5f);
+        VibrationEffect scaledDown = effect.scale(0.5f);
+        assertEquals(effect, scaledDown);
+    }
+
+    @Test
+    @RequiresFlagsEnabled(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
+    public void testScaleVendorEffect() {
+        VibrationEffect effect = VibrationEffect.createVendorEffect(createNonEmptyBundle());
+
+        VibrationEffect scaledUp = effect.scale(1.5f);
+        assertEquals(effect, scaledUp);
+
+        VibrationEffect scaledDown = effect.scale(0.5f);
         assertEquals(effect, scaledDown);
     }
 
     @Test
     public void testScaleComposed() {
-        VibrationEffect.Composed effect =
-                (VibrationEffect.Composed) VibrationEffect.startComposition()
+        VibrationEffect effect = VibrationEffect.startComposition()
                     .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 0.5f, 1)
                     .addEffect(TEST_ONE_SHOT)
                     .compose();
 
-        VibrationEffect.Composed scaledUp = effect.scale(1.5f);
-        assertTrue(0.5f < ((PrimitiveSegment) scaledUp.getSegments().get(0)).getScale());
-        assertTrue(100 / 255f < ((StepSegment) scaledUp.getSegments().get(1)).getAmplitude());
+        VibrationEffect scaledUp = effect.scale(1.5f);
+        assertTrue(0.5f < getPrimitiveSegment(scaledUp, 0).getScale());
+        assertTrue(100 / 255f < getStepSegment(scaledUp, 1).getAmplitude());
 
-        VibrationEffect.Composed scaledDown = effect.scale(0.5f);
-        assertTrue(0.5f > ((PrimitiveSegment) scaledDown.getSegments().get(0)).getScale());
-        assertTrue(100 / 255f > ((StepSegment) scaledDown.getSegments().get(1)).getAmplitude());
+        VibrationEffect scaledDown = effect.scale(0.5f);
+        assertTrue(0.5f > getPrimitiveSegment(scaledDown, 0).getScale());
+        assertTrue(100 / 255f > getStepSegment(scaledDown, 1).getAmplitude());
+    }
+
+    @Test
+    public void testApplyEffectStrengthToOneShotWaveformAndPrimitives() {
+        VibrationEffect oneShot = VibrationEffect.createOneShot(100, 100);
+        VibrationEffect waveform = VibrationEffect.createWaveform(new long[] { 10, 20 }, 0);
+        VibrationEffect composition = VibrationEffect.startComposition()
+                .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+                .compose();
+
+        assertEquals(oneShot, oneShot.applyEffectStrength(VibrationEffect.EFFECT_STRENGTH_STRONG));
+        assertEquals(waveform,
+                waveform.applyEffectStrength(VibrationEffect.EFFECT_STRENGTH_STRONG));
+        assertEquals(composition,
+                composition.applyEffectStrength(VibrationEffect.EFFECT_STRENGTH_STRONG));
+    }
+
+    @Test
+    public void testApplyEffectStrengthToPredefinedEffect() {
+        VibrationEffect effect = VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK);
+
+        VibrationEffect scaledUp =
+                effect.applyEffectStrength(VibrationEffect.EFFECT_STRENGTH_STRONG);
+        assertNotEquals(effect, scaledUp);
+        assertEquals(VibrationEffect.EFFECT_STRENGTH_STRONG,
+                getPrebakedSegment(scaledUp, 0).getEffectStrength());
+
+        VibrationEffect scaledDown =
+                effect.applyEffectStrength(VibrationEffect.EFFECT_STRENGTH_LIGHT);
+        assertNotEquals(effect, scaledDown);
+        assertEquals(VibrationEffect.EFFECT_STRENGTH_LIGHT,
+                getPrebakedSegment(scaledDown, 0).getEffectStrength());
+    }
+
+    @Test
+    @RequiresFlagsEnabled(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
+    public void testApplyEffectStrengthToVendorEffect() {
+        VibrationEffect effect = VibrationEffect.createVendorEffect(createNonEmptyBundle());
+
+        VibrationEffect scaledUp =
+                effect.applyEffectStrength(VibrationEffect.EFFECT_STRENGTH_STRONG);
+        assertNotEquals(effect, scaledUp);
+
+        VibrationEffect scaledDown =
+                effect.applyEffectStrength(VibrationEffect.EFFECT_STRENGTH_LIGHT);
+        assertNotEquals(effect, scaledDown);
     }
 
     private void doTestApplyRepeatingWithNonRepeatingOriginal(@NotNull VibrationEffect original) {
@@ -819,6 +905,15 @@
     }
 
     @Test
+    @RequiresFlagsEnabled(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
+    public void testApplyRepeatingIndefinitely_vendorEffect() {
+        VibrationEffect effect = VibrationEffect.createVendorEffect(createNonEmptyBundle());
+
+        assertEquals(effect, effect.applyRepeatingIndefinitely(true, 10));
+        assertEquals(effect, effect.applyRepeatingIndefinitely(false, 10));
+    }
+
+    @Test
     public void testDuration() {
         assertEquals(1, VibrationEffect.createOneShot(1, 1).getDuration());
         assertEquals(-1, VibrationEffect.get(VibrationEffect.EFFECT_CLICK).getDuration());
@@ -832,6 +927,10 @@
                 new long[]{1, 2, 3}, new int[]{1, 2, 3}, -1).getDuration());
         assertEquals(Long.MAX_VALUE, VibrationEffect.createWaveform(
                 new long[]{1, 2, 3}, new int[]{1, 2, 3}, 0).getDuration());
+        if (Flags.vendorVibrationEffects()) {
+            assertEquals(-1,
+                    VibrationEffect.createVendorEffect(createNonEmptyBundle()).getDuration());
+        }
     }
 
     @Test
@@ -872,6 +971,19 @@
     }
 
     @Test
+    @RequiresFlagsEnabled(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
+    public void testAreVibrationFeaturesSupported_vendorEffects() {
+        VibratorInfo supportedVibratorInfo = new VibratorInfo.Builder(/* id= */ 1)
+                .setCapabilities(IVibrator.CAP_PERFORM_VENDOR_EFFECTS)
+                .build();
+
+        assertTrue(VibrationEffect.createVendorEffect(createNonEmptyBundle())
+                .areVibrationFeaturesSupported(supportedVibratorInfo));
+        assertFalse(VibrationEffect.createVendorEffect(createNonEmptyBundle())
+                .areVibrationFeaturesSupported(new VibratorInfo.Builder(/* id= */ 1).build()));
+    }
+
+    @Test
     public void testIsHapticFeedbackCandidate_repeatingEffects_notCandidates() {
         assertFalse(VibrationEffect.createWaveform(
                 new long[]{1, 2, 3}, new int[]{1, 2, 3}, 0).isHapticFeedbackCandidate());
@@ -952,6 +1064,34 @@
         assertTrue(VibrationEffect.get(VibrationEffect.EFFECT_TICK).isHapticFeedbackCandidate());
     }
 
+    @Test
+    @RequiresFlagsEnabled(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
+    public void testIsHapticFeedbackCandidate_vendorEffects_notCandidates() {
+        assertFalse(VibrationEffect.createVendorEffect(createNonEmptyBundle())
+                .isHapticFeedbackCandidate());
+    }
+
+    @Test
+    public void testParcelingComposed() {
+        Parcel p = Parcel.obtain();
+        VibrationEffect effect = VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK);
+        effect.writeToParcel(p, 0);
+        p.setDataPosition(0);
+        VibrationEffect parceledEffect = VibrationEffect.Composed.CREATOR.createFromParcel(p);
+        assertThat(parceledEffect).isEqualTo(effect);
+    }
+
+    @Test
+    @RequiresFlagsEnabled(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
+    public void testParcelingVendorEffect() {
+        Parcel p = Parcel.obtain();
+        VibrationEffect effect = VibrationEffect.createVendorEffect(createNonEmptyBundle());
+        effect.writeToParcel(p, 0);
+        p.setDataPosition(0);
+        VibrationEffect parceledEffect = VibrationEffect.VendorEffect.CREATOR.createFromParcel(p);
+        assertThat(parceledEffect).isEqualTo(effect);
+    }
+
     private void assertArrayEq(long[] expected, long[] actual) {
         assertTrue(
                 String.format("Expected pattern %s, but was %s",
@@ -992,4 +1132,35 @@
 
         return context;
     }
+
+    private StepSegment getStepSegment(VibrationEffect effect, int index) {
+        VibrationEffectSegment segment = getEffectSegment(effect, index);
+        assertThat(segment).isInstanceOf(StepSegment.class);
+        return (StepSegment) segment;
+    }
+
+    private PrimitiveSegment getPrimitiveSegment(VibrationEffect effect, int index) {
+        VibrationEffectSegment segment = getEffectSegment(effect, index);
+        assertThat(segment).isInstanceOf(PrimitiveSegment.class);
+        return (PrimitiveSegment) segment;
+    }
+
+    private PrebakedSegment getPrebakedSegment(VibrationEffect effect, int index) {
+        VibrationEffectSegment segment = getEffectSegment(effect, index);
+        assertThat(segment).isInstanceOf(PrebakedSegment.class);
+        return (PrebakedSegment) segment;
+    }
+
+    private VibrationEffectSegment getEffectSegment(VibrationEffect effect, int index) {
+        assertThat(effect).isInstanceOf(VibrationEffect.Composed.class);
+        VibrationEffect.Composed composed = (VibrationEffect.Composed) effect;
+        assertThat(index).isLessThan(composed.getSegments().size());
+        return composed.getSegments().get(index);
+    }
+
+    private PersistableBundle createNonEmptyBundle() {
+        PersistableBundle bundle = new PersistableBundle();
+        bundle.putInt("key", 1);
+        return bundle;
+    }
 }
diff --git a/core/tests/vibrator/src/android/os/VibratorTest.java b/core/tests/vibrator/src/android/os/VibratorTest.java
index cfa12bb..6210a00 100644
--- a/core/tests/vibrator/src/android/os/VibratorTest.java
+++ b/core/tests/vibrator/src/android/os/VibratorTest.java
@@ -222,6 +222,18 @@
     }
 
     @Test
+    public void vibrate_withVibrationAttributesAndReason_usesGivenAttributesAndReason() {
+        VibrationEffect effect = VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
+        VibrationAttributes attributes = new VibrationAttributes.Builder().setUsage(
+                VibrationAttributes.USAGE_TOUCH).build();
+        String reason = "reason";
+
+        mVibratorSpy.vibrate(effect, attributes, reason);
+
+        verify(mVibratorSpy).vibrate(anyInt(), anyString(), eq(effect), eq(reason), eq(attributes));
+    }
+
+    @Test
     public void vibrate_withAudioAttributes_createsVibrationAttributesWithSameUsage() {
         VibrationEffect effect = VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
         AudioAttributes audioAttributes = new AudioAttributes.Builder().setUsage(
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 5d4139e..1fe6ca7 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -505,6 +505,7 @@
         <permission name="android.permission.RENOUNCE_PERMISSIONS" />
         <permission name="android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS" />
         <permission name="android.permission.GET_PROCESS_STATE_AND_OOM_SCORE" />
+        <permission name="android.permission.READ_DROPBOX_DATA" />
         <permission name="android.permission.READ_LOGS" />
         <permission name="android.permission.BRIGHTNESS_SLIDER_USAGE" />
         <permission name="android.permission.ACCESS_AMBIENT_LIGHT_STATS" />
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/util/AcceptOnceConsumer.java b/libs/WindowManager/Jetpack/src/androidx/window/common/AcceptOnceConsumer.java
similarity index 96%
rename from libs/WindowManager/Jetpack/src/androidx/window/util/AcceptOnceConsumer.java
rename to libs/WindowManager/Jetpack/src/androidx/window/common/AcceptOnceConsumer.java
index fe60037..c2f827a 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/util/AcceptOnceConsumer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/common/AcceptOnceConsumer.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package androidx.window.util;
+package androidx.window.common;
 
 import android.annotation.NonNull;
 
@@ -23,7 +23,7 @@
 /**
  * A base class that works with {@link BaseDataProducer} to add/remove a consumer that should
  * only be used once when {@link BaseDataProducer#notifyDataChanged} is called.
- * @param <T> The type of data this producer returns through {@link DataProducer#getData}.
+ * @param <T> The type of data this producer returns through {@link BaseDataProducer#getData}.
  */
 public class AcceptOnceConsumer<T> implements Consumer<T> {
     private final Consumer<T> mCallback;
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/util/BaseDataProducer.java b/libs/WindowManager/Jetpack/src/androidx/window/common/BaseDataProducer.java
similarity index 85%
rename from libs/WindowManager/Jetpack/src/androidx/window/util/BaseDataProducer.java
rename to libs/WindowManager/Jetpack/src/androidx/window/common/BaseDataProducer.java
index de52f09..e7099dc 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/util/BaseDataProducer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/common/BaseDataProducer.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package androidx.window.util;
+package androidx.window.common;
 
 import androidx.annotation.GuardedBy;
 import androidx.annotation.NonNull;
@@ -26,13 +26,12 @@
 import java.util.function.Consumer;
 
 /**
- * Base class that provides the implementation for the callback mechanism of the
- * {@link DataProducer} API.  This class is thread safe for adding, removing, and notifying
- * consumers.
+ * Base class that manages listeners when listening to a piece of data that changes.  This class is
+ * thread safe for adding, removing, and notifying consumers.
  *
- * @param <T> The type of data this producer returns through {@link DataProducer#getData}.
+ * @param <T> The type of data this producer returns through {@link BaseDataProducer#getData}.
  */
-public abstract class BaseDataProducer<T> implements DataProducer<T>,
+public abstract class BaseDataProducer<T> implements
         AcceptOnceConsumer.AcceptOnceProducerCallback<T> {
 
     private final Object mLock = new Object();
@@ -42,12 +41,17 @@
     private final Set<Consumer<T>> mCallbacksToRemove = new HashSet<>();
 
     /**
+     * Emits the first available data at that point in time.
+     * @param dataConsumer a {@link Consumer} that will receive one value.
+     */
+    public abstract void getData(@NonNull Consumer<T> dataConsumer);
+
+    /**
      * Adds a callback to the set of callbacks listening for data. Data is delivered through
      * {@link BaseDataProducer#notifyDataChanged(Object)}. This method is thread safe. Callers
      * should ensure that callbacks are thread safe.
      * @param callback that will receive data from the producer.
      */
-    @Override
     public final void addDataChangedCallback(@NonNull Consumer<T> callback) {
         synchronized (mLock) {
             mCallbacks.add(callback);
@@ -63,7 +67,6 @@
      * @param callback that was registered in
      * {@link BaseDataProducer#addDataChangedCallback(Consumer)}.
      */
-    @Override
     public final void removeDataChangedCallback(@NonNull Consumer<T> callback) {
         synchronized (mLock) {
             mCallbacks.remove(callback);
@@ -92,8 +95,8 @@
 
     /**
      * Called to notify all registered consumers that the data provided
-     * by {@link DataProducer#getData} has changed. Calls to this are thread save but callbacks need
-     * to ensure thread safety.
+     * by {@link BaseDataProducer#getData} has changed. Calls to this are thread save but callbacks
+     * need to ensure thread safety.
      */
     protected void notifyDataChanged(T value) {
         synchronized (mLock) {
@@ -122,4 +125,4 @@
             mCallbacksToRemove.add(callback);
         }
     }
-}
\ No newline at end of file
+}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/common/CommonFoldingFeature.java b/libs/WindowManager/Jetpack/src/androidx/window/common/CommonFoldingFeature.java
index e37dea4..b95bca1 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/common/CommonFoldingFeature.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/common/CommonFoldingFeature.java
@@ -16,7 +16,7 @@
 
 package androidx.window.common;
 
-import static androidx.window.util.ExtensionHelper.isZero;
+import static androidx.window.common.ExtensionHelper.isZero;
 
 import android.annotation.IntDef;
 import android.annotation.Nullable;
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducer.java b/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducer.java
index 98935e9..b2bc3de 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducer.java
@@ -31,9 +31,6 @@
 import android.util.Log;
 import android.util.SparseIntArray;
 
-import androidx.window.util.AcceptOnceConsumer;
-import androidx.window.util.BaseDataProducer;
-
 import com.android.internal.R;
 
 import java.util.ArrayList;
@@ -44,7 +41,7 @@
 import java.util.function.Consumer;
 
 /**
- * An implementation of {@link androidx.window.util.BaseDataProducer} that returns
+ * An implementation of {@link BaseDataProducer} that returns
  * the device's posture by mapping the state returned from {@link DeviceStateManager} to
  * values provided in the resources' config at {@link R.array#config_device_state_postures}.
  */
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/util/ExtensionHelper.java b/libs/WindowManager/Jetpack/src/androidx/window/common/ExtensionHelper.java
similarity index 99%
rename from libs/WindowManager/Jetpack/src/androidx/window/util/ExtensionHelper.java
rename to libs/WindowManager/Jetpack/src/androidx/window/common/ExtensionHelper.java
index a08db79..f466d60 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/util/ExtensionHelper.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/common/ExtensionHelper.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package androidx.window.util;
+package androidx.window.common;
 
 import static android.view.Surface.ROTATION_270;
 import static android.view.Surface.ROTATION_90;
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/common/RawFoldingFeatureProducer.java b/libs/WindowManager/Jetpack/src/androidx/window/common/RawFoldingFeatureProducer.java
index 8906e6d..6d758f1 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/common/RawFoldingFeatureProducer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/common/RawFoldingFeatureProducer.java
@@ -26,15 +26,13 @@
 import android.provider.Settings;
 import android.text.TextUtils;
 
-import androidx.window.util.BaseDataProducer;
-
 import com.android.internal.R;
 
 import java.util.Optional;
 import java.util.function.Consumer;
 
 /**
- * Implementation of {@link androidx.window.util.DataProducer} that produces a
+ * Implementation of {@link BaseDataProducer} that produces a
  * {@link String} that can be parsed to a {@link CommonFoldingFeature}.
  * {@link RawFoldingFeatureProducer} searches for the value in two places. The first check is in
  * settings where the {@link String} property is saved with the key
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java
index ecf4720..7f11fea 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java
@@ -39,6 +39,8 @@
 import androidx.window.extensions.layout.WindowLayoutComponent;
 import androidx.window.extensions.layout.WindowLayoutComponentImpl;
 
+import com.android.window.flags.Flags;
+
 import java.util.Objects;
 
 
@@ -55,11 +57,9 @@
      */
     private static final int NO_LEVEL_OVERRIDE = -1;
 
-    /**
-     * The min version of the WM Extensions that must be supported in the current platform version.
-     */
-    @VisibleForTesting
-    static final int EXTENSIONS_VERSION_CURRENT_PLATFORM = 6;
+    private static final int EXTENSIONS_VERSION_V7 = 7;
+
+    private static final int EXTENSIONS_VERSION_V6 = 6;
 
     private final Object mLock = new Object();
     private volatile DeviceStateManagerFoldingFeatureProducer mFoldingFeatureProducer;
@@ -67,7 +67,6 @@
     private volatile SplitController mSplitController;
     private volatile WindowAreaComponent mWindowAreaComponent;
 
-    private final int mVersion = EXTENSIONS_VERSION_CURRENT_PLATFORM;
     private final boolean mIsActivityEmbeddingEnabled;
 
     WindowExtensionsImpl() {
@@ -76,9 +75,22 @@
         Log.i(TAG, generateLogMessage());
     }
 
+    /**
+     * The min version of the WM Extensions that must be supported in the current platform version.
+     */
+    @VisibleForTesting
+    static int getExtensionsVersionCurrentPlatform() {
+        if (Flags.activityEmbeddingAnimationCustomizationFlag()) {
+            // Activity Embedding animation customization is the only major feature for v7.
+            return EXTENSIONS_VERSION_V7;
+        } else {
+            return EXTENSIONS_VERSION_V6;
+        }
+    }
+
     private String generateLogMessage() {
         final StringBuilder logBuilder = new StringBuilder("Initializing Window Extensions, "
-                + "vendor API level=" + mVersion);
+                + "vendor API level=" + getExtensionsVersionCurrentPlatform());
         final int levelOverride = getLevelOverride();
         if (levelOverride != NO_LEVEL_OVERRIDE) {
             logBuilder.append(", override to ").append(levelOverride);
@@ -91,7 +103,12 @@
     @Override
     public int getVendorApiLevel() {
         final int levelOverride = getLevelOverride();
-        return (levelOverride != NO_LEVEL_OVERRIDE) ? levelOverride : mVersion;
+        return hasLevelOverride() ? levelOverride : getExtensionsVersionCurrentPlatform();
+    }
+
+    @VisibleForTesting
+    boolean hasLevelOverride() {
+        return getLevelOverride() != NO_LEVEL_OVERRIDE;
     }
 
     private int getLevelOverride() {
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java
index 544f0f3..882a8d0 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java
@@ -565,6 +565,7 @@
         return true;
     }
 
+    // Only called by onTouch() and mRenderer is already null-checked.
     @GuardedBy("mLock")
     private void onStartDragging(@NonNull MotionEvent event) {
         mVelocityTracker = VelocityTracker.obtain();
@@ -590,6 +591,7 @@
         });
     }
 
+    // Only called by onTouch() and mRenderer is already null-checked.
     @GuardedBy("mLock")
     private void onDrag(@NonNull MotionEvent event) {
         if (mVelocityTracker != null) {
@@ -660,8 +662,10 @@
 
     @GuardedBy("mLock")
     private void updateDividerPosition(int position) {
-        mRenderer.setDividerPosition(position);
-        mRenderer.updateSurface();
+        if (mRenderer != null) {
+            mRenderer.setDividerPosition(position);
+            mRenderer.updateSurface();
+        }
     }
 
     @GuardedBy("mLock")
@@ -669,7 +673,10 @@
         // Veil visibility change should be applied together with the surface boost transaction in
         // the wct.
         final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
-        mRenderer.hideVeils(t);
+
+        if (mRenderer != null) {
+            mRenderer.hideVeils(t);
+        }
 
         // Callbacks must be executed on the executor to release mLock and prevent deadlocks.
         // mDecorSurfaceOwner may change between here and when the callback is executed,
@@ -684,8 +691,10 @@
                         }
                     });
         });
-        mRenderer.mIsDragging = false;
-        mRenderer.mDragHandle.setPressed(mRenderer.mIsDragging);
+        if (mRenderer != null) {
+            mRenderer.mIsDragging = false;
+            mRenderer.mDragHandle.setPressed(mRenderer.mIsDragging);
+        }
     }
 
     /**
@@ -1090,13 +1099,14 @@
         @NonNull
         private final SurfaceControl mDividerSurface;
         @NonNull
+        private final SurfaceControl mDividerLineSurface;
+        @NonNull
         private final WindowlessWindowManager mWindowlessWindowManager;
         @NonNull
         private final SurfaceControlViewHost mViewHost;
         @NonNull
         private final FrameLayout mDividerLayout;
-        @NonNull
-        private final View mDividerLine;
+        @Nullable
         private View mDragHandle;
         @NonNull
         private final View.OnTouchListener mListener;
@@ -1115,7 +1125,10 @@
             mProperties = properties;
             mListener = listener;
 
-            mDividerSurface = createChildSurface("DividerSurface", true /* visible */);
+            mDividerSurface = createChildSurface(
+                    mProperties.mDecorSurface, "DividerSurface", true /* visible */);
+            mDividerLineSurface = createChildSurface(
+                    mDividerSurface, "DividerLineSurface", true /* visible */);
             mWindowlessWindowManager = new WindowlessWindowManager(
                     mProperties.mConfiguration,
                     mDividerSurface,
@@ -1127,7 +1140,6 @@
                     context, displayManager.getDisplay(mProperties.mDisplayId),
                     mWindowlessWindowManager, "DividerContainer");
             mDividerLayout = new FrameLayout(context);
-            mDividerLine = new View(context);
 
             update();
         }
@@ -1220,6 +1232,7 @@
                 dividerSurfacePosition = mDividerPosition;
             }
 
+            // Update the divider surface position relative to the decor surface
             if (mProperties.mIsVerticalSplit) {
                 t.setPosition(mDividerSurface, dividerSurfacePosition, 0.0f);
                 t.setWindowCrop(mDividerSurface, mDividerSurfaceWidthPx, taskBounds.height());
@@ -1228,10 +1241,24 @@
                 t.setWindowCrop(mDividerSurface, taskBounds.width(), mDividerSurfaceWidthPx);
             }
 
-            // Update divider line position in the surface
+            // Update divider line surface position relative to the divider surface
             final int offset = mDividerPosition - dividerSurfacePosition;
-            mDividerLine.setX(mProperties.mIsVerticalSplit ? offset : 0);
-            mDividerLine.setY(mProperties.mIsVerticalSplit ? 0 : offset);
+            if (mProperties.mIsVerticalSplit) {
+                t.setPosition(mDividerLineSurface, offset, 0);
+                t.setWindowCrop(mDividerLineSurface,
+                        mProperties.mDividerWidthPx, taskBounds.height());
+            } else {
+                t.setPosition(mDividerLineSurface, 0, offset);
+                t.setWindowCrop(mDividerLineSurface,
+                        taskBounds.width(), mProperties.mDividerWidthPx);
+            }
+
+            // Update divider line surface visibility and color.
+            // If a container is fully expanded, the divider line is invisible unless dragging.
+            final boolean isDividerLineVisible = !mProperties.mIsDraggableExpandType || mIsDragging;
+            t.setVisibility(mDividerLineSurface, isDividerLineVisible);
+            t.setColor(mDividerLineSurface, colorToFloatArray(
+                    Color.valueOf(mProperties.mDividerAttributes.getDividerColor())));
 
             if (mIsDragging) {
                 updateVeils(t);
@@ -1277,21 +1304,6 @@
          */
         private void updateDivider(@NonNull SurfaceControl.Transaction t) {
             mDividerLayout.removeAllViews();
-            mDividerLayout.addView(mDividerLine);
-            if (mProperties.mIsDraggableExpandType && !mIsDragging) {
-                // If a container is fully expanded, the divider overlays on the expanded container.
-                mDividerLine.setBackgroundColor(Color.TRANSPARENT);
-            } else {
-                mDividerLine.setBackgroundColor(mProperties.mDividerAttributes.getDividerColor());
-            }
-            final Rect taskBounds = mProperties.mConfiguration.windowConfiguration.getBounds();
-            mDividerLine.setLayoutParams(
-                    mProperties.mIsVerticalSplit
-                            ? new FrameLayout.LayoutParams(
-                                    mProperties.mDividerWidthPx, taskBounds.height())
-                            : new FrameLayout.LayoutParams(
-                                    taskBounds.width(), mProperties.mDividerWidthPx)
-            );
             if (mProperties.mDividerAttributes.getDividerType()
                     == DividerAttributes.DIVIDER_TYPE_DRAGGABLE) {
                 createVeils();
@@ -1345,10 +1357,11 @@
         }
 
         @NonNull
-        private SurfaceControl createChildSurface(@NonNull String name, boolean visible) {
+        private SurfaceControl createChildSurface(
+                @NonNull SurfaceControl parent, @NonNull String name, boolean visible) {
             final Rect bounds = mProperties.mConfiguration.windowConfiguration.getBounds();
             return new SurfaceControl.Builder()
-                    .setParent(mProperties.mDecorSurface)
+                    .setParent(parent)
                     .setName(name)
                     .setHidden(!visible)
                     .setCallsite("DividerManager.createChildSurface")
@@ -1359,10 +1372,12 @@
 
         private void createVeils() {
             if (mPrimaryVeil == null) {
-                mPrimaryVeil = createChildSurface("DividerPrimaryVeil", false /* visible */);
+                mPrimaryVeil = createChildSurface(
+                        mProperties.mDecorSurface, "DividerPrimaryVeil", false /* visible */);
             }
             if (mSecondaryVeil == null) {
-                mSecondaryVeil = createChildSurface("DividerSecondaryVeil", false /* visible */);
+                mSecondaryVeil = createChildSurface(
+                        mProperties.mDecorSurface, "DividerSecondaryVeil", false /* visible */);
             }
         }
 
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
index 8e1fde0..e555176 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -119,7 +119,8 @@
 
     // TODO(b/243518738): Move to WM Extensions if we have requirement of overlay without
     //  association. It's not set in WM Extensions nor Wm Jetpack library currently.
-    private static final String KEY_OVERLAY_ASSOCIATE_WITH_LAUNCHING_ACTIVITY =
+    @VisibleForTesting
+    static final String KEY_OVERLAY_ASSOCIATE_WITH_LAUNCHING_ACTIVITY =
             "androidx.window.extensions.embedding.shouldAssociateWithLaunchingActivity";
 
     @VisibleForTesting
@@ -1333,13 +1334,24 @@
         if (shouldContainerBeExpanded(container)) {
             // Make sure that the existing container is expanded.
             mPresenter.expandTaskFragment(wct, container);
-        } else {
-            // Put activity into a new expanded container.
-            final TaskFragmentContainer newContainer =
-                    new TaskFragmentContainer.Builder(this, getTaskId(activity), activity)
-                            .setPendingAppearedActivity(activity).build();
-            mPresenter.expandActivity(wct, newContainer.getTaskFragmentToken(), activity);
+            return;
         }
+
+        final SplitContainer splitContainer = getActiveSplitForContainer(container);
+        if (splitContainer instanceof SplitPinContainer
+                && !container.isPinned() && container.getRunningActivityCount() == 1) {
+            // This is already the expected state when the pinned container is shown with an
+            // expanded activity in a standalone container on the side. Moving the activity into
+            // another new expanded container again is not necessary and could result in
+            // recursively creating new TaskFragmentContainers if the activity somehow relaunched.
+            return;
+        }
+
+        // Put activity into a new expanded container.
+        final TaskFragmentContainer newContainer =
+                new TaskFragmentContainer.Builder(this, getTaskId(activity), activity)
+                        .setPendingAppearedActivity(activity).build();
+        mPresenter.expandActivity(wct, newContainer.getTaskFragmentToken(), activity);
     }
 
     /**
@@ -2742,89 +2754,70 @@
         }
 
         final int taskId = getTaskId(launchActivity);
-        if (!overlayContainers.isEmpty()) {
-            for (final TaskFragmentContainer overlayContainer : overlayContainers) {
-                final boolean isTopNonFinishingOverlay = overlayContainer.equals(
-                        overlayContainer.getTaskContainer().getTopNonFinishingTaskFragmentContainer(
-                                true /* includePin */, true /* includeOverlay */));
-                if (taskId != overlayContainer.getTaskId()) {
-                    // If there's an overlay container with same tag in a different task,
-                    // dismiss the overlay container since the tag must be unique per process.
-                    if (overlayTag.equals(overlayContainer.getOverlayTag())) {
-                        Log.w(TAG, "The overlay container with tag:"
-                                + overlayContainer.getOverlayTag() + " is dismissed because"
-                                + " there's an existing overlay container with the same tag but"
-                                + " different task ID:" + overlayContainer.getTaskId() + ". "
-                                + "The new associated activity is " + launchActivity);
-                        mPresenter.cleanupContainer(wct, overlayContainer,
-                                false /* shouldFinishDependant */);
-                    }
-                    continue;
-                }
-                if (!overlayTag.equals(overlayContainer.getOverlayTag())) {
-                    // If there's an overlay container with different tag on top in the same
-                    // task, dismiss the existing overlay container.
-                    if (isTopNonFinishingOverlay) {
-                        mPresenter.cleanupContainer(wct, overlayContainer,
-                                false /* shouldFinishDependant */);
-                    }
-                    continue;
-                }
-                // The overlay container has the same tag and task ID with the new launching
-                // overlay container.
-                if (!isTopNonFinishingOverlay) {
-                    // Dismiss the invisible overlay container regardless of activity
-                    // association if it collides the tag of new launched overlay container .
-                    Log.w(TAG, "The invisible overlay container with tag:"
-                            + overlayContainer.getOverlayTag() + " is dismissed because"
-                            + " there's a launching overlay container with the same tag."
-                            + " The new associated activity is " + launchActivity);
-                    mPresenter.cleanupContainer(wct, overlayContainer,
-                            false /* shouldFinishDependant */);
-                    continue;
-                }
-                // Requesting an always-on-top overlay.
-                if (!associateLaunchingActivity) {
-                    if (overlayContainer.isOverlayWithActivityAssociation()) {
-                        // Dismiss the overlay container since it has associated with an activity.
-                        Log.w(TAG, "The overlay container with tag:"
-                                + overlayContainer.getOverlayTag() + " is dismissed because"
-                                + " there's an existing overlay container with the same tag but"
-                                + " different associated launching activity. The overlay container"
-                                + " doesn't associate with any activity.");
-                        mPresenter.cleanupContainer(wct, overlayContainer,
-                                false /* shouldFinishDependant */);
-                        continue;
-                    } else {
-                        // The existing overlay container doesn't associate an activity as well.
-                        // Just update the overlay and return.
-                        // Note that going to this condition means the tag, task ID matches a
-                        // visible always-on-top overlay, and won't dismiss any overlay any more.
-                        mPresenter.applyActivityStackAttributes(wct, overlayContainer, attrs,
-                                getMinDimensions(intent));
-                        return overlayContainer;
-                    }
-                }
-                if (launchActivity.getActivityToken()
-                        != overlayContainer.getAssociatedActivityToken()) {
-                    Log.w(TAG, "The overlay container with tag:"
-                            + overlayContainer.getOverlayTag() + " is dismissed because"
-                            + " there's an existing overlay container with the same tag but"
-                            + " different associated launching activity. The new associated"
-                            + " activity is " + launchActivity);
-                    // The associated activity must be the same, or it will be dismissed.
-                    mPresenter.cleanupContainer(wct, overlayContainer,
-                            false /* shouldFinishDependant */);
-                    continue;
-                }
-                // Reaching here means the launching activity launch an overlay container with the
-                // same task ID, tag, while there's a previously launching visible overlay
-                // container. We'll regard it as updating the existing overlay container.
+        // Overlay container policy:
+        // 1. Overlay tag must be unique per process.
+        //   a. For associated overlay, if a new launched overlay container has the same tag as
+        //      an existing one, the existing overlay will be dismissed regardless of its task
+        //      and window hierarchy.
+        //   b. For always-on-top overlay, if there's an overlay container has the same tag in the
+        //      launched task, the overlay container will be re-used, which means the
+        //      ActivityStackAttributes will be applied and the launched activity will be positioned
+        //      on top of the overlay container.
+        // 2. There must be at most one overlay that partially occludes a visible activity per task.
+        //   a. For associated overlay, only the top visible overlay container in the launched task
+        //      will be dismissed.
+        //   b. Always-on-top overlay is always visible. If there's an overlay with different tags
+        //      in the same task, the overlay will be dismissed in case an activity above
+        //      the overlay is dismissed and the overlay is shown unexpectedly.
+        for (final TaskFragmentContainer overlayContainer : overlayContainers) {
+            final boolean isTopNonFinishingOverlay = overlayContainer.isTopNonFinishingChild();
+            final boolean areInSameTask = taskId == overlayContainer.getTaskId();
+            final boolean haveSameTag = overlayTag.equals(overlayContainer.getOverlayTag());
+            if (!associateLaunchingActivity && overlayContainer.isAlwaysOnTopOverlay()
+                    && haveSameTag && areInSameTask) {
+                // Just launch the activity and update the existing always-on-top overlay
+                // if the requested overlay is an always-on-top overlay with the same tag
+                // as the existing one.
                 mPresenter.applyActivityStackAttributes(wct, overlayContainer, attrs,
                         getMinDimensions(intent));
                 return overlayContainer;
-
             }
+            if (haveSameTag) {
+                // For other tag match, we should clean up the existing overlay since the overlay
+                // tag must be unique per process.
+                Log.w(TAG, "The overlay container with tag:"
+                        + overlayContainer.getOverlayTag() + " is dismissed with "
+                        + " the launching activity=" + launchActivity
+                        + " because there's an existing overlay container with the same tag.");
+                mPresenter.cleanupContainer(wct, overlayContainer,
+                        false /* shouldFinishDependant */);
+            }
+            if (!areInSameTask) {
+                // Early return here because we won't clean-up or update overlay from different
+                // tasks except tag collision.
+                continue;
+            }
+            if (associateLaunchingActivity) {
+                // For associated overlay, we only dismiss the overlay if it's the top non-finishing
+                // child of its parent container.
+                if (isTopNonFinishingOverlay) {
+                    Log.w(TAG, "The on-top overlay container with tag:"
+                            + overlayContainer.getOverlayTag() + " is dismissed with "
+                            + " the launching activity=" + launchActivity
+                            + "because we only allow one overlay on top.");
+                    mPresenter.cleanupContainer(wct, overlayContainer,
+                            false /* shouldFinishDependant */);
+                }
+                continue;
+            }
+            // Otherwise, we should clean up the overlay in the task because we only allow one
+            // overlay when an always-on-top overlay is launched.
+            Log.w(TAG, "The overlay container with tag:"
+                    + overlayContainer.getOverlayTag() + " is dismissed with "
+                    + " the launching activity=" + launchActivity
+                    + "because an always-on-top overlay is launched.");
+            mPresenter.cleanupContainer(wct, overlayContainer,
+                    false /* shouldFinishDependant */);
         }
         // Launch the overlay container to the task with taskId.
         return createEmptyContainer(wct, intent, taskId, attrs, launchActivity, overlayTag,
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
index 7173b0c..d0e2c99 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
@@ -340,6 +340,13 @@
         return mInfo != null && mInfo.isVisible();
     }
 
+    /**
+     * See {@link TaskFragmentInfo#isTopNonFinishingChild()}
+     */
+    boolean isTopNonFinishingChild() {
+        return mInfo != null && mInfo.isTopNonFinishingChild();
+    }
+
     /** Whether the TaskFragment is in an intermediate state waiting for the server update.*/
     boolean isInIntermediateState() {
         if (mInfo == null) {
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java
index 859bc2c..a3ef68a 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java
@@ -21,9 +21,9 @@
 
 import static androidx.window.common.CommonFoldingFeature.COMMON_STATE_FLAT;
 import static androidx.window.common.CommonFoldingFeature.COMMON_STATE_HALF_OPENED;
-import static androidx.window.util.ExtensionHelper.isZero;
-import static androidx.window.util.ExtensionHelper.rotateRectToDisplayRotation;
-import static androidx.window.util.ExtensionHelper.transformToWindowSpaceRect;
+import static androidx.window.common.ExtensionHelper.isZero;
+import static androidx.window.common.ExtensionHelper.rotateRectToDisplayRotation;
+import static androidx.window.common.ExtensionHelper.transformToWindowSpaceRect;
 
 import android.app.Activity;
 import android.app.ActivityThread;
@@ -31,10 +31,12 @@
 import android.app.WindowConfiguration;
 import android.content.ComponentCallbacks;
 import android.content.Context;
+import android.content.ContextWrapper;
 import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.os.Bundle;
 import android.os.IBinder;
+import android.os.StrictMode;
 import android.util.ArrayMap;
 import android.util.Log;
 
@@ -136,14 +138,23 @@
                     || containsConsumer(consumer)) {
                 return;
             }
+            final IllegalArgumentException exception = new IllegalArgumentException(
+                    "Context must be a UI Context with display association, which should be"
+                    + " an Activity, WindowContext or InputMethodService");
             if (!context.isUiContext()) {
-                throw new IllegalArgumentException("Context must be a UI Context, which should be"
-                        + " an Activity, WindowContext or InputMethodService");
+                throw exception;
             }
             if (context.getAssociatedDisplayId() == INVALID_DISPLAY) {
-                Log.w(TAG, "The registered Context is a UI Context but not associated with any"
-                        + " display. This Context may not receive any WindowLayoutInfo update");
+                // This is to identify if #isUiContext of a non-UI Context is overridden.
+                // #isUiContext is more likely to be overridden than #getAssociatedDisplayId
+                // since #isUiContext is a public API.
+                StrictMode.onIncorrectContextUsed("The registered Context is a UI Context "
+                        + "but not associated with any display. "
+                        + "This Context may not receive any WindowLayoutInfo update. "
+                        + dumpAllBaseContextToString(context), exception);
             }
+            Log.d(TAG, "Register WindowLayoutInfoListener on "
+                    + dumpAllBaseContextToString(context));
             mFoldingFeatureProducer.getData((features) -> {
                 WindowLayoutInfo newWindowLayout = getWindowLayoutInfo(context, features);
                 consumer.accept(newWindowLayout);
@@ -162,6 +173,16 @@
         }
     }
 
+    @NonNull
+    private String dumpAllBaseContextToString(@NonNull Context context) {
+        final StringBuilder builder = new StringBuilder("Context=" + context);
+        while ((context instanceof ContextWrapper wrapper) && wrapper.getBaseContext() != null) {
+            context = wrapper.getBaseContext();
+            builder.append(", of which baseContext=").append(context);
+        }
+        return builder.toString();
+    }
+
     @Override
     public void removeWindowLayoutInfoListener(
             @NonNull java.util.function.Consumer<WindowLayoutInfo> consumer) {
@@ -417,9 +438,19 @@
      */
     private boolean shouldReportDisplayFeatures(@NonNull @UiContext Context context) {
         int displayId = context.getAssociatedDisplayId();
+        if (!context.isUiContext() || displayId == INVALID_DISPLAY) {
+            // This could happen if a caller sets MutableContextWrapper's base Context to a non-UI
+            // Context.
+            StrictMode.onIncorrectContextUsed("Context is not a UI Context anymore."
+                    + " Was the base context changed? It's suggested to unregister"
+                    + " the windowLayoutInfo callback before changing the base Context."
+                    + " UI Contexts are Activity, InputMethodService or context created"
+                    + " with createWindowContext. " + dumpAllBaseContextToString(context),
+                    new UnsupportedOperationException("Context is not a UI Context anymore."
+                            + " Was the base context changed?"));
+        }
         if (displayId != DEFAULT_DISPLAY) {
-            // Display features are not supported on secondary displays or the context is not
-            // associated with any display.
+            // Display features are not supported on secondary displays.
             return false;
         }
 
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java
index 339908a..b63fd08 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java
@@ -25,11 +25,11 @@
 import android.os.IBinder;
 
 import androidx.annotation.NonNull;
+import androidx.window.common.BaseDataProducer;
 import androidx.window.common.CommonFoldingFeature;
 import androidx.window.common.DeviceStateManagerFoldingFeatureProducer;
 import androidx.window.common.EmptyLifecycleCallbacksAdapter;
 import androidx.window.common.RawFoldingFeatureProducer;
-import androidx.window.util.BaseDataProducer;
 
 import java.util.ArrayList;
 import java.util.List;
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SidecarHelper.java b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SidecarHelper.java
index bb6ab47..4fd03e4 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SidecarHelper.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SidecarHelper.java
@@ -17,8 +17,8 @@
 
 import static android.view.Display.DEFAULT_DISPLAY;
 
-import static androidx.window.util.ExtensionHelper.rotateRectToDisplayRotation;
-import static androidx.window.util.ExtensionHelper.transformToWindowSpaceRect;
+import static androidx.window.common.ExtensionHelper.rotateRectToDisplayRotation;
+import static androidx.window.common.ExtensionHelper.transformToWindowSpaceRect;
 
 import android.annotation.NonNull;
 import android.app.Activity;
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/util/DataProducer.java b/libs/WindowManager/Jetpack/src/androidx/window/util/DataProducer.java
deleted file mode 100644
index ec301dc..0000000
--- a/libs/WindowManager/Jetpack/src/androidx/window/util/DataProducer.java
+++ /dev/null
@@ -1,44 +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 androidx.window.util;
-
-import android.annotation.NonNull;
-
-import java.util.function.Consumer;
-
-/**
- * Produces data through {@link DataProducer#getData} and provides a mechanism for receiving
- * a callback when the data managed by the produces has changed.
- *
- * @param <T> The type of data this producer returns through {@link DataProducer#getData}.
- */
-public interface DataProducer<T> {
-    /**
-     * Emits the first available data at that point in time.
-     * @param dataConsumer a {@link Consumer} that will receive one value.
-     */
-    void getData(@NonNull Consumer<T> dataConsumer);
-
-    /**
-     * Adds a callback to be notified when the data returned
-     * from {@link DataProducer#getData} has changed.
-     */
-    void addDataChangedCallback(@NonNull Consumer<T> callback);
-
-    /** Removes a callback previously added with {@link #addDataChangedCallback(Consumer)}. */
-    void removeDataChangedCallback(@NonNull Consumer<T> callback);
-}
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/util/ExtensionHelperTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/common/ExtensionHelperTest.java
similarity index 99%
rename from libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/util/ExtensionHelperTest.java
rename to libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/common/ExtensionHelperTest.java
index 3278cdf..b6e9519 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/util/ExtensionHelperTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/common/ExtensionHelperTest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package androidx.window.util;
+package androidx.window.common;
 
 import static org.junit.Assert.assertEquals;
 
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/WindowExtensionsTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/WindowExtensionsTest.java
index c5aaddc..92f4814 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/WindowExtensionsTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/WindowExtensionsTest.java
@@ -16,7 +16,7 @@
 
 package androidx.window.extensions;
 
-import static androidx.window.extensions.WindowExtensionsImpl.EXTENSIONS_VERSION_CURRENT_PLATFORM;
+import static androidx.window.extensions.WindowExtensionsImpl.getExtensionsVersionCurrentPlatform;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -59,7 +59,8 @@
     @Test
     public void testGetVendorApiLevel_extensionsEnabled_matchesCurrentVersion() {
         assumeTrue(WindowManager.hasWindowExtensionsEnabled());
-        assertThat(mVersion).isEqualTo(EXTENSIONS_VERSION_CURRENT_PLATFORM);
+        assumeFalse(((WindowExtensionsImpl) mExtensions).hasLevelOverride());
+        assertThat(mVersion).isEqualTo(getExtensionsVersionCurrentPlatform());
     }
 
     @Test
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/EmbeddingTestUtils.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/EmbeddingTestUtils.java
index d649c6d..7dc78fd 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/EmbeddingTestUtils.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/EmbeddingTestUtils.java
@@ -163,12 +163,14 @@
     }
 
     /** Creates a mock TaskFragmentInfo for the given TaskFragment. */
+    @NonNull
     static TaskFragmentInfo createMockTaskFragmentInfo(@NonNull TaskFragmentContainer container,
             @NonNull Activity activity) {
         return createMockTaskFragmentInfo(container, activity, true /* isVisible */);
     }
 
     /** Creates a mock TaskFragmentInfo for the given TaskFragment. */
+    @NonNull
     static TaskFragmentInfo createMockTaskFragmentInfo(@NonNull TaskFragmentContainer container,
             @NonNull Activity activity, boolean isVisible) {
         return new TaskFragmentInfo(container.getTaskFragmentToken(),
@@ -182,7 +184,27 @@
                 false /* isTaskClearedForReuse */,
                 false /* isTaskFragmentClearedForPip */,
                 false /* isClearedForReorderActivityToFront */,
-                new Point());
+                new Point(),
+                false /* isTopChild */);
+    }
+
+    /** Creates a mock TaskFragmentInfo for the given TaskFragment. */
+    @NonNull
+    static TaskFragmentInfo createMockTaskFragmentInfo(@NonNull TaskFragmentContainer container,
+            @NonNull Activity activity, boolean isVisible, boolean isOnTop) {
+        return new TaskFragmentInfo(container.getTaskFragmentToken(),
+                mock(WindowContainerToken.class),
+                new Configuration(),
+                1,
+                isVisible,
+                Collections.singletonList(activity.getActivityToken()),
+                new ArrayList<>(),
+                new Point(),
+                false /* isTaskClearedForReuse */,
+                false /* isTaskFragmentClearedForPip */,
+                false /* isClearedForReorderActivityToFront */,
+                new Point(),
+                isOnTop);
     }
 
     static ActivityInfo createActivityInfoWithMinDimensions() {
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizerTest.java
index ad41b18..8911d18 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizerTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizerTest.java
@@ -114,6 +114,7 @@
                 mock(WindowContainerToken.class), new Configuration(), 0 /* runningActivityCount */,
                 false /* isVisible */, new ArrayList<>(), new ArrayList<>(), new Point(),
                 false /* isTaskClearedForReuse */, false /* isTaskFragmentClearedForPip */,
-                false /* isClearedForReorderActivityToFront */, new Point());
+                false /* isClearedForReorderActivityToFront */, new Point(),
+                false /* isTopChild */);
     }
 }
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java
index 1c4c887..475475b 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java
@@ -30,6 +30,7 @@
 import static androidx.window.extensions.embedding.EmbeddingTestUtils.createSplitPlaceholderRuleBuilder;
 import static androidx.window.extensions.embedding.EmbeddingTestUtils.createSplitRule;
 import static androidx.window.extensions.embedding.EmbeddingTestUtils.createTfContainer;
+import static androidx.window.extensions.embedding.SplitController.KEY_OVERLAY_ASSOCIATE_WITH_LAUNCHING_ACTIVITY;
 import static androidx.window.extensions.embedding.SplitPresenter.CONTAINER_POSITION_BOTTOM;
 import static androidx.window.extensions.embedding.SplitPresenter.CONTAINER_POSITION_LEFT;
 import static androidx.window.extensions.embedding.SplitPresenter.CONTAINER_POSITION_RIGHT;
@@ -94,6 +95,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;
 
@@ -267,7 +269,7 @@
     }
 
     @Test
-    public void testCreateOrUpdateOverlay_visibleOverlaySameTagInTask_dismissOverlay() {
+    public void testCreateOrUpdateOverlay_topOverlayInTask_dismissOverlay() {
         createExistingOverlayContainers();
 
         final TaskFragmentContainer overlayContainer =
@@ -295,26 +297,6 @@
     }
 
     @Test
-    public void testCreateOrUpdateOverlay_sameTagTaskAndActivity_updateOverlay() {
-        createExistingOverlayContainers();
-
-        final Rect bounds = new Rect(0, 0, 100, 100);
-        mSplitController.setActivityStackAttributesCalculator(params ->
-                new ActivityStackAttributes.Builder().setRelativeBounds(bounds).build());
-        final TaskFragmentContainer overlayContainer = createOrUpdateOverlayTaskFragmentIfNeeded(
-                mOverlayContainer1.getOverlayTag());
-
-        assertWithMessage("overlayContainer1 must be updated since the new overlay container"
-                + " is launched with the same tag and task")
-                .that(mSplitController.getAllNonFinishingOverlayContainers())
-                .containsExactly(mOverlayContainer1, mOverlayContainer2);
-
-        assertThat(overlayContainer).isEqualTo(mOverlayContainer1);
-        verify(mSplitPresenter).resizeTaskFragment(eq(mTransaction),
-                eq(mOverlayContainer1.getTaskFragmentToken()), eq(bounds));
-    }
-
-    @Test
     public void testCreateOrUpdateOverlay_sameTagAndTaskButNotActivity_dismissOverlay() {
         createExistingOverlayContainers();
 
@@ -362,6 +344,43 @@
     }
 
     @Test
+    public void testCreateOrUpdateAlwaysOnTopOverlay_dismissMultipleOverlaysInTask() {
+        createExistingOverlayContainers();
+        // Create another overlay in task.
+        final TaskFragmentContainer overlayContainer3 =
+                createTestOverlayContainer(TASK_ID, "test3");
+        assertThat(mSplitController.getAllNonFinishingOverlayContainers())
+                .containsExactly(mOverlayContainer1, mOverlayContainer2, overlayContainer3);
+
+        final TaskFragmentContainer overlayContainer =
+                createOrUpdateAlwaysOnTopOverlay("test4");
+
+        assertWithMessage("overlayContainer1 and overlayContainer3 must be dismissed")
+                .that(mSplitController.getAllNonFinishingOverlayContainers())
+                .containsExactly(mOverlayContainer2, overlayContainer);
+    }
+
+    @Test
+    public void testCreateOrUpdateAlwaysOnTopOverlay_updateOverlay() {
+        createExistingOverlayContainers();
+        // Create another overlay in task.
+        final TaskFragmentContainer alwaysOnTopOverlay = createTestOverlayContainer(TASK_ID,
+                "test3", true /* isVisible */, false /* associateLaunchingActivity */);
+        final ActivityStackAttributes attrs = new ActivityStackAttributes.Builder()
+                .setRelativeBounds(new Rect(0, 0, 100, 100)).build();
+        mSplitController.setActivityStackAttributesCalculator(params -> attrs);
+
+        Mockito.clearInvocations(mSplitPresenter);
+        final TaskFragmentContainer overlayContainer =
+                createOrUpdateAlwaysOnTopOverlay(alwaysOnTopOverlay.getOverlayTag());
+
+        assertWithMessage("overlayContainer1 and overlayContainer3 must be dismissed")
+                .that(mSplitController.getAllNonFinishingOverlayContainers())
+                .containsExactly(mOverlayContainer2, alwaysOnTopOverlay);
+        assertThat(overlayContainer).isEqualTo(alwaysOnTopOverlay);
+    }
+
+    @Test
     public void testCreateOrUpdateOverlay_launchFromSplit_returnNull() {
         final Activity primaryActivity = createMockActivity();
         final Activity secondaryActivity = createMockActivity();
@@ -381,13 +400,13 @@
     }
 
     private void createExistingOverlayContainers() {
-        createExistingOverlayContainers(true /* visible */);
+        createExistingOverlayContainers(true /* isOnTop */);
     }
 
-    private void createExistingOverlayContainers(boolean visible) {
-        mOverlayContainer1 = createTestOverlayContainer(TASK_ID, "test1", visible,
+    private void createExistingOverlayContainers(boolean isOnTop) {
+        mOverlayContainer1 = createTestOverlayContainer(TASK_ID, "test1", isOnTop,
                 true /* associatedLaunchingActivity */, mActivity);
-        mOverlayContainer2 = createTestOverlayContainer(TASK_ID + 1, "test2", visible);
+        mOverlayContainer2 = createTestOverlayContainer(TASK_ID + 1, "test2", isOnTop);
         List<TaskFragmentContainer> overlayContainers = mSplitController
                 .getAllNonFinishingOverlayContainers();
         assertThat(overlayContainers).containsExactly(mOverlayContainer1, mOverlayContainer2);
@@ -966,6 +985,16 @@
                 launchOptions, mIntent, activity);
     }
 
+    @Nullable
+    private TaskFragmentContainer createOrUpdateAlwaysOnTopOverlay(
+            @NonNull String tag) {
+        final Bundle launchOptions = new Bundle();
+        launchOptions.putBoolean(KEY_OVERLAY_ASSOCIATE_WITH_LAUNCHING_ACTIVITY, false);
+        launchOptions.putString(KEY_OVERLAY_TAG, tag);
+        return mSplitController.createOrUpdateOverlayTaskFragmentIfNeeded(mTransaction,
+                launchOptions, mIntent, createMockActivity());
+    }
+
     /** Creates a mock TaskFragment that has been registered and appeared in the organizer. */
     @NonNull
     private TaskFragmentContainer createMockTaskFragmentContainer(@NonNull Activity activity) {
@@ -975,10 +1004,10 @@
     /** Creates a mock TaskFragment that has been registered and appeared in the organizer. */
     @NonNull
     private TaskFragmentContainer createMockTaskFragmentContainer(
-            @NonNull Activity activity, boolean isVisible) {
+            @NonNull Activity activity, boolean isOnTop) {
         final TaskFragmentContainer container = createTfContainer(mSplitController,
                 activity.getTaskId(), activity);
-        setupTaskFragmentInfo(container, activity, isVisible);
+        setupTaskFragmentInfo(container, activity, isOnTop);
         return container;
     }
 
@@ -990,8 +1019,8 @@
 
     @NonNull
     private TaskFragmentContainer createTestOverlayContainer(int taskId, @NonNull String tag,
-            boolean isVisible) {
-        return createTestOverlayContainer(taskId, tag, isVisible,
+            boolean isOnTop) {
+        return createTestOverlayContainer(taskId, tag, isOnTop,
                 true /* associateLaunchingActivity */);
     }
 
@@ -1002,11 +1031,9 @@
                 null /* launchingActivity */);
     }
 
-    // TODO(b/243518738): add more test coverage on overlay container without activity association
-    //  once we have use cases.
     @NonNull
     private TaskFragmentContainer createTestOverlayContainer(int taskId, @NonNull String tag,
-            boolean isVisible, boolean associateLaunchingActivity,
+            boolean isOnTop, boolean associateLaunchingActivity,
             @Nullable Activity launchingActivity) {
         final Activity activity = launchingActivity != null
                 ? launchingActivity : createMockActivity();
@@ -1017,14 +1044,15 @@
                         .setLaunchOptions(Bundle.EMPTY)
                         .setAssociatedActivity(associateLaunchingActivity ? activity : null)
                         .build();
-        setupTaskFragmentInfo(overlayContainer, createMockActivity(), isVisible);
+        setupTaskFragmentInfo(overlayContainer, createMockActivity(), isOnTop);
         return overlayContainer;
     }
 
     private void setupTaskFragmentInfo(@NonNull TaskFragmentContainer container,
                                        @NonNull Activity activity,
-                                       boolean isVisible) {
-        final TaskFragmentInfo info = createMockTaskFragmentInfo(container, activity, isVisible);
+                                       boolean isOnTop) {
+        final TaskFragmentInfo info = createMockTaskFragmentInfo(container, activity, isOnTop,
+                isOnTop);
         container.setInfo(mTransaction, info);
         mSplitPresenter.mFragmentInfos.put(container.getTaskFragmentToken(), info);
     }
diff --git a/libs/WindowManager/Shell/res/drawable/desktop_mode_ic_handle_menu_new_window.xml b/libs/WindowManager/Shell/res/drawable/desktop_mode_ic_handle_menu_new_window.xml
new file mode 100644
index 0000000..c154059
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/desktop_mode_ic_handle_menu_new_window.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.
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="20dp"
+    android:height="20dp"
+    android:viewportWidth="20"
+    android:viewportHeight="20">
+  <path
+      android:pathData="M15 16V14H13V12.5H15V10.5H16.5V12.5H18.5V14H16.5V16H15ZM3.5 17C3.09722 17 2.74306 16.8542 2.4375 16.5625C2.14583 16.2569 2 15.9028 2 15.5V4.5C2 4.08333 2.14583 3.72917 2.4375 3.4375C2.74306 3.14583 3.09722 3 3.5 3H14.5C14.9167 3 15.2708 3.14583 15.5625 3.4375C15.8542 3.72917 16 4.08333 16 4.5V9H14.5V7H3.5V15.5H13.625V17H3.5ZM3.5 5.5H14.5V4.5H3.5V5.5ZM3.5 5.5V4.5V5.5Z"
+      android:fillColor="#1C1C14"/>
+</vector>
diff --git a/libs/WindowManager/Shell/res/layout/compat_ui_layout.xml b/libs/WindowManager/Shell/res/layout/compat_ui_layout.xml
index 257fe15..62782a7 100644
--- a/libs/WindowManager/Shell/res/layout/compat_ui_layout.xml
+++ b/libs/WindowManager/Shell/res/layout/compat_ui_layout.xml
@@ -21,38 +21,6 @@
     android:orientation="vertical"
     android:gravity="bottom|end">
 
-    <include android:id="@+id/camera_compat_hint"
-        android:visibility="gone"
-        android:layout_width="@dimen/camera_compat_hint_width"
-        android:layout_height="wrap_content"
-        layout="@layout/compat_mode_hint"/>
-
-    <LinearLayout
-        android:id="@+id/camera_compat_control"
-        android:visibility="gone"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:clipToPadding="false"
-        android:layout_marginEnd="@dimen/compat_button_margin"
-        android:layout_marginBottom="@dimen/compat_button_margin"
-        android:orientation="vertical">
-
-        <ImageButton
-            android:id="@+id/camera_compat_treatment_button"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:background="@android:color/transparent"/>
-
-        <ImageButton
-            android:id="@+id/camera_compat_dismiss_button"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:src="@drawable/camera_compat_dismiss_ripple"
-            android:background="@android:color/transparent"
-            android:contentDescription="@string/camera_compat_dismiss_button_description"/>
-
-    </LinearLayout>
-
     <include android:id="@+id/size_compat_hint"
         android:visibility="gone"
         android:layout_width="@dimen/compat_hint_width"
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml
index 864f7cd..eea3de8 100644
--- a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml
@@ -17,6 +17,7 @@
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
     xmlns:tools="http://schemas.android.com/tools"
+    android:id="@+id/handle_menu"
     android:layout_width="@dimen/desktop_mode_handle_menu_width"
     android:layout_height="wrap_content"
     android:clipChildren="false"
@@ -124,7 +125,7 @@
     <LinearLayout
         android:id="@+id/more_actions_pill"
         android:layout_width="match_parent"
-        android:layout_height="@dimen/desktop_mode_handle_menu_more_actions_pill_height"
+        android:layout_height="wrap_content"
         android:layout_marginTop="@dimen/desktop_mode_handle_menu_pill_spacing_margin"
         android:layout_marginStart="1dp"
         android:orientation="vertical"
@@ -138,6 +139,14 @@
             android:drawableStart="@drawable/desktop_mode_ic_handle_menu_screenshot"
             android:drawableTint="?androidprv:attr/materialColorOnSurface"
             style="@style/DesktopModeHandleMenuActionButton"/>
+
+        <Button
+            android:id="@+id/new_window_button"
+            android:contentDescription="@string/new_window_text"
+            android:text="@string/new_window_text"
+            android:drawableStart="@drawable/desktop_mode_ic_handle_menu_new_window"
+            android:drawableTint="?androidprv:attr/materialColorOnSurface"
+            style="@style/DesktopModeHandleMenuActionButton" />
     </LinearLayout>
 
     <LinearLayout
diff --git a/libs/WindowManager/Shell/res/values-af/strings.xml b/libs/WindowManager/Shell/res/values-af/strings.xml
index 6e8a679..e58ff6a 100644
--- a/libs/WindowManager/Shell/res/values-af/strings.xml
+++ b/libs/WindowManager/Shell/res/values-af/strings.xml
@@ -84,10 +84,8 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Borrel"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Bestuur"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Borrel is toegemaak."</string>
-    <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
-    <skip />
-    <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
-    <skip />
+    <string name="bubble_shortcut_label" msgid="666269077944378311">"Borrels"</string>
+    <string name="bubble_shortcut_long_label" msgid="6088437544312894043">"Wys borrels"</string>
     <string name="restart_button_description" msgid="4564728020654658478">"Tik om hierdie app te herbegin vir ’n beter aansig"</string>
     <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Verander hierdie app se aspekverhouding in Instellings"</string>
     <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Verander aspekverhouding"</string>
@@ -109,7 +107,7 @@
     <string name="minimize_button_text" msgid="271592547935841753">"Maak klein"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Maak toe"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Terug"</string>
-    <string name="handle_text" msgid="1766582106752184456">"Handvatsel"</string>
+    <string name="handle_text" msgid="4419667835599523257">"Apphandvatsel"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"Appikoon"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Volskerm"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Rekenaarmodus"</string>
@@ -118,8 +116,7 @@
     <string name="float_button_text" msgid="9221657008391364581">"Sweef"</string>
     <string name="select_text" msgid="5139083974039906583">"Kies"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"Skermskoot"</string>
-    <!-- no translation found for open_in_browser_text (9181692926376072904) -->
-    <skip />
+    <string name="open_in_browser_text" msgid="9181692926376072904">"Maak in blaaier oop"</string>
     <string name="close_text" msgid="4986518933445178928">"Maak toe"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Maak kieslys toe"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"Maak kieslys oop"</string>
diff --git a/libs/WindowManager/Shell/res/values-am/strings.xml b/libs/WindowManager/Shell/res/values-am/strings.xml
index ac7935d..3208ea9 100644
--- a/libs/WindowManager/Shell/res/values-am/strings.xml
+++ b/libs/WindowManager/Shell/res/values-am/strings.xml
@@ -107,7 +107,7 @@
     <string name="minimize_button_text" msgid="271592547935841753">"አሳንስ"</string>
     <string name="close_button_text" msgid="2913281996024033299">"ዝጋ"</string>
     <string name="back_button_text" msgid="1469718707134137085">"ተመለስ"</string>
-    <string name="handle_text" msgid="1766582106752184456">"መያዣ"</string>
+    <string name="handle_text" msgid="4419667835599523257">"የመተግበሪያ መያዣ"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"የመተግበሪያ አዶ"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"ሙሉ ማያ"</string>
     <string name="desktop_text" msgid="1077633567027630454">"የዴስክቶፕ ሁነታ"</string>
@@ -116,8 +116,7 @@
     <string name="float_button_text" msgid="9221657008391364581">"ተንሳፋፊ"</string>
     <string name="select_text" msgid="5139083974039906583">"ምረጥ"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"ቅጽበታዊ ገፅ ዕይታ"</string>
-    <!-- no translation found for open_in_browser_text (9181692926376072904) -->
-    <skip />
+    <string name="open_in_browser_text" msgid="9181692926376072904">"በአሳሽ ውስጥ ክፈት"</string>
     <string name="close_text" msgid="4986518933445178928">"ዝጋ"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"ምናሌ ዝጋ"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"ምናሌን ክፈት"</string>
diff --git a/libs/WindowManager/Shell/res/values-ar/strings.xml b/libs/WindowManager/Shell/res/values-ar/strings.xml
index 53ff6da..18db50e 100644
--- a/libs/WindowManager/Shell/res/values-ar/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ar/strings.xml
@@ -107,7 +107,7 @@
     <string name="minimize_button_text" msgid="271592547935841753">"تصغير"</string>
     <string name="close_button_text" msgid="2913281996024033299">"إغلاق"</string>
     <string name="back_button_text" msgid="1469718707134137085">"رجوع"</string>
-    <string name="handle_text" msgid="1766582106752184456">"مقبض"</string>
+    <string name="handle_text" msgid="4419667835599523257">"مقبض التطبيق"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"رمز التطبيق"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"ملء الشاشة"</string>
     <string name="desktop_text" msgid="1077633567027630454">"وضع سطح المكتب"</string>
@@ -116,8 +116,7 @@
     <string name="float_button_text" msgid="9221657008391364581">"نافذة عائمة"</string>
     <string name="select_text" msgid="5139083974039906583">"اختيار"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"لقطة شاشة"</string>
-    <!-- no translation found for open_in_browser_text (9181692926376072904) -->
-    <skip />
+    <string name="open_in_browser_text" msgid="9181692926376072904">"فتح في المتصفِّح"</string>
     <string name="close_text" msgid="4986518933445178928">"إغلاق"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"إغلاق القائمة"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"فتح القائمة"</string>
diff --git a/libs/WindowManager/Shell/res/values-as/strings.xml b/libs/WindowManager/Shell/res/values-as/strings.xml
index b94ab2e..1951772 100644
--- a/libs/WindowManager/Shell/res/values-as/strings.xml
+++ b/libs/WindowManager/Shell/res/values-as/strings.xml
@@ -107,7 +107,7 @@
     <string name="minimize_button_text" msgid="271592547935841753">"মিনিমাইজ কৰক"</string>
     <string name="close_button_text" msgid="2913281996024033299">"বন্ধ কৰক"</string>
     <string name="back_button_text" msgid="1469718707134137085">"উভতি যাওক"</string>
-    <string name="handle_text" msgid="1766582106752184456">"হেণ্ডেল"</string>
+    <string name="handle_text" msgid="4419667835599523257">"এপৰ হেণ্ডেল"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"এপৰ চিহ্ন"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"সম্পূৰ্ণ স্ক্ৰীন"</string>
     <string name="desktop_text" msgid="1077633567027630454">"ডেস্কটপ ম’ড"</string>
diff --git a/libs/WindowManager/Shell/res/values-az/strings.xml b/libs/WindowManager/Shell/res/values-az/strings.xml
index dd3e0e3..32e0dd6 100644
--- a/libs/WindowManager/Shell/res/values-az/strings.xml
+++ b/libs/WindowManager/Shell/res/values-az/strings.xml
@@ -107,7 +107,7 @@
     <string name="minimize_button_text" msgid="271592547935841753">"Kiçildin"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Bağlayın"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Geriyə"</string>
-    <string name="handle_text" msgid="1766582106752184456">"Hər kəsə açıq istifadəçi adı"</string>
+    <string name="handle_text" msgid="4419667835599523257">"Tətbiq ləqəbi"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"Tətbiq ikonası"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Tam Ekran"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Masaüstü Rejimi"</string>
@@ -116,8 +116,7 @@
     <string name="float_button_text" msgid="9221657008391364581">"Üzən pəncərə"</string>
     <string name="select_text" msgid="5139083974039906583">"Seçin"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"Skrinşot"</string>
-    <!-- no translation found for open_in_browser_text (9181692926376072904) -->
-    <skip />
+    <string name="open_in_browser_text" msgid="9181692926376072904">"Brauzerdə açın"</string>
     <string name="close_text" msgid="4986518933445178928">"Bağlayın"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Menyunu bağlayın"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"Menyunu açın"</string>
diff --git a/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml
index af695e7..1656e02 100644
--- a/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml
@@ -107,7 +107,7 @@
     <string name="minimize_button_text" msgid="271592547935841753">"Umanjite"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Zatvorite"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Nazad"</string>
-    <string name="handle_text" msgid="1766582106752184456">"Identifikator"</string>
+    <string name="handle_text" msgid="4419667835599523257">"Identifikator aplikacije"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"Ikona aplikacije"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Preko celog ekrana"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Režim za računare"</string>
diff --git a/libs/WindowManager/Shell/res/values-be/strings.xml b/libs/WindowManager/Shell/res/values-be/strings.xml
index dbbc07f..26f3d3c 100644
--- a/libs/WindowManager/Shell/res/values-be/strings.xml
+++ b/libs/WindowManager/Shell/res/values-be/strings.xml
@@ -107,7 +107,7 @@
     <string name="minimize_button_text" msgid="271592547935841753">"Згарнуць"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Закрыць"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Назад"</string>
-    <string name="handle_text" msgid="1766582106752184456">"Маркер"</string>
+    <string name="handle_text" msgid="4419667835599523257">"Маркер праграмы"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"Значок праграмы"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"На ўвесь экран"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Рэжым працоўнага стала"</string>
diff --git a/libs/WindowManager/Shell/res/values-bg/strings.xml b/libs/WindowManager/Shell/res/values-bg/strings.xml
index bba2f99..7c4f25e 100644
--- a/libs/WindowManager/Shell/res/values-bg/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bg/strings.xml
@@ -107,7 +107,7 @@
     <string name="minimize_button_text" msgid="271592547935841753">"Намаляване"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Затваряне"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Назад"</string>
-    <string name="handle_text" msgid="1766582106752184456">"Манипулатор"</string>
+    <string name="handle_text" msgid="4419667835599523257">"Манипулатор за приложението"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"Икона на приложението"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Цял екран"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Режим за настолни компютри"</string>
@@ -116,8 +116,7 @@
     <string name="float_button_text" msgid="9221657008391364581">"Плаващо"</string>
     <string name="select_text" msgid="5139083974039906583">"Избиране"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"Екранна снимка"</string>
-    <!-- no translation found for open_in_browser_text (9181692926376072904) -->
-    <skip />
+    <string name="open_in_browser_text" msgid="9181692926376072904">"Отваряне в браузър"</string>
     <string name="close_text" msgid="4986518933445178928">"Затваряне"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Затваряне на менюто"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"Отваряне на менюто"</string>
diff --git a/libs/WindowManager/Shell/res/values-bn/strings.xml b/libs/WindowManager/Shell/res/values-bn/strings.xml
index cb08182..4286162 100644
--- a/libs/WindowManager/Shell/res/values-bn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bn/strings.xml
@@ -107,7 +107,7 @@
     <string name="minimize_button_text" msgid="271592547935841753">"ছোট করুন"</string>
     <string name="close_button_text" msgid="2913281996024033299">"বন্ধ করুন"</string>
     <string name="back_button_text" msgid="1469718707134137085">"ফিরে যান"</string>
-    <string name="handle_text" msgid="1766582106752184456">"হাতল"</string>
+    <string name="handle_text" msgid="4419667835599523257">"অ্যাপের হ্যান্ডেল"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"অ্যাপ আইকন"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"ফুলস্ক্রিন"</string>
     <string name="desktop_text" msgid="1077633567027630454">"ডেস্কটপ মোড"</string>
@@ -116,8 +116,7 @@
     <string name="float_button_text" msgid="9221657008391364581">"ফ্লোট"</string>
     <string name="select_text" msgid="5139083974039906583">"বেছে নিন"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"স্ক্রিনশট"</string>
-    <!-- no translation found for open_in_browser_text (9181692926376072904) -->
-    <skip />
+    <string name="open_in_browser_text" msgid="9181692926376072904">"ব্রাউজারে খুলুন"</string>
     <string name="close_text" msgid="4986518933445178928">"বন্ধ করুন"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"\'মেনু\' বন্ধ করুন"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"মেনু খুলুন"</string>
diff --git a/libs/WindowManager/Shell/res/values-bs/strings.xml b/libs/WindowManager/Shell/res/values-bs/strings.xml
index e7505e5..11f5e48 100644
--- a/libs/WindowManager/Shell/res/values-bs/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bs/strings.xml
@@ -107,7 +107,7 @@
     <string name="minimize_button_text" msgid="271592547935841753">"Minimiziranje"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Zatvaranje"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Nazad"</string>
-    <string name="handle_text" msgid="1766582106752184456">"Identifikator"</string>
+    <string name="handle_text" msgid="4419667835599523257">"Ručica aplikacije"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"Ikona aplikacije"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Cijeli ekran"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Način rada radne površine"</string>
@@ -116,7 +116,7 @@
     <string name="float_button_text" msgid="9221657008391364581">"Lebdeći"</string>
     <string name="select_text" msgid="5139083974039906583">"Odabir"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"Snimak ekrana"</string>
-    <string name="open_in_browser_text" msgid="9181692926376072904">"Otvori u pregledniku"</string>
+    <string name="open_in_browser_text" msgid="9181692926376072904">"Otvaranje u pregledniku"</string>
     <string name="close_text" msgid="4986518933445178928">"Zatvaranje"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Zatvaranje menija"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"Otvaranje menija"</string>
diff --git a/libs/WindowManager/Shell/res/values-ca/strings.xml b/libs/WindowManager/Shell/res/values-ca/strings.xml
index ce9f547..e1fc7d3 100644
--- a/libs/WindowManager/Shell/res/values-ca/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ca/strings.xml
@@ -107,7 +107,7 @@
     <string name="minimize_button_text" msgid="271592547935841753">"Minimitza"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Tanca"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Enrere"</string>
-    <string name="handle_text" msgid="1766582106752184456">"Ansa"</string>
+    <string name="handle_text" msgid="4419667835599523257">"Identificador de l\'aplicació"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"Icona de l\'aplicació"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Pantalla completa"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Mode d\'escriptori"</string>
diff --git a/libs/WindowManager/Shell/res/values-cs/strings.xml b/libs/WindowManager/Shell/res/values-cs/strings.xml
index 06941f6..e428210 100644
--- a/libs/WindowManager/Shell/res/values-cs/strings.xml
+++ b/libs/WindowManager/Shell/res/values-cs/strings.xml
@@ -107,7 +107,7 @@
     <string name="minimize_button_text" msgid="271592547935841753">"Minimalizovat"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Zavřít"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Zpět"</string>
-    <string name="handle_text" msgid="1766582106752184456">"Úchyt"</string>
+    <string name="handle_text" msgid="4419667835599523257">"Popisovač aplikace"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"Ikona aplikace"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Celá obrazovka"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Režim počítače"</string>
@@ -116,8 +116,7 @@
     <string name="float_button_text" msgid="9221657008391364581">"Plovoucí"</string>
     <string name="select_text" msgid="5139083974039906583">"Vybrat"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"Snímek obrazovky"</string>
-    <!-- no translation found for open_in_browser_text (9181692926376072904) -->
-    <skip />
+    <string name="open_in_browser_text" msgid="9181692926376072904">"Otevřít v prohlížeči"</string>
     <string name="close_text" msgid="4986518933445178928">"Zavřít"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Zavřít nabídku"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"Otevřít nabídku"</string>
diff --git a/libs/WindowManager/Shell/res/values-da/strings.xml b/libs/WindowManager/Shell/res/values-da/strings.xml
index 7883bb0..36c0330 100644
--- a/libs/WindowManager/Shell/res/values-da/strings.xml
+++ b/libs/WindowManager/Shell/res/values-da/strings.xml
@@ -107,7 +107,7 @@
     <string name="minimize_button_text" msgid="271592547935841753">"Minimer"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Luk"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Tilbage"</string>
-    <string name="handle_text" msgid="1766582106752184456">"Håndtag"</string>
+    <string name="handle_text" msgid="4419667835599523257">"Apphåndtag"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"Appikon"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Fuld skærm"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Computertilstand"</string>
@@ -116,8 +116,7 @@
     <string name="float_button_text" msgid="9221657008391364581">"Svævende"</string>
     <string name="select_text" msgid="5139083974039906583">"Vælg"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"Screenshot"</string>
-    <!-- no translation found for open_in_browser_text (9181692926376072904) -->
-    <skip />
+    <string name="open_in_browser_text" msgid="9181692926376072904">"Åbn i browser"</string>
     <string name="close_text" msgid="4986518933445178928">"Luk"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Luk menu"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"Åbn menu"</string>
diff --git a/libs/WindowManager/Shell/res/values-de/strings.xml b/libs/WindowManager/Shell/res/values-de/strings.xml
index 6bb8523..0ad112c 100644
--- a/libs/WindowManager/Shell/res/values-de/strings.xml
+++ b/libs/WindowManager/Shell/res/values-de/strings.xml
@@ -107,7 +107,7 @@
     <string name="minimize_button_text" msgid="271592547935841753">"Minimieren"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Schließen"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Zurück"</string>
-    <string name="handle_text" msgid="1766582106752184456">"Ziehpunkt"</string>
+    <string name="handle_text" msgid="4419667835599523257">"App-Ziehpunkt"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"App-Symbol"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Vollbild"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Desktopmodus"</string>
@@ -116,8 +116,7 @@
     <string name="float_button_text" msgid="9221657008391364581">"Frei schwebend"</string>
     <string name="select_text" msgid="5139083974039906583">"Auswählen"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"Screenshot"</string>
-    <!-- no translation found for open_in_browser_text (9181692926376072904) -->
-    <skip />
+    <string name="open_in_browser_text" msgid="9181692926376072904">"Im Browser öffnen"</string>
     <string name="close_text" msgid="4986518933445178928">"Schließen"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Menü schließen"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"Menü öffnen"</string>
diff --git a/libs/WindowManager/Shell/res/values-el/strings.xml b/libs/WindowManager/Shell/res/values-el/strings.xml
index a640b94..1e2fec1 100644
--- a/libs/WindowManager/Shell/res/values-el/strings.xml
+++ b/libs/WindowManager/Shell/res/values-el/strings.xml
@@ -107,7 +107,7 @@
     <string name="minimize_button_text" msgid="271592547935841753">"Ελαχιστοποίηση"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Κλείσιμο"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Πίσω"</string>
-    <string name="handle_text" msgid="1766582106752184456">"Λαβή"</string>
+    <string name="handle_text" msgid="4419667835599523257">"Λαβή εφαρμογής"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"Εικονίδιο εφαρμογής"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Πλήρης οθόνη"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Λειτουργία επιφάνειας εργασίας"</string>
@@ -116,8 +116,7 @@
     <string name="float_button_text" msgid="9221657008391364581">"Κινούμενο"</string>
     <string name="select_text" msgid="5139083974039906583">"Επιλογή"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"Στιγμιότυπο οθόνης"</string>
-    <!-- no translation found for open_in_browser_text (9181692926376072904) -->
-    <skip />
+    <string name="open_in_browser_text" msgid="9181692926376072904">"Άνοιγμα σε πρόγραμμα περιήγησης"</string>
     <string name="close_text" msgid="4986518933445178928">"Κλείσιμο"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Κλείσιμο μενού"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"Άνοιγμα μενού"</string>
diff --git a/libs/WindowManager/Shell/res/values-en-rAU/strings.xml b/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
index 4708d5f..71701c9 100644
--- a/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
@@ -107,7 +107,7 @@
     <string name="minimize_button_text" msgid="271592547935841753">"Minimise"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Close"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Back"</string>
-    <string name="handle_text" msgid="1766582106752184456">"Handle"</string>
+    <string name="handle_text" msgid="4419667835599523257">"App handle"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"App icon"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Full screen"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Desktop mode"</string>
@@ -116,8 +116,7 @@
     <string name="float_button_text" msgid="9221657008391364581">"Float"</string>
     <string name="select_text" msgid="5139083974039906583">"Select"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"Screenshot"</string>
-    <!-- no translation found for open_in_browser_text (9181692926376072904) -->
-    <skip />
+    <string name="open_in_browser_text" msgid="9181692926376072904">"Open in browser"</string>
     <string name="close_text" msgid="4986518933445178928">"Close"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Close menu"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"Open menu"</string>
diff --git a/libs/WindowManager/Shell/res/values-en-rCA/strings.xml b/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
index bd8a636..5ab4af3 100644
--- a/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
@@ -107,7 +107,7 @@
     <string name="minimize_button_text" msgid="271592547935841753">"Minimize"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Close"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Back"</string>
-    <string name="handle_text" msgid="1766582106752184456">"Handle"</string>
+    <string name="handle_text" msgid="4419667835599523257">"App handle"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"App Icon"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Fullscreen"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Desktop Mode"</string>
diff --git a/libs/WindowManager/Shell/res/values-en-rGB/strings.xml b/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
index 4708d5f..71701c9 100644
--- a/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
@@ -107,7 +107,7 @@
     <string name="minimize_button_text" msgid="271592547935841753">"Minimise"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Close"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Back"</string>
-    <string name="handle_text" msgid="1766582106752184456">"Handle"</string>
+    <string name="handle_text" msgid="4419667835599523257">"App handle"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"App icon"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Full screen"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Desktop mode"</string>
@@ -116,8 +116,7 @@
     <string name="float_button_text" msgid="9221657008391364581">"Float"</string>
     <string name="select_text" msgid="5139083974039906583">"Select"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"Screenshot"</string>
-    <!-- no translation found for open_in_browser_text (9181692926376072904) -->
-    <skip />
+    <string name="open_in_browser_text" msgid="9181692926376072904">"Open in browser"</string>
     <string name="close_text" msgid="4986518933445178928">"Close"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Close menu"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"Open menu"</string>
diff --git a/libs/WindowManager/Shell/res/values-en-rIN/strings.xml b/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
index 4708d5f..71701c9 100644
--- a/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
@@ -107,7 +107,7 @@
     <string name="minimize_button_text" msgid="271592547935841753">"Minimise"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Close"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Back"</string>
-    <string name="handle_text" msgid="1766582106752184456">"Handle"</string>
+    <string name="handle_text" msgid="4419667835599523257">"App handle"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"App icon"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Full screen"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Desktop mode"</string>
@@ -116,8 +116,7 @@
     <string name="float_button_text" msgid="9221657008391364581">"Float"</string>
     <string name="select_text" msgid="5139083974039906583">"Select"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"Screenshot"</string>
-    <!-- no translation found for open_in_browser_text (9181692926376072904) -->
-    <skip />
+    <string name="open_in_browser_text" msgid="9181692926376072904">"Open in browser"</string>
     <string name="close_text" msgid="4986518933445178928">"Close"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Close menu"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"Open menu"</string>
diff --git a/libs/WindowManager/Shell/res/values-en-rXC/strings.xml b/libs/WindowManager/Shell/res/values-en-rXC/strings.xml
index 3503080..42d5f06 100644
--- a/libs/WindowManager/Shell/res/values-en-rXC/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rXC/strings.xml
@@ -107,7 +107,7 @@
     <string name="minimize_button_text" msgid="271592547935841753">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‎‏‏‏‏‎‎‎‏‎‎‏‏‏‎‎‎‏‏‏‏‏‏‏‎‏‏‏‎‏‏‏‏‏‏‎‏‎‏‏‎‏‏‏‏‏‎‏‏‎‏‏‏‎‏‏‎‎‏‎Minimize‎‏‎‎‏‎"</string>
     <string name="close_button_text" msgid="2913281996024033299">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‎‎‎‏‏‎‏‏‏‎‎‎‎‎‏‏‏‎‏‎‎‎‏‎‏‎‎‏‎‎‎‏‏‏‏‎‎‎‏‏‎‏‏‎‏‏‎‎‎‎‎‎‎‏‎‎‏‏‎Close‎‏‎‎‏‎"</string>
     <string name="back_button_text" msgid="1469718707134137085">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‎‎‎‏‏‎‎‏‎‏‎‏‏‏‏‏‎‏‎‏‏‎‎‎‎‎‏‎‎‏‎‎‏‎‎‏‏‏‏‎‏‎‎‎‎‎‎‎‏‎‏‏‏‏‏‏‎‏‎Back‎‏‎‎‏‎"</string>
-    <string name="handle_text" msgid="1766582106752184456">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‎‎‏‎‎‎‎‏‎‎‎‎‏‎‏‎‎‏‎‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‎‎‎‏‏‎‎‏‏‏‎‎‎‎‏‎‎‎‏‎‎‎‎Handle‎‏‎‎‏‎"</string>
+    <string name="handle_text" msgid="4419667835599523257">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‎‏‎‏‎‏‎‏‎‏‏‏‎‏‎‎‎‎‎‏‏‏‏‎‏‎‏‏‏‎‎‏‏‎‎‎‏‎‎‎‎‏‎‎‎‎‏‏‎‏‏‎‏‏‏‎‎‏‎App handle‎‏‎‎‏‎"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‏‏‎‎‏‎‏‏‏‎‎‏‎‎‎‎‏‏‎‏‎‎‏‏‏‏‎‎‏‏‏‎‎‎‎‎‏‎‏‏‎‎‏‎‏‏‎‏‏‏‏‎‏‎‎‎‏‏‎App Icon‎‏‎‎‏‎"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‎‎‎‎‏‎‎‎‎‏‎‏‏‎‎‎‎‎‏‏‎‏‏‏‎‏‏‏‏‏‎‎‏‎‏‏‏‎‏‎‎‎‏‎‏‏‏‎‏‏‎‎‏‎‏‏‏‏‎Fullscreen‎‏‎‎‏‎"</string>
     <string name="desktop_text" msgid="1077633567027630454">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‏‎‏‏‏‏‎‏‎‎‏‎‎‎‎‏‏‎‎‎‎‎‎‏‎‏‎‎‎‎‏‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‎‏‏‏‎‏‏‎‎Desktop Mode‎‏‎‎‏‎"</string>
diff --git a/libs/WindowManager/Shell/res/values-es-rUS/strings.xml b/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
index 72bafcd..1e30912 100644
--- a/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
+++ b/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
@@ -107,7 +107,7 @@
     <string name="minimize_button_text" msgid="271592547935841753">"Minimizar"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Cerrar"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Atrás"</string>
-    <string name="handle_text" msgid="1766582106752184456">"Controlador"</string>
+    <string name="handle_text" msgid="4419667835599523257">"Controlador de la app"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"Ícono de la app"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Pantalla completa"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Modo de escritorio"</string>
@@ -116,8 +116,7 @@
     <string name="float_button_text" msgid="9221657008391364581">"Flotante"</string>
     <string name="select_text" msgid="5139083974039906583">"Seleccionar"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"Captura de pantalla"</string>
-    <!-- no translation found for open_in_browser_text (9181692926376072904) -->
-    <skip />
+    <string name="open_in_browser_text" msgid="9181692926376072904">"Abrir en el navegador"</string>
     <string name="close_text" msgid="4986518933445178928">"Cerrar"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Cerrar menú"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"Abrir el menú"</string>
diff --git a/libs/WindowManager/Shell/res/values-es/strings.xml b/libs/WindowManager/Shell/res/values-es/strings.xml
index d907a56..312e297 100644
--- a/libs/WindowManager/Shell/res/values-es/strings.xml
+++ b/libs/WindowManager/Shell/res/values-es/strings.xml
@@ -107,7 +107,7 @@
     <string name="minimize_button_text" msgid="271592547935841753">"Minimizar"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Cerrar"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Atrás"</string>
-    <string name="handle_text" msgid="1766582106752184456">"Controlador"</string>
+    <string name="handle_text" msgid="4419667835599523257">"Controlador de la aplicación"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"Icono de la aplicación"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Pantalla completa"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Modo Escritorio"</string>
@@ -116,8 +116,7 @@
     <string name="float_button_text" msgid="9221657008391364581">"Flotante"</string>
     <string name="select_text" msgid="5139083974039906583">"Seleccionar"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"Captura de pantalla"</string>
-    <!-- no translation found for open_in_browser_text (9181692926376072904) -->
-    <skip />
+    <string name="open_in_browser_text" msgid="9181692926376072904">"Abrir en el navegador"</string>
     <string name="close_text" msgid="4986518933445178928">"Cerrar"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Cerrar menú"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"Abrir menú"</string>
diff --git a/libs/WindowManager/Shell/res/values-et/strings.xml b/libs/WindowManager/Shell/res/values-et/strings.xml
index 7d8103e..5e4ef81 100644
--- a/libs/WindowManager/Shell/res/values-et/strings.xml
+++ b/libs/WindowManager/Shell/res/values-et/strings.xml
@@ -107,7 +107,7 @@
     <string name="minimize_button_text" msgid="271592547935841753">"Minimeeri"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Sule"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Tagasi"</string>
-    <string name="handle_text" msgid="1766582106752184456">"Käepide"</string>
+    <string name="handle_text" msgid="4419667835599523257">"Rakenduse element"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"Rakenduse ikoon"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Täisekraan"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Lauaarvuti režiim"</string>
@@ -116,8 +116,7 @@
     <string name="float_button_text" msgid="9221657008391364581">"Hõljuv"</string>
     <string name="select_text" msgid="5139083974039906583">"Vali"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"Ekraanipilt"</string>
-    <!-- no translation found for open_in_browser_text (9181692926376072904) -->
-    <skip />
+    <string name="open_in_browser_text" msgid="9181692926376072904">"Avamine brauseris"</string>
     <string name="close_text" msgid="4986518933445178928">"Sule"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Sule menüü"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"Ava menüü"</string>
diff --git a/libs/WindowManager/Shell/res/values-eu/strings.xml b/libs/WindowManager/Shell/res/values-eu/strings.xml
index 19e028f..40d67a2 100644
--- a/libs/WindowManager/Shell/res/values-eu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-eu/strings.xml
@@ -107,7 +107,7 @@
     <string name="minimize_button_text" msgid="271592547935841753">"Minimizatu"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Itxi"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Atzera"</string>
-    <string name="handle_text" msgid="1766582106752184456">"Kontu-izena"</string>
+    <string name="handle_text" msgid="4419667835599523257">"Aplikazioaren kontrol-puntua"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"Aplikazioaren ikonoa"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Pantaila osoa"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Ordenagailuetarako modua"</string>
@@ -116,8 +116,7 @@
     <string name="float_button_text" msgid="9221657008391364581">"Leiho gainerakorra"</string>
     <string name="select_text" msgid="5139083974039906583">"Hautatu"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"Pantaila-argazkia"</string>
-    <!-- no translation found for open_in_browser_text (9181692926376072904) -->
-    <skip />
+    <string name="open_in_browser_text" msgid="9181692926376072904">"Ireki arakatzailean"</string>
     <string name="close_text" msgid="4986518933445178928">"Itxi"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Itxi menua"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"Ireki menua"</string>
diff --git a/libs/WindowManager/Shell/res/values-fa/strings.xml b/libs/WindowManager/Shell/res/values-fa/strings.xml
index 2bb9415..55da319 100644
--- a/libs/WindowManager/Shell/res/values-fa/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fa/strings.xml
@@ -107,7 +107,7 @@
     <string name="minimize_button_text" msgid="271592547935841753">"کوچک کردن"</string>
     <string name="close_button_text" msgid="2913281996024033299">"بستن"</string>
     <string name="back_button_text" msgid="1469718707134137085">"برگشتن"</string>
-    <string name="handle_text" msgid="1766582106752184456">"دستگیره"</string>
+    <string name="handle_text" msgid="4419667835599523257">"دستگیره برنامه"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"نماد برنامه"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"تمام‌صفحه"</string>
     <string name="desktop_text" msgid="1077633567027630454">"حالت رایانه"</string>
@@ -116,8 +116,7 @@
     <string name="float_button_text" msgid="9221657008391364581">"شناور"</string>
     <string name="select_text" msgid="5139083974039906583">"انتخاب"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"نماگرفت"</string>
-    <!-- no translation found for open_in_browser_text (9181692926376072904) -->
-    <skip />
+    <string name="open_in_browser_text" msgid="9181692926376072904">"باز کردن در مرورگر"</string>
     <string name="close_text" msgid="4986518933445178928">"بستن"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"بستن منو"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"باز کردن منو"</string>
diff --git a/libs/WindowManager/Shell/res/values-fi/strings.xml b/libs/WindowManager/Shell/res/values-fi/strings.xml
index fcc4150..c2610ff 100644
--- a/libs/WindowManager/Shell/res/values-fi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fi/strings.xml
@@ -107,7 +107,7 @@
     <string name="minimize_button_text" msgid="271592547935841753">"Pienennä"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Sulje"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Takaisin"</string>
-    <string name="handle_text" msgid="1766582106752184456">"Kahva"</string>
+    <string name="handle_text" msgid="4419667835599523257">"Sovelluksen tunnus"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"Sovelluskuvake"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Koko näyttö"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Työpöytätila"</string>
@@ -116,8 +116,7 @@
     <string name="float_button_text" msgid="9221657008391364581">"Kelluva ikkuna"</string>
     <string name="select_text" msgid="5139083974039906583">"Valitse"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"Kuvakaappaus"</string>
-    <!-- no translation found for open_in_browser_text (9181692926376072904) -->
-    <skip />
+    <string name="open_in_browser_text" msgid="9181692926376072904">"Avaa selaimessa"</string>
     <string name="close_text" msgid="4986518933445178928">"Sulje"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Sulje valikko"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"Avaa valikko"</string>
diff --git a/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml b/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
index 268b409..7a59b62 100644
--- a/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
@@ -107,7 +107,7 @@
     <string name="minimize_button_text" msgid="271592547935841753">"Réduire"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Fermer"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Retour"</string>
-    <string name="handle_text" msgid="1766582106752184456">"Identifiant"</string>
+    <string name="handle_text" msgid="4419667835599523257">"Poignée de l\'appli"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"Icône de l\'appli"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Plein écran"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Mode Bureau"</string>
@@ -116,8 +116,7 @@
     <string name="float_button_text" msgid="9221657008391364581">"Flottant"</string>
     <string name="select_text" msgid="5139083974039906583">"Sélectionner"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"Capture d\'écran"</string>
-    <!-- no translation found for open_in_browser_text (9181692926376072904) -->
-    <skip />
+    <string name="open_in_browser_text" msgid="9181692926376072904">"Ouvrir dans le navigateur"</string>
     <string name="close_text" msgid="4986518933445178928">"Fermer"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Fermer le menu"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"Ouvrir le menu"</string>
diff --git a/libs/WindowManager/Shell/res/values-fr/strings.xml b/libs/WindowManager/Shell/res/values-fr/strings.xml
index 1762a29..0cf944f 100644
--- a/libs/WindowManager/Shell/res/values-fr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fr/strings.xml
@@ -107,7 +107,7 @@
     <string name="minimize_button_text" msgid="271592547935841753">"Réduire"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Fermer"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Retour"</string>
-    <string name="handle_text" msgid="1766582106752184456">"Poignée"</string>
+    <string name="handle_text" msgid="4419667835599523257">"Poignée de l\'appli"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"Icône d\'application"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Plein écran"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Mode ordinateur"</string>
@@ -116,8 +116,7 @@
     <string name="float_button_text" msgid="9221657008391364581">"Flottante"</string>
     <string name="select_text" msgid="5139083974039906583">"Sélectionner"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"Capture d\'écran"</string>
-    <!-- no translation found for open_in_browser_text (9181692926376072904) -->
-    <skip />
+    <string name="open_in_browser_text" msgid="9181692926376072904">"Ouvrir dans un navigateur"</string>
     <string name="close_text" msgid="4986518933445178928">"Fermer"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Fermer le menu"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"Ouvrir le menu"</string>
diff --git a/libs/WindowManager/Shell/res/values-gl/strings.xml b/libs/WindowManager/Shell/res/values-gl/strings.xml
index 94e7ad5..0f2a601 100644
--- a/libs/WindowManager/Shell/res/values-gl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-gl/strings.xml
@@ -84,10 +84,8 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Burbulla"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Xestionar"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Ignorouse a burbulla."</string>
-    <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
-    <skip />
-    <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
-    <skip />
+    <string name="bubble_shortcut_label" msgid="666269077944378311">"Burbullas"</string>
+    <string name="bubble_shortcut_long_label" msgid="6088437544312894043">"Mostrar burbullas"</string>
     <string name="restart_button_description" msgid="4564728020654658478">"Toca o botón para reiniciar esta aplicación e gozar dunha mellor visualización"</string>
     <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Cambia a proporción desta aplicación en Configuración"</string>
     <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Cambiar a proporción"</string>
@@ -109,7 +107,7 @@
     <string name="minimize_button_text" msgid="271592547935841753">"Minimizar"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Pechar"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Atrás"</string>
-    <string name="handle_text" msgid="1766582106752184456">"Controlador"</string>
+    <string name="handle_text" msgid="4419667835599523257">"Controlador da aplicación"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"Icona de aplicación"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Pantalla completa"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Modo de escritorio"</string>
diff --git a/libs/WindowManager/Shell/res/values-gu/strings.xml b/libs/WindowManager/Shell/res/values-gu/strings.xml
index 8a03a4d..6151da0 100644
--- a/libs/WindowManager/Shell/res/values-gu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-gu/strings.xml
@@ -107,7 +107,7 @@
     <string name="minimize_button_text" msgid="271592547935841753">"નાનું કરો"</string>
     <string name="close_button_text" msgid="2913281996024033299">"બંધ કરો"</string>
     <string name="back_button_text" msgid="1469718707134137085">"પાછળ"</string>
-    <string name="handle_text" msgid="1766582106752184456">"હૅન્ડલ"</string>
+    <string name="handle_text" msgid="4419667835599523257">"ઍપનું હૅન્ડલ"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"ઍપનું આઇકન"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"પૂર્ણસ્ક્રીન"</string>
     <string name="desktop_text" msgid="1077633567027630454">"ડેસ્કટૉપ મોડ"</string>
@@ -116,8 +116,7 @@
     <string name="float_button_text" msgid="9221657008391364581">"ફ્લોટિંગ વિન્ડો"</string>
     <string name="select_text" msgid="5139083974039906583">"પસંદ કરો"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"સ્ક્રીનશૉટ"</string>
-    <!-- no translation found for open_in_browser_text (9181692926376072904) -->
-    <skip />
+    <string name="open_in_browser_text" msgid="9181692926376072904">"બ્રાઉઝરમાં ખોલો"</string>
     <string name="close_text" msgid="4986518933445178928">"બંધ કરો"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"મેનૂ બંધ કરો"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"મેનૂ ખોલો"</string>
diff --git a/libs/WindowManager/Shell/res/values-hi/strings.xml b/libs/WindowManager/Shell/res/values-hi/strings.xml
index b73f449..04e76bc 100644
--- a/libs/WindowManager/Shell/res/values-hi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hi/strings.xml
@@ -107,7 +107,7 @@
     <string name="minimize_button_text" msgid="271592547935841753">"विंडो छोटी करें"</string>
     <string name="close_button_text" msgid="2913281996024033299">"बंद करें"</string>
     <string name="back_button_text" msgid="1469718707134137085">"वापस जाएं"</string>
-    <string name="handle_text" msgid="1766582106752184456">"हैंडल"</string>
+    <string name="handle_text" msgid="4419667835599523257">"ऐप्लिकेशन का हैंडल"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"ऐप्लिकेशन आइकॉन"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"फ़ुलस्क्रीन"</string>
     <string name="desktop_text" msgid="1077633567027630454">"डेस्कटॉप मोड"</string>
@@ -116,8 +116,7 @@
     <string name="float_button_text" msgid="9221657008391364581">"फ़्लोट"</string>
     <string name="select_text" msgid="5139083974039906583">"चुनें"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"स्क्रीनशॉट"</string>
-    <!-- no translation found for open_in_browser_text (9181692926376072904) -->
-    <skip />
+    <string name="open_in_browser_text" msgid="9181692926376072904">"ब्राउज़र में खोलें"</string>
     <string name="close_text" msgid="4986518933445178928">"बंद करें"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"मेन्यू बंद करें"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"मेन्यू खोलें"</string>
diff --git a/libs/WindowManager/Shell/res/values-hr/strings.xml b/libs/WindowManager/Shell/res/values-hr/strings.xml
index 000b0e6..aa2ee17 100644
--- a/libs/WindowManager/Shell/res/values-hr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hr/strings.xml
@@ -107,7 +107,7 @@
     <string name="minimize_button_text" msgid="271592547935841753">"Minimiziraj"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Zatvori"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Natrag"</string>
-    <string name="handle_text" msgid="1766582106752184456">"Pokazivač"</string>
+    <string name="handle_text" msgid="4419667835599523257">"Pokazivač aplikacije"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"Ikona aplikacije"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Puni zaslon"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Stolni način rada"</string>
diff --git a/libs/WindowManager/Shell/res/values-hu/strings.xml b/libs/WindowManager/Shell/res/values-hu/strings.xml
index f4cf754..8ffeeed 100644
--- a/libs/WindowManager/Shell/res/values-hu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hu/strings.xml
@@ -107,7 +107,7 @@
     <string name="minimize_button_text" msgid="271592547935841753">"Kis méret"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Bezárás"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Vissza"</string>
-    <string name="handle_text" msgid="1766582106752184456">"Fogópont"</string>
+    <string name="handle_text" msgid="4419667835599523257">"App fogópontja"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"Alkalmazásikon"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Teljes képernyő"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Asztali üzemmód"</string>
@@ -116,8 +116,7 @@
     <string name="float_button_text" msgid="9221657008391364581">"Lebegő"</string>
     <string name="select_text" msgid="5139083974039906583">"Kiválasztás"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"Képernyőkép"</string>
-    <!-- no translation found for open_in_browser_text (9181692926376072904) -->
-    <skip />
+    <string name="open_in_browser_text" msgid="9181692926376072904">"Megnyitás böngészőben"</string>
     <string name="close_text" msgid="4986518933445178928">"Bezárás"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Menü bezárása"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"Menü megnyitása"</string>
diff --git a/libs/WindowManager/Shell/res/values-hy/strings.xml b/libs/WindowManager/Shell/res/values-hy/strings.xml
index 1d2d2ff..b3fccfa 100644
--- a/libs/WindowManager/Shell/res/values-hy/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hy/strings.xml
@@ -107,7 +107,7 @@
     <string name="minimize_button_text" msgid="271592547935841753">"Ծալել"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Փակել"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Հետ"</string>
-    <string name="handle_text" msgid="1766582106752184456">"Նշիչ"</string>
+    <string name="handle_text" msgid="4419667835599523257">"Հավելվածի կեղծանուն"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"Հավելվածի պատկերակ"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Լիաէկրան"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Համակարգչի ռեժիմ"</string>
diff --git a/libs/WindowManager/Shell/res/values-in/strings.xml b/libs/WindowManager/Shell/res/values-in/strings.xml
index 0e662f6..38b24f9 100644
--- a/libs/WindowManager/Shell/res/values-in/strings.xml
+++ b/libs/WindowManager/Shell/res/values-in/strings.xml
@@ -107,7 +107,7 @@
     <string name="minimize_button_text" msgid="271592547935841753">"Minimalkan"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Tutup"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Kembali"</string>
-    <string name="handle_text" msgid="1766582106752184456">"Tuas"</string>
+    <string name="handle_text" msgid="4419667835599523257">"Penanganan aplikasi"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"Ikon Aplikasi"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Layar Penuh"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Mode Desktop"</string>
@@ -116,8 +116,7 @@
     <string name="float_button_text" msgid="9221657008391364581">"Mengambang"</string>
     <string name="select_text" msgid="5139083974039906583">"Pilih"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"Screenshot"</string>
-    <!-- no translation found for open_in_browser_text (9181692926376072904) -->
-    <skip />
+    <string name="open_in_browser_text" msgid="9181692926376072904">"Buka di browser"</string>
     <string name="close_text" msgid="4986518933445178928">"Tutup"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Tutup Menu"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"Buka Menu"</string>
diff --git a/libs/WindowManager/Shell/res/values-is/strings.xml b/libs/WindowManager/Shell/res/values-is/strings.xml
index fb1df4d..5b1f4d2 100644
--- a/libs/WindowManager/Shell/res/values-is/strings.xml
+++ b/libs/WindowManager/Shell/res/values-is/strings.xml
@@ -107,7 +107,7 @@
     <string name="minimize_button_text" msgid="271592547935841753">"Minnka"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Loka"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Til baka"</string>
-    <string name="handle_text" msgid="1766582106752184456">"Handfang"</string>
+    <string name="handle_text" msgid="4419667835599523257">"Handfang forrits"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"Tákn forrits"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Allur skjárinn"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Skjáborðsstilling"</string>
@@ -116,8 +116,7 @@
     <string name="float_button_text" msgid="9221657008391364581">"Reikult"</string>
     <string name="select_text" msgid="5139083974039906583">"Velja"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"Skjámynd"</string>
-    <!-- no translation found for open_in_browser_text (9181692926376072904) -->
-    <skip />
+    <string name="open_in_browser_text" msgid="9181692926376072904">"Opna í vafra"</string>
     <string name="close_text" msgid="4986518933445178928">"Loka"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Loka valmynd"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"Opna valmynd"</string>
diff --git a/libs/WindowManager/Shell/res/values-it/strings.xml b/libs/WindowManager/Shell/res/values-it/strings.xml
index 9c825be..adc9569 100644
--- a/libs/WindowManager/Shell/res/values-it/strings.xml
+++ b/libs/WindowManager/Shell/res/values-it/strings.xml
@@ -107,7 +107,7 @@
     <string name="minimize_button_text" msgid="271592547935841753">"Riduci a icona"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Chiudi"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Indietro"</string>
-    <string name="handle_text" msgid="1766582106752184456">"Handle"</string>
+    <string name="handle_text" msgid="4419667835599523257">"Punto di manipolazione app"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"Icona dell\'app"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Schermo intero"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Modalità desktop"</string>
@@ -116,8 +116,7 @@
     <string name="float_button_text" msgid="9221657008391364581">"Mobile"</string>
     <string name="select_text" msgid="5139083974039906583">"Seleziona"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"Screenshot"</string>
-    <!-- no translation found for open_in_browser_text (9181692926376072904) -->
-    <skip />
+    <string name="open_in_browser_text" msgid="9181692926376072904">"Apri nel browser"</string>
     <string name="close_text" msgid="4986518933445178928">"Chiudi"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Chiudi il menu"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"Apri menu"</string>
diff --git a/libs/WindowManager/Shell/res/values-iw/strings.xml b/libs/WindowManager/Shell/res/values-iw/strings.xml
index abd0f6b..9bae1c9 100644
--- a/libs/WindowManager/Shell/res/values-iw/strings.xml
+++ b/libs/WindowManager/Shell/res/values-iw/strings.xml
@@ -107,7 +107,7 @@
     <string name="minimize_button_text" msgid="271592547935841753">"מזעור"</string>
     <string name="close_button_text" msgid="2913281996024033299">"סגירה"</string>
     <string name="back_button_text" msgid="1469718707134137085">"חזרה"</string>
-    <string name="handle_text" msgid="1766582106752184456">"נקודת אחיזה"</string>
+    <string name="handle_text" msgid="4419667835599523257">"נקודת אחיזה לאפליקציה"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"סמל האפליקציה"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"מסך מלא"</string>
     <string name="desktop_text" msgid="1077633567027630454">"ממשק המחשב"</string>
@@ -116,8 +116,7 @@
     <string name="float_button_text" msgid="9221657008391364581">"בלונים"</string>
     <string name="select_text" msgid="5139083974039906583">"בחירה"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"צילום מסך"</string>
-    <!-- no translation found for open_in_browser_text (9181692926376072904) -->
-    <skip />
+    <string name="open_in_browser_text" msgid="9181692926376072904">"פתיחה בדפדפן"</string>
     <string name="close_text" msgid="4986518933445178928">"סגירה"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"סגירת התפריט"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"פתיחת התפריט"</string>
diff --git a/libs/WindowManager/Shell/res/values-ja/strings.xml b/libs/WindowManager/Shell/res/values-ja/strings.xml
index 55fd8b1..fff794e 100644
--- a/libs/WindowManager/Shell/res/values-ja/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ja/strings.xml
@@ -107,7 +107,7 @@
     <string name="minimize_button_text" msgid="271592547935841753">"最小化"</string>
     <string name="close_button_text" msgid="2913281996024033299">"閉じる"</string>
     <string name="back_button_text" msgid="1469718707134137085">"戻る"</string>
-    <string name="handle_text" msgid="1766582106752184456">"ハンドル"</string>
+    <string name="handle_text" msgid="4419667835599523257">"アプリハンドル"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"アプリのアイコン"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"全画面表示"</string>
     <string name="desktop_text" msgid="1077633567027630454">"デスクトップ モード"</string>
diff --git a/libs/WindowManager/Shell/res/values-ka/strings.xml b/libs/WindowManager/Shell/res/values-ka/strings.xml
index 2354208..a73c4f0 100644
--- a/libs/WindowManager/Shell/res/values-ka/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ka/strings.xml
@@ -107,7 +107,7 @@
     <string name="minimize_button_text" msgid="271592547935841753">"ჩაკეცვა"</string>
     <string name="close_button_text" msgid="2913281996024033299">"დახურვა"</string>
     <string name="back_button_text" msgid="1469718707134137085">"უკან"</string>
-    <string name="handle_text" msgid="1766582106752184456">"იდენტიფიკატორი"</string>
+    <string name="handle_text" msgid="4419667835599523257">"აპის იდენტიფიკატორი"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"აპის ხატულა"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"სრულ ეკრანზე"</string>
     <string name="desktop_text" msgid="1077633567027630454">"დესკტოპის რეჟიმი"</string>
diff --git a/libs/WindowManager/Shell/res/values-kk/strings.xml b/libs/WindowManager/Shell/res/values-kk/strings.xml
index 1818373..5a7197e 100644
--- a/libs/WindowManager/Shell/res/values-kk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-kk/strings.xml
@@ -107,7 +107,7 @@
     <string name="minimize_button_text" msgid="271592547935841753">"Кішірейту"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Жабу"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Артқа"</string>
-    <string name="handle_text" msgid="1766582106752184456">"Идентификатор"</string>
+    <string name="handle_text" msgid="4419667835599523257">"Қолданба идентификаторы"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"Қолданба белгішесі"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Толық экран"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Компьютер режимі"</string>
@@ -116,8 +116,7 @@
     <string name="float_button_text" msgid="9221657008391364581">"Қалқыма"</string>
     <string name="select_text" msgid="5139083974039906583">"Таңдау"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"Скриншот"</string>
-    <!-- no translation found for open_in_browser_text (9181692926376072904) -->
-    <skip />
+    <string name="open_in_browser_text" msgid="9181692926376072904">"Браузерден ашу"</string>
     <string name="close_text" msgid="4986518933445178928">"Жабу"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Мәзірді жабу"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"Мәзірді ашу"</string>
diff --git a/libs/WindowManager/Shell/res/values-km/strings.xml b/libs/WindowManager/Shell/res/values-km/strings.xml
index 69c0e1e..4db7aea 100644
--- a/libs/WindowManager/Shell/res/values-km/strings.xml
+++ b/libs/WindowManager/Shell/res/values-km/strings.xml
@@ -107,7 +107,7 @@
     <string name="minimize_button_text" msgid="271592547935841753">"បង្រួម"</string>
     <string name="close_button_text" msgid="2913281996024033299">"បិទ"</string>
     <string name="back_button_text" msgid="1469718707134137085">"ថយក្រោយ"</string>
-    <string name="handle_text" msgid="1766582106752184456">"ឈ្មោះអ្នកប្រើប្រាស់"</string>
+    <string name="handle_text" msgid="4419667835599523257">"ឈ្មោះអ្នកប្រើប្រាស់កម្មវិធី"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"រូប​កម្មវិធី"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"អេក្រង់​ពេញ"</string>
     <string name="desktop_text" msgid="1077633567027630454">"មុខងារកុំព្យូទ័រ"</string>
diff --git a/libs/WindowManager/Shell/res/values-kn/strings.xml b/libs/WindowManager/Shell/res/values-kn/strings.xml
index 88a29df..5615afc 100644
--- a/libs/WindowManager/Shell/res/values-kn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-kn/strings.xml
@@ -107,7 +107,7 @@
     <string name="minimize_button_text" msgid="271592547935841753">"ಕುಗ್ಗಿಸಿ"</string>
     <string name="close_button_text" msgid="2913281996024033299">"ಮುಚ್ಚಿರಿ"</string>
     <string name="back_button_text" msgid="1469718707134137085">"ಹಿಂದಕ್ಕೆ"</string>
-    <string name="handle_text" msgid="1766582106752184456">"ಹ್ಯಾಂಡಲ್"</string>
+    <string name="handle_text" msgid="4419667835599523257">"ಆ್ಯಪ್ ಹ್ಯಾಂಡಲ್"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"ಆ್ಯಪ್ ಐಕಾನ್"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"ಫುಲ್‌ಸ್ಕ್ರೀನ್"</string>
     <string name="desktop_text" msgid="1077633567027630454">"ಡೆಸ್ಕ್‌ಟಾಪ್ ಮೋಡ್"</string>
diff --git a/libs/WindowManager/Shell/res/values-ko/strings.xml b/libs/WindowManager/Shell/res/values-ko/strings.xml
index a22667d8..33b980d 100644
--- a/libs/WindowManager/Shell/res/values-ko/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ko/strings.xml
@@ -107,7 +107,7 @@
     <string name="minimize_button_text" msgid="271592547935841753">"최소화"</string>
     <string name="close_button_text" msgid="2913281996024033299">"닫기"</string>
     <string name="back_button_text" msgid="1469718707134137085">"뒤로"</string>
-    <string name="handle_text" msgid="1766582106752184456">"핸들"</string>
+    <string name="handle_text" msgid="4419667835599523257">"앱 핸들"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"앱 아이콘"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"전체 화면"</string>
     <string name="desktop_text" msgid="1077633567027630454">"데스크톱 모드"</string>
@@ -116,8 +116,7 @@
     <string name="float_button_text" msgid="9221657008391364581">"플로팅"</string>
     <string name="select_text" msgid="5139083974039906583">"선택"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"스크린샷"</string>
-    <!-- no translation found for open_in_browser_text (9181692926376072904) -->
-    <skip />
+    <string name="open_in_browser_text" msgid="9181692926376072904">"브라우저에서 열기"</string>
     <string name="close_text" msgid="4986518933445178928">"닫기"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"메뉴 닫기"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"메뉴 열기"</string>
diff --git a/libs/WindowManager/Shell/res/values-ky/strings.xml b/libs/WindowManager/Shell/res/values-ky/strings.xml
index 289d930..bf3ca52 100644
--- a/libs/WindowManager/Shell/res/values-ky/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ky/strings.xml
@@ -107,7 +107,7 @@
     <string name="minimize_button_text" msgid="271592547935841753">"Кичирейтүү"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Жабуу"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Артка"</string>
-    <string name="handle_text" msgid="1766582106752184456">"Маркер"</string>
+    <string name="handle_text" msgid="4419667835599523257">"Колдонмонун маркери"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"Колдонмонун сүрөтчөсү"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Толук экран"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Компьютер режими"</string>
diff --git a/libs/WindowManager/Shell/res/values-lo/strings.xml b/libs/WindowManager/Shell/res/values-lo/strings.xml
index 0b84d2c..ac6fa00 100644
--- a/libs/WindowManager/Shell/res/values-lo/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lo/strings.xml
@@ -107,7 +107,7 @@
     <string name="minimize_button_text" msgid="271592547935841753">"ຫຍໍ້ລົງ"</string>
     <string name="close_button_text" msgid="2913281996024033299">"ປິດ"</string>
     <string name="back_button_text" msgid="1469718707134137085">"ກັບຄືນ"</string>
-    <string name="handle_text" msgid="1766582106752184456">"ມືບັງຄັບ"</string>
+    <string name="handle_text" msgid="4419667835599523257">"ຊື່ຜູ້ໃຊ້ແອັບ"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"ໄອຄອນແອັບ"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"ເຕັມຈໍ"</string>
     <string name="desktop_text" msgid="1077633567027630454">"ໂໝດເດັສທັອບ"</string>
diff --git a/libs/WindowManager/Shell/res/values-lt/strings.xml b/libs/WindowManager/Shell/res/values-lt/strings.xml
index 5b0e6b7..97b53d3 100644
--- a/libs/WindowManager/Shell/res/values-lt/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lt/strings.xml
@@ -107,7 +107,7 @@
     <string name="minimize_button_text" msgid="271592547935841753">"Sumažinti"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Uždaryti"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Atgal"</string>
-    <string name="handle_text" msgid="1766582106752184456">"Rankenėlė"</string>
+    <string name="handle_text" msgid="4419667835599523257">"Programos kreipinys"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"Programos piktograma"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Visas ekranas"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Stalinio kompiuterio režimas"</string>
@@ -116,8 +116,7 @@
     <string name="float_button_text" msgid="9221657008391364581">"Slankusis langas"</string>
     <string name="select_text" msgid="5139083974039906583">"Pasirinkti"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"Ekrano kopija"</string>
-    <!-- no translation found for open_in_browser_text (9181692926376072904) -->
-    <skip />
+    <string name="open_in_browser_text" msgid="9181692926376072904">"Atidaryti naršyklėje"</string>
     <string name="close_text" msgid="4986518933445178928">"Uždaryti"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Uždaryti meniu"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"Atidaryti meniu"</string>
diff --git a/libs/WindowManager/Shell/res/values-lv/strings.xml b/libs/WindowManager/Shell/res/values-lv/strings.xml
index 704b2ed..3a7e90a 100644
--- a/libs/WindowManager/Shell/res/values-lv/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lv/strings.xml
@@ -107,7 +107,7 @@
     <string name="minimize_button_text" msgid="271592547935841753">"Minimizēt"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Aizvērt"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Atpakaļ"</string>
-    <string name="handle_text" msgid="1766582106752184456">"Turis"</string>
+    <string name="handle_text" msgid="4419667835599523257">"Lietotnes turis"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"Lietotnes ikona"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Pilnekrāna režīms"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Darbvirsmas režīms"</string>
@@ -116,8 +116,7 @@
     <string name="float_button_text" msgid="9221657008391364581">"Peldošs"</string>
     <string name="select_text" msgid="5139083974039906583">"Atlasīt"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"Ekrānuzņēmums"</string>
-    <!-- no translation found for open_in_browser_text (9181692926376072904) -->
-    <skip />
+    <string name="open_in_browser_text" msgid="9181692926376072904">"Atvērt pārlūkā"</string>
     <string name="close_text" msgid="4986518933445178928">"Aizvērt"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Aizvērt izvēlni"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"Atvērt izvēlni"</string>
diff --git a/libs/WindowManager/Shell/res/values-mk/strings.xml b/libs/WindowManager/Shell/res/values-mk/strings.xml
index b257f80..f7867ce 100644
--- a/libs/WindowManager/Shell/res/values-mk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mk/strings.xml
@@ -107,7 +107,7 @@
     <string name="minimize_button_text" msgid="271592547935841753">"Минимизирај"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Затвори"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Назад"</string>
-    <string name="handle_text" msgid="1766582106752184456">"Прекар"</string>
+    <string name="handle_text" msgid="4419667835599523257">"Прекар на апликацијата"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"Икона на апликацијата"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Цел екран"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Режим за компјутер"</string>
@@ -116,8 +116,7 @@
     <string name="float_button_text" msgid="9221657008391364581">"Лебдечко"</string>
     <string name="select_text" msgid="5139083974039906583">"Изберете"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"Слика од екранот"</string>
-    <!-- no translation found for open_in_browser_text (9181692926376072904) -->
-    <skip />
+    <string name="open_in_browser_text" msgid="9181692926376072904">"Отвори во прелистувач"</string>
     <string name="close_text" msgid="4986518933445178928">"Затворете"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Затворете го менито"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"Отвори го менито"</string>
diff --git a/libs/WindowManager/Shell/res/values-ml/strings.xml b/libs/WindowManager/Shell/res/values-ml/strings.xml
index 2efd983..68ff074 100644
--- a/libs/WindowManager/Shell/res/values-ml/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ml/strings.xml
@@ -107,7 +107,7 @@
     <string name="minimize_button_text" msgid="271592547935841753">"ചെറുതാക്കുക"</string>
     <string name="close_button_text" msgid="2913281996024033299">"അടയ്ക്കുക"</string>
     <string name="back_button_text" msgid="1469718707134137085">"മടങ്ങുക"</string>
-    <string name="handle_text" msgid="1766582106752184456">"ഹാൻഡിൽ"</string>
+    <string name="handle_text" msgid="4419667835599523257">"ആപ്പ് ഹാൻഡിൽ"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"ആപ്പ് ഐക്കൺ"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"പൂർണ്ണസ്ക്രീൻ"</string>
     <string name="desktop_text" msgid="1077633567027630454">"ഡെസ്‌ക്ടോപ്പ് മോഡ്"</string>
diff --git a/libs/WindowManager/Shell/res/values-mn/strings.xml b/libs/WindowManager/Shell/res/values-mn/strings.xml
index a343065..27b38bc 100644
--- a/libs/WindowManager/Shell/res/values-mn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mn/strings.xml
@@ -107,7 +107,7 @@
     <string name="minimize_button_text" msgid="271592547935841753">"Багасгах"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Хаах"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Буцах"</string>
-    <string name="handle_text" msgid="1766582106752184456">"Бариул"</string>
+    <string name="handle_text" msgid="4419667835599523257">"Аппын бариул"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"Aппын дүрс тэмдэг"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Бүтэн дэлгэц"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Дэлгэцийн горим"</string>
@@ -116,8 +116,7 @@
     <string name="float_button_text" msgid="9221657008391364581">"Хөвөгч"</string>
     <string name="select_text" msgid="5139083974039906583">"Сонгох"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"Дэлгэцийн агшин"</string>
-    <!-- no translation found for open_in_browser_text (9181692926376072904) -->
-    <skip />
+    <string name="open_in_browser_text" msgid="9181692926376072904">"Хөтчид нээх"</string>
     <string name="close_text" msgid="4986518933445178928">"Хаах"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Цэсийг хаах"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"Цэс нээх"</string>
diff --git a/libs/WindowManager/Shell/res/values-mr/strings.xml b/libs/WindowManager/Shell/res/values-mr/strings.xml
index ef71e86..f57f063 100644
--- a/libs/WindowManager/Shell/res/values-mr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mr/strings.xml
@@ -107,7 +107,7 @@
     <string name="minimize_button_text" msgid="271592547935841753">"लहान करा"</string>
     <string name="close_button_text" msgid="2913281996024033299">"बंद करा"</string>
     <string name="back_button_text" msgid="1469718707134137085">"मागे जा"</string>
-    <string name="handle_text" msgid="1766582106752184456">"हँडल"</string>
+    <string name="handle_text" msgid="4419667835599523257">"अ‍ॅपचे हँडल"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"अ‍ॅप आयकन"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"फुलस्‍क्रीन"</string>
     <string name="desktop_text" msgid="1077633567027630454">"डेस्कटॉप मोड"</string>
diff --git a/libs/WindowManager/Shell/res/values-ms/strings.xml b/libs/WindowManager/Shell/res/values-ms/strings.xml
index a9a97d8..9c3b871 100644
--- a/libs/WindowManager/Shell/res/values-ms/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ms/strings.xml
@@ -107,7 +107,7 @@
     <string name="minimize_button_text" msgid="271592547935841753">"Minimumkan"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Tutup"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Kembali"</string>
-    <string name="handle_text" msgid="1766582106752184456">"Pemegang"</string>
+    <string name="handle_text" msgid="4419667835599523257">"Pengendalian apl"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"Ikon Apl"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Skrin penuh"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Mod Desktop"</string>
diff --git a/libs/WindowManager/Shell/res/values-my/strings.xml b/libs/WindowManager/Shell/res/values-my/strings.xml
index 781148d..2685c56 100644
--- a/libs/WindowManager/Shell/res/values-my/strings.xml
+++ b/libs/WindowManager/Shell/res/values-my/strings.xml
@@ -107,7 +107,7 @@
     <string name="minimize_button_text" msgid="271592547935841753">"ချုံ့ရန်"</string>
     <string name="close_button_text" msgid="2913281996024033299">"ပိတ်ရန်"</string>
     <string name="back_button_text" msgid="1469718707134137085">"နောက်သို့"</string>
-    <string name="handle_text" msgid="1766582106752184456">"သုံးသူအမည်"</string>
+    <string name="handle_text" msgid="4419667835599523257">"အက်ပ်သုံးသူအမည်"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"အက်ပ်သင်္ကေတ"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"ဖန်သားပြင်အပြည့်"</string>
     <string name="desktop_text" msgid="1077633567027630454">"ဒက်စ်တော့မုဒ်"</string>
@@ -116,8 +116,7 @@
     <string name="float_button_text" msgid="9221657008391364581">"မျှောရန်"</string>
     <string name="select_text" msgid="5139083974039906583">"ရွေးရန်"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"ဖန်သားပြင်ဓာတ်ပုံ"</string>
-    <!-- no translation found for open_in_browser_text (9181692926376072904) -->
-    <skip />
+    <string name="open_in_browser_text" msgid="9181692926376072904">"ဘရောင်ဇာတွင် ဖွင့်ရန်"</string>
     <string name="close_text" msgid="4986518933445178928">"ပိတ်ရန်"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"မီနူး ပိတ်ရန်"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"မီနူး ဖွင့်ရန်"</string>
diff --git a/libs/WindowManager/Shell/res/values-nb/strings.xml b/libs/WindowManager/Shell/res/values-nb/strings.xml
index 9dc8501..36c0b8b 100644
--- a/libs/WindowManager/Shell/res/values-nb/strings.xml
+++ b/libs/WindowManager/Shell/res/values-nb/strings.xml
@@ -107,7 +107,7 @@
     <string name="minimize_button_text" msgid="271592547935841753">"Minimer"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Lukk"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Tilbake"</string>
-    <string name="handle_text" msgid="1766582106752184456">"Håndtak"</string>
+    <string name="handle_text" msgid="4419667835599523257">"Apphåndtak"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"Appikon"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Fullskjerm"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Skrivebordmodus"</string>
@@ -116,8 +116,7 @@
     <string name="float_button_text" msgid="9221657008391364581">"Svevende"</string>
     <string name="select_text" msgid="5139083974039906583">"Velg"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"Skjermbilde"</string>
-    <!-- no translation found for open_in_browser_text (9181692926376072904) -->
-    <skip />
+    <string name="open_in_browser_text" msgid="9181692926376072904">"Åpne i nettleseren"</string>
     <string name="close_text" msgid="4986518933445178928">"Lukk"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Lukk menyen"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"Åpne menyen"</string>
diff --git a/libs/WindowManager/Shell/res/values-ne/strings.xml b/libs/WindowManager/Shell/res/values-ne/strings.xml
index 7cc4a43..4e96f5f5 100644
--- a/libs/WindowManager/Shell/res/values-ne/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ne/strings.xml
@@ -107,7 +107,7 @@
     <string name="minimize_button_text" msgid="271592547935841753">"मिनिमाइज गर्नुहोस्"</string>
     <string name="close_button_text" msgid="2913281996024033299">"बन्द गर्नुहोस्"</string>
     <string name="back_button_text" msgid="1469718707134137085">"पछाडि"</string>
-    <string name="handle_text" msgid="1766582106752184456">"ह्यान्डल"</string>
+    <string name="handle_text" msgid="4419667835599523257">"एपको ह्यान्डल"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"एपको आइकन"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"फुल स्क्रिन"</string>
     <string name="desktop_text" msgid="1077633567027630454">"डेस्कटप मोड"</string>
diff --git a/libs/WindowManager/Shell/res/values-nl/strings.xml b/libs/WindowManager/Shell/res/values-nl/strings.xml
index 7480add..5ae084a 100644
--- a/libs/WindowManager/Shell/res/values-nl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-nl/strings.xml
@@ -107,7 +107,7 @@
     <string name="minimize_button_text" msgid="271592547935841753">"Minimaliseren"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Sluiten"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Terug"</string>
-    <string name="handle_text" msgid="1766582106752184456">"Gebruikersnaam"</string>
+    <string name="handle_text" msgid="4419667835599523257">"App-handgreep"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"App-icoon"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Volledig scherm"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Desktopmodus"</string>
diff --git a/libs/WindowManager/Shell/res/values-or/strings.xml b/libs/WindowManager/Shell/res/values-or/strings.xml
index 5412bb8..536c6fe 100644
--- a/libs/WindowManager/Shell/res/values-or/strings.xml
+++ b/libs/WindowManager/Shell/res/values-or/strings.xml
@@ -107,7 +107,7 @@
     <string name="minimize_button_text" msgid="271592547935841753">"ଛୋଟ କରନ୍ତୁ"</string>
     <string name="close_button_text" msgid="2913281996024033299">"ବନ୍ଦ କରନ୍ତୁ"</string>
     <string name="back_button_text" msgid="1469718707134137085">"ପଛକୁ ଫେରନ୍ତୁ"</string>
-    <string name="handle_text" msgid="1766582106752184456">"ହେଣ୍ଡେଲ"</string>
+    <string name="handle_text" msgid="4419667835599523257">"ଆପର ହେଣ୍ଡେଲ"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"ଆପ ଆଇକନ"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"ପୂର୍ଣ୍ଣସ୍କ୍ରିନ"</string>
     <string name="desktop_text" msgid="1077633567027630454">"ଡେସ୍କଟପ ମୋଡ"</string>
@@ -116,8 +116,7 @@
     <string name="float_button_text" msgid="9221657008391364581">"ଫ୍ଲୋଟ"</string>
     <string name="select_text" msgid="5139083974039906583">"ଚୟନ କରନ୍ତୁ"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"ସ୍କ୍ରିନସଟ"</string>
-    <!-- no translation found for open_in_browser_text (9181692926376072904) -->
-    <skip />
+    <string name="open_in_browser_text" msgid="9181692926376072904">"ବ୍ରାଉଜରରେ ଖୋଲନ୍ତୁ"</string>
     <string name="close_text" msgid="4986518933445178928">"ବନ୍ଦ କରନ୍ତୁ"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"ମେନୁ ବନ୍ଦ କରନ୍ତୁ"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"ମେନୁ ଖୋଲନ୍ତୁ"</string>
diff --git a/libs/WindowManager/Shell/res/values-pa/strings.xml b/libs/WindowManager/Shell/res/values-pa/strings.xml
index abff90d..bdef224 100644
--- a/libs/WindowManager/Shell/res/values-pa/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pa/strings.xml
@@ -107,7 +107,7 @@
     <string name="minimize_button_text" msgid="271592547935841753">"ਛੋਟਾ ਕਰੋ"</string>
     <string name="close_button_text" msgid="2913281996024033299">"ਬੰਦ ਕਰੋ"</string>
     <string name="back_button_text" msgid="1469718707134137085">"ਪਿੱਛੇ"</string>
-    <string name="handle_text" msgid="1766582106752184456">"ਹੈਂਡਲ"</string>
+    <string name="handle_text" msgid="4419667835599523257">"ਐਪ ਹੈਂਡਲ"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"ਐਪ ਪ੍ਰਤੀਕ"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"ਪੂਰੀ-ਸਕ੍ਰੀਨ"</string>
     <string name="desktop_text" msgid="1077633567027630454">"ਡੈਸਕਟਾਪ ਮੋਡ"</string>
@@ -116,8 +116,7 @@
     <string name="float_button_text" msgid="9221657008391364581">"ਫ਼ਲੋਟ"</string>
     <string name="select_text" msgid="5139083974039906583">"ਚੁਣੋ"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"ਸਕ੍ਰੀਨਸ਼ਾਟ"</string>
-    <!-- no translation found for open_in_browser_text (9181692926376072904) -->
-    <skip />
+    <string name="open_in_browser_text" msgid="9181692926376072904">"ਬ੍ਰਾਊਜ਼ਰ ਵਿੱਚ ਖੋਲ੍ਹੋ"</string>
     <string name="close_text" msgid="4986518933445178928">"ਬੰਦ ਕਰੋ"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"ਮੀਨੂ ਬੰਦ ਕਰੋ"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"ਮੀਨੂ ਖੋਲ੍ਹੋ"</string>
diff --git a/libs/WindowManager/Shell/res/values-pl/strings.xml b/libs/WindowManager/Shell/res/values-pl/strings.xml
index 1c268bd..41c4bb0 100644
--- a/libs/WindowManager/Shell/res/values-pl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pl/strings.xml
@@ -107,7 +107,7 @@
     <string name="minimize_button_text" msgid="271592547935841753">"Minimalizuj"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Zamknij"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Wstecz"</string>
-    <string name="handle_text" msgid="1766582106752184456">"Uchwyt"</string>
+    <string name="handle_text" msgid="4419667835599523257">"Uchwyt aplikacji"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"Ikona aplikacji"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Pełny ekran"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Tryb pulpitu"</string>
diff --git a/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml b/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
index 82566fe..83d34c0 100644
--- a/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
@@ -107,7 +107,7 @@
     <string name="minimize_button_text" msgid="271592547935841753">"Minimizar"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Fechar"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Voltar"</string>
-    <string name="handle_text" msgid="1766582106752184456">"Alça"</string>
+    <string name="handle_text" msgid="4419667835599523257">"Identificador do app"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"Ícone do app"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Tela cheia"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Modo área de trabalho"</string>
diff --git a/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml b/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
index b2e8918..425ad05 100644
--- a/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
@@ -107,7 +107,7 @@
     <string name="minimize_button_text" msgid="271592547935841753">"Minimizar"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Fechar"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Anterior"</string>
-    <string name="handle_text" msgid="1766582106752184456">"Indicador"</string>
+    <string name="handle_text" msgid="4419667835599523257">"Indicador da app"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"Ícone da app"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Ecrã inteiro"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Modo de ambiente de trabalho"</string>
diff --git a/libs/WindowManager/Shell/res/values-pt/strings.xml b/libs/WindowManager/Shell/res/values-pt/strings.xml
index 82566fe..83d34c0 100644
--- a/libs/WindowManager/Shell/res/values-pt/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt/strings.xml
@@ -107,7 +107,7 @@
     <string name="minimize_button_text" msgid="271592547935841753">"Minimizar"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Fechar"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Voltar"</string>
-    <string name="handle_text" msgid="1766582106752184456">"Alça"</string>
+    <string name="handle_text" msgid="4419667835599523257">"Identificador do app"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"Ícone do app"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Tela cheia"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Modo área de trabalho"</string>
diff --git a/libs/WindowManager/Shell/res/values-ro/strings.xml b/libs/WindowManager/Shell/res/values-ro/strings.xml
index 67dc389..ea1a6b7 100644
--- a/libs/WindowManager/Shell/res/values-ro/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ro/strings.xml
@@ -107,7 +107,7 @@
     <string name="minimize_button_text" msgid="271592547935841753">"Minimizează"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Închide"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Înapoi"</string>
-    <string name="handle_text" msgid="1766582106752184456">"Ghidaj"</string>
+    <string name="handle_text" msgid="4419667835599523257">"Handle de aplicație"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"Pictograma aplicației"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Ecran complet"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Modul desktop"</string>
@@ -116,8 +116,7 @@
     <string name="float_button_text" msgid="9221657008391364581">"Flotantă"</string>
     <string name="select_text" msgid="5139083974039906583">"Selectează"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"Captură de ecran"</string>
-    <!-- no translation found for open_in_browser_text (9181692926376072904) -->
-    <skip />
+    <string name="open_in_browser_text" msgid="9181692926376072904">"Deschide în browser"</string>
     <string name="close_text" msgid="4986518933445178928">"Închide"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Închide meniul"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"Deschide meniul"</string>
diff --git a/libs/WindowManager/Shell/res/values-ru/strings.xml b/libs/WindowManager/Shell/res/values-ru/strings.xml
index b2a6030..d0292be 100644
--- a/libs/WindowManager/Shell/res/values-ru/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ru/strings.xml
@@ -107,7 +107,7 @@
     <string name="minimize_button_text" msgid="271592547935841753">"Свернуть"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Закрыть"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Назад"</string>
-    <string name="handle_text" msgid="1766582106752184456">"Маркер"</string>
+    <string name="handle_text" msgid="4419667835599523257">"Обозначение приложения"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"Значок приложения"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Полноэкранный режим"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Режим компьютера"</string>
diff --git a/libs/WindowManager/Shell/res/values-si/strings.xml b/libs/WindowManager/Shell/res/values-si/strings.xml
index 34eaad0..7994222 100644
--- a/libs/WindowManager/Shell/res/values-si/strings.xml
+++ b/libs/WindowManager/Shell/res/values-si/strings.xml
@@ -107,7 +107,7 @@
     <string name="minimize_button_text" msgid="271592547935841753">"කුඩා කරන්න"</string>
     <string name="close_button_text" msgid="2913281996024033299">"වසන්න"</string>
     <string name="back_button_text" msgid="1469718707134137085">"ආපසු"</string>
-    <string name="handle_text" msgid="1766582106752184456">"හැඬලය"</string>
+    <string name="handle_text" msgid="4419667835599523257">"යෙදුම් හසුරුව"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"යෙදුම් නිරූපකය"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"පූර්ණ තිරය"</string>
     <string name="desktop_text" msgid="1077633567027630454">"ඩෙස්ක්ටොප් ප්‍රකාරය"</string>
@@ -116,8 +116,7 @@
     <string name="float_button_text" msgid="9221657008391364581">"පාවෙන"</string>
     <string name="select_text" msgid="5139083974039906583">"තෝරන්න"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"තිර රුව"</string>
-    <!-- no translation found for open_in_browser_text (9181692926376072904) -->
-    <skip />
+    <string name="open_in_browser_text" msgid="9181692926376072904">"බ්‍රව්සරයේ විවෘත කරන්න"</string>
     <string name="close_text" msgid="4986518933445178928">"වසන්න"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"මෙනුව වසන්න"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"මෙනුව විවෘත කරන්න"</string>
diff --git a/libs/WindowManager/Shell/res/values-sk/strings.xml b/libs/WindowManager/Shell/res/values-sk/strings.xml
index 4687f39..896a85b 100644
--- a/libs/WindowManager/Shell/res/values-sk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sk/strings.xml
@@ -107,7 +107,7 @@
     <string name="minimize_button_text" msgid="271592547935841753">"Minimalizovať"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Zavrieť"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Späť"</string>
-    <string name="handle_text" msgid="1766582106752184456">"Rukoväť"</string>
+    <string name="handle_text" msgid="4419667835599523257">"Rukoväť aplikácie"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"Ikona aplikácie"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Celá obrazovka"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Režim počítača"</string>
@@ -116,8 +116,7 @@
     <string name="float_button_text" msgid="9221657008391364581">"Plávajúce"</string>
     <string name="select_text" msgid="5139083974039906583">"Vybrať"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"Snímka obrazovky"</string>
-    <!-- no translation found for open_in_browser_text (9181692926376072904) -->
-    <skip />
+    <string name="open_in_browser_text" msgid="9181692926376072904">"Otvoriť v prehliadači"</string>
     <string name="close_text" msgid="4986518933445178928">"Zavrieť"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Zavrieť ponuku"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"Otvoriť ponuku"</string>
diff --git a/libs/WindowManager/Shell/res/values-sl/strings.xml b/libs/WindowManager/Shell/res/values-sl/strings.xml
index 5848f92..3418c48 100644
--- a/libs/WindowManager/Shell/res/values-sl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sl/strings.xml
@@ -107,7 +107,7 @@
     <string name="minimize_button_text" msgid="271592547935841753">"Minimiraj"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Zapri"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Nazaj"</string>
-    <string name="handle_text" msgid="1766582106752184456">"Ročica"</string>
+    <string name="handle_text" msgid="4419667835599523257">"Identifikator aplikacije"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"Ikona aplikacije"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Celozaslonsko"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Namizni način"</string>
diff --git a/libs/WindowManager/Shell/res/values-sq/strings.xml b/libs/WindowManager/Shell/res/values-sq/strings.xml
index 5f8f97e..306fcaa 100644
--- a/libs/WindowManager/Shell/res/values-sq/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sq/strings.xml
@@ -107,7 +107,7 @@
     <string name="minimize_button_text" msgid="271592547935841753">"Minimizo"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Mbyll"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Pas"</string>
-    <string name="handle_text" msgid="1766582106752184456">"Emërtimi"</string>
+    <string name="handle_text" msgid="4419667835599523257">"Emërtimi i aplikacionit"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"Ikona e aplikacionit"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Ekrani i plotë"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Modaliteti i desktopit"</string>
@@ -116,8 +116,7 @@
     <string name="float_button_text" msgid="9221657008391364581">"Pluskuese"</string>
     <string name="select_text" msgid="5139083974039906583">"Zgjidh"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"Pamja e ekranit"</string>
-    <!-- no translation found for open_in_browser_text (9181692926376072904) -->
-    <skip />
+    <string name="open_in_browser_text" msgid="9181692926376072904">"Hape në shfletues"</string>
     <string name="close_text" msgid="4986518933445178928">"Mbyll"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Mbyll menynë"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"Hap menynë"</string>
diff --git a/libs/WindowManager/Shell/res/values-sr/strings.xml b/libs/WindowManager/Shell/res/values-sr/strings.xml
index 8402eef..8fd824e 100644
--- a/libs/WindowManager/Shell/res/values-sr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sr/strings.xml
@@ -107,7 +107,7 @@
     <string name="minimize_button_text" msgid="271592547935841753">"Умањите"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Затворите"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Назад"</string>
-    <string name="handle_text" msgid="1766582106752184456">"Идентификатор"</string>
+    <string name="handle_text" msgid="4419667835599523257">"Идентификатор апликације"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"Икона апликације"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Преко целог екрана"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Режим за рачунаре"</string>
diff --git a/libs/WindowManager/Shell/res/values-sv/strings.xml b/libs/WindowManager/Shell/res/values-sv/strings.xml
index a991152..b6375c6 100644
--- a/libs/WindowManager/Shell/res/values-sv/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sv/strings.xml
@@ -107,7 +107,7 @@
     <string name="minimize_button_text" msgid="271592547935841753">"Minimera"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Stäng"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Tillbaka"</string>
-    <string name="handle_text" msgid="1766582106752184456">"Handtag"</string>
+    <string name="handle_text" msgid="4419667835599523257">"Apphandtag"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"Appikon"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Helskärm"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Datorläge"</string>
@@ -116,8 +116,7 @@
     <string name="float_button_text" msgid="9221657008391364581">"Svävande"</string>
     <string name="select_text" msgid="5139083974039906583">"Välj"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"Skärmbild"</string>
-    <!-- no translation found for open_in_browser_text (9181692926376072904) -->
-    <skip />
+    <string name="open_in_browser_text" msgid="9181692926376072904">"Öppna i webbläsaren"</string>
     <string name="close_text" msgid="4986518933445178928">"Stäng"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Stäng menyn"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"Öppna menyn"</string>
diff --git a/libs/WindowManager/Shell/res/values-sw/strings.xml b/libs/WindowManager/Shell/res/values-sw/strings.xml
index 94ea2f2..2eadf34 100644
--- a/libs/WindowManager/Shell/res/values-sw/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sw/strings.xml
@@ -107,7 +107,7 @@
     <string name="minimize_button_text" msgid="271592547935841753">"Punguza"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Funga"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Rudi nyuma"</string>
-    <string name="handle_text" msgid="1766582106752184456">"Ncha"</string>
+    <string name="handle_text" msgid="4419667835599523257">"Utambulisho wa programu"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"Aikoni ya Programu"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Skrini nzima"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Hali ya Kompyuta ya mezani"</string>
@@ -116,8 +116,7 @@
     <string name="float_button_text" msgid="9221657008391364581">"Inayoelea"</string>
     <string name="select_text" msgid="5139083974039906583">"Chagua"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"Picha ya skrini"</string>
-    <!-- no translation found for open_in_browser_text (9181692926376072904) -->
-    <skip />
+    <string name="open_in_browser_text" msgid="9181692926376072904">"Fungua katika kivinjari"</string>
     <string name="close_text" msgid="4986518933445178928">"Funga"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Funga Menyu"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"Fungua Menyu"</string>
diff --git a/libs/WindowManager/Shell/res/values-ta/strings.xml b/libs/WindowManager/Shell/res/values-ta/strings.xml
index 866fe56..36f69ce 100644
--- a/libs/WindowManager/Shell/res/values-ta/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ta/strings.xml
@@ -107,7 +107,7 @@
     <string name="minimize_button_text" msgid="271592547935841753">"சிறிதாக்கும்"</string>
     <string name="close_button_text" msgid="2913281996024033299">"மூடும்"</string>
     <string name="back_button_text" msgid="1469718707134137085">"பின்செல்லும்"</string>
-    <string name="handle_text" msgid="1766582106752184456">"ஹேண்டில்"</string>
+    <string name="handle_text" msgid="4419667835599523257">"ஆப்ஸ் ஹேண்டில்"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"ஆப்ஸ் ஐகான்"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"முழுத்திரை"</string>
     <string name="desktop_text" msgid="1077633567027630454">"டெஸ்க்டாப் பயன்முறை"</string>
@@ -116,8 +116,7 @@
     <string name="float_button_text" msgid="9221657008391364581">"மிதக்கும் சாளரம்"</string>
     <string name="select_text" msgid="5139083974039906583">"தேர்ந்தெடுக்கும்"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"ஸ்கிரீன்ஷாட்"</string>
-    <!-- no translation found for open_in_browser_text (9181692926376072904) -->
-    <skip />
+    <string name="open_in_browser_text" msgid="9181692926376072904">"உலாவியில் திறக்கும்"</string>
     <string name="close_text" msgid="4986518933445178928">"மூடும்"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"மெனுவை மூடும்"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"மெனுவைத் திற"</string>
diff --git a/libs/WindowManager/Shell/res/values-te/strings.xml b/libs/WindowManager/Shell/res/values-te/strings.xml
index ef094fc..2446ae8 100644
--- a/libs/WindowManager/Shell/res/values-te/strings.xml
+++ b/libs/WindowManager/Shell/res/values-te/strings.xml
@@ -107,7 +107,7 @@
     <string name="minimize_button_text" msgid="271592547935841753">"కుదించండి"</string>
     <string name="close_button_text" msgid="2913281996024033299">"మూసివేయండి"</string>
     <string name="back_button_text" msgid="1469718707134137085">"వెనుకకు"</string>
-    <string name="handle_text" msgid="1766582106752184456">"హ్యాండిల్"</string>
+    <string name="handle_text" msgid="4419667835599523257">"యాప్ హ్యాండిల్"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"యాప్ చిహ్నం"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"ఫుల్-స్క్రీన్"</string>
     <string name="desktop_text" msgid="1077633567027630454">"డెస్క్‌టాప్ మోడ్"</string>
diff --git a/libs/WindowManager/Shell/res/values-th/strings.xml b/libs/WindowManager/Shell/res/values-th/strings.xml
index 762a81a..10cf216 100644
--- a/libs/WindowManager/Shell/res/values-th/strings.xml
+++ b/libs/WindowManager/Shell/res/values-th/strings.xml
@@ -107,7 +107,7 @@
     <string name="minimize_button_text" msgid="271592547935841753">"ย่อ"</string>
     <string name="close_button_text" msgid="2913281996024033299">"ปิด"</string>
     <string name="back_button_text" msgid="1469718707134137085">"กลับ"</string>
-    <string name="handle_text" msgid="1766582106752184456">"แฮนเดิล"</string>
+    <string name="handle_text" msgid="4419667835599523257">"แฮนเดิลแอป"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"ไอคอนแอป"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"เต็มหน้าจอ"</string>
     <string name="desktop_text" msgid="1077633567027630454">"โหมดเดสก์ท็อป"</string>
diff --git a/libs/WindowManager/Shell/res/values-tl/strings.xml b/libs/WindowManager/Shell/res/values-tl/strings.xml
index 418f500..497ac56b 100644
--- a/libs/WindowManager/Shell/res/values-tl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-tl/strings.xml
@@ -107,7 +107,7 @@
     <string name="minimize_button_text" msgid="271592547935841753">"I-minimize"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Isara"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Bumalik"</string>
-    <string name="handle_text" msgid="1766582106752184456">"Handle"</string>
+    <string name="handle_text" msgid="4419667835599523257">"Handle ng app"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"Icon ng App"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Fullscreen"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Desktop Mode"</string>
diff --git a/libs/WindowManager/Shell/res/values-tr/strings.xml b/libs/WindowManager/Shell/res/values-tr/strings.xml
index e70f170..05eb1ba 100644
--- a/libs/WindowManager/Shell/res/values-tr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-tr/strings.xml
@@ -107,7 +107,7 @@
     <string name="minimize_button_text" msgid="271592547935841753">"Küçült"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Kapat"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Geri"</string>
-    <string name="handle_text" msgid="1766582106752184456">"Herkese açık kullanıcı adı"</string>
+    <string name="handle_text" msgid="4419667835599523257">"Uygulama tanıtıcısı"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"Uygulama Simgesi"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Tam Ekran"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Masaüstü Modu"</string>
@@ -116,8 +116,7 @@
     <string name="float_button_text" msgid="9221657008391364581">"Havada Süzülen"</string>
     <string name="select_text" msgid="5139083974039906583">"Seç"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"Ekran görüntüsü"</string>
-    <!-- no translation found for open_in_browser_text (9181692926376072904) -->
-    <skip />
+    <string name="open_in_browser_text" msgid="9181692926376072904">"Tarayıcıda aç"</string>
     <string name="close_text" msgid="4986518933445178928">"Kapat"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Menüyü kapat"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"Menüyü Aç"</string>
diff --git a/libs/WindowManager/Shell/res/values-uk/strings.xml b/libs/WindowManager/Shell/res/values-uk/strings.xml
index b243a15..5c3cfaa 100644
--- a/libs/WindowManager/Shell/res/values-uk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-uk/strings.xml
@@ -107,7 +107,7 @@
     <string name="minimize_button_text" msgid="271592547935841753">"Згорнути"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Закрити"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Назад"</string>
-    <string name="handle_text" msgid="1766582106752184456">"Маркер"</string>
+    <string name="handle_text" msgid="4419667835599523257">"Дескриптор додатка"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"Значок додатка"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"На весь екран"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Режим комп’ютера"</string>
@@ -116,8 +116,7 @@
     <string name="float_button_text" msgid="9221657008391364581">"Плаваюче вікно"</string>
     <string name="select_text" msgid="5139083974039906583">"Вибрати"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"Знімок екрана"</string>
-    <!-- no translation found for open_in_browser_text (9181692926376072904) -->
-    <skip />
+    <string name="open_in_browser_text" msgid="9181692926376072904">"Відкрити у вебпереглядачі"</string>
     <string name="close_text" msgid="4986518933445178928">"Закрити"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Закрити меню"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"Відкрити меню"</string>
diff --git a/libs/WindowManager/Shell/res/values-ur/strings.xml b/libs/WindowManager/Shell/res/values-ur/strings.xml
index 2cf9f32..98c433b 100644
--- a/libs/WindowManager/Shell/res/values-ur/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ur/strings.xml
@@ -107,7 +107,7 @@
     <string name="minimize_button_text" msgid="271592547935841753">"چھوٹا کریں"</string>
     <string name="close_button_text" msgid="2913281996024033299">"بند کریں"</string>
     <string name="back_button_text" msgid="1469718707134137085">"پیچھے"</string>
-    <string name="handle_text" msgid="1766582106752184456">"ہینڈل"</string>
+    <string name="handle_text" msgid="4419667835599523257">"ایپ ہینڈل"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"ایپ کا آئیکن"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"مکمل اسکرین"</string>
     <string name="desktop_text" msgid="1077633567027630454">"ڈیسک ٹاپ موڈ"</string>
diff --git a/libs/WindowManager/Shell/res/values-uz/strings.xml b/libs/WindowManager/Shell/res/values-uz/strings.xml
index f8cd43f..3ec676d 100644
--- a/libs/WindowManager/Shell/res/values-uz/strings.xml
+++ b/libs/WindowManager/Shell/res/values-uz/strings.xml
@@ -107,7 +107,7 @@
     <string name="minimize_button_text" msgid="271592547935841753">"Kichraytirish"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Yopish"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Orqaga"</string>
-    <string name="handle_text" msgid="1766582106752184456">"Identifikator"</string>
+    <string name="handle_text" msgid="4419667835599523257">"Ilova identifikatori"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"Ilova belgisi"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Butun ekran"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Desktop rejimi"</string>
diff --git a/libs/WindowManager/Shell/res/values-vi/strings.xml b/libs/WindowManager/Shell/res/values-vi/strings.xml
index a813b26..125d703 100644
--- a/libs/WindowManager/Shell/res/values-vi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-vi/strings.xml
@@ -107,7 +107,7 @@
     <string name="minimize_button_text" msgid="271592547935841753">"Thu nhỏ"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Đóng"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Quay lại"</string>
-    <string name="handle_text" msgid="1766582106752184456">"Xử lý"</string>
+    <string name="handle_text" msgid="4419667835599523257">"Ô điều khiển ứng dụng"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"Biểu tượng ứng dụng"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Toàn màn hình"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Chế độ máy tính"</string>
@@ -116,8 +116,7 @@
     <string name="float_button_text" msgid="9221657008391364581">"Nổi"</string>
     <string name="select_text" msgid="5139083974039906583">"Chọn"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"Ảnh chụp màn hình"</string>
-    <!-- no translation found for open_in_browser_text (9181692926376072904) -->
-    <skip />
+    <string name="open_in_browser_text" msgid="9181692926376072904">"Mở trong trình duyệt"</string>
     <string name="close_text" msgid="4986518933445178928">"Đóng"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Đóng trình đơn"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"Mở Trình đơn"</string>
diff --git a/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml b/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
index 6f21fdf..6ccc38b 100644
--- a/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
@@ -107,7 +107,7 @@
     <string name="minimize_button_text" msgid="271592547935841753">"最小化"</string>
     <string name="close_button_text" msgid="2913281996024033299">"关闭"</string>
     <string name="back_button_text" msgid="1469718707134137085">"返回"</string>
-    <string name="handle_text" msgid="1766582106752184456">"处理"</string>
+    <string name="handle_text" msgid="4419667835599523257">"应用手柄"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"应用图标"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"全屏"</string>
     <string name="desktop_text" msgid="1077633567027630454">"桌面模式"</string>
@@ -116,8 +116,7 @@
     <string name="float_button_text" msgid="9221657008391364581">"悬浮"</string>
     <string name="select_text" msgid="5139083974039906583">"选择"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"屏幕截图"</string>
-    <!-- no translation found for open_in_browser_text (9181692926376072904) -->
-    <skip />
+    <string name="open_in_browser_text" msgid="9181692926376072904">"在浏览器中打开"</string>
     <string name="close_text" msgid="4986518933445178928">"关闭"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"关闭菜单"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"打开菜单"</string>
diff --git a/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml b/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
index 159db8f..2787c1e 100644
--- a/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
@@ -107,7 +107,7 @@
     <string name="minimize_button_text" msgid="271592547935841753">"最小化"</string>
     <string name="close_button_text" msgid="2913281996024033299">"關閉"</string>
     <string name="back_button_text" msgid="1469718707134137085">"返去"</string>
-    <string name="handle_text" msgid="1766582106752184456">"控點"</string>
+    <string name="handle_text" msgid="4419667835599523257">"應用程式控點"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"應用程式圖示"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"全螢幕"</string>
     <string name="desktop_text" msgid="1077633567027630454">"桌面模式"</string>
@@ -116,8 +116,7 @@
     <string name="float_button_text" msgid="9221657008391364581">"浮動"</string>
     <string name="select_text" msgid="5139083974039906583">"選取"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"螢幕截圖"</string>
-    <!-- no translation found for open_in_browser_text (9181692926376072904) -->
-    <skip />
+    <string name="open_in_browser_text" msgid="9181692926376072904">"在瀏覽器中開啟"</string>
     <string name="close_text" msgid="4986518933445178928">"關閉"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"關閉選單"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"打開選單"</string>
diff --git a/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml b/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
index 8a1b2bf..72050fb 100644
--- a/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
@@ -107,7 +107,7 @@
     <string name="minimize_button_text" msgid="271592547935841753">"最小化"</string>
     <string name="close_button_text" msgid="2913281996024033299">"關閉"</string>
     <string name="back_button_text" msgid="1469718707134137085">"返回"</string>
-    <string name="handle_text" msgid="1766582106752184456">"控點"</string>
+    <string name="handle_text" msgid="4419667835599523257">"應用程式控制代碼"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"應用程式圖示"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"全螢幕"</string>
     <string name="desktop_text" msgid="1077633567027630454">"電腦模式"</string>
@@ -116,8 +116,7 @@
     <string name="float_button_text" msgid="9221657008391364581">"浮動"</string>
     <string name="select_text" msgid="5139083974039906583">"選取"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"螢幕截圖"</string>
-    <!-- no translation found for open_in_browser_text (9181692926376072904) -->
-    <skip />
+    <string name="open_in_browser_text" msgid="9181692926376072904">"在瀏覽器中開啟"</string>
     <string name="close_text" msgid="4986518933445178928">"關閉"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"關閉選單"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"開啟選單"</string>
diff --git a/libs/WindowManager/Shell/res/values-zu/strings.xml b/libs/WindowManager/Shell/res/values-zu/strings.xml
index ba3df30..ac62a7b 100644
--- a/libs/WindowManager/Shell/res/values-zu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zu/strings.xml
@@ -107,7 +107,7 @@
     <string name="minimize_button_text" msgid="271592547935841753">"Nciphisa"</string>
     <string name="close_button_text" msgid="2913281996024033299">"Vala"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Emuva"</string>
-    <string name="handle_text" msgid="1766582106752184456">"Isibambo"</string>
+    <string name="handle_text" msgid="4419667835599523257">"Inkomba ye-App"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"Isithonjana Se-app"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Isikrini esigcwele"</string>
     <string name="desktop_text" msgid="1077633567027630454">"Imodi Yedeskithophu"</string>
diff --git a/libs/WindowManager/Shell/res/values/config.xml b/libs/WindowManager/Shell/res/values/config.xml
index c2ba064..39f6d8c 100644
--- a/libs/WindowManager/Shell/res/values/config.xml
+++ b/libs/WindowManager/Shell/res/values/config.xml
@@ -179,4 +179,8 @@
 
     <!-- Whether pointer pilfer is required to start back animation. -->
     <bool name="config_backAnimationRequiresPointerPilfer">true</bool>
+
+    <!-- This is to be overridden to define a list of packages mapped to web links which will be
+         parsed and utilized for desktop windowing's app-to-web feature. -->
+    <string name="generic_links_list" translatable="false"/>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index 269a586..e6807ac 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -296,9 +296,6 @@
     <!-- The width of the compat hint. -->
     <dimen name="compat_hint_width">188dp</dimen>
 
-    <!-- The width of the camera compat hint. -->
-    <dimen name="camera_compat_hint_width">143dp</dimen>
-
     <!-- The corner radius of the letterbox education dialog. -->
     <dimen name="letterbox_education_dialog_corner_radius">28dp</dimen>
 
@@ -500,7 +497,7 @@
 
     <!-- The maximum height of the handle menu in desktop mode. Four pills (52dp each) plus 2dp
         spacing between them plus 4dp top padding. -->
-    <dimen name="desktop_mode_handle_menu_height">218dp</dimen>
+    <dimen name="desktop_mode_handle_menu_height">270dp</dimen>
 
     <!-- The elevation set on the handle menu pills. -->
     <dimen name="desktop_mode_handle_menu_pill_elevation">1dp</dimen>
@@ -511,8 +508,11 @@
     <!-- The height of the handle menu's "Windowing" pill in desktop mode. -->
     <dimen name="desktop_mode_handle_menu_windowing_pill_height">52dp</dimen>
 
-    <!-- The height of the handle menu's "More Actions" pill in desktop mode. -->
-    <dimen name="desktop_mode_handle_menu_more_actions_pill_height">52dp</dimen>
+    <!-- The maximum height of the handle menu's "New Window" button in desktop mode. -->
+    <dimen name="desktop_mode_handle_menu_new_window_height">52dp</dimen>
+
+    <!-- The maximum height of the handle menu's "Screenshot" button in desktop mode. -->
+    <dimen name="desktop_mode_handle_menu_screenshot_height">52dp</dimen>
 
     <!-- The height of the handle menu's "Open in browser" pill in desktop mode. -->
     <dimen name="desktop_mode_handle_menu_open_in_browser_pill_height">52dp</dimen>
@@ -554,15 +554,10 @@
          enable_windowing_edge_drag_resize is disabled. -->
     <dimen name="freeform_resize_corner">44dp</dimen>
 
-    <!-- The width of the area at the sides of the screen where a freeform task will transition to
-    split select if dragged until the touch input is within the range. -->
-    <dimen name="desktop_mode_transition_area_width">32dp</dimen>
+    <!-- The thickness in dp for all desktop drag transition regions. -->
+    <dimen name="desktop_mode_transition_region_thickness">44dp</dimen>
 
-    <!-- The width of the area where a desktop task will transition to fullscreen. -->
-    <dimen name="desktop_mode_fullscreen_from_desktop_width">80dp</dimen>
-
-    <!-- The height of the area where a desktop task will transition to fullscreen. -->
-    <dimen name="desktop_mode_fullscreen_from_desktop_height">40dp</dimen>
+    <item type="dimen" format="float" name="desktop_mode_fullscreen_region_scale">0.4</item>
 
     <!-- The height on the screen where drag to the left or right edge will result in a
     desktop task snapping to split size. The empty space between this and the top is to allow
diff --git a/libs/WindowManager/Shell/res/values/strings.xml b/libs/WindowManager/Shell/res/values/strings.xml
index 4e7cfb6..0a8166f 100644
--- a/libs/WindowManager/Shell/res/values/strings.xml
+++ b/libs/WindowManager/Shell/res/values/strings.xml
@@ -263,7 +263,7 @@
     <!-- Accessibility text for the caption back button [CHAR LIMIT=NONE] -->
     <string name="back_button_text">Back</string>
     <!-- Accessibility text for the caption handle [CHAR LIMIT=NONE] -->
-    <string name="handle_text">Handle</string>
+    <string name="handle_text">App handle</string>
     <!-- Accessibility text for the handle menu app icon [CHAR LIMIT=NONE] -->
     <string name="app_icon_text">App Icon</string>
     <!-- Accessibility text for the handle fullscreen button [CHAR LIMIT=NONE] -->
@@ -282,6 +282,8 @@
     <string name="screenshot_text">Screenshot</string>
     <!-- Accessibility text for the handle menu open in browser button [CHAR LIMIT=NONE] -->
     <string name="open_in_browser_text">Open in browser</string>
+    <!-- Accessibility text for the handle menu new window button [CHAR LIMIT=NONE] -->
+    <string name="new_window_text">New Window</string>
     <!-- Accessibility text for the handle menu close button [CHAR LIMIT=NONE] -->
     <string name="close_text">Close</string>
     <!-- Accessibility text for the handle menu close menu button [CHAR LIMIT=NONE] -->
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlags.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlags.kt
index f0d80a0..6d63971 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlags.kt
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlags.kt
@@ -26,129 +26,112 @@
  *
  * The class computes whether a Desktop Windowing flag should be enabled by using the aconfig flag
  * value and the developer option override state (if applicable).
- **/
+ */
 enum class DesktopModeFlags(
     // Function called to obtain aconfig flag value.
     private val flagFunction: () -> Boolean,
     // Whether the flag state should be affected by developer option.
     private val shouldOverrideByDevOption: Boolean
 ) {
-  // All desktop mode related flags will be added here
-  DESKTOP_WINDOWING_MODE(Flags::enableDesktopWindowingMode, true),
-  WALLPAPER_ACTIVITY(Flags::enableDesktopWindowingWallpaperActivity, true);
+    // All desktop mode related flags will be added here
+    DESKTOP_WINDOWING_MODE(Flags::enableDesktopWindowingMode, true),
+    CASCADING_WINDOWS(Flags::enableCascadingWindows, true),
+    WALLPAPER_ACTIVITY(Flags::enableDesktopWindowingWallpaperActivity, true),
+    MODALS_POLICY(Flags::enableDesktopWindowingModalsPolicy, true),
+    THEMED_APP_HEADERS(Flags::enableThemedAppHeaders, true),
+    QUICK_SWITCH(Flags::enableDesktopWindowingQuickSwitch, true),
+    APP_HEADER_WITH_TASK_DENSITY(Flags::enableAppHeaderWithTaskDensity, true),
+    TASK_STACK_OBSERVER_IN_SHELL(Flags::enableTaskStackObserverInShell, true),
+    SIZE_CONSTRAINTS(Flags::enableDesktopWindowingSizeConstraints, true),
+    DYNAMIC_INITIAL_BOUNDS(Flags::enableWindowingDynamicInitialBounds, true),
+    ENABLE_DESKTOP_WINDOWING_TASK_LIMIT(Flags::enableDesktopWindowingTaskLimit, true),
+    BACK_NAVIGATION(Flags::enableDesktopWindowingBackNavigation, true),
+    EDGE_DRAG_RESIZE(Flags::enableWindowingEdgeDragResize, true),
+    TASKBAR_RUNNING_APPS(Flags::enableDesktopWindowingTaskbarRunningApps, true);
 
-  /**
-   * Determines state of flag based on the actual flag and desktop mode developer option overrides.
-   *
-   * Note, this method makes sure that a constant developer toggle overrides is read until reboot.
-   */
-  fun isEnabled(context: Context): Boolean =
-      if (!Flags.showDesktopWindowingDevOption() ||
-          !shouldOverrideByDevOption ||
-          context.contentResolver == null) {
-        flagFunction()
-      } else {
-        val shouldToggleBeEnabledByDefault = DesktopModeStatus.shouldDevOptionBeEnabledByDefault()
-        when (getToggleOverride(context)) {
-          ToggleOverride.OVERRIDE_UNSET -> flagFunction()
-          // When toggle override matches its default state, don't override flags. This helps users
-          // reset their feature overrides.
-          ToggleOverride.OVERRIDE_OFF ->
-              if (shouldToggleBeEnabledByDefault) false else flagFunction()
-          ToggleOverride.OVERRIDE_ON -> if (shouldToggleBeEnabledByDefault) flagFunction() else true
-        }
-      }
-
-  private fun getToggleOverride(context: Context): ToggleOverride {
-    val override =
-        cachedToggleOverride
-            ?: run {
-              val override = getToggleOverrideFromSystem(context)
-              // Cache toggle override the first time we encounter context. Override does not change
-              // with context, as context is just used to fetch System Property and Settings.Global
-              cachedToggleOverride = override
-              Log.d(TAG, "Toggle override initialized to: $override")
-              override
+    /**
+     * Determines state of flag based on the actual flag and desktop mode developer option
+     * overrides.
+     */
+    fun isEnabled(context: Context): Boolean =
+        if (!Flags.showDesktopWindowingDevOption() ||
+            !shouldOverrideByDevOption ||
+            context.contentResolver == null) {
+            flagFunction()
+        } else {
+            val shouldToggleBeEnabledByDefault =
+                DesktopModeStatus.shouldDevOptionBeEnabledByDefault()
+            when (getToggleOverride(context)) {
+                ToggleOverride.OVERRIDE_UNSET -> flagFunction()
+                // When toggle override matches its default state, don't override flags. This helps
+                // users reset their feature overrides.
+                ToggleOverride.OVERRIDE_OFF ->
+                    if (shouldToggleBeEnabledByDefault) false else flagFunction()
+                ToggleOverride.OVERRIDE_ON ->
+                    if (shouldToggleBeEnabledByDefault) flagFunction() else true
             }
-
-    return override
-  }
-
-  private fun getToggleOverrideFromSystem(context: Context): ToggleOverride {
-    // A non-persistent System Property is used to store override to ensure it remains
-    // constant till reboot.
-    val overrideFromSystemProperties: ToggleOverride? =
-        System.getProperty(SYSTEM_PROPERTY_OVERRIDE_KEY, null).convertToToggleOverride()
-    return overrideFromSystemProperties
-        ?: run {
-          // Read Setting Global if System Property is not present (just after reboot)
-          // or not valid (user manually changed the value)
-          val overrideFromSettingsGlobal =
-              convertToToggleOverrideWithFallback(
-                  Settings.Global.getInt(
-                      context.contentResolver,
-                      Settings.Global.DEVELOPMENT_OVERRIDE_DESKTOP_MODE_FEATURES,
-                      ToggleOverride.OVERRIDE_UNSET.setting),
-                  ToggleOverride.OVERRIDE_UNSET)
-          // Initialize System Property
-          System.setProperty(
-              SYSTEM_PROPERTY_OVERRIDE_KEY, overrideFromSettingsGlobal.setting.toString())
-
-          overrideFromSettingsGlobal
         }
-  }
 
-  /**
-   * Override state of desktop mode developer option toggle.
-   *
-   * @property setting The integer value that is associated with the developer option toggle
-   *   override
-   */
-  enum class ToggleOverride(val setting: Int) {
-    /** No override is set. */
-    OVERRIDE_UNSET(-1),
-    /** Override to off. */
-    OVERRIDE_OFF(0),
-    /** Override to on. */
-    OVERRIDE_ON(1)
-  }
+    private fun getToggleOverride(context: Context): ToggleOverride {
+        val override =
+            cachedToggleOverride
+                ?: run {
+                    val override = getToggleOverrideFromSystem(context)
+                    // Cache toggle override the first time we encounter context. Override does not
+                    // change with context, as context is just used to fetch Settings.Global
+                    cachedToggleOverride = override
+                    Log.d(TAG, "Toggle override initialized to: $override")
+                    override
+                }
 
-  private fun String?.convertToToggleOverride(): ToggleOverride? {
-    val intValue = this?.toIntOrNull() ?: return null
-    return settingToToggleOverrideMap[intValue]
-        ?: run {
-          Log.w(TAG, "Unknown toggleOverride int $intValue")
-          null
-        }
-  }
-
-  companion object {
-    private const val TAG = "DesktopModeFlags"
-
-    /**
-     * Key for non-persistent System Property which is used to store desktop windowing developer
-     * option overrides.
-     */
-    private const val SYSTEM_PROPERTY_OVERRIDE_KEY = "sys.wmshell.desktopmode.dev_toggle_override"
-
-    /**
-     * Local cache for toggle override, which is initialized once on its first access. It needs to
-     * be refreshed only on reboots as overridden state takes effect on reboots.
-     */
-    private var cachedToggleOverride: ToggleOverride? = null
-
-    private val settingToToggleOverrideMap = ToggleOverride.entries.associateBy { it.setting }
-
-    @JvmStatic
-    fun convertToToggleOverrideWithFallback(
-        overrideInt: Int,
-        fallbackOverride: ToggleOverride
-    ): ToggleOverride {
-      return settingToToggleOverrideMap[overrideInt]
-          ?: run {
-            Log.w(TAG, "Unknown toggleOverride int $overrideInt")
-            fallbackOverride
-          }
+        return override
     }
-  }
+
+    private fun getToggleOverrideFromSystem(context: Context): ToggleOverride =
+        convertToToggleOverrideWithFallback(
+            Settings.Global.getInt(
+                context.contentResolver,
+                Settings.Global.DEVELOPMENT_OVERRIDE_DESKTOP_MODE_FEATURES,
+                ToggleOverride.OVERRIDE_UNSET.setting),
+            ToggleOverride.OVERRIDE_UNSET)
+
+    /**
+     * Override state of desktop mode developer option toggle.
+     *
+     * @property setting The integer value that is associated with the developer option toggle
+     *   override
+     */
+    enum class ToggleOverride(val setting: Int) {
+        /** No override is set. */
+        OVERRIDE_UNSET(-1),
+        /** Override to off. */
+        OVERRIDE_OFF(0),
+        /** Override to on. */
+        OVERRIDE_ON(1)
+    }
+
+    companion object {
+        private const val TAG = "DesktopModeFlags"
+
+        /**
+         * Local cache for toggle override, which is initialized once on its first access. It needs
+         * to be refreshed only on reboots as overridden state is expected to take effect on
+         * reboots.
+         */
+        private var cachedToggleOverride: ToggleOverride? = null
+
+        private val settingToToggleOverrideMap = ToggleOverride.entries.associateBy { it.setting }
+
+        @JvmStatic
+        fun convertToToggleOverrideWithFallback(
+            overrideInt: Int,
+            fallbackOverride: ToggleOverride
+        ): ToggleOverride {
+            return settingToToggleOverrideMap[overrideInt]
+                ?: run {
+                    Log.w(TAG, "Unknown toggleOverride int $overrideInt")
+                    fallbackOverride
+                }
+        }
+    }
 }
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 fc4710f..282385a 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
@@ -70,6 +70,10 @@
     private static final boolean ENFORCE_DEVICE_RESTRICTIONS = SystemProperties.getBoolean(
             "persist.wm.debug.desktop_mode_enforce_device_restrictions", true);
 
+    private static final boolean USE_APP_TO_WEB_BUILD_TIME_GENERIC_LINKS =
+            SystemProperties.getBoolean(
+                    "persist.wm.debug.use_app_to_web_build_time_generic_links", true);
+
     /** Whether the desktop density override is enabled. */
     public static final boolean DESKTOP_DENSITY_OVERRIDE_ENABLED =
             SystemProperties.getBoolean("persist.wm.debug.desktop_mode_density_enabled", false);
@@ -85,20 +89,15 @@
     private static final int DESKTOP_DENSITY_MAX = 1000;
 
     /**
-     * Default value for {@code MAX_TASK_LIMIT}.
-     */
-    @VisibleForTesting
-    public static final int DEFAULT_MAX_TASK_LIMIT = 4;
-
-    // TODO(b/335131008): add a config-overlay field for the max number of tasks in Desktop Mode
-    /**
-     * Flag declaring the maximum number of Tasks to show in Desktop Mode at any one time.
+     * Sysprop declaring the maximum number of Tasks to show in Desktop Mode at any one time.
      *
-     * <p> The limit does NOT affect Picture-in-Picture, Bubbles, or System Modals (like a screen
+     * <p>If it is not defined, then {@code R.integer.config_maxDesktopWindowingActiveTasks} is
+     * used.
+     *
+     * <p>The limit does NOT affect Picture-in-Picture, Bubbles, or System Modals (like a screen
      * recording window, or Bluetooth pairing window).
      */
-    private static final int MAX_TASK_LIMIT = SystemProperties.getInt(
-            "persist.wm.debug.desktop_max_task_limit", DEFAULT_MAX_TASK_LIMIT);
+    private static final String MAX_TASK_LIMIT_SYS_PROP = "persist.wm.debug.desktop_max_task_limit";
 
     /**
      * Return {@code true} if veiled resizing is active. If false, fluid resizing is used.
@@ -135,8 +134,9 @@
     /**
      * Return the maximum limit on the number of Tasks to show in Desktop Mode at any one time.
      */
-    public static int getMaxTaskLimit() {
-        return MAX_TASK_LIMIT;
+    public static int getMaxTaskLimit(@NonNull Context context) {
+        return SystemProperties.getInt(MAX_TASK_LIMIT_SYS_PROP,
+                context.getResources().getInteger(R.integer.config_maxDesktopWindowingActiveTasks));
     }
 
     /**
@@ -176,6 +176,13 @@
     }
 
     /**
+     * Returns {@code true} if the app-to-web feature is using the build-time generic links list.
+     */
+    public static boolean useAppToWebBuildTimeGenericLinks() {
+        return USE_APP_TO_WEB_BUILD_TIME_GENERIC_LINKS;
+    }
+
+    /**
      * Return {@code true} if the override desktop density is enabled.
      */
     private static boolean isDesktopDensityOverrideEnabled() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
index f014e55..452d12a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
@@ -24,7 +24,6 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 
-import static com.android.wm.shell.compatui.impl.CompatUIEventsKt.CAMERA_CONTROL_STATE_UPDATE;
 import static com.android.wm.shell.compatui.impl.CompatUIEventsKt.SIZE_COMPAT_RESTART_BUTTON_APPEARED;
 import static com.android.wm.shell.compatui.impl.CompatUIEventsKt.SIZE_COMPAT_RESTART_BUTTON_CLICKED;
 import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TASK_ORG;
@@ -61,7 +60,6 @@
 import com.android.wm.shell.compatui.CompatUIController;
 import com.android.wm.shell.compatui.api.CompatUIHandler;
 import com.android.wm.shell.compatui.api.CompatUIInfo;
-import com.android.wm.shell.compatui.impl.CompatUIEvents.CameraControlStateUpdated;
 import com.android.wm.shell.compatui.impl.CompatUIEvents.SizeCompatRestartButtonAppeared;
 import com.android.wm.shell.compatui.impl.CompatUIEvents.SizeCompatRestartButtonClicked;
 import com.android.wm.shell.recents.RecentTasksController;
@@ -265,9 +263,6 @@
                     case SIZE_COMPAT_RESTART_BUTTON_CLICKED:
                         onSizeCompatRestartButtonClicked(compatUIEvent.asType());
                         break;
-                    case CAMERA_CONTROL_STATE_UPDATE:
-                        onCameraControlStateUpdated(compatUIEvent.asType());
-                        break;
                     default:
 
                 }
@@ -690,6 +685,15 @@
         return result;
     }
 
+    /** Return list of {@link RunningTaskInfo}s on all the displays. */
+    public ArrayList<RunningTaskInfo> getRunningTasks() {
+        ArrayList<RunningTaskInfo> result = new ArrayList<>();
+        for (int i = 0; i < mTasks.size(); i++) {
+            result.add(mTasks.valueAt(i).getTaskInfo());
+        }
+        return result;
+    }
+
     /** Gets running task by taskId. Returns {@code null} if no such task observed. */
     @Nullable
     public RunningTaskInfo getRunningTaskInfo(int taskId) {
@@ -808,21 +812,6 @@
         restartTaskTopActivityProcessIfVisible(info.getTaskInfo().token);
     }
 
-    @VisibleForTesting
-    void onCameraControlStateUpdated(@NonNull CameraControlStateUpdated compatUIEvent) {
-        final int taskId = compatUIEvent.getTaskId();
-        final int state = compatUIEvent.getState();
-        final TaskAppearedInfo info;
-        synchronized (mLock) {
-            info = mTasks.get(taskId);
-        }
-        if (info == null) {
-            return;
-        }
-        updateCameraCompatControlState(info.getTaskInfo().token, state);
-    }
-
-
     private void logSizeCompatRestartButtonEventReported(@NonNull TaskAppearedInfo info,
             int event) {
         ActivityInfo topActivityInfo = info.getTaskInfo().topActivityInfo;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/AppToWebGenericLinksParser.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/AppToWebGenericLinksParser.kt
new file mode 100644
index 0000000..56447de
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/AppToWebGenericLinksParser.kt
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.apptoweb
+
+import android.content.Context
+import android.provider.DeviceConfig
+import android.webkit.URLUtil
+import com.android.internal.annotations.VisibleForTesting
+import com.android.wm.shell.R
+import com.android.wm.shell.common.ShellExecutor
+import com.android.wm.shell.shared.annotations.ShellMainThread
+import com.android.wm.shell.shared.desktopmode.DesktopModeStatus.useAppToWebBuildTimeGenericLinks
+
+/**
+ * Retrieves the build-time or server-side generic links list and parses and stores the
+ * package-to-url pairs.
+ */
+class AppToWebGenericLinksParser(
+    private val context: Context,
+    @ShellMainThread private val mainExecutor: ShellExecutor
+) {
+    private val genericLinksMap: MutableMap<String, String> = mutableMapOf()
+
+    init {
+        // If using the server-side generic links list, register a listener
+        if (!useAppToWebBuildTimeGenericLinks()) {
+            DeviceConfigListener()
+        }
+
+        updateGenericLinksMap()
+    }
+
+    /** Returns the generic link associated with the [packageName] or null if there is none. */
+    fun getGenericLink(packageName: String): String? = genericLinksMap[packageName]
+
+    private fun updateGenericLinksMap() {
+        val genericLinksList =
+            if (useAppToWebBuildTimeGenericLinks()) {
+                context.resources.getString(R.string.generic_links_list)
+            } else {
+                DeviceConfig.getString(NAMESPACE, FLAG_GENERIC_LINKS, /* defaultValue= */ "")
+            } ?: return
+
+        parseGenericLinkList(genericLinksList)
+    }
+
+    private fun parseGenericLinkList(genericLinksList: String) {
+        val newEntries =
+            genericLinksList
+                .split(" ")
+                .filter { it.contains(':') }
+                .map {
+                    val (packageName, url) = it.split(':', limit = 2)
+                    return@map packageName to url
+                }
+                .filter { URLUtil.isNetworkUrl(it.second) }
+
+        genericLinksMap.clear()
+        genericLinksMap.putAll(newEntries)
+    }
+
+    /**
+     * Listens for changes to the server-side generic links list and updates the package to url map
+     * if [DesktopModeStatus#useBuildTimeGenericLinkList()] is set to false.
+     */
+    inner class DeviceConfigListener : DeviceConfig.OnPropertiesChangedListener {
+        init {
+            DeviceConfig.addOnPropertiesChangedListener(NAMESPACE, mainExecutor, this)
+        }
+
+        override fun onPropertiesChanged(properties: DeviceConfig.Properties) {
+            if (properties.keyset.contains(FLAG_GENERIC_LINKS)) {
+                updateGenericLinksMap()
+            }
+        }
+    }
+
+    companion object {
+        private const val NAMESPACE = DeviceConfig.NAMESPACE_APP_COMPAT_OVERRIDES
+        @VisibleForTesting const val FLAG_GENERIC_LINKS = "generic_links_flag"
+    }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/OWNERS
new file mode 100644
index 0000000..bfe1306a
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/OWNERS
@@ -0,0 +1,6 @@
+atsjenk@google.com
+jorgegil@google.com
+madym@google.com
+pbdr@google.com
+tkachenkoi@google.com
+vaniadesmonda@google.com
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 a9fdea3..f14f419 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
@@ -16,7 +16,12 @@
 
 package com.android.wm.shell.back;
 
+import static android.view.RemoteAnimationTarget.MODE_CLOSING;
+import static android.view.RemoteAnimationTarget.MODE_OPENING;
+import static android.window.TransitionInfo.FLAG_BACK_GESTURE_ANIMATED;
+
 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.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_BACK_PREVIEW;
 import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_BACK_ANIMATION;
@@ -30,11 +35,13 @@
 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;
@@ -48,6 +55,7 @@
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.RemoteAnimationTarget;
+import android.view.SurfaceControl;
 import android.view.WindowManager;
 import android.window.BackAnimationAdapter;
 import android.window.BackEvent;
@@ -57,6 +65,9 @@
 import android.window.IBackAnimationFinishedCallback;
 import android.window.IBackAnimationRunner;
 import android.window.IOnBackInvokedCallback;
+import android.window.TransitionInfo;
+import android.window.TransitionRequestInfo;
+import android.window.WindowContainerTransaction;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.protolog.ProtoLog;
@@ -66,12 +77,14 @@
 import com.android.wm.shell.common.ExternalInterfaceBinder;
 import com.android.wm.shell.common.RemoteCallable;
 import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.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;
 import com.android.wm.shell.sysui.ShellController;
 import com.android.wm.shell.sysui.ShellInit;
+import com.android.wm.shell.transition.Transitions;
 
 import java.io.PrintWriter;
 import java.util.concurrent.atomic.AtomicBoolean;
@@ -101,6 +114,7 @@
 
     /** Tracks if an uninterruptible animation is in progress */
     private boolean mPostCommitAnimationInProgress = false;
+    private boolean mRealCallbackInvoked = false;
 
     /** Tracks if we should start the back gesture on the next motion move event */
     private boolean mShouldStartOnNextMoveEvent = false;
@@ -123,6 +137,8 @@
     private final ShellExecutor mShellExecutor;
     private final Handler mBgHandler;
     private final WindowManager mWindowManager;
+    private final Transitions mTransitions;
+    private final BackTransitionHandler mBackTransitionHandler;
     @VisibleForTesting
     final Rect mTouchableArea = new Rect();
 
@@ -190,7 +206,8 @@
             Context context,
             @NonNull BackAnimationBackground backAnimationBackground,
             ShellBackAnimationRegistry shellBackAnimationRegistry,
-            ShellCommandHandler shellCommandHandler) {
+            ShellCommandHandler shellCommandHandler,
+            Transitions transitions) {
         this(
                 shellInit,
                 shellController,
@@ -201,7 +218,8 @@
                 context.getContentResolver(),
                 backAnimationBackground,
                 shellBackAnimationRegistry,
-                shellCommandHandler);
+                shellCommandHandler,
+                transitions);
     }
 
     @VisibleForTesting
@@ -215,7 +233,8 @@
             ContentResolver contentResolver,
             @NonNull BackAnimationBackground backAnimationBackground,
             ShellBackAnimationRegistry shellBackAnimationRegistry,
-            ShellCommandHandler shellCommandHandler) {
+            ShellCommandHandler shellCommandHandler,
+            Transitions transitions) {
         mShellController = shellController;
         mShellExecutor = shellExecutor;
         mActivityTaskManager = activityTaskManager;
@@ -230,6 +249,9 @@
         mLatencyTracker = LatencyTracker.getInstance(mContext);
         mShellCommandHandler = shellCommandHandler;
         mWindowManager = context.getSystemService(WindowManager.class);
+        mTransitions = transitions;
+        mBackTransitionHandler = new BackTransitionHandler();
+        mTransitions.addHandler(mBackTransitionHandler);
         updateTouchableArea();
     }
 
@@ -730,7 +752,7 @@
             mBackAnimationFinishedCallback = null;
         }
 
-        if (mBackNavigationInfo != null) {
+        if (mBackNavigationInfo != null && !mRealCallbackInvoked) {
             final IOnBackInvokedCallback callback = mBackNavigationInfo.getOnBackInvokedCallback();
             if (touchTracker.getTriggerBack()) {
                 dispatchOnBackInvoked(callback);
@@ -738,6 +760,7 @@
                 tryDispatchOnBackCancelled(callback);
             }
         }
+        mRealCallbackInvoked = false;
         finishBackNavigation(touchTracker.getTriggerBack());
     }
 
@@ -815,14 +838,38 @@
 
         // The next callback should be {@link #onBackAnimationFinished}.
         if (mCurrentTracker.getTriggerBack()) {
-            // notify gesture finished
-            mBackNavigationInfo.onBackGestureFinished(true);
+            if (migratePredictiveBackTransition()) {
+                // 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);
+            }
+
+            // start post animation
             dispatchOnBackInvoked(mActiveCallback);
         } else {
             tryDispatchOnBackCancelled(mActiveCallback);
         }
     }
 
+    // Close window won't create any transition
+    private boolean shouldTriggerCloseTransition() {
+        if (mBackNavigationInfo == null) {
+            return false;
+        }
+        int type = mBackNavigationInfo.getType();
+        return type == BackNavigationInfo.TYPE_RETURN_TO_HOME
+                || type == BackNavigationInfo.TYPE_CROSS_TASK
+                || type == BackNavigationInfo.TYPE_CROSS_ACTIVITY;
+    }
     /**
      * Called when the post commit animation is completed or timeout.
      * This will trigger the real {@link IOnBackInvokedCallback} behavior.
@@ -857,6 +904,7 @@
                     "mCurrentBackGestureInfo was null when back animation finished");
         }
         resetTouchTracker();
+        mBackTransitionHandler.onAnimationFinished();
     }
 
     /**
@@ -1016,11 +1064,13 @@
                                     endLatencyTracking();
                                     if (!validateAnimationTargets(apps)) {
                                         Log.e(TAG, "Invalid animation targets!");
+                                        mBackTransitionHandler.consumeQueuedTransitionIfNeeded();
                                         return;
                                     }
                                     mBackAnimationFinishedCallback = finishedCallback;
                                     mApps = apps;
                                     startSystemAnimation();
+                                    mBackTransitionHandler.consumeQueuedTransitionIfNeeded();
 
                                     // Dispatch the first progress after animation start for
                                     // smoothing the initial animation, instead of waiting for next
@@ -1041,6 +1091,7 @@
                     public void onAnimationCancelled() {
                         mShellExecutor.execute(
                                 () -> {
+                                    mBackTransitionHandler.consumeQueuedTransitionIfNeeded();
                                     if (!mShellBackAnimationRegistry.cancel(
                                             mBackNavigationInfo != null
                                                     ? mBackNavigationInfo.getType()
@@ -1073,4 +1124,249 @@
         mQueuedTracker.dump(pw, prefix + "    ");
     }
 
+    class BackTransitionHandler implements Transitions.TransitionHandler {
+
+        Runnable mOnAnimationFinishCallback;
+        boolean mCloseTransitionRequested;
+        boolean mOpeningRunning;
+        SurfaceControl.Transaction mFinishOpenTransaction;
+        Transitions.TransitionFinishCallback mFinishOpenTransitionCallback;
+        QueuedTransition mQueuedTransition = null;
+        void onAnimationFinished() {
+            if (!mCloseTransitionRequested) {
+                applyFinishOpenTransition();
+            }
+            if (mOnAnimationFinishCallback != null) {
+                mOnAnimationFinishCallback.run();
+                mOnAnimationFinishCallback = null;
+            }
+        }
+
+        void consumeQueuedTransitionIfNeeded() {
+            if (mQueuedTransition != null) {
+                mQueuedTransition.consume();
+                mQueuedTransition = null;
+            }
+        }
+
+        private void applyFinishOpenTransition() {
+            if (mFinishOpenTransaction != null) {
+                mFinishOpenTransaction.apply();
+                mFinishOpenTransaction = null;
+            }
+            if (mFinishOpenTransitionCallback != null) {
+                mFinishOpenTransitionCallback.onTransitionFinished(null);
+                mFinishOpenTransitionCallback = null;
+            }
+            mOpeningRunning = false;
+        }
+
+        private void applyAndFinish(@NonNull SurfaceControl.Transaction st,
+                @NonNull SurfaceControl.Transaction ft,
+                @NonNull Transitions.TransitionFinishCallback finishCallback) {
+            applyFinishOpenTransition();
+            st.apply();
+            ft.apply();
+            finishCallback.onTransitionFinished(null);
+            mCloseTransitionRequested = false;
+        }
+        @Override
+        public boolean startAnimation(@NonNull IBinder transition,
+                @NonNull TransitionInfo info,
+                @NonNull SurfaceControl.Transaction st,
+                @NonNull SurfaceControl.Transaction ft,
+                @NonNull Transitions.TransitionFinishCallback finishCallback) {
+            // Both mShellExecutor and Transitions#mMainExecutor are ShellMainThread, so we don't
+            // need to post to ShellExecutor when called.
+            if (info.getType() != WindowManager.TRANSIT_PREPARE_BACK_NAVIGATION
+                    && !isGestureBackTransition(info)) {
+                return false;
+            }
+            if (mApps == null || mApps.length == 0) {
+                if (mBackNavigationInfo != null && mShellBackAnimationRegistry
+                        .isWaitingAnimation(mBackNavigationInfo.getType())) {
+                    // Waiting for animation? Queue update to wait for animation start.
+                    consumeQueuedTransitionIfNeeded();
+                    mQueuedTransition = new QueuedTransition(info, st, ft, finishCallback);
+                } else {
+                    // animation was done, consume directly
+                    applyAndFinish(st, ft, finishCallback);
+                }
+                return true;
+            }
+
+            if (handlePrepareTransition(info, st, ft, finishCallback)) {
+                return true;
+            }
+            return handleCloseTransition(info, st, ft, finishCallback);
+        }
+
+        @Override
+        public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
+                @NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget,
+                @NonNull Transitions.TransitionFinishCallback finishCallback) {
+            if (!isGestureBackTransition(info)) {
+                if (mOpeningRunning) {
+                    applyFinishOpenTransition();
+                }
+                if (mQueuedTransition != null) {
+                    consumeQueuedTransitionIfNeeded();
+                }
+                return;
+            }
+            // Handle the commit transition if this handler is running the open transition.
+            finishCallback.onTransitionFinished(null);
+            if (mCloseTransitionRequested) {
+                if (mApps == null || mApps.length == 0) {
+                    if (mQueuedTransition == null) {
+                        // animation was done
+                        applyFinishOpenTransition();
+                        mCloseTransitionRequested = false;
+                    } // else, let queued transition to play
+                } else {
+                    // we are animating, wait until animation finish
+                    mOnAnimationFinishCallback = () -> {
+                        applyFinishOpenTransition();
+                        mCloseTransitionRequested = false;
+                    };
+                }
+            }
+        }
+
+        /**
+         * Check whether this transition is prepare for predictive back animation, which could
+         * happen when core make an activity become visible.
+         */
+        private boolean handlePrepareTransition(
+                @NonNull TransitionInfo info,
+                @NonNull SurfaceControl.Transaction st,
+                @NonNull SurfaceControl.Transaction ft,
+                @NonNull Transitions.TransitionFinishCallback finishCallback) {
+            if (info.getType() != WindowManager.TRANSIT_PREPARE_BACK_NAVIGATION) {
+                return false;
+            }
+
+            SurfaceControl openingLeash = null;
+            for (int i = mApps.length - 1; i >= 0; --i) {
+                if (mApps[i].mode == MODE_OPENING) {
+                    openingLeash = mApps[i].leash;
+                }
+            }
+            if (openingLeash != null) {
+                for (int i = info.getChanges().size() - 1; i >= 0; --i) {
+                    final TransitionInfo.Change c = info.getChanges().get(i);
+                    if (TransitionUtil.isOpeningMode(c.getMode())) {
+                        final Point offset = c.getEndRelOffset();
+                        st.setPosition(c.getLeash(), offset.x, offset.y);
+                        st.reparent(c.getLeash(), openingLeash);
+                    }
+                }
+            }
+            st.apply();
+            mFinishOpenTransaction = ft;
+            mFinishOpenTransitionCallback = finishCallback;
+            mOpeningRunning = true;
+            return true;
+        }
+
+        private boolean isGestureBackTransition(@NonNull TransitionInfo info) {
+            for (int i = info.getChanges().size() - 1; i >= 0; --i) {
+                final TransitionInfo.Change c = info.getChanges().get(i);
+                if (c.hasFlags(FLAG_BACK_GESTURE_ANIMATED)
+                        && (TransitionUtil.isOpeningMode(c.getMode())
+                        || TransitionUtil.isClosingMode(c.getMode()))) {
+                    return true;
+                }
+            }
+            return false;
+        }
+        /**
+         * Check whether this transition is triggered from back gesture commitment.
+         * Reparent the transition targets to animation leashes, so the animation won't be broken.
+         */
+        private boolean handleCloseTransition(@NonNull TransitionInfo info,
+                @NonNull SurfaceControl.Transaction st,
+                @NonNull SurfaceControl.Transaction ft,
+                @NonNull Transitions.TransitionFinishCallback finishCallback) {
+            SurfaceControl openingLeash = null;
+            SurfaceControl closingLeash = null;
+            for (int i = mApps.length - 1; i >= 0; --i) {
+                if (mApps[i].mode == MODE_OPENING) {
+                    openingLeash = mApps[i].leash;
+                }
+                if (mApps[i].mode == MODE_CLOSING) {
+                    closingLeash = mApps[i].leash;
+                }
+            }
+            if (openingLeash != null && closingLeash != null) {
+                for (int i = info.getChanges().size() - 1; i >= 0; --i) {
+                    final TransitionInfo.Change c = info.getChanges().get(i);
+                    if (TransitionUtil.isOpeningMode(c.getMode())) {
+                        final Point offset = c.getEndRelOffset();
+                        st.setPosition(c.getLeash(), offset.x, offset.y);
+                        st.reparent(c.getLeash(), openingLeash);
+                    } else if (TransitionUtil.isClosingMode(c.getMode())) {
+                        st.reparent(c.getLeash(), closingLeash);
+                    }
+                }
+            }
+            st.apply();
+            // mApps must exists
+            mOnAnimationFinishCallback = () -> {
+                ft.apply();
+                finishCallback.onTransitionFinished(null);
+                mCloseTransitionRequested = false;
+            };
+            return true;
+        }
+
+        @Nullable
+        @Override
+        public WindowContainerTransaction handleRequest(
+                @NonNull IBinder transition,
+                @NonNull TransitionRequestInfo request) {
+            if (request.getType() == WindowManager.TRANSIT_PREPARE_BACK_NAVIGATION) {
+                return new WindowContainerTransaction();
+            }
+            if (TransitionUtil.isClosingType(request.getType()) && mCloseTransitionRequested) {
+                return new WindowContainerTransaction();
+            }
+            return null;
+        }
+
+        class QueuedTransition {
+            final TransitionInfo mInfo;
+            final SurfaceControl.Transaction mSt;
+            final  SurfaceControl.Transaction mFt;
+            final Transitions.TransitionFinishCallback mFinishCallback;
+            QueuedTransition(@NonNull TransitionInfo info,
+                    @NonNull SurfaceControl.Transaction st,
+                    @NonNull SurfaceControl.Transaction ft,
+                    @NonNull Transitions.TransitionFinishCallback finishCallback) {
+                mInfo = info;
+                mSt = st;
+                mFt = ft;
+                mFinishCallback = finishCallback;
+            }
+
+            void consume() {
+                // not animating, consume transition directly
+                if (mApps == null || mApps.length == 0) {
+                    applyAndFinish(mSt, mFt, mFinishCallback);
+                    return;
+                }
+                // we are animating
+                if (handlePrepareTransition(mInfo, mSt, mFt, mFinishCallback)) {
+                    // handle merge transition if any
+                    if (mCloseTransitionRequested) {
+                        mOnAnimationFinishCallback = () -> {
+                            applyFinishOpenTransition();
+                            mCloseTransitionRequested = false;
+                        };
+                    }
+                }
+                handleCloseTransition(mInfo, mSt, mFt, mFinishCallback);
+            }
+        }
+    }
 }
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 fca8a62..949a723 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
@@ -734,6 +734,9 @@
     public void setBubbleBarLocation(BubbleBarLocation bubbleBarLocation) {
         if (canShowAsBubbleBar()) {
             mBubblePositioner.setBubbleBarLocation(bubbleBarLocation);
+            if (mLayerView != null && !mLayerView.isExpandedViewDragged()) {
+                mLayerView.updateExpandedView();
+            }
             BubbleBarUpdate bubbleBarUpdate = new BubbleBarUpdate();
             bubbleBarUpdate.bubbleBarLocation = bubbleBarLocation;
             mBubbleStateListener.onBubbleStateChange(bubbleBarUpdate);
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 d4d9d00..fdb4523 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
@@ -372,6 +372,7 @@
         //   ==> activity view
         //   ==> manage button
         bringChildToFront(mManageButton);
+        setManageClickListener();
 
         applyThemeAttrs();
 
@@ -502,6 +503,7 @@
                 R.layout.bubble_manage_button, this /* parent */, false /* attach */);
         addView(mManageButton);
         mManageButton.setVisibility(visibility);
+        setManageClickListener();
         post(() -> {
             int touchAreaHeight =
                     getResources().getDimensionPixelSize(
@@ -646,9 +648,8 @@
         }
     }
 
-    // TODO: Could listener be passed when we pass StackView / can we avoid setting this like this
-    void setManageClickListener(OnClickListener manageClickListener) {
-        mManageButton.setOnClickListener(manageClickListener);
+    private void setManageClickListener() {
+        mManageButton.setOnClickListener(v -> mStackView.onManageBubbleClicked());
     }
 
     /**
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 f93f19d..8f8b77b 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
@@ -1374,7 +1374,6 @@
         // The menu itself should respect locale direction so the icons are on the correct side.
         mManageMenu.setLayoutDirection(LAYOUT_DIRECTION_LOCALE);
         addView(mManageMenu);
-        updateManageButtonListener();
     }
 
     /**
@@ -3375,14 +3374,6 @@
             mExpandedViewContainer.setAlpha(0f);
             mExpandedViewContainer.addView(bev);
 
-            postDelayed(() -> {
-                // Set the Manage button click handler from postDelayed. This appears to resolve
-                // a race condition with adding the BubbleExpandedView view to the expanded view
-                // container. Due to the race condition the click handler sometimes is not set up
-                // correctly and is never called.
-                updateManageButtonListener();
-            }, 0);
-
             if (!mIsExpansionAnimating) {
                 mIsBubbleSwitchAnimating = true;
                 mSurfaceSynchronizer.syncSurfaceAndRun(() -> {
@@ -3392,13 +3383,8 @@
         }
     }
 
-    private void updateManageButtonListener() {
-        BubbleExpandedView bev = getExpandedView();
-        if (mIsExpanded && bev != null) {
-            bev.setManageClickListener((view) -> {
-                showManageMenu(true /* show */);
-            });
-        }
+    void onManageBubbleClicked() {
+        showManageMenu(true /* show */);
     }
 
     /**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java
index 972dce5..24c568c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java
@@ -169,6 +169,8 @@
                     R.layout.bubble_overflow_container, null /* root */);
             mOverflowView.initialize(expandedViewManager, positioner);
             addView(mOverflowView);
+            // Don't show handle for overflow
+            mHandleView.setVisibility(View.GONE);
         } else {
             mTaskView = bubbleTaskView.getTaskView();
             mBubbleTaskViewHelper = new BubbleTaskViewHelper(mContext, expandedViewManager,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewDragController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewDragController.kt
index fa1091c..d45ed0d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewDragController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewDragController.kt
@@ -38,6 +38,9 @@
     var isStuckToDismiss: Boolean = false
         private set
 
+    var isDragged: Boolean = false
+        private set
+
     private var expandedViewInitialTranslationX = 0f
     private var expandedViewInitialTranslationY = 0f
     private val magnetizedExpandedView: MagnetizedObject<BubbleBarExpandedView> =
@@ -94,6 +97,7 @@
             // While animating, don't allow new touch events
             if (expandedView.isAnimating) return false
             pinController.onDragStart(bubblePositioner.isBubbleBarOnLeft)
+            isDragged = true
             return true
         }
 
@@ -141,6 +145,7 @@
                 dismissView.hide()
             }
             isMoving = false
+            isDragged = false
         }
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarHandleView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarHandleView.java
index d54a6b0..c91567d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarHandleView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarHandleView.java
@@ -80,6 +80,7 @@
                 outline.setPath(mPath);
             }
         });
+        setContentDescription(getResources().getString(R.string.handle_text));
     }
 
     /**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
index badc409..9fa85cf 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
@@ -175,6 +175,11 @@
         return mIsExpanded;
     }
 
+    /** Return whether the expanded view is being dragged */
+    public boolean isExpandedViewDragged() {
+        return mDragController != null && mDragController.isDragged();
+    }
+
     /** Shows the expanded view of the provided bubble. */
     public void showExpandedView(BubbleViewProvider b) {
         BubbleBarExpandedView expandedView = b.getBubbleBarExpandedView();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
index f4ac5f2..d7d19f7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
@@ -315,7 +315,7 @@
                         }
                     }
                     if (!mImeShowing) {
-                        removeImeSurface();
+                        removeImeSurface(mDisplayId);
                     }
                 }
             } else if (!android.view.inputmethod.Flags.refactorInsetsController()
@@ -344,8 +344,7 @@
 
             if (android.view.inputmethod.Flags.refactorInsetsController()) {
                 if (pendingImeStartAnimation) {
-                    startAnimation(true, true /* forceRestart */,
-                            null /* statsToken */);
+                    startAnimation(true, true /* forceRestart */);
                 }
             }
         }
@@ -398,8 +397,7 @@
                 // already (e.g., when focussing an editText in activity B, while and editText in
                 // activity A is focussed), we will not get a call of #insetsControlChanged, and
                 // therefore have to start the show animation from here
-                startAnimation(mImeRequestedVisible /* show */, false /* forceRestart */,
-                        null /* TODO statsToken */);
+                startAnimation(mImeRequestedVisible /* show */, false /* forceRestart */);
             }
         }
 
@@ -436,16 +434,31 @@
                     .navBarFrameHeight();
         }
 
+        private void startAnimation(final boolean show, final boolean forceRestart) {
+            final var imeSource = mInsetsState.peekSource(InsetsSource.ID_IME);
+            if (imeSource == null || mImeSourceControl == null) {
+                return;
+            }
+            final var statsToken = mImeSourceControl.getImeStatsToken();
+
+            startAnimation(show, forceRestart, statsToken);
+        }
+
         private void startAnimation(final boolean show, final boolean forceRestart,
                 @SoftInputShowHideReason int reason) {
             final var imeSource = mInsetsState.peekSource(InsetsSource.ID_IME);
             if (imeSource == null || mImeSourceControl == null) {
                 return;
             }
-            final var statsToken = ImeTracker.forLogging().onStart(
-                    show ? ImeTracker.TYPE_SHOW : ImeTracker.TYPE_HIDE, ImeTracker.ORIGIN_WM_SHELL,
-                    reason, false /* fromUser */);
-
+            final ImeTracker.Token statsToken;
+            if (android.view.inputmethod.Flags.refactorInsetsController()
+                    && mImeSourceControl.getImeStatsToken() != null) {
+                statsToken = mImeSourceControl.getImeStatsToken();
+            } else {
+                statsToken = ImeTracker.forLogging().onStart(
+                        show ? ImeTracker.TYPE_SHOW : ImeTracker.TYPE_HIDE,
+                        ImeTracker.ORIGIN_WM_SHELL, reason, false /* fromUser */);
+            }
             startAnimation(show, forceRestart, statsToken);
         }
 
@@ -604,7 +617,7 @@
                                 || hasLeash) {
                             t.hide(mImeSourceControl.getLeash());
                         }
-                        removeImeSurface();
+                        removeImeSurface(mDisplayId);
                         ImeTracker.forLogging().onHidden(mStatsToken);
                     } else if (mAnimationDirection == DIRECTION_SHOW && !mCancelled) {
                         ImeTracker.forLogging().onShown(mStatsToken);
@@ -658,10 +671,10 @@
         }
     }
 
-    void removeImeSurface() {
+    void removeImeSurface(int displayId) {
         // Remove the IME surface to make the insets invisible for
         // non-client controlled insets.
-        InputMethodManagerGlobal.removeImeSurface(
+        InputMethodManagerGlobal.removeImeSurface(displayId,
                 e -> Slog.e(TAG, "Failed to remove IME surface.", e));
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java
index 86cec02..84e32a2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java
@@ -81,6 +81,7 @@
     private boolean mHasNavigationBar = false;
     private boolean mHasStatusBar = false;
     private int mNavBarFrameHeight = 0;
+    private int mTaskbarFrameHeight = 0;
     private boolean mAllowSeamlessRotationDespiteNavBarMoving = false;
     private boolean mNavigationBarCanMove = false;
     private boolean mReverseDefaultRotation = false;
@@ -119,6 +120,7 @@
                 && mNavigationBarCanMove == other.mNavigationBarCanMove
                 && mReverseDefaultRotation == other.mReverseDefaultRotation
                 && mNavBarFrameHeight == other.mNavBarFrameHeight
+                && mTaskbarFrameHeight == other.mTaskbarFrameHeight
                 && Objects.equals(mInsetsState, other.mInsetsState);
     }
 
@@ -126,7 +128,7 @@
     public int hashCode() {
         return Objects.hash(mUiMode, mWidth, mHeight, mCutout, mRotation, mDensityDpi,
                 mNonDecorInsets, mStableInsets, mHasNavigationBar, mHasStatusBar,
-                mNavBarFrameHeight, mAllowSeamlessRotationDespiteNavBarMoving,
+                mNavBarFrameHeight, mTaskbarFrameHeight, mAllowSeamlessRotationDespiteNavBarMoving,
                 mNavigationBarCanMove, mReverseDefaultRotation, mInsetsState);
     }
 
@@ -176,6 +178,7 @@
         mNavigationBarCanMove = dl.mNavigationBarCanMove;
         mReverseDefaultRotation = dl.mReverseDefaultRotation;
         mNavBarFrameHeight = dl.mNavBarFrameHeight;
+        mTaskbarFrameHeight = dl.mTaskbarFrameHeight;
         mNonDecorInsets.set(dl.mNonDecorInsets);
         mStableInsets.set(dl.mStableInsets);
         mInsetsState.set(dl.mInsetsState, true /* copySources */);
@@ -214,7 +217,8 @@
         if (mHasStatusBar) {
             convertNonDecorInsetsToStableInsets(res, mStableInsets, mCutout, mHasStatusBar);
         }
-        mNavBarFrameHeight = getNavigationBarFrameHeight(res, mWidth > mHeight);
+        mNavBarFrameHeight = getNavigationBarFrameHeight(res, /* landscape */ mWidth > mHeight);
+        mTaskbarFrameHeight = SystemBarUtils.getTaskbarHeight(res);
     }
 
     /**
@@ -321,6 +325,17 @@
         outBounds.inset(mStableInsets);
     }
 
+    /** Predicts the calculated stable bounds when in Desktop Mode. */
+    public void getStableBoundsForDesktopMode(Rect outBounds) {
+        getStableBounds(outBounds);
+
+        if (mNavBarFrameHeight != mTaskbarFrameHeight) {
+            // Currently not in pinned taskbar mode, exclude taskbar insets instead of current
+            // navigation insets from bounds.
+            outBounds.bottom = mHeight - mTaskbarFrameHeight;
+        }
+    }
+
     /**
      * Gets navigation bar position for this layout
      * @return Navigation bar position for this layout.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/ImeListener.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/ImeListener.kt
new file mode 100644
index 0000000..a34d7be
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/ImeListener.kt
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.common
+
+import android.graphics.Rect
+import android.view.InsetsSource
+import android.view.InsetsState
+import com.android.wm.shell.common.DisplayInsetsController.OnInsetsChangedListener
+
+abstract class ImeListener(
+    private val mDisplayController: DisplayController,
+    private val mDisplayId: Int
+) : OnInsetsChangedListener {
+    // The last insets state
+    private val mInsetsState = InsetsState()
+    private val mTmpBounds = Rect()
+
+    override fun insetsChanged(insetsState: InsetsState) {
+        if (mInsetsState == insetsState) {
+            return
+        }
+
+        // Get the stable bounds that account for display cutout and system bars to calculate the
+        // relative IME height
+        val layout = mDisplayController.getDisplayLayout(mDisplayId)
+        if (layout == null) {
+            return
+        }
+        layout.getStableBounds(mTmpBounds)
+
+        val wasVisible = getImeVisibilityAndHeight(mInsetsState).first
+        val oldHeight = getImeVisibilityAndHeight(mInsetsState).second
+
+        val isVisible = getImeVisibilityAndHeight(insetsState).first
+        val newHeight = getImeVisibilityAndHeight(insetsState).second
+
+        mInsetsState.set(insetsState, true)
+        if (wasVisible != isVisible || oldHeight != newHeight) {
+            onImeVisibilityChanged(isVisible, newHeight)
+        }
+    }
+
+    private fun getImeVisibilityAndHeight(
+            insetsState: InsetsState): Pair<Boolean, Int> {
+        val source = insetsState.peekSource(InsetsSource.ID_IME)
+        val frame = if (source != null && source.isVisible) source.frame else null
+        val height = if (frame != null) mTmpBounds.bottom - frame.top else 0
+        val visible = source?.isVisible ?: false
+        return Pair(visible, height)
+    }
+
+    /**
+     * To be overridden by implementations to handle IME changes.
+     */
+    protected abstract fun onImeVisibilityChanged(imeVisible: Boolean, imeHeight: Int)
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/MultiInstanceHelper.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/MultiInstanceHelper.kt
index a6be640..4cd2fd0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/MultiInstanceHelper.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/MultiInstanceHelper.kt
@@ -22,7 +22,6 @@
 import android.content.pm.PackageManager
 import android.os.UserHandle
 import android.view.WindowManager.PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI
-import com.android.internal.annotations.VisibleForTesting
 import com.android.internal.protolog.ProtoLog
 import com.android.wm.shell.R
 import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL
@@ -41,7 +40,6 @@
     /**
      * Returns whether a specific component desires to be launched in multiple instances.
      */
-    @VisibleForTesting
     fun supportsMultiInstanceSplit(componentName: ComponentName?): Boolean {
         if (componentName == null || componentName.packageName == null) {
             // TODO(b/262864589): Handle empty component case
@@ -60,7 +58,7 @@
 
         if (!supportsMultiInstanceProperty) {
             // If not checking the multi-instance properties, then return early
-            return false;
+            return false
         }
 
         // Check the activity property first
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsState.java
index 64a1b0c..140d776 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsState.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsState.java
@@ -30,6 +30,7 @@
 import android.os.RemoteException;
 import android.util.ArraySet;
 import android.util.Size;
+import android.util.SparseArray;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.protolog.ProtoLog;
@@ -42,9 +43,7 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
 import java.util.function.Consumer;
@@ -69,26 +68,36 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface StashType {}
 
+    public static final int NAMED_KCA_LAUNCHER_SHELF = 0;
+    public static final int NAMED_KCA_TABLETOP_MODE = 1;
+
+    @IntDef(prefix = { "NAMED_KCA_" }, value = {
+            NAMED_KCA_LAUNCHER_SHELF,
+            NAMED_KCA_TABLETOP_MODE
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface NamedKca {}
+
     private static final String TAG = PipBoundsState.class.getSimpleName();
 
-    private final @NonNull Rect mBounds = new Rect();
-    private final @NonNull Rect mMovementBounds = new Rect();
-    private final @NonNull Rect mNormalBounds = new Rect();
-    private final @NonNull Rect mExpandedBounds = new Rect();
-    private final @NonNull Rect mNormalMovementBounds = new Rect();
-    private final @NonNull Rect mExpandedMovementBounds = new Rect();
-    private final @NonNull PipDisplayLayoutState mPipDisplayLayoutState;
+    @NonNull private final Rect mBounds = new Rect();
+    @NonNull private final Rect mMovementBounds = new Rect();
+    @NonNull private final Rect mNormalBounds = new Rect();
+    @NonNull private final Rect mExpandedBounds = new Rect();
+    @NonNull private final Rect mNormalMovementBounds = new Rect();
+    @NonNull private final Rect mExpandedMovementBounds = new Rect();
+    @NonNull private final PipDisplayLayoutState mPipDisplayLayoutState;
     private final Point mMaxSize = new Point();
     private final Point mMinSize = new Point();
-    private final @NonNull Context mContext;
+    @NonNull private final Context mContext;
     private float mAspectRatio;
     private int mStashedState = STASH_TYPE_NONE;
     private int mStashOffset;
-    private @Nullable PipReentryState mPipReentryState;
+    @Nullable private PipReentryState mPipReentryState;
     private final LauncherState mLauncherState = new LauncherState();
-    private final @NonNull SizeSpecSource mSizeSpecSource;
-    private @Nullable ComponentName mLastPipComponentName;
-    private final @NonNull MotionBoundsState mMotionBoundsState = new MotionBoundsState();
+    @NonNull private final SizeSpecSource mSizeSpecSource;
+    @Nullable private ComponentName mLastPipComponentName;
+    @NonNull private final MotionBoundsState mMotionBoundsState = new MotionBoundsState();
     private boolean mIsImeShowing;
     private int mImeHeight;
     private boolean mIsShelfShowing;
@@ -120,12 +129,18 @@
      * as unrestricted keep clear area. Values in this map would be appended to
      * {@link #getUnrestrictedKeepClearAreas()} and this is meant for internal usage only.
      */
-    private final Map<String, Rect> mNamedUnrestrictedKeepClearAreas = new HashMap<>();
+    private final SparseArray<Rect> mNamedUnrestrictedKeepClearAreas = new SparseArray<>();
 
-    private @Nullable Runnable mOnMinimalSizeChangeCallback;
-    private @Nullable TriConsumer<Boolean, Integer, Boolean> mOnShelfVisibilityChangeCallback;
-    private List<Consumer<Rect>> mOnPipExclusionBoundsChangeCallbacks = new ArrayList<>();
-    private List<Consumer<Float>> mOnAspectRatioChangedCallbacks = new ArrayList<>();
+    @Nullable private Runnable mOnMinimalSizeChangeCallback;
+    @Nullable private TriConsumer<Boolean, Integer, Boolean> mOnShelfVisibilityChangeCallback;
+    private final List<Consumer<Rect>> mOnPipExclusionBoundsChangeCallbacks = new ArrayList<>();
+    private final List<Consumer<Float>> mOnAspectRatioChangedCallbacks = new ArrayList<>();
+
+    /**
+     * This is used to set the launcher shelf height ahead of non-auto-enter-pip animation,
+     * to avoid the race condition. See also {@link #NAMED_KCA_LAUNCHER_SHELF}.
+     */
+    public final Rect mCachedLauncherShelfHeightKeepClearArea = new Rect();
 
     // the size of the current bounds relative to the max size spec
     private float mBoundsScale;
@@ -430,17 +445,32 @@
         mUnrestrictedKeepClearAreas.addAll(unrestrictedAreas);
     }
 
-    /** Add a named unrestricted keep clear area. */
-    public void addNamedUnrestrictedKeepClearArea(@NonNull String name, Rect unrestrictedArea) {
-        mNamedUnrestrictedKeepClearAreas.put(name, unrestrictedArea);
+    /** Set a named unrestricted keep clear area. */
+    public void setNamedUnrestrictedKeepClearArea(
+            @NamedKca int tag, @Nullable Rect unrestrictedArea) {
+        if (unrestrictedArea == null) {
+            mNamedUnrestrictedKeepClearAreas.remove(tag);
+        } else {
+            mNamedUnrestrictedKeepClearAreas.put(tag, unrestrictedArea);
+            if (tag == NAMED_KCA_LAUNCHER_SHELF) {
+                mCachedLauncherShelfHeightKeepClearArea.set(unrestrictedArea);
+            }
+        }
     }
 
-    /** Remove a named unrestricted keep clear area. */
-    public void removeNamedUnrestrictedKeepClearArea(@NonNull String name) {
-        mNamedUnrestrictedKeepClearAreas.remove(name);
+    /**
+     * Forcefully set the keep-clear-area for launcher shelf height if applicable.
+     * This is used for entering PiP in button navigation mode to make sure the destination bounds
+     * calculation includes the shelf height, to avoid race conditions that such callback is sent
+     * from Launcher after the entering animation is started.
+     */
+    public void mayUseCachedLauncherShelfHeight() {
+        if (!mCachedLauncherShelfHeightKeepClearArea.isEmpty()) {
+            setNamedUnrestrictedKeepClearArea(
+                    NAMED_KCA_LAUNCHER_SHELF, mCachedLauncherShelfHeightKeepClearArea);
+        }
     }
 
-
     /**
      * @return restricted keep clear areas.
      */
@@ -454,9 +484,12 @@
      */
     @NonNull
     public Set<Rect> getUnrestrictedKeepClearAreas() {
-        if (mNamedUnrestrictedKeepClearAreas.isEmpty()) return mUnrestrictedKeepClearAreas;
+        if (mNamedUnrestrictedKeepClearAreas.size() == 0) return mUnrestrictedKeepClearAreas;
         final Set<Rect> unrestrictedAreas = new ArraySet<>(mUnrestrictedKeepClearAreas);
-        unrestrictedAreas.addAll(mNamedUnrestrictedKeepClearAreas.values());
+        for (int i = 0; i < mNamedUnrestrictedKeepClearAreas.size(); i++) {
+            final int key = mNamedUnrestrictedKeepClearAreas.keyAt(i);
+            unrestrictedAreas.add(mNamedUnrestrictedKeepClearAreas.get(key));
+        }
         return unrestrictedAreas;
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerSnapAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerSnapAlgorithm.java
index bc6ed1f..2e1789a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerSnapAlgorithm.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerSnapAlgorithm.java
@@ -16,7 +16,6 @@
 
 package com.android.wm.shell.common.split;
 
-import static android.view.WindowManager.DOCKED_INVALID;
 import static android.view.WindowManager.DOCKED_LEFT;
 import static android.view.WindowManager.DOCKED_RIGHT;
 
@@ -29,13 +28,8 @@
 import static com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_START_AND_DISMISS;
 import static com.android.wm.shell.common.split.SplitScreenConstants.SnapPosition;
 
-import android.content.Context;
-import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.graphics.Rect;
-import android.hardware.display.DisplayManager;
-import android.view.Display;
-import android.view.DisplayInfo;
 
 import androidx.annotation.Nullable;
 
@@ -86,7 +80,7 @@
     private final float mFixedRatio;
     /** Allows split ratios to calculated dynamically instead of using {@link #mFixedRatio}. */
     private final boolean mAllowFlexibleSplitRatios;
-    private boolean mIsHorizontalDivision;
+    private final boolean mIsHorizontalDivision;
 
     /** The first target which is still splitting the screen */
     private final SnapTarget mFirstSplitTarget;
@@ -98,27 +92,6 @@
     private final SnapTarget mDismissEndTarget;
     private final SnapTarget mMiddleTarget;
 
-    public static DividerSnapAlgorithm create(Context ctx, Rect insets) {
-        DisplayInfo displayInfo = new DisplayInfo();
-        ctx.getSystemService(DisplayManager.class).getDisplay(
-                Display.DEFAULT_DISPLAY).getDisplayInfo(displayInfo);
-        int dividerWindowWidth = ctx.getResources().getDimensionPixelSize(
-                com.android.internal.R.dimen.docked_stack_divider_thickness);
-        int dividerInsets = ctx.getResources().getDimensionPixelSize(
-                com.android.internal.R.dimen.docked_stack_divider_insets);
-        return new DividerSnapAlgorithm(ctx.getResources(),
-                displayInfo.logicalWidth, displayInfo.logicalHeight,
-                dividerWindowWidth - 2 * dividerInsets,
-                ctx.getApplicationContext().getResources().getConfiguration().orientation
-                        == Configuration.ORIENTATION_PORTRAIT,
-                insets);
-    }
-
-    public DividerSnapAlgorithm(Resources res, int displayWidth, int displayHeight, int dividerSize,
-            boolean isHorizontalDivision, Rect insets) {
-        this(res, displayWidth, displayHeight, dividerSize, isHorizontalDivision, insets,
-                DOCKED_INVALID, false /* minimized */, true /* resizable */);
-    }
 
     public DividerSnapAlgorithm(Resources res, int displayWidth, int displayHeight, int dividerSize,
         boolean isHorizontalDivision, Rect insets, int dockSide) {
@@ -160,29 +133,11 @@
     }
 
     /**
-     * @return whether it's feasible to enable split screen in the current configuration, i.e. when
-     *         snapping in the middle both tasks are larger than the minimal task size.
-     */
-    public boolean isSplitScreenFeasible() {
-        int statusBarSize = mInsets.top;
-        int navBarSize = mIsHorizontalDivision ? mInsets.bottom : mInsets.right;
-        int size = mIsHorizontalDivision
-                ? mDisplayHeight
-                : mDisplayWidth;
-        int availableSpace = size - navBarSize - statusBarSize - mDividerSize;
-        return availableSpace / 2 >= mMinimalSizeResizableTask;
-    }
-
-    public SnapTarget calculateSnapTarget(int position, float velocity) {
-        return calculateSnapTarget(position, velocity, true /* hardDismiss */);
-    }
-
-    /**
      * @param position the top/left position of the divider
      * @param velocity current dragging velocity
-     * @param hardDismiss if set, make it a bit harder to get reach the dismiss targets
+     * @param hardToDismiss if set, make it a bit harder to get reach the dismiss targets
      */
-    public SnapTarget calculateSnapTarget(int position, float velocity, boolean hardDismiss) {
+    public SnapTarget calculateSnapTarget(int position, float velocity, boolean hardToDismiss) {
         if (position < mFirstSplitTarget.position && velocity < -mMinDismissVelocityPxPerSecond) {
             return mDismissStartTarget;
         }
@@ -190,7 +145,7 @@
             return mDismissEndTarget;
         }
         if (Math.abs(velocity) < mMinFlingVelocityPxPerSecond) {
-            return snap(position, hardDismiss);
+            return snap(position, hardToDismiss);
         }
         if (velocity < 0) {
             return mFirstSplitTarget;
@@ -236,19 +191,6 @@
         return 0f;
     }
 
-    public SnapTarget getClosestDismissTarget(int position) {
-        if (position < mFirstSplitTarget.position) {
-            return mDismissStartTarget;
-        } else if (position > mLastSplitTarget.position) {
-            return mDismissEndTarget;
-        } else if (position - mDismissStartTarget.position
-                < mDismissEndTarget.position - position) {
-            return mDismissStartTarget;
-        } else {
-            return mDismissEndTarget;
-        }
-    }
-
     public SnapTarget getFirstSplitTarget() {
         return mFirstSplitTarget;
     }
@@ -411,22 +353,6 @@
         return mMiddleTarget;
     }
 
-    public SnapTarget getNextTarget(SnapTarget snapTarget) {
-        int index = mTargets.indexOf(snapTarget);
-        if (index != -1 && index < mTargets.size() - 1) {
-            return mTargets.get(index + 1);
-        }
-        return snapTarget;
-    }
-
-    public SnapTarget getPreviousTarget(SnapTarget snapTarget) {
-        int index = mTargets.indexOf(snapTarget);
-        if (index != -1 && index > 0) {
-            return mTargets.get(index - 1);
-        }
-        return snapTarget;
-    }
-
     /**
      * @return whether or not there are more than 1 split targets that do not include the two
      * dismiss targets, used in deciding to display the middle target for accessibility
@@ -451,26 +377,6 @@
     }
 
     /**
-     * Cycles through all non-dismiss targets with a stepping of {@param increment}. It moves left
-     * if {@param increment} is negative and moves right otherwise.
-     */
-    public SnapTarget cycleNonDismissTarget(SnapTarget snapTarget, int increment) {
-        int index = mTargets.indexOf(snapTarget);
-        if (index != -1) {
-            SnapTarget newTarget = mTargets.get((index + mTargets.size() + increment)
-                    % mTargets.size());
-            if (newTarget == mDismissStartTarget) {
-                return mLastSplitTarget;
-            } else if (newTarget == mDismissEndTarget) {
-                return mFirstSplitTarget;
-            } else {
-                return newTarget;
-            }
-        }
-        return snapTarget;
-    }
-
-    /**
      * Represents a snap target for the divider.
      */
     public static class SnapTarget {
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 5097ed8..19a109e 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
@@ -31,6 +31,7 @@
 import android.app.ActivityManager;
 import android.content.Context;
 import android.content.res.Configuration;
+import android.graphics.Color;
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
@@ -201,7 +202,7 @@
     /** Showing resizing hint. */
     public void onResizing(ActivityManager.RunningTaskInfo resizingTask, Rect newBounds,
             Rect sideBounds, SurfaceControl.Transaction t, int offsetX, int offsetY,
-            boolean immediately, float[] veilColor) {
+            boolean immediately) {
         if (mResizingIconView == null) {
             return;
         }
@@ -234,7 +235,7 @@
         if (mBackgroundLeash == null) {
             mBackgroundLeash = SurfaceUtils.makeColorLayer(mHostLeash,
                     RESIZING_BACKGROUND_SURFACE_NAME, mSurfaceSession);
-            t.setColor(mBackgroundLeash, veilColor)
+            t.setColor(mBackgroundLeash, getResizingBackgroundColor(resizingTask))
                     .setLayer(mBackgroundLeash, Integer.MAX_VALUE - 1);
         }
 
@@ -245,7 +246,7 @@
             mGapBackgroundLeash = SurfaceUtils.makeColorLayer(mHostLeash,
                     GAP_BACKGROUND_SURFACE_NAME, mSurfaceSession);
             // Fill up another side bounds area.
-            t.setColor(mGapBackgroundLeash, veilColor)
+            t.setColor(mGapBackgroundLeash, getResizingBackgroundColor(resizingTask))
                     .setLayer(mGapBackgroundLeash, Integer.MAX_VALUE - 2)
                     .setPosition(mGapBackgroundLeash, left, top)
                     .setWindowCrop(mGapBackgroundLeash, sideBounds.width(), sideBounds.height());
@@ -486,4 +487,9 @@
             mIcon = null;
         }
     }
+
+    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/SplitScreenUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenUtils.java
index e8226051..f9259e7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenUtils.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenUtils.java
@@ -16,6 +16,8 @@
 
 package com.android.wm.shell.common.split;
 
+import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_ALL_KINDS_WITH_ALL_PINNED;
+
 import static com.android.wm.shell.common.split.SplitScreenConstants.CONTROLLED_ACTIVITY_TYPES;
 import static com.android.wm.shell.common.split.SplitScreenConstants.CONTROLLED_WINDOWING_MODES;
 import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
@@ -24,18 +26,25 @@
 
 import android.app.ActivityManager;
 import android.app.PendingIntent;
+import android.content.ComponentName;
 import android.content.Intent;
+import android.content.pm.LauncherApps;
+import android.content.pm.ShortcutInfo;
 import android.content.res.Configuration;
 import android.content.res.Resources;
-import android.graphics.Color;
 import android.graphics.Rect;
+import android.os.UserHandle;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
 import com.android.internal.util.ArrayUtils;
 import com.android.wm.shell.Flags;
 import com.android.wm.shell.ShellTaskOrganizer;
 
+import java.util.Arrays;
+import java.util.List;
+
 /** Helper utility class for split screen components to use. */
 public class SplitScreenUtils {
     /** Reverse the split position. */
@@ -128,10 +137,4 @@
             return isLandscape;
         }
     }
-
-    /** Returns the specified background color that matches a RunningTaskInfo. */
-    public static Color getResizingBackgroundColor(ActivityManager.RunningTaskInfo taskInfo) {
-        final int taskBgColor = taskInfo.taskDescription.getBackgroundColor();
-        return Color.valueOf(taskBgColor == -1 ? Color.WHITE : taskBgColor);
-    }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUILayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUILayout.java
index 2b0bd32..688f8ca 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUILayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUILayout.java
@@ -16,10 +16,7 @@
 
 package com.android.wm.shell.compatui;
 
-import static android.app.CameraCompatTaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED;
-
 import android.annotation.IdRes;
-import android.app.CameraCompatTaskInfo.CameraCompatControlState;
 import android.content.Context;
 import android.util.AttributeSet;
 import android.view.View;
@@ -57,28 +54,10 @@
         mWindowManager = windowManager;
     }
 
-    void updateCameraTreatmentButton(@CameraCompatControlState int newState) {
-        int buttonBkgId = newState == CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED
-                ? R.drawable.camera_compat_treatment_suggested_ripple
-                : R.drawable.camera_compat_treatment_applied_ripple;
-        int hintStringId = newState == CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED
-                ? R.string.camera_compat_treatment_suggested_button_description
-                : R.string.camera_compat_treatment_applied_button_description;
-        final ImageButton button = findViewById(R.id.camera_compat_treatment_button);
-        button.setImageResource(buttonBkgId);
-        button.setContentDescription(getResources().getString(hintStringId));
-        final LinearLayout hint = findViewById(R.id.camera_compat_hint);
-        ((TextView) hint.findViewById(R.id.compat_mode_hint_text)).setText(hintStringId);
-    }
-
     void setSizeCompatHintVisibility(boolean show) {
         setViewVisibility(R.id.size_compat_hint, show);
     }
 
-    void setCameraCompatHintVisibility(boolean show) {
-        setViewVisibility(R.id.camera_compat_hint, show);
-    }
-
     void setRestartButtonVisibility(boolean show) {
         setViewVisibility(R.id.size_compat_restart_button, show);
         // Hint should never be visible without button.
@@ -87,14 +66,6 @@
         }
     }
 
-    void setCameraControlVisibility(boolean show) {
-        setViewVisibility(R.id.camera_compat_control, show);
-        // Hint should never be visible without button.
-        if (!show) {
-            setCameraCompatHintVisibility(/* show= */ false);
-        }
-    }
-
     private void setViewVisibility(@IdRes int resId, boolean show) {
         final View view = findViewById(resId);
         int visibility = show ? View.VISIBLE : View.GONE;
@@ -127,26 +98,5 @@
         ((TextView) sizeCompatHint.findViewById(R.id.compat_mode_hint_text))
                 .setText(R.string.restart_button_description);
         sizeCompatHint.setOnClickListener(view -> setSizeCompatHintVisibility(/* show= */ false));
-
-        final ImageButton cameraTreatmentButton =
-                findViewById(R.id.camera_compat_treatment_button);
-        cameraTreatmentButton.setOnClickListener(
-                view -> mWindowManager.onCameraTreatmentButtonClicked());
-        cameraTreatmentButton.setOnLongClickListener(view -> {
-            mWindowManager.onCameraButtonLongClicked();
-            return true;
-        });
-
-        final ImageButton cameraDismissButton = findViewById(R.id.camera_compat_dismiss_button);
-        cameraDismissButton.setOnClickListener(
-                view -> mWindowManager.onCameraDismissButtonClicked());
-        cameraDismissButton.setOnLongClickListener(view -> {
-            mWindowManager.onCameraButtonLongClicked();
-            return true;
-        });
-
-        final LinearLayout cameraCompatHint = findViewById(R.id.camera_compat_hint);
-        cameraCompatHint.setOnClickListener(
-                view -> setCameraCompatHintVisibility(/* show= */ false));
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java
index 1931212..271c07d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java
@@ -16,20 +16,16 @@
 
 package com.android.wm.shell.compatui;
 
-import static android.app.CameraCompatTaskInfo.CAMERA_COMPAT_CONTROL_DISMISSED;
-import static android.app.CameraCompatTaskInfo.CAMERA_COMPAT_CONTROL_HIDDEN;
-import static android.app.CameraCompatTaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED;
-import static android.app.CameraCompatTaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED;
 import static android.view.WindowManager.LARGE_SCREEN_SMALLEST_SCREEN_WIDTH_DP;
 import static android.window.TaskConstants.TASK_CHILD_LAYER_COMPAT_UI;
 
+import static com.android.wm.shell.shared.desktopmode.DesktopModeFlags.DESKTOP_WINDOWING_MODE;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.app.CameraCompatTaskInfo.CameraCompatControlState;
 import android.app.TaskInfo;
 import android.content.Context;
 import android.graphics.Rect;
-import android.util.Log;
 import android.util.Pair;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -42,8 +38,8 @@
 import com.android.wm.shell.common.SyncTransactionQueue;
 import com.android.wm.shell.compatui.CompatUIController.CompatUIHintsState;
 import com.android.wm.shell.compatui.api.CompatUIEvent;
-import com.android.wm.shell.compatui.impl.CompatUIEvents.CameraControlStateUpdated;
 import com.android.wm.shell.compatui.impl.CompatUIEvents.SizeCompatRestartButtonAppeared;
+import com.android.wm.shell.shared.desktopmode.DesktopModeFlags;
 
 import java.util.function.Consumer;
 
@@ -66,10 +62,6 @@
     boolean mHasSizeCompat;
 
     @VisibleForTesting
-    @CameraCompatControlState
-    int mCameraCompatControlState = CAMERA_COMPAT_CONTROL_HIDDEN;
-
-    @VisibleForTesting
     @NonNull
     CompatUIHintsState mCompatUIHintsState;
 
@@ -91,12 +83,11 @@
         super(context, taskInfo, syncQueue, taskListener, displayLayout);
         mCallback = callback;
         mHasSizeCompat = taskInfo.appCompatTaskInfo.topActivityInSizeCompat;
-        if (Flags.enableDesktopWindowingMode() && Flags.enableWindowingDynamicInitialBounds()) {
+        if (DESKTOP_WINDOWING_MODE.isEnabled(mContext)
+                && DesktopModeFlags.DYNAMIC_INITIAL_BOUNDS.isEnabled(context)) {
             // Don't show the SCM button for freeform tasks
             mHasSizeCompat &= !taskInfo.isFreeform();
         }
-        mCameraCompatControlState =
-                taskInfo.appCompatTaskInfo.cameraCompatTaskInfo.cameraCompatControlState;
         mCompatUIHintsState = compatUIHintsState;
         mCompatUIConfiguration = compatUIConfiguration;
         mOnRestartButtonClicked = onRestartButtonClicked;
@@ -120,8 +111,7 @@
 
     @Override
     protected boolean eligibleToShowLayout() {
-        return (mHasSizeCompat && shouldShowSizeCompatRestartButton(getLastTaskInfo()))
-                || shouldShowCameraControl();
+        return mHasSizeCompat && shouldShowSizeCompatRestartButton(getLastTaskInfo());
     }
 
     @Override
@@ -148,21 +138,18 @@
     public boolean updateCompatInfo(TaskInfo taskInfo, ShellTaskOrganizer.TaskListener taskListener,
             boolean canShow) {
         final boolean prevHasSizeCompat = mHasSizeCompat;
-        final int prevCameraCompatControlState = mCameraCompatControlState;
         mHasSizeCompat = taskInfo.appCompatTaskInfo.topActivityInSizeCompat;
-        if (Flags.enableDesktopWindowingMode() && Flags.enableWindowingDynamicInitialBounds()) {
+        if (DESKTOP_WINDOWING_MODE.isEnabled(mContext)
+                && DesktopModeFlags.DYNAMIC_INITIAL_BOUNDS.isEnabled(mContext)) {
             // Don't show the SCM button for freeform tasks
             mHasSizeCompat &= !taskInfo.isFreeform();
         }
-        mCameraCompatControlState =
-                taskInfo.appCompatTaskInfo.cameraCompatTaskInfo.cameraCompatControlState;
 
         if (!super.updateCompatInfo(taskInfo, taskListener, canShow)) {
             return false;
         }
 
-        if (prevHasSizeCompat != mHasSizeCompat
-                || prevCameraCompatControlState != mCameraCompatControlState) {
+        if (prevHasSizeCompat != mHasSizeCompat) {
             updateVisibilityOfViews();
         }
 
@@ -174,34 +161,6 @@
         mOnRestartButtonClicked.accept(Pair.create(getLastTaskInfo(), getTaskListener()));
     }
 
-    /** Called when the camera treatment button is clicked. */
-    void onCameraTreatmentButtonClicked() {
-        if (!shouldShowCameraControl()) {
-            Log.w(getTag(), "Camera compat shouldn't receive clicks in the hidden state.");
-            return;
-        }
-        // When a camera control is shown, only two states are allowed: "treament applied" and
-        // "treatment suggested". Clicks on the conrol's treatment button toggle between these
-        // two states.
-        mCameraCompatControlState =
-                mCameraCompatControlState == CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED
-                        ? CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED
-                        : CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED;
-        mCallback.accept(new CameraControlStateUpdated(mTaskId, mCameraCompatControlState));
-        mLayout.updateCameraTreatmentButton(mCameraCompatControlState);
-    }
-
-    /** Called when the camera dismiss button is clicked. */
-    void onCameraDismissButtonClicked() {
-        if (!shouldShowCameraControl()) {
-            Log.w(getTag(), "Camera compat shouldn't receive clicks in the hidden state.");
-            return;
-        }
-        mCameraCompatControlState = CAMERA_COMPAT_CONTROL_DISMISSED;
-        mCallback.accept(new CameraControlStateUpdated(mTaskId, CAMERA_COMPAT_CONTROL_DISMISSED));
-        mLayout.setCameraControlVisibility(/* show= */ false);
-    }
-
     /** Called when the restart button is long clicked. */
     void onRestartButtonLongClicked() {
         if (mLayout == null) {
@@ -210,14 +169,6 @@
         mLayout.setSizeCompatHintVisibility(/* show= */ true);
     }
 
-    /** Called when either dismiss or treatment camera buttons is long clicked. */
-    void onCameraButtonLongClicked() {
-        if (mLayout == null) {
-            return;
-        }
-        mLayout.setCameraCompatHintVisibility(/* show= */ true);
-    }
-
     @Override
     @VisibleForTesting
     public void updateSurfacePosition() {
@@ -265,6 +216,7 @@
             return false;
         }
         final float percentageAreaOfLetterboxInTask = (float) letterboxArea / taskArea * 100;
+
         return percentageAreaOfLetterboxInTask < mHideScmTolerance;
     }
 
@@ -279,21 +231,5 @@
             mLayout.setSizeCompatHintVisibility(/* show= */ true);
             mCompatUIHintsState.mHasShownSizeCompatHint = true;
         }
-
-        // Camera control for stretched issues.
-        mLayout.setCameraControlVisibility(shouldShowCameraControl());
-        // Only show by default for the first time.
-        if (shouldShowCameraControl() && !mCompatUIHintsState.mHasShownCameraCompatHint) {
-            mLayout.setCameraCompatHintVisibility(/* show= */ true);
-            mCompatUIHintsState.mHasShownCameraCompatHint = true;
-        }
-        if (shouldShowCameraControl()) {
-            mLayout.updateCameraTreatmentButton(mCameraCompatControlState);
-        }
-    }
-
-    private boolean shouldShowCameraControl() {
-        return mCameraCompatControlState != CAMERA_COMPAT_CONTROL_HIDDEN
-                && mCameraCompatControlState != CAMERA_COMPAT_CONTROL_DISMISSED;
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/impl/CompatUIEvents.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/impl/CompatUIEvents.kt
index 58ce8ed..23205c3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/impl/CompatUIEvents.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/impl/CompatUIEvents.kt
@@ -16,13 +16,10 @@
 
 package com.android.wm.shell.compatui.impl
 
-import android.app.AppCompatTaskInfo
-import android.app.CameraCompatTaskInfo
 import com.android.wm.shell.compatui.api.CompatUIEvent
 
 internal const val SIZE_COMPAT_RESTART_BUTTON_APPEARED = 0
 internal const val SIZE_COMPAT_RESTART_BUTTON_CLICKED = 1
-internal const val CAMERA_CONTROL_STATE_UPDATE = 2
 
 /**
  * All the {@link CompatUIEvent} the Compat UI Framework can handle
@@ -35,10 +32,4 @@
     /** Sent when the size compat restart button is clicked. */
     data class SizeCompatRestartButtonClicked(val taskId: Int) :
             CompatUIEvents(SIZE_COMPAT_RESTART_BUTTON_CLICKED)
-
-    /** Sent when the camera compat control state is updated. */
-    data class CameraControlStateUpdated(
-            val taskId: Int,
-            @CameraCompatTaskInfo.CameraCompatControlState val state: Int
-    ) : CompatUIEvents(CAMERA_CONTROL_STATE_UPDATE)
 }
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
index 4b548cb..717a414 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
@@ -375,7 +375,8 @@
             @ShellBackgroundThread Handler backgroundHandler,
             BackAnimationBackground backAnimationBackground,
             Optional<ShellBackAnimationRegistry> shellBackAnimationRegistry,
-            ShellCommandHandler shellCommandHandler) {
+            ShellCommandHandler shellCommandHandler,
+            Transitions transitions) {
         if (BackAnimationController.IS_ENABLED) {
             return shellBackAnimationRegistry.map(
                     (animations) ->
@@ -387,7 +388,8 @@
                                     context,
                                     backAnimationBackground,
                                     animations,
-                                    shellCommandHandler));
+                                    shellCommandHandler,
+                                    transitions));
         }
         return Optional.empty();
     }
@@ -946,10 +948,11 @@
     @WMSingleton
     @Provides
     static TaskStackTransitionObserver provideTaskStackTransitionObserver(
+            Context context,
             Lazy<Transitions> transitions,
             ShellInit shellInit
     ) {
-        return new TaskStackTransitionObserver(transitions, shellInit);
+        return new TaskStackTransitionObserver(context, transitions, shellInit);
     }
 
     //
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 80f6a63..9cfbde4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -16,6 +16,8 @@
 
 package com.android.wm.shell.dagger;
 
+import static com.android.wm.shell.shared.desktopmode.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_TASK_LIMIT;
+
 import android.annotation.Nullable;
 import android.app.KeyguardManager;
 import android.content.Context;
@@ -30,11 +32,11 @@
 import com.android.internal.logging.UiEventLogger;
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.launcher3.icons.IconProvider;
-import com.android.window.flags.Flags;
 import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
 import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.WindowManagerShellWrapper;
 import com.android.wm.shell.activityembedding.ActivityEmbeddingController;
+import com.android.wm.shell.apptoweb.AppToWebGenericLinksParser;
 import com.android.wm.shell.bubbles.BubbleController;
 import com.android.wm.shell.bubbles.BubbleData;
 import com.android.wm.shell.bubbles.BubbleDataRepository;
@@ -223,7 +225,9 @@
             Transitions transitions,
             Optional<DesktopTasksController> desktopTasksController,
             RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
-            InteractionJankMonitor interactionJankMonitor) {
+            InteractionJankMonitor interactionJankMonitor,
+            AppToWebGenericLinksParser genericLinksParser,
+            MultiInstanceHelper multiInstanceHelper) {
         if (DesktopModeStatus.canEnterDesktopMode(context)) {
             return new DesktopModeWindowDecorViewModel(
                     context,
@@ -242,7 +246,9 @@
                     transitions,
                     desktopTasksController,
                     rootTaskDisplayAreaOrganizer,
-                    interactionJankMonitor);
+                    interactionJankMonitor,
+                    genericLinksParser,
+                    multiInstanceHelper);
         }
         return new CaptionWindowDecorViewModel(
                 context,
@@ -259,6 +265,15 @@
                 transitions);
     }
 
+    @WMSingleton
+    @Provides
+    static AppToWebGenericLinksParser provideGenericLinksParser(
+            Context context,
+            @ShellMainThread ShellExecutor mainExecutor
+    ) {
+        return new AppToWebGenericLinksParser(context, mainExecutor);
+    }
+
     //
     // Freeform
     //
@@ -534,7 +549,8 @@
             MultiInstanceHelper multiInstanceHelper,
             @ShellMainThread ShellExecutor mainExecutor,
             Optional<DesktopTasksLimiter> desktopTasksLimiter,
-            Optional<RecentTasksController> recentTasksController) {
+            Optional<RecentTasksController> recentTasksController,
+            InteractionJankMonitor interactionJankMonitor) {
         return new DesktopTasksController(context, shellInit, shellCommandHandler, shellController,
                 displayController, shellTaskOrganizer, syncQueue, rootTaskDisplayAreaOrganizer,
                 dragAndDropController, transitions, keyguardManager, enterDesktopTransitionHandler,
@@ -542,7 +558,8 @@
                 dragToDesktopTransitionHandler, desktopModeTaskRepository,
                 desktopModeLoggerTransitionObserver, launchAdjacentController,
                 recentsTransitionHandler, multiInstanceHelper,
-                mainExecutor, desktopTasksLimiter, recentTasksController.orElse(null));
+                mainExecutor, desktopTasksLimiter, recentTasksController.orElse(null),
+                interactionJankMonitor);
     }
 
     @WMSingleton
@@ -551,14 +568,23 @@
             Context context,
             Transitions transitions,
             @DynamicOverride DesktopModeTaskRepository desktopModeTaskRepository,
-            ShellTaskOrganizer shellTaskOrganizer) {
+            ShellTaskOrganizer shellTaskOrganizer,
+            InteractionJankMonitor interactionJankMonitor) {
+        int maxTaskLimit = DesktopModeStatus.getMaxTaskLimit(context);
         if (!DesktopModeStatus.canEnterDesktopMode(context)
-                || !Flags.enableDesktopWindowingTaskLimit()) {
+                || !ENABLE_DESKTOP_WINDOWING_TASK_LIMIT.isEnabled(context)
+                || maxTaskLimit <= 0) {
             return Optional.empty();
         }
         return Optional.of(
                 new DesktopTasksLimiter(
-                        transitions, desktopModeTaskRepository, shellTaskOrganizer));
+                        transitions,
+                        desktopModeTaskRepository,
+                        shellTaskOrganizer,
+                        maxTaskLimit,
+                        interactionJankMonitor,
+                        context)
+        );
     }
 
 
@@ -568,9 +594,10 @@
             Context context,
             Transitions transitions,
             RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
-            Optional<DesktopTasksLimiter> desktopTasksLimiter) {
+            Optional<DesktopTasksLimiter> desktopTasksLimiter,
+            InteractionJankMonitor interactionJankMonitor) {
         return new DragToDesktopTransitionHandler(context, transitions,
-                rootTaskDisplayAreaOrganizer);
+                rootTaskDisplayAreaOrganizer, interactionJankMonitor);
     }
 
     @WMSingleton
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 ea7e968..06c1e68 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
@@ -104,6 +104,7 @@
             TaskStackListenerImpl taskStackListener,
             ShellTaskOrganizer shellTaskOrganizer,
             PipTransitionState pipTransitionState,
+            PipTouchHandler pipTouchHandler,
             @ShellMainThread ShellExecutor mainExecutor) {
         if (!PipUtils.isPip2ExperimentEnabled()) {
             return Optional.empty();
@@ -112,7 +113,7 @@
                     context, shellInit, shellCommandHandler, shellController, displayController,
                     displayInsetsController, pipBoundsState, pipBoundsAlgorithm,
                     pipDisplayLayoutState, pipScheduler, taskStackListener, shellTaskOrganizer,
-                    pipTransitionState, mainExecutor));
+                    pipTransitionState, pipTouchHandler, mainExecutor));
         }
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeShellCommandHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeShellCommandHandler.kt
index 1a6ca0e..eca3c1f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeShellCommandHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeShellCommandHandler.kt
@@ -63,8 +63,7 @@
                 pw.println("Error: task id should be an integer")
                 return false
             }
-
-        return controller.moveToDesktop(taskId, transitionSource = UNKNOWN)
+        return controller.moveTaskToDesktop(taskId, transitionSource = UNKNOWN)
     }
 
     private fun runMoveToNextDisplay(args: Array<String>, pw: PrintWriter): Boolean {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt
index 247cc42..8375294 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt
@@ -32,77 +32,77 @@
 import java.util.concurrent.Executor
 import java.util.function.Consumer
 
-/** Keeps track of task data related to desktop mode. */
+/** Tracks task data for Desktop Mode. */
 class DesktopModeTaskRepository {
 
-    /** Task data that is tracked per display */
-    private data class DisplayData(
-        /**
-         * Set of task ids that are marked as active in desktop mode. Active tasks in desktop mode
-         * are freeform tasks that are visible or have been visible after desktop mode was
-         * activated. Task gets removed from this list when it vanishes. Or when desktop mode is
-         * turned off.
-         */
+    /**
+     * Task data tracked per desktop.
+     *
+     * @property activeTasks task ids of active tasks currently or previously visible in Desktop
+     * mode session. Tasks become inactive when task closes or when desktop mode session ends.
+     * @property visibleTasks task ids for active freeform tasks that are currently visible. There
+     * might be other active tasks in desktop mode that are not visible.
+     * @property minimizedTasks task ids for active freeform tasks that are currently minimized.
+     * @property closingTasks task ids for tasks that are going to close, but are currently visible.
+     * @property freeformTasksInZOrder list of current freeform task ids ordered from top to bottom
+     * (top is at index 0).
+     */
+    private data class DesktopTaskData(
         val activeTasks: ArraySet<Int> = ArraySet(),
         val visibleTasks: ArraySet<Int> = ArraySet(),
         val minimizedTasks: ArraySet<Int> = ArraySet(),
-        // Tasks that are closing, but are still visible
         // TODO(b/332682201): Remove when the repository state is updated via TransitionObserver
         val closingTasks: ArraySet<Int> = ArraySet(),
-        // Tasks currently in freeform mode, ordered from top to bottom (top is at index 0).
         val freeformTasksInZOrder: ArrayList<Int> = ArrayList(),
     )
 
-    // Token of the current wallpaper activity, used to remove it when the last task is removed
+    /* Current wallpaper activity token to remove wallpaper activity when last task is removed. */
     var wallpaperActivityToken: WindowContainerToken? = null
+
     private val activeTasksListeners = ArraySet<ActiveTasksListener>()
-    // Track visible tasks separately because a task may be part of the desktop but not visible.
     private val visibleTasksListeners = ArrayMap<VisibleTasksListener, Executor>()
-    // Track corner/caption regions of desktop tasks, used to determine gesture exclusion
+
+    /* Tracks corner/caption regions of desktop tasks, used to determine gesture exclusion. */
     private val desktopExclusionRegions = SparseArray<Region>()
-    // Track last bounds of task before toggled to stable bounds
+
+    /* Tracks last bounds of task before toggled to stable bounds. */
     private val boundsBeforeMaximizeByTaskId = SparseArray<Rect>()
+
     private var desktopGestureExclusionListener: Consumer<Region>? = null
     private var desktopGestureExclusionExecutor: Executor? = null
 
-    private val displayData =
-        object : SparseArray<DisplayData>() {
-            /**
-             * Get the [DisplayData] associated with this [displayId]
-             *
-             * Creates a new instance if one does not exist
-             */
-            fun getOrCreate(displayId: Int): DisplayData {
-                if (!contains(displayId)) {
-                    put(displayId, DisplayData())
-                }
-                return get(displayId)
-            }
-        }
+    private val desktopTaskDataByDisplayId = object : SparseArray<DesktopTaskData>() {
+        /** Gets [DesktopTaskData] for existing [displayId] or creates a new one. */
+        fun getOrCreate(displayId: Int): DesktopTaskData =
+            this[displayId] ?: DesktopTaskData().also { this[displayId] = it }
+    }
 
-    /** Add a [ActiveTasksListener] to be notified of updates to active tasks in the repository. */
+    /** Adds [activeTasksListener] to be notified of updates to active tasks. */
     fun addActiveTaskListener(activeTasksListener: ActiveTasksListener) {
         activeTasksListeners.add(activeTasksListener)
     }
 
-    /** Add a [VisibleTasksListener] to be notified when freeform tasks are visible or not. */
+    /** Adds [visibleTasksListener] to be notified of updates to visible tasks. */
     fun addVisibleTasksListener(visibleTasksListener: VisibleTasksListener, executor: Executor) {
         visibleTasksListeners[visibleTasksListener] = executor
-        displayData.keyIterator().forEach {
+        desktopTaskDataByDisplayId.keyIterator().forEach {
+            val visibleTaskCount = getVisibleTaskCount(it)
             executor.execute {
-                visibleTasksListener.onTasksVisibilityChanged(it, visibleTaskCount(it))
+                visibleTasksListener.onTasksVisibilityChanged(it, visibleTaskCount)
             }
         }
     }
 
-    /** Returns a list of all [DisplayData]. */
-    private fun displayDataList(): Sequence<DisplayData> =
-        displayData.valueIterator().asSequence()
+    /** Updates tasks changes on all the active task listeners for given display id. */
+    private fun updateActiveTasksListeners(displayId: Int) {
+        activeTasksListeners.onEach { it.onActiveTasksChanged(displayId) }
+    }
 
-    /**
-     * Add a Consumer which will inform other classes of changes to exclusion regions for all
-     * Desktop tasks.
-     */
+    /** Returns a list of all [DesktopTaskData] in the repository. */
+    private fun desktopTaskDataSequence(): Sequence<DesktopTaskData> =
+        desktopTaskDataByDisplayId.valueIterator().asSequence()
+
+    /** Adds [regionListener] to inform about changes to exclusion regions for all Desktop tasks. */
     fun setExclusionRegionListener(regionListener: Consumer<Region>, executor: Executor) {
         desktopGestureExclusionListener = regionListener
         desktopGestureExclusionExecutor = executor
@@ -111,7 +111,7 @@
         }
     }
 
-    /** Create a new merged region representative of all exclusion regions in all desktop tasks. */
+    /** Creates a new merged region representative of all exclusion regions in all desktop tasks. */
     private fun calculateDesktopExclusionRegion(): Region {
         val desktopExclusionRegion = Region()
         desktopExclusionRegions.valueIterator().forEach { taskExclusionRegion ->
@@ -120,192 +120,120 @@
         return desktopExclusionRegion
     }
 
-    /** Remove a previously registered [ActiveTasksListener] */
+    /** Remove the previously registered [activeTasksListener] */
     fun removeActiveTasksListener(activeTasksListener: ActiveTasksListener) {
         activeTasksListeners.remove(activeTasksListener)
     }
 
-    /** Remove a previously registered [VisibleTasksListener] */
+    /** Removes the previously registered [visibleTasksListener]. */
     fun removeVisibleTasksListener(visibleTasksListener: VisibleTasksListener) {
         visibleTasksListeners.remove(visibleTasksListener)
     }
 
-    /**
-     * Mark a task with given [taskId] as active on given [displayId]
-     *
-     * @return `true` if the task was not active on given [displayId]
-     */
-    fun addActiveTask(displayId: Int, taskId: Int): Boolean {
-        // Check if task is active on another display, if so, remove it
-        displayData.forEach { id, data ->
-            if (id != displayId && data.activeTasks.remove(taskId)) {
-                activeTasksListeners.onEach { it.onActiveTasksChanged(id) }
+    /** Adds task with [taskId] to the list of active tasks on [displayId]. */
+    fun addActiveTask(displayId: Int, taskId: Int) {
+        // Removes task if it is active on another display excluding [displayId].
+        removeActiveTask(taskId, excludedDisplayId = displayId)
+
+        if (desktopTaskDataByDisplayId.getOrCreate(displayId).activeTasks.add(taskId)) {
+            logD("Adds active task=%d displayId=%d", taskId, displayId)
+            updateActiveTasksListeners(displayId)
+        }
+    }
+
+    /** Removes task from active task list of displays excluding the [excludedDisplayId]. */
+    fun removeActiveTask(taskId: Int, excludedDisplayId: Int? = null) {
+        desktopTaskDataByDisplayId.forEach { displayId, desktopTaskData ->
+            if ((displayId != excludedDisplayId)
+                    && desktopTaskData.activeTasks.remove(taskId)) {
+                logD("Removed active task=%d displayId=%d", taskId, displayId)
+                updateActiveTasksListeners(displayId)
             }
         }
-
-        val added = displayData.getOrCreate(displayId).activeTasks.add(taskId)
-        if (added) {
-            ProtoLog.d(
-                WM_SHELL_DESKTOP_MODE,
-                "DesktopTaskRepo: add active task=%d displayId=%d",
-                taskId,
-                displayId
-            )
-            activeTasksListeners.onEach { it.onActiveTasksChanged(displayId) }
-        }
-        return added
     }
 
-    /**
-     * Remove task with given [taskId] from active tasks.
-     *
-     * @return `true` if the task was active
-     */
-    fun removeActiveTask(taskId: Int): Boolean {
-        var result = false
-        displayData.forEach { displayId, data ->
-            if (data.activeTasks.remove(taskId)) {
-                activeTasksListeners.onEach { it.onActiveTasksChanged(displayId) }
-                result = true
+    /** Adds given task to the closing task list for [displayId]. */
+    fun addClosingTask(displayId: Int, taskId: Int) {
+        if (desktopTaskDataByDisplayId.getOrCreate(displayId).closingTasks.add(taskId)) {
+            logD("Added closing task=%d displayId=%d", taskId, displayId)
+        } else {
+            // If the task hasn't been removed from closing list after it disappeared.
+            logW("Task with taskId=%d displayId=%d is already closing", taskId, displayId)
+        }
+    }
+
+    /** Removes task from the list of closing tasks for [displayId]. */
+    fun removeClosingTask(taskId: Int) {
+        desktopTaskDataByDisplayId.forEach { displayId, taskInfo ->
+            if (taskInfo.closingTasks.remove(taskId)) {
+                logD("Removed closing task=%d displayId=%d", taskId, displayId)
             }
         }
-        if (result) {
-            ProtoLog.d(WM_SHELL_DESKTOP_MODE, "DesktopTaskRepo: remove active task=%d", taskId)
-        }
-        return result
     }
 
-    /**
-     * Mark a task with given [taskId] as closing on given [displayId]
-     *
-     * @return `true` if the task was not closing on given [displayId]
-     */
-    fun addClosingTask(displayId: Int, taskId: Int): Boolean {
-        val added = displayData.getOrCreate(displayId).closingTasks.add(taskId)
-        if (added) {
-            ProtoLog.d(
-                WM_SHELL_DESKTOP_MODE,
-                "DesktopTaskRepo: added closing task=%d displayId=%d",
-                taskId,
-                displayId
-            )
-        }
-        return added
-    }
+    fun isActiveTask(taskId: Int) = desktopTaskDataSequence().any { taskId in it.activeTasks }
+    fun isClosingTask(taskId: Int) = desktopTaskDataSequence().any { taskId in it.closingTasks }
+    fun isVisibleTask(taskId: Int) = desktopTaskDataSequence().any { taskId in it.visibleTasks }
+    fun isMinimizedTask(taskId: Int) = desktopTaskDataSequence().any { taskId in it.minimizedTasks }
 
-    /**
-     * Remove task with given [taskId] from closing tasks.
-     *
-     * @return `true` if the task was closing
-     */
-    fun removeClosingTask(taskId: Int): Boolean {
-        var removed = false
-        displayData.forEach { _, data ->
-            if (data.closingTasks.remove(taskId)) {
-                removed = true
-            }
-        }
-        if (removed) {
-            ProtoLog.d(WM_SHELL_DESKTOP_MODE, "DesktopTaskRepo: remove closing task=%d", taskId)
-        }
-        return removed
-    }
-
-    fun isActiveTask(taskId: Int) = displayDataList().any { taskId in it.activeTasks }
-    fun isClosingTask(taskId: Int) = displayDataList().any { taskId in it.closingTasks }
-    fun isVisibleTask(taskId: Int) = displayDataList().any { taskId in it.visibleTasks }
-    fun isMinimizedTask(taskId: Int) = displayDataList().any { taskId in it.minimizedTasks }
-
-    /**
-     * Check if a task with the given [taskId] is the only visible, non-closing, not-minimized task
-     * on its display
-     */
+    /** Checks if a task is the only visible, non-closing, non-minimized task on its display. */
     fun isOnlyVisibleNonClosingTask(taskId: Int): Boolean =
-        displayDataList().any { data ->
-            data.visibleTasks
-                .subtract(data.closingTasks)
-                .subtract(data.minimizedTasks)
-                .singleOrNull() == taskId
+        desktopTaskDataSequence().any { it.visibleTasks
+            .subtract(it.closingTasks)
+            .subtract(it.minimizedTasks)
+            .singleOrNull() == taskId
         }
 
-    /** Get a set of the active tasks for given [displayId] */
-    fun getActiveTasks(displayId: Int): ArraySet<Int> {
-        return ArraySet(displayData[displayId]?.activeTasks)
-    }
+    fun getActiveTasks(displayId: Int): ArraySet<Int> =
+        ArraySet(desktopTaskDataByDisplayId[displayId]?.activeTasks)
 
-    /** Returns the minimized tasks for the given [displayId]. */
     fun getMinimizedTasks(displayId: Int): ArraySet<Int> =
-        ArraySet(displayData[displayId]?.minimizedTasks)
+        ArraySet(desktopTaskDataByDisplayId[displayId]?.minimizedTasks)
 
-    /**
-     * Returns a list of Tasks IDs representing all active non-minimized Tasks on the given display,
-     * ordered from front to back.
-     */
-    fun getActiveNonMinimizedTasksOrderedFrontToBack(displayId: Int): List<Int> {
-        val activeTasks = getActiveTasks(displayId)
-        val allTasksInZOrder = getFreeformTasksInZOrder(displayId)
-        return activeTasks
-            // Don't show already minimized Tasks
-            .filter { taskId -> !isMinimizedTask(taskId) }
-            .sortedBy { taskId -> allTasksInZOrder.indexOf(taskId) }
+    /** Returns all active non-minimized tasks for [displayId] ordered from top to bottom. */
+    fun getActiveNonMinimizedOrderedTasks(displayId: Int): List<Int> =
+        getFreeformTasksInZOrder(displayId).filter { !isMinimizedTask(it) }
+
+    /** Returns a list of freeform tasks, ordered from top-bottom (top at index 0). */
+    fun getFreeformTasksInZOrder(displayId: Int): ArrayList<Int> =
+        ArrayList(desktopTaskDataByDisplayId[displayId]?.freeformTasksInZOrder ?: emptyList())
+
+    /** Removes task from visible tasks of all displays except [excludedDisplayId]. */
+    private fun removeVisibleTask(taskId: Int, excludedDisplayId: Int? = null) {
+        desktopTaskDataByDisplayId.forEach { displayId, data ->
+            if ((displayId != excludedDisplayId) && data.visibleTasks.remove(taskId)) {
+                notifyVisibleTaskListeners(displayId, data.visibleTasks.size)
+            }
+        }
     }
 
-    /** Get a list of freeform tasks, ordered from top-bottom (top at index 0). */
-    fun getFreeformTasksInZOrder(displayId: Int): ArrayList<Int> =
-        ArrayList(displayData[displayId]?.freeformTasksInZOrder ?: emptyList())
-
     /**
-     * Updates whether a freeform task with this id is visible or not and notifies listeners.
+     * Updates visibility of a freeform task with [taskId] on [displayId] and notifies listeners.
      *
-     * If the task was visible on a different display with a different displayId, it is removed from
-     * the set of visible tasks on that display. Listeners will be notified.
+     * If task was visible on a different display with a different [displayId], removes from
+     * the set of visible tasks on that display and notifies listeners.
      */
-    fun updateVisibleFreeformTasks(displayId: Int, taskId: Int, visible: Boolean) {
+    fun updateTaskVisibility(displayId: Int, taskId: Int, visible: Boolean) {
         if (visible) {
-            // Task is visible. Check if we need to remove it from any other display.
-            val otherDisplays = displayData.keyIterator().asSequence().filter { it != displayId }
-            for (otherDisplayId in otherDisplays) {
-                if (displayData[otherDisplayId].visibleTasks.remove(taskId)) {
-                    notifyVisibleTaskListeners(
-                        otherDisplayId,
-                        displayData[otherDisplayId].visibleTasks.size
-                    )
-                }
-            }
+            // If task is visible, remove it from any other display besides [displayId].
+            removeVisibleTask(taskId, excludedDisplayId = displayId)
         } else if (displayId == INVALID_DISPLAY) {
             // Task has vanished. Check which display to remove the task from.
-            displayData.forEach { displayId, data ->
-                if (data.visibleTasks.remove(taskId)) {
-                    notifyVisibleTaskListeners(displayId, data.visibleTasks.size)
-                }
-            }
+            removeVisibleTask(taskId)
             return
         }
-
-        val prevCount = visibleTaskCount(displayId)
+        val prevCount = getVisibleTaskCount(displayId)
         if (visible) {
-            displayData.getOrCreate(displayId).visibleTasks.add(taskId)
+            desktopTaskDataByDisplayId.getOrCreate(displayId).visibleTasks.add(taskId)
             unminimizeTask(displayId, taskId)
         } else {
-            displayData[displayId]?.visibleTasks?.remove(taskId)
+            desktopTaskDataByDisplayId[displayId]?.visibleTasks?.remove(taskId)
         }
-        val newCount = visibleTaskCount(displayId)
-
-        // Check if count changed
+        val newCount = getVisibleTaskCount(displayId)
         if (prevCount != newCount) {
-            ProtoLog.d(
-                WM_SHELL_DESKTOP_MODE,
-                "DesktopTaskRepo: update task visibility taskId=%d visible=%b displayId=%d",
-                taskId,
-                visible,
-                displayId
-            )
-            ProtoLog.d(
-                WM_SHELL_DESKTOP_MODE,
-                "DesktopTaskRepo: visibleTaskCount has changed from %d to %d",
-                prevCount,
-                newCount
-            )
+            logD("Update task visibility taskId=%d visible=%b displayId=%d",
+                taskId, visible, displayId)
+            logD("VisibleTaskCount has changed from %d to %d", prevCount, newCount)
             notifyVisibleTaskListeners(displayId, newCount)
         }
     }
@@ -316,72 +244,80 @@
         }
     }
 
-    /** Get number of tasks that are marked as visible on given [displayId] */
-    fun visibleTaskCount(displayId: Int): Int {
-        ProtoLog.d(
-            WM_SHELL_DESKTOP_MODE,
-            "DesktopTaskRepo: visibleTaskCount= %d",
-            displayData[displayId]?.visibleTasks?.size ?: 0
-        )
-        return displayData[displayId]?.visibleTasks?.size ?: 0
-    }
+    /** Gets number of visible tasks on given [displayId] */
+    fun getVisibleTaskCount(displayId: Int): Int =
+        desktopTaskDataByDisplayId[displayId]?.visibleTasks?.size ?: 0.also {
+            logD("getVisibleTaskCount=$it")
+        }
 
-    /** Add (or move if it already exists) the task to the top of the ordered list. */
-    // TODO(b/342417921): Identify if there is additional checks needed to move tasks for
-    // multi-display scenarios.
+    /**
+     * Adds task (or moves if it already exists) to the top of the ordered list.
+     *
+     * Unminimizes the task if it is minimized.
+     */
     fun addOrMoveFreeformTaskToTop(displayId: Int, taskId: Int) {
-        ProtoLog.d(
-            WM_SHELL_DESKTOP_MODE,
-            "DesktopTaskRepo: add or move task to top: display=%d, taskId=%d",
-            displayId,
-            taskId
-        )
-        displayData[displayId]?.freeformTasksInZOrder?.remove(taskId)
-        displayData.getOrCreate(displayId).freeformTasksInZOrder.add(0, taskId)
+        logD("Add or move task to top: display=%d taskId=%d", taskId, displayId)
+        desktopTaskDataByDisplayId[displayId]?.freeformTasksInZOrder?.remove(taskId)
+        desktopTaskDataByDisplayId.getOrCreate(displayId).freeformTasksInZOrder.add(0, taskId)
+        // Unminimize the task if it is minimized.
+        unminimizeTask(displayId, taskId)
     }
 
-    /** Mark a Task as minimized. */
+    /** Minimizes the task for [taskId] and [displayId] */
     fun minimizeTask(displayId: Int, taskId: Int) {
-        ProtoLog.v(
-            WM_SHELL_DESKTOP_MODE,
-            "DesktopModeTaskRepository: minimize Task: display=%d, task=%d",
-            displayId,
-            taskId
-        )
-        displayData.getOrCreate(displayId).minimizedTasks.add(taskId)
+        logD("Minimize Task: display=%d, task=%d", displayId, taskId)
+        desktopTaskDataByDisplayId.getOrCreate(displayId).minimizedTasks.add(taskId)
     }
 
-    /** Mark a Task as non-minimized. */
+    /** Unminimizes the task for [taskId] and [displayId] */
     fun unminimizeTask(displayId: Int, taskId: Int) {
-        ProtoLog.v(
-            WM_SHELL_DESKTOP_MODE,
-            "DesktopModeTaskRepository: unminimize Task: display=%d, task=%d",
-            displayId,
-            taskId
-        )
-        displayData[displayId]?.minimizedTasks?.remove(taskId)
+        logD("Unminimize Task: display=%d, task=%d", displayId, taskId)
+        desktopTaskDataByDisplayId[displayId]?.minimizedTasks?.remove(taskId) ?:
+            logW("Unminimize Task: display=%d, task=%d, no task data", displayId, taskId)
     }
 
-    /** Remove the task from the ordered list. */
-    fun removeFreeformTask(displayId: Int, taskId: Int) {
-        ProtoLog.d(
-            WM_SHELL_DESKTOP_MODE,
-            "DesktopTaskRepo: remove freeform task from ordered list: display=%d, taskId=%d",
-            displayId,
-            taskId
-        )
-        displayData[displayId]?.freeformTasksInZOrder?.remove(taskId)
-        boundsBeforeMaximizeByTaskId.remove(taskId)
-        ProtoLog.d(
-            WM_SHELL_DESKTOP_MODE,
-            "DesktopTaskRepo: remaining freeform tasks: %s",
-            displayData[displayId]?.freeformTasksInZOrder?.toDumpString() ?: ""
-        )
+    private fun getDisplayIdForTask(taskId: Int): Int? {
+        desktopTaskDataByDisplayId.forEach { displayId, data ->
+            if (taskId in data.freeformTasksInZOrder) {
+                return displayId
+            }
+        }
+        logW("No display id found for task: taskId=%d", taskId)
+        return null
     }
 
     /**
-     * Updates the active desktop gesture exclusion regions; if desktopExclusionRegions has been
-     * accepted by desktopGestureExclusionListener, it will be updated in the appropriate classes.
+     * Removes [taskId] from the respective display. If [INVALID_DISPLAY], the original display id
+     * will be looked up from the task id.
+     */
+    fun removeFreeformTask(displayId: Int, taskId: Int) {
+        logD("Removes freeform task: taskId=%d", taskId)
+        if (displayId == INVALID_DISPLAY) {
+            // Removes the original display id of the task.
+            getDisplayIdForTask(taskId)?.let { removeTaskFromDisplay(it, taskId) }
+        } else {
+            removeTaskFromDisplay(displayId, taskId)
+        }
+    }
+
+    /** Removes given task from a valid [displayId] and updates the repository state. */
+    private fun removeTaskFromDisplay(displayId: Int, taskId: Int) {
+        logD("Removes freeform task: taskId=%d, displayId=%d", taskId, displayId)
+        desktopTaskDataByDisplayId[displayId]?.freeformTasksInZOrder?.remove(taskId)
+        boundsBeforeMaximizeByTaskId.remove(taskId)
+        logD("Remaining freeform tasks: %s",
+            desktopTaskDataByDisplayId[displayId]?.freeformTasksInZOrder?.toDumpString())
+        // Remove task from unminimized task if it is minimized.
+        unminimizeTask(displayId, taskId)
+        removeActiveTask(taskId)
+        updateTaskVisibility(displayId, taskId, visible = false);
+    }
+
+    /**
+     * Updates active desktop gesture exclusion regions.
+     *
+     * If [desktopExclusionRegions] is accepted by [desktopGestureExclusionListener], updates it in
+     * appropriate classes.
      */
     fun updateTaskExclusionRegions(taskId: Int, taskExclusionRegions: Region) {
         desktopExclusionRegions.put(taskId, taskExclusionRegions)
@@ -391,9 +327,10 @@
     }
 
     /**
-     * Removes the desktop gesture exclusion region for the specified task; if exclusionRegion has
-     * been accepted by desktopGestureExclusionListener, it will be updated in the appropriate
-     * classes.
+     * Removes desktop gesture exclusion region for the specified task.
+     *
+     * If [desktopExclusionRegions] is accepted by [desktopGestureExclusionListener], updates it in
+     * appropriate classes.
      */
     fun removeExclusionRegion(taskId: Int) {
         desktopExclusionRegions.delete(taskId)
@@ -403,26 +340,24 @@
     }
 
     /** Removes and returns the bounds saved before maximizing the given task. */
-    fun removeBoundsBeforeMaximize(taskId: Int): Rect? {
-        return boundsBeforeMaximizeByTaskId.removeReturnOld(taskId)
-    }
+    fun removeBoundsBeforeMaximize(taskId: Int): Rect? =
+        boundsBeforeMaximizeByTaskId.removeReturnOld(taskId)
 
     /** Saves the bounds of the given task before maximizing. */
-    fun saveBoundsBeforeMaximize(taskId: Int, bounds: Rect) {
+    fun saveBoundsBeforeMaximize(taskId: Int, bounds: Rect) =
         boundsBeforeMaximizeByTaskId.set(taskId, Rect(bounds))
-    }
 
     internal fun dump(pw: PrintWriter, prefix: String) {
         val innerPrefix = "$prefix  "
         pw.println("${prefix}DesktopModeTaskRepository")
-        dumpDisplayData(pw, innerPrefix)
+        dumpDesktopTaskData(pw, innerPrefix)
         pw.println("${innerPrefix}activeTasksListeners=${activeTasksListeners.size}")
         pw.println("${innerPrefix}visibleTasksListeners=${visibleTasksListeners.size}")
     }
 
-    private fun dumpDisplayData(pw: PrintWriter, prefix: String) {
+    private fun dumpDesktopTaskData(pw: PrintWriter, prefix: String) {
         val innerPrefix = "$prefix  "
-        displayData.forEach { displayId, data ->
+        desktopTaskDataByDisplayId.forEach { displayId, data ->
             pw.println("${prefix}Display $displayId:")
             pw.println("${innerPrefix}activeTasks=${data.activeTasks.toDumpString()}")
             pw.println("${innerPrefix}visibleTasks=${data.visibleTasks.toDumpString()}")
@@ -432,23 +367,29 @@
         }
     }
 
-    /**
-     * Defines interface for classes that can listen to changes for active tasks in desktop mode.
-     */
+    /** Listens to changes for active tasks in desktop mode. */
     interface ActiveTasksListener {
-        /** Called when the active tasks change in desktop mode. */
         fun onActiveTasksChanged(displayId: Int) {}
     }
 
-    /**
-     * Defines interface for classes that can listen to changes for visible tasks in desktop mode.
-     */
+    /** Listens to changes for visible tasks in desktop mode. */
     interface VisibleTasksListener {
-        /** Called when the desktop changes the number of visible freeform tasks. */
         fun onTasksVisibilityChanged(displayId: Int, visibleTasksCount: Int) {}
     }
+
+    private fun logD(msg: String, vararg arguments: Any?) {
+        ProtoLog.d(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments)
+    }
+
+    private fun logW(msg: String, vararg arguments: Any?) {
+        ProtoLog.w(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments)
+    }
+
+    companion object {
+        private const val TAG = "DesktopModeTaskRepository"
+    }
 }
 
-private fun <T> Iterable<T>.toDumpString(): String {
-    return joinToString(separator = ", ", prefix = "[", postfix = "]")
-}
+private fun <T> Iterable<T>.toDumpString(): String =
+    joinToString(separator = ", ", prefix = "[", postfix = "]")
+
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt
index da212e7..9fcf73d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt
@@ -52,8 +52,10 @@
     val idealSize = calculateIdealSize(screenBounds, scale)
     // If no top activity exists, apps fullscreen bounds and aspect ratio cannot be calculated.
     // Instead default to the desired initial bounds.
+    val stableBounds = Rect()
+    displayLayout.getStableBoundsForDesktopMode(stableBounds)
     val topActivityInfo =
-        taskInfo.topActivityInfo ?: return positionInScreen(idealSize, screenBounds)
+        taskInfo.topActivityInfo ?: return positionInScreen(idealSize, stableBounds)
 
     val initialSize: Size =
         when (taskInfo.configuration.orientation) {
@@ -100,7 +102,7 @@
             }
         }
 
-    return positionInScreen(initialSize, screenBounds)
+    return positionInScreen(initialSize, stableBounds)
 }
 
 /**
@@ -163,17 +165,11 @@
 }
 
 /** Adjusts bounds to be positioned in the middle of the screen. */
-private fun positionInScreen(desiredSize: Size, screenBounds: Rect): Rect {
-    // TODO(b/325240051): Position apps with bottom heavy offset
-    val heightOffset = (screenBounds.height() - desiredSize.height) / 2
-    val widthOffset = (screenBounds.width() - desiredSize.width) / 2
-    return Rect(
-        widthOffset,
-        heightOffset,
-        desiredSize.width + widthOffset,
-        desiredSize.height + heightOffset
-    )
-}
+private fun positionInScreen(desiredSize: Size, stableBounds: Rect): Rect =
+    Rect(0, 0, desiredSize.width, desiredSize.height).apply {
+        val offset = DesktopTaskPosition.Center.getTopLeftCoordinates(stableBounds, this)
+        offsetTo(offset.x, offset.y)
+    }
 
 /**
  * Adjusts bounds to be positioned in the middle of the area provided, not necessarily the
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 ed0d2b8..6011db7 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
@@ -105,7 +105,7 @@
         // If we are in freeform, we don't want a visible indicator in the "freeform" drag zone.
         IndicatorType result = IndicatorType.NO_INDICATOR;
         final int transitionAreaWidth = mContext.getResources().getDimensionPixelSize(
-                com.android.wm.shell.R.dimen.desktop_mode_transition_area_width);
+                com.android.wm.shell.R.dimen.desktop_mode_transition_region_thickness);
         // Because drags in freeform use task position for indicator calculation, we need to
         // account for the possibility of the task going off the top of the screen by captionHeight
         final int captionHeight = mContext.getResources().getDimensionPixelSize(
@@ -140,18 +140,19 @@
         final Region region = new Region();
         int transitionHeight = windowingMode == WINDOWING_MODE_FREEFORM
                 ? mContext.getResources().getDimensionPixelSize(
-                com.android.wm.shell.R.dimen.desktop_mode_fullscreen_from_desktop_height)
+                com.android.wm.shell.R.dimen.desktop_mode_transition_region_thickness)
                 : 2 * layout.stableInsets().top;
-        // A thin, short Rect at the top of the screen.
+        // A Rect at the top of the screen that takes up the center 40%.
         if (windowingMode == WINDOWING_MODE_FREEFORM) {
-            int fromFreeformWidth = mContext.getResources().getDimensionPixelSize(
-                    com.android.wm.shell.R.dimen.desktop_mode_fullscreen_from_desktop_width);
-            region.union(new Rect((layout.width() / 2) - (fromFreeformWidth / 2),
+            final float toFullscreenScale = mContext.getResources().getFloat(
+                    R.dimen.desktop_mode_fullscreen_region_scale);
+            final float toFullscreenWidth = (layout.width() * toFullscreenScale);
+            region.union(new Rect((int) ((layout.width() / 2f) - (toFullscreenWidth / 2f)),
                     -captionHeight,
-                    (layout.width() / 2) + (fromFreeformWidth / 2),
+                    (int) ((layout.width() / 2f) + (toFullscreenWidth / 2f)),
                     transitionHeight));
         }
-        // A screen-wide, shorter Rect if the task is in fullscreen or split.
+        // A screen-wide Rect if the task is in fullscreen or split.
         if (windowingMode == WINDOWING_MODE_FULLSCREEN
                 || windowingMode == WINDOWING_MODE_MULTI_WINDOW) {
             region.union(new Rect(0,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTaskPosition.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTaskPosition.kt
new file mode 100644
index 0000000..97abda8
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTaskPosition.kt
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.desktopmode
+
+import android.app.TaskInfo
+import android.content.res.Resources
+import android.graphics.Point
+import android.graphics.Rect
+import android.view.Gravity
+import com.android.internal.annotations.VisibleForTesting
+import com.android.wm.shell.desktopmode.DesktopTaskPosition.BottomLeft
+import com.android.wm.shell.desktopmode.DesktopTaskPosition.BottomRight
+import com.android.wm.shell.desktopmode.DesktopTaskPosition.Center
+import com.android.wm.shell.desktopmode.DesktopTaskPosition.TopLeft
+import com.android.wm.shell.desktopmode.DesktopTaskPosition.TopRight
+import com.android.wm.shell.R
+
+/**
+ * The position of a task window in desktop mode.
+ */
+sealed class DesktopTaskPosition {
+    data object Center : DesktopTaskPosition() {
+        private const val WINDOW_HEIGHT_PROPORTION = 0.375
+
+        override fun getTopLeftCoordinates(frame: Rect, window: Rect): Point {
+            val x = (frame.width() - window.width()) / 2
+            // Position with more margin at the bottom.
+            val y = (frame.height() - window.height()) * WINDOW_HEIGHT_PROPORTION + frame.top
+            return Point(x, y.toInt())
+        }
+
+        override fun next(): DesktopTaskPosition {
+            return BottomRight
+        }
+    }
+
+    data object BottomRight : DesktopTaskPosition() {
+        override fun getTopLeftCoordinates(frame: Rect, window: Rect): Point {
+            return Point(frame.right - window.width(), frame.bottom - window.height())
+        }
+
+        override fun next(): DesktopTaskPosition {
+            return TopLeft
+        }
+    }
+
+    data object TopLeft : DesktopTaskPosition() {
+        override fun getTopLeftCoordinates(frame: Rect, window: Rect): Point {
+            return Point(frame.left, frame.top)
+        }
+
+        override fun next(): DesktopTaskPosition {
+            return BottomLeft
+        }
+    }
+
+    data object BottomLeft : DesktopTaskPosition() {
+        override fun getTopLeftCoordinates(frame: Rect, window: Rect): Point {
+            return Point(frame.left, frame.bottom - window.height())
+        }
+
+        override fun next(): DesktopTaskPosition {
+            return TopRight
+        }
+    }
+
+    data object TopRight : DesktopTaskPosition() {
+        override fun getTopLeftCoordinates(frame: Rect, window: Rect): Point {
+            return Point(frame.right - window.width(), frame.top)
+        }
+
+        override fun next(): DesktopTaskPosition {
+            return Center
+        }
+    }
+
+    /**
+     * Returns the top left coordinates for the window to be placed in the given
+     * DesktopTaskPosition in the frame.
+     */
+    abstract fun getTopLeftCoordinates(frame: Rect, window: Rect): Point
+
+    abstract fun next(): DesktopTaskPosition
+}
+
+/**
+ * If the app has specified horizontal or vertical gravity layout, don't change the
+ * task position for cascading effect.
+ */
+fun canChangeTaskPosition(taskInfo: TaskInfo): Boolean {
+    taskInfo.topActivityInfo?.windowLayout?.let {
+        val horizontalGravityApplied = it.gravity.and(Gravity.HORIZONTAL_GRAVITY_MASK)
+        val verticalGravityApplied = it.gravity.and(Gravity.VERTICAL_GRAVITY_MASK)
+        return horizontalGravityApplied == 0 && verticalGravityApplied == 0
+    }
+    return true
+}
+
+/**
+ * Returns the current DesktopTaskPosition for a given window in the frame.
+ */
+@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+fun Rect.getDesktopTaskPosition(bounds: Rect): DesktopTaskPosition {
+    return when {
+        top == bounds.top && left == bounds.left -> TopLeft
+        top == bounds.top && right == bounds.right -> TopRight
+        bottom == bounds.bottom && left == bounds.left -> BottomLeft
+        bottom == bounds.bottom && right == bounds.right -> BottomRight
+        else -> Center
+    }
+}
+
+internal fun cascadeWindow(res: Resources, frame: Rect, prev: Rect, dest: Rect) {
+    val candidateBounds = Rect(dest)
+    val lastPos = frame.getDesktopTaskPosition(prev)
+    var destCoord = Center.getTopLeftCoordinates(frame, candidateBounds)
+    candidateBounds.offsetTo(destCoord.x, destCoord.y)
+    // If the default center position is not free or if last focused window is not at the
+    // center, get the next cascading window position.
+    if (!prevBoundsMovedAboveThreshold(res, prev, candidateBounds) || Center != lastPos) {
+        val nextCascadingPos = lastPos.next()
+        destCoord = nextCascadingPos.getTopLeftCoordinates(frame, dest)
+    }
+    dest.offsetTo(destCoord.x, destCoord.y)
+}
+
+internal fun prevBoundsMovedAboveThreshold(res: Resources, prev: Rect, newBounds: Rect): Boolean {
+    // This is the required minimum dp for a task to be touchable.
+    val moveThresholdPx = res.getDimensionPixelSize(
+        R.dimen.freeform_required_visible_empty_space_in_header)
+    val leftFar = newBounds.left - prev.left > moveThresholdPx
+    val topFar = newBounds.top - prev.top > moveThresholdPx
+    val rightFar = prev.right - newBounds.right > moveThresholdPx
+    val bottomFar = prev.bottom - newBounds.bottom > moveThresholdPx
+
+    return leftFar || topFar || rightFar || bottomFar
+}
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 de901b5..3e417b6 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
@@ -49,9 +49,11 @@
 import android.window.WindowContainerTransaction
 import androidx.annotation.BinderThread
 import com.android.internal.annotations.VisibleForTesting
+import com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_HOLD
+import com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_RELEASE
+import com.android.internal.jank.InteractionJankMonitor
 import com.android.internal.policy.ScreenDecorationsUtils
 import com.android.internal.protolog.ProtoLog
-import com.android.window.flags.Flags
 import com.android.wm.shell.RootTaskDisplayAreaOrganizer
 import com.android.wm.shell.ShellTaskOrganizer
 import com.android.wm.shell.common.DisplayController
@@ -64,6 +66,7 @@
 import com.android.wm.shell.common.ShellExecutor
 import com.android.wm.shell.common.SingleInstanceRemoteListener
 import com.android.wm.shell.common.SyncTransactionQueue
+import com.android.wm.shell.shared.desktopmode.DesktopModeFlags.WALLPAPER_ACTIVITY
 import com.android.wm.shell.common.desktopmode.DesktopModeTransitionSource
 import com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT
 import com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT
@@ -76,6 +79,7 @@
 import com.android.wm.shell.recents.RecentTasksController
 import com.android.wm.shell.recents.RecentsTransitionHandler
 import com.android.wm.shell.recents.RecentsTransitionStateListener
+import com.android.wm.shell.shared.desktopmode.DesktopModeFlags
 import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
 import com.android.wm.shell.shared.desktopmode.DesktopModeStatus.DESKTOP_DENSITY_OVERRIDE
 import com.android.wm.shell.shared.desktopmode.DesktopModeStatus.useDesktopOverrideDensity
@@ -94,6 +98,7 @@
 import com.android.wm.shell.windowdecor.MoveToDesktopAnimator
 import com.android.wm.shell.windowdecor.OnTaskResizeAnimationListener
 import com.android.wm.shell.windowdecor.extension.isFullscreen
+import com.android.wm.shell.windowdecor.extension.isMultiWindow
 import java.io.PrintWriter
 import java.util.Optional
 import java.util.concurrent.Executor
@@ -116,14 +121,15 @@
     private val exitDesktopTaskTransitionHandler: ExitDesktopTaskTransitionHandler,
     private val toggleResizeDesktopTaskTransitionHandler: ToggleResizeDesktopTaskTransitionHandler,
     private val dragToDesktopTransitionHandler: DragToDesktopTransitionHandler,
-    private val desktopModeTaskRepository: DesktopModeTaskRepository,
+    private val taskRepository: DesktopModeTaskRepository,
     private val desktopModeLoggerTransitionObserver: DesktopModeLoggerTransitionObserver,
     private val launchAdjacentController: LaunchAdjacentController,
     private val recentsTransitionHandler: RecentsTransitionHandler,
     private val multiInstanceHelper: MultiInstanceHelper,
     @ShellMainThread private val mainExecutor: ShellExecutor,
     private val desktopTasksLimiter: Optional<DesktopTasksLimiter>,
-    private val recentTasksController: RecentTasksController?
+    private val recentTasksController: RecentTasksController?,
+    private val interactionJankMonitor: InteractionJankMonitor
 ) :
     RemoteCallable<DesktopTasksController>,
     Transitions.TransitionHandler,
@@ -175,7 +181,7 @@
     }
 
     private fun onInit() {
-        ProtoLog.d(WM_SHELL_DESKTOP_MODE, "Initialize DesktopTasksController")
+        logD("onInit")
         shellCommandHandler.addDumpCallback(this::dump, this)
         shellCommandHandler.addCommandCallback("desktopmode", desktopModeShellCommandHandler, this)
         shellController.addExternalInterface(
@@ -184,16 +190,12 @@
             this
         )
         transitions.addHandler(this)
-        desktopModeTaskRepository.addVisibleTasksListener(taskVisibilityListener, mainExecutor)
+        taskRepository.addVisibleTasksListener(taskVisibilityListener, mainExecutor)
         dragToDesktopTransitionHandler.setDragToDesktopStateListener(dragToDesktopStateListener)
         recentsTransitionHandler.addTransitionStateListener(
             object : RecentsTransitionStateListener {
                 override fun onAnimationStateChanged(running: Boolean) {
-                    ProtoLog.v(
-                        WM_SHELL_DESKTOP_MODE,
-                        "DesktopTasksController: recents animation state changed running=%b",
-                        running
-                    )
+                    logV("Recents animation state changed running=%b", running)
                     recentsAnimationRunning = running
                 }
             }
@@ -221,7 +223,7 @@
     /** Returns the transition type for the given remote transition. */
     private fun transitionType(remoteTransition: RemoteTransition?): Int {
         if (remoteTransition == null) {
-            ProtoLog.v(WM_SHELL_DESKTOP_MODE, "DesktopTasksController: remoteTransition is null")
+            logV("RemoteTransition is null")
             return TRANSIT_NONE
         }
         return TRANSIT_TO_FRONT
@@ -229,7 +231,7 @@
 
     /** Show all tasks, that are part of the desktop, on top of launcher */
     fun showDesktopApps(displayId: Int, remoteTransition: RemoteTransition? = null) {
-        ProtoLog.v(WM_SHELL_DESKTOP_MODE, "DesktopTasksController: showDesktopApps")
+        logV("showDesktopApps")
         val wct = WindowContainerTransaction()
         bringDesktopAppsToFront(displayId, wct)
 
@@ -249,114 +251,97 @@
 
     /** Gets number of visible tasks in [displayId]. */
     fun visibleTaskCount(displayId: Int): Int =
-        desktopModeTaskRepository.visibleTaskCount(displayId)
+        taskRepository.getVisibleTaskCount(displayId)
 
     /** Returns true if any tasks are visible in Desktop Mode. */
     fun isDesktopModeShowing(displayId: Int): Boolean = visibleTaskCount(displayId) > 0
 
-    /** Enter desktop by using the focused task in given `displayId` */
+    /** Moves focused task to desktop mode for given [displayId]. */
     fun moveFocusedTaskToDesktop(displayId: Int, transitionSource: DesktopModeTransitionSource) {
-        val allFocusedTasks =
-            shellTaskOrganizer.getRunningTasks(displayId).filter { taskInfo ->
-                taskInfo.isFocused &&
-                    (taskInfo.windowingMode == WINDOWING_MODE_FULLSCREEN ||
-                        taskInfo.windowingMode == WINDOWING_MODE_MULTI_WINDOW) &&
-                    taskInfo.activityType != ACTIVITY_TYPE_HOME
-            }
-        if (allFocusedTasks.isNotEmpty()) {
-            when (allFocusedTasks.size) {
-                2 -> {
-                    // Split-screen case where there are two focused tasks, then we find the child
-                    // task to move to desktop.
-                    val splitFocusedTask =
-                        if (allFocusedTasks[0].taskId == allFocusedTasks[1].parentTaskId) {
-                            allFocusedTasks[1]
-                        } else {
-                            allFocusedTasks[0]
-                        }
-                    moveToDesktop(splitFocusedTask, transitionSource = transitionSource)
-                }
-                1 -> {
-                    // Fullscreen case where we move the current focused task.
-                    moveToDesktop(allFocusedTasks[0].taskId, transitionSource = transitionSource)
-                }
-                else -> {
-                    ProtoLog.w(
-                        WM_SHELL_DESKTOP_MODE,
-                        "DesktopTasksController: Cannot enter desktop, expected less " +
-                            "than 3 focused tasks but found %d",
-                        allFocusedTasks.size
-                    )
-                }
-            }
+        val allFocusedTasks = getAllFocusedTasks(displayId)
+        when (allFocusedTasks.size) {
+            0 -> return
+            // Full screen case
+            1 -> moveRunningTaskToDesktop(
+                allFocusedTasks.single(), transitionSource = transitionSource)
+            // Split-screen case where there are two focused tasks, then we find the child
+            // task to move to desktop.
+            2 -> moveRunningTaskToDesktop(
+                getSplitFocusedTask(allFocusedTasks[0], allFocusedTasks[1]),
+                    transitionSource = transitionSource)
+            else -> logW(
+                "DesktopTasksController: Cannot enter desktop, expected less " +
+                "than 3 focused tasks but found %d", allFocusedTasks.size)
         }
     }
 
-    /** Move a task with given `taskId` to desktop */
-    fun moveToDesktop(
+    /**
+     * Returns all focused tasks in full screen or split screen mode in [displayId] when
+     * it is not the home activity.
+     */
+    private fun getAllFocusedTasks(displayId: Int): List<RunningTaskInfo> =
+        shellTaskOrganizer.getRunningTasks(displayId).filter {
+            it.isFocused &&
+            (it.windowingMode == WINDOWING_MODE_FULLSCREEN ||
+                it.windowingMode == WINDOWING_MODE_MULTI_WINDOW) &&
+            it.activityType != ACTIVITY_TYPE_HOME
+        }
+
+    /** Returns child task from two focused tasks in split screen mode. */
+    private fun getSplitFocusedTask(task1: RunningTaskInfo, task2: RunningTaskInfo) =
+        if (task1.taskId == task2.parentTaskId) task2 else task1
+
+    /** Moves task to desktop mode if task is running, else launches it in desktop mode. */
+    fun moveTaskToDesktop(
         taskId: Int,
         wct: WindowContainerTransaction = WindowContainerTransaction(),
         transitionSource: DesktopModeTransitionSource,
     ): Boolean {
-        shellTaskOrganizer.getRunningTaskInfo(taskId)?.let {
-            moveToDesktop(it, wct, transitionSource)
+        val runningTask = shellTaskOrganizer.getRunningTaskInfo(taskId)
+        if (runningTask == null) {
+            return moveBackgroundTaskToDesktop(taskId, wct, transitionSource)
         }
-            ?: moveToDesktopFromNonRunningTask(taskId, wct, transitionSource)
+        moveRunningTaskToDesktop(runningTask, wct, transitionSource)
         return true
     }
 
-    private fun moveToDesktopFromNonRunningTask(
-        taskId: Int,
-        wct: WindowContainerTransaction,
-        transitionSource: DesktopModeTransitionSource,
+    private fun moveBackgroundTaskToDesktop(
+            taskId: Int,
+            wct: WindowContainerTransaction,
+            transitionSource: DesktopModeTransitionSource,
     ): Boolean {
-        recentTasksController?.findTaskInBackground(taskId)?.let {
-            ProtoLog.v(
-                WM_SHELL_DESKTOP_MODE,
-                "DesktopTasksController: moveToDesktopFromNonRunningTask taskId=%d",
-                taskId
-            )
-            // TODO(342378842): Instead of using default display, support multiple displays
-            val taskToMinimize =
-                bringDesktopAppsToFrontBeforeShowingNewTask(DEFAULT_DISPLAY, wct, taskId)
-            addMoveToDesktopChangesNonRunningTask(wct, taskId)
-            // TODO(343149901): Add DPI changes for task launch
-            val transition = enterDesktopTaskTransitionHandler.moveToDesktop(wct, transitionSource)
-            addPendingMinimizeTransition(transition, taskToMinimize)
-            return true
+        if (recentTasksController?.findTaskInBackground(taskId) == null) {
+            logW("moveBackgroundTaskToDesktop taskId=%d not found", taskId)
+            return false
         }
-            ?: return false
+        logV("moveBackgroundTaskToDesktop with taskId=%d, displayId=%d", taskId)
+        // TODO(342378842): Instead of using default display, support multiple displays
+        val taskToMinimize = bringDesktopAppsToFrontBeforeShowingNewTask(
+            DEFAULT_DISPLAY, wct, taskId)
+        wct.startTask(
+            taskId,
+            ActivityOptions.makeBasic().apply {
+                launchWindowingMode = WINDOWING_MODE_FREEFORM
+            }.toBundle(),
+        )
+        // TODO(343149901): Add DPI changes for task launch
+        val transition = enterDesktopTaskTransitionHandler.moveToDesktop(wct, transitionSource)
+        addPendingMinimizeTransition(transition, taskToMinimize)
+        return true
     }
 
-    private fun addMoveToDesktopChangesNonRunningTask(
-        wct: WindowContainerTransaction,
-        taskId: Int
-    ) {
-        val options = ActivityOptions.makeBasic()
-        options.launchWindowingMode = WINDOWING_MODE_FREEFORM
-        wct.startTask(taskId, options.toBundle())
-    }
-
-    /** Move a task to desktop */
-    fun moveToDesktop(
+   /** Moves a running task to desktop. */
+    fun moveRunningTaskToDesktop(
         task: RunningTaskInfo,
         wct: WindowContainerTransaction = WindowContainerTransaction(),
         transitionSource: DesktopModeTransitionSource,
     ) {
-        if (Flags.enableDesktopWindowingModalsPolicy()
+        if (DesktopModeFlags.MODALS_POLICY.isEnabled(context)
             && isTopActivityExemptFromDesktopWindowing(context, task)) {
-            ProtoLog.w(
-                WM_SHELL_DESKTOP_MODE,
-                "DesktopTasksController: Cannot enter desktop, " +
-                        "ineligible top activity found."
-            )
+            logW("Cannot enter desktop for taskId %d, ineligible top activity found", task.taskId)
             return
         }
-        ProtoLog.v(
-            WM_SHELL_DESKTOP_MODE,
-            "DesktopTasksController: moveToDesktop taskId=%d",
-            task.taskId
-        )
+        logV("moveRunningTaskToDesktop taskId=%d", task.taskId)
         exitSplitIfApplicable(wct, task)
         // Bring other apps to front first
         val taskToMinimize =
@@ -378,12 +363,11 @@
     fun startDragToDesktop(
         taskInfo: RunningTaskInfo,
         dragToDesktopValueAnimator: MoveToDesktopAnimator,
+        taskSurface: SurfaceControl,
     ) {
-        ProtoLog.v(
-            WM_SHELL_DESKTOP_MODE,
-            "DesktopTasksController: startDragToDesktop taskId=%d",
-            taskInfo.taskId
-        )
+        logV("startDragToDesktop taskId=%d", taskInfo.taskId)
+        interactionJankMonitor.begin(taskSurface, context,
+            CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_HOLD)
         dragToDesktopTransitionHandler.startDragToDesktopTransition(
             taskInfo.taskId,
             dragToDesktopValueAnimator
@@ -394,7 +378,7 @@
      * The second part of the animated drag to desktop transition, called after
      * [startDragToDesktop].
      */
-    private fun finalizeDragToDesktop(taskInfo: RunningTaskInfo, freeformBounds: Rect) {
+    private fun finalizeDragToDesktop(taskInfo: RunningTaskInfo) {
         ProtoLog.v(
             WM_SHELL_DESKTOP_MODE,
             "DesktopTasksController: finalizeDragToDesktop taskId=%d",
@@ -406,7 +390,6 @@
         val taskToMinimize =
             bringDesktopAppsToFrontBeforeShowingNewTask(taskInfo.displayId, wct, taskInfo.taskId)
         addMoveToDesktopChanges(wct, taskInfo)
-        wct.setBounds(taskInfo.token, freeformBounds)
         val transition = dragToDesktopTransitionHandler.finishDragToDesktopTransition(wct)
         transition?.let { addPendingMinimizeTransition(it, taskToMinimize) }
     }
@@ -434,17 +417,10 @@
      * @param taskId task id of the window that's being closed
      */
     fun onDesktopWindowClose(wct: WindowContainerTransaction, displayId: Int, taskId: Int) {
-        if (desktopModeTaskRepository.isOnlyVisibleNonClosingTask(taskId)) {
+        if (taskRepository.isOnlyVisibleNonClosingTask(taskId)) {
             removeWallpaperActivity(wct)
         }
-        if (!desktopModeTaskRepository.addClosingTask(displayId, taskId)) {
-            // Could happen if the task hasn't been removed from closing list after it disappeared
-            ProtoLog.w(
-                WM_SHELL_DESKTOP_MODE,
-                "DesktopTasksController: the task with taskId=%d is already closing!",
-                taskId
-            )
-        }
+        taskRepository.addClosingTask(displayId, taskId)
     }
 
     /** Move a task with given `taskId` to fullscreen */
@@ -463,11 +439,7 @@
 
     /** Move a desktop app to split screen. */
     fun moveToSplit(task: RunningTaskInfo) {
-        ProtoLog.v(
-            WM_SHELL_DESKTOP_MODE,
-            "DesktopTasksController: moveToSplit taskId=%d",
-            task.taskId
-        )
+        logV( "moveToSplit taskId=%s", task.taskId)
         val wct = WindowContainerTransaction()
         wct.setBounds(task.token, Rect())
         // Rather than set windowing mode to multi-window at task level, set it to
@@ -496,11 +468,7 @@
      * [startDragToDesktop].
      */
     fun cancelDragToDesktop(task: RunningTaskInfo) {
-        ProtoLog.v(
-            WM_SHELL_DESKTOP_MODE,
-            "DesktopTasksController: cancelDragToDesktop taskId=%d",
-            task.taskId
-        )
+        logV("cancelDragToDesktop taskId=%d", task.taskId)
         dragToDesktopTransitionHandler.cancelDragToDesktopTransition(
             DragToDesktopTransitionHandler.CancelState.STANDARD_CANCEL
         )
@@ -511,11 +479,7 @@
         position: Point,
         transitionSource: DesktopModeTransitionSource
     ) {
-        ProtoLog.v(
-            WM_SHELL_DESKTOP_MODE,
-            "DesktopTasksController: moveToFullscreen with animation taskId=%d",
-            task.taskId
-        )
+        logV("moveToFullscreenWithAnimation taskId=%d", task.taskId)
         val wct = WindowContainerTransaction()
         addMoveToFullscreenChanges(wct, task)
 
@@ -539,12 +503,7 @@
 
     /** Move a task to the front */
     fun moveTaskToFront(taskInfo: RunningTaskInfo) {
-        ProtoLog.v(
-            WM_SHELL_DESKTOP_MODE,
-            "DesktopTasksController: moveTaskToFront taskId=%d",
-            taskInfo.taskId
-        )
-
+        logV("moveTaskToFront taskId=%s", taskInfo.taskId)
         val wct = WindowContainerTransaction()
         wct.reorder(taskInfo.token, true)
         val taskToMinimize = addAndGetMinimizeChangesIfNeeded(taskInfo.displayId, wct, taskInfo)
@@ -570,15 +529,10 @@
     fun moveToNextDisplay(taskId: Int) {
         val task = shellTaskOrganizer.getRunningTaskInfo(taskId)
         if (task == null) {
-            ProtoLog.w(WM_SHELL_DESKTOP_MODE, "moveToNextDisplay: taskId=%d not found", taskId)
+            logW("moveToNextDisplay: taskId=%d not found", taskId)
             return
         }
-        ProtoLog.v(
-            WM_SHELL_DESKTOP_MODE,
-            "moveToNextDisplay: taskId=%d taskDisplayId=%d",
-            taskId,
-            task.displayId
-        )
+        logV("moveToNextDisplay: taskId=%d displayId=%d", taskId, task.displayId)
 
         val displayIds = rootTaskDisplayAreaOrganizer.displayIds.sorted()
         // Get the first display id that is higher than current task display id
@@ -588,7 +542,7 @@
             newDisplayId = displayIds.firstOrNull { displayId -> displayId < task.displayId }
         }
         if (newDisplayId == null) {
-            ProtoLog.w(WM_SHELL_DESKTOP_MODE, "moveToNextDisplay: next display not found")
+            logW("moveToNextDisplay: next display not found")
             return
         }
         moveToDisplay(task, newDisplayId)
@@ -600,21 +554,15 @@
      * No-op if task is already on that display per [RunningTaskInfo.displayId].
      */
     private fun moveToDisplay(task: RunningTaskInfo, displayId: Int) {
-        ProtoLog.v(
-            WM_SHELL_DESKTOP_MODE,
-            "moveToDisplay: taskId=%d displayId=%d",
-            task.taskId,
-            displayId
-        )
-
+        logV("moveToDisplay: taskId=%d displayId=%d", task.taskId, displayId)
         if (task.displayId == displayId) {
-            ProtoLog.d(WM_SHELL_DESKTOP_MODE, "moveToDisplay: task already on display")
+            logD("moveToDisplay: task already on display %d", displayId)
             return
         }
 
         val displayAreaInfo = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(displayId)
         if (displayAreaInfo == null) {
-            ProtoLog.w(WM_SHELL_DESKTOP_MODE, "moveToDisplay: display not found")
+            logW("moveToDisplay: display not found")
             return
         }
 
@@ -651,11 +599,11 @@
             // If the task's pre-maximize stable bounds were saved, toggle the task to those bounds.
             // Otherwise, toggle to the default bounds.
             val taskBoundsBeforeMaximize =
-                desktopModeTaskRepository.removeBoundsBeforeMaximize(taskInfo.taskId)
+                taskRepository.removeBoundsBeforeMaximize(taskInfo.taskId)
             if (taskBoundsBeforeMaximize != null) {
                 destinationBounds.set(taskBoundsBeforeMaximize)
             } else {
-                if (Flags.enableWindowingDynamicInitialBounds()) {
+                if (DesktopModeFlags.DYNAMIC_INITIAL_BOUNDS.isEnabled(context)) {
                     destinationBounds.set(calculateInitialBounds(displayLayout, taskInfo))
                 } else {
                     destinationBounds.set(getDefaultDesktopTaskBounds(displayLayout))
@@ -664,7 +612,7 @@
         } else {
             // Save current bounds so that task can be restored back to original bounds if necessary
             // and toggle to the stable bounds.
-            desktopModeTaskRepository.saveBoundsBeforeMaximize(taskInfo.taskId, currentTaskBounds)
+            taskRepository.saveBoundsBeforeMaximize(taskInfo.taskId, currentTaskBounds)
 
             if (taskInfo.isResizeable) {
                 // if resizable then expand to entire stable bounds (full display minus insets)
@@ -691,16 +639,23 @@
     /**
      * Quick-resize to the right or left half of the stable bounds.
      *
+     * @param taskInfo current task that is being snap-resized via dragging or maximize menu button
+     * @param currentDragBounds current position of the task leash being dragged (or current task
+     *                          bounds if being snapped resize via maximize menu button)
      * @param position the portion of the screen (RIGHT or LEFT) we want to snap the task to.
      */
-    fun snapToHalfScreen(taskInfo: RunningTaskInfo, position: SnapPosition) {
+    fun snapToHalfScreen(
+        taskInfo: RunningTaskInfo,
+        currentDragBounds: Rect,
+        position: SnapPosition
+    ) {
         val destinationBounds = getSnapBounds(taskInfo, position)
 
         if (destinationBounds == taskInfo.configuration.windowConfiguration.bounds) return
 
         val wct = WindowContainerTransaction().setBounds(taskInfo.token, destinationBounds)
         if (Transitions.ENABLE_SHELL_TRANSITIONS) {
-            toggleResizeDesktopTaskTransitionHandler.startTransition(wct)
+            toggleResizeDesktopTaskTransitionHandler.startTransition(wct, currentDragBounds)
         } else {
             shellTaskOrganizer.applyTransaction(wct)
         }
@@ -769,23 +724,18 @@
         wct: WindowContainerTransaction,
         newTaskIdInFront: Int? = null
     ): RunningTaskInfo? {
-        ProtoLog.v(
-            WM_SHELL_DESKTOP_MODE,
-            "DesktopTasksController: bringDesktopAppsToFront, newTaskIdInFront=%s",
-            newTaskIdInFront ?: "null"
-        )
-
+        logV("bringDesktopAppsToFront, newTaskId=%d", newTaskIdInFront)
         // Move home to front, ensures that we go back home when all desktop windows are closed
         moveHomeTask(wct, toTop = true)
 
         // Currently, we only handle the desktop on the default display really.
-        if (displayId == DEFAULT_DISPLAY && Flags.enableDesktopWindowingWallpaperActivity()) {
+        if (displayId == DEFAULT_DISPLAY && WALLPAPER_ACTIVITY.isEnabled(context)) {
             // Add translucent wallpaper activity to show the wallpaper underneath
             addWallpaperActivity(wct)
         }
 
         val nonMinimizedTasksOrderedFrontToBack =
-            desktopModeTaskRepository.getActiveNonMinimizedTasksOrderedFrontToBack(displayId)
+            taskRepository.getActiveNonMinimizedOrderedTasks(displayId)
         // If we're adding a new Task we might need to minimize an old one
         val taskToMinimize: RunningTaskInfo? =
             if (newTaskIdInFront != null && desktopTasksLimiter.isPresent) {
@@ -803,7 +753,7 @@
             .filter { taskId -> taskId != taskToMinimize?.taskId }
             .mapNotNull { taskId -> shellTaskOrganizer.getRunningTaskInfo(taskId) }
             .reversed() // Start from the back so the front task is brought forward last
-            .forEach { task -> wct.reorder(task.token, true /* onTop */) }
+            .forEach { task -> wct.reorder(task.token, /* onTop= */ true) }
         return taskToMinimize
     }
 
@@ -811,11 +761,11 @@
         shellTaskOrganizer
             .getRunningTasks(context.displayId)
             .firstOrNull { task -> task.activityType == ACTIVITY_TYPE_HOME }
-            ?.let { homeTask -> wct.reorder(homeTask.getToken(), toTop /* onTop */) }
+            ?.let { homeTask -> wct.reorder(homeTask.getToken(), /* onTop= */ toTop) }
     }
 
     private fun addWallpaperActivity(wct: WindowContainerTransaction) {
-        ProtoLog.v(WM_SHELL_DESKTOP_MODE, "DesktopTasksController: addWallpaper")
+        logV("addWallpaperActivity")
         val intent = Intent(context, DesktopWallpaperActivity::class.java)
         val options =
             ActivityOptions.makeBasic().apply {
@@ -833,8 +783,8 @@
     }
 
     private fun removeWallpaperActivity(wct: WindowContainerTransaction) {
-        desktopModeTaskRepository.wallpaperActivityToken?.let { token ->
-            ProtoLog.v(WM_SHELL_DESKTOP_MODE, "DesktopTasksController: removeWallpaper")
+        taskRepository.wallpaperActivityToken?.let { token ->
+            logV("removeWallpaperActivity")
             wct.removeTask(token)
         }
     }
@@ -872,16 +822,16 @@
         transition: IBinder,
         request: TransitionRequestInfo
     ): WindowContainerTransaction? {
-        ProtoLog.v(
-            WM_SHELL_DESKTOP_MODE,
-            "DesktopTasksController: handleRequest request=%s",
-            request
-        )
+        logV("handleRequest request=%s", request)
         // Check if we should skip handling this transition
         var reason = ""
         val triggerTask = request.triggerTask
+        var shouldHandleMidRecentsFreeformLaunch =
+            recentsAnimationRunning && isFreeformRelaunch(triggerTask, request)
         val shouldHandleRequest =
             when {
+                // Handle freeform relaunch during recents animation
+                shouldHandleMidRecentsFreeformLaunch -> true
                 recentsAnimationRunning -> {
                     reason = "recents animation is running"
                     false
@@ -914,17 +864,15 @@
             }
 
         if (!shouldHandleRequest) {
-            ProtoLog.v(
-                WM_SHELL_DESKTOP_MODE,
-                "DesktopTasksController: skipping handleRequest reason=%s",
-                reason
-            )
+            logV("skipping handleRequest reason=%s", reason)
             return null
         }
 
         val result =
             triggerTask?.let { task ->
                 when {
+                    // Check if freeform task launch during recents should be handled
+                    shouldHandleMidRecentsFreeformLaunch -> handleMidRecentsFreeformTaskLaunch(task)
                     // Check if the closing task needs to be handled
                     TransitionUtil.isClosingType(request.type) -> handleTaskClosing(task)
                     // Check if the top task shouldn't be allowed to enter desktop mode
@@ -938,11 +886,7 @@
                     }
                 }
             }
-        ProtoLog.v(
-            WM_SHELL_DESKTOP_MODE,
-            "DesktopTasksController: handleRequest result=%s",
-            result ?: "null"
-        )
+        logV("handleRequest result=%s", result)
         return result
     }
 
@@ -962,39 +906,109 @@
             .forEach { finishTransaction.setCornerRadius(it.leash, cornerRadius) }
     }
 
+    /** Returns whether an existing desktop task is being relaunched in freeform or not. */
+    private fun isFreeformRelaunch(triggerTask: RunningTaskInfo?, request: TransitionRequestInfo) =
+        (triggerTask != null && triggerTask.windowingMode == WINDOWING_MODE_FREEFORM
+                && TransitionUtil.isOpeningType(request.type)
+                && taskRepository.isActiveTask(triggerTask.taskId))
+
     private fun isIncompatibleTask(task: TaskInfo) =
-        Flags.enableDesktopWindowingModalsPolicy()
+        DesktopModeFlags.MODALS_POLICY.isEnabled(context)
                 && isTopActivityExemptFromDesktopWindowing(context, task)
 
     private fun shouldHandleTaskClosing(request: TransitionRequestInfo): Boolean {
-        return Flags.enableDesktopWindowingWallpaperActivity() &&
+        return WALLPAPER_ACTIVITY.isEnabled(context) &&
             TransitionUtil.isClosingType(request.type) &&
             request.triggerTask != null
     }
 
+    fun openNewWindow(
+        taskInfo: RunningTaskInfo
+    ) {
+        // TODO(b/337915660): Add a transition handler for these; animations
+        //  need updates in some cases.
+        val newTaskWindowingMode = when {
+            taskInfo.isFreeform -> {
+                WINDOWING_MODE_FREEFORM
+            }
+            taskInfo.isFullscreen || taskInfo.isMultiWindow -> {
+                WINDOWING_MODE_MULTI_WINDOW
+            }
+            else -> {
+                error("Invalid windowing mode: ${taskInfo.windowingMode}")
+            }
+        }
+
+        val baseActivity = taskInfo.baseActivity ?: return
+        val fillIn: Intent = context.packageManager
+            .getLaunchIntentForPackage(
+                baseActivity.packageName
+            ) ?: return
+        fillIn
+            .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_MULTIPLE_TASK)
+        val options =
+            ActivityOptions.makeBasic().apply {
+                launchWindowingMode = newTaskWindowingMode
+                isPendingIntentBackgroundActivityLaunchAllowedByPermission = true
+                pendingIntentBackgroundActivityStartMode =
+                    ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED
+            }
+        val launchIntent = PendingIntent.getActivity(
+            context,
+            /* requestCode= */ 0,
+            fillIn,
+            PendingIntent.FLAG_IMMUTABLE
+        )
+        when (newTaskWindowingMode) {
+            WINDOWING_MODE_MULTI_WINDOW -> {
+                val splitPosition = splitScreenController.determineNewInstancePosition(taskInfo)
+                splitScreenController.startIntent(
+                    launchIntent, context.userId, fillIn, splitPosition,
+                    options.toBundle(), null /* hideTaskToken */
+                )
+            }
+            WINDOWING_MODE_FREEFORM -> {
+                // TODO(b/336289597): This currently does not respect the desktop window limit.
+                val wct = WindowContainerTransaction()
+                wct.sendPendingIntent(launchIntent, fillIn, options.toBundle())
+                transitions.startTransition(TRANSIT_OPEN, wct, null)
+            }
+        }
+    }
+
+    /**
+     * Handles the case where a freeform task is launched from recents.
+     *
+     * This is a special case where we want to launch the task in fullscreen instead of freeform.
+     */
+    private fun handleMidRecentsFreeformTaskLaunch(
+        task: RunningTaskInfo
+    ): WindowContainerTransaction? {
+        logV("DesktopTasksController: handleMidRecentsFreeformTaskLaunch")
+        val wct = WindowContainerTransaction()
+        addMoveToFullscreenChanges(wct, task)
+        wct.reorder(task.token, true)
+        return wct
+    }
+
     private fun handleFreeformTaskLaunch(
         task: RunningTaskInfo,
         transition: IBinder
     ): WindowContainerTransaction? {
-        ProtoLog.v(WM_SHELL_DESKTOP_MODE, "DesktopTasksController: handleFreeformTaskLaunch")
+        logV("handleFreeformTaskLaunch")
         if (keyguardManager.isKeyguardLocked) {
             // Do NOT handle freeform task launch when locked.
             // It will be launched in fullscreen windowing mode (Details: b/160925539)
-            ProtoLog.v(WM_SHELL_DESKTOP_MODE, "DesktopTasksController: skip keyguard is locked")
+            logV("skip keyguard is locked")
             return null
         }
         val wct = WindowContainerTransaction()
         if (!isDesktopModeShowing(task.displayId)) {
-            ProtoLog.d(
-                WM_SHELL_DESKTOP_MODE,
-                "DesktopTasksController: bring desktop tasks to front on transition" +
-                    " taskId=%d",
-                task.taskId
-            )
+            logD("Bring desktop tasks to front on transition=taskId=%d", task.taskId)
             // We are outside of desktop mode and already existing desktop task is being launched.
             // We should make this task go to fullscreen instead of freeform. Note that this means
             // any re-launch of a freeform window outside of desktop will be in fullscreen.
-            if (desktopModeTaskRepository.isActiveTask(task.taskId)) {
+            if (taskRepository.isActiveTask(task.taskId)) {
                 addMoveToFullscreenChanges(wct, task)
                 return wct
             }
@@ -1019,14 +1033,9 @@
         task: RunningTaskInfo,
         transition: IBinder
     ): WindowContainerTransaction? {
-        ProtoLog.v(WM_SHELL_DESKTOP_MODE, "DesktopTasksController: handleFullscreenTaskLaunch")
+        logV("handleFullscreenTaskLaunch")
         if (isDesktopModeShowing(task.displayId)) {
-            ProtoLog.d(
-                WM_SHELL_DESKTOP_MODE,
-                "DesktopTasksController: switch fullscreen task to freeform on transition" +
-                    " taskId=%d",
-                task.taskId
-            )
+            logD("Switch fullscreen task to freeform on transition: taskId=%d", task.taskId)
             return WindowContainerTransaction().also { wct ->
                 addMoveToDesktopChanges(wct, task)
                 // In some launches home task is moved behind new task being launched. Make sure
@@ -1053,33 +1062,28 @@
 
     /** Handle task closing by removing wallpaper activity if it's the last active task */
     private fun handleTaskClosing(task: RunningTaskInfo): WindowContainerTransaction? {
-        ProtoLog.v(WM_SHELL_DESKTOP_MODE, "DesktopTasksController: handleTaskClosing")
+        logV("handleTaskClosing")
         val wct = WindowContainerTransaction()
-        if (desktopModeTaskRepository.isOnlyVisibleNonClosingTask(task.taskId)
-            && desktopModeTaskRepository.wallpaperActivityToken != null) {
+        if (taskRepository.isOnlyVisibleNonClosingTask(task.taskId)
+            && taskRepository.wallpaperActivityToken != null) {
             // Remove wallpaper activity when the last active task is removed
             removeWallpaperActivity(wct)
         }
-        if (!desktopModeTaskRepository.addClosingTask(task.displayId, task.taskId)) {
-            // Could happen if the task hasn't been removed from closing list after it disappeared
-            ProtoLog.w(
-                WM_SHELL_DESKTOP_MODE,
-                "DesktopTasksController: the task with taskId=%d is already closing!",
-                task.taskId
-            )
-        }
+        taskRepository.addClosingTask(task.displayId, task.taskId)
         // If a CLOSE or TO_BACK is triggered on a desktop task, remove the task.
-        if (Flags.enableDesktopWindowingBackNavigation() &&
-            desktopModeTaskRepository.isVisibleTask(task.taskId)) {
+        if (DesktopModeFlags.BACK_NAVIGATION.isEnabled(context) &&
+            taskRepository.isVisibleTask(task.taskId)) {
             wct.removeTask(task.token)
         }
         return if (wct.isEmpty) null else wct
     }
 
-    private fun addMoveToDesktopChanges(
+    @VisibleForTesting
+    fun addMoveToDesktopChanges(
         wct: WindowContainerTransaction,
         taskInfo: RunningTaskInfo
     ) {
+        val displayLayout = displayController.getDisplayLayout(taskInfo.displayId) ?: return
         val tdaInfo = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(taskInfo.displayId)!!
         val tdaWindowingMode = tdaInfo.configuration.windowConfiguration.windowingMode
         val targetWindowingMode =
@@ -1089,6 +1093,28 @@
             } else {
                 WINDOWING_MODE_FREEFORM
             }
+        val initialBounds = if (DesktopModeFlags.DYNAMIC_INITIAL_BOUNDS.isEnabled(context)) {
+            calculateInitialBounds(displayLayout, taskInfo)
+        } else {
+            getDefaultDesktopTaskBounds(displayLayout)
+        }
+
+        if (DesktopModeFlags.CASCADING_WINDOWS.isEnabled(context)) {
+            val stableBounds = Rect()
+            displayLayout.getStableBoundsForDesktopMode(stableBounds)
+
+            val activeTasks = taskRepository
+                .getActiveNonMinimizedOrderedTasks(taskInfo.displayId)
+            activeTasks.firstOrNull()?.let { activeTask ->
+                shellTaskOrganizer.getRunningTaskInfo(activeTask)?.let {
+                    cascadeWindow(context.resources, stableBounds,
+                        it.configuration.windowConfiguration.bounds, initialBounds)
+                }
+            }
+        }
+        if (canChangeTaskPosition(taskInfo)) {
+            wct.setBounds(taskInfo.token, initialBounds)
+        }
         wct.setWindowingMode(taskInfo.token, targetWindowingMode)
         wct.reorder(taskInfo.token, true /* onTop */)
         if (useDesktopOverrideDensity()) {
@@ -1114,7 +1140,7 @@
         if (useDesktopOverrideDensity()) {
             wct.setDensityDpi(taskInfo.token, getDefaultDensityDpi())
         }
-        if (desktopModeTaskRepository.isOnlyVisibleNonClosingTask(taskInfo.taskId)) {
+        if (taskRepository.isOnlyVisibleNonClosingTask(taskInfo.taskId)) {
             // Remove wallpaper activity when leaving desktop mode
             removeWallpaperActivity(wct)
         }
@@ -1133,7 +1159,7 @@
         // The task's density may have been overridden in freeform; revert it here as we don't
         // want it overridden in multi-window.
         wct.setDensityDpi(taskInfo.token, getDefaultDensityDpi())
-        if (desktopModeTaskRepository.isOnlyVisibleNonClosingTask(taskInfo.taskId)) {
+        if (taskRepository.isOnlyVisibleNonClosingTask(taskInfo.taskId)) {
             // Remove wallpaper activity when leaving desktop mode
             removeWallpaperActivity(wct)
         }
@@ -1247,7 +1273,7 @@
         taskSurface: SurfaceControl,
         inputX: Float,
         taskTop: Float
-    ): DesktopModeVisualIndicator.IndicatorType {
+    ): IndicatorType {
         // If the visual indicator does not exist, create it.
         val indicator =
             visualIndicator
@@ -1270,13 +1296,15 @@
      * @param taskInfo the task being dragged.
      * @param position position of surface when drag ends.
      * @param inputCoordinate the coordinates of the motion event
-     * @param taskBounds the updated bounds of the task being dragged.
+     * @param currentDragBounds the current bounds of where the visible task is (might be actual
+     *                          task bounds or just task leash)
+     * @param validDragArea the bounds of where the task can be dragged within the display.
      */
     fun onDragPositioningEnd(
         taskInfo: RunningTaskInfo,
         position: Point,
         inputCoordinate: PointF,
-        taskBounds: Rect,
+        currentDragBounds: Rect,
         validDragArea: Rect
     ) {
         if (taskInfo.configuration.windowConfiguration.windowingMode != WINDOWING_MODE_FREEFORM) {
@@ -1286,41 +1314,40 @@
         val indicator = visualIndicator ?: return
         val indicatorType =
             indicator.updateIndicatorType(
-                PointF(inputCoordinate.x, taskBounds.top.toFloat()),
+                PointF(inputCoordinate.x, currentDragBounds.top.toFloat()),
                 taskInfo.windowingMode
             )
         when (indicatorType) {
-            DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR -> {
+            IndicatorType.TO_FULLSCREEN_INDICATOR -> {
                 moveToFullscreenWithAnimation(
                     taskInfo,
                     position,
                     DesktopModeTransitionSource.TASK_DRAG
                 )
             }
-            DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_LEFT_INDICATOR -> {
+            IndicatorType.TO_SPLIT_LEFT_INDICATOR -> {
                 releaseVisualIndicator()
-                snapToHalfScreen(taskInfo, SnapPosition.LEFT)
+                snapToHalfScreen(taskInfo, currentDragBounds, SnapPosition.LEFT)
             }
-            DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_RIGHT_INDICATOR -> {
+            IndicatorType.TO_SPLIT_RIGHT_INDICATOR -> {
                 releaseVisualIndicator()
-                snapToHalfScreen(taskInfo, SnapPosition.RIGHT)
+                snapToHalfScreen(taskInfo, currentDragBounds, SnapPosition.RIGHT)
             }
-            DesktopModeVisualIndicator.IndicatorType.NO_INDICATOR -> {
-                // If task bounds are outside valid drag area, snap them inward and perform a
-                // transaction to set bounds.
-                if (
-                    DragPositioningCallbackUtility.snapTaskBoundsIfNecessary(
-                        taskBounds,
-                        validDragArea
-                    )
-                ) {
-                    val wct = WindowContainerTransaction()
-                    wct.setBounds(taskInfo.token, taskBounds)
-                    transitions.startTransition(TRANSIT_CHANGE, wct, null)
-                }
+            IndicatorType.NO_INDICATOR -> {
+                // If task bounds are outside valid drag area, snap them inward
+                DragPositioningCallbackUtility.snapTaskBoundsIfNecessary(
+                    currentDragBounds,
+                    validDragArea
+                )
+
+                // Update task bounds so that the task position will match the position of its leash
+                val wct = WindowContainerTransaction()
+                wct.setBounds(taskInfo.token, currentDragBounds)
+                transitions.startTransition(TRANSIT_CHANGE, wct, null)
+
                 releaseVisualIndicator()
             }
-            DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR -> {
+            IndicatorType.TO_DESKTOP_INDICATOR -> {
                 throw IllegalArgumentException(
                     "Should not be receiving TO_DESKTOP_INDICATOR for " + "a freeform task."
                 )
@@ -1340,18 +1367,18 @@
     fun onDragPositioningEndThroughStatusBar(
         inputCoordinates: PointF,
         taskInfo: RunningTaskInfo,
+        taskSurface: SurfaceControl,
     ): IndicatorType {
+        // End the drag_hold CUJ interaction.
+        interactionJankMonitor.end(CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_HOLD)
         val indicator = getVisualIndicator() ?: return IndicatorType.NO_INDICATOR
         val indicatorType = indicator.updateIndicatorType(inputCoordinates, taskInfo.windowingMode)
         when (indicatorType) {
             IndicatorType.TO_DESKTOP_INDICATOR -> {
-                val displayLayout = displayController.getDisplayLayout(taskInfo.displayId)
-                    ?: return IndicatorType.NO_INDICATOR
-                if (Flags.enableWindowingDynamicInitialBounds()) {
-                    finalizeDragToDesktop(taskInfo, calculateInitialBounds(displayLayout, taskInfo))
-                } else {
-                    finalizeDragToDesktop(taskInfo, getDefaultDesktopTaskBounds(displayLayout))
-                }
+                // Start a new jank interaction for the drag release to desktop window animation.
+                interactionJankMonitor.begin(taskSurface, context,
+                    CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_RELEASE, "to_desktop")
+                finalizeDragToDesktop(taskInfo)
             }
             IndicatorType.NO_INDICATOR,
             IndicatorType.TO_FULLSCREEN_INDICATOR -> {
@@ -1369,12 +1396,12 @@
 
     /** Update the exclusion region for a specified task */
     fun onExclusionRegionChanged(taskId: Int, exclusionRegion: Region) {
-        desktopModeTaskRepository.updateTaskExclusionRegions(taskId, exclusionRegion)
+        taskRepository.updateTaskExclusionRegions(taskId, exclusionRegion)
     }
 
     /** Remove a previously tracked exclusion region for a specified task. */
     fun removeExclusionRegionForTask(taskId: Int) {
-        desktopModeTaskRepository.removeExclusionRegion(taskId)
+        taskRepository.removeExclusionRegion(taskId)
     }
 
     /**
@@ -1384,7 +1411,7 @@
      * @param callbackExecutor the executor to call the listener on.
      */
     fun addVisibleTasksListener(listener: VisibleTasksListener, callbackExecutor: Executor) {
-        desktopModeTaskRepository.addVisibleTasksListener(listener, callbackExecutor)
+        taskRepository.addVisibleTasksListener(listener, callbackExecutor)
     }
 
     /**
@@ -1394,7 +1421,7 @@
      * @param callbackExecutor the executor to call the listener on.
      */
     fun setTaskRegionListener(listener: Consumer<Region>, callbackExecutor: Executor) {
-        desktopModeTaskRepository.setExclusionRegionListener(listener, callbackExecutor)
+        taskRepository.setExclusionRegionListener(listener, callbackExecutor)
     }
 
     override fun onUnhandledDrag(
@@ -1412,7 +1439,7 @@
         if (!multiInstanceHelper.supportsMultiInstanceSplit(launchComponent)) {
             // TODO(b/320797628): Should only return early if there is an existing running task, and
             //                    notify the user as well. But for now, just ignore the drop.
-            ProtoLog.v(WM_SHELL_DESKTOP_MODE, "Dropped intent does not support multi-instance")
+            logV("Dropped intent does not support multi-instance")
             return false
         }
 
@@ -1444,7 +1471,7 @@
     private fun dump(pw: PrintWriter, prefix: String) {
         val innerPrefix = "$prefix  "
         pw.println("${prefix}DesktopTasksController")
-        desktopModeTaskRepository.dump(pw, innerPrefix)
+        taskRepository.dump(pw, innerPrefix)
     }
 
     /** The interface for calls from outside the shell, within the host process. */
@@ -1519,12 +1546,12 @@
                 SingleInstanceRemoteListener<DesktopTasksController, IDesktopTaskListener>(
                     controller,
                     { c ->
-                        c.desktopModeTaskRepository.addVisibleTasksListener(
+                        c.taskRepository.addVisibleTasksListener(
                             listener,
                             c.mainExecutor
                         )
                     },
-                    { c -> c.desktopModeTaskRepository.removeVisibleTasksListener(listener) }
+                    { c -> c.taskRepository.removeVisibleTasksListener(listener) }
                 )
         }
 
@@ -1551,10 +1578,8 @@
         }
 
         override fun hideStashedDesktopApps(displayId: Int) {
-            ProtoLog.w(
-                WM_SHELL_DESKTOP_MODE,
-                "IDesktopModeImpl: hideStashedDesktopApps is deprecated"
-            )
+            ProtoLog.w(WM_SHELL_DESKTOP_MODE,
+                "IDesktopModeImpl: hideStashedDesktopApps is deprecated")
         }
 
         override fun getVisibleTaskCount(displayId: Int): Int {
@@ -1578,27 +1603,35 @@
         }
 
         override fun setTaskListener(listener: IDesktopTaskListener?) {
-            ProtoLog.v(
-                WM_SHELL_DESKTOP_MODE,
-                "IDesktopModeImpl: set task listener=%s",
-                listener ?: "null"
-            )
+            ProtoLog.v(WM_SHELL_DESKTOP_MODE, "IDesktopModeImpl: set task listener=%s", listener)
             executeRemoteCallWithTaskPermission(controller, "setTaskListener") { _ ->
                 listener?.let { remoteListener.register(it) } ?: remoteListener.unregister()
             }
         }
 
         override fun moveToDesktop(taskId: Int, transitionSource: DesktopModeTransitionSource) {
-            executeRemoteCallWithTaskPermission(controller, "moveToDesktop") { c ->
-                c.moveToDesktop(taskId, transitionSource = transitionSource)
+            executeRemoteCallWithTaskPermission(controller, "moveTaskToDesktop") { c ->
+                c.moveTaskToDesktop(taskId, transitionSource = transitionSource)
             }
         }
     }
 
+    private fun logV(msg: String, vararg arguments: Any?) {
+        ProtoLog.v(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments)
+    }
+    private fun logD(msg: String, vararg arguments: Any?) {
+        ProtoLog.d(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments)
+    }
+    private fun logW(msg: String, vararg arguments: Any?) {
+        ProtoLog.w(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments)
+    }
+
     companion object {
         @JvmField
         val DESKTOP_MODE_INITIAL_BOUNDS_SCALE =
             SystemProperties.getInt("persist.wm.debug.desktop_mode_initial_bounds_scale", 75) / 100f
+
+        private const val TAG = "DesktopTasksController"
     }
 
     /** The positions on a screen that a task can snap to. */
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 534cc22..38675129 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
@@ -17,16 +17,18 @@
 package com.android.wm.shell.desktopmode
 
 import android.app.ActivityManager.RunningTaskInfo
+import android.content.Context
 import android.os.IBinder
 import android.view.SurfaceControl
 import android.view.WindowManager.TRANSIT_TO_BACK
 import android.window.TransitionInfo
 import android.window.WindowContainerTransaction
 import androidx.annotation.VisibleForTesting
+import com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_MINIMIZE_WINDOW
+import com.android.internal.jank.InteractionJankMonitor
 import com.android.internal.protolog.ProtoLog
 import com.android.wm.shell.ShellTaskOrganizer
 import com.android.wm.shell.protolog.ShellProtoLogGroup
-import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
 import com.android.wm.shell.transition.Transitions
 import com.android.wm.shell.transition.Transitions.TransitionObserver
 
@@ -34,39 +36,56 @@
  * Limits the number of tasks shown in Desktop Mode.
  *
  * This class should only be used if
- * [com.android.window.flags.Flags.enableDesktopWindowingTaskLimit()] is true.
+ * [com.android.wm.shell.shared.desktopmode.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_TASK_LIMIT]
+ * is enabled and [maxTasksLimit] is strictly greater than 0.
  */
 class DesktopTasksLimiter (
         transitions: Transitions,
         private val taskRepository: DesktopModeTaskRepository,
         private val shellTaskOrganizer: ShellTaskOrganizer,
+        private val maxTasksLimit: Int,
+        private val interactionJankMonitor: InteractionJankMonitor,
+        private val context: Context
 ) {
     private val minimizeTransitionObserver = MinimizeTransitionObserver()
     @VisibleForTesting
     val leftoverMinimizedTasksRemover = LeftoverMinimizedTasksRemover()
 
     init {
+        require(maxTasksLimit > 0) {
+            "DesktopTasksLimiter should not be created with a maxTasksLimit at 0 or less. " +
+                    "Current value: $maxTasksLimit."
+        }
         transitions.registerObserver(minimizeTransitionObserver)
         taskRepository.addActiveTaskListener(leftoverMinimizedTasksRemover)
+        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
+            "DesktopTasksLimiter: starting limiter with a maximum of %d tasks", maxTasksLimit)
     }
 
-    private data class TaskDetails (val displayId: Int, val taskId: Int)
+    private data class TaskDetails(
+        val displayId: Int,
+        val taskId: Int,
+        var transitionInfo: TransitionInfo?
+    )
 
     // TODO(b/333018485): replace this observer when implementing the minimize-animation
     private inner class MinimizeTransitionObserver : TransitionObserver {
         private val mPendingTransitionTokensAndTasks = mutableMapOf<IBinder, TaskDetails>()
+        private val mActiveTransitionTokensAndTasks = mutableMapOf<IBinder, TaskDetails>()
 
         fun addPendingTransitionToken(transition: IBinder, taskDetails: TaskDetails) {
             mPendingTransitionTokensAndTasks[transition] = taskDetails
         }
 
         override fun onTransitionReady(
-                transition: IBinder,
-                info: TransitionInfo,
-                startTransaction: SurfaceControl.Transaction,
-                finishTransaction: SurfaceControl.Transaction
+            transition: IBinder,
+            info: TransitionInfo,
+            startTransaction: SurfaceControl.Transaction,
+            finishTransaction: SurfaceControl.Transaction
         ) {
             val taskToMinimize = mPendingTransitionTokensAndTasks.remove(transition) ?: return
+            taskToMinimize.transitionInfo = info
+            mActiveTransitionTokensAndTasks[transition] = taskToMinimize
 
             if (!taskRepository.isActiveTask(taskToMinimize.taskId)) return
 
@@ -77,15 +96,16 @@
                         taskToMinimize.taskId)
                 return
             }
+
             this@DesktopTasksLimiter.markTaskMinimized(
                     taskToMinimize.displayId, taskToMinimize.taskId)
         }
 
         /**
-         * Returns whether the given Task is being reordered to the back in the given transition, or
-         * is already invisible.
+         * Returns whether the Task [taskDetails] is being reordered to the back in the transition
+         * [info], or is already invisible.
          *
-         * <p> This check can be used to double-check that a task was indeed minimized before
+         * This check can be used to double-check that a task was indeed minimized before
          * marking it as such.
          */
         private fun isTaskReorderedToBackOrInvisible(
@@ -100,9 +120,21 @@
             return taskChange.mode == TRANSIT_TO_BACK
         }
 
-        override fun onTransitionStarting(transition: IBinder) {}
+        override fun onTransitionStarting(transition: IBinder) {
+            val mActiveTaskDetails = mActiveTransitionTokensAndTasks[transition]
+            if (mActiveTaskDetails != null && mActiveTaskDetails.transitionInfo != null) {
+                // Begin minimize window CUJ instrumentation.
+                interactionJankMonitor.begin(
+                    mActiveTaskDetails.transitionInfo?.rootLeash, context,
+                    CUJ_DESKTOP_MODE_MINIMIZE_WINDOW
+                )
+            }
+        }
 
         override fun onTransitionMerged(merged: IBinder, playing: IBinder) {
+            if (mActiveTransitionTokensAndTasks.remove(merged) != null) {
+                interactionJankMonitor.end(CUJ_DESKTOP_MODE_MINIMIZE_WINDOW)
+            }
             mPendingTransitionTokensAndTasks.remove(merged)?.let { taskToTransfer ->
                 mPendingTransitionTokensAndTasks[playing] = taskToTransfer
             }
@@ -112,6 +144,13 @@
             ProtoLog.v(
                     ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
                     "DesktopTasksLimiter: transition %s finished", transition)
+            if (mActiveTransitionTokensAndTasks.remove(transition) != null) {
+                if (aborted) {
+                    interactionJankMonitor.cancel(CUJ_DESKTOP_MODE_MINIMIZE_WINDOW)
+                } else {
+                    interactionJankMonitor.end(CUJ_DESKTOP_MODE_MINIMIZE_WINDOW)
+                }
+            }
             mPendingTransitionTokensAndTasks.remove(transition)
         }
     }
@@ -125,8 +164,7 @@
         }
 
         fun removeLeftoverMinimizedTasks(displayId: Int, wct: WindowContainerTransaction) {
-            if (taskRepository
-                .getActiveNonMinimizedTasksOrderedFrontToBack(displayId).isNotEmpty()) {
+            if (taskRepository.getActiveNonMinimizedOrderedTasks(displayId).isNotEmpty()) {
                 return
             }
             val remainingMinimizedTasks = taskRepository.getMinimizedTasks(displayId)
@@ -135,7 +173,9 @@
             }
             ProtoLog.v(
                 ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
-                "DesktopTasksLimiter: removing leftover minimized tasks: $remainingMinimizedTasks")
+                "DesktopTasksLimiter: removing leftover minimized tasks: %s",
+                remainingMinimizedTasks,
+            )
             remainingMinimizedTasks.forEach { taskIdToRemove ->
                 val taskToRemove = shellTaskOrganizer.getRunningTaskInfo(taskIdToRemove)
                 if (taskToRemove != null) {
@@ -146,8 +186,8 @@
     }
 
     /**
-     * Mark a task as minimized, this should only be done after the corresponding transition has
-     * finished so we don't minimize the task if the transition fails.
+     * Mark [taskId], which must be on [displayId], as minimized, this should only be done after the
+     * corresponding transition has finished so we don't minimize the task if the transition fails.
      */
     private fun markTaskMinimized(displayId: Int, taskId: Int) {
         ProtoLog.v(
@@ -158,11 +198,9 @@
 
     /**
      * Add a minimize-transition to [wct] if adding [newFrontTaskInfo] brings us over the task
-     * limit.
+     * limit, returning the task to minimize.
      *
-     * @param transition the transition that the minimize-transition will be appended to, or null if
-     * the transition will be started later.
-     * @return the ID of the minimized task, or null if no task is being minimized.
+     * The task must be on [displayId].
      */
     fun addAndGetMinimizeTaskChangesIfNeeded(
             displayId: Int,
@@ -174,7 +212,7 @@
                 "DesktopTasksLimiter: addMinimizeBackTaskChangesIfNeeded, newFrontTask=%d",
                 newFrontTaskInfo.taskId)
         val newTaskListOrderedFrontToBack = createOrderedTaskListWithGivenTaskInFront(
-                taskRepository.getActiveNonMinimizedTasksOrderedFrontToBack(displayId),
+                taskRepository.getActiveNonMinimizedOrderedTasks(displayId),
                 newFrontTaskInfo.taskId)
         val taskToMinimize = getTaskToMinimizeIfNeeded(newTaskListOrderedFrontToBack)
         if (taskToMinimize != null) {
@@ -190,16 +228,10 @@
      */
     fun addPendingMinimizeChange(transition: IBinder, displayId: Int, taskId: Int) {
         minimizeTransitionObserver.addPendingTransitionToken(
-                transition, TaskDetails(displayId, taskId))
+                transition, TaskDetails(displayId, taskId, transitionInfo = null))
     }
 
     /**
-     * Returns the maximum number of tasks that should ever be displayed at the same time in Desktop
-     * Mode.
-     */
-    fun getMaxTaskLimit(): Int = DesktopModeStatus.getMaxTaskLimit()
-
-    /**
      * Returns the Task to minimize given 1. a list of visible tasks ordered from front to back and
      * 2. a new task placed in front of all the others.
      */
@@ -216,20 +248,22 @@
     fun getTaskToMinimizeIfNeeded(
             visibleFreeformTaskIdsOrderedFrontToBack: List<Int>
     ): RunningTaskInfo? {
-        if (visibleFreeformTaskIdsOrderedFrontToBack.size <= getMaxTaskLimit()) {
+        if (visibleFreeformTaskIdsOrderedFrontToBack.size <= maxTasksLimit) {
             ProtoLog.v(
                     ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
                     "DesktopTasksLimiter: no need to minimize; tasks below limit")
             // No need to minimize anything
             return null
         }
+        val taskIdToMinimize = visibleFreeformTaskIdsOrderedFrontToBack.last()
         val taskToMinimize =
-                shellTaskOrganizer.getRunningTaskInfo(
-                        visibleFreeformTaskIdsOrderedFrontToBack.last())
+                shellTaskOrganizer.getRunningTaskInfo(taskIdToMinimize)
         if (taskToMinimize == null) {
             ProtoLog.e(
                     ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
-                    "DesktopTasksLimiter: taskToMinimize == null")
+                    "DesktopTasksLimiter: taskToMinimize(taskId = %d) == null",
+                    taskIdToMinimize,
+                )
             return null
         }
         return taskToMinimize
@@ -244,7 +278,5 @@
     }
 
     @VisibleForTesting
-    fun getTransitionObserver(): TransitionObserver {
-        return minimizeTransitionObserver
-    }
+    fun getTransitionObserver(): TransitionObserver = minimizeTransitionObserver
 }
\ No newline at end of file
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 246fd92..74e53fa 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
@@ -23,9 +23,9 @@
 import android.window.TransitionInfo
 import android.window.WindowContainerTransaction
 import com.android.internal.protolog.ProtoLog
-import com.android.window.flags.Flags.enableDesktopWindowingWallpaperActivity
 import com.android.wm.shell.ShellTaskOrganizer
 import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
+import com.android.wm.shell.shared.desktopmode.DesktopModeFlags.WALLPAPER_ACTIVITY
 import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
 import com.android.wm.shell.sysui.ShellInit
 import com.android.wm.shell.transition.Transitions
@@ -36,7 +36,7 @@
  * mode and other transitions that originate both within and outside shell.
  */
 class DesktopTasksTransitionObserver(
-    context: Context,
+    private val context: Context,
     private val desktopModeTaskRepository: DesktopModeTaskRepository,
     private val transitions: Transitions,
     private val shellTaskOrganizer: ShellTaskOrganizer,
@@ -79,7 +79,7 @@
     }
 
     private fun updateWallpaperToken(info: TransitionInfo) {
-        if (!enableDesktopWindowingWallpaperActivity()) {
+        if (!WALLPAPER_ACTIVITY.isEnabled(context)) {
             return
         }
         info.changes.forEach { change ->
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 ddee8fa..5221a45 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
@@ -29,6 +29,9 @@
 import android.window.TransitionRequestInfo
 import android.window.WindowContainerToken
 import android.window.WindowContainerTransaction
+import com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_HOLD
+import com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_RELEASE
+import com.android.internal.jank.InteractionJankMonitor
 import com.android.internal.protolog.ProtoLog
 import com.android.wm.shell.RootTaskDisplayAreaOrganizer
 import com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT
@@ -57,17 +60,20 @@
     private val context: Context,
     private val transitions: Transitions,
     private val taskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer,
-    private val transactionSupplier: Supplier<SurfaceControl.Transaction>
+    private val interactionJankMonitor: InteractionJankMonitor,
+    private val transactionSupplier: Supplier<SurfaceControl.Transaction>,
 ) : TransitionHandler {
 
     constructor(
         context: Context,
         transitions: Transitions,
-        rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer
+        rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer,
+        interactionJankMonitor: InteractionJankMonitor
     ) : this(
         context,
         transitions,
         rootTaskDisplayAreaOrganizer,
+        interactionJankMonitor,
         Supplier { SurfaceControl.Transaction() }
     )
 
@@ -214,16 +220,18 @@
             startCancelAnimation()
         } else if (
             state.draggedTaskChange != null &&
-            (cancelState == CancelState.CANCEL_SPLIT_LEFT ||
+                (cancelState == CancelState.CANCEL_SPLIT_LEFT ||
                     cancelState == CancelState.CANCEL_SPLIT_RIGHT)
-            ) {
+        ) {
             // We have a valid dragged task, but the animation will be handled by
             // SplitScreenController; request the transition here.
-            @SplitPosition val splitPosition = if (cancelState == CancelState.CANCEL_SPLIT_LEFT) {
-                SPLIT_POSITION_TOP_OR_LEFT
-            } else {
-                SPLIT_POSITION_BOTTOM_OR_RIGHT
-            }
+            @SplitPosition
+            val splitPosition =
+                if (cancelState == CancelState.CANCEL_SPLIT_LEFT) {
+                    SPLIT_POSITION_TOP_OR_LEFT
+                } else {
+                    SPLIT_POSITION_BOTTOM_OR_RIGHT
+                }
             val wct = WindowContainerTransaction()
             restoreWindowOrder(wct, state)
             state.startTransitionFinishTransaction?.apply()
@@ -246,20 +254,20 @@
         wct: WindowContainerTransaction
     ) {
         val state = requireTransitionState()
-        val taskInfo = state.draggedTaskChange?.taskInfo
-            ?: error("Expected non-null taskInfo")
+        val taskInfo = state.draggedTaskChange?.taskInfo ?: error("Expected non-null taskInfo")
         val taskBounds = Rect(taskInfo.configuration.windowConfiguration.bounds)
         val taskScale = state.dragAnimator.scale
         val scaledWidth = taskBounds.width() * taskScale
         val scaledHeight = taskBounds.height() * taskScale
         val dragPosition = PointF(state.dragAnimator.position)
         state.dragAnimator.cancelAnimator()
-        val animatedTaskBounds = Rect(
-            dragPosition.x.toInt(),
-            dragPosition.y.toInt(),
-            (dragPosition.x + scaledWidth).toInt(),
-            (dragPosition.y + scaledHeight).toInt()
-        )
+        val animatedTaskBounds =
+            Rect(
+                dragPosition.x.toInt(),
+                dragPosition.y.toInt(),
+                (dragPosition.x + scaledWidth).toInt(),
+                (dragPosition.y + scaledHeight).toInt()
+            )
         requestSplitSelect(wct, taskInfo, splitPosition, animatedTaskBounds)
     }
 
@@ -280,12 +288,7 @@
         }
         wct.setWindowingMode(taskInfo.token, WINDOWING_MODE_MULTI_WINDOW)
         wct.setDensityDpi(taskInfo.token, context.resources.displayMetrics.densityDpi)
-        splitScreenController.requestEnterSplitSelect(
-            taskInfo,
-            wct,
-            splitPosition,
-            taskBounds
-        )
+        splitScreenController.requestEnterSplitSelect(taskInfo, wct, splitPosition, taskBounds)
     }
 
     override fun startAnimation(
@@ -384,7 +387,7 @@
                 // occurred.
                 if (
                     change.taskInfo?.taskId == state.draggedTaskId &&
-                    state.cancelState != CancelState.STANDARD_CANCEL
+                        state.cancelState != CancelState.STANDARD_CANCEL
                 ) {
                     // We need access to the dragged task's change in both non-cancel and split
                     // cancel cases.
@@ -392,8 +395,8 @@
                 }
                 if (
                     change.taskInfo?.taskId == state.draggedTaskId &&
-                    state.cancelState == CancelState.NO_CANCEL
-                    ) {
+                        state.cancelState == CancelState.NO_CANCEL
+                ) {
                     taskDisplayAreaOrganizer.reparentToDisplayArea(
                         change.endDisplayId,
                         change.leash,
@@ -426,19 +429,20 @@
             startCancelDragToDesktopTransition()
         } else if (
             state.cancelState == CancelState.CANCEL_SPLIT_LEFT ||
-            state.cancelState == CancelState.CANCEL_SPLIT_RIGHT
-            ){
+                state.cancelState == CancelState.CANCEL_SPLIT_RIGHT
+        ) {
             // Cancel-early case for split-cancel. The state was flagged already as a cancel for
             // requesting split select. Similar to the above, this can happen due to quick fling
             // gestures. We can simply request split here without needing to calculate animated
             // task bounds as the task has not shrunk at all.
-            val splitPosition = if (state.cancelState == CancelState.CANCEL_SPLIT_LEFT) {
-                SPLIT_POSITION_TOP_OR_LEFT
-            } else {
-                SPLIT_POSITION_BOTTOM_OR_RIGHT
-            }
-            val taskInfo = state.draggedTaskChange?.taskInfo
-                ?: error("Expected non-null task info.")
+            val splitPosition =
+                if (state.cancelState == CancelState.CANCEL_SPLIT_LEFT) {
+                    SPLIT_POSITION_TOP_OR_LEFT
+                } else {
+                    SPLIT_POSITION_BOTTOM_OR_RIGHT
+                }
+            val taskInfo =
+                state.draggedTaskChange?.taskInfo ?: error("Expected non-null task info.")
             val wct = WindowContainerTransaction()
             restoreWindowOrder(wct)
             state.startTransitionFinishTransaction?.apply()
@@ -457,8 +461,10 @@
     ) {
         val state = requireTransitionState()
         // We don't want to merge the split select animation if that's what we requested.
-        if (state.cancelState == CancelState.CANCEL_SPLIT_LEFT ||
-            state.cancelState == CancelState.CANCEL_SPLIT_RIGHT) {
+        if (
+            state.cancelState == CancelState.CANCEL_SPLIT_LEFT ||
+                state.cancelState == CancelState.CANCEL_SPLIT_RIGHT
+        ) {
             clearState()
             return
         }
@@ -567,6 +573,9 @@
                                 onTaskResizeAnimationListener.onAnimationEnd(state.draggedTaskId)
                                 startTransitionFinishCb.onTransitionFinished(null /* null */)
                                 clearState()
+                                interactionJankMonitor.end(
+                                    CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_RELEASE
+                                )
                             }
                         }
                     )
@@ -604,6 +613,10 @@
                 "DragToDesktop: onTransitionConsumed() start transition aborted"
             )
             state.startAborted = true
+            // Cancel CUJ interaction if the transition is aborted.
+            interactionJankMonitor.cancel(CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_HOLD)
+        } else if (state.cancelTransitionToken != transition) {
+            interactionJankMonitor.cancel(CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_RELEASE)
         }
     }
 
@@ -661,9 +674,7 @@
         val wct = WindowContainerTransaction()
         restoreWindowOrder(wct, state)
         state.cancelTransitionToken =
-            transitions.startTransition(
-                TRANSIT_DESKTOP_MODE_CANCEL_DRAG_TO_DESKTOP, wct, this
-            )
+            transitions.startTransition(TRANSIT_DESKTOP_MODE_CANCEL_DRAG_TO_DESKTOP, wct, this)
     }
 
     private fun restoreWindowOrder(
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/OWNERS
index 7ad68aa..b01b2b7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/OWNERS
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/OWNERS
@@ -2,7 +2,6 @@
 atsjenk@google.com
 jorgegil@google.com
 madym@google.com
-nmusgrave@google.com
 pbdr@google.com
 tkachenkoi@google.com
 vaniadesmonda@google.com
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ToggleResizeDesktopTaskTransitionHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ToggleResizeDesktopTaskTransitionHandler.kt
index c35d77a..bf185a4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ToggleResizeDesktopTaskTransitionHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ToggleResizeDesktopTaskTransitionHandler.kt
@@ -45,15 +45,24 @@
     private lateinit var onTaskResizeAnimationListener: OnTaskResizeAnimationListener
 
     private var boundsAnimator: Animator? = null
+    private var initialBounds: Rect? = null
 
     constructor(
         transitions: Transitions,
         interactionJankMonitor: InteractionJankMonitor
     ) : this(transitions, Supplier { SurfaceControl.Transaction() }, interactionJankMonitor)
 
-    /** Starts a quick resize transition. */
-    fun startTransition(wct: WindowContainerTransaction) {
+    /**
+     * Starts a quick resize transition.
+     *
+     *  @param wct WindowContainerTransaction that will update core about the task changes applied
+     *  @param taskLeashBounds current bounds of the task leash (Note: not guaranteed to be the
+     *                         bounds of the actual task). This is provided so that the animation
+     *                         resizing can begin where the task leash currently is for smoother UX.
+     */
+    fun startTransition(wct: WindowContainerTransaction, taskLeashBounds: Rect? = null) {
         transitions.startTransition(TRANSIT_DESKTOP_MODE_TOGGLE_RESIZE, wct, this)
+        initialBounds = taskLeashBounds
     }
 
     fun setOnTaskResizeAnimationListener(listener: OnTaskResizeAnimationListener) {
@@ -70,7 +79,7 @@
         val change = findRelevantChange(info)
         val leash = change.leash
         val taskId = checkNotNull(change.taskInfo).taskId
-        val startBounds = change.startAbsBounds
+        val startBounds = initialBounds ?: change.startAbsBounds
         val endBounds = change.endAbsBounds
 
         val tx = transactionSupplier.get()
@@ -92,7 +101,7 @@
                             onTaskResizeAnimationListener.onAnimationStart(
                                 taskId,
                                 startTransaction,
-                                startBounds
+                                startBounds,
                             )
                         },
                         onEnd = {
@@ -106,6 +115,7 @@
                                 .show(leash)
                             onTaskResizeAnimationListener.onAnimationEnd(taskId)
                             finishCallback.onTransitionFinished(null)
+                            initialBounds = null
                             boundsAnimator = null
                             interactionJankMonitor.end(
                                 Cuj.CUJ_DESKTOP_MODE_MAXIMIZE_WINDOW)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java
index e4aa115..d03a561 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java
@@ -21,11 +21,11 @@
 import static android.content.pm.ActivityInfo.CONFIG_ASSETS_PATHS;
 import static android.content.pm.ActivityInfo.CONFIG_UI_MODE;
 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME;
 import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION;
 
 import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
 import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
-import static com.android.wm.shell.common.split.SplitScreenUtils.getResizingBackgroundColor;
 import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_BOTTOM;
 import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_LEFT;
 import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_RIGHT;
@@ -40,6 +40,7 @@
 import android.content.Context;
 import android.content.res.Configuration;
 import android.content.res.Resources;
+import android.graphics.Color;
 import android.graphics.Insets;
 import android.graphics.Point;
 import android.graphics.Rect;
@@ -290,7 +291,7 @@
                 final int activityType = taskInfo1.getActivityType();
                 if (activityType == ACTIVITY_TYPE_STANDARD) {
                     Drawable icon1 = mIconProvider.getIcon(taskInfo1.topActivityInfo);
-                    int bgColor1 = getResizingBackgroundColor(taskInfo1).toArgb();
+                    int bgColor1 = getResizingBackgroundColor(taskInfo1);
                     mDropZoneView1.setAppInfo(bgColor1, icon1);
                     mDropZoneView2.setAppInfo(bgColor1, icon1);
                     mDropZoneView1.setForceIgnoreBottomMargin(false);
@@ -312,10 +313,10 @@
                     mSplitScreenController.getTaskInfo(SPLIT_POSITION_BOTTOM_OR_RIGHT);
             if (topOrLeftTask != null && bottomOrRightTask != null) {
                 Drawable topOrLeftIcon = mIconProvider.getIcon(topOrLeftTask.topActivityInfo);
-                int topOrLeftColor = getResizingBackgroundColor(topOrLeftTask).toArgb();
+                int topOrLeftColor = getResizingBackgroundColor(topOrLeftTask);
                 Drawable bottomOrRightIcon = mIconProvider.getIcon(
                         bottomOrRightTask.topActivityInfo);
-                int bottomOrRightColor = getResizingBackgroundColor(bottomOrRightTask).toArgb();
+                int bottomOrRightColor = getResizingBackgroundColor(bottomOrRightTask);
                 mDropZoneView1.setAppInfo(topOrLeftColor, topOrLeftIcon);
                 mDropZoneView2.setAppInfo(bottomOrRightColor, bottomOrRightIcon);
             }
@@ -586,6 +587,11 @@
         }
     }
 
+    private static int getResizingBackgroundColor(ActivityManager.RunningTaskInfo taskInfo) {
+        final int taskBgColor = taskInfo.taskDescription.getBackgroundColor();
+        return Color.valueOf(taskBgColor == -1 ? Color.WHITE : taskBgColor).toArgb();
+    }
+
     /**
      * Dumps information about this drag layout.
      */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
index 4531967..456767a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
@@ -99,14 +99,10 @@
         if (DesktopModeStatus.canEnterDesktopMode(mContext)) {
             mDesktopModeTaskRepository.ifPresent(repository -> {
                 repository.addOrMoveFreeformTaskToTop(taskInfo.displayId, taskInfo.taskId);
-                repository.unminimizeTask(taskInfo.displayId, taskInfo.taskId);
                 if (taskInfo.isVisible) {
-                    if (repository.addActiveTask(taskInfo.displayId, taskInfo.taskId)) {
-                        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
-                                "Adding active freeform task: #%d", taskInfo.taskId);
-                    }
-                    repository.updateVisibleFreeformTasks(taskInfo.displayId, taskInfo.taskId,
-                            true);
+                    repository.addActiveTask(taskInfo.displayId, taskInfo.taskId);
+                    repository.updateTaskVisibility(taskInfo.displayId, taskInfo.taskId,
+                        /* visible= */ true);
                 }
             });
         }
@@ -121,12 +117,6 @@
         if (DesktopModeStatus.canEnterDesktopMode(mContext)) {
             mDesktopModeTaskRepository.ifPresent(repository -> {
                 repository.removeFreeformTask(taskInfo.displayId, taskInfo.taskId);
-                repository.unminimizeTask(taskInfo.displayId, taskInfo.taskId);
-                if (repository.removeActiveTask(taskInfo.taskId)) {
-                    ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
-                            "Removing active freeform task: #%d", taskInfo.taskId);
-                }
-                repository.updateVisibleFreeformTasks(taskInfo.displayId, taskInfo.taskId, false);
             });
         }
         mWindowDecorationViewModel.onTaskVanished(taskInfo);
@@ -146,16 +136,11 @@
         if (DesktopModeStatus.canEnterDesktopMode(mContext)) {
             mDesktopModeTaskRepository.ifPresent(repository -> {
                 if (taskInfo.isVisible) {
-                    if (repository.addActiveTask(taskInfo.displayId, taskInfo.taskId)) {
-                        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
-                                "Adding active freeform task: #%d", taskInfo.taskId);
-                    }
-                } else if (repository.isClosingTask(taskInfo.taskId)
-                        && repository.removeClosingTask(taskInfo.taskId)) {
-                    ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
-                            "Removing closing freeform task: #%d", taskInfo.taskId);
+                    repository.addActiveTask(taskInfo.displayId, taskInfo.taskId);
+                } else if (repository.isClosingTask(taskInfo.taskId)) {
+                    repository.removeClosingTask(taskInfo.taskId);
                 }
-                repository.updateVisibleFreeformTasks(taskInfo.displayId, taskInfo.taskId,
+                repository.updateTaskVisibility(taskInfo.displayId, taskInfo.taskId,
                         taskInfo.isVisible);
             });
         }
@@ -172,7 +157,6 @@
         if (DesktopModeStatus.canEnterDesktopMode(mContext) && taskInfo.isFocused) {
             mDesktopModeTaskRepository.ifPresent(repository -> {
                 repository.addOrMoveFreeformTaskToTop(taskInfo.displayId, taskInfo.taskId);
-                repository.unminimizeTask(taskInfo.displayId, taskInfo.taskId);
             });
         }
     }
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 9bb9d86..ba97c832 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
@@ -21,6 +21,7 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.util.RotationUtils.deltaRotation;
 import static android.util.RotationUtils.rotateBounds;
+import static android.view.Surface.ROTATION_0;
 import static android.view.Surface.ROTATION_270;
 import static android.view.Surface.ROTATION_90;
 import static android.view.WindowManager.TRANSIT_CHANGE;
@@ -1020,6 +1021,9 @@
             mPipMenuController.attach(leash);
         }
 
+        // Make sure we have the launcher shelf into destination bounds calculation
+        // before the animator starts.
+        mPipBoundsState.mayUseCachedLauncherShelfHeight();
         final Rect destinationBounds = mPipBoundsAlgorithm.getEntryDestinationBounds();
         final Rect currentBounds = pipChange.getStartAbsBounds();
 
@@ -1180,10 +1184,15 @@
                 ? pipTaskInfo.configuration.windowConfiguration.getBounds()
                 : mPipOrganizer.mAppBounds;
 
+        // Populate the final surface control transactions from PipTransitionAnimator,
+        // display cutout insets is handled in the swipe pip to home animator, empty it out here
+        // to avoid flicker.
+        final Rect savedDisplayCutoutInsets = new Rect(pipTaskInfo.displayCutoutInsets);
+        pipTaskInfo.displayCutoutInsets.setEmpty();
         final PipAnimationController.PipTransitionAnimator animator =
                 mPipAnimationController.getAnimator(pipTaskInfo, leash, sourceBounds, sourceBounds,
                         destinationBounds, sourceHintRect, TRANSITION_DIRECTION_TO_PIP,
-                        0 /* startingAngle */, 0 /* rotationDelta */)
+                        0 /* startingAngle */, ROTATION_0 /* rotationDelta */)
                         .setPipTransactionHandler(mTransactionConsumer)
                         .setTransitionDirection(TRANSITION_DIRECTION_TO_PIP);
         // The start state is the end state for swipe-auto-pip.
@@ -1191,6 +1200,7 @@
         animator.applySurfaceControlTransaction(leash, startTransaction,
                 PipAnimationController.FRACTION_END);
         startTransaction.apply();
+        pipTaskInfo.displayCutoutInsets.set(savedDisplayCutoutInsets);
 
         mPipBoundsState.setBounds(destinationBounds);
         final SurfaceControl.Transaction tx = new SurfaceControl.Transaction();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index 0cb7e17..284620e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -272,6 +272,7 @@
         final boolean changed = onDisplayRotationChanged(mContext, outBounds, currentBounds,
                 mTmpInsetBounds, displayId, fromRotation, toRotation, t);
         if (changed) {
+            mMenuController.hideMenu();
             // If the pip was in the offset zone earlier, adjust the new bounds to the bottom of the
             // movement bounds
             mTouchHandler.adjustBoundsForRotation(outBounds, mPipBoundsState.getBounds(),
@@ -646,9 +647,9 @@
                 });
 
         mTabletopModeController.registerOnTabletopModeChangedListener((isInTabletopMode) -> {
-            final String tag = "tabletop-mode";
             if (!isInTabletopMode) {
-                mPipBoundsState.removeNamedUnrestrictedKeepClearArea(tag);
+                mPipBoundsState.setNamedUnrestrictedKeepClearArea(
+                        PipBoundsState.NAMED_KCA_TABLETOP_MODE, null);
                 return;
             }
 
@@ -657,14 +658,16 @@
             if (mTabletopModeController.getPreferredHalfInTabletopMode()
                     == TabletopModeController.PREFERRED_TABLETOP_HALF_TOP) {
                 // Prefer top, avoid the bottom half of the display.
-                mPipBoundsState.addNamedUnrestrictedKeepClearArea(tag, new Rect(
-                        displayBounds.left, displayBounds.centerY(),
-                        displayBounds.right, displayBounds.bottom));
+                mPipBoundsState.setNamedUnrestrictedKeepClearArea(
+                        PipBoundsState.NAMED_KCA_TABLETOP_MODE, new Rect(
+                                displayBounds.left, displayBounds.centerY(),
+                                displayBounds.right, displayBounds.bottom));
             } else {
                 // Prefer bottom, avoid the top half of the display.
-                mPipBoundsState.addNamedUnrestrictedKeepClearArea(tag, new Rect(
-                        displayBounds.left, displayBounds.top,
-                        displayBounds.right, displayBounds.centerY()));
+                mPipBoundsState.setNamedUnrestrictedKeepClearArea(
+                        PipBoundsState.NAMED_KCA_TABLETOP_MODE, new Rect(
+                                displayBounds.left, displayBounds.top,
+                                displayBounds.right, displayBounds.centerY()));
             }
 
             // Try to move the PiP window if we have entered PiP mode.
@@ -916,10 +919,12 @@
                     0, mPipBoundsState.getDisplayBounds().bottom - height,
                     mPipBoundsState.getDisplayBounds().right,
                     mPipBoundsState.getDisplayBounds().bottom);
-            mPipBoundsState.addNamedUnrestrictedKeepClearArea(LAUNCHER_KEEP_CLEAR_AREA_TAG, rect);
+            mPipBoundsState.setNamedUnrestrictedKeepClearArea(
+                    PipBoundsState.NAMED_KCA_LAUNCHER_SHELF, rect);
             updatePipPositionForKeepClearAreas();
         } else {
-            mPipBoundsState.removeNamedUnrestrictedKeepClearArea(LAUNCHER_KEEP_CLEAR_AREA_TAG);
+            mPipBoundsState.setNamedUnrestrictedKeepClearArea(
+                    PipBoundsState.NAMED_KCA_LAUNCHER_SHELF, null);
             // postpone moving in response to hide of Launcher in case there's another change
             mMainExecutor.removeCallbacks(mMovePipInResponseToKeepClearAreasChangeCallback);
             mMainExecutor.executeDelayed(
@@ -968,8 +973,8 @@
             int launcherRotation, Rect hotseatKeepClearArea) {
         // preemptively add the keep clear area for Hotseat, so that it is taken into account
         // when calculating the entry destination bounds of PiP window
-        mPipBoundsState.addNamedUnrestrictedKeepClearArea(LAUNCHER_KEEP_CLEAR_AREA_TAG,
-                hotseatKeepClearArea);
+        mPipBoundsState.setNamedUnrestrictedKeepClearArea(
+                PipBoundsState.NAMED_KCA_LAUNCHER_SHELF, hotseatKeepClearArea);
         onDisplayRotationChangedNotInPip(mContext, launcherRotation);
         // cache current min/max size
         Point minSize = mPipBoundsState.getMinSize();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
index df3803d..999ab95 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
@@ -416,6 +416,17 @@
         // location now.
         mSpringingToTouch = false;
 
+        // Boost the velocityX if it's zero to forcefully push it towards the nearest edge.
+        // We don't simply change the xEndValue below since the PhysicsAnimator would rely on the
+        // same velocityX to find out which edge to snap to.
+        if (velocityX == 0) {
+            final int motionCenterX = mPipBoundsState
+                    .getMotionBoundsState().getBoundsInMotion().centerX();
+            final int displayCenterX = mPipBoundsState
+                    .getDisplayBounds().centerX();
+            velocityX = (motionCenterX < displayCenterX) ? -0.001f : 0.001f;
+        }
+
         mTemporaryBoundsPhysicsAnimator
                 .spring(FloatProperties.RECT_WIDTH, getBounds().width(), mSpringConfig)
                 .spring(FloatProperties.RECT_HEIGHT, getBounds().height(), mSpringConfig)
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 b939b16..94fe286 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
@@ -44,6 +44,7 @@
 import com.android.wm.shell.common.DisplayInsetsController;
 import com.android.wm.shell.common.DisplayLayout;
 import com.android.wm.shell.common.ExternalInterfaceBinder;
+import com.android.wm.shell.common.ImeListener;
 import com.android.wm.shell.common.RemoteCallable;
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.SingleInstanceRemoteListener;
@@ -56,7 +57,6 @@
 import com.android.wm.shell.common.pip.PipDisplayLayoutState;
 import com.android.wm.shell.common.pip.PipUtils;
 import com.android.wm.shell.pip.Pip;
-import com.android.wm.shell.pip.PipTransitionController;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
 import com.android.wm.shell.sysui.ConfigurationChangeListener;
 import com.android.wm.shell.sysui.ShellCommandHandler;
@@ -88,6 +88,7 @@
     private final TaskStackListenerImpl mTaskStackListener;
     private final ShellTaskOrganizer mShellTaskOrganizer;
     private final PipTransitionState mPipTransitionState;
+    private final PipTouchHandler mPipTouchHandler;
     private final ShellExecutor mMainExecutor;
     private final PipImpl mImpl;
     private Consumer<Boolean> mOnIsInPipStateChangedListener;
@@ -130,6 +131,7 @@
             TaskStackListenerImpl taskStackListener,
             ShellTaskOrganizer shellTaskOrganizer,
             PipTransitionState pipTransitionState,
+            PipTouchHandler pipTouchHandler,
             ShellExecutor mainExecutor) {
         mContext = context;
         mShellCommandHandler = shellCommandHandler;
@@ -144,6 +146,7 @@
         mShellTaskOrganizer = shellTaskOrganizer;
         mPipTransitionState = pipTransitionState;
         mPipTransitionState.addPipTransitionStateChangedListener(this);
+        mPipTouchHandler = pipTouchHandler;
         mMainExecutor = mainExecutor;
         mImpl = new PipImpl();
 
@@ -168,6 +171,7 @@
             TaskStackListenerImpl taskStackListener,
             ShellTaskOrganizer shellTaskOrganizer,
             PipTransitionState pipTransitionState,
+            PipTouchHandler pipTouchHandler,
             ShellExecutor mainExecutor) {
         if (!context.getPackageManager().hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)) {
             ProtoLog.w(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
@@ -177,7 +181,7 @@
         return new PipController(context, shellInit, shellCommandHandler, shellController,
                 displayController, displayInsetsController, pipBoundsState, pipBoundsAlgorithm,
                 pipDisplayLayoutState, pipScheduler, taskStackListener, shellTaskOrganizer,
-                pipTransitionState, mainExecutor);
+                pipTransitionState, pipTouchHandler, mainExecutor);
     }
 
     public PipImpl getPipImpl() {
@@ -201,6 +205,13 @@
                                         .getDisplayLayout(mPipDisplayLayoutState.getDisplayId()));
                     }
                 });
+        mDisplayInsetsController.addInsetsChangedListener(mPipDisplayLayoutState.getDisplayId(),
+                new ImeListener(mDisplayController, mPipDisplayLayoutState.getDisplayId()) {
+                    @Override
+                    public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) {
+                        mPipTouchHandler.onImeVisibilityChanged(imeVisible, imeHeight);
+                    }
+                });
 
         // Allow other outside processes to bind to PiP controller using the key below.
         mShellController.addExternalInterface(KEY_EXTRA_SHELL_PIP,
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 ea02de9d..83253c6 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
@@ -134,6 +134,8 @@
     private final PhysicsAnimator.SpringConfig mConflictResolutionSpringConfig =
             new PhysicsAnimator.SpringConfig(STIFFNESS_LOW, DAMPING_RATIO_NO_BOUNCY);
 
+    @Nullable private Runnable mUpdateMovementBoundsRunnable;
+
     private final Consumer<Rect> mUpdateBoundsCallback = (Rect newBounds) -> {
         if (mPipBoundsState.getBounds().equals(newBounds)) {
             return;
@@ -141,6 +143,7 @@
 
         mMenuController.updateMenuLayout(newBounds);
         mPipBoundsState.setBounds(newBounds);
+        maybeUpdateMovementBounds();
     };
 
     /**
@@ -416,6 +419,17 @@
         // location now.
         mSpringingToTouch = false;
 
+        // Boost the velocityX if it's zero to forcefully push it towards the nearest edge.
+        // We don't simply change the xEndValue below since the PhysicsAnimator would rely on the
+        // same velocityX to find out which edge to snap to.
+        if (velocityX == 0) {
+            final int motionCenterX = mPipBoundsState
+                    .getMotionBoundsState().getBoundsInMotion().centerX();
+            final int displayCenterX = mPipBoundsState
+                    .getDisplayBounds().centerX();
+            velocityX = (motionCenterX < displayCenterX) ? -0.001f : 0.001f;
+        }
+
         mTemporaryBoundsPhysicsAnimator
                 .spring(FloatProperties.RECT_WIDTH, getBounds().width(), mSpringConfig)
                 .spring(FloatProperties.RECT_HEIGHT, getBounds().height(), mSpringConfig)
@@ -555,11 +569,20 @@
                             + " callers=\n%s", TAG, originalBounds, offset,
                     Debug.getCallers(5, "    "));
         }
+        if (offset == 0) {
+            return;
+        }
+
         cancelPhysicsAnimation();
-        /*
-        mPipTaskOrganizer.scheduleOffsetPip(originalBounds, offset, SHIFT_DURATION,
-                mUpdateBoundsCallback);
-         */
+
+        Rect adjustedBounds = new Rect(originalBounds);
+        adjustedBounds.offset(0, offset);
+
+        setAnimatingToBounds(adjustedBounds);
+        Bundle extra = new Bundle();
+        extra.putBoolean(ANIMATING_BOUNDS_CHANGE, true);
+        extra.putInt(ANIMATING_BOUNDS_CHANGE_DURATION, SHIFT_DURATION);
+        mPipTransitionState.setState(PipTransitionState.SCHEDULED_BOUNDS_CHANGE, extra);
     }
 
     /**
@@ -574,11 +597,11 @@
     /** Set new fling configs whose min/max values respect the given movement bounds. */
     private void rebuildFlingConfigs() {
         mFlingConfigX = new PhysicsAnimator.FlingConfig(DEFAULT_FRICTION,
-                mPipBoundsAlgorithm.getMovementBounds(getBounds()).left,
-                mPipBoundsAlgorithm.getMovementBounds(getBounds()).right);
+                mPipBoundsState.getMovementBounds().left,
+                mPipBoundsState.getMovementBounds().right);
         mFlingConfigY = new PhysicsAnimator.FlingConfig(DEFAULT_FRICTION,
-                mPipBoundsAlgorithm.getMovementBounds(getBounds()).top,
-                mPipBoundsAlgorithm.getMovementBounds(getBounds()).bottom);
+                mPipBoundsState.getMovementBounds().top,
+                mPipBoundsState.getMovementBounds().bottom);
         final Rect insetBounds = mPipBoundsState.getDisplayLayout().stableInsets();
         mStashConfigX = new PhysicsAnimator.FlingConfig(
                 DEFAULT_FRICTION,
@@ -660,6 +683,16 @@
         cleanUpHighPerfSessionMaybe();
     }
 
+    void setUpdateMovementBoundsRunnable(Runnable updateMovementBoundsRunnable) {
+        mUpdateMovementBoundsRunnable = updateMovementBoundsRunnable;
+    }
+
+    private void maybeUpdateMovementBounds() {
+        if (mUpdateMovementBoundsRunnable != null)  {
+            mUpdateMovementBoundsRunnable.run();
+        }
+    }
+
     /**
      * Notifies the floating coordinator that we're moving, and sets the animating to bounds so
      * we return these bounds from
@@ -796,8 +829,14 @@
                 startTx, finishTx, mPipBoundsState.getBounds(), mPipBoundsState.getBounds(),
                 destinationBounds, duration, 0f /* angle */);
         animator.setAnimationEndCallback(() -> {
-            mPipBoundsState.setBounds(destinationBounds);
-            // All motion operations have actually finished, so make bounds cache updates.
+            mUpdateBoundsCallback.accept(destinationBounds);
+
+            // In case an ongoing drag/fling was present before a deterministic resize transition
+            // kicked in, we need to update the update bounds properly before cleaning in-motion
+            // state.
+            mPipBoundsState.getMotionBoundsState().setBoundsInMotion(destinationBounds);
+            settlePipBoundsAfterPhysicsAnimation(false /* animatingAfter */);
+
             cleanUpHighPerfSessionMaybe();
             // Signal that we are done with resize transition
             mPipScheduler.scheduleFinishResizePip(true /* configAtEnd */);
@@ -806,7 +845,7 @@
     }
 
     private void settlePipBoundsAfterPhysicsAnimation(boolean animatingAfter) {
-        if (!animatingAfter) {
+        if (!animatingAfter && mPipBoundsState.getMotionBoundsState().isInMotion()) {
             // The physics animation ended, though we may not necessarily be done animating, such as
             // when we're still dragging after moving out of the magnetic target. Only set the final
             // bounds state and clear motion bounds completely if the whole animation is over.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipResizeGestureHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipResizeGestureHandler.java
index 5b0ca18..d28204a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipResizeGestureHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipResizeGestureHandler.java
@@ -146,8 +146,8 @@
         mUpdateResizeBoundsCallback = (rect) -> {
             mUserResizeBounds.set(rect);
             // mMotionHelper.synchronizePinnedStackBounds();
-            mUpdateMovementBoundsRunnable.run();
             mPipBoundsState.setBounds(rect);
+            mUpdateMovementBoundsRunnable.run();
             resetState();
         };
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java
index 53b80e8..f387e72 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java
@@ -199,6 +199,7 @@
         mMenuController.addListener(new PipMenuListener());
         mGesture = new DefaultPipTouchGesture();
         mMotionHelper = pipMotionHelper;
+        mMotionHelper.setUpdateMovementBoundsRunnable(this::updateMovementBounds);
         mPipDismissTargetHandler = new PipDismissTargetHandler(context, pipUiEventLogger,
                 mMotionHelper, mainExecutor);
         mTouchState = new PipTouchState(ViewConfiguration.get(context),
@@ -317,6 +318,8 @@
         mFloatingContentCoordinator.onContentRemoved(mMotionHelper);
         mPipResizeGestureHandler.onActivityUnpinned();
         mPipInputConsumer.unregisterInputConsumer();
+        mPipBoundsState.setHasUserMovedPip(false);
+        mPipBoundsState.setHasUserResizedPip(false);
     }
 
     void onPinnedStackAnimationEnded(
@@ -346,6 +349,22 @@
     void onImeVisibilityChanged(boolean imeVisible, int imeHeight) {
         mIsImeShowing = imeVisible;
         mImeHeight = imeHeight;
+
+        // Cache new movement bounds using the new potential IME height.
+        updateMovementBounds();
+
+        mPipTransitionState.setOnIdlePipTransitionStateRunnable(() -> {
+            int delta = mPipBoundsState.getMovementBounds().bottom
+                    - mPipBoundsState.getBounds().top;
+
+            boolean hasUserInteracted = (mPipBoundsState.hasUserMovedPip()
+                    || mPipBoundsState.hasUserResizedPip());
+            if ((imeVisible && delta < 0) || (!imeVisible && !hasUserInteracted)) {
+                // The policy is to ignore an IME disappearing if user has interacted with PiP.
+                // Otherwise, only offset due to an appearing IME if PiP occludes it.
+                mMotionHelper.animateToOffset(mPipBoundsState.getBounds(), delta);
+            }
+        });
     }
 
     void onShelfVisibilityChanged(boolean shelfVisible, int shelfHeight) {
@@ -1077,6 +1096,7 @@
         switch (newState) {
             case PipTransitionState.ENTERED_PIP:
                 onActivityPinned();
+                updateMovementBounds();
                 mTouchState.setAllowInputEvents(true);
                 mTouchState.reset();
                 break;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransitionState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransitionState.java
index 29272be..a132796f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransitionState.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransitionState.java
@@ -149,6 +149,12 @@
     @Nullable
     private SurfaceControl mSwipePipToHomeOverlay;
 
+    //
+    // Scheduling-related state
+    //
+    @Nullable
+    private Runnable mOnIdlePipTransitionStateRunnable;
+
     /**
      * An interface to track state updates as we progress through PiP transitions.
      */
@@ -197,6 +203,8 @@
             mState = state;
             dispatchPipTransitionStateChanged(prevState, mState, extra);
         }
+
+        maybeRunOnIdlePipTransitionStateCallback();
     }
 
     /**
@@ -231,6 +239,29 @@
     }
 
     /**
+     * Schedule a callback to run when in a valid idle PiP state.
+     *
+     * <p>We only allow for one callback to be scheduled to avoid cases with multiple transitions
+     * being scheduled. For instance, if user double taps and IME shows, this would
+     * schedule a bounds change transition for IME appearing. But if some other transition would
+     * want to animate PiP before the scheduled callback executes, we would rather want to replace
+     * the existing callback with a new one, to avoid multiple animations
+     * as soon as we are idle.</p>
+     */
+    public void setOnIdlePipTransitionStateRunnable(
+            @Nullable Runnable onIdlePipTransitionStateRunnable) {
+        mOnIdlePipTransitionStateRunnable = onIdlePipTransitionStateRunnable;
+        maybeRunOnIdlePipTransitionStateCallback();
+    }
+
+    private void maybeRunOnIdlePipTransitionStateCallback() {
+        if (mOnIdlePipTransitionStateRunnable != null && isPipStateIdle()) {
+            mOnIdlePipTransitionStateRunnable.run();
+            mOnIdlePipTransitionStateRunnable = null;
+        }
+    }
+
+    /**
      * Adds a {@link PipTransitionStateChangedListener} for future PiP transition state updates.
      */
     public void addPipTransitionStateChangedListener(PipTransitionStateChangedListener listener) {
@@ -318,6 +349,11 @@
         throw new IllegalStateException("Unknown state: " + state);
     }
 
+    public boolean isPipStateIdle() {
+        // This needs to be a valid in-PiP state that isn't a transient state.
+        return mState == ENTERED_PIP || mState == CHANGED_PIP_BOUNDS;
+    }
+
     @Override
     public String toString() {
         return String.format("PipTransitionState(mState=%s, mInSwipePipToHomeTransition=%b)",
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
index d001b2c..da7e03f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
@@ -19,8 +19,6 @@
 import static android.app.ActivityTaskManager.INVALID_TASK_ID;
 import static android.content.pm.PackageManager.FEATURE_PC;
 
-import static com.android.window.flags.Flags.enableDesktopWindowingTaskbarRunningApps;
-import static com.android.window.flags.Flags.enableTaskStackObserverInShell;
 import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_RECENT_TASKS;
 
 import android.app.ActivityManager;
@@ -55,6 +53,7 @@
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
 import com.android.wm.shell.shared.annotations.ExternalThread;
 import com.android.wm.shell.shared.annotations.ShellMainThread;
+import com.android.wm.shell.shared.desktopmode.DesktopModeFlags;
 import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
 import com.android.wm.shell.sysui.ShellCommandHandler;
 import com.android.wm.shell.sysui.ShellController;
@@ -351,7 +350,7 @@
 
     private void notifyTaskMovedToFront(ActivityManager.RunningTaskInfo taskInfo) {
         if (mListener == null
-                || !enableTaskStackObserverInShell()
+                || !DesktopModeFlags.TASK_STACK_OBSERVER_IN_SHELL.isEnabled(mContext)
                 || taskInfo.realActivity == null) {
             return;
         }
@@ -365,7 +364,7 @@
     private boolean shouldEnableRunningTasksForDesktopMode() {
         return mPcFeatureEnabled
                 || (DesktopModeStatus.canEnterDesktopMode(mContext)
-                && enableDesktopWindowingTaskbarRunningApps());
+                && DesktopModeFlags.TASKBAR_RUNNING_APPS.isEnabled(mContext));
     }
 
     @VisibleForTesting
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 234b4d0..ad3f4f8 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
@@ -19,12 +19,14 @@
 import static android.app.ActivityTaskManager.INVALID_TASK_ID;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.view.WindowManager.KEYGUARD_VISIBILITY_TRANSIT_FLAGS;
 import static android.view.WindowManager.TRANSIT_CHANGE;
 import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_LOCKED;
 import static android.view.WindowManager.TRANSIT_PIP;
 import static android.view.WindowManager.TRANSIT_SLEEP;
 import static android.view.WindowManager.TRANSIT_TO_FRONT;
+import static android.window.TransitionInfo.FLAG_MOVED_TO_TOP;
 import static android.window.TransitionInfo.FLAG_TRANSLUCENT;
 
 import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_CAN_HAND_OFF_ANIMATION;
@@ -775,6 +777,20 @@
                     // Don't consider order-only & non-leaf changes as changing apps.
                     if (!TransitionUtil.isOrderOnly(change) && isLeafTask) {
                         hasChangingApp = true;
+                        // Check if the changing app is moving to top and fullscreen. This handles
+                        // the case where we moved from desktop to recents and launching a desktop
+                        // task in fullscreen.
+                        if ((change.getFlags() & FLAG_MOVED_TO_TOP) != 0
+                                && taskInfo != null
+                                && taskInfo.getWindowingMode()
+                                == WINDOWING_MODE_FULLSCREEN) {
+                            if (openingTasks == null) {
+                                openingTasks = new ArrayList<>();
+                                openingTaskIsLeafs = new IntArray();
+                            }
+                            openingTasks.add(change);
+                            openingTaskIsLeafs.add(1);
+                        }
                     } else if (isLeafTask && taskInfo.topActivityType == ACTIVITY_TYPE_HOME
                             && !isRecentsTask ) {
                         // Unless it is a 3p launcher. This means that the 3p launcher was already
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/TaskStackTransitionObserver.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/TaskStackTransitionObserver.kt
index 8ee72b4..3a0bdb9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/TaskStackTransitionObserver.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/TaskStackTransitionObserver.kt
@@ -18,13 +18,14 @@
 
 import android.app.ActivityManager.RunningTaskInfo
 import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
+import android.content.Context
 import android.os.IBinder
 import android.util.ArrayMap
 import android.view.SurfaceControl
 import android.view.WindowManager
 import android.window.TransitionInfo
-import com.android.window.flags.Flags.enableTaskStackObserverInShell
 import com.android.wm.shell.shared.TransitionUtil
+import com.android.wm.shell.shared.desktopmode.DesktopModeFlags
 import com.android.wm.shell.sysui.ShellInit
 import com.android.wm.shell.transition.Transitions
 import dagger.Lazy
@@ -37,6 +38,7 @@
  * TODO(346588978) Move split/pip signals here as well so that launcher don't need to handle it
  */
 class TaskStackTransitionObserver(
+    private val context: Context,
     private val transitions: Lazy<Transitions>,
     shellInit: ShellInit
 ) : Transitions.TransitionObserver {
@@ -62,7 +64,7 @@
         startTransaction: SurfaceControl.Transaction,
         finishTransaction: SurfaceControl.Transaction
     ) {
-        if (enableTaskStackObserverInShell()) {
+        if (DesktopModeFlags.TASK_STACK_OBSERVER_IN_SHELL.isEnabled(context)) {
             val taskInfoList = mutableListOf<RunningTaskInfo>()
             val transitionTypeList = mutableListOf<Int>()
 
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 b857556..c4af148 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
@@ -19,6 +19,7 @@
 import static android.app.ActivityManager.START_SUCCESS;
 import static android.app.ActivityManager.START_TASK_TO_FRONT;
 import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
 import static android.content.Intent.FLAG_ACTIVITY_NO_USER_ACTION;
 import static android.view.Display.DEFAULT_DISPLAY;
@@ -431,6 +432,20 @@
         mStageCoordinator.clearSplitPairedInRecents(reason);
     }
 
+    /**
+     * Determines which split position a new instance of a task should take.
+     * @param callingTask The task requesting a new instance.
+     * @return the split position of the new instance
+     */
+    public int determineNewInstancePosition(@NonNull ActivityManager.RunningTaskInfo callingTask) {
+        if (callingTask.getWindowingMode() == WINDOWING_MODE_FULLSCREEN
+                || getSplitPosition(callingTask.taskId) == SPLIT_POSITION_TOP_OR_LEFT) {
+            return SPLIT_POSITION_BOTTOM_OR_RIGHT;
+        } else {
+            return SPLIT_POSITION_TOP_OR_LEFT;
+        }
+    }
+
     public void enterSplitScreen(int taskId, boolean leftOrTop) {
         enterSplitScreen(taskId, leftOrTop, new WindowContainerTransaction());
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
index b3dab85..48d17ec6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
@@ -68,6 +68,7 @@
     DismissSession mPendingDismiss = null;
     EnterSession mPendingEnter = null;
     TransitSession mPendingResize = null;
+    TransitSession mPendingRemotePassthrough = null;
 
     private IBinder mAnimatingTransition = null;
     private OneShotRemoteHandler mActiveRemoteHandler = null;
@@ -320,6 +321,11 @@
         return mPendingResize != null && mPendingResize.mTransition == transition;
     }
 
+    boolean isPendingPassThrough(IBinder transition) {
+        return mPendingRemotePassthrough != null &&
+                mPendingRemotePassthrough.mTransition == transition;
+    }
+
     @Nullable
     private TransitSession getPendingTransition(IBinder transition) {
         if (isPendingEnter(transition)) {
@@ -331,6 +337,9 @@
         } else if (isPendingResize(transition)) {
             ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "\tresolved resize transition");
             return mPendingResize;
+        } else if (isPendingPassThrough(transition)) {
+            ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "\tresolved passThrough transition");
+            return mPendingRemotePassthrough;
         }
         return null;
     }
@@ -378,6 +387,19 @@
                 extraTransitType, resizeAnim);
     }
 
+    /** Sets a transition to enter split. */
+    void setRemotePassThroughTransition(@NonNull IBinder transition,
+            @Nullable RemoteTransition remoteTransition) {
+        mPendingRemotePassthrough = new TransitSession(
+                transition, null, null,
+                remoteTransition, Transitions.TRANSIT_SPLIT_PASSTHROUGH);
+
+        ProtoLog.v(WM_SHELL_TRANSITIONS, "  splitTransition "
+                + " deduced remote passthrough split screen");
+        ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "setRemotePassThrough: transitType=%d remote=%s",
+                Transitions.TRANSIT_SPLIT_PASSTHROUGH, remoteTransition);
+    }
+
     /** Starts a transition to dismiss split. */
     IBinder startDismissTransition(WindowContainerTransaction wct,
             Transitions.TransitionHandler handler, @SplitScreen.StageType int dismissTop,
@@ -474,6 +496,12 @@
             mPendingResize.onConsumed(aborted);
             mPendingResize = null;
             ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTransitionConsumed for resize transition");
+        } else if (isPendingPassThrough(transition)) {
+            mPendingRemotePassthrough.onConsumed(aborted);
+            mPendingRemotePassthrough.mRemoteHandler.onTransitionConsumed(transition, aborted,
+                    finishT);
+            mPendingRemotePassthrough = null;
+            ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTransitionConsumed for passThrough transition");
         }
 
         // TODO: handle transition consumed for active remote handler
@@ -495,6 +523,10 @@
             mPendingResize.onFinished(wct, mFinishTransaction);
             mPendingResize = null;
             ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onFinish for resize transition");
+        } else if (isPendingPassThrough(mAnimatingTransition)) {
+            mPendingRemotePassthrough.onFinished(wct, mFinishTransaction);
+            mPendingRemotePassthrough = null;
+            ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onFinish for passThrough transition");
         }
 
         mActiveRemoteHandler = null;
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 a4f32c4..2531ff1 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
@@ -41,7 +41,6 @@
 import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
 import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED;
 import static com.android.wm.shell.common.split.SplitScreenConstants.splitPositionToString;
-import static com.android.wm.shell.common.split.SplitScreenUtils.getResizingBackgroundColor;
 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;
@@ -2458,13 +2457,8 @@
         updateSurfaceBounds(layout, t, shouldUseParallaxEffect);
         getMainStageBounds(mTempRect1);
         getSideStageBounds(mTempRect2);
-        // TODO (b/307490004): "commonColor" below is a temporary fix to ensure the colors on both
-        //  sides match. When b/307490004 is fixed, this code can be reverted.
-        float[] commonColor = getResizingBackgroundColor(mSideStage.mRootTaskInfo).getComponents();
-        mMainStage.onResizing(
-                mTempRect1, mTempRect2, t, offsetX, offsetY, mShowDecorImmediately, commonColor);
-        mSideStage.onResizing(
-                mTempRect2, mTempRect1, t, offsetX, offsetY, mShowDecorImmediately, commonColor);
+        mMainStage.onResizing(mTempRect1, mTempRect2, t, offsetX, offsetY, mShowDecorImmediately);
+        mSideStage.onResizing(mTempRect2, mTempRect1, t, offsetX, offsetY, mShowDecorImmediately);
         t.apply();
         mTransactionPool.release(t);
     }
@@ -2710,7 +2704,7 @@
             @Nullable TransitionRequestInfo request) {
         final ActivityManager.RunningTaskInfo triggerTask = request.getTriggerTask();
         if (triggerTask == null) {
-            if (isSplitScreenVisible()) {
+            if (isSplitActive()) {
                 ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "handleRequest: transition=%d display rotation",
                         request.getDebugId());
                 // Check if the display is rotating.
@@ -2720,6 +2714,10 @@
                         && displayChange.getStartRotation() != displayChange.getEndRotation()) {
                     mSplitLayout.setFreezeDividerWindow(true);
                 }
+                if (request.getRemoteTransition() != null) {
+                    mSplitTransitions.setRemotePassThroughTransition(transition,
+                            request.getRemoteTransition());
+                }
                 // Still want to monitor everything while in split-screen, so return non-null.
                 return new WindowContainerTransaction();
             } else {
@@ -3046,6 +3044,13 @@
                 notifySplitAnimationFinished();
                 return true;
             }
+        } else if (mSplitTransitions.isPendingPassThrough(transition)) {
+            ProtoLog.d(WM_SHELL_SPLIT_SCREEN,
+                    "startAnimation: passThrough transition=%d", info.getDebugId());
+            mSplitTransitions.mPendingRemotePassthrough.mRemoteHandler.startAnimation(transition,
+                    info, startTransaction, finishTransaction, finishCallback);
+            notifySplitAnimationFinished();
+            return true;
         }
 
         return startPendingAnimation(transition, info, startTransaction, finishTransaction,
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 1076eca..d1ab3e9 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
@@ -314,10 +314,10 @@
     }
 
     void onResizing(Rect newBounds, Rect sideBounds, SurfaceControl.Transaction t, int offsetX,
-            int offsetY, boolean immediately, float[] veilColor) {
+            int offsetY, boolean immediately) {
         if (mSplitDecorManager != null && mRootTaskInfo != null) {
             mSplitDecorManager.onResizing(mRootTaskInfo, newBounds, sideBounds, t, offsetX,
-                    offsetY, immediately, veilColor);
+                    offsetY, immediately);
         }
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java
index e6d1b45..15fe7ab 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java
@@ -217,6 +217,11 @@
         return null;
     }
 
+    /** Returns true if the given {@code taskInfo} belongs to a task view. */
+    public boolean isTaskViewTask(ActivityManager.RunningTaskInfo taskInfo) {
+        return findTaskView(taskInfo) != null;
+    }
+
     void startTaskView(@NonNull WindowContainerTransaction wct,
             @NonNull TaskViewTaskController taskView, @NonNull IBinder launchCookie) {
         updateVisibilityState(taskView, true /* visible */);
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 fc8b1d2..c850ff8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
@@ -38,9 +38,9 @@
 import static android.window.TransitionInfo.FLAG_NO_ANIMATION;
 import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT;
 
-import static com.android.window.flags.Flags.enforceShellThreadModel;
 import static com.android.window.flags.Flags.ensureWallpaperInTransitions;
 import static com.android.systemui.shared.Flags.returnAnimationFrameworkLibrary;
+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;
 import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_SHELL_TRANSITIONS;
@@ -190,6 +190,9 @@
             // TRANSIT_FIRST_CUSTOM + 17
             TaskFragmentOrganizer.TASK_FRAGMENT_TRANSIT_DRAG_RESIZE;
 
+    /** Remote Transition that split accepts but ultimately needs to be animated by the remote. */
+    public static final int TRANSIT_SPLIT_PASSTHROUGH = TRANSIT_FIRST_CUSTOM + 18;
+
     /** Transition type for desktop mode transitions. */
     public static final int TRANSIT_DESKTOP_MODE_TYPES =
             WindowManager.TRANSIT_FIRST_CUSTOM + 100;
@@ -819,7 +822,8 @@
             }
             // The change has already animated by back gesture, don't need to play transition
             // animation on it.
-            if (change.hasFlags(FLAG_BACK_GESTURE_ANIMATED)) {
+            if (!migratePredictiveBackTransition()
+                    && change.hasFlags(FLAG_BACK_GESTURE_ANIMATED)) {
                 info.getChanges().remove(i);
             }
         }
@@ -933,9 +937,7 @@
     }
 
     private void onMerged(@NonNull IBinder playingToken, @NonNull IBinder mergedToken) {
-        if (enforceShellThreadModel()) {
-            mMainExecutor.assertCurrentThread();
-        }
+        mMainExecutor.assertCurrentThread();
 
         ActiveTransition playing = mKnownTransitions.get(playingToken);
         if (playing == null) {
@@ -1084,9 +1086,7 @@
     }
 
     private void onFinish(IBinder token, @Nullable WindowContainerTransaction wct) {
-        if (enforceShellThreadModel()) {
-            mMainExecutor.assertCurrentThread();
-        }
+        mMainExecutor.assertCurrentThread();
 
         final ActiveTransition active = mKnownTransitions.get(token);
         if (active == null) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
index 5c230c0..d8dba71 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
@@ -33,6 +33,7 @@
 import android.hardware.input.InputManager;
 import android.os.Handler;
 import android.os.RemoteException;
+import android.os.UserHandle;
 import android.provider.Settings;
 import android.util.Log;
 import android.util.SparseArray;
@@ -288,6 +289,7 @@
         final CaptionWindowDecoration windowDecoration =
                 new CaptionWindowDecoration(
                         mContext,
+                        mContext.createContextAsUser(UserHandle.of(taskInfo.userId), 0 /* flags */),
                         mDisplayController,
                         mTaskOrganizer,
                         taskInfo,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
index cf42a49..9de0651 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
@@ -77,6 +77,7 @@
 
     CaptionWindowDecoration(
             Context context,
+            @NonNull Context userContext,
             DisplayController displayController,
             ShellTaskOrganizer taskOrganizer,
             RunningTaskInfo taskInfo,
@@ -85,7 +86,7 @@
             @ShellBackgroundThread ShellExecutor bgExecutor,
             Choreographer choreographer,
             SyncTransactionQueue syncQueue) {
-        super(context, displayController, taskOrganizer, taskInfo, taskSurface);
+        super(context, userContext, displayController, taskOrganizer, taskInfo, taskSurface);
         mHandler = handler;
         mBgExecutor = bgExecutor;
         mChoreographer = choreographer;
@@ -278,7 +279,7 @@
 
         final Resources res = mResult.mRootView.getResources();
         mDragResizeListener.setGeometry(new DragResizeWindowGeometry(0 /* taskCornerRadius */,
-                new Size(mResult.mWidth, mResult.mHeight), getResizeEdgeHandleSize(res),
+                new Size(mResult.mWidth, mResult.mHeight), getResizeEdgeHandleSize(mContext, res),
                 getFineResizeCornerSize(res), getLargeResizeCornerSize(res)), touchSlop);
     }
 
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 8312aef..d68c018 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
@@ -58,6 +58,7 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.RemoteException;
+import android.os.UserHandle;
 import android.util.Log;
 import android.util.SparseArray;
 import android.view.Choreographer;
@@ -87,9 +88,12 @@
 import com.android.wm.shell.R;
 import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
 import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.apptoweb.AppToWebGenericLinksParser;
+import com.android.wm.shell.common.DisplayChangeController;
 import com.android.wm.shell.common.DisplayController;
 import com.android.wm.shell.common.DisplayInsetsController;
 import com.android.wm.shell.common.DisplayLayout;
+import com.android.wm.shell.common.MultiInstanceHelper;
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.SyncTransactionQueue;
 import com.android.wm.shell.common.desktopmode.DesktopModeTransitionSource;
@@ -100,6 +104,7 @@
 import com.android.wm.shell.desktopmode.DesktopWallpaperActivity;
 import com.android.wm.shell.freeform.FreeformTaskTransitionStarter;
 import com.android.wm.shell.shared.annotations.ShellBackgroundThread;
+import com.android.wm.shell.shared.desktopmode.DesktopModeFlags;
 import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
 import com.android.wm.shell.splitscreen.SplitScreen;
 import com.android.wm.shell.splitscreen.SplitScreen.StageType;
@@ -113,6 +118,8 @@
 import com.android.wm.shell.windowdecor.extension.TaskInfoKt;
 import com.android.wm.shell.windowdecor.viewholder.AppHeaderViewHolder;
 
+import kotlin.Unit;
+
 import java.io.PrintWriter;
 import java.util.Optional;
 import java.util.function.Supplier;
@@ -141,6 +148,7 @@
     private final DesktopTasksController mDesktopTasksController;
     private final InputManager mInputManager;
     private final InteractionJankMonitor mInteractionJankMonitor;
+    private final MultiInstanceHelper mMultiInstanceHelper;
     private boolean mTransitionDragActive;
 
     private SparseArray<EventReceiver> mEventReceiversByDisplay = new SparseArray<>();
@@ -162,11 +170,13 @@
     private final DesktopModeKeyguardChangeListener mDesktopModeKeyguardChangeListener =
             new DesktopModeKeyguardChangeListener();
     private final RootTaskDisplayAreaOrganizer mRootTaskDisplayAreaOrganizer;
+    private final AppToWebGenericLinksParser mGenericLinksParser;
     private final DisplayInsetsController mDisplayInsetsController;
     private final Region mExclusionRegion = Region.obtain();
     private boolean mInImmersiveMode;
     private final String mSysUIPackageName;
 
+    private final DisplayChangeController.OnDisplayChangingListener mOnDisplayChangingListener;
     private final ISystemGestureExclusionListener mGestureExclusionListener =
             new ISystemGestureExclusionListener.Stub() {
                 @Override
@@ -198,7 +208,9 @@
             Transitions transitions,
             Optional<DesktopTasksController> desktopTasksController,
             RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
-            InteractionJankMonitor interactionJankMonitor
+            InteractionJankMonitor interactionJankMonitor,
+            AppToWebGenericLinksParser genericLinksParser,
+            MultiInstanceHelper multiInstanceHelper
     ) {
         this(
                 context,
@@ -216,6 +228,8 @@
                 syncQueue,
                 transitions,
                 desktopTasksController,
+                genericLinksParser,
+                multiInstanceHelper,
                 new DesktopModeWindowDecoration.Factory(),
                 new InputMonitorFactory(),
                 SurfaceControl.Transaction::new,
@@ -241,6 +255,8 @@
             SyncTransactionQueue syncQueue,
             Transitions transitions,
             Optional<DesktopTasksController> desktopTasksController,
+            AppToWebGenericLinksParser genericLinksParser,
+            MultiInstanceHelper multiInstanceHelper,
             DesktopModeWindowDecoration.Factory desktopModeWindowDecorFactory,
             InputMonitorFactory inputMonitorFactory,
             Supplier<SurfaceControl.Transaction> transactionFactory,
@@ -260,17 +276,44 @@
         mSyncQueue = syncQueue;
         mTransitions = transitions;
         mDesktopTasksController = desktopTasksController.get();
+        mMultiInstanceHelper = multiInstanceHelper;
         mShellCommandHandler = shellCommandHandler;
         mWindowManager = windowManager;
         mDesktopModeWindowDecorFactory = desktopModeWindowDecorFactory;
         mInputMonitorFactory = inputMonitorFactory;
         mTransactionFactory = transactionFactory;
         mRootTaskDisplayAreaOrganizer = rootTaskDisplayAreaOrganizer;
+        mGenericLinksParser = genericLinksParser;
         mInputManager = mContext.getSystemService(InputManager.class);
         mWindowDecorByTaskId = windowDecorByTaskId;
         mSysUIPackageName = mContext.getResources().getString(
                 com.android.internal.R.string.config_systemUi);
         mInteractionJankMonitor = interactionJankMonitor;
+        mOnDisplayChangingListener = (displayId, fromRotation, toRotation, displayAreaInfo, t) -> {
+            DesktopModeWindowDecoration decoration;
+            RunningTaskInfo taskInfo;
+            for (int i = 0; i < mWindowDecorByTaskId.size(); i++) {
+                decoration = mWindowDecorByTaskId.valueAt(i);
+                if (decoration == null) {
+                    continue;
+                } else {
+                    taskInfo = decoration.mTaskInfo;
+                }
+
+                // Check if display has been rotated between portrait & landscape
+                if (displayId == taskInfo.displayId && taskInfo.isFreeform()
+                        && (fromRotation % 2 != toRotation % 2)) {
+                    // Check if the task bounds on the rotated display will be out of bounds.
+                    // If so, then update task bounds to be within reachable area.
+                    final Rect taskBounds = new Rect(
+                            taskInfo.configuration.windowConfiguration.getBounds());
+                    if (DragPositioningCallbackUtility.snapTaskBoundsIfNecessary(
+                            taskBounds, decoration.calculateValidDragArea())) {
+                        t.setBounds(taskInfo.token, taskBounds);
+                    }
+                }
+            }
+        };
 
         shellInit.addInitCallback(this::onInit, this);
     }
@@ -281,7 +324,8 @@
         mDisplayInsetsController.addInsetsChangedListener(mContext.getDisplayId(),
                 new DesktopModeOnInsetsChangedListener());
         mDesktopTasksController.setOnTaskResizeAnimationListener(
-                new DeskopModeOnTaskResizeAnimationListener());
+                new DesktopModeOnTaskResizeAnimationListener());
+        mDisplayController.addDisplayChangingController(mOnDisplayChangingListener);
         try {
             mWindowManager.registerSystemGestureExclusionListener(mGestureExclusionListener,
                     mContext.getDisplayId());
@@ -403,13 +447,13 @@
         mWindowDecorByTaskId.remove(taskInfo.taskId);
     }
 
-    private void onMaximizeOrRestore(int taskId, String tag) {
+    private void onMaximizeOrRestore(int taskId, String source) {
         final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(taskId);
         if (decoration == null) {
             return;
         }
         mInteractionJankMonitor.begin(
-                decoration.mTaskSurface, mContext, Cuj.CUJ_DESKTOP_MODE_MAXIMIZE_WINDOW, tag);
+                decoration.mTaskSurface, mContext, Cuj.CUJ_DESKTOP_MODE_MAXIMIZE_WINDOW, source);
         mDesktopTasksController.toggleDesktopTaskSize(decoration.mTaskInfo);
         decoration.closeHandleMenu();
         decoration.closeMaximizeMenu();
@@ -421,15 +465,20 @@
             return;
         }
         mDesktopTasksController.snapToHalfScreen(decoration.mTaskInfo,
+                decoration.mTaskInfo.configuration.windowConfiguration.getBounds(),
                 left ? SnapPosition.LEFT : SnapPosition.RIGHT);
         decoration.closeHandleMenu();
         decoration.closeMaximizeMenu();
     }
 
-    private void onOpenInBrowser(@NonNull DesktopModeWindowDecoration decor, @NonNull Uri uri) {
+    private void onOpenInBrowser(int taskId, @NonNull Uri uri) {
+        final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(taskId);
+        if (decoration == null) {
+            return;
+        }
         openInBrowser(uri);
-        decor.closeHandleMenu();
-        decor.closeMaximizeMenu();
+        decoration.closeHandleMenu();
+        decoration.closeMaximizeMenu();
     }
 
     private void openInBrowser(Uri uri) {
@@ -439,6 +488,57 @@
         mContext.startActivity(intent);
     }
 
+    private void onToDesktop(int taskId, DesktopModeTransitionSource source) {
+        final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(taskId);
+        if (decoration == null) {
+            return;
+        }
+        final WindowContainerTransaction wct = new WindowContainerTransaction();
+        mInteractionJankMonitor.begin(decoration.mTaskSurface, mContext,
+                CUJ_DESKTOP_MODE_ENTER_MODE_APP_HANDLE_MENU);
+        // App sometimes draws before the insets from WindowDecoration#relayout have
+        // been added, so they must be added here
+        decoration.addCaptionInset(wct);
+        mDesktopTasksController.moveTaskToDesktop(taskId, wct, source);
+        decoration.closeHandleMenu();
+    }
+
+    private void onToFullscreen(int taskId) {
+        final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(taskId);
+        if (decoration == null) {
+            return;
+        }
+        decoration.closeHandleMenu();
+        if (isTaskInSplitScreen(taskId)) {
+            mSplitScreenController.moveTaskToFullscreen(taskId,
+                    SplitScreenController.EXIT_REASON_DESKTOP_MODE);
+        } else {
+            mDesktopTasksController.moveToFullscreen(taskId,
+                    DesktopModeTransitionSource.APP_HANDLE_MENU_BUTTON);
+        }
+    }
+
+    private void onToSplitScreen(int taskId) {
+        final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(taskId);
+        if (decoration == null) {
+            return;
+        }
+        decoration.closeHandleMenu();
+        // When the app enters split-select, the handle will no longer be visible, meaning
+        // we shouldn't receive input for it any longer.
+        decoration.disposeStatusBarInputLayer();
+        mDesktopTasksController.requestSplit(decoration.mTaskInfo, false /* leftOrTop */);
+    }
+
+    private void onNewWindow(int taskId) {
+        final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(taskId);
+        if (decoration == null) {
+            return;
+        }
+        decoration.closeHandleMenu();
+        mDesktopTasksController.openNewWindow(decoration.mTaskInfo);
+    }
+
     private class DesktopModeTouchEventListener extends GestureDetector.SimpleOnGestureListener
             implements View.OnClickListener, View.OnTouchListener, View.OnLongClickListener,
             View.OnGenericMotionListener, DragDetector.MotionEventHandler {
@@ -495,40 +595,7 @@
                 if (!decoration.isHandleMenuActive()) {
                     moveTaskToFront(decoration.mTaskInfo);
                     decoration.createHandleMenu(mSplitScreenController);
-                } else {
-                    decoration.closeHandleMenu();
                 }
-            } else if (id == R.id.desktop_button) {
-                final WindowContainerTransaction wct = new WindowContainerTransaction();
-                // App sometimes draws before the insets from WindowDecoration#relayout have
-                // been added, so they must be added here
-                mInteractionJankMonitor.begin(decoration.mTaskSurface, mContext,
-                        CUJ_DESKTOP_MODE_ENTER_MODE_APP_HANDLE_MENU);
-                mWindowDecorByTaskId.get(mTaskId).addCaptionInset(wct);
-                mDesktopTasksController.moveToDesktop(mTaskId, wct,
-                        DesktopModeTransitionSource.APP_HANDLE_MENU_BUTTON);
-                decoration.closeHandleMenu();
-            } else if (id == R.id.fullscreen_button) {
-                decoration.closeHandleMenu();
-                if (isTaskInSplitScreen(mTaskId)) {
-                    mSplitScreenController.moveTaskToFullscreen(mTaskId,
-                            SplitScreenController.EXIT_REASON_DESKTOP_MODE);
-                } else {
-                    mDesktopTasksController.moveToFullscreen(mTaskId,
-                            DesktopModeTransitionSource.APP_HANDLE_MENU_BUTTON);
-                }
-            } else if (id == R.id.split_screen_button) {
-                decoration.closeHandleMenu();
-                // When the app enters split-select, the handle will no longer be visible, meaning
-                // we shouldn't receive input for it any longer.
-                decoration.disposeStatusBarInputLayer();
-                mDesktopTasksController.requestSplit(decoration.mTaskInfo);
-            } else if (id == R.id.open_in_browser_button) {
-                // TODO(b/346441962): let the decoration handle the click gesture and only call back
-                //  to the ViewModel via #setOpenInBrowserClickListener
-                decoration.onOpenInBrowserClick();
-            } else if (id == R.id.collapse_menu_button) {
-                decoration.closeHandleMenu();
             } else if (id == R.id.maximize_window) {
                 // TODO(b/346441962): move click detection logic into the decor's
                 //  {@link AppHeaderViewHolder}. Let it encapsulate the that and have it report
@@ -542,6 +609,7 @@
         @Override
         public boolean onTouch(View v, MotionEvent e) {
             final int id = v.getId();
+            final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(mTaskId);
             if ((e.getSource() & SOURCE_TOUCHSCREEN) == SOURCE_TOUCHSCREEN) {
                 mTouchscreenInUse = e.getActionMasked() != ACTION_UP
                         && e.getActionMasked() != ACTION_CANCEL;
@@ -551,7 +619,6 @@
                     && id != R.id.maximize_window) {
                 return false;
             }
-            final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(mTaskId);
             moveTaskToFront(decoration.mTaskInfo);
 
             final int actionMasked = e.getActionMasked();
@@ -580,7 +647,6 @@
                 mShouldPilferCaptionEvents = !(downInCustomizableCaptionRegion
                         && downInExclusionRegion && isTransparentCaption) && !isResizeEvent;
             }
-
             if (!mShouldPilferCaptionEvents) {
                 // The event will be handled by a window below or pilfered by resize handler.
                 return false;
@@ -594,9 +660,6 @@
                 // Gesture is finished, reset state.
                 mShouldPilferCaptionEvents = false;
             }
-            if (!mHasLongClicked && id != R.id.maximize_window) {
-                decoration.closeMaximizeMenuIfNeeded(e);
-            }
             return mDragDetector.onMotionEvent(v, e);
         }
 
@@ -669,12 +732,6 @@
                 View v, MotionEvent e) {
             final int id = v.getId();
             if (id == R.id.caption_handle) {
-                if (e.getActionMasked() == MotionEvent.ACTION_DOWN) {
-                    // Caption handle is located within the status bar region, meaning the
-                    // DisplayPolicy will attempt to transfer this input to status bar if it's
-                    // a swipe down. Pilfer here to keep the gesture in handle alone.
-                    mInputManager.pilferPointers(v.getViewRootImpl().getInputToken());
-                }
                 handleCaptionThroughStatusBar(e, decoration);
                 final boolean wasDragging = mIsDragging;
                 updateDragStatus(e.getActionMasked());
@@ -739,6 +796,9 @@
                             (int) (e.getRawY(dragPointerIdx) - e.getY(dragPointerIdx)));
                     final Rect newTaskBounds = mDragPositioningCallback.onDragPositioningEnd(
                             e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx));
+                    // Tasks bounds haven't actually been updated (only its leash), so pass to
+                    // DesktopTasksController to allow secondary transformations (i.e. snap resizing
+                    // or transforming to fullscreen) before setting new task bounds.
                     mDesktopTasksController.onDragPositioningEnd(taskInfo, position,
                             new PointF(e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx)),
                             newTaskBounds, decoration.calculateValidDragArea());
@@ -895,10 +955,6 @@
      *
      * @param relevantDecor the window decoration of the focused task's caption. This method only
      *                      handles motion events outside this caption's bounds.
-     * TODO(b/349135068): Outside-touch detection no longer works with the
-     *  enableAdditionalWindowsAboveStatusBar flag enabled. This
-     *  will be fixed once we can add FLAG_WATCH_OUTSIDE_TOUCH to relevant menus,
-     *  at which point, all EventReceivers and external touch logic should be removed.
      */
     private void handleEventOutsideCaption(MotionEvent ev,
             DesktopModeWindowDecoration relevantDecor) {
@@ -909,9 +965,8 @@
         relevantDecor.updateHoverAndPressStatus(ev);
         final int action = ev.getActionMasked();
         if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
-            if (!mTransitionDragActive) {
+            if (!mTransitionDragActive && !Flags.enableAdditionalWindowsAboveStatusBar()) {
                 relevantDecor.closeHandleMenuIfNeeded(ev);
-                relevantDecor.closeMaximizeMenuIfNeeded(ev);
             }
         }
     }
@@ -970,7 +1025,9 @@
                         relevantDecor.updateHoverAndPressStatus(ev);
                         DesktopModeVisualIndicator.IndicatorType resultType =
                                 mDesktopTasksController.onDragPositioningEndThroughStatusBar(
-                                new PointF(ev.getRawX(), ev.getRawY()), relevantDecor.mTaskInfo);
+                                        new PointF(ev.getRawX(), ev.getRawY()),
+                                        relevantDecor.mTaskInfo,
+                                        relevantDecor.mTaskSurface);
                         // If we are entering split select, handle will no longer be visible and
                         // should not be receiving any input.
                         if (resultType == TO_SPLIT_LEFT_INDICATOR
@@ -1010,7 +1067,7 @@
                                     mContext, mDragToDesktopAnimationStartBounds,
                                     relevantDecor.mTaskInfo, relevantDecor.mTaskSurface);
                             mDesktopTasksController.startDragToDesktop(relevantDecor.mTaskInfo,
-                                    mMoveToDesktopAnimator);
+                                    mMoveToDesktopAnimator, relevantDecor.mTaskSurface);
                         }
                     }
                     if (mMoveToDesktopAnimator != null) {
@@ -1122,7 +1179,7 @@
                 && taskInfo.isFocused) {
             return false;
         }
-        if (Flags.enableDesktopWindowingModalsPolicy()
+        if (DesktopModeFlags.MODALS_POLICY.isEnabled(mContext)
                 && isTopActivityExemptFromDesktopWindowing(mContext, taskInfo)) {
             return false;
         }
@@ -1146,6 +1203,7 @@
         final DesktopModeWindowDecoration windowDecoration =
                 mDesktopModeWindowDecorFactory.create(
                         mContext,
+                        mContext.createContextAsUser(UserHandle.of(taskInfo.userId), 0 /* flags */),
                         mDisplayController,
                         mSplitScreenController,
                         mTaskOrganizer,
@@ -1155,18 +1213,20 @@
                         mBgExecutor,
                         mMainChoreographer,
                         mSyncQueue,
-                        mRootTaskDisplayAreaOrganizer);
+                        mRootTaskDisplayAreaOrganizer,
+                        mGenericLinksParser,
+                        mMultiInstanceHelper);
         mWindowDecorByTaskId.put(taskInfo.taskId, windowDecoration);
 
         final DragPositioningCallback dragPositioningCallback;
         if (!DesktopModeStatus.isVeiledResizeEnabled()) {
-            dragPositioningCallback =  new FluidResizeTaskPositioner(
+            dragPositioningCallback = new FluidResizeTaskPositioner(
                     mTaskOrganizer, mTransitions, windowDecoration, mDisplayController,
                     mDragStartListener, mTransactionFactory);
             windowDecoration.setTaskDragResizer(
                     (FluidResizeTaskPositioner) dragPositioningCallback);
         } else {
-            dragPositioningCallback =  new VeiledResizeTaskPositioner(
+            dragPositioningCallback = new VeiledResizeTaskPositioner(
                     mTaskOrganizer, windowDecoration, mDisplayController,
                     mDragStartListener, mTransitions, mInteractionJankMonitor);
             windowDecoration.setTaskDragResizer(
@@ -1175,14 +1235,36 @@
 
         final DesktopModeTouchEventListener touchEventListener =
                 new DesktopModeTouchEventListener(taskInfo, dragPositioningCallback);
-        windowDecoration.setOnMaximizeOrRestoreClickListener(this::onMaximizeOrRestore);
-        windowDecoration.setOnLeftSnapClickListener((taskId, tag) -> {
-            onSnapResize(taskId, true /* isLeft */);
+        windowDecoration.setOnMaximizeOrRestoreClickListener(() -> {
+            onMaximizeOrRestore(taskInfo.taskId, "maximize_menu");
+            return Unit.INSTANCE;
         });
-        windowDecoration.setOnRightSnapClickListener((taskId, tag) -> {
-            onSnapResize(taskId, false /* isLeft */);
+        windowDecoration.setOnLeftSnapClickListener(() -> {
+            onSnapResize(taskInfo.taskId, true /* isLeft */);
+            return Unit.INSTANCE;
         });
-        windowDecoration.setOpenInBrowserClickListener(this::onOpenInBrowser);
+        windowDecoration.setOnRightSnapClickListener(() -> {
+            onSnapResize(taskInfo.taskId, false /* isLeft */);
+            return Unit.INSTANCE;
+        });
+        windowDecoration.setOnToDesktopClickListener(desktopModeTransitionSource -> {
+            onToDesktop(taskInfo.taskId, desktopModeTransitionSource);
+        });
+        windowDecoration.setOnToFullscreenClickListener(() -> {
+            onToFullscreen(taskInfo.taskId);
+            return Unit.INSTANCE;
+        });
+        windowDecoration.setOnToSplitScreenClickListener(() -> {
+            onToSplitScreen(taskInfo.taskId);
+            return Unit.INSTANCE;
+        });
+        windowDecoration.setOpenInBrowserClickListener((uri) -> {
+            onOpenInBrowser(taskInfo.taskId, uri);
+        });
+        windowDecoration.setOnNewWindowClickListener(() -> {
+            onNewWindow(taskInfo.taskId);
+            return Unit.INSTANCE;
+        });
         windowDecoration.setCaptionListeners(
                 touchEventListener, touchEventListener, touchEventListener, touchEventListener);
         windowDecoration.setExclusionRegionListener(mExclusionRegionListener);
@@ -1217,7 +1299,7 @@
         pw.println(innerPrefix + "mWindowDecorByTaskId=" + mWindowDecorByTaskId);
     }
 
-    private class DeskopModeOnTaskResizeAnimationListener
+    private class DesktopModeOnTaskResizeAnimationListener
             implements OnTaskResizeAnimationListener {
         @Override
         public void onAnimationStart(int taskId, Transaction t, Rect bounds) {
@@ -1246,7 +1328,6 @@
         }
     }
 
-
     private class DragStartListenerImpl
             implements DragPositioningCallbackUtility.DragStartListener {
         @Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
index 529def7..d70e225 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
@@ -19,17 +19,21 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.windowingModeToString;
+import static android.view.InsetsSource.FLAG_FORCE_CONSUMING;
+import static android.view.InsetsSource.FLAG_FORCE_CONSUMING_OPAQUE_CAPTION_BAR;
 import static android.view.MotionEvent.ACTION_CANCEL;
 import static android.view.MotionEvent.ACTION_DOWN;
 import static android.view.MotionEvent.ACTION_UP;
 
 import static com.android.launcher3.icons.BaseIconFactory.MODE_DEFAULT;
+import static com.android.wm.shell.common.desktopmode.DesktopModeTransitionSource.APP_HANDLE_MENU_BUTTON;
 import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
 import static com.android.wm.shell.windowdecor.DragResizeWindowGeometry.getFineResizeCornerSize;
 import static com.android.wm.shell.windowdecor.DragResizeWindowGeometry.getLargeResizeCornerSize;
 import static com.android.wm.shell.windowdecor.DragResizeWindowGeometry.getResizeEdgeHandleSize;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.SuppressLint;
 import android.app.ActivityManager;
 import android.app.WindowConfiguration.WindowingMode;
@@ -49,8 +53,8 @@
 import android.net.Uri;
 import android.os.Handler;
 import android.os.Trace;
-import android.util.Log;
 import android.util.Size;
+import android.util.Slog;
 import android.view.Choreographer;
 import android.view.MotionEvent;
 import android.view.SurfaceControl;
@@ -68,21 +72,26 @@
 import com.android.wm.shell.R;
 import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
 import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.apptoweb.AppToWebGenericLinksParser;
 import com.android.wm.shell.common.DisplayController;
 import com.android.wm.shell.common.DisplayLayout;
+import com.android.wm.shell.common.MultiInstanceHelper;
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.SyncTransactionQueue;
+import com.android.wm.shell.common.desktopmode.DesktopModeTransitionSource;
 import com.android.wm.shell.shared.annotations.ShellBackgroundThread;
+import com.android.wm.shell.shared.desktopmode.DesktopModeFlags;
 import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
 import com.android.wm.shell.splitscreen.SplitScreenController;
-import com.android.wm.shell.windowdecor.common.OnTaskActionClickListener;
 import com.android.wm.shell.windowdecor.extension.TaskInfoKt;
 import com.android.wm.shell.windowdecor.viewholder.AppHandleViewHolder;
 import com.android.wm.shell.windowdecor.viewholder.AppHeaderViewHolder;
 import com.android.wm.shell.windowdecor.viewholder.WindowDecorationViewHolder;
 
 import kotlin.Unit;
+import kotlin.jvm.functions.Function0;
 
+import java.util.function.Consumer;
 import java.util.function.Supplier;
 
 /**
@@ -109,9 +118,13 @@
     private View.OnTouchListener mOnCaptionTouchListener;
     private View.OnLongClickListener mOnCaptionLongClickListener;
     private View.OnGenericMotionListener mOnCaptionGenericMotionListener;
-    private OnTaskActionClickListener mOnMaximizeOrRestoreClickListener;
-    private OnTaskActionClickListener mOnLeftSnapClickListener;
-    private OnTaskActionClickListener mOnRightSnapClickListener;
+    private Function0<Unit> mOnMaximizeOrRestoreClickListener;
+    private Function0<Unit> mOnLeftSnapClickListener;
+    private Function0<Unit> mOnRightSnapClickListener;
+    private Consumer<DesktopModeTransitionSource> mOnToDesktopClickListener;
+    private Function0<Unit> mOnToFullscreenClickListener;
+    private Function0<Unit> mOnToSplitscreenClickListener;
+    private Function0<Unit> mOnNewWindowClickListener;
     private DragPositioningCallback mDragPositioningCallback;
     private DragResizeInputListener mDragResizeListener;
     private DragDetector mDragDetector;
@@ -133,12 +146,15 @@
 
     private CharSequence mAppName;
     private CapturedLink mCapturedLink;
-    private OpenInBrowserClickListener mOpenInBrowserClickListener;
+    private Uri mGenericLink;
+    private Consumer<Uri> mOpenInBrowserClickListener;
 
     private ExclusionRegionListener mExclusionRegionListener;
 
     private final RootTaskDisplayAreaOrganizer mRootTaskDisplayAreaOrganizer;
     private final MaximizeMenuFactory mMaximizeMenuFactory;
+    private final HandleMenuFactory mHandleMenuFactory;
+    private final AppToWebGenericLinksParser mGenericLinksParser;
 
     // Hover state for the maximize menu and button. The menu will remain open as long as either of
     // these is true. See {@link #onMaximizeHoverStateChanged()}.
@@ -149,29 +165,11 @@
     // to cancel the close.
     private final Runnable mCloseMaximizeWindowRunnable = this::closeMaximizeMenu;
     private final Runnable mCapturedLinkExpiredRunnable = this::onCapturedLinkExpired;
+    private final MultiInstanceHelper mMultiInstanceHelper;
 
     DesktopModeWindowDecoration(
             Context context,
-            DisplayController displayController,
-            SplitScreenController splitScreenController,
-            ShellTaskOrganizer taskOrganizer,
-            ActivityManager.RunningTaskInfo taskInfo,
-            SurfaceControl taskSurface,
-            Handler handler,
-            @ShellBackgroundThread ShellExecutor bgExecutor,
-            Choreographer choreographer,
-            SyncTransactionQueue syncQueue,
-            RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer) {
-        this (context, displayController, splitScreenController, taskOrganizer, taskInfo,
-                taskSurface, handler, bgExecutor, choreographer, syncQueue,
-                rootTaskDisplayAreaOrganizer, SurfaceControl.Builder::new,
-                SurfaceControl.Transaction::new,  WindowContainerTransaction::new,
-                SurfaceControl::new, new SurfaceControlViewHostFactory() {},
-                DefaultMaximizeMenuFactory.INSTANCE);
-    }
-
-    DesktopModeWindowDecoration(
-            Context context,
+            @NonNull Context userContext,
             DisplayController displayController,
             SplitScreenController splitScreenController,
             ShellTaskOrganizer taskOrganizer,
@@ -182,13 +180,40 @@
             Choreographer choreographer,
             SyncTransactionQueue syncQueue,
             RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
+            AppToWebGenericLinksParser genericLinksParser,
+            MultiInstanceHelper multiInstanceHelper) {
+        this (context, userContext, displayController, splitScreenController, taskOrganizer,
+                taskInfo, taskSurface, handler, bgExecutor, choreographer, syncQueue,
+                rootTaskDisplayAreaOrganizer, genericLinksParser, SurfaceControl.Builder::new,
+                SurfaceControl.Transaction::new,  WindowContainerTransaction::new,
+                SurfaceControl::new, new SurfaceControlViewHostFactory() {},
+                DefaultMaximizeMenuFactory.INSTANCE, DefaultHandleMenuFactory.INSTANCE,
+                multiInstanceHelper);
+    }
+
+    DesktopModeWindowDecoration(
+            Context context,
+            @NonNull Context userContext,
+            DisplayController displayController,
+            SplitScreenController splitScreenController,
+            ShellTaskOrganizer taskOrganizer,
+            ActivityManager.RunningTaskInfo taskInfo,
+            SurfaceControl taskSurface,
+            Handler handler,
+            @ShellBackgroundThread ShellExecutor bgExecutor,
+            Choreographer choreographer,
+            SyncTransactionQueue syncQueue,
+            RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
+            AppToWebGenericLinksParser genericLinksParser,
             Supplier<SurfaceControl.Builder> surfaceControlBuilderSupplier,
             Supplier<SurfaceControl.Transaction> surfaceControlTransactionSupplier,
             Supplier<WindowContainerTransaction> windowContainerTransactionSupplier,
             Supplier<SurfaceControl> surfaceControlSupplier,
             SurfaceControlViewHostFactory surfaceControlViewHostFactory,
-            MaximizeMenuFactory maximizeMenuFactory) {
-        super(context, displayController, taskOrganizer, taskInfo, taskSurface,
+            MaximizeMenuFactory maximizeMenuFactory,
+            HandleMenuFactory handleMenuFactory,
+            MultiInstanceHelper multiInstanceHelper) {
+        super(context, userContext, displayController, taskOrganizer, taskInfo, taskSurface,
                 surfaceControlBuilderSupplier, surfaceControlTransactionSupplier,
                 windowContainerTransactionSupplier, surfaceControlSupplier,
                 surfaceControlViewHostFactory);
@@ -198,7 +223,10 @@
         mChoreographer = choreographer;
         mSyncQueue = syncQueue;
         mRootTaskDisplayAreaOrganizer = rootTaskDisplayAreaOrganizer;
+        mGenericLinksParser = genericLinksParser;
         mMaximizeMenuFactory = maximizeMenuFactory;
+        mHandleMenuFactory = handleMenuFactory;
+        mMultiInstanceHelper = multiInstanceHelper;
     }
 
     /**
@@ -207,24 +235,42 @@
      * TODO(b/346441962): hook this up to double-tap and the header's maximize button, instead of
      *  having the ViewModel deal with parsing motion events.
      */
-    void setOnMaximizeOrRestoreClickListener(OnTaskActionClickListener listener) {
+    void setOnMaximizeOrRestoreClickListener(Function0<Unit> listener) {
         mOnMaximizeOrRestoreClickListener = listener;
     }
 
-    /**
-     * Register a listener to be called back when one of the tasks snap-left action is triggered.
-     */
-    void setOnLeftSnapClickListener(OnTaskActionClickListener listener) {
+    /** Registers a listener to be called when the decoration's snap-left action is triggered.*/
+    void setOnLeftSnapClickListener(Function0<Unit> listener) {
         mOnLeftSnapClickListener = listener;
     }
 
-    /**
-     * Register a listener to be called back when one of the tasks' snap-right action is triggered.
-     */
-    void setOnRightSnapClickListener(OnTaskActionClickListener listener) {
+    /** Registers a listener to be called when the decoration's snap-right action is triggered. */
+    void setOnRightSnapClickListener(Function0<Unit> listener) {
         mOnRightSnapClickListener = listener;
     }
 
+    /** Registers a listener to be called when the decoration's to-desktop action is triggered. */
+    void setOnToDesktopClickListener(Consumer<DesktopModeTransitionSource> listener) {
+        mOnToDesktopClickListener = listener;
+    }
+
+    /**
+     * Registers a listener to be called when the decoration's to-fullscreen action is triggered.
+     */
+    void setOnToFullscreenClickListener(Function0<Unit> listener) {
+        mOnToFullscreenClickListener = listener;
+    }
+
+    /** Registers a listener to be called when the decoration's to-split action is triggered. */
+    void setOnToSplitScreenClickListener(Function0<Unit> listener) {
+        mOnToSplitscreenClickListener = listener;
+    }
+
+    /** Registers a listener to be called when the decoration's new window action is triggered. */
+    void setOnNewWindowClickListener(Function0<Unit> listener) {
+        mOnNewWindowClickListener = listener;
+    }
+
     void setCaptionListeners(
             View.OnClickListener onCaptionButtonClickListener,
             View.OnTouchListener onCaptionTouchListener,
@@ -249,7 +295,7 @@
         mDragDetector.setTouchSlop(ViewConfiguration.get(mContext).getScaledTouchSlop());
     }
 
-    void setOpenInBrowserClickListener(OpenInBrowserClickListener listener) {
+    void setOpenInBrowserClickListener(Consumer<Uri> listener) {
         mOpenInBrowserClickListener = listener;
     }
 
@@ -266,8 +312,7 @@
         // transaction (that applies task crop) is synced with the buffer transaction (that draws
         // the View). Both will be shown on screen at the same, whereas applying them independently
         // causes flickering. See b/270202228.
-        final boolean applyTransactionOnDraw =
-                taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM;
+        final boolean applyTransactionOnDraw = taskInfo.isFreeform();
         relayout(taskInfo, t, t, applyTransactionOnDraw, shouldSetTaskPositionAndCrop);
         if (!applyTransactionOnDraw) {
             t.apply();
@@ -278,7 +323,7 @@
             SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT,
             boolean applyStartTransactionOnDraw, boolean shouldSetTaskPositionAndCrop) {
         Trace.beginSection("DesktopModeWindowDecoration#relayout");
-        if (taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) {
+        if (taskInfo.isFreeform()) {
             // The Task is in Freeform mode -> show its header in sync since it's an integral part
             // of the window itself - a delayed header might cause bad UX.
             relayoutInSync(taskInfo, startT, finishT, applyStartTransactionOnDraw,
@@ -424,10 +469,14 @@
         }
     }
 
-    void onOpenInBrowserClick() {
-        if (mOpenInBrowserClickListener == null || mCapturedLink == null) return;
-        mOpenInBrowserClickListener.onClick(this, mCapturedLink.mUri);
-        onCapturedLinkExpired();
+    @Nullable
+    private Uri getBrowserLink() {
+        // If the captured link is available and has not expired, return the captured link.
+        // Otherwise, return the generic link which is set to null if a generic link is unavailable.
+        if (mCapturedLink != null && !mCapturedLink.mExpired) {
+            return mCapturedLink.mUri;
+        }
+        return mGenericLink;
     }
 
     private void updateDragResizeListener(SurfaceControl oldDecorationSurface) {
@@ -465,17 +514,16 @@
         final Resources res = mResult.mRootView.getResources();
         if (mDragResizeListener.setGeometry(
                 new DragResizeWindowGeometry(mRelayoutParams.mCornerRadius,
-                        new Size(mResult.mWidth, mResult.mHeight), getResizeEdgeHandleSize(res),
-                        getFineResizeCornerSize(res), getLargeResizeCornerSize(res)), touchSlop)
+                        new Size(mResult.mWidth, mResult.mHeight),
+                        getResizeEdgeHandleSize(mContext, res), getFineResizeCornerSize(res),
+                        getLargeResizeCornerSize(res)), touchSlop)
                 || !mTaskInfo.positionInParent.equals(mPositionInParent)) {
             updateExclusionRegion();
         }
     }
 
     private static boolean isDragResizable(ActivityManager.RunningTaskInfo taskInfo) {
-        final boolean isFreeform =
-                taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM;
-        return isFreeform && taskInfo.isResizeable;
+        return taskInfo.isFreeform() && taskInfo.isResizeable;
     }
 
     private void updateMaximizeMenu(SurfaceControl.Transaction startT) {
@@ -520,11 +568,7 @@
             return new AppHandleViewHolder(
                     mResult.mRootView,
                     mOnCaptionTouchListener,
-                    mOnCaptionButtonClickListener,
-                    (v, event) -> {
-                        updateHoverAndPressStatus(event);
-                        return true;
-                    }
+                    mOnCaptionButtonClickListener
             );
         } else if (mRelayoutParams.mLayoutResId
                 == R.layout.desktop_mode_app_header) {
@@ -574,6 +618,17 @@
                 // through to the windows below so that the app can respond to input events on
                 // their custom content.
                 relayoutParams.mInputFeatures |= WindowManager.LayoutParams.INPUT_FEATURE_SPY;
+            } else {
+                if (Flags.enableCaptionCompatInsetForceConsumption()) {
+                    // Force-consume the caption bar insets when the app tries to hide the caption.
+                    // This improves app compatibility of immersive apps.
+                    relayoutParams.mInsetSourceFlags |= FLAG_FORCE_CONSUMING;
+                }
+            }
+            if (Flags.enableCaptionCompatInsetForceConsumptionAlways()) {
+                // Always force-consume the caption bar insets for maximum app compatibility,
+                // including non-immersive apps that just don't handle caption insets properly.
+                relayoutParams.mInsetSourceFlags |= FLAG_FORCE_CONSUMING_OPAQUE_CAPTION_BAR;
             }
             // Report occluding elements as bounding rects to the insets system so that apps can
             // draw in the empty space in the center:
@@ -589,9 +644,11 @@
             controlsElement.mWidthResId = R.dimen.desktop_mode_customizable_caption_margin_end;
             controlsElement.mAlignment = RelayoutParams.OccludingCaptionElement.Alignment.END;
             relayoutParams.mOccludingCaptionElements.add(controlsElement);
-        } else if (isAppHandle) {
+        } else if (isAppHandle && !Flags.enableAdditionalWindowsAboveStatusBar()) {
             // The focused decor (fullscreen/split) does not need to handle input because input in
             // the App Handle is handled by the InputMonitor in DesktopModeWindowDecorViewModel.
+            // Note: This does not apply with the above flag enabled as the status bar input layer
+            // will forward events to the handle directly.
             relayoutParams.mInputFeatures
                     |= WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL;
         }
@@ -610,7 +667,7 @@
         // TODO(b/301119301): consider moving the config data needed for diffs to relayout params
         // instead of using a whole Configuration as a parameter.
         final Configuration windowDecorConfig = new Configuration();
-        if (Flags.enableAppHeaderWithTaskDensity() && isAppHeader) {
+        if (DesktopModeFlags.APP_HEADER_WITH_TASK_DENSITY.isEnabled(context) && isAppHeader) {
             // Should match the density of the task. The task may have had its density overridden
             // to be different that SysUI's.
             windowDecorConfig.setTo(taskInfo.configuration);
@@ -700,10 +757,10 @@
             }
             final ComponentName baseActivity = mTaskInfo.baseActivity;
             if (baseActivity == null) {
-                Log.e(TAG, "Base activity component not found in task");
+                Slog.e(TAG, "Base activity component not found in task");
                 return;
             }
-            final PackageManager pm = mContext.getApplicationContext().getPackageManager();
+            final PackageManager pm = mUserContext.getPackageManager();
             final ActivityInfo activityInfo = pm.getActivityInfo(baseActivity, 0 /* flags */);
             final IconProvider provider = new IconProvider(mContext);
             final Drawable appIconDrawable = provider.getIcon(activityInfo);
@@ -719,7 +776,7 @@
             final ApplicationInfo applicationInfo = activityInfo.applicationInfo;
             mAppName = pm.getApplicationLabel(applicationInfo);
         } catch (PackageManager.NameNotFoundException e) {
-            Log.e(TAG, "Base activity's component name cannot be found on the system");
+            Slog.e(TAG, "Base activity's component name cannot be found on the system", e);
         } finally {
             Trace.endSection();
         }
@@ -870,6 +927,10 @@
                     mIsMaximizeMenuHovered = hovered;
                     onMaximizeHoverStateChanged();
                     return null;
+                },
+                () -> {
+                    closeMaximizeMenu();
+                    return null;
                 }
         );
     }
@@ -914,28 +975,56 @@
      */
     void createHandleMenu(SplitScreenController splitScreenController) {
         loadAppInfoIfNeeded();
-        mHandleMenu = new HandleMenu(
+        updateGenericLink();
+        mHandleMenu = mHandleMenuFactory.create(
                 this,
                 mRelayoutParams.mLayoutResId,
-                mOnCaptionButtonClickListener,
-                mOnCaptionTouchListener,
                 mAppIconBitmap,
                 mAppName,
-                mDisplayController,
                 splitScreenController,
                 DesktopModeStatus.canEnterDesktopMode(mContext),
-                browserLinkAvailable(),
+                Flags.enableDesktopWindowingMultiInstanceFeatures()
+                        && mMultiInstanceHelper
+                        .supportsMultiInstanceSplit(mTaskInfo.baseActivity),
+                getBrowserLink(),
                 mResult.mCaptionWidth,
                 mResult.mCaptionHeight,
                 mResult.mCaptionX
         );
         mWindowDecorViewHolder.onHandleMenuOpened();
-        mHandleMenu.show();
+        mHandleMenu.show(
+                /* onToDesktopClickListener= */ () -> {
+                    mOnToDesktopClickListener.accept(APP_HANDLE_MENU_BUTTON);
+                    return Unit.INSTANCE;
+                },
+                /* onToFullscreenClickListener= */ mOnToFullscreenClickListener,
+                /* onToSplitScreenClickListener= */ mOnToSplitscreenClickListener,
+                /* onNewWindowClickListener= */ mOnNewWindowClickListener,
+                /* openInBrowserClickListener= */ (uri) -> {
+                    mOpenInBrowserClickListener.accept(uri);
+                    onCapturedLinkExpired();
+                    return Unit.INSTANCE;
+                },
+                /* onCloseMenuClickListener= */ () -> {
+                    closeHandleMenu();
+                    return Unit.INSTANCE;
+                },
+                /* onOutsideTouchListener= */ () -> {
+                    closeHandleMenu();
+                    return Unit.INSTANCE;
+                }
+        );
     }
 
-    @VisibleForTesting
-    boolean browserLinkAvailable() {
-        return mCapturedLink != null && !mCapturedLink.mExpired;
+    private void updateGenericLink() {
+        final ComponentName baseActivity = mTaskInfo.baseActivity;
+        if (baseActivity == null) {
+            return;
+        }
+
+        final String genericLink =
+                mGenericLinksParser.getGenericLink(baseActivity.getPackageName());
+        mGenericLink = genericLink == null ? null : Uri.parse(genericLink);
     }
 
     /**
@@ -1073,8 +1162,13 @@
             handle.performClick();
         }
         if (isHandleMenuActive()) {
-            mHandleMenu.checkMotionEvent(ev);
-            closeHandleMenuIfNeeded(ev);
+            // If the whole handle menu can be touched directly, rely on FLAG_WATCH_OUTSIDE_TOUCH.
+            // This is for the case that some of the handle menu is underneath the status bar.
+            if (isAppHandle(mWindowDecorViewHolder)
+                    && !Flags.enableAdditionalWindowsAboveStatusBar()) {
+                mHandleMenu.checkMotionEvent(ev);
+                closeHandleMenuIfNeeded(ev);
+            }
         }
     }
 
@@ -1210,6 +1304,7 @@
 
         DesktopModeWindowDecoration create(
                 Context context,
+                @NonNull Context userContext,
                 DisplayController displayController,
                 SplitScreenController splitScreenController,
                 ShellTaskOrganizer taskOrganizer,
@@ -1219,9 +1314,12 @@
                 @ShellBackgroundThread ShellExecutor bgExecutor,
                 Choreographer choreographer,
                 SyncTransactionQueue syncQueue,
-                RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer) {
+                RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
+                AppToWebGenericLinksParser genericLinksParser,
+                MultiInstanceHelper multiInstanceHelper) {
             return new DesktopModeWindowDecoration(
                     context,
+                    userContext,
                     displayController,
                     splitScreenController,
                     taskOrganizer,
@@ -1231,7 +1329,9 @@
                     bgExecutor,
                     choreographer,
                     syncQueue,
-                    rootTaskDisplayAreaOrganizer);
+                    rootTaskDisplayAreaOrganizer,
+                    genericLinksParser,
+                    multiInstanceHelper);
         }
     }
 
@@ -1252,14 +1352,6 @@
         }
     }
 
-
-    /** Listener for the handle menu's "Open in browser" button */
-    interface OpenInBrowserClickListener {
-
-        /** Inform the implementing class that the "Open in browser" button has been clicked */
-        void onClick(DesktopModeWindowDecoration decoration, Uri uri);
-    }
-
     interface ExclusionRegionListener {
         /** Inform the implementing class of this task's change in region resize handles */
         void onExclusionRegionChanged(int taskId, Region region);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragDetector.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragDetector.java
index da26898..3fd3656 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragDetector.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragDetector.java
@@ -19,7 +19,11 @@
 import static android.view.InputDevice.SOURCE_TOUCHSCREEN;
 import static android.view.MotionEvent.ACTION_CANCEL;
 import static android.view.MotionEvent.ACTION_DOWN;
+import static android.view.MotionEvent.ACTION_HOVER_ENTER;
+import static android.view.MotionEvent.ACTION_HOVER_EXIT;
+import static android.view.MotionEvent.ACTION_HOVER_MOVE;
 import static android.view.MotionEvent.ACTION_MOVE;
+import static android.view.MotionEvent.ACTION_POINTER_UP;
 import static android.view.MotionEvent.ACTION_UP;
 
 import android.graphics.PointF;
@@ -43,7 +47,7 @@
     private final PointF mInputDownPoint = new PointF();
     private int mTouchSlop;
     private boolean mIsDragEvent;
-    private int mDragPointerId;
+    private int mDragPointerId = -1;
 
     private boolean mResultOfDownAction;
 
@@ -67,7 +71,7 @@
      *
      * @return the result returned by {@link #mEventHandler}, or the result when
      * {@link #mEventHandler} handles the previous down event if the event shouldn't be passed
-    */
+     */
     boolean onMotionEvent(View v, MotionEvent ev) {
         final boolean isTouchScreen =
                 (ev.getSource() & SOURCE_TOUCHSCREEN) == SOURCE_TOUCHSCREEN;
@@ -86,10 +90,14 @@
                 return mResultOfDownAction;
             }
             case ACTION_MOVE: {
-                if (ev.findPointerIndex(mDragPointerId) == -1) {
-                    mDragPointerId = ev.getPointerId(0);
+                if (mDragPointerId == -1) {
+                    // The primary pointer was lifted, ignore the rest of the gesture.
+                    return mResultOfDownAction;
                 }
                 final int dragPointerIndex = ev.findPointerIndex(mDragPointerId);
+                if (dragPointerIndex == -1) {
+                    throw new IllegalStateException("Failed to find primary pointer!");
+                }
                 if (!mIsDragEvent) {
                     float dx = ev.getRawX(dragPointerIndex) - mInputDownPoint.x;
                     float dy = ev.getRawY(dragPointerIndex) - mInputDownPoint.y;
@@ -99,22 +107,52 @@
                 }
                 // The event handler should only be notified about 'move' events if a drag has been
                 // detected.
-                if (mIsDragEvent) {
-                    return mEventHandler.handleMotionEvent(v, ev);
-                } else {
+                if (!mIsDragEvent) {
                     return mResultOfDownAction;
                 }
+                return mEventHandler.handleMotionEvent(v,
+                        getSinglePointerEvent(ev, mDragPointerId));
+            }
+            case ACTION_HOVER_ENTER:
+            case ACTION_HOVER_MOVE:
+            case ACTION_HOVER_EXIT: {
+                return mEventHandler.handleMotionEvent(v,
+                        getSinglePointerEvent(ev, mDragPointerId));
+            }
+            case ACTION_POINTER_UP: {
+                if (mDragPointerId == -1) {
+                    // The primary pointer was lifted, ignore the rest of the gesture.
+                    return mResultOfDownAction;
+                }
+                if (mDragPointerId != ev.getPointerId(ev.getActionIndex())) {
+                    // Ignore a secondary pointer being lifted.
+                    return mResultOfDownAction;
+                }
+                // The primary pointer is being lifted.
+                final int dragPointerId = mDragPointerId;
+                mDragPointerId = -1;
+                return mEventHandler.handleMotionEvent(v, getSinglePointerEvent(ev, dragPointerId));
             }
             case ACTION_UP:
             case ACTION_CANCEL: {
+                final int dragPointerId = mDragPointerId;
                 resetState();
-                return mEventHandler.handleMotionEvent(v, ev);
+                if (dragPointerId == -1) {
+                    // The primary pointer was lifted, ignore the rest of the gesture.
+                    return mResultOfDownAction;
+                }
+                return mEventHandler.handleMotionEvent(v, getSinglePointerEvent(ev, dragPointerId));
             }
             default:
-                return mEventHandler.handleMotionEvent(v, ev);
+                // Ignore other events.
+                return mResultOfDownAction;
         }
     }
 
+    private static MotionEvent getSinglePointerEvent(MotionEvent ev, int pointerId) {
+        return ev.getPointerCount() > 1 ? ev.split(1 << pointerId) : ev;
+    }
+
     void setTouchSlop(int touchSlop) {
         mTouchSlop = touchSlop;
     }
@@ -129,4 +167,4 @@
     interface MotionEventHandler {
         boolean handleMotionEvent(@Nullable View v, MotionEvent ev);
     }
-}
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtility.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtility.java
index 2fd3eaa..0f2de70 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtility.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtility.java
@@ -30,9 +30,9 @@
 
 import androidx.annotation.NonNull;
 
-import com.android.window.flags.Flags;
 import com.android.wm.shell.R;
 import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.shared.desktopmode.DesktopModeFlags;
 import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
 
 /**
@@ -245,7 +245,7 @@
 
     private static boolean isSizeConstraintForDesktopModeEnabled(Context context) {
         return DesktopModeStatus.canEnterDesktopMode(context)
-                && Flags.enableDesktopWindowingSizeConstraints();
+                && DesktopModeFlags.SIZE_CONSTRAINTS.isEnabled(context);
     }
 
     interface DragStartListener {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
index 32df8b3..1729548 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
@@ -81,6 +81,7 @@
     private final InputChannel mInputChannel;
     private final TaskResizeInputEventReceiver mInputEventReceiver;
 
+    private final Context mContext;
     private final SurfaceControl mInputSinkSurface;
     private final IBinder mSinkClientToken;
     private final InputChannel mSinkInputChannel;
@@ -97,6 +98,7 @@
             Supplier<SurfaceControl.Builder> surfaceControlBuilderSupplier,
             Supplier<SurfaceControl.Transaction> surfaceControlTransactionSupplier,
             DisplayController displayController) {
+        mContext = context;
         mSurfaceControlTransactionSupplier = surfaceControlTransactionSupplier;
         mDisplayId = displayId;
         mDecorationSurface = decorationSurface;
@@ -180,7 +182,7 @@
 
         mTouchRegion.setEmpty();
         // Apply the geometry to the touch region.
-        geometry.union(mTouchRegion);
+        geometry.union(mContext, mTouchRegion);
         mInputEventReceiver.setGeometry(geometry);
         mInputEventReceiver.setTouchRegion(mTouchRegion);
 
@@ -354,7 +356,7 @@
          */
         @NonNull Region getCornersRegion() {
             Region region = new Region();
-            mDragResizeWindowGeometry.union(region);
+            mDragResizeWindowGeometry.union(mContext, region);
             return region;
         }
 
@@ -395,7 +397,7 @@
             // Touch events are tracked in four corners. Other events are tracked in resize edges.
             switch (e.getActionMasked()) {
                 case MotionEvent.ACTION_DOWN: {
-                    mShouldHandleEvents = mDragResizeWindowGeometry.shouldHandleEvent(e,
+                    mShouldHandleEvents = mDragResizeWindowGeometry.shouldHandleEvent(mContext, e,
                             new Point() /* offset */);
                     if (mShouldHandleEvents) {
                         // Save the id of the pointer for this drag interaction; we will use the
@@ -405,8 +407,9 @@
                         float y = e.getY(0);
                         float rawX = e.getRawX(0);
                         float rawY = e.getRawY(0);
-                        final int ctrlType = mDragResizeWindowGeometry.calculateCtrlType(
-                                isEventFromTouchscreen(e), isEdgeResizePermitted(e), x, y);
+                        final int ctrlType = mDragResizeWindowGeometry.calculateCtrlType(mContext,
+                                isEventFromTouchscreen(e), isEdgeResizePermitted(mContext, e), x,
+                                y);
                         ProtoLog.d(WM_SHELL_DESKTOP_MODE,
                                 "%s: Handling action down, update ctrlType to %d", TAG, ctrlType);
                         mDragStartTaskBounds = mCallback.onDragPositioningStart(ctrlType,
@@ -496,7 +499,7 @@
             // Since we are handling cursor, we know that this is not a touchscreen event, and
             // that edge resizing should always be allowed.
             @DragPositioningCallback.CtrlType int ctrlType =
-                    mDragResizeWindowGeometry.calculateCtrlType(/* isTouchscreen= */
+                    mDragResizeWindowGeometry.calculateCtrlType(mContext, /* isTouchscreen= */
                             false, /* isEdgeResizePermitted= */ true, x, y);
 
             int cursorType = PointerIcon.TYPE_DEFAULT;
@@ -537,7 +540,7 @@
         }
 
         private boolean shouldHandleEvent(MotionEvent e, Point offset) {
-            return mDragResizeWindowGeometry.shouldHandleEvent(e, offset);
+            return mDragResizeWindowGeometry.shouldHandleEvent(mContext, e, offset);
         }
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeWindowGeometry.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeWindowGeometry.java
index ba5f079..014d61d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeWindowGeometry.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeWindowGeometry.java
@@ -18,7 +18,7 @@
 
 import static android.view.InputDevice.SOURCE_TOUCHSCREEN;
 
-import static com.android.window.flags.Flags.enableWindowingEdgeDragResize;
+import static com.android.wm.shell.shared.desktopmode.DesktopModeFlags.EDGE_DRAG_RESIZE;
 import static com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_BOTTOM;
 import static com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_LEFT;
 import static com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_RIGHT;
@@ -26,6 +26,7 @@
 import static com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_UNDEFINED;
 
 import android.annotation.NonNull;
+import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Point;
 import android.graphics.Rect;
@@ -69,8 +70,8 @@
     /**
      * Returns the resource value to use for the resize handle on the edge of the window.
      */
-    static int getResizeEdgeHandleSize(@NonNull Resources res) {
-        return enableWindowingEdgeDragResize()
+    static int getResizeEdgeHandleSize(@NonNull Context context, @NonNull Resources res) {
+        return EDGE_DRAG_RESIZE.isEnabled(context)
                 ? res.getDimensionPixelSize(R.dimen.desktop_mode_edge_handle)
                 : res.getDimensionPixelSize(R.dimen.freeform_resize_handle);
     }
@@ -103,11 +104,11 @@
      * Returns the union of all regions that can be touched for drag resizing; the corners window
      * and window edges.
      */
-    void union(@NonNull Region region) {
+    void union(@NonNull Context context, @NonNull Region region) {
         // Apply the edge resize regions.
         mTaskEdges.union(region);
 
-        if (enableWindowingEdgeDragResize()) {
+        if (EDGE_DRAG_RESIZE.isEnabled(context)) {
             // Apply the corners as well for the larger corners, to ensure we capture all possible
             // touches.
             mLargeTaskCorners.union(region);
@@ -120,11 +121,12 @@
     /**
      * Returns if this MotionEvent should be handled, based on its source and position.
      */
-    boolean shouldHandleEvent(@NonNull MotionEvent e, @NonNull Point offset) {
+    boolean shouldHandleEvent(@NonNull Context context, @NonNull MotionEvent e,
+            @NonNull Point offset) {
         final float x = e.getX(0) + offset.x;
         final float y = e.getY(0) + offset.y;
 
-        if (enableWindowingEdgeDragResize()) {
+        if (EDGE_DRAG_RESIZE.isEnabled(context)) {
             // First check if touch falls within a corner.
             // Large corner bounds are used for course input like touch, otherwise fine bounds.
             boolean result = isEventFromTouchscreen(e)
@@ -132,7 +134,7 @@
                     : isInCornerBounds(mFineTaskCorners, x, y);
             // Check if touch falls within the edge resize handle. Limit edge resizing to stylus and
             // mouse input.
-            if (!result && isEdgeResizePermitted(e)) {
+            if (!result && isEdgeResizePermitted(context, e)) {
                 result = isInEdgeResizeBounds(x, y);
             }
             return result;
@@ -148,8 +150,8 @@
         return (e.getSource() & SOURCE_TOUCHSCREEN) == SOURCE_TOUCHSCREEN;
     }
 
-    static boolean isEdgeResizePermitted(@NonNull MotionEvent e) {
-        if (enableWindowingEdgeDragResize()) {
+    static boolean isEdgeResizePermitted(@NonNull Context context, @NonNull MotionEvent e) {
+        if (EDGE_DRAG_RESIZE.isEnabled(context)) {
             return e.getToolType(0) == MotionEvent.TOOL_TYPE_STYLUS
                     || e.getToolType(0) == MotionEvent.TOOL_TYPE_MOUSE;
         } else {
@@ -174,8 +176,9 @@
      *                              resize region.
      */
     @DragPositioningCallback.CtrlType
-    int calculateCtrlType(boolean isTouchscreen, boolean isEdgeResizePermitted, float x, float y) {
-        if (enableWindowingEdgeDragResize()) {
+    int calculateCtrlType(@NonNull Context context, boolean isTouchscreen,
+            boolean isEdgeResizePermitted, float x, float y) {
+        if (EDGE_DRAG_RESIZE.isEnabled(context)) {
             // First check if touch falls within a corner.
             // Large corner bounds are used for course input like touch, otherwise fine bounds.
             int ctrlType = isTouchscreen
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositioner.java
index 76096b0..e2d42b2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositioner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositioner.java
@@ -152,11 +152,8 @@
             }
             mDragResizeEndTransition = mTransitions.startTransition(TRANSIT_CHANGE, wct, this);
         } else if (mCtrlType == CTRL_TYPE_UNDEFINED) {
-            final WindowContainerTransaction wct = new WindowContainerTransaction();
             DragPositioningCallbackUtility.updateTaskBounds(mRepositionTaskBounds,
                     mTaskBoundsAtDragStart, mRepositionStartPoint, x, y);
-            wct.setBounds(mWindowDecoration.mTaskInfo.token, mRepositionTaskBounds);
-            mTransitions.startTransition(TRANSIT_CHANGE, wct, this);
         }
 
         mTaskBoundsAtDragStart.setEmpty();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt
index e174e83..b348d65 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt
@@ -17,7 +17,8 @@
 
 import android.annotation.ColorInt
 import android.annotation.DimenRes
-import android.app.ActivityManager
+import android.annotation.SuppressLint
+import android.app.ActivityManager.RunningTaskInfo
 import android.content.Context
 import android.content.res.ColorStateList
 import android.content.res.Resources
@@ -27,9 +28,13 @@
 import android.graphics.Point
 import android.graphics.PointF
 import android.graphics.Rect
+import android.net.Uri
+import android.view.LayoutInflater
 import android.view.MotionEvent
+import android.view.MotionEvent.ACTION_OUTSIDE
 import android.view.SurfaceControl
 import android.view.View
+import android.view.WindowManager
 import android.widget.Button
 import android.widget.ImageButton
 import android.widget.ImageView
@@ -40,7 +45,6 @@
 import androidx.core.view.isGone
 import com.android.window.flags.Flags
 import com.android.wm.shell.R
-import com.android.wm.shell.common.DisplayController
 import com.android.wm.shell.common.split.SplitScreenConstants
 import com.android.wm.shell.splitscreen.SplitScreenController
 import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalSystemViewContainer
@@ -61,21 +65,18 @@
 class HandleMenu(
     private val parentDecor: DesktopModeWindowDecoration,
     private val layoutResId: Int,
-    private val onClickListener: View.OnClickListener?,
-    private val onTouchListener: View.OnTouchListener?,
     private val appIconBitmap: Bitmap?,
     private val appName: CharSequence?,
-    private val displayController: DisplayController,
     private val splitScreenController: SplitScreenController,
     private val shouldShowWindowingPill: Boolean,
-    private val shouldShowBrowserPill: Boolean,
+    private val shouldShowNewWindowButton: Boolean,
+    private val openInBrowserLink: Uri?,
     private val captionWidth: Int,
     private val captionHeight: Int,
     captionX: Int
 ) {
     private val context: Context = parentDecor.mDecorWindowContext
-    private val taskInfo: ActivityManager.RunningTaskInfo = parentDecor.mTaskInfo
-    private val decorThemeUtil = DecorThemeUtil(context)
+    private val taskInfo: RunningTaskInfo = parentDecor.mTaskInfo
 
     private val isViewAboveStatusBar: Boolean
         get() = (Flags.enableAdditionalWindowsAboveStatusBar() && !taskInfo.isFreeform)
@@ -91,10 +92,9 @@
     private val marginMenuStart = loadDimensionPixelSize(
         R.dimen.desktop_mode_handle_menu_margin_start)
 
-    private var handleMenuAnimator: HandleMenuAnimator? = null
-
     @VisibleForTesting
     var handleMenuViewContainer: AdditionalViewContainer? = null
+    private var handleMenuView: HandleMenuView? = null
 
     // Position of the handle menu used for laying out the handle view.
     @VisibleForTesting
@@ -106,159 +106,95 @@
     // those as well.
     private val globalMenuPosition: Point = Point()
 
+    private val shouldShowBrowserPill: Boolean
+        get() = openInBrowserLink != null
+
     init {
         updateHandleMenuPillPositions(captionX)
     }
 
-    fun show() {
+    fun show(
+        onToDesktopClickListener: () -> Unit,
+        onToFullscreenClickListener: () -> Unit,
+        onToSplitScreenClickListener: () -> Unit,
+        onNewWindowClickListener: () -> Unit,
+        openInBrowserClickListener: (Uri) -> Unit,
+        onCloseMenuClickListener: () -> Unit,
+        onOutsideTouchListener: () -> Unit,
+    ) {
         val ssg = SurfaceSyncGroup(TAG)
         val t = SurfaceControl.Transaction()
 
-        createHandleMenuViewContainer(t, ssg)
+        createHandleMenu(
+            t = t,
+            ssg = ssg,
+            onToDesktopClickListener = onToDesktopClickListener,
+            onToFullscreenClickListener = onToFullscreenClickListener,
+            onToSplitScreenClickListener = onToSplitScreenClickListener,
+            onNewWindowClickListener = onNewWindowClickListener,
+            openInBrowserClickListener = openInBrowserClickListener,
+            onCloseMenuClickListener = onCloseMenuClickListener,
+            onOutsideTouchListener = onOutsideTouchListener,
+        )
         ssg.addTransaction(t)
         ssg.markSyncReady()
-        setupHandleMenu()
-        animateHandleMenu()
+
+        handleMenuView?.animateOpenMenu()
     }
 
-    private fun createHandleMenuViewContainer(
+    private fun createHandleMenu(
         t: SurfaceControl.Transaction,
-        ssg: SurfaceSyncGroup
+        ssg: SurfaceSyncGroup,
+        onToDesktopClickListener: () -> Unit,
+        onToFullscreenClickListener: () -> Unit,
+        onToSplitScreenClickListener: () -> Unit,
+        onNewWindowClickListener: () -> Unit,
+        openInBrowserClickListener: (Uri) -> Unit,
+        onCloseMenuClickListener: () -> Unit,
+        onOutsideTouchListener: () -> Unit
     ) {
+        val handleMenuView = HandleMenuView(
+            context = context,
+            menuWidth = menuWidth,
+            captionHeight = captionHeight,
+            shouldShowWindowingPill = shouldShowWindowingPill,
+            shouldShowBrowserPill = shouldShowBrowserPill,
+            shouldShowNewWindowButton = shouldShowNewWindowButton
+        ).apply {
+            bind(taskInfo, appIconBitmap, appName)
+            this.onToDesktopClickListener = onToDesktopClickListener
+            this.onToFullscreenClickListener = onToFullscreenClickListener
+            this.onToSplitScreenClickListener = onToSplitScreenClickListener
+            this.onNewWindowClickListener = onNewWindowClickListener
+            this.onOpenInBrowserClickListener = {
+                openInBrowserClickListener.invoke(openInBrowserLink!!)
+            }
+            this.onCloseMenuClickListener = onCloseMenuClickListener
+            this.onOutsideTouchListener = onOutsideTouchListener
+        }
+
         val x = handleMenuPosition.x.toInt()
         val y = handleMenuPosition.y.toInt()
         handleMenuViewContainer =
             if (!taskInfo.isFreeform && Flags.enableAdditionalWindowsAboveStatusBar()) {
                 AdditionalSystemViewContainer(
                     context = context,
-                    layoutId = R.layout.desktop_mode_window_decor_handle_menu,
                     taskId = taskInfo.taskId,
                     x = x,
                     y = y,
                     width = menuWidth,
-                    height = menuHeight
+                    height = menuHeight,
+                    flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or
+                            WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,
+                    view = handleMenuView.rootView
                 )
             } else {
                 parentDecor.addWindow(
-                    R.layout.desktop_mode_window_decor_handle_menu, "Handle Menu",
-                    t, ssg, x, y, menuWidth, menuHeight
+                    handleMenuView.rootView, "Handle Menu", t, ssg, x, y, menuWidth, menuHeight
                 )
             }
-        handleMenuViewContainer?.view?.let { view ->
-            handleMenuAnimator =
-                HandleMenuAnimator(view, menuWidth, captionHeight.toFloat())
-        }
-    }
 
-    /**
-     * Animates the appearance of the handle menu and its three pills.
-     */
-    private fun animateHandleMenu() {
-        when {
-            taskInfo.isFullscreen || taskInfo.isMultiWindow -> {
-                handleMenuAnimator?.animateCaptionHandleExpandToOpen()
-            }
-            else -> {
-                handleMenuAnimator?.animateOpen()
-            }
-        }
-    }
-
-    /**
-     * Set up all three pills of the handle menu: app info pill, windowing pill, & more actions
-     * pill.
-     */
-    private fun setupHandleMenu() {
-        val handleMenu = handleMenuViewContainer?.view ?: return
-        handleMenu.setOnTouchListener(onTouchListener)
-
-        val style = calculateMenuStyle()
-        setupAppInfoPill(handleMenu, style)
-        if (shouldShowWindowingPill) {
-            setupWindowingPill(handleMenu, style)
-        }
-        setupMoreActionsPill(handleMenu, style)
-        setupOpenInBrowserPill(handleMenu, style)
-    }
-
-    /**
-     * Set up interactive elements of handle menu's app info pill.
-     */
-    private fun setupAppInfoPill(handleMenu: View, style: MenuStyle) {
-        val pill = handleMenu.requireViewById<View>(R.id.app_info_pill).apply {
-            background.colorFilter = BlendModeColorFilter(style.backgroundColor, BlendMode.MULTIPLY)
-        }
-
-        pill.requireViewById<HandleMenuImageButton>(R.id.collapse_menu_button)
-            .let { collapseBtn ->
-                collapseBtn.imageTintList = ColorStateList.valueOf(style.textColor)
-                collapseBtn.setOnClickListener(onClickListener)
-                collapseBtn.taskInfo = taskInfo
-            }
-        pill.requireViewById<ImageView>(R.id.application_icon).let { appIcon ->
-            appIcon.setImageBitmap(appIconBitmap)
-        }
-        pill.requireViewById<TextView>(R.id.application_name).let { appNameView ->
-            appNameView.text = appName
-            appNameView.setTextColor(style.textColor)
-        }
-    }
-
-    /**
-     * Set up interactive elements and color of handle menu's windowing pill.
-     */
-    private fun setupWindowingPill(handleMenu: View, style: MenuStyle) {
-        val pill = handleMenu.requireViewById<View>(R.id.windowing_pill).apply {
-            background.colorFilter = BlendModeColorFilter(style.backgroundColor, BlendMode.MULTIPLY)
-        }
-
-        val fullscreenBtn = pill.requireViewById<ImageButton>(R.id.fullscreen_button)
-        val splitscreenBtn = pill.requireViewById<ImageButton>(R.id.split_screen_button)
-        val floatingBtn = pill.requireViewById<ImageButton>(R.id.floating_button)
-        // TODO: Remove once implemented.
-        floatingBtn.visibility = View.GONE
-        val desktopBtn = handleMenu.requireViewById<ImageButton>(R.id.desktop_button)
-
-        fullscreenBtn.setOnClickListener(onClickListener)
-        splitscreenBtn.setOnClickListener(onClickListener)
-        floatingBtn.setOnClickListener(onClickListener)
-        desktopBtn.setOnClickListener(onClickListener)
-
-        fullscreenBtn.isSelected = taskInfo.isFullscreen
-        fullscreenBtn.imageTintList = style.windowingButtonColor
-        splitscreenBtn.isSelected = taskInfo.isMultiWindow
-        splitscreenBtn.imageTintList = style.windowingButtonColor
-        floatingBtn.isSelected = taskInfo.isPinned
-        floatingBtn.imageTintList = style.windowingButtonColor
-        desktopBtn.isSelected = taskInfo.isFreeform
-        desktopBtn.imageTintList = style.windowingButtonColor
-    }
-
-    /**
-     * Set up interactive elements & height of handle menu's more actions pill
-     */
-    private fun setupMoreActionsPill(handleMenu: View, style: MenuStyle) {
-        val pill = handleMenu.requireViewById<View>(R.id.more_actions_pill).apply {
-            isGone = !SHOULD_SHOW_MORE_ACTIONS_PILL
-            background.colorFilter = BlendModeColorFilter(style.backgroundColor, BlendMode.MULTIPLY)
-        }
-        pill.requireViewById<Button>(R.id.screenshot_button).let { screenshotBtn ->
-            screenshotBtn.setTextColor(style.textColor)
-            screenshotBtn.compoundDrawableTintList = ColorStateList.valueOf(style.textColor)
-        }
-    }
-
-    private fun setupOpenInBrowserPill(handleMenu: View, style: MenuStyle) {
-        val pill = handleMenu.requireViewById<View>(R.id.open_in_browser_pill).apply {
-            isGone = !shouldShowBrowserPill
-            background.colorFilter = BlendModeColorFilter(style.backgroundColor, BlendMode.MULTIPLY)
-        }
-
-        pill.requireViewById<Button>(R.id.open_in_browser_button).let { browserButton ->
-            browserButton.setOnClickListener(onClickListener)
-            browserButton.setTextColor(style.textColor)
-            browserButton.compoundDrawableTintList = ColorStateList.valueOf(style.textColor)
-        }
+        this.handleMenuView = handleMenuView
     }
 
     /**
@@ -352,16 +288,8 @@
     fun checkMotionEvent(ev: MotionEvent) {
         // If the menu view is above status bar, we can let the views handle input directly.
         if (isViewAboveStatusBar) return
-        val handleMenu = handleMenuViewContainer?.view ?: return
-        val collapse = handleMenu.findViewById<HandleMenuImageButton>(R.id.collapse_menu_button)
         val inputPoint = translateInputToLocalSpace(ev)
-        val inputInCollapseButton = pointInView(collapse, inputPoint.x, inputPoint.y)
-        val action = ev.actionMasked
-        collapse.isHovered = inputInCollapseButton && action != MotionEvent.ACTION_UP
-        collapse.isPressed = inputInCollapseButton && action == MotionEvent.ACTION_DOWN
-        if (action == MotionEvent.ACTION_UP && inputInCollapseButton) {
-            collapse.performClick()
-        }
+        handleMenuView?.checkMotionEvent(ev, inputPoint)
     }
 
     // Translate the input point from display coordinates to the same space as the handle menu.
@@ -432,9 +360,17 @@
                 R.dimen.desktop_mode_handle_menu_windowing_pill_height)
             menuHeight -= pillTopMargin
         }
-        if (!SHOULD_SHOW_MORE_ACTIONS_PILL) {
+        if (!SHOULD_SHOW_SCREENSHOT_BUTTON) {
             menuHeight -= loadDimensionPixelSize(
-                R.dimen.desktop_mode_handle_menu_more_actions_pill_height)
+                R.dimen.desktop_mode_handle_menu_screenshot_height
+            )
+        }
+        if (!shouldShowNewWindowButton) {
+            menuHeight -= loadDimensionPixelSize(
+                R.dimen.desktop_mode_handle_menu_new_window_height
+            )
+        }
+        if (!SHOULD_SHOW_SCREENSHOT_BUTTON && !shouldShowNewWindowButton) {
             menuHeight -= pillTopMargin
         }
         if (!shouldShowBrowserPill) {
@@ -453,47 +389,291 @@
     }
 
     fun close() {
-        val after = {
+        handleMenuView?.animateCloseMenu {
             handleMenuViewContainer?.releaseView()
             handleMenuViewContainer = null
         }
-        if (taskInfo.isFullscreen || taskInfo.isMultiWindow) {
-            handleMenuAnimator?.animateCollapseIntoHandleClose(after)
-        } else {
-            handleMenuAnimator?.animateClose(after)
-        }
     }
 
-    private fun calculateMenuStyle(): MenuStyle {
-        val colorScheme = decorThemeUtil.getColorScheme(taskInfo)
-        return MenuStyle(
-            backgroundColor = colorScheme.surfaceBright.toArgb(),
-            textColor = colorScheme.onSurface.toArgb(),
-            windowingButtonColor = ColorStateList(
-                arrayOf(
-                    intArrayOf(android.R.attr.state_pressed),
-                    intArrayOf(android.R.attr.state_focused),
-                    intArrayOf(android.R.attr.state_selected),
-                    intArrayOf(),
+    /** The view within the Handle Menu, with options to change the windowing mode and more. */
+    @SuppressLint("ClickableViewAccessibility")
+    class HandleMenuView(
+        context: Context,
+        menuWidth: Int,
+        captionHeight: Int,
+        private val shouldShowWindowingPill: Boolean,
+        private val shouldShowBrowserPill: Boolean,
+        private val shouldShowNewWindowButton: Boolean
+    ) {
+        val rootView = LayoutInflater.from(context)
+            .inflate(R.layout.desktop_mode_window_decor_handle_menu, null /* root */) as View
+
+        // App Info Pill.
+        private val appInfoPill = rootView.requireViewById<View>(R.id.app_info_pill)
+        private val collapseMenuButton = appInfoPill.requireViewById<HandleMenuImageButton>(
+            R.id.collapse_menu_button)
+        private val appIconView = appInfoPill.requireViewById<ImageView>(R.id.application_icon)
+        private val appNameView = appInfoPill.requireViewById<TextView>(R.id.application_name)
+
+        // Windowing Pill.
+        private val windowingPill = rootView.requireViewById<View>(R.id.windowing_pill)
+        private val fullscreenBtn = windowingPill.requireViewById<ImageButton>(
+            R.id.fullscreen_button)
+        private val splitscreenBtn = windowingPill.requireViewById<ImageButton>(
+            R.id.split_screen_button)
+        private val floatingBtn = windowingPill.requireViewById<ImageButton>(R.id.floating_button)
+        private val desktopBtn = windowingPill.requireViewById<ImageButton>(R.id.desktop_button)
+
+        // More Actions Pill.
+        private val moreActionsPill = rootView.requireViewById<View>(R.id.more_actions_pill)
+        private val screenshotBtn = moreActionsPill.requireViewById<Button>(R.id.screenshot_button)
+        private val newWindowBtn = moreActionsPill.requireViewById<Button>(R.id.new_window_button)
+
+        // Open in Browser Pill.
+        private val openInBrowserPill = rootView.requireViewById<View>(R.id.open_in_browser_pill)
+        private val browserBtn = openInBrowserPill.requireViewById<Button>(
+            R.id.open_in_browser_button)
+
+        private val decorThemeUtil = DecorThemeUtil(context)
+        private val animator = HandleMenuAnimator(rootView, menuWidth, captionHeight.toFloat())
+
+        private lateinit var taskInfo: RunningTaskInfo
+        private lateinit var style: MenuStyle
+
+        var onToDesktopClickListener: (() -> Unit)? = null
+        var onToFullscreenClickListener: (() -> Unit)? = null
+        var onToSplitScreenClickListener: (() -> Unit)? = null
+        var onNewWindowClickListener: (() -> Unit)? = null
+        var onOpenInBrowserClickListener: (() -> Unit)? = null
+        var onCloseMenuClickListener: (() -> Unit)? = null
+        var onOutsideTouchListener: (() -> Unit)? = null
+
+        init {
+            fullscreenBtn.setOnClickListener { onToFullscreenClickListener?.invoke() }
+            splitscreenBtn.setOnClickListener { onToSplitScreenClickListener?.invoke() }
+            desktopBtn.setOnClickListener { onToDesktopClickListener?.invoke() }
+            browserBtn.setOnClickListener { onOpenInBrowserClickListener?.invoke() }
+            collapseMenuButton.setOnClickListener { onCloseMenuClickListener?.invoke() }
+            newWindowBtn.setOnClickListener { onNewWindowClickListener?.invoke() }
+
+            rootView.setOnTouchListener { _, event ->
+                if (event.actionMasked == ACTION_OUTSIDE) {
+                    onOutsideTouchListener?.invoke()
+                    return@setOnTouchListener false
+                }
+                return@setOnTouchListener true
+            }
+        }
+
+        /** Binds the menu views to the new data. */
+        fun bind(taskInfo: RunningTaskInfo, appIconBitmap: Bitmap?, appName: CharSequence?) {
+            this.taskInfo = taskInfo
+            this.style = calculateMenuStyle(taskInfo)
+
+            bindAppInfoPill(style, appIconBitmap, appName)
+            if (shouldShowWindowingPill) {
+                bindWindowingPill(style)
+            }
+            bindMoreActionsPill(style)
+            bindOpenInBrowserPill(style)
+        }
+
+        /** Animates the menu opening. */
+        fun animateOpenMenu() {
+            if (taskInfo.isFullscreen || taskInfo.isMultiWindow) {
+                animator.animateCaptionHandleExpandToOpen()
+            } else {
+                animator.animateOpen()
+            }
+        }
+
+        /** Animates the menu closing. */
+        fun animateCloseMenu(onAnimFinish: () -> Unit) {
+            if (taskInfo.isFullscreen || taskInfo.isMultiWindow) {
+                animator.animateCollapseIntoHandleClose(onAnimFinish)
+            } else {
+                animator.animateClose(onAnimFinish)
+            }
+        }
+
+        /**
+         * Checks whether a motion event falls inside this menu, and invokes a click of the
+         * collapse button if needed.
+         * Note: should only be called when regular click detection doesn't work because input is
+         * detected through the status bar layer with a global input monitor.
+         */
+        fun checkMotionEvent(ev: MotionEvent, inputPointLocal: PointF) {
+            val inputInCollapseButton = pointInView(
+                collapseMenuButton,
+                inputPointLocal.x,
+                inputPointLocal.y
+            )
+            val action = ev.actionMasked
+            collapseMenuButton.isHovered = inputInCollapseButton
+                    && action != MotionEvent.ACTION_UP
+            collapseMenuButton.isPressed = inputInCollapseButton
+                    && action == MotionEvent.ACTION_DOWN
+            if (action == MotionEvent.ACTION_UP && inputInCollapseButton) {
+                collapseMenuButton.performClick()
+            }
+        }
+
+        private fun pointInView(v: View?, x: Float, y: Float): Boolean {
+            return v != null && v.left <= x && v.right >= x && v.top <= y && v.bottom >= y
+        }
+
+        private fun calculateMenuStyle(taskInfo: RunningTaskInfo): MenuStyle {
+            val colorScheme = decorThemeUtil.getColorScheme(taskInfo)
+            return MenuStyle(
+                backgroundColor = colorScheme.surfaceBright.toArgb(),
+                textColor = colorScheme.onSurface.toArgb(),
+                windowingButtonColor = ColorStateList(
+                    arrayOf(
+                        intArrayOf(android.R.attr.state_pressed),
+                        intArrayOf(android.R.attr.state_focused),
+                        intArrayOf(android.R.attr.state_selected),
+                        intArrayOf(),
+                    ),
+                    intArrayOf(
+                        colorScheme.onSurface.toArgb(),
+                        colorScheme.onSurface.toArgb(),
+                        colorScheme.primary.toArgb(),
+                        colorScheme.onSurface.toArgb(),
+                    )
                 ),
-                intArrayOf(
-                    colorScheme.onSurface.toArgb(),
-                    colorScheme.onSurface.toArgb(),
-                    colorScheme.primary.toArgb(),
-                    colorScheme.onSurface.toArgb(),
+            )
+        }
+
+        private fun bindAppInfoPill(
+            style: MenuStyle,
+            appIconBitmap: Bitmap?,
+            appName: CharSequence?
+        ) {
+            appInfoPill.background.colorFilter = BlendModeColorFilter(
+                style.backgroundColor, BlendMode.MULTIPLY
+            )
+
+            collapseMenuButton.apply {
+                imageTintList = ColorStateList.valueOf(style.textColor)
+                this.taskInfo = this@HandleMenuView.taskInfo
+            }
+            appIconView.setImageBitmap(appIconBitmap)
+            appNameView.apply {
+                text = appName
+                setTextColor(style.textColor)
+            }
+        }
+
+        private fun bindWindowingPill(style: MenuStyle) {
+            windowingPill.background.colorFilter = BlendModeColorFilter(
+                style.backgroundColor, BlendMode.MULTIPLY
+            )
+
+            // TODO: Remove once implemented.
+            floatingBtn.visibility = View.GONE
+
+            fullscreenBtn.isSelected = taskInfo.isFullscreen
+            fullscreenBtn.imageTintList = style.windowingButtonColor
+            splitscreenBtn.isSelected = taskInfo.isMultiWindow
+            splitscreenBtn.imageTintList = style.windowingButtonColor
+            floatingBtn.isSelected = taskInfo.isPinned
+            floatingBtn.imageTintList = style.windowingButtonColor
+            desktopBtn.isSelected = taskInfo.isFreeform
+            desktopBtn.imageTintList = style.windowingButtonColor
+        }
+
+        private fun bindMoreActionsPill(style: MenuStyle) {
+            moreActionsPill.apply {
+                isGone = !shouldShowNewWindowButton && !SHOULD_SHOW_SCREENSHOT_BUTTON
+            }
+            screenshotBtn.apply {
+                isGone = !SHOULD_SHOW_SCREENSHOT_BUTTON
+                background.colorFilter =
+                    BlendModeColorFilter(style.backgroundColor, BlendMode.MULTIPLY
                 )
-            ),
+                setTextColor(style.textColor)
+                compoundDrawableTintList = ColorStateList.valueOf(style.textColor)
+            }
+            newWindowBtn.apply {
+                isGone = !shouldShowNewWindowButton
+                background.colorFilter =
+                    BlendModeColorFilter(style.backgroundColor, BlendMode.MULTIPLY)
+                setTextColor(style.textColor)
+                compoundDrawableTintList = ColorStateList.valueOf(style.textColor)
+            }
+        }
+
+        private fun bindOpenInBrowserPill(style: MenuStyle) {
+            openInBrowserPill.apply {
+                isGone = !shouldShowBrowserPill
+                background.colorFilter = BlendModeColorFilter(
+                    style.backgroundColor, BlendMode.MULTIPLY
+                )
+            }
+
+            browserBtn.apply {
+                setTextColor(style.textColor)
+                compoundDrawableTintList = ColorStateList.valueOf(style.textColor)
+            }
+        }
+
+        private data class MenuStyle(
+            @ColorInt val backgroundColor: Int,
+            @ColorInt val textColor: Int,
+            val windowingButtonColor: ColorStateList,
         )
     }
 
-    private data class MenuStyle(
-        @ColorInt val backgroundColor: Int,
-        @ColorInt val textColor: Int,
-        val windowingButtonColor: ColorStateList,
-    )
-
     companion object {
         private const val TAG = "HandleMenu"
-        private const val SHOULD_SHOW_MORE_ACTIONS_PILL = false
+        private const val SHOULD_SHOW_SCREENSHOT_BUTTON = false
+    }
+}
+
+/** A factory interface to create a [HandleMenu]. */
+interface HandleMenuFactory {
+    fun create(
+        parentDecor: DesktopModeWindowDecoration,
+        layoutResId: Int,
+        appIconBitmap: Bitmap?,
+        appName: CharSequence?,
+        splitScreenController: SplitScreenController,
+        shouldShowWindowingPill: Boolean,
+        shouldShowNewWindowButton: Boolean,
+        openInBrowserLink: Uri?,
+        captionWidth: Int,
+        captionHeight: Int,
+        captionX: Int
+    ): HandleMenu
+}
+
+/** A [HandleMenuFactory] implementation that creates a [HandleMenu].  */
+object DefaultHandleMenuFactory : HandleMenuFactory {
+    override fun create(
+        parentDecor: DesktopModeWindowDecoration,
+        layoutResId: Int,
+        appIconBitmap: Bitmap?,
+        appName: CharSequence?,
+        splitScreenController: SplitScreenController,
+        shouldShowWindowingPill: Boolean,
+        shouldShowNewWindowButton: Boolean,
+        openInBrowserLink: Uri?,
+        captionWidth: Int,
+        captionHeight: Int,
+        captionX: Int
+    ): HandleMenu {
+        return HandleMenu(
+            parentDecor,
+            layoutResId,
+            appIconBitmap,
+            appName,
+            splitScreenController,
+            shouldShowWindowingPill,
+            shouldShowNewWindowButton,
+            openInBrowserLink,
+            captionWidth,
+            captionHeight,
+            captionX
+        )
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeButtonView.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeButtonView.kt
index 4f04901..4faed01 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeButtonView.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeButtonView.kt
@@ -31,8 +31,8 @@
 import androidx.core.animation.doOnEnd
 import androidx.core.animation.doOnStart
 import androidx.core.content.ContextCompat
-import com.android.window.flags.Flags
 import com.android.wm.shell.R
+import com.android.wm.shell.shared.desktopmode.DesktopModeFlags
 
 private const val OPEN_MAXIMIZE_MENU_DELAY_ON_HOVER_MS = 350
 private const val MAX_DRAWABLE_ALPHA = 255
@@ -108,7 +108,7 @@
         baseForegroundColor: Int? = null,
         rippleDrawable: RippleDrawable? = null
     ) {
-        if (Flags.enableThemedAppHeaders()) {
+        if (DesktopModeFlags.THEMED_APP_HEADERS.isEnabled(context)) {
             requireNotNull(iconForegroundColor) { "Icon foreground color must be non-null" }
             requireNotNull(baseForegroundColor) { "Base foreground color must be non-null" }
             requireNotNull(rippleDrawable) { "Ripple drawable must be non-null" }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt
index 5f9f8d6..013f506 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt
@@ -40,6 +40,7 @@
 import android.view.MotionEvent.ACTION_HOVER_ENTER
 import android.view.MotionEvent.ACTION_HOVER_EXIT
 import android.view.MotionEvent.ACTION_HOVER_MOVE
+import android.view.MotionEvent.ACTION_OUTSIDE
 import android.view.SurfaceControl
 import android.view.SurfaceControl.Transaction
 import android.view.SurfaceControlViewHost
@@ -65,7 +66,6 @@
 import com.android.wm.shell.windowdecor.common.DecorThemeUtil
 import com.android.wm.shell.windowdecor.common.OPACITY_12
 import com.android.wm.shell.windowdecor.common.OPACITY_40
-import com.android.wm.shell.windowdecor.common.OnTaskActionClickListener
 import com.android.wm.shell.windowdecor.common.withAlpha
 import java.util.function.Supplier
 
@@ -101,17 +101,19 @@
 
     /** Creates and shows the maximize window. */
     fun show(
-        onMaximizeClickListener: OnTaskActionClickListener,
-        onLeftSnapClickListener: OnTaskActionClickListener,
-        onRightSnapClickListener: OnTaskActionClickListener,
-        onHoverListener: (Boolean) -> Unit
+        onMaximizeOrRestoreClickListener: () -> Unit,
+        onLeftSnapClickListener: () -> Unit,
+        onRightSnapClickListener: () -> Unit,
+        onHoverListener: (Boolean) -> Unit,
+        onOutsideTouchListener: () -> Unit,
     ) {
         if (maximizeMenu != null) return
         createMaximizeMenu(
-            onMaximizeClickListener = onMaximizeClickListener,
+            onMaximizeClickListener = onMaximizeOrRestoreClickListener,
             onLeftSnapClickListener = onLeftSnapClickListener,
             onRightSnapClickListener = onRightSnapClickListener,
-            onHoverListener = onHoverListener
+            onHoverListener = onHoverListener,
+            onOutsideTouchListener = onOutsideTouchListener
         )
         maximizeMenuView?.animateOpenMenu()
     }
@@ -126,10 +128,11 @@
 
     /** Create a maximize menu that is attached to the display area. */
     private fun createMaximizeMenu(
-        onMaximizeClickListener: OnTaskActionClickListener,
-        onLeftSnapClickListener: OnTaskActionClickListener,
-        onRightSnapClickListener: OnTaskActionClickListener,
-        onHoverListener: (Boolean) -> Unit
+        onMaximizeClickListener: () -> Unit,
+        onLeftSnapClickListener: () -> Unit,
+        onRightSnapClickListener: () -> Unit,
+        onHoverListener: (Boolean) -> Unit,
+        onOutsideTouchListener: () -> Unit
     ) {
         val t = transactionSupplier.get()
         val builder = SurfaceControl.Builder()
@@ -142,7 +145,8 @@
                 menuWidth,
                 menuHeight,
                 WindowManager.LayoutParams.TYPE_APPLICATION,
-                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
+                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+                        or WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,
                 PixelFormat.TRANSPARENT
         )
         lp.title = "Maximize Menu for Task=" + taskInfo.taskId
@@ -160,18 +164,12 @@
             menuHeight = menuHeight,
             menuPadding = menuPadding,
         ).also { menuView ->
-            val taskId = taskInfo.taskId
             menuView.bind(taskInfo)
-            menuView.onMaximizeClickListener = {
-                onMaximizeClickListener.onClick(taskId, "maximize_menu_option")
-            }
-            menuView.onLeftSnapClickListener = {
-                onLeftSnapClickListener.onClick(taskId, "left_snap_option")
-            }
-            menuView.onRightSnapClickListener = {
-                onRightSnapClickListener.onClick(taskId, "right_snap_option")
-            }
+            menuView.onMaximizeClickListener = onMaximizeClickListener
+            menuView.onLeftSnapClickListener = onLeftSnapClickListener
+            menuView.onRightSnapClickListener = onRightSnapClickListener
             menuView.onMenuHoverListener = onHoverListener
+            menuView.onOutsideTouchListener = onOutsideTouchListener
             viewHost.setView(menuView.rootView, lp)
         }
 
@@ -268,6 +266,8 @@
         var onRightSnapClickListener: (() -> Unit)? = null
         /** Invoked whenever the hover state of the menu changes. */
         var onMenuHoverListener: ((Boolean) -> Unit)? = null
+        /** Invoked whenever a click occurs outside the menu */
+        var onOutsideTouchListener: (() -> Unit)? = null
 
         init {
             overlay.setOnHoverListener { _, event ->
@@ -312,6 +312,13 @@
             maximizeButton.setOnClickListener { onMaximizeClickListener?.invoke() }
             snapRightButton.setOnClickListener { onRightSnapClickListener?.invoke() }
             snapLeftButton.setOnClickListener { onLeftSnapClickListener?.invoke() }
+            rootView.setOnTouchListener { _, event ->
+                if (event.actionMasked == ACTION_OUTSIDE) {
+                    onOutsideTouchListener?.invoke()
+                    false
+                }
+                true
+            }
 
             // To prevent aliasing.
             maximizeButton.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java
index b5b476d..237492e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java
@@ -163,14 +163,7 @@
         } else {
             DragPositioningCallbackUtility.updateTaskBounds(mRepositionTaskBounds,
                     mTaskBoundsAtDragStart, mRepositionStartPoint, x, y);
-            if (!mTaskBoundsAtDragStart.equals(mRepositionTaskBounds)) {
-                final WindowContainerTransaction wct = new WindowContainerTransaction();
-                wct.setBounds(mDesktopWindowDecoration.mTaskInfo.token, mRepositionTaskBounds);
-                mTransitions.startTransition(TRANSIT_CHANGE, wct, this);
-            } else {
-                // Drag-move ended where it originally started, no need to update WM.
                 mInteractionJankMonitor.end(CUJ_DESKTOP_MODE_DRAG_WINDOW);
-            }
         }
 
         mCtrlType = CTRL_TYPE_UNDEFINED;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
index a691f59..4cab6e4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
@@ -19,10 +19,10 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.content.res.Configuration.DENSITY_DPI_UNDEFINED;
-import static android.view.InsetsSource.FLAG_FORCE_CONSUMING;
 import static android.view.WindowInsets.Type.captionBar;
 import static android.view.WindowInsets.Type.mandatorySystemGestures;
 import static android.view.WindowInsets.Type.statusBars;
+import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
 
 import android.annotation.NonNull;
@@ -54,7 +54,6 @@
 import android.window.WindowContainerTransaction;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.window.flags.Flags;
 import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.common.DisplayController;
 import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
@@ -107,6 +106,7 @@
      * System-wide context. Only used to create context with overridden configurations.
      */
     final Context mContext;
+    final @NonNull Context mUserContext;
     final @NonNull DisplayController mDisplayController;
     final ShellTaskOrganizer mTaskOrganizer;
     final Supplier<SurfaceControl.Builder> mSurfaceControlBuilderSupplier;
@@ -148,11 +148,12 @@
 
     WindowDecoration(
             Context context,
+            @NonNull Context userContext,
             DisplayController displayController,
             ShellTaskOrganizer taskOrganizer,
             RunningTaskInfo taskInfo,
             SurfaceControl taskSurface) {
-        this(context, displayController, taskOrganizer, taskInfo, taskSurface,
+        this(context, userContext, displayController, taskOrganizer, taskInfo, taskSurface,
                 SurfaceControl.Builder::new, SurfaceControl.Transaction::new,
                 WindowContainerTransaction::new, SurfaceControl::new,
                 new SurfaceControlViewHostFactory() {});
@@ -160,6 +161,7 @@
 
     WindowDecoration(
             Context context,
+            @NonNull Context userContext,
             @NonNull DisplayController displayController,
             ShellTaskOrganizer taskOrganizer,
             RunningTaskInfo taskInfo,
@@ -170,6 +172,7 @@
             Supplier<SurfaceControl> surfaceControlSupplier,
             SurfaceControlViewHostFactory surfaceControlViewHostFactory) {
         mContext = context;
+        mUserContext = userContext;
         mDisplayController = displayController;
         mTaskOrganizer = taskOrganizer;
         mTaskInfo = taskInfo;
@@ -178,7 +181,6 @@
         mSurfaceControlTransactionSupplier = surfaceControlTransactionSupplier;
         mWindowContainerTransactionSupplier = windowContainerTransactionSupplier;
         mSurfaceControlViewHostFactory = surfaceControlViewHostFactory;
-
         mDisplay = mDisplayController.getDisplay(mTaskInfo.displayId);
     }
 
@@ -375,7 +377,8 @@
         }
 
         final WindowDecorationInsets newInsets = new WindowDecorationInsets(
-                mTaskInfo.token, mOwner, captionInsetsRect, boundingRects);
+                mTaskInfo.token, mOwner, captionInsetsRect, boundingRects,
+                params.mInsetSourceFlags);
         if (!newInsets.equals(mWindowDecorationInsets)) {
             // Add or update this caption as an insets source.
             mWindowDecorationInsets = newInsets;
@@ -610,6 +613,47 @@
      * Create a window associated with this WindowDecoration.
      * Note that subclass must dispose of this when the task is hidden/closed.
      *
+     * @param v            View to attach to the window
+     * @param t            the transaction to apply
+     * @param xPos         x position of new window
+     * @param yPos         y position of new window
+     * @param width        width of new window
+     * @param height       height of new window
+     * @return the {@link AdditionalViewHostViewContainer} that was added.
+     */
+    AdditionalViewHostViewContainer addWindow(@NonNull View v, @NonNull String namePrefix,
+            @NonNull SurfaceControl.Transaction t, @NonNull SurfaceSyncGroup ssg,
+            int xPos, int yPos, int width, int height) {
+        final SurfaceControl.Builder builder = mSurfaceControlBuilderSupplier.get();
+        SurfaceControl windowSurfaceControl = builder
+                .setName(namePrefix + " of Task=" + mTaskInfo.taskId)
+                .setContainerLayer()
+                .setParent(mDecorationContainerSurface)
+                .setCallsite("WindowDecoration.addWindow")
+                .build();
+        t.setPosition(windowSurfaceControl, xPos, yPos)
+                .setWindowCrop(windowSurfaceControl, width, height)
+                .show(windowSurfaceControl);
+        final WindowManager.LayoutParams lp =
+                new WindowManager.LayoutParams(width, height, TYPE_APPLICATION,
+                        WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+                        | FLAG_WATCH_OUTSIDE_TOUCH,
+                        PixelFormat.TRANSPARENT);
+        lp.setTitle("Additional window of Task=" + mTaskInfo.taskId);
+        lp.setTrustedOverlay();
+        WindowlessWindowManager windowManager = new WindowlessWindowManager(mTaskInfo.configuration,
+                windowSurfaceControl, null /* hostInputToken */);
+        SurfaceControlViewHost viewHost = mSurfaceControlViewHostFactory
+                .create(mDecorWindowContext, mDisplay, windowManager);
+        ssg.add(viewHost.getSurfacePackage(), () -> viewHost.setView(v, lp));
+        return new AdditionalViewHostViewContainer(windowSurfaceControl, viewHost,
+                mSurfaceControlTransactionSupplier);
+    }
+
+    /**
+     * Create a window associated with this WindowDecoration.
+     * Note that subclass must dispose of this when the task is hidden/closed.
+     *
      * @param layoutId     layout to make the window from
      * @param t            the transaction to apply
      * @param xPos         x position of new window
@@ -621,31 +665,8 @@
     AdditionalViewHostViewContainer addWindow(int layoutId, String namePrefix,
             SurfaceControl.Transaction t, SurfaceSyncGroup ssg, int xPos, int yPos,
             int width, int height) {
-        final SurfaceControl.Builder builder = mSurfaceControlBuilderSupplier.get();
-        SurfaceControl windowSurfaceControl = builder
-                .setName(namePrefix + " of Task=" + mTaskInfo.taskId)
-                .setContainerLayer()
-                .setParent(mDecorationContainerSurface)
-                .setCallsite("WindowDecoration.addWindow")
-                .build();
-        View v = LayoutInflater.from(mDecorWindowContext).inflate(layoutId, null);
-
-        t.setPosition(windowSurfaceControl, xPos, yPos)
-                .setWindowCrop(windowSurfaceControl, width, height)
-                .show(windowSurfaceControl);
-        final WindowManager.LayoutParams lp =
-                new WindowManager.LayoutParams(width, height, TYPE_APPLICATION,
-                        WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
-                        PixelFormat.TRANSPARENT);
-        lp.setTitle("Additional window of Task=" + mTaskInfo.taskId);
-        lp.setTrustedOverlay();
-        WindowlessWindowManager windowManager = new WindowlessWindowManager(mTaskInfo.configuration,
-                windowSurfaceControl, null /* hostInputToken */);
-        SurfaceControlViewHost viewHost = mSurfaceControlViewHostFactory
-                .create(mDecorWindowContext, mDisplay, windowManager);
-        ssg.add(viewHost.getSurfacePackage(), () -> viewHost.setView(v, lp));
-        return new AdditionalViewHostViewContainer(windowSurfaceControl, viewHost,
-                mSurfaceControlTransactionSupplier);
+        final View v = LayoutInflater.from(mDecorWindowContext).inflate(layoutId, null);
+        return addWindow(v, namePrefix, t, ssg, xPos, yPos, width, height);
     }
 
     /**
@@ -660,7 +681,7 @@
         final int captionHeight = loadDimensionPixelSize(mContext.getResources(), captionHeightId);
         final Rect captionInsets = new Rect(0, 0, 0, captionHeight);
         final WindowDecorationInsets newInsets = new WindowDecorationInsets(mTaskInfo.token,
-                mOwner, captionInsets, null /* boundingRets */);
+                mOwner, captionInsets, null /* boundingRets */, 0 /* flags */);
         if (!newInsets.equals(mWindowDecorationInsets)) {
             mWindowDecorationInsets = newInsets;
             mWindowDecorationInsets.addOrUpdate(wct);
@@ -674,6 +695,7 @@
         int mCaptionWidthId;
         final List<OccludingCaptionElement> mOccludingCaptionElements = new ArrayList<>();
         int mInputFeatures;
+        @InsetsSource.Flags int mInsetSourceFlags;
 
         int mShadowRadiusId;
         int mCornerRadius;
@@ -689,6 +711,7 @@
             mCaptionWidthId = Resources.ID_NULL;
             mOccludingCaptionElements.clear();
             mInputFeatures = 0;
+            mInsetSourceFlags = 0;
 
             mShadowRadiusId = Resources.ID_NULL;
             mCornerRadius = 0;
@@ -753,20 +776,20 @@
         private final Binder mOwner;
         private final Rect mFrame;
         private final Rect[] mBoundingRects;
+        private final @InsetsSource.Flags int mFlags;
 
         private WindowDecorationInsets(WindowContainerToken token, Binder owner, Rect frame,
-                Rect[] boundingRects) {
+                Rect[] boundingRects, @InsetsSource.Flags int flags) {
             mToken = token;
             mOwner = owner;
             mFrame = frame;
             mBoundingRects = boundingRects;
+            mFlags = flags;
         }
 
         void addOrUpdate(WindowContainerTransaction wct) {
-            final @InsetsSource.Flags int captionSourceFlags =
-                    Flags.enableCaptionCompatInsetForceConsumption() ? FLAG_FORCE_CONSUMING : 0;
             wct.addInsetsSource(mToken, mOwner, INDEX, captionBar(), mFrame, mBoundingRects,
-                    captionSourceFlags);
+                    mFlags);
             wct.addInsetsSource(mToken, mOwner, INDEX, mandatorySystemGestures(), mFrame,
                     mBoundingRects, 0 /* flags */);
         }
@@ -782,12 +805,13 @@
             if (!(o instanceof WindowDecoration.WindowDecorationInsets that)) return false;
             return Objects.equals(mToken, that.mToken) && Objects.equals(mOwner,
                     that.mOwner) && Objects.equals(mFrame, that.mFrame)
-                    && Objects.deepEquals(mBoundingRects, that.mBoundingRects);
+                    && Objects.deepEquals(mBoundingRects, that.mBoundingRects)
+                    && mFlags == that.mFlags;
         }
 
         @Override
         public int hashCode() {
-            return Objects.hash(mToken, mOwner, mFrame, Arrays.hashCode(mBoundingRects));
+            return Objects.hash(mToken, mOwner, mFrame, Arrays.hashCode(mBoundingRects), mFlags);
         }
     }
-}
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/additionalviewcontainer/AdditionalSystemViewContainer.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/additionalviewcontainer/AdditionalSystemViewContainer.kt
index 6a354f1..cadd80e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/additionalviewcontainer/AdditionalSystemViewContainer.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/additionalviewcontainer/AdditionalSystemViewContainer.kt
@@ -16,6 +16,7 @@
 
 package com.android.wm.shell.windowdecor.additionalviewcontainer
 
+import android.annotation.LayoutRes
 import android.content.Context
 import android.graphics.PixelFormat
 import android.view.Gravity
@@ -29,27 +30,56 @@
  * for view containers that should be above the status bar layer.
  */
 class AdditionalSystemViewContainer(
-    private val context: Context,
+    context: Context,
     taskId: Int,
     x: Int,
     y: Int,
     width: Int,
     height: Int,
-    layoutId: Int? = null
-) : AdditionalViewContainer() {
+    flags: Int,
     override val view: View
+) : AdditionalViewContainer() {
+
+    constructor(
+        context: Context,
+        taskId: Int,
+        x: Int,
+        y: Int,
+        width: Int,
+        height: Int,
+        flags: Int,
+        @LayoutRes layoutId: Int
+    ) : this(
+        context = context,
+        taskId = taskId,
+        x = x,
+        y = y,
+        width = width,
+        height = height,
+        flags = flags,
+        view = LayoutInflater.from(context).inflate(layoutId, null /* parent */)
+    )
+
+    constructor(
+        context: Context, taskId: Int, x: Int, y: Int, width: Int, height: Int, flags: Int
+    ) : this(
+        context = context,
+        taskId = taskId,
+        x = x,
+        y = y,
+        width = width,
+        height = height,
+        flags = flags,
+        view = View(context)
+    )
+
     val windowManager: WindowManager? = context.getSystemService(WindowManager::class.java)
 
     init {
-        if (layoutId != null) {
-            view = LayoutInflater.from(context).inflate(layoutId, null)
-        } else {
-            view = View(context)
-        }
         val lp = WindowManager.LayoutParams(
             width, height, x, y,
             WindowManager.LayoutParams.TYPE_STATUS_BAR_ADDITIONAL,
-            WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
+            flags,
             PixelFormat.TRANSPARENT
         ).apply {
             title = "Additional view container of Task=$taskId"
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 76dfe37..753723c 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
@@ -21,10 +21,11 @@
 import android.content.res.ColorStateList
 import android.graphics.Color
 import android.graphics.Point
+import android.hardware.input.InputManager
+import android.view.MotionEvent.ACTION_DOWN
 import android.view.SurfaceControl
 import android.view.View
 import android.view.View.OnClickListener
-import android.view.View.OnHoverListener
 import android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS
 import android.view.WindowManager
 import android.widget.ImageButton
@@ -39,9 +40,8 @@
  */
 internal class AppHandleViewHolder(
     rootView: View,
-    private val onCaptionTouchListener: View.OnTouchListener,
-    private val onCaptionButtonClickListener: OnClickListener,
-    private val onCaptionHoverListener: OnHoverListener,
+    onCaptionTouchListener: View.OnTouchListener,
+    onCaptionButtonClickListener: OnClickListener
 ) : WindowDecorationViewHolder(rootView) {
 
     companion object {
@@ -51,6 +51,7 @@
     private val windowManager = context.getSystemService(WindowManager::class.java)
     private val captionView: View = rootView.requireViewById(R.id.desktop_mode_caption)
     private val captionHandle: ImageButton = rootView.requireViewById(R.id.caption_handle)
+    private val inputManager = context.getSystemService(InputManager::class.java)
 
     // An invisible View that takes up the same coordinates as captionHandle but is layered
     // above the status bar. The purpose of this View is to receive input intended for
@@ -61,7 +62,6 @@
         captionView.setOnTouchListener(onCaptionTouchListener)
         captionHandle.setOnTouchListener(onCaptionTouchListener)
         captionHandle.setOnClickListener(onCaptionButtonClickListener)
-        captionHandle.setOnHoverListener(onCaptionHoverListener)
     }
 
     override fun bindData(
@@ -97,7 +97,8 @@
                                           handleHeight: Int) {
         if (!Flags.enableAdditionalWindowsAboveStatusBar()) return
         statusBarInputLayer = AdditionalSystemViewContainer(context, taskInfo.taskId,
-            handlePosition.x, handlePosition.y, handleWidth, handleHeight)
+            handlePosition.x, handlePosition.y, handleWidth, handleHeight,
+            WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE)
         val view = statusBarInputLayer?.view
         val lp = view?.layoutParams as WindowManager.LayoutParams
         lp.title = "Handle Input Layer of task " + taskInfo.taskId
@@ -106,10 +107,19 @@
         // gesture listener that receives events before window. This is to prevent notification
         // shade gesture when we swipe down to enter desktop.
         lp.inputFeatures = WindowManager.LayoutParams.INPUT_FEATURE_SPY
-        view.id = R.id.caption_handle
-        view.setOnClickListener(onCaptionButtonClickListener)
-        view.setOnTouchListener(onCaptionTouchListener)
-        view.setOnHoverListener(onCaptionHoverListener)
+        view.setOnHoverListener { _, event ->
+            captionHandle.onHoverEvent(event)
+        }
+        // Caption handle is located within the status bar region, meaning the
+        // DisplayPolicy will attempt to transfer this input to status bar if it's
+        // a swipe down. Pilfer here to keep the gesture in handle alone.
+        view.setOnTouchListener { v, event ->
+            if (event.actionMasked == ACTION_DOWN) {
+                inputManager.pilferPointers(v.viewRootImpl.inputToken)
+            }
+            captionHandle.dispatchTouchEvent(event)
+            true
+        }
         windowManager.updateViewLayout(view, lp)
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHeaderViewHolder.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHeaderViewHolder.kt
index b704d9c..17b3dea 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHeaderViewHolder.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHeaderViewHolder.kt
@@ -42,8 +42,8 @@
 import com.android.internal.R.attr.materialColorSurfaceContainerHigh
 import com.android.internal.R.attr.materialColorSurfaceContainerLow
 import com.android.internal.R.attr.materialColorSurfaceDim
-import com.android.window.flags.Flags
 import com.android.wm.shell.R
+import com.android.wm.shell.shared.desktopmode.DesktopModeFlags
 import com.android.wm.shell.windowdecor.MaximizeButtonView
 import com.android.wm.shell.windowdecor.common.DecorThemeUtil
 import com.android.wm.shell.windowdecor.common.OPACITY_100
@@ -144,7 +144,7 @@
         height: Int,
         isCaptionVisible: Boolean
     ) {
-        if (Flags.enableThemedAppHeaders()) {
+        if (DesktopModeFlags.THEMED_APP_HEADERS.isEnabled(context)) {
             bindDataWithThemedHeaders(taskInfo)
         } else {
             bindDataLegacy(taskInfo)
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/scenarios/MinimizeWindowOnAppOpen.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/scenarios/MinimizeWindowOnAppOpen.kt
new file mode 100644
index 0000000..c847710
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/scenarios/MinimizeWindowOnAppOpen.kt
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.service.desktopmode.scenarios
+
+import android.app.Instrumentation
+import android.tools.traces.parsers.WindowManagerStateHelper
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import com.android.launcher3.tapl.LauncherInstrumentation
+import com.android.server.wm.flicker.helpers.DesktopModeAppHelper
+import com.android.server.wm.flicker.helpers.ImeAppHelper
+import com.android.server.wm.flicker.helpers.LetterboxAppHelper
+import com.android.server.wm.flicker.helpers.MailAppHelper
+import com.android.server.wm.flicker.helpers.NewTasksAppHelper
+import com.android.server.wm.flicker.helpers.SimpleAppHelper
+import com.android.window.flags.Flags
+import org.junit.After
+import org.junit.Assume
+import org.junit.Before
+import org.junit.Ignore
+import org.junit.Test
+
+/**
+ * Base scenario test for minimizing the least recently used window when a new window is opened
+ * above the window limit. For tangor devices, which this test currently runs on, the window limit
+ * is 4.
+ */
+@Ignore("Base Test Class")
+abstract class MinimizeWindowOnAppOpen()
+{
+    private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+    private val tapl = LauncherInstrumentation()
+    private val wmHelper = WindowManagerStateHelper(instrumentation)
+    private val device = UiDevice.getInstance(instrumentation)
+
+    private val testApp = DesktopModeAppHelper(SimpleAppHelper(instrumentation))
+    private val mailApp = DesktopModeAppHelper(MailAppHelper(instrumentation))
+    private val newTasksApp = DesktopModeAppHelper(NewTasksAppHelper(instrumentation))
+    private val imeApp = DesktopModeAppHelper(ImeAppHelper(instrumentation))
+    private val letterboxAppHelper = DesktopModeAppHelper(LetterboxAppHelper(instrumentation))
+
+    @Before
+    fun setup() {
+        Assume.assumeTrue(Flags.enableDesktopWindowingMode() && tapl.isTablet)
+        testApp.enterDesktopWithDrag(wmHelper, device)
+        mailApp.launchViaIntent(wmHelper)
+        newTasksApp.launchViaIntent(wmHelper)
+        imeApp.launchViaIntent(wmHelper)
+    }
+
+    @Test
+    open fun openAppToMinimizeWindow() {
+        // Launch a new app while 4 apps are already open on desktop. This should result in the
+        // first app we opened to be minimized.
+        letterboxAppHelper.launchViaIntent(wmHelper)
+    }
+
+    @After
+    fun teardown() {
+        testApp.exit(wmHelper)
+        mailApp.exit(wmHelper)
+        newTasksApp.exit(wmHelper)
+        imeApp.exit(wmHelper)
+        letterboxAppHelper.exit(wmHelper)
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
index e91828b..716a148 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
@@ -16,10 +16,6 @@
 
 package com.android.wm.shell;
 
-import static android.app.CameraCompatTaskInfo.CAMERA_COMPAT_CONTROL_DISMISSED;
-import static android.app.CameraCompatTaskInfo.CAMERA_COMPAT_CONTROL_HIDDEN;
-import static android.app.CameraCompatTaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED;
-import static android.app.CameraCompatTaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
@@ -443,84 +439,6 @@
     }
 
     @Test
-    public void testOnCameraCompatActivityChanged() {
-        final RunningTaskInfo taskInfo1 = createTaskInfo(/* taskId= */ 1,
-                WINDOWING_MODE_FULLSCREEN);
-        taskInfo1.displayId = DEFAULT_DISPLAY;
-        taskInfo1.appCompatTaskInfo.cameraCompatTaskInfo.cameraCompatControlState =
-                CAMERA_COMPAT_CONTROL_HIDDEN;
-        final TrackingTaskListener taskListener = new TrackingTaskListener();
-        mOrganizer.addListenerForType(taskListener, TASK_LISTENER_TYPE_FULLSCREEN);
-        mOrganizer.onTaskAppeared(taskInfo1, /* leash= */ null);
-
-        // Task listener sent to compat UI is null if top activity doesn't request a camera
-        // compat control.
-        verifyOnCompatInfoChangedInvokedWith(taskInfo1, null /* taskListener */);
-
-        // Task listener is non-null when request a camera compat control for a visible task.
-        clearInvocations(mCompatUI);
-        final RunningTaskInfo taskInfo2 =
-                createTaskInfo(taskInfo1.taskId, taskInfo1.getWindowingMode());
-        taskInfo2.displayId = taskInfo1.displayId;
-        taskInfo2.appCompatTaskInfo.cameraCompatTaskInfo.cameraCompatControlState =
-                CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED;
-        taskInfo2.isVisible = true;
-        mOrganizer.onTaskInfoChanged(taskInfo2);
-        verifyOnCompatInfoChangedInvokedWith(taskInfo2, taskListener);
-
-        // CompatUIController#onCompatInfoChanged is called when requested state for a camera
-        // compat control changes for a visible task.
-        clearInvocations(mCompatUI);
-        final RunningTaskInfo taskInfo3 =
-                createTaskInfo(taskInfo1.taskId, taskInfo1.getWindowingMode());
-        taskInfo3.displayId = taskInfo1.displayId;
-        taskInfo3.appCompatTaskInfo.cameraCompatTaskInfo.cameraCompatControlState =
-                CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED;
-        taskInfo3.isVisible = true;
-        mOrganizer.onTaskInfoChanged(taskInfo3);
-        verifyOnCompatInfoChangedInvokedWith(taskInfo3, taskListener);
-
-        // CompatUIController#onCompatInfoChanged is called when a top activity goes in size compat
-        // mode for a visible task that has a compat control.
-        clearInvocations(mCompatUI);
-        final RunningTaskInfo taskInfo4 =
-                createTaskInfo(taskInfo1.taskId, taskInfo1.getWindowingMode());
-        taskInfo4.displayId = taskInfo1.displayId;
-        taskInfo4.appCompatTaskInfo.topActivityInSizeCompat = true;
-        taskInfo4.appCompatTaskInfo.cameraCompatTaskInfo.cameraCompatControlState =
-                CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED;
-        taskInfo4.isVisible = true;
-        mOrganizer.onTaskInfoChanged(taskInfo4);
-        verifyOnCompatInfoChangedInvokedWith(taskInfo4, taskListener);
-
-        // Task linster is null when a camera compat control is dimissed for a visible task.
-        clearInvocations(mCompatUI);
-        final RunningTaskInfo taskInfo5 =
-                createTaskInfo(taskInfo1.taskId, taskInfo1.getWindowingMode());
-        taskInfo5.displayId = taskInfo1.displayId;
-        taskInfo5.appCompatTaskInfo.cameraCompatTaskInfo.cameraCompatControlState =
-                CAMERA_COMPAT_CONTROL_DISMISSED;
-        taskInfo5.isVisible = true;
-        mOrganizer.onTaskInfoChanged(taskInfo5);
-        verifyOnCompatInfoChangedInvokedWith(taskInfo5, null /* taskListener */);
-
-        // Task linster is null when request a camera compat control for a invisible task.
-        clearInvocations(mCompatUI);
-        final RunningTaskInfo taskInfo6 =
-                createTaskInfo(taskInfo1.taskId, taskInfo1.getWindowingMode());
-        taskInfo6.displayId = taskInfo1.displayId;
-        taskInfo6.appCompatTaskInfo.cameraCompatTaskInfo.cameraCompatControlState =
-                CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED;
-        taskInfo6.isVisible = false;
-        mOrganizer.onTaskInfoChanged(taskInfo6);
-        verifyOnCompatInfoChangedInvokedWith(taskInfo6, null /* taskListener */);
-
-        clearInvocations(mCompatUI);
-        mOrganizer.onTaskVanished(taskInfo1);
-        verifyOnCompatInfoChangedInvokedWith(taskInfo1, null /* taskListener */);
-    }
-
-    @Test
     public void testAddLocusListener() {
         RunningTaskInfo task1 = createTaskInfo(/* taskId= */ 1, WINDOWING_MODE_MULTI_WINDOW);
         task1.isVisible = true;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apptoweb/AppToWebGenericLinksParserTests.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apptoweb/AppToWebGenericLinksParserTests.kt
new file mode 100644
index 0000000..053027f
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apptoweb/AppToWebGenericLinksParserTests.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.apptoweb
+
+import android.provider.DeviceConfig
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import android.testing.TestableResources
+import androidx.test.filters.SmallTest
+import com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn
+import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
+import com.android.dx.mockito.inline.extended.StaticMockitoSession
+import com.android.wm.shell.R
+import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.common.ShellExecutor
+import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
+import com.android.wm.shell.apptoweb.AppToWebGenericLinksParser.Companion.FLAG_GENERIC_LINKS
+import junit.framework.TestCase.assertEquals
+import junit.framework.TestCase.assertNull
+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.quality.Strictness
+
+/**
+ * Tests for [AppToWebGenericLinksParser].
+ *
+ * Build/Install/Run: atest WMShellUnitTests:AppToWebGenericLinksParserTests
+ */
+@SmallTest
+@TestableLooper.RunWithLooper
+@RunWith(AndroidTestingRunner::class)
+class AppToWebGenericLinksParserTests : ShellTestCase() {
+    @Mock private lateinit var mockExecutor: ShellExecutor
+
+    private lateinit var genericLinksParser: AppToWebGenericLinksParser
+    private lateinit var mockitoSession: StaticMockitoSession
+    private lateinit var resources: TestableResources
+
+    @Before
+    fun setup() {
+        MockitoAnnotations.initMocks(this)
+
+        mockitoSession =
+            mockitoSession()
+                .strictness(Strictness.LENIENT)
+                .spyStatic(DesktopModeStatus::class.java)
+                .startMocking()
+        resources = mContext.getOrCreateTestableResources()
+        resources.addOverride(R.string.generic_links_list, BUILD_TIME_LIST)
+        DeviceConfig.setProperty(
+            NAMESPACE,
+            FLAG_GENERIC_LINKS,
+            SERVER_SIDE_LIST,
+            false /* makeDefault */
+        )
+    }
+
+    @After
+    fun tearDown() {
+        mockitoSession.finishMocking()
+    }
+
+    @Test
+    fun init_usingBuildTimeList() {
+        doReturn(true).`when` { DesktopModeStatus.useAppToWebBuildTimeGenericLinks() }
+        genericLinksParser = AppToWebGenericLinksParser(mContext, mockExecutor)
+        // Assert build-time list correctly parsed
+        assertEquals(URL_B, genericLinksParser.getGenericLink(PACKAGE_NAME_1))
+    }
+
+    @Test
+    fun init_usingServerSideList() {
+        doReturn(false).`when` { DesktopModeStatus.useAppToWebBuildTimeGenericLinks() }
+        genericLinksParser = AppToWebGenericLinksParser(mContext, mockExecutor)
+        // Assert server side list correctly parsed
+        assertEquals(URL_S, genericLinksParser.getGenericLink(PACKAGE_NAME_1))
+    }
+
+    @Test
+    fun init_ignoresMalformedPair() {
+        doReturn(true).`when` { DesktopModeStatus.useAppToWebBuildTimeGenericLinks() }
+        val packageName2 = "com.google.android.slides"
+        val url2 = "https://docs.google.com"
+        resources.addOverride(R.string.generic_links_list,
+                "$PACKAGE_NAME_1:$URL_B error $packageName2:$url2")
+        genericLinksParser = AppToWebGenericLinksParser(mContext, mockExecutor)
+        // Assert generics links list correctly parsed
+        assertEquals(URL_B, genericLinksParser.getGenericLink(PACKAGE_NAME_1))
+        assertEquals(url2, genericLinksParser.getGenericLink(packageName2))
+    }
+
+
+    @Test
+    fun onlySavesValidPackageToUrlMaps() {
+        doReturn(true).`when` { DesktopModeStatus.useAppToWebBuildTimeGenericLinks() }
+        resources.addOverride(R.string.generic_links_list, "$PACKAGE_NAME_1:www.yout")
+        genericLinksParser = AppToWebGenericLinksParser(mContext, mockExecutor)
+        // Verify map with invalid url not saved
+        assertNull(genericLinksParser.getGenericLink(PACKAGE_NAME_1))
+    }
+
+    companion object {
+        private const val PACKAGE_NAME_1 = "com.google.android.youtube"
+
+        private const val URL_B = "http://www.youtube.com"
+        private const val URL_S = "http://www.google.com"
+
+        private const val SERVER_SIDE_LIST = "$PACKAGE_NAME_1:$URL_S"
+        private const val BUILD_TIME_LIST = "$PACKAGE_NAME_1:$URL_B"
+
+        private const val NAMESPACE = DeviceConfig.NAMESPACE_APP_COMPAT_OVERRIDES
+    }
+}
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 57e469d..56fad95 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
@@ -68,6 +68,7 @@
 import com.android.wm.shell.sysui.ShellController;
 import com.android.wm.shell.sysui.ShellInit;
 import com.android.wm.shell.sysui.ShellSharedConstants;
+import com.android.wm.shell.transition.Transitions;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -114,6 +115,8 @@
     @Mock
     private ShellCommandHandler mShellCommandHandler;
     @Mock
+    private Transitions mTransitions;
+    @Mock
     private RootTaskDisplayAreaOrganizer mRootTaskDisplayAreaOrganizer;
 
     private BackAnimationController mController;
@@ -156,7 +159,8 @@
                         mContentResolver,
                         mAnimationBackground,
                         mShellBackAnimationRegistry,
-                        mShellCommandHandler);
+                        mShellCommandHandler,
+                        mTransitions);
         mShellInit.init();
         mShellExecutor.flushAll();
         mTouchableRegion = new Rect(0, 0, 100, 100);
@@ -316,7 +320,8 @@
                         mContentResolver,
                         mAnimationBackground,
                         mShellBackAnimationRegistry,
-                        mShellCommandHandler);
+                        mShellCommandHandler,
+                        mTransitions);
         shellInit.init();
         registerAnimation(BackNavigationInfo.TYPE_RETURN_TO_HOME);
 
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java
index 2c0aa12..764d5a9 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java
@@ -83,7 +83,7 @@
             }
         }, mExecutor) {
             @Override
-            void removeImeSurface() { }
+            void removeImeSurface(int displayId) { }
         }.new PerDisplay(DEFAULT_DISPLAY, ROTATION_0);
     }
 
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/ImeListenerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/ImeListenerTest.kt
new file mode 100644
index 0000000..3b0a072
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/ImeListenerTest.kt
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.common
+
+import android.content.res.Configuration
+import android.content.res.Resources
+import android.graphics.Insets
+import android.graphics.Rect
+import android.testing.AndroidTestingRunner
+import android.view.DisplayCutout
+import android.view.DisplayInfo
+import android.view.InsetsSource.ID_IME
+import android.view.InsetsState
+import android.view.Surface
+import android.view.WindowInsets.Type
+import androidx.test.filters.SmallTest
+import com.android.internal.R
+import com.android.wm.shell.ShellTestCase
+import junit.framework.Assert.assertEquals
+import junit.framework.Assert.assertFalse
+import junit.framework.Assert.assertTrue
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.kotlin.whenever
+
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class ImeListenerTest : ShellTestCase() {
+    private lateinit var imeListener: CachingImeListener
+    private lateinit var displayLayout: DisplayLayout
+
+    @Mock private lateinit var displayController: DisplayController
+    @Before
+    fun setUp() {
+        val resources = createResources(40, 50, false)
+        val displayInfo = createDisplayInfo(1000, 1500, 0, Surface.ROTATION_0)
+        displayLayout = DisplayLayout(displayInfo, resources, false, false)
+        whenever(displayController.getDisplayLayout(DEFAULT_DISPLAY_ID)).thenReturn(displayLayout)
+        imeListener = CachingImeListener(displayController, DEFAULT_DISPLAY_ID)
+    }
+
+    @Test
+    fun testImeAppears() {
+        val insetsState = createInsetsStateWithIme(true, DEFAULT_IME_HEIGHT)
+        imeListener.insetsChanged(insetsState)
+        assertTrue("Ime insets source should become visible", imeListener.cachedImeVisible)
+        assertEquals(DEFAULT_IME_HEIGHT, imeListener.cachedImeHeight)
+    }
+
+    @Test
+    fun testImeAppears_thenDisappears() {
+        // Send insetsState with an IME as a visible source.
+        val insetsStateWithIme = createInsetsStateWithIme(true, DEFAULT_IME_HEIGHT)
+        imeListener.insetsChanged(insetsStateWithIme)
+
+        // Send insetsState without IME.
+        val insetsStateWithoutIme = createInsetsStateWithIme(false, 0)
+        imeListener.insetsChanged(insetsStateWithoutIme)
+
+        assertFalse("Ime insets source should become invisible",
+                imeListener.cachedImeVisible)
+        assertEquals(0, imeListener.cachedImeHeight)
+    }
+
+    private fun createInsetsStateWithIme(isVisible: Boolean, imeHeight: Int): InsetsState {
+        val stableBounds = Rect()
+        displayLayout.getStableBounds(stableBounds)
+        val insetsState = InsetsState()
+
+        val insetsSource = insetsState.getOrCreateSource(ID_IME, Type.ime())
+        insetsSource.setVisible(isVisible)
+        insetsSource.setFrame(stableBounds.left, stableBounds.bottom - imeHeight,
+                stableBounds.right, stableBounds.bottom)
+        return insetsState
+    }
+
+    private fun createDisplayInfo(width: Int, height: Int, cutoutHeight: Int,
+                                  rotation: Int): DisplayInfo {
+        val info = DisplayInfo()
+        info.logicalWidth = width
+        info.logicalHeight = height
+        info.rotation = rotation
+        if (cutoutHeight > 0) {
+            info.displayCutout = DisplayCutout(
+                    Insets.of(0, cutoutHeight, 0, 0) /* safeInsets */,
+                    null /* boundLeft */,
+                    Rect(width / 2 - cutoutHeight, 0, width / 2 + cutoutHeight,
+                            cutoutHeight) /* boundTop */, null /* boundRight */,
+                    null /* boundBottom */)
+        } else {
+            info.displayCutout = DisplayCutout.NO_CUTOUT
+        }
+        info.logicalDensityDpi = 300
+        return info
+    }
+
+    private fun createResources(navLand: Int, navPort: Int, navMoves: Boolean): Resources {
+        val cfg = Configuration()
+        cfg.uiMode = Configuration.UI_MODE_TYPE_NORMAL
+        val res = Mockito.mock(Resources::class.java)
+        Mockito.doReturn(navLand).whenever(res).getDimensionPixelSize(
+                R.dimen.navigation_bar_height_landscape_car_mode)
+        Mockito.doReturn(navPort).whenever(res).getDimensionPixelSize(
+                R.dimen.navigation_bar_height_car_mode)
+        Mockito.doReturn(navLand).whenever(res).getDimensionPixelSize(
+                R.dimen.navigation_bar_width_car_mode)
+        Mockito.doReturn(navLand).whenever(res).getDimensionPixelSize(
+                R.dimen.navigation_bar_height_landscape)
+        Mockito.doReturn(navPort).whenever(res).getDimensionPixelSize(
+                R.dimen.navigation_bar_height)
+        Mockito.doReturn(navLand).whenever(res).getDimensionPixelSize(
+                R.dimen.navigation_bar_width)
+        Mockito.doReturn(navMoves).whenever(res).getBoolean(R.bool.config_navBarCanMove)
+        Mockito.doReturn(cfg).whenever(res).configuration
+        return res
+    }
+
+    private class CachingImeListener(
+            displayController: DisplayController,
+            displayId: Int
+    ) : ImeListener(displayController, displayId) {
+        var cachedImeVisible = false
+        var cachedImeHeight = 0
+        public override fun onImeVisibilityChanged(imeVisible: Boolean, imeHeight: Int) {
+            cachedImeVisible = imeVisible
+            cachedImeHeight = imeHeight
+        }
+    }
+
+    companion object {
+        private const val DEFAULT_DISPLAY_ID = 0
+        private const val DEFAULT_IME_HEIGHT = 500
+    }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
index fc7a777..77e22cd 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
@@ -16,8 +16,6 @@
 
 package com.android.wm.shell.compatui;
 
-import static android.app.CameraCompatTaskInfo.CAMERA_COMPAT_CONTROL_HIDDEN;
-import static android.app.CameraCompatTaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED;
 import static android.view.WindowInsets.Type.navigationBars;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
@@ -34,7 +32,6 @@
 import static org.mockito.Mockito.verify;
 
 import android.app.ActivityManager.RunningTaskInfo;
-import android.app.CameraCompatTaskInfo.CameraCompatControlState;
 import android.app.TaskInfo;
 import android.content.Context;
 import android.content.res.Configuration;
@@ -199,8 +196,7 @@
     @Test
     @RequiresFlagsDisabled(Flags.FLAG_APP_COMPAT_UI_FRAMEWORK)
     public void testOnCompatInfoChanged() {
-        TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, /* hasSizeCompat= */ true,
-                CAMERA_COMPAT_CONTROL_HIDDEN);
+        TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, /* hasSizeCompat= */ true);
 
         // Verify that the compat controls are added with non-null task listener.
         mController.onCompatInfoChanged(new CompatUIInfo(taskInfo, mMockTaskListener));
@@ -214,8 +210,7 @@
         // Verify that the compat controls and letterbox education are updated with new size compat
         // info.
         clearInvocations(mMockCompatLayout, mMockLetterboxEduLayout, mController);
-        taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, /* hasSizeCompat= */ true,
-                CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED);
+        taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, /* hasSizeCompat= */ true);
         mController.onCompatInfoChanged(new CompatUIInfo(taskInfo, mMockTaskListener));
 
         verify(mMockCompatLayout).updateCompatInfo(taskInfo, mMockTaskListener,
@@ -228,7 +223,7 @@
         // Verify that compat controls and letterbox education are removed with null task listener.
         clearInvocations(mMockCompatLayout, mMockLetterboxEduLayout, mController);
         mController.onCompatInfoChanged(new CompatUIInfo(createTaskInfo(DISPLAY_ID, TASK_ID,
-                /* hasSizeCompat= */ true, CAMERA_COMPAT_CONTROL_HIDDEN),
+                /* hasSizeCompat= */ true),
                 /* taskListener= */ null));
 
         verify(mMockCompatLayout).release();
@@ -243,8 +238,7 @@
         doReturn(false).when(mMockLetterboxEduLayout).createLayout(anyBoolean());
         doReturn(false).when(mMockRestartDialogLayout).createLayout(anyBoolean());
 
-        TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, /* hasSizeCompat= */ true,
-                CAMERA_COMPAT_CONTROL_HIDDEN);
+        TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, /* hasSizeCompat= */ true);
         mController.onCompatInfoChanged(new CompatUIInfo(taskInfo, mMockTaskListener));
 
         verify(mController).createCompatUiWindowManager(any(), eq(taskInfo), eq(mMockTaskListener));
@@ -274,8 +268,7 @@
         doReturn(false).when(mMockLetterboxEduLayout).updateCompatInfo(any(), any(), anyBoolean());
         doReturn(false).when(mMockRestartDialogLayout).updateCompatInfo(any(), any(), anyBoolean());
 
-        TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, /* hasSizeCompat= */ true,
-                CAMERA_COMPAT_CONTROL_HIDDEN);
+        TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, /* hasSizeCompat= */ true);
         mController.onCompatInfoChanged(new CompatUIInfo(taskInfo, mMockTaskListener));
 
         verify(mController).createCompatUiWindowManager(any(), eq(taskInfo), eq(mMockTaskListener));
@@ -326,7 +319,7 @@
     public void testOnDisplayRemoved() {
         mController.onDisplayAdded(DISPLAY_ID);
         mController.onCompatInfoChanged(new CompatUIInfo(createTaskInfo(DISPLAY_ID, TASK_ID,
-                /* hasSizeCompat= */ true, CAMERA_COMPAT_CONTROL_HIDDEN), mMockTaskListener));
+                /* hasSizeCompat= */ true), mMockTaskListener));
 
         mController.onDisplayRemoved(DISPLAY_ID + 1);
 
@@ -348,7 +341,7 @@
     @RequiresFlagsDisabled(Flags.FLAG_APP_COMPAT_UI_FRAMEWORK)
     public void testOnDisplayConfigurationChanged() {
         mController.onCompatInfoChanged(new CompatUIInfo(createTaskInfo(DISPLAY_ID, TASK_ID,
-                /* hasSizeCompat= */ true, CAMERA_COMPAT_CONTROL_HIDDEN), mMockTaskListener));
+                /* hasSizeCompat= */ true), mMockTaskListener));
 
         mController.onDisplayConfigurationChanged(DISPLAY_ID + 1, new Configuration());
 
@@ -368,7 +361,7 @@
     public void testInsetsChanged() {
         mController.onDisplayAdded(DISPLAY_ID);
         mController.onCompatInfoChanged(new CompatUIInfo(createTaskInfo(DISPLAY_ID, TASK_ID,
-                /* hasSizeCompat= */ true, CAMERA_COMPAT_CONTROL_HIDDEN), mMockTaskListener));
+                /* hasSizeCompat= */ true), mMockTaskListener));
         InsetsState insetsState = new InsetsState();
         InsetsSource insetsSource = new InsetsSource(
                 InsetsSource.createId(null, 0, navigationBars()), navigationBars());
@@ -395,7 +388,7 @@
     @RequiresFlagsDisabled(Flags.FLAG_APP_COMPAT_UI_FRAMEWORK)
     public void testChangeLayoutsVisibilityOnImeShowHide() {
         mController.onCompatInfoChanged(new CompatUIInfo(createTaskInfo(DISPLAY_ID, TASK_ID,
-                /* hasSizeCompat= */ true, CAMERA_COMPAT_CONTROL_HIDDEN), mMockTaskListener));
+                /* hasSizeCompat= */ true), mMockTaskListener));
 
         // Verify that the restart button is hidden after IME is showing.
         mController.onImeVisibilityChanged(DISPLAY_ID, /* isShowing= */ true);
@@ -405,8 +398,7 @@
         verify(mMockRestartDialogLayout).updateVisibility(false);
 
         // Verify button remains hidden while IME is showing.
-        TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, /* hasSizeCompat= */ true,
-                CAMERA_COMPAT_CONTROL_HIDDEN);
+        TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, /* hasSizeCompat= */ true);
         mController.onCompatInfoChanged(new CompatUIInfo(taskInfo, mMockTaskListener));
 
         verify(mMockCompatLayout).updateCompatInfo(taskInfo, mMockTaskListener,
@@ -428,7 +420,7 @@
     @RequiresFlagsDisabled(Flags.FLAG_APP_COMPAT_UI_FRAMEWORK)
     public void testChangeLayoutsVisibilityOnKeyguardShowingChanged() {
         mController.onCompatInfoChanged(new CompatUIInfo(createTaskInfo(DISPLAY_ID, TASK_ID,
-                /* hasSizeCompat= */ true, CAMERA_COMPAT_CONTROL_HIDDEN), mMockTaskListener));
+                /* hasSizeCompat= */ true), mMockTaskListener));
 
         // Verify that the restart button is hidden after keyguard becomes showing.
         mController.onKeyguardVisibilityChanged(true, false, false);
@@ -438,8 +430,7 @@
         verify(mMockRestartDialogLayout).updateVisibility(false);
 
         // Verify button remains hidden while keyguard is showing.
-        TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, /* hasSizeCompat= */ true,
-                CAMERA_COMPAT_CONTROL_HIDDEN);
+        TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, /* hasSizeCompat= */ true);
         mController.onCompatInfoChanged(new CompatUIInfo(taskInfo, mMockTaskListener));
 
         verify(mMockCompatLayout).updateCompatInfo(taskInfo, mMockTaskListener,
@@ -461,7 +452,7 @@
     @RequiresFlagsDisabled(Flags.FLAG_APP_COMPAT_UI_FRAMEWORK)
     public void testLayoutsRemainHiddenOnKeyguardShowingFalseWhenImeIsShowing() {
         mController.onCompatInfoChanged(new CompatUIInfo(createTaskInfo(DISPLAY_ID, TASK_ID,
-                /* hasSizeCompat= */ true, CAMERA_COMPAT_CONTROL_HIDDEN), mMockTaskListener));
+                /* hasSizeCompat= */ true), mMockTaskListener));
 
         mController.onImeVisibilityChanged(DISPLAY_ID, /* isShowing= */ true);
         mController.onKeyguardVisibilityChanged(true, false, false);
@@ -491,7 +482,7 @@
     @RequiresFlagsDisabled(Flags.FLAG_APP_COMPAT_UI_FRAMEWORK)
     public void testLayoutsRemainHiddenOnImeHideWhenKeyguardIsShowing() {
         mController.onCompatInfoChanged(new CompatUIInfo(createTaskInfo(DISPLAY_ID, TASK_ID,
-                /* hasSizeCompat= */ true, CAMERA_COMPAT_CONTROL_HIDDEN), mMockTaskListener));
+                /* hasSizeCompat= */ true), mMockTaskListener));
 
         mController.onImeVisibilityChanged(DISPLAY_ID, /* isShowing= */ true);
         mController.onKeyguardVisibilityChanged(true, false, false);
@@ -520,8 +511,7 @@
     @Test
     @RequiresFlagsDisabled(Flags.FLAG_APP_COMPAT_UI_FRAMEWORK)
     public void testRestartLayoutRecreatedIfNeeded() {
-        final TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID,
-                /* hasSizeCompat= */ true, CAMERA_COMPAT_CONTROL_HIDDEN);
+        final TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, /* hasSizeCompat= */ true);
         doReturn(true).when(mMockRestartDialogLayout)
                 .needsToBeRecreated(any(TaskInfo.class),
                         any(ShellTaskOrganizer.TaskListener.class));
@@ -536,8 +526,7 @@
     @Test
     @RequiresFlagsDisabled(Flags.FLAG_APP_COMPAT_UI_FRAMEWORK)
     public void testRestartLayoutNotRecreatedIfNotNeeded() {
-        final TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID,
-                /* hasSizeCompat= */ true, CAMERA_COMPAT_CONTROL_HIDDEN);
+        final TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, /* hasSizeCompat= */ true);
         doReturn(false).when(mMockRestartDialogLayout)
                 .needsToBeRecreated(any(TaskInfo.class),
                         any(ShellTaskOrganizer.TaskListener.class));
@@ -557,9 +546,8 @@
         Assert.assertTrue(mController.hasShownUserAspectRatioSettingsButton());
 
         // Create new task
-        final TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID,
-                /* hasSizeCompat= */ true, CAMERA_COMPAT_CONTROL_HIDDEN, /* isVisible */ true,
-                /* isFocused */ true);
+        final TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, /* hasSizeCompat= */ true,
+                /* isVisible */ true, /* isFocused */ true);
 
         // Simulate new task being shown
         mController.updateActiveTaskInfo(taskInfo);
@@ -574,9 +562,8 @@
     @RequiresFlagsDisabled(Flags.FLAG_APP_COMPAT_UI_FRAMEWORK)
     public void testUpdateActiveTaskInfo_newTask_notVisibleOrFocused_notUpdated() {
         // Create new task
-        final TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID,
-                /* hasSizeCompat= */ true, CAMERA_COMPAT_CONTROL_HIDDEN, /* isVisible */ true,
-                /* isFocused */ true);
+        final TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, /* hasSizeCompat= */ true,
+                /* isVisible */ true, /* isFocused */ true);
 
         // Simulate task being shown
         mController.updateActiveTaskInfo(taskInfo);
@@ -593,9 +580,8 @@
         final int newTaskId = TASK_ID + 1;
 
         // Create visible but NOT focused task
-        final TaskInfo taskInfo1 = createTaskInfo(DISPLAY_ID, newTaskId,
-                /* hasSizeCompat= */ true, CAMERA_COMPAT_CONTROL_HIDDEN, /* isVisible */ true,
-                /* isFocused */ false);
+        final TaskInfo taskInfo1 = createTaskInfo(DISPLAY_ID, newTaskId, /* hasSizeCompat= */ true,
+                /* isVisible */ true, /* isFocused */ false);
 
         // Simulate new task being shown
         mController.updateActiveTaskInfo(taskInfo1);
@@ -606,9 +592,8 @@
         Assert.assertTrue(mController.hasShownUserAspectRatioSettingsButton());
 
         // Create focused but NOT visible task
-        final TaskInfo taskInfo2 = createTaskInfo(DISPLAY_ID, newTaskId,
-                /* hasSizeCompat= */ true, CAMERA_COMPAT_CONTROL_HIDDEN, /* isVisible */ false,
-                /* isFocused */ true);
+        final TaskInfo taskInfo2 = createTaskInfo(DISPLAY_ID, newTaskId, /* hasSizeCompat= */ true,
+                /* isVisible */ false, /* isFocused */ true);
 
         // Simulate new task being shown
         mController.updateActiveTaskInfo(taskInfo2);
@@ -619,9 +604,8 @@
         Assert.assertTrue(mController.hasShownUserAspectRatioSettingsButton());
 
         // Create NOT focused but NOT visible task
-        final TaskInfo taskInfo3 = createTaskInfo(DISPLAY_ID, newTaskId,
-                /* hasSizeCompat= */ true, CAMERA_COMPAT_CONTROL_HIDDEN, /* isVisible */ false,
-                /* isFocused */ false);
+        final TaskInfo taskInfo3 = createTaskInfo(DISPLAY_ID, newTaskId, /* hasSizeCompat= */ true,
+                /* isVisible */ false, /* isFocused */ false);
 
         // Simulate new task being shown
         mController.updateActiveTaskInfo(taskInfo3);
@@ -636,9 +620,8 @@
     @RequiresFlagsDisabled(Flags.FLAG_APP_COMPAT_UI_FRAMEWORK)
     public void testUpdateActiveTaskInfo_sameTask_notUpdated() {
         // Create new task
-        final TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID,
-                /* hasSizeCompat= */ true, CAMERA_COMPAT_CONTROL_HIDDEN, /* isVisible */ true,
-                /* isFocused */ true);
+        final TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, /* hasSizeCompat= */ true,
+                /* isVisible */ true, /* isFocused */ true);
 
         // Simulate new task being shown
         mController.updateActiveTaskInfo(taskInfo);
@@ -665,9 +648,8 @@
     @RequiresFlagsDisabled(Flags.FLAG_APP_COMPAT_UI_FRAMEWORK)
     public void testUpdateActiveTaskInfo_transparentTask_notUpdated() {
         // Create new task
-        final TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID,
-                /* hasSizeCompat= */ true, CAMERA_COMPAT_CONTROL_HIDDEN, /* isVisible */ true,
-                /* isFocused */ true);
+        final TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, /* hasSizeCompat= */ true,
+                /* isVisible */ true, /* isFocused */ true);
 
         // Simulate new task being shown
         mController.updateActiveTaskInfo(taskInfo);
@@ -684,9 +666,8 @@
         final int newTaskId = TASK_ID + 1;
 
         // Create transparent task
-        final TaskInfo taskInfo1 = createTaskInfo(DISPLAY_ID, newTaskId,
-                /* hasSizeCompat= */ true, CAMERA_COMPAT_CONTROL_HIDDEN, /* isVisible */ true,
-                /* isFocused */ true, /* isTopActivityTransparent */ true);
+        final TaskInfo taskInfo1 = createTaskInfo(DISPLAY_ID, newTaskId, /* hasSizeCompat= */ true,
+                /* isVisible */ true, /* isFocused */ true, /* isTopActivityTransparent */ true);
 
         // Simulate new task being shown
         mController.updateActiveTaskInfo(taskInfo1);
@@ -699,8 +680,7 @@
 
     @Test
     public void testLetterboxEduLayout_notCreatedWhenLetterboxEducationIsDisabled() {
-        TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, /* hasSizeCompat= */ true,
-                CAMERA_COMPAT_CONTROL_HIDDEN);
+        TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, /* hasSizeCompat= */ true);
         taskInfo.appCompatTaskInfo.isLetterboxEducationEnabled = false;
 
         mController.onCompatInfoChanged(new CompatUIInfo(taskInfo, mMockTaskListener));
@@ -709,29 +689,23 @@
                 eq(mMockTaskListener));
     }
 
-    private static TaskInfo createTaskInfo(int displayId, int taskId, boolean hasSizeCompat,
-            @CameraCompatControlState int cameraCompatControlState) {
-        return createTaskInfo(displayId, taskId, hasSizeCompat, cameraCompatControlState,
-                /* isVisible */ false, /* isFocused */ false,
-                /* isTopActivityTransparent */ false);
+    private static TaskInfo createTaskInfo(int displayId, int taskId, boolean hasSizeCompat) {
+        return createTaskInfo(displayId, taskId, hasSizeCompat, /* isVisible */ false,
+                /* isFocused */ false, /* isTopActivityTransparent */ false);
     }
 
     private static TaskInfo createTaskInfo(int displayId, int taskId, boolean hasSizeCompat,
-            @CameraCompatControlState int cameraCompatControlState, boolean isVisible,
-            boolean isFocused) {
-        return createTaskInfo(displayId, taskId, hasSizeCompat, cameraCompatControlState,
+            boolean isVisible, boolean isFocused) {
+        return createTaskInfo(displayId, taskId, hasSizeCompat,
                 isVisible, isFocused, /* isTopActivityTransparent */ false);
     }
 
     private static TaskInfo createTaskInfo(int displayId, int taskId, boolean hasSizeCompat,
-            @CameraCompatControlState int cameraCompatControlState, boolean isVisible,
-            boolean isFocused, boolean isTopActivityTransparent) {
+            boolean isVisible, boolean isFocused, boolean isTopActivityTransparent) {
         RunningTaskInfo taskInfo = new RunningTaskInfo();
         taskInfo.taskId = taskId;
         taskInfo.displayId = displayId;
         taskInfo.appCompatTaskInfo.topActivityInSizeCompat = hasSizeCompat;
-        taskInfo.appCompatTaskInfo.cameraCompatTaskInfo.cameraCompatControlState =
-                cameraCompatControlState;
         taskInfo.isVisible = isVisible;
         taskInfo.isFocused = isFocused;
         taskInfo.isTopActivityTransparent = isTopActivityTransparent;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUILayoutTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUILayoutTest.java
index 33d69f5..3b93861 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUILayoutTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUILayoutTest.java
@@ -16,20 +16,13 @@
 
 package com.android.wm.shell.compatui;
 
-import static android.app.CameraCompatTaskInfo.CAMERA_COMPAT_CONTROL_DISMISSED;
-import static android.app.CameraCompatTaskInfo.CAMERA_COMPAT_CONTROL_HIDDEN;
-import static android.app.CameraCompatTaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED;
-import static android.app.CameraCompatTaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED;
-
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 
-import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.verify;
 
 import android.app.ActivityManager;
-import android.app.CameraCompatTaskInfo.CameraCompatControlState;
 import android.app.TaskInfo;
 import android.graphics.Rect;
 import android.platform.test.annotations.RequiresFlagsDisabled;
@@ -52,7 +45,6 @@
 import com.android.wm.shell.common.SyncTransactionQueue;
 import com.android.wm.shell.compatui.CompatUIController.CompatUIHintsState;
 import com.android.wm.shell.compatui.api.CompatUIEvent;
-import com.android.wm.shell.compatui.impl.CompatUIEvents;
 
 import junit.framework.Assert;
 
@@ -97,7 +89,7 @@
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         doReturn(100).when(mCompatUIConfiguration).getHideSizeCompatRestartButtonTolerance();
-        mTaskInfo = createTaskInfo(/* hasSizeCompat= */ false, CAMERA_COMPAT_CONTROL_HIDDEN);
+        mTaskInfo = createTaskInfo(/* hasSizeCompat= */ false);
         mWindowManager = new CompatUIWindowManager(mContext, mTaskInfo, mSyncTransactionQueue,
                 mCallback, mTaskListener, new DisplayLayout(), new CompatUIHintsState(),
                 mCompatUIConfiguration, mOnRestartButtonClicked);
@@ -151,113 +143,13 @@
         verify(mLayout).setSizeCompatHintVisibility(/* show= */ false);
     }
 
-    @Test
-    @RequiresFlagsDisabled(Flags.FLAG_APP_COMPAT_UI_FRAMEWORK)
-    public void testUpdateCameraTreatmentButton_treatmentAppliedByDefault() {
-        mWindowManager.mCameraCompatControlState = CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED;
-        mWindowManager.createLayout(/* canShow= */ true);
-        final ImageButton button =
-                mLayout.findViewById(R.id.camera_compat_treatment_button);
-        button.performClick();
-
-        verify(mWindowManager).onCameraTreatmentButtonClicked();
-        verifyOnCameraControlStateUpdatedInvokedWith(TASK_ID,
-                CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
-
-        button.performClick();
-
-        verifyOnCameraControlStateUpdatedInvokedWith(TASK_ID,
-                CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED);
-    }
-
-    @Test
-    @RequiresFlagsDisabled(Flags.FLAG_APP_COMPAT_UI_FRAMEWORK)
-    public void testUpdateCameraTreatmentButton_treatmentSuggestedByDefault() {
-        mWindowManager.mCameraCompatControlState = CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED;
-        mWindowManager.createLayout(/* canShow= */ true);
-        final ImageButton button =
-                mLayout.findViewById(R.id.camera_compat_treatment_button);
-        button.performClick();
-
-        verify(mWindowManager).onCameraTreatmentButtonClicked();
-        verifyOnCameraControlStateUpdatedInvokedWith(TASK_ID,
-                CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED);
-
-        button.performClick();
-
-        verifyOnCameraControlStateUpdatedInvokedWith(TASK_ID,
-                CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
-    }
-
-    @Test
-    @RequiresFlagsDisabled(Flags.FLAG_APP_COMPAT_UI_FRAMEWORK)
-    public void testOnCameraDismissButtonClicked() {
-        mWindowManager.mCameraCompatControlState = CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED;
-        mWindowManager.createLayout(/* canShow= */ true);
-        final ImageButton button =
-                mLayout.findViewById(R.id.camera_compat_dismiss_button);
-        button.performClick();
-
-        verify(mWindowManager).onCameraDismissButtonClicked();
-        verifyOnCameraControlStateUpdatedInvokedWith(TASK_ID, CAMERA_COMPAT_CONTROL_DISMISSED);
-        verify(mLayout).setCameraControlVisibility(/* show */ false);
-    }
-
-    @Test
-    @RequiresFlagsDisabled(Flags.FLAG_APP_COMPAT_UI_FRAMEWORK)
-    public void testOnLongClickForCameraTreatmentButton() {
-        doNothing().when(mWindowManager).onCameraButtonLongClicked();
-
-        final ImageButton button =
-                mLayout.findViewById(R.id.camera_compat_treatment_button);
-        button.performLongClick();
-
-        verify(mWindowManager).onCameraButtonLongClicked();
-    }
-
-    @Test
-    @RequiresFlagsDisabled(Flags.FLAG_APP_COMPAT_UI_FRAMEWORK)
-    public void testOnLongClickForCameraDismissButton() {
-        doNothing().when(mWindowManager).onCameraButtonLongClicked();
-
-        final ImageButton button = mLayout.findViewById(R.id.camera_compat_dismiss_button);
-        button.performLongClick();
-
-        verify(mWindowManager).onCameraButtonLongClicked();
-    }
-
-    @Test
-    @RequiresFlagsDisabled(Flags.FLAG_APP_COMPAT_UI_FRAMEWORK)
-    public void testOnClickForCameraCompatHint() {
-        mWindowManager.mCameraCompatControlState = CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED;
-        mWindowManager.createLayout(/* canShow= */ true);
-        final LinearLayout hint = mLayout.findViewById(R.id.camera_compat_hint);
-        hint.performClick();
-
-        verify(mLayout).setCameraCompatHintVisibility(/* show= */ false);
-    }
-
-    private static TaskInfo createTaskInfo(boolean hasSizeCompat,
-            @CameraCompatControlState int cameraCompatControlState) {
+    private static TaskInfo createTaskInfo(boolean hasSizeCompat) {
         ActivityManager.RunningTaskInfo taskInfo = new ActivityManager.RunningTaskInfo();
         taskInfo.taskId = TASK_ID;
         taskInfo.appCompatTaskInfo.topActivityInSizeCompat = hasSizeCompat;
-        taskInfo.appCompatTaskInfo.cameraCompatTaskInfo.cameraCompatControlState =
-                cameraCompatControlState;
         taskInfo.appCompatTaskInfo.topActivityLetterboxHeight = 1000;
         taskInfo.appCompatTaskInfo.topActivityLetterboxWidth = 1000;
         taskInfo.configuration.windowConfiguration.setBounds(new Rect(0, 0, 2000, 2000));
         return taskInfo;
     }
-
-    private void verifyOnCameraControlStateUpdatedInvokedWith(int taskId, int state) {
-        final ArgumentCaptor<CompatUIEvent> captureValue = ArgumentCaptor.forClass(
-                CompatUIEvent.class);
-        verify(mCallback).accept(captureValue.capture());
-        final CompatUIEvents.CameraControlStateUpdated compatUIEvent =
-                (CompatUIEvents.CameraControlStateUpdated) captureValue.getValue();
-        Assert.assertEquals((compatUIEvent).getTaskId(), taskId);
-        Assert.assertEquals((compatUIEvent).getState(), state);
-        clearInvocations(mCallback);
-    }
 }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java
index eb3da8f..c5033f3 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java
@@ -16,10 +16,6 @@
 
 package com.android.wm.shell.compatui;
 
-import static android.app.CameraCompatTaskInfo.CAMERA_COMPAT_CONTROL_DISMISSED;
-import static android.app.CameraCompatTaskInfo.CAMERA_COMPAT_CONTROL_HIDDEN;
-import static android.app.CameraCompatTaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED;
-import static android.app.CameraCompatTaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED;
 import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT;
 import static android.view.WindowInsets.Type.navigationBars;
 import static android.view.WindowManager.LARGE_SCREEN_SMALLEST_SCREEN_WIDTH_DP;
@@ -36,10 +32,10 @@
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
 import android.app.ActivityManager;
-import android.app.CameraCompatTaskInfo;
 import android.app.TaskInfo;
 import android.content.res.Configuration;
 import android.graphics.Rect;
@@ -65,7 +61,6 @@
 import com.android.wm.shell.common.SyncTransactionQueue;
 import com.android.wm.shell.compatui.CompatUIController.CompatUIHintsState;
 import com.android.wm.shell.compatui.api.CompatUIEvent;
-import com.android.wm.shell.compatui.impl.CompatUIEvents;
 
 import junit.framework.Assert;
 
@@ -115,7 +110,7 @@
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         doReturn(100).when(mCompatUIConfiguration).getHideSizeCompatRestartButtonTolerance();
-        mTaskInfo = createTaskInfo(/* hasSizeCompat= */ false, CAMERA_COMPAT_CONTROL_HIDDEN);
+        mTaskInfo = createTaskInfo(/* hasSizeCompat= */ false);
 
         final DisplayInfo displayInfo = new DisplayInfo();
         displayInfo.logicalWidth = TASK_WIDTH;
@@ -186,45 +181,6 @@
 
     @Test
     @RequiresFlagsDisabled(FLAG_APP_COMPAT_UI_FRAMEWORK)
-    public void testCreateCameraCompatControl() {
-        // Doesn't create layout if show is false.
-        mWindowManager.mCameraCompatControlState = CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED;
-        assertTrue(mWindowManager.createLayout(/* canShow= */ false));
-
-        verify(mWindowManager, never()).inflateLayout();
-
-        // Doesn't create hint popup.
-        mWindowManager.mCompatUIHintsState.mHasShownCameraCompatHint = true;
-        assertTrue(mWindowManager.createLayout(/* canShow= */ true));
-
-        verify(mWindowManager).inflateLayout();
-        verify(mLayout).setCameraControlVisibility(/* show= */ true);
-        verify(mLayout, never()).setCameraCompatHintVisibility(/* show= */ true);
-
-        // Creates hint popup.
-        clearInvocations(mWindowManager);
-        clearInvocations(mLayout);
-        mWindowManager.release();
-        mWindowManager.mCompatUIHintsState.mHasShownCameraCompatHint = false;
-        assertTrue(mWindowManager.createLayout(/* canShow= */ true));
-
-        verify(mWindowManager).inflateLayout();
-        assertNotNull(mLayout);
-        verify(mLayout).setCameraControlVisibility(/* show= */ true);
-        verify(mLayout).setCameraCompatHintVisibility(/* show= */ true);
-        assertTrue(mWindowManager.mCompatUIHintsState.mHasShownCameraCompatHint);
-
-        // Returns false and doesn't create layout if Camera Compat state is hidden
-        clearInvocations(mWindowManager);
-        mWindowManager.release();
-        mWindowManager.mCameraCompatControlState = CAMERA_COMPAT_CONTROL_HIDDEN;
-        assertFalse(mWindowManager.createLayout(/* canShow= */ true));
-
-        verify(mWindowManager, never()).inflateLayout();
-    }
-
-    @Test
-    @RequiresFlagsDisabled(FLAG_APP_COMPAT_UI_FRAMEWORK)
     public void testRelease() {
         mWindowManager.mHasSizeCompat = true;
         mWindowManager.createLayout(/* canShow= */ true);
@@ -241,10 +197,11 @@
     public void testUpdateCompatInfo() {
         mWindowManager.mHasSizeCompat = true;
         mWindowManager.createLayout(/* canShow= */ true);
+        verify(mLayout).setRestartButtonVisibility(/* show= */ true);
 
         // No diff
         clearInvocations(mWindowManager);
-        TaskInfo taskInfo = createTaskInfo(/* hasSizeCompat= */ true, CAMERA_COMPAT_CONTROL_HIDDEN);
+        TaskInfo taskInfo = createTaskInfo(/* hasSizeCompat= */ true);
         doReturn(true).when(mWindowManager).shouldShowSizeCompatRestartButton(any());
         assertTrue(mWindowManager.updateCompatInfo(taskInfo, mTaskListener, /* canShow= */ true));
 
@@ -261,58 +218,25 @@
         verify(mWindowManager).release();
         verify(mWindowManager).createLayout(/* canShow= */ true);
 
-        // Change Camera Compat state, show a control.
+        // Change has Size Compat to false, no more CompatIU.
         clearInvocations(mWindowManager);
         clearInvocations(mLayout);
-        taskInfo = createTaskInfo(/* hasSizeCompat= */ true,
-                CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED);
-        assertTrue(mWindowManager.updateCompatInfo(taskInfo, newTaskListener, /* canShow= */ true));
-
-        verify(mLayout).setCameraControlVisibility(/* show= */ true);
-        verify(mLayout).updateCameraTreatmentButton(
-                CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED);
-
-        // Change Camera Compat state, update a control.
-        clearInvocations(mWindowManager);
-        clearInvocations(mLayout);
-        taskInfo = createTaskInfo(/* hasSizeCompat= */ true,
-                CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
-        assertTrue(mWindowManager.updateCompatInfo(taskInfo, newTaskListener, /* canShow= */ true));
-
-        verify(mLayout).setCameraControlVisibility(/* show= */ true);
-        verify(mLayout).updateCameraTreatmentButton(
-                CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
-
-        // Change has Size Compat to false, hides restart button.
-        clearInvocations(mWindowManager);
-        clearInvocations(mLayout);
-        taskInfo = createTaskInfo(/* hasSizeCompat= */ false,
-                CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
-        assertTrue(mWindowManager.updateCompatInfo(taskInfo, newTaskListener, /* canShow= */ true));
-
-        verify(mLayout).setRestartButtonVisibility(/* show= */ false);
+        taskInfo = createTaskInfo(/* hasSizeCompat= */ false);
+        assertFalse(mWindowManager.updateCompatInfo(taskInfo, newTaskListener,
+                /* canShow= */ true));
 
         // Change has Size Compat to true, shows restart button.
         clearInvocations(mWindowManager);
         clearInvocations(mLayout);
-        taskInfo = createTaskInfo(/* hasSizeCompat= */ true,
-                CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+        taskInfo = createTaskInfo(/* hasSizeCompat= */ true);
         assertTrue(mWindowManager.updateCompatInfo(taskInfo, newTaskListener, /* canShow= */ true));
 
-        verify(mLayout).setRestartButtonVisibility(/* show= */ true);
-
-        // Change Camera Compat state to dismissed, hide a control.
-        clearInvocations(mWindowManager);
-        clearInvocations(mLayout);
-        taskInfo = createTaskInfo(/* hasSizeCompat= */ true, CAMERA_COMPAT_CONTROL_DISMISSED);
-        assertTrue(mWindowManager.updateCompatInfo(taskInfo, newTaskListener, /* canShow= */ true));
-
-        verify(mLayout).setCameraControlVisibility(/* show= */ false);
+        verify(mLayout, times(2)).setRestartButtonVisibility(/* show= */ true);
 
         // Change task bounds, update position.
         clearInvocations(mWindowManager);
         clearInvocations(mLayout);
-        taskInfo = createTaskInfo(/* hasSizeCompat= */ true, CAMERA_COMPAT_CONTROL_HIDDEN);
+        taskInfo = createTaskInfo(/* hasSizeCompat= */ true);
         taskInfo.configuration.windowConfiguration.setBounds(new Rect(0, 1000, 0, 2000));
         assertTrue(mWindowManager.updateCompatInfo(taskInfo, newTaskListener, /* canShow= */ true));
 
@@ -321,7 +245,7 @@
         // Change has Size Compat to false, release layout.
         clearInvocations(mWindowManager);
         clearInvocations(mLayout);
-        taskInfo = createTaskInfo(/* hasSizeCompat= */ false, CAMERA_COMPAT_CONTROL_HIDDEN);
+        taskInfo = createTaskInfo(/* hasSizeCompat= */ false);
         assertFalse(
                 mWindowManager.updateCompatInfo(taskInfo, newTaskListener, /* canShow= */ true));
 
@@ -338,15 +262,14 @@
         // Change topActivityInSizeCompat to false and pass canShow true, layout shouldn't be
         // inflated
         clearInvocations(mWindowManager);
-        TaskInfo taskInfo = createTaskInfo(/* hasSizeCompat= */ false,
-                CAMERA_COMPAT_CONTROL_HIDDEN);
+        TaskInfo taskInfo = createTaskInfo(/* hasSizeCompat= */ false);
         mWindowManager.updateCompatInfo(taskInfo, mTaskListener, /* canShow= */ true);
 
         verify(mWindowManager, never()).inflateLayout();
 
         // Change topActivityInSizeCompat to true and pass canShow true, layout should be inflated.
         clearInvocations(mWindowManager);
-        taskInfo = createTaskInfo(/* hasSizeCompat= */ true, CAMERA_COMPAT_CONTROL_HIDDEN);
+        taskInfo = createTaskInfo(/* hasSizeCompat= */ true);
         mWindowManager.updateCompatInfo(taskInfo, mTaskListener, /* canShow= */ true);
 
         verify(mWindowManager).inflateLayout();
@@ -443,37 +366,6 @@
 
     @Test
     @RequiresFlagsDisabled(FLAG_APP_COMPAT_UI_FRAMEWORK)
-    public void testOnCameraDismissButtonClicked() {
-        mWindowManager.mCameraCompatControlState = CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED;
-        mWindowManager.createLayout(/* canShow= */ true);
-        clearInvocations(mLayout);
-        mWindowManager.onCameraDismissButtonClicked();
-
-        verifyOnCameraControlStateUpdatedInvokedWith(TASK_ID, CAMERA_COMPAT_CONTROL_DISMISSED);
-        verify(mLayout).setCameraControlVisibility(/* show= */ false);
-    }
-
-    @Test
-    @RequiresFlagsDisabled(FLAG_APP_COMPAT_UI_FRAMEWORK)
-    public void testOnCameraTreatmentButtonClicked() {
-        mWindowManager.mCameraCompatControlState = CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED;
-        mWindowManager.createLayout(/* canShow= */ true);
-        clearInvocations(mLayout);
-        mWindowManager.onCameraTreatmentButtonClicked();
-
-        verifyOnCameraControlStateUpdatedInvokedWith(TASK_ID,
-                CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED);
-        verify(mLayout).updateCameraTreatmentButton(CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED);
-
-        mWindowManager.onCameraTreatmentButtonClicked();
-
-        verifyOnCameraControlStateUpdatedInvokedWith(TASK_ID,
-                CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
-        verify(mLayout).updateCameraTreatmentButton(CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
-    }
-
-    @Test
-    @RequiresFlagsDisabled(FLAG_APP_COMPAT_UI_FRAMEWORK)
     public void testOnRestartButtonClicked() {
         mWindowManager.onRestartButtonClicked();
 
@@ -505,22 +397,6 @@
 
     @Test
     @RequiresFlagsDisabled(FLAG_APP_COMPAT_UI_FRAMEWORK)
-    public void testOnCameraControlLongClicked_showHint() {
-       // Not create hint popup.
-        mWindowManager.mCameraCompatControlState = CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED;
-        mWindowManager.mCompatUIHintsState.mHasShownCameraCompatHint = true;
-        mWindowManager.createLayout(/* canShow= */ true);
-
-        verify(mWindowManager).inflateLayout();
-        verify(mLayout, never()).setCameraCompatHintVisibility(/* show= */ true);
-
-        mWindowManager.onCameraButtonLongClicked();
-
-        verify(mLayout).setCameraCompatHintVisibility(/* show= */ true);
-    }
-
-    @Test
-    @RequiresFlagsDisabled(FLAG_APP_COMPAT_UI_FRAMEWORK)
     public void testWhenDockedStateHasChanged_needsToBeRecreated() {
         ActivityManager.RunningTaskInfo newTaskInfo = new ActivityManager.RunningTaskInfo();
         newTaskInfo.configuration.uiMode |= Configuration.UI_MODE_TYPE_DESK;
@@ -538,7 +414,7 @@
                 mCompatUIConfiguration, mOnRestartButtonClicked);
 
         // Simulate rotation of activity in square display
-        TaskInfo taskInfo = createTaskInfo(true, CAMERA_COMPAT_CONTROL_HIDDEN);
+        TaskInfo taskInfo = createTaskInfo(true);
         taskInfo.appCompatTaskInfo.topActivityLetterboxHeight = TASK_HEIGHT;
         taskInfo.appCompatTaskInfo.topActivityLetterboxWidth = 1850;
 
@@ -567,13 +443,10 @@
         assertTrue(mWindowManager.shouldShowSizeCompatRestartButton(taskInfo));
     }
 
-    private static TaskInfo createTaskInfo(boolean hasSizeCompat,
-            @CameraCompatTaskInfo.CameraCompatControlState int cameraCompatControlState) {
+    private static TaskInfo createTaskInfo(boolean hasSizeCompat) {
         ActivityManager.RunningTaskInfo taskInfo = new ActivityManager.RunningTaskInfo();
         taskInfo.taskId = TASK_ID;
         taskInfo.appCompatTaskInfo.topActivityInSizeCompat = hasSizeCompat;
-        taskInfo.appCompatTaskInfo.cameraCompatTaskInfo.cameraCompatControlState =
-                cameraCompatControlState;
         taskInfo.configuration.uiMode &= ~Configuration.UI_MODE_TYPE_DESK;
         // Letterboxed activity that takes half the screen should show size compat restart button
         taskInfo.appCompatTaskInfo.topActivityLetterboxHeight = 1000;
@@ -582,15 +455,4 @@
         taskInfo.configuration.smallestScreenWidthDp = LARGE_SCREEN_SMALLEST_SCREEN_WIDTH_DP;
         return taskInfo;
     }
-
-    private void verifyOnCameraControlStateUpdatedInvokedWith(int taskId, int state) {
-        final ArgumentCaptor<CompatUIEvent> captureValue = ArgumentCaptor.forClass(
-                CompatUIEvent.class);
-        verify(mCallback).accept(captureValue.capture());
-        final CompatUIEvents.CameraControlStateUpdated compatUIEvent =
-                (CompatUIEvents.CameraControlStateUpdated) captureValue.getValue();
-        Assert.assertEquals((compatUIEvent).getTaskId(), taskId);
-        Assert.assertEquals((compatUIEvent).getState(), state);
-        clearInvocations(mCallback);
-    }
 }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsLayoutTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsLayoutTest.java
index 3fa21ce..7a64196 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsLayoutTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsLayoutTest.java
@@ -16,8 +16,6 @@
 
 package com.android.wm.shell.compatui;
 
-import static android.app.CameraCompatTaskInfo.CAMERA_COMPAT_CONTROL_HIDDEN;
-
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 import static com.android.window.flags.Flags.FLAG_APP_COMPAT_UI_FRAMEWORK;
 
@@ -26,7 +24,6 @@
 import static org.mockito.Mockito.verify;
 
 import android.app.ActivityManager;
-import android.app.CameraCompatTaskInfo.CameraCompatControlState;
 import android.app.TaskInfo;
 import android.content.ComponentName;
 import android.platform.test.annotations.RequiresFlagsDisabled;
@@ -98,7 +95,7 @@
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
-        mTaskInfo = createTaskInfo(/* hasSizeCompat= */ false, CAMERA_COMPAT_CONTROL_HIDDEN);
+        mTaskInfo = createTaskInfo(/* hasSizeCompat= */ false);
         mWindowManager = new UserAspectRatioSettingsWindowManager(mContext, mTaskInfo,
                 mSyncTransactionQueue, mTaskListener, new DisplayLayout(),
                 new CompatUIController.CompatUIHintsState(),
@@ -155,13 +152,10 @@
         verify(mLayout).setUserAspectRatioSettingsHintVisibility(/* show= */ false);
     }
 
-    private static TaskInfo createTaskInfo(boolean hasSizeCompat,
-            @CameraCompatControlState int cameraCompatControlState) {
+    private static TaskInfo createTaskInfo(boolean hasSizeCompat) {
         ActivityManager.RunningTaskInfo taskInfo = new ActivityManager.RunningTaskInfo();
         taskInfo.taskId = TASK_ID;
         taskInfo.appCompatTaskInfo.topActivityInSizeCompat = hasSizeCompat;
-        taskInfo.appCompatTaskInfo.cameraCompatTaskInfo.cameraCompatControlState =
-                cameraCompatControlState;
         taskInfo.realActivity = new ComponentName("com.mypackage.test", "TestActivity");
         return taskInfo;
     }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepositoryTest.kt
index 18b08bf..d3404f7 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepositoryTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepositoryTest.kt
@@ -41,32 +41,44 @@
     }
 
     @Test
-    fun addActiveTask_listenerNotifiedAndTaskIsActive() {
+    fun addActiveTask_notifiesListener() {
         val listener = TestListener()
         repo.addActiveTaskListener(listener)
 
         repo.addActiveTask(DEFAULT_DISPLAY, taskId = 1)
+
         assertThat(listener.activeChangesOnDefaultDisplay).isEqualTo(1)
+    }
+
+    @Test
+    fun addActiveTask_taskIsActive() {
+        val listener = TestListener()
+        repo.addActiveTaskListener(listener)
+
+        repo.addActiveTask(DEFAULT_DISPLAY, taskId = 1)
+
         assertThat(repo.isActiveTask(1)).isTrue()
     }
 
     @Test
-    fun addActiveTask_sameTaskDoesNotNotify() {
+    fun addSameActiveTaskTwice_notifiesOnce() {
         val listener = TestListener()
         repo.addActiveTaskListener(listener)
 
         repo.addActiveTask(DEFAULT_DISPLAY, taskId = 1)
         repo.addActiveTask(DEFAULT_DISPLAY, taskId = 1)
+
         assertThat(listener.activeChangesOnDefaultDisplay).isEqualTo(1)
     }
 
     @Test
-    fun addActiveTask_multipleTasksAddedNotifiesForEach() {
+    fun addActiveTask_multipleTasksAdded_notifiesForAllTasks() {
         val listener = TestListener()
         repo.addActiveTaskListener(listener)
 
         repo.addActiveTask(DEFAULT_DISPLAY, taskId = 1)
         repo.addActiveTask(DEFAULT_DISPLAY, taskId = 2)
+
         assertThat(listener.activeChangesOnDefaultDisplay).isEqualTo(2)
     }
 
@@ -84,22 +96,35 @@
     }
 
     @Test
-    fun removeActiveTask_listenerNotifiedAndTaskNotActive() {
+    fun removeActiveTask_notifiesListener() {
         val listener = TestListener()
         repo.addActiveTaskListener(listener)
-
         repo.addActiveTask(DEFAULT_DISPLAY, taskId = 1)
+
         repo.removeActiveTask(1)
+
         // Notify once for add and once for remove
         assertThat(listener.activeChangesOnDefaultDisplay).isEqualTo(2)
+    }
+
+    @Test
+    fun removeActiveTask_taskNotActive() {
+        val listener = TestListener()
+        repo.addActiveTaskListener(listener)
+        repo.addActiveTask(DEFAULT_DISPLAY, taskId = 1)
+
+        repo.removeActiveTask(1)
+
         assertThat(repo.isActiveTask(1)).isFalse()
     }
 
     @Test
-    fun removeActiveTask_removeNotExistingTaskDoesNotNotify() {
+    fun removeActiveTask_nonExistingTask_doesNotNotify() {
         val listener = TestListener()
         repo.addActiveTaskListener(listener)
+
         repo.removeActiveTask(99)
+
         assertThat(listener.activeChangesOnDefaultDisplay).isEqualTo(0)
     }
 
@@ -108,32 +133,38 @@
         val listener = TestListener()
         repo.addActiveTaskListener(listener)
         repo.addActiveTask(DEFAULT_DISPLAY, taskId = 1)
+
         repo.removeActiveTask(1)
+
         assertThat(listener.activeChangesOnSecondaryDisplay).isEqualTo(0)
         assertThat(repo.isActiveTask(1)).isFalse()
     }
 
     @Test
-    fun isActiveTask_notExistingTaskReturnsFalse() {
+    fun isActiveTask_nonExistingTask_returnsFalse() {
         assertThat(repo.isActiveTask(99)).isFalse()
     }
 
     @Test
-    fun isOnlyVisibleNonClosingTask_noTasks() {
+    fun isOnlyVisibleNonClosingTask_noTasks_returnsFalse() {
         // No visible tasks
         assertThat(repo.isOnlyVisibleNonClosingTask(1)).isFalse()
+    }
+
+    @Test
+    fun isClosingTask_noTasks_returnsFalse() {
+        // No visible tasks
         assertThat(repo.isClosingTask(1)).isFalse()
     }
 
     @Test
-    fun isOnlyVisibleNonClosingTask_singleVisibleNonClosingTask() {
-        repo.updateVisibleFreeformTasks(DEFAULT_DISPLAY, taskId = 1, visible = true)
+    fun updateTaskVisibility_singleVisibleNonClosingTask_updatesTasksCorrectly() {
+        repo.updateTaskVisibility(DEFAULT_DISPLAY, taskId = 1, visible = true)
 
-        // The only visible task
         assertThat(repo.isVisibleTask(1)).isTrue()
         assertThat(repo.isClosingTask(1)).isFalse()
         assertThat(repo.isOnlyVisibleNonClosingTask(1)).isTrue()
-        // Not a visible task
+
         assertThat(repo.isVisibleTask(99)).isFalse()
         assertThat(repo.isClosingTask(99)).isFalse()
         assertThat(repo.isOnlyVisibleNonClosingTask(99)).isFalse()
@@ -141,7 +172,7 @@
 
     @Test
     fun isOnlyVisibleNonClosingTask_singleVisibleClosingTask() {
-        repo.updateVisibleFreeformTasks(DEFAULT_DISPLAY, taskId = 1, visible = true)
+        repo.updateTaskVisibility(DEFAULT_DISPLAY, taskId = 1, visible = true)
         repo.addClosingTask(DEFAULT_DISPLAY, 1)
 
         // A visible task that's closing
@@ -155,7 +186,7 @@
 
     @Test
     fun isOnlyVisibleNonClosingTask_singleVisibleMinimizedTask() {
-        repo.updateVisibleFreeformTasks(DEFAULT_DISPLAY, taskId = 1, visible = true)
+        repo.updateTaskVisibility(DEFAULT_DISPLAY, taskId = 1, visible = true)
         repo.minimizeTask(DEFAULT_DISPLAY, 1)
 
         // The visible task that's closing
@@ -169,8 +200,8 @@
 
     @Test
     fun isOnlyVisibleNonClosingTask_multipleVisibleNonClosingTasks() {
-        repo.updateVisibleFreeformTasks(DEFAULT_DISPLAY, taskId = 1, visible = true)
-        repo.updateVisibleFreeformTasks(DEFAULT_DISPLAY, taskId = 2, visible = true)
+        repo.updateTaskVisibility(DEFAULT_DISPLAY, taskId = 1, visible = true)
+        repo.updateTaskVisibility(DEFAULT_DISPLAY, taskId = 2, visible = true)
 
         // Not the only task
         assertThat(repo.isVisibleTask(1)).isTrue()
@@ -188,9 +219,9 @@
 
     @Test
     fun isOnlyVisibleNonClosingTask_multipleDisplays() {
-        repo.updateVisibleFreeformTasks(DEFAULT_DISPLAY, taskId = 1, visible = true)
-        repo.updateVisibleFreeformTasks(DEFAULT_DISPLAY, taskId = 2, visible = true)
-        repo.updateVisibleFreeformTasks(SECOND_DISPLAY, taskId = 3, visible = true)
+        repo.updateTaskVisibility(DEFAULT_DISPLAY, taskId = 1, visible = true)
+        repo.updateTaskVisibility(DEFAULT_DISPLAY, taskId = 2, visible = true)
+        repo.updateTaskVisibility(SECOND_DISPLAY, taskId = 3, visible = true)
 
         // Not the only task on DEFAULT_DISPLAY
         assertThat(repo.isVisibleTask(1)).isTrue()
@@ -207,10 +238,11 @@
     }
 
     @Test
-    fun addListener_notifiesVisibleFreeformTask() {
-        repo.updateVisibleFreeformTasks(DEFAULT_DISPLAY, taskId = 1, visible = true)
+    fun addVisibleTasksListener_notifiesVisibleFreeformTask() {
+        repo.updateTaskVisibility(DEFAULT_DISPLAY, taskId = 1, visible = true)
         val listener = TestVisibilityListener()
         val executor = TestShellExecutor()
+
         repo.addVisibleTasksListener(listener, executor)
         executor.flushAll()
 
@@ -220,7 +252,7 @@
 
     @Test
     fun addListener_tasksOnDifferentDisplay_doesNotNotify() {
-        repo.updateVisibleFreeformTasks(SECOND_DISPLAY, taskId = 1, visible = true)
+        repo.updateTaskVisibility(SECOND_DISPLAY, taskId = 1, visible = true)
         val listener = TestVisibilityListener()
         val executor = TestShellExecutor()
         repo.addVisibleTasksListener(listener, executor)
@@ -232,12 +264,13 @@
     }
 
     @Test
-    fun updateVisibleFreeformTasks_addVisibleTasksNotifiesListener() {
+    fun updateTaskVisibility_addVisibleTasksNotifiesListener() {
         val listener = TestVisibilityListener()
         val executor = TestShellExecutor()
         repo.addVisibleTasksListener(listener, executor)
-        repo.updateVisibleFreeformTasks(DEFAULT_DISPLAY, taskId = 1, visible = true)
-        repo.updateVisibleFreeformTasks(DEFAULT_DISPLAY, taskId = 2, visible = true)
+
+        repo.updateTaskVisibility(DEFAULT_DISPLAY, taskId = 1, visible = true)
+        repo.updateTaskVisibility(DEFAULT_DISPLAY, taskId = 2, visible = true)
         executor.flushAll()
 
         assertThat(listener.visibleTasksCountOnDefaultDisplay).isEqualTo(2)
@@ -245,12 +278,12 @@
     }
 
     @Test
-    fun updateVisibleFreeformTasks_addVisibleTaskNotifiesListenerForThatDisplay() {
+    fun updateTaskVisibility_addVisibleTaskNotifiesListenerForThatDisplay() {
         val listener = TestVisibilityListener()
         val executor = TestShellExecutor()
         repo.addVisibleTasksListener(listener, executor)
 
-        repo.updateVisibleFreeformTasks(DEFAULT_DISPLAY, taskId = 1, visible = true)
+        repo.updateTaskVisibility(DEFAULT_DISPLAY, taskId = 1, visible = true)
         executor.flushAll()
 
         assertThat(listener.visibleTasksCountOnDefaultDisplay).isEqualTo(1)
@@ -258,7 +291,7 @@
         assertThat(listener.visibleTasksCountOnSecondaryDisplay).isEqualTo(0)
         assertThat(listener.visibleChangesOnSecondaryDisplay).isEqualTo(0)
 
-        repo.updateVisibleFreeformTasks(displayId = 1, taskId = 2, visible = true)
+        repo.updateTaskVisibility(displayId = 1, taskId = 2, visible = true)
         executor.flushAll()
 
         // Listener for secondary display is notified
@@ -269,17 +302,17 @@
     }
 
     @Test
-    fun updateVisibleFreeformTasks_taskOnDefaultBecomesVisibleOnSecondDisplay_listenersNotified() {
+    fun updateTaskVisibility_taskOnDefaultBecomesVisibleOnSecondDisplay_listenersNotified() {
         val listener = TestVisibilityListener()
         val executor = TestShellExecutor()
         repo.addVisibleTasksListener(listener, executor)
 
-        repo.updateVisibleFreeformTasks(DEFAULT_DISPLAY, taskId = 1, visible = true)
+        repo.updateTaskVisibility(DEFAULT_DISPLAY, taskId = 1, visible = true)
         executor.flushAll()
         assertThat(listener.visibleTasksCountOnDefaultDisplay).isEqualTo(1)
 
         // Mark task 1 visible on secondary display
-        repo.updateVisibleFreeformTasks(displayId = 1, taskId = 1, visible = true)
+        repo.updateTaskVisibility(displayId = 1, taskId = 1, visible = true)
         executor.flushAll()
 
         // Default display should have 2 calls
@@ -294,21 +327,22 @@
     }
 
     @Test
-    fun updateVisibleFreeformTasks_removeVisibleTasksNotifiesListener() {
+    fun updateTaskVisibility_removeVisibleTasksNotifiesListener() {
         val listener = TestVisibilityListener()
         val executor = TestShellExecutor()
         repo.addVisibleTasksListener(listener, executor)
-        repo.updateVisibleFreeformTasks(DEFAULT_DISPLAY, taskId = 1, visible = true)
-        repo.updateVisibleFreeformTasks(DEFAULT_DISPLAY, taskId = 2, visible = true)
+        repo.updateTaskVisibility(DEFAULT_DISPLAY, taskId = 1, visible = true)
+        repo.updateTaskVisibility(DEFAULT_DISPLAY, taskId = 2, visible = true)
         executor.flushAll()
 
         assertThat(listener.visibleTasksCountOnDefaultDisplay).isEqualTo(2)
-        repo.updateVisibleFreeformTasks(DEFAULT_DISPLAY, taskId = 1, visible = false)
+
+        repo.updateTaskVisibility(DEFAULT_DISPLAY, taskId = 1, visible = false)
         executor.flushAll()
 
         assertThat(listener.visibleChangesOnDefaultDisplay).isEqualTo(3)
 
-        repo.updateVisibleFreeformTasks(DEFAULT_DISPLAY, taskId = 2, visible = false)
+        repo.updateTaskVisibility(DEFAULT_DISPLAY, taskId = 2, visible = false)
         executor.flushAll()
 
         assertThat(listener.visibleTasksCountOnDefaultDisplay).isEqualTo(0)
@@ -320,16 +354,17 @@
      * This tests that task is removed from the last parent display when it vanishes.
      */
     @Test
-    fun updateVisibleFreeformTasks_removeVisibleTasksRemovesTaskWithInvalidDisplay() {
+    fun updateTaskVisibility_removeVisibleTasksRemovesTaskWithInvalidDisplay() {
         val listener = TestVisibilityListener()
         val executor = TestShellExecutor()
         repo.addVisibleTasksListener(listener, executor)
-        repo.updateVisibleFreeformTasks(DEFAULT_DISPLAY, taskId = 1, visible = true)
-        repo.updateVisibleFreeformTasks(DEFAULT_DISPLAY, taskId = 2, visible = true)
+        repo.updateTaskVisibility(DEFAULT_DISPLAY, taskId = 1, visible = true)
+        repo.updateTaskVisibility(DEFAULT_DISPLAY, taskId = 2, visible = true)
         executor.flushAll()
 
         assertThat(listener.visibleTasksCountOnDefaultDisplay).isEqualTo(2)
-        repo.updateVisibleFreeformTasks(INVALID_DISPLAY, taskId = 1, visible = false)
+
+        repo.updateTaskVisibility(INVALID_DISPLAY, taskId = 1, visible = false)
         executor.flushAll()
 
         assertThat(listener.visibleChangesOnDefaultDisplay).isEqualTo(3)
@@ -337,65 +372,73 @@
     }
 
     @Test
-    fun visibleTaskCount_defaultDisplay_returnsCorrectCount() {
+    fun getVisibleTaskCount_defaultDisplay_returnsCorrectCount() {
         // No tasks, count is 0
-        assertThat(repo.visibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(0)
+        assertThat(repo.getVisibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(0)
 
         // New task increments count to 1
-        repo.updateVisibleFreeformTasks(DEFAULT_DISPLAY, taskId = 1, visible = true)
-        assertThat(repo.visibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(1)
+        repo.updateTaskVisibility(DEFAULT_DISPLAY, taskId = 1, visible = true)
+
+        assertThat(repo.getVisibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(1)
 
         // Visibility update to same task does not increase count
-        repo.updateVisibleFreeformTasks(DEFAULT_DISPLAY, taskId = 1, visible = true)
-        assertThat(repo.visibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(1)
+        repo.updateTaskVisibility(DEFAULT_DISPLAY, taskId = 1, visible = true)
+
+        assertThat(repo.getVisibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(1)
 
         // Second task visible increments count
-        repo.updateVisibleFreeformTasks(DEFAULT_DISPLAY, taskId = 2, visible = true)
-        assertThat(repo.visibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(2)
+        repo.updateTaskVisibility(DEFAULT_DISPLAY, taskId = 2, visible = true)
+
+        assertThat(repo.getVisibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(2)
 
         // Hiding a task decrements count
-        repo.updateVisibleFreeformTasks(DEFAULT_DISPLAY, taskId = 1, visible = false)
-        assertThat(repo.visibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(1)
+        repo.updateTaskVisibility(DEFAULT_DISPLAY, taskId = 1, visible = false)
+        assertThat(repo.getVisibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(1)
 
         // Hiding all tasks leaves count at 0
-        repo.updateVisibleFreeformTasks(DEFAULT_DISPLAY, taskId = 2, visible = false)
-        assertThat(repo.visibleTaskCount(displayId = 9)).isEqualTo(0)
+        repo.updateTaskVisibility(DEFAULT_DISPLAY, taskId = 2, visible = false)
+        assertThat(repo.getVisibleTaskCount(displayId = 9)).isEqualTo(0)
 
         // Hiding a not existing task, count remains at 0
-        repo.updateVisibleFreeformTasks(DEFAULT_DISPLAY, taskId = 999, visible = false)
-        assertThat(repo.visibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(0)
+        repo.updateTaskVisibility(DEFAULT_DISPLAY, taskId = 999, visible = false)
+        assertThat(repo.getVisibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(0)
     }
 
     @Test
-    fun visibleTaskCount_multipleDisplays_returnsCorrectCount() {
-        assertThat(repo.visibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(0)
-        assertThat(repo.visibleTaskCount(SECOND_DISPLAY)).isEqualTo(0)
+    fun getVisibleTaskCount_multipleDisplays_returnsCorrectCount() {
+        assertThat(repo.getVisibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(0)
+        assertThat(repo.getVisibleTaskCount(SECOND_DISPLAY)).isEqualTo(0)
 
         // New task on default display increments count for that display only
-        repo.updateVisibleFreeformTasks(DEFAULT_DISPLAY, taskId = 1, visible = true)
-        assertThat(repo.visibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(1)
-        assertThat(repo.visibleTaskCount(SECOND_DISPLAY)).isEqualTo(0)
+        repo.updateTaskVisibility(DEFAULT_DISPLAY, taskId = 1, visible = true)
+
+        assertThat(repo.getVisibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(1)
+        assertThat(repo.getVisibleTaskCount(SECOND_DISPLAY)).isEqualTo(0)
 
         // New task on secondary display, increments count for that display only
-        repo.updateVisibleFreeformTasks(SECOND_DISPLAY, taskId = 2, visible = true)
-        assertThat(repo.visibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(1)
-        assertThat(repo.visibleTaskCount(SECOND_DISPLAY)).isEqualTo(1)
+        repo.updateTaskVisibility(SECOND_DISPLAY, taskId = 2, visible = true)
+
+        assertThat(repo.getVisibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(1)
+        assertThat(repo.getVisibleTaskCount(SECOND_DISPLAY)).isEqualTo(1)
 
         // Marking task visible on another display, updates counts for both displays
-        repo.updateVisibleFreeformTasks(SECOND_DISPLAY, taskId = 1, visible = true)
-        assertThat(repo.visibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(0)
-        assertThat(repo.visibleTaskCount(SECOND_DISPLAY)).isEqualTo(2)
+        repo.updateTaskVisibility(SECOND_DISPLAY, taskId = 1, visible = true)
+
+        assertThat(repo.getVisibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(0)
+        assertThat(repo.getVisibleTaskCount(SECOND_DISPLAY)).isEqualTo(2)
 
         // Marking task that is on secondary display, hidden on default display, does not affect
         // secondary display
-        repo.updateVisibleFreeformTasks(DEFAULT_DISPLAY, taskId = 1, visible = false)
-        assertThat(repo.visibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(0)
-        assertThat(repo.visibleTaskCount(SECOND_DISPLAY)).isEqualTo(2)
+        repo.updateTaskVisibility(DEFAULT_DISPLAY, taskId = 1, visible = false)
+
+        assertThat(repo.getVisibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(0)
+        assertThat(repo.getVisibleTaskCount(SECOND_DISPLAY)).isEqualTo(2)
 
         // Hiding a task on that display, decrements count
-        repo.updateVisibleFreeformTasks(SECOND_DISPLAY, taskId = 1, visible = false)
-        assertThat(repo.visibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(0)
-        assertThat(repo.visibleTaskCount(SECOND_DISPLAY)).isEqualTo(1)
+        repo.updateTaskVisibility(SECOND_DISPLAY, taskId = 1, visible = false)
+
+        assertThat(repo.getVisibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(0)
+        assertThat(repo.getVisibleTaskCount(SECOND_DISPLAY)).isEqualTo(1)
     }
 
     @Test
@@ -425,18 +468,116 @@
     }
 
     @Test
+    fun addOrMoveFreeformTaskToTop_taskIsMinimized_unminimizesTask() {
+        repo.addOrMoveFreeformTaskToTop(DEFAULT_DISPLAY, 5)
+        repo.addOrMoveFreeformTaskToTop(DEFAULT_DISPLAY, 6)
+        repo.addOrMoveFreeformTaskToTop(DEFAULT_DISPLAY, 7)
+        repo.minimizeTask(displayId = 0, taskId = 6)
+
+        val tasks = repo.getFreeformTasksInZOrder(DEFAULT_DISPLAY)
+        assertThat(tasks).containsExactly(7, 6, 5).inOrder()
+        assertThat(repo.isMinimizedTask(taskId = 6)).isTrue()
+    }
+
+    @Test
+    fun addOrMoveFreeformTaskToTop_taskIsUnminimized_noop() {
+        repo.addOrMoveFreeformTaskToTop(DEFAULT_DISPLAY, 5)
+        repo.addOrMoveFreeformTaskToTop(DEFAULT_DISPLAY, 6)
+        repo.addOrMoveFreeformTaskToTop(DEFAULT_DISPLAY, 7)
+
+        val tasks = repo.getFreeformTasksInZOrder(DEFAULT_DISPLAY)
+        assertThat(tasks).containsExactly(7, 6, 5).inOrder()
+        assertThat(repo.isMinimizedTask(taskId = 6)).isFalse()
+    }
+
+    @Test
+    fun removeFreeformTask_invalidDisplay_removesTaskFromFreeformTasks() {
+        repo.addOrMoveFreeformTaskToTop(DEFAULT_DISPLAY, taskId = 1)
+
+        repo.removeFreeformTask(INVALID_DISPLAY, taskId = 1)
+
+        val invalidDisplayTasks = repo.getFreeformTasksInZOrder(INVALID_DISPLAY)
+        assertThat(invalidDisplayTasks).isEmpty()
+        val validDisplayTasks = repo.getFreeformTasksInZOrder(DEFAULT_DISPLAY)
+        assertThat(validDisplayTasks).isEmpty()
+    }
+
+    @Test
+    fun removeFreeformTask_validDisplay_removesTaskFromFreeformTasks() {
+        repo.addOrMoveFreeformTaskToTop(DEFAULT_DISPLAY, taskId = 1)
+
+        repo.removeFreeformTask(DEFAULT_DISPLAY, taskId = 1)
+
+        val tasks = repo.getFreeformTasksInZOrder(DEFAULT_DISPLAY)
+        assertThat(tasks).isEmpty()
+    }
+
+    @Test
+    fun removeFreeformTask_validDisplay_differentDisplay_doesNotRemovesTask() {
+        repo.addOrMoveFreeformTaskToTop(DEFAULT_DISPLAY, taskId = 1)
+
+        repo.removeFreeformTask(SECOND_DISPLAY, taskId = 1)
+
+        val tasks = repo.getFreeformTasksInZOrder(DEFAULT_DISPLAY)
+        assertThat(tasks).containsExactly(1)
+    }
+
+    @Test
     fun removeFreeformTask_removesTaskBoundsBeforeMaximize() {
         val taskId = 1
+        repo.addActiveTask(THIRD_DISPLAY, taskId)
+        repo.addOrMoveFreeformTaskToTop(THIRD_DISPLAY, taskId)
         repo.saveBoundsBeforeMaximize(taskId, Rect(0, 0, 200, 200))
+
         repo.removeFreeformTask(THIRD_DISPLAY, taskId)
+
         assertThat(repo.removeBoundsBeforeMaximize(taskId)).isNull()
     }
 
     @Test
+    fun removeFreeformTask_removesActiveTask() {
+        val taskId = 1
+        val listener = TestListener()
+        repo.addActiveTaskListener(listener)
+        repo.addActiveTask(DEFAULT_DISPLAY, taskId)
+        repo.addOrMoveFreeformTaskToTop(DEFAULT_DISPLAY, taskId)
+
+        repo.removeFreeformTask(THIRD_DISPLAY, taskId)
+
+        assertThat(repo.isActiveTask(taskId)).isFalse()
+        assertThat(listener.activeChangesOnDefaultDisplay).isEqualTo(2)
+    }
+
+    @Test
+    fun removeFreeformTask_unminimizesTask() {
+        val taskId = 1
+        repo.addActiveTask(DEFAULT_DISPLAY, taskId)
+        repo.addOrMoveFreeformTaskToTop(DEFAULT_DISPLAY, taskId)
+        repo.minimizeTask(DEFAULT_DISPLAY, taskId)
+
+        repo.removeFreeformTask(DEFAULT_DISPLAY, taskId)
+
+        assertThat(repo.isMinimizedTask(taskId)).isFalse()
+    }
+
+    @Test
+    fun removeFreeformTask_updatesTaskVisibility() {
+        val taskId = 1
+        repo.addActiveTask(DEFAULT_DISPLAY, taskId)
+        repo.addOrMoveFreeformTaskToTop(DEFAULT_DISPLAY, taskId)
+
+        repo.removeFreeformTask(THIRD_DISPLAY, taskId)
+
+        assertThat(repo.isVisibleTask(taskId)).isFalse()
+    }
+
+    @Test
     fun saveBoundsBeforeMaximize_boundsSavedByTaskId() {
         val taskId = 1
         val bounds = Rect(0, 0, 200, 200)
+
         repo.saveBoundsBeforeMaximize(taskId, bounds)
+
         assertThat(repo.removeBoundsBeforeMaximize(taskId)).isEqualTo(bounds)
     }
 
@@ -446,17 +587,20 @@
         val bounds = Rect(0, 0, 200, 200)
         repo.saveBoundsBeforeMaximize(taskId, bounds)
         repo.removeBoundsBeforeMaximize(taskId)
-        assertThat(repo.removeBoundsBeforeMaximize(taskId)).isNull()
+
+        val boundsBeforeMaximize = repo.removeBoundsBeforeMaximize(taskId)
+
+        assertThat(boundsBeforeMaximize).isNull()
     }
 
     @Test
-    fun minimizeTaskNotCalled_noTasksMinimized() {
+    fun isMinimizedTask_minimizeTaskNotCalled_noTasksMinimized() {
         assertThat(repo.isMinimizedTask(taskId = 0)).isFalse()
         assertThat(repo.isMinimizedTask(taskId = 1)).isFalse()
     }
 
     @Test
-    fun minimizeTask_onlyThatTaskIsMinimized() {
+    fun minimizeTask_minimizesCorrectTask() {
         repo.minimizeTask(displayId = 0, taskId = 0)
 
         assertThat(repo.isMinimizedTask(taskId = 0)).isTrue()
@@ -465,8 +609,9 @@
     }
 
     @Test
-    fun unminimizeTask_taskNoLongerMinimized() {
+    fun unminimizeTask_unminimizesTask() {
         repo.minimizeTask(displayId = 0, taskId = 0)
+
         repo.unminimizeTask(displayId = 0, taskId = 0)
 
         assertThat(repo.isMinimizedTask(taskId = 0)).isFalse()
@@ -478,6 +623,7 @@
     fun unminimizeTask_nonExistentTask_doesntCrash() {
         repo.unminimizeTask(displayId = 0, taskId = 0)
 
+        // No change
         assertThat(repo.isMinimizedTask(taskId = 0)).isFalse()
         assertThat(repo.isMinimizedTask(taskId = 1)).isFalse()
         assertThat(repo.isMinimizedTask(taskId = 2)).isFalse()
@@ -485,41 +631,44 @@
 
 
     @Test
-    fun updateVisibleFreeformTasks_toVisible_taskIsUnminimized() {
+    fun updateTaskVisibility_minimizedTaskBecomesVisible_unminimizesTask() {
         repo.minimizeTask(displayId = 10, taskId = 2)
+        repo.updateTaskVisibility(displayId = 10, taskId = 2, visible = true)
 
-        repo.updateVisibleFreeformTasks(displayId = 10, taskId = 2, visible = true)
+        val isMinimizedTask = repo.isMinimizedTask(taskId = 2)
 
-        assertThat(repo.isMinimizedTask(taskId = 2)).isFalse()
+        assertThat(isMinimizedTask).isFalse()
     }
 
     @Test
-    fun getActiveNonMinimizedTasksOrderedFrontToBack_returnsFreeformTasksInCorrectOrder() {
+    fun getActiveNonMinimizedOrderedTasks_returnsFreeformTasksInCorrectOrder() {
         repo.addActiveTask(displayId = DEFAULT_DISPLAY, taskId = 1)
         repo.addActiveTask(displayId = DEFAULT_DISPLAY, taskId = 2)
         repo.addActiveTask(displayId = DEFAULT_DISPLAY, taskId = 3)
-        // The front-most task will be the one added last through addOrMoveFreeformTaskToTop
+        // The front-most task will be the one added last through `addOrMoveFreeformTaskToTop`
         repo.addOrMoveFreeformTaskToTop(displayId = DEFAULT_DISPLAY, taskId = 3)
         repo.addOrMoveFreeformTaskToTop(displayId = 0, taskId = 2)
         repo.addOrMoveFreeformTaskToTop(displayId = 0, taskId = 1)
 
-        assertThat(repo.getActiveNonMinimizedTasksOrderedFrontToBack(displayId = 0))
-            .containsExactly(1, 2, 3).inOrder()
+        val tasks = repo.getActiveNonMinimizedOrderedTasks(displayId = 0)
+
+        assertThat(tasks).containsExactly(1, 2, 3).inOrder()
     }
 
     @Test
-    fun getActiveNonMinimizedTasksOrderedFrontToBack_minimizedTaskNotIncluded() {
+    fun getActiveNonMinimizedOrderedTasks_excludesMinimizedTasks() {
         repo.addActiveTask(displayId = DEFAULT_DISPLAY, taskId = 1)
         repo.addActiveTask(displayId = DEFAULT_DISPLAY, taskId = 2)
         repo.addActiveTask(displayId = DEFAULT_DISPLAY, taskId = 3)
-        // The front-most task will be the one added last through addOrMoveFreeformTaskToTop
+        // The front-most task will be the one added last through `addOrMoveFreeformTaskToTop`
         repo.addOrMoveFreeformTaskToTop(displayId = DEFAULT_DISPLAY, taskId = 3)
         repo.addOrMoveFreeformTaskToTop(displayId = DEFAULT_DISPLAY, taskId = 2)
         repo.addOrMoveFreeformTaskToTop(displayId = DEFAULT_DISPLAY, taskId = 1)
         repo.minimizeTask(displayId = DEFAULT_DISPLAY, taskId = 2)
 
-        assertThat(repo.getActiveNonMinimizedTasksOrderedFrontToBack(
-            displayId = DEFAULT_DISPLAY)).containsExactly(1, 3).inOrder()
+        val tasks = repo.getActiveNonMinimizedOrderedTasks(displayId = DEFAULT_DISPLAY)
+
+        assertThat(tasks).containsExactly(1, 3).inOrder()
     }
 
 
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicatorTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicatorTest.kt
index bd39aa6..2dea43b 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicatorTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicatorTest.kt
@@ -61,20 +61,23 @@
 
     @Test
     fun testFullscreenRegionCalculation() {
-        val transitionHeight = context.resources.getDimensionPixelSize(
-            R.dimen.desktop_mode_fullscreen_from_desktop_height)
-        val fromFreeformWidth = mContext.resources.getDimensionPixelSize(
-            R.dimen.desktop_mode_fullscreen_from_desktop_width
-        )
         var testRegion = visualIndicator.calculateFullscreenRegion(displayLayout,
             WINDOWING_MODE_FULLSCREEN, CAPTION_HEIGHT)
         assertThat(testRegion.bounds).isEqualTo(Rect(0, -50, 2400, 2 * STABLE_INSETS.top))
         testRegion = visualIndicator.calculateFullscreenRegion(displayLayout,
             WINDOWING_MODE_FREEFORM, CAPTION_HEIGHT)
+
+        val transitionHeight = context.resources.getDimensionPixelSize(
+            R.dimen.desktop_mode_transition_region_thickness)
+        val toFullscreenScale = mContext.resources.getFloat(
+            R.dimen.desktop_mode_fullscreen_region_scale
+        )
+        val toFullscreenWidth = displayLayout.width() * toFullscreenScale
+
         assertThat(testRegion.bounds).isEqualTo(Rect(
-            DISPLAY_BOUNDS.width() / 2 - fromFreeformWidth / 2,
+            (DISPLAY_BOUNDS.width() / 2f - toFullscreenWidth / 2f).toInt(),
             -50,
-            DISPLAY_BOUNDS.width() / 2 + fromFreeformWidth / 2,
+            (DISPLAY_BOUNDS.width() / 2f + toFullscreenWidth / 2f).toInt(),
             transitionHeight))
         testRegion = visualIndicator.calculateFullscreenRegion(displayLayout,
             WINDOWING_MODE_MULTI_WINDOW, CAPTION_HEIGHT)
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 8421365..e6c72cd 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
@@ -43,6 +43,7 @@
 import android.platform.test.flag.junit.SetFlagsRule
 import android.testing.AndroidTestingRunner
 import android.view.Display.DEFAULT_DISPLAY
+import android.view.Gravity
 import android.view.SurfaceControl
 import android.view.WindowManager
 import android.view.WindowManager.TRANSIT_CHANGE
@@ -66,9 +67,11 @@
 import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
 import com.android.dx.mockito.inline.extended.ExtendedMockito.never
 import com.android.dx.mockito.inline.extended.StaticMockitoSession
+import com.android.internal.jank.InteractionJankMonitor
 import com.android.window.flags.Flags
 import com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE
 import com.android.wm.shell.MockToken
+import com.android.wm.shell.R
 import com.android.wm.shell.RootTaskDisplayAreaOrganizer
 import com.android.wm.shell.ShellTaskOrganizer
 import com.android.wm.shell.ShellTestCase
@@ -82,6 +85,7 @@
 import com.android.wm.shell.common.SyncTransactionQueue
 import com.android.wm.shell.common.desktopmode.DesktopModeTransitionSource.UNKNOWN
 import com.android.wm.shell.common.split.SplitScreenConstants
+import com.android.wm.shell.desktopmode.DesktopTasksController.SnapPosition
 import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFreeformTask
 import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFullscreenTask
 import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createHomeTask
@@ -128,8 +132,8 @@
 import org.mockito.kotlin.anyOrNull
 import org.mockito.kotlin.atLeastOnce
 import org.mockito.kotlin.capture
+import org.mockito.kotlin.whenever
 import org.mockito.quality.Strictness
-import org.mockito.Mockito.`when` as whenever
 
 /**
  * Test class for {@link DesktopTasksController}
@@ -166,11 +170,14 @@
   @Mock lateinit var desktopModeLoggerTransitionObserver: DesktopModeLoggerTransitionObserver
   @Mock lateinit var desktopModeVisualIndicator: DesktopModeVisualIndicator
   @Mock lateinit var recentTasksController: RecentTasksController
+  @Mock
+  private lateinit var mockInteractionJankMonitor: InteractionJankMonitor
+  @Mock private lateinit var mockSurface: SurfaceControl
 
   private lateinit var mockitoSession: StaticMockitoSession
   private lateinit var controller: DesktopTasksController
   private lateinit var shellInit: ShellInit
-  private lateinit var desktopModeTaskRepository: DesktopModeTaskRepository
+  private lateinit var taskRepository: DesktopModeTaskRepository
   private lateinit var desktopTasksLimiter: DesktopTasksLimiter
   private lateinit var recentsTransitionStateListener: RecentsTransitionStateListener
 
@@ -181,12 +188,12 @@
 
   private val DISPLAY_DIMENSION_SHORT = 1600
   private val DISPLAY_DIMENSION_LONG = 2560
-  private val DEFAULT_LANDSCAPE_BOUNDS = Rect(320, 200, 2240, 1400)
-  private val DEFAULT_PORTRAIT_BOUNDS = Rect(200, 320, 1400, 2240)
-  private val RESIZABLE_LANDSCAPE_BOUNDS = Rect(25, 680, 1575, 1880)
-  private val RESIZABLE_PORTRAIT_BOUNDS = Rect(680, 200, 1880, 1400)
-  private val UNRESIZABLE_LANDSCAPE_BOUNDS = Rect(25, 699, 1575, 1861)
-  private val UNRESIZABLE_PORTRAIT_BOUNDS = Rect(830, 200, 1730, 1400)
+  private val DEFAULT_LANDSCAPE_BOUNDS = Rect(320, 75, 2240, 1275)
+  private val DEFAULT_PORTRAIT_BOUNDS = Rect(200, 165, 1400, 2085)
+  private val RESIZABLE_LANDSCAPE_BOUNDS = Rect(25, 435, 1575, 1635)
+  private val RESIZABLE_PORTRAIT_BOUNDS = Rect(680, 75, 1880, 1275)
+  private val UNRESIZABLE_LANDSCAPE_BOUNDS = Rect(25, 449, 1575, 1611)
+  private val UNRESIZABLE_PORTRAIT_BOUNDS = Rect(830, 75, 1730, 1275)
 
   @Before
   fun setUp() {
@@ -198,9 +205,15 @@
     doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
 
     shellInit = spy(ShellInit(testExecutor))
-    desktopModeTaskRepository = DesktopModeTaskRepository()
+    taskRepository = DesktopModeTaskRepository()
     desktopTasksLimiter =
-        DesktopTasksLimiter(transitions, desktopModeTaskRepository, shellTaskOrganizer)
+        DesktopTasksLimiter(
+            transitions,
+            taskRepository,
+            shellTaskOrganizer,
+            MAX_TASK_LIMIT,
+            mockInteractionJankMonitor,
+            mContext)
 
     whenever(shellTaskOrganizer.getRunningTasks(anyInt())).thenAnswer { runningTasks }
     whenever(transitions.startTransition(anyInt(), any(), isNull())).thenAnswer { Binder() }
@@ -241,14 +254,15 @@
         exitDesktopTransitionHandler,
         toggleResizeDesktopTaskTransitionHandler,
         dragToDesktopTransitionHandler,
-        desktopModeTaskRepository,
+        taskRepository,
         desktopModeLoggerTransitionObserver,
         launchAdjacentController,
         recentsTransitionHandler,
         multiInstanceHelper,
         shellExecutor,
         Optional.of(desktopTasksLimiter),
-        recentTasksController)
+        recentTasksController,
+        mockInteractionJankMonitor)
   }
 
   @After
@@ -264,7 +278,7 @@
   }
 
   @Test
-  fun instantiate_canNotEnterDesktopMode_doNotAddInitCallback() {
+  fun instantiate_cannotEnterDesktopMode_doNotAddInitCallback() {
     whenever(DesktopModeStatus.canEnterDesktopMode(context)).thenReturn(false)
     clearInvocations(shellInit)
 
@@ -518,7 +532,7 @@
 
     markTaskHidden(freeformTask)
     markTaskHidden(minimizedTask)
-    desktopModeTaskRepository.minimizeTask(DEFAULT_DISPLAY, minimizedTask.taskId)
+    taskRepository.minimizeTask(DEFAULT_DISPLAY, minimizedTask.taskId)
     controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition()))
 
     val wct = getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java)
@@ -537,7 +551,7 @@
 
     markTaskHidden(freeformTask)
     markTaskHidden(minimizedTask)
-    desktopModeTaskRepository.minimizeTask(DEFAULT_DISPLAY, minimizedTask.taskId)
+    taskRepository.minimizeTask(DEFAULT_DISPLAY, minimizedTask.taskId)
     controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition()))
 
     val wct = getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java)
@@ -580,40 +594,195 @@
   }
 
   @Test
+  fun addMoveToDesktopChanges_gravityLeft_noBoundsApplied() {
+    setUpLandscapeDisplay()
+    val task = setUpFullscreenTask(gravity = Gravity.LEFT)
+    val wct = WindowContainerTransaction()
+    controller.addMoveToDesktopChanges(wct, task)
+
+    val finalBounds = findBoundsChange(wct, task)
+    assertThat(finalBounds).isEqualTo(Rect())
+  }
+
+  @Test
+  fun addMoveToDesktopChanges_gravityRight_noBoundsApplied() {
+    setUpLandscapeDisplay()
+    val task = setUpFullscreenTask(gravity = Gravity.RIGHT)
+    val wct = WindowContainerTransaction()
+    controller.addMoveToDesktopChanges(wct, task)
+
+    val finalBounds = findBoundsChange(wct, task)
+    assertThat(finalBounds).isEqualTo(Rect())
+  }
+
+  @Test
+  fun addMoveToDesktopChanges_gravityTop_noBoundsApplied() {
+    setUpLandscapeDisplay()
+    val task = setUpFullscreenTask(gravity = Gravity.TOP)
+    val wct = WindowContainerTransaction()
+    controller.addMoveToDesktopChanges(wct, task)
+
+    val finalBounds = findBoundsChange(wct, task)
+    assertThat(finalBounds).isEqualTo(Rect())
+  }
+
+  @Test
+  fun addMoveToDesktopChanges_gravityBottom_noBoundsApplied() {
+    setUpLandscapeDisplay()
+    val task = setUpFullscreenTask(gravity = Gravity.BOTTOM)
+    val wct = WindowContainerTransaction()
+    controller.addMoveToDesktopChanges(wct, task)
+
+    val finalBounds = findBoundsChange(wct, task)
+    assertThat(finalBounds).isEqualTo(Rect())
+  }
+
+  @Test
+  @EnableFlags(Flags.FLAG_ENABLE_CASCADING_WINDOWS)
+  fun addMoveToDesktopChanges_positionBottomRight() {
+    setUpLandscapeDisplay()
+    val stableBounds = Rect()
+    displayLayout.getStableBoundsForDesktopMode(stableBounds)
+
+    setUpFreeformTask(bounds = DEFAULT_LANDSCAPE_BOUNDS)
+
+    val task = setUpFullscreenTask()
+    val wct = WindowContainerTransaction()
+    controller.addMoveToDesktopChanges(wct, task)
+
+    val finalBounds = findBoundsChange(wct, task)
+    assertThat(stableBounds.getDesktopTaskPosition(finalBounds!!))
+      .isEqualTo(DesktopTaskPosition.BottomRight)
+  }
+
+  @Test
+  @EnableFlags(Flags.FLAG_ENABLE_CASCADING_WINDOWS)
+  fun addMoveToDesktopChanges_positionTopLeft() {
+    setUpLandscapeDisplay()
+    val stableBounds = Rect()
+    displayLayout.getStableBoundsForDesktopMode(stableBounds)
+
+    addFreeformTaskAtPosition(DesktopTaskPosition.BottomRight, stableBounds)
+
+    val task = setUpFullscreenTask()
+    val wct = WindowContainerTransaction()
+    controller.addMoveToDesktopChanges(wct, task)
+
+    val finalBounds = findBoundsChange(wct, task)
+    assertThat(stableBounds.getDesktopTaskPosition(finalBounds!!))
+      .isEqualTo(DesktopTaskPosition.TopLeft)
+  }
+
+  @Test
+  @EnableFlags(Flags.FLAG_ENABLE_CASCADING_WINDOWS)
+  fun addMoveToDesktopChanges_positionBottomLeft() {
+    setUpLandscapeDisplay()
+    val stableBounds = Rect()
+    displayLayout.getStableBoundsForDesktopMode(stableBounds)
+
+    addFreeformTaskAtPosition(DesktopTaskPosition.TopLeft, stableBounds)
+
+    val task = setUpFullscreenTask()
+    val wct = WindowContainerTransaction()
+    controller.addMoveToDesktopChanges(wct, task)
+
+    val finalBounds = findBoundsChange(wct, task)
+    assertThat(stableBounds.getDesktopTaskPosition(finalBounds!!))
+      .isEqualTo(DesktopTaskPosition.BottomLeft)
+  }
+
+  @Test
+  @EnableFlags(Flags.FLAG_ENABLE_CASCADING_WINDOWS)
+  fun addMoveToDesktopChanges_positionTopRight() {
+    setUpLandscapeDisplay()
+    val stableBounds = Rect()
+    displayLayout.getStableBoundsForDesktopMode(stableBounds)
+
+    addFreeformTaskAtPosition(DesktopTaskPosition.BottomLeft, stableBounds)
+
+    val task = setUpFullscreenTask()
+    val wct = WindowContainerTransaction()
+    controller.addMoveToDesktopChanges(wct, task)
+
+    val finalBounds = findBoundsChange(wct, task)
+    assertThat(stableBounds.getDesktopTaskPosition(finalBounds!!))
+      .isEqualTo(DesktopTaskPosition.TopRight)
+  }
+
+  @Test
+  @EnableFlags(Flags.FLAG_ENABLE_CASCADING_WINDOWS)
+  fun addMoveToDesktopChanges_positionResetsToCenter() {
+    setUpLandscapeDisplay()
+    val stableBounds = Rect()
+    displayLayout.getStableBoundsForDesktopMode(stableBounds)
+
+    addFreeformTaskAtPosition(DesktopTaskPosition.TopRight, stableBounds)
+
+    val task = setUpFullscreenTask()
+    val wct = WindowContainerTransaction()
+    controller.addMoveToDesktopChanges(wct, task)
+
+    val finalBounds = findBoundsChange(wct, task)
+    assertThat(stableBounds.getDesktopTaskPosition(finalBounds!!))
+      .isEqualTo(DesktopTaskPosition.Center)
+  }
+
+  @Test
+  @EnableFlags(Flags.FLAG_ENABLE_CASCADING_WINDOWS)
+  fun addMoveToDesktopChanges_defaultToCenterIfFree() {
+    setUpLandscapeDisplay()
+    val stableBounds = Rect()
+    displayLayout.getStableBoundsForDesktopMode(stableBounds)
+
+    val minTouchTarget = context.resources.getDimensionPixelSize(
+      R.dimen.freeform_required_visible_empty_space_in_header)
+    addFreeformTaskAtPosition(DesktopTaskPosition.Center, stableBounds,
+      Rect(0, 0, 1600, 1200), Point(0, minTouchTarget + 1))
+
+    val task = setUpFullscreenTask()
+    val wct = WindowContainerTransaction()
+    controller.addMoveToDesktopChanges(wct, task)
+
+    val finalBounds = findBoundsChange(wct, task)
+    assertThat(stableBounds.getDesktopTaskPosition(finalBounds!!))
+      .isEqualTo(DesktopTaskPosition.Center)
+  }
+
+  @Test
   fun moveToDesktop_tdaFullscreen_windowingModeSetToFreeform() {
     val task = setUpFullscreenTask()
     val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!!
     tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN
-    controller.moveToDesktop(task, transitionSource = UNKNOWN)
+    controller.moveRunningTaskToDesktop(task, transitionSource = UNKNOWN)
     val wct = getLatestEnterDesktopWct()
     assertThat(wct.changes[task.token.asBinder()]?.windowingMode).isEqualTo(WINDOWING_MODE_FREEFORM)
   }
 
   @Test
-  fun moveToDesktop_tdaFreeform_windowingModeSetToUndefined() {
+  fun moveRunningTaskToDesktop_tdaFreeform_windowingModeSetToUndefined() {
     val task = setUpFullscreenTask()
     val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!!
     tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FREEFORM
-    controller.moveToDesktop(task, transitionSource = UNKNOWN)
+    controller.moveRunningTaskToDesktop(task, transitionSource = UNKNOWN)
     val wct = getLatestEnterDesktopWct()
     assertThat(wct.changes[task.token.asBinder()]?.windowingMode)
         .isEqualTo(WINDOWING_MODE_UNDEFINED)
   }
 
   @Test
-  fun moveToDesktop_nonExistentTask_doesNothing() {
-    controller.moveToDesktop(999, transitionSource = UNKNOWN)
+  fun moveTaskToDesktop_nonExistentTask_doesNothing() {
+    controller.moveTaskToDesktop(999, transitionSource = UNKNOWN)
     verifyEnterDesktopWCTNotExecuted()
   }
 
   @Test
   @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
-  fun moveToDesktop_desktopWallpaperDisabled_nonRunningTask_launchesInFreeform() {
+  fun moveTaskToDesktop_desktopWallpaperDisabled_nonRunningTask_launchesInFreeform() {
     val task = createTaskInfo(1)
     whenever(shellTaskOrganizer.getRunningTaskInfo(anyInt())).thenReturn(null)
     whenever(recentTasksController.findTaskInBackground(anyInt())).thenReturn(task)
 
-    controller.moveToDesktop(task.taskId, transitionSource = UNKNOWN)
+    controller.moveTaskToDesktop(task.taskId, transitionSource = UNKNOWN)
 
     with(getLatestEnterDesktopWct()) {
       assertLaunchTaskAt(0, task.taskId, WINDOWING_MODE_FREEFORM)
@@ -622,12 +791,12 @@
 
   @Test
   @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
-  fun moveToDesktop_desktopWallpaperEnabled_nonRunningTask_launchesInFreeform() {
+  fun moveTaskToDesktop_desktopWallpaperEnabled_nonRunningTask_launchesInFreeform() {
     val task = createTaskInfo(1)
     whenever(shellTaskOrganizer.getRunningTaskInfo(anyInt())).thenReturn(null)
     whenever(recentTasksController.findTaskInBackground(anyInt())).thenReturn(task)
 
-    controller.moveToDesktop(task.taskId, transitionSource = UNKNOWN)
+    controller.moveTaskToDesktop(task.taskId, transitionSource = UNKNOWN)
 
     with(getLatestEnterDesktopWct()) {
       // Add desktop wallpaper activity
@@ -639,7 +808,7 @@
 
   @Test
   @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
-  fun moveToDesktop_topActivityTranslucentWithStyleFloating_taskIsMovedToDesktop() {
+  fun moveRunningTaskToDesktop_topActivityTranslucentWithStyleFloating_taskIsMovedToDesktop() {
     val task =
       setUpFullscreenTask().apply {
         isTopActivityTransparent = true
@@ -647,7 +816,7 @@
         numActivities = 1
       }
 
-    controller.moveToDesktop(task, transitionSource = UNKNOWN)
+    controller.moveRunningTaskToDesktop(task, transitionSource = UNKNOWN)
 
     val wct = getLatestEnterDesktopWct()
     assertThat(wct.changes[task.token.asBinder()]?.windowingMode).isEqualTo(WINDOWING_MODE_FREEFORM)
@@ -655,7 +824,7 @@
 
   @Test
   @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
-  fun moveToDesktop_topActivityTranslucentWithoutStyleFloating_doesNothing() {
+  fun moveRunningTaskToDesktop_topActivityTranslucentWithoutStyleFloating_doesNothing() {
     val task =
       setUpFullscreenTask().apply {
         isTopActivityTransparent = true
@@ -663,13 +832,13 @@
         numActivities = 1
       }
 
-    controller.moveToDesktop(task, transitionSource = UNKNOWN)
+    controller.moveRunningTaskToDesktop(task, transitionSource = UNKNOWN)
     verifyEnterDesktopWCTNotExecuted()
   }
 
   @Test
   @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
-  fun moveToDesktop_systemUIActivity_doesNothing() {
+  fun moveRunningTaskToDesktop_systemUIActivity_doesNothing() {
     val task = setUpFullscreenTask()
 
     // Set task as systemUI package
@@ -678,15 +847,15 @@
     val baseComponent = ComponentName(systemUIPackageName, /* class */ "")
     task.baseActivity = baseComponent
 
-    controller.moveToDesktop(task, transitionSource = UNKNOWN)
+    controller.moveRunningTaskToDesktop(task, transitionSource = UNKNOWN)
     verifyEnterDesktopWCTNotExecuted()
   }
 
   @Test
-  fun moveToDesktop_deviceSupported_taskIsMovedToDesktop() {
+  fun moveRunningTaskToDesktop_deviceSupported_taskIsMovedToDesktop() {
     val task = setUpFullscreenTask()
 
-    controller.moveToDesktop(task, transitionSource = UNKNOWN)
+    controller.moveRunningTaskToDesktop(task, transitionSource = UNKNOWN)
 
     val wct = getLatestEnterDesktopWct()
     assertThat(wct.changes[task.token.asBinder()]?.windowingMode).isEqualTo(WINDOWING_MODE_FREEFORM)
@@ -694,13 +863,13 @@
 
   @Test
   @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
-  fun moveToDesktop_otherFreeformTasksBroughtToFront_desktopWallpaperDisabled() {
+  fun moveRunningTaskToDesktop_otherFreeformTasksBroughtToFront_desktopWallpaperDisabled() {
     val homeTask = setUpHomeTask()
     val freeformTask = setUpFreeformTask()
     val fullscreenTask = setUpFullscreenTask()
     markTaskHidden(freeformTask)
 
-    controller.moveToDesktop(fullscreenTask, transitionSource = UNKNOWN)
+    controller.moveRunningTaskToDesktop(fullscreenTask, transitionSource = UNKNOWN)
 
     with(getLatestEnterDesktopWct()) {
       // Operations should include home task, freeform task
@@ -713,12 +882,12 @@
 
   @Test
   @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
-  fun moveToDesktop_otherFreeformTasksBroughtToFront_desktopWallpaperEnabled() {
+  fun moveRunningTaskToDesktop_otherFreeformTasksBroughtToFront_desktopWallpaperEnabled() {
     val freeformTask = setUpFreeformTask()
     val fullscreenTask = setUpFullscreenTask()
     markTaskHidden(freeformTask)
 
-    controller.moveToDesktop(fullscreenTask, transitionSource = UNKNOWN)
+    controller.moveRunningTaskToDesktop(fullscreenTask, transitionSource = UNKNOWN)
 
     with(getLatestEnterDesktopWct()) {
       // Operations should include wallpaper intent, freeform task, fullscreen task
@@ -732,7 +901,7 @@
   }
 
   @Test
-  fun moveToDesktop_onlyFreeformTasksFromCurrentDisplayBroughtToFront() {
+  fun moveRunningTaskToDesktop_onlyFreeformTasksFromCurrentDisplayBroughtToFront() {
     setUpHomeTask(displayId = DEFAULT_DISPLAY)
     val freeformTaskDefault = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
     val fullscreenTaskDefault = setUpFullscreenTask(displayId = DEFAULT_DISPLAY)
@@ -742,7 +911,7 @@
     val freeformTaskSecond = setUpFreeformTask(displayId = SECOND_DISPLAY)
     markTaskHidden(freeformTaskSecond)
 
-    controller.moveToDesktop(fullscreenTaskDefault, transitionSource = UNKNOWN)
+    controller.moveRunningTaskToDesktop(fullscreenTaskDefault, transitionSource = UNKNOWN)
 
     with(getLatestEnterDesktopWct()) {
       // Check that hierarchy operations do not include tasks from second display
@@ -753,9 +922,9 @@
   }
 
   @Test
-  fun moveToDesktop_splitTaskExitsSplit() {
+  fun moveRunningTaskToDesktop_splitTaskExitsSplit() {
     val task = setUpSplitScreenTask()
-    controller.moveToDesktop(task, transitionSource = UNKNOWN)
+    controller.moveRunningTaskToDesktop(task, transitionSource = UNKNOWN)
     val wct = getLatestEnterDesktopWct()
     assertThat(wct.changes[task.token.asBinder()]?.windowingMode).isEqualTo(WINDOWING_MODE_FREEFORM)
     verify(splitScreenController)
@@ -763,9 +932,9 @@
   }
 
   @Test
-  fun moveToDesktop_fullscreenTaskDoesNotExitSplit() {
+  fun moveRunningTaskToDesktop_fullscreenTaskDoesNotExitSplit() {
     val task = setUpFullscreenTask()
-    controller.moveToDesktop(task, transitionSource = UNKNOWN)
+    controller.moveRunningTaskToDesktop(task, transitionSource = UNKNOWN)
     val wct = getLatestEnterDesktopWct()
     assertThat(wct.changes[task.token.asBinder()]?.windowingMode).isEqualTo(WINDOWING_MODE_FREEFORM)
     verify(splitScreenController, never())
@@ -774,46 +943,42 @@
 
   @Test
   @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
-  fun moveToDesktop_desktopWallpaperDisabled_bringsTasksOver_dontShowBackTask() {
-    val taskLimit = desktopTasksLimiter.getMaxTaskLimit()
-    val freeformTasks = (1..taskLimit).map { _ -> setUpFreeformTask() }
+  fun moveRunningTaskToDesktop_desktopWallpaperDisabled_bringsTasksOver_dontShowBackTask() {
+    val freeformTasks = (1..MAX_TASK_LIMIT).map { _ -> setUpFreeformTask() }
     val newTask = setUpFullscreenTask()
     val homeTask = setUpHomeTask()
 
-    controller.moveToDesktop(newTask, transitionSource = UNKNOWN)
+    controller.moveRunningTaskToDesktop(newTask, transitionSource = UNKNOWN)
 
     val wct = getLatestEnterDesktopWct()
-    assertThat(wct.hierarchyOps.size).isEqualTo(taskLimit + 1) // visible tasks + home
+    assertThat(wct.hierarchyOps.size).isEqualTo(MAX_TASK_LIMIT + 1) // visible tasks + home
     wct.assertReorderAt(0, homeTask)
     wct.assertReorderSequenceInRange(
-      range = 1..<(taskLimit + 1),
-      *freeformTasks.drop(1).toTypedArray(), // Skipping freeformTasks[0]
-      newTask
-    )
+        range = 1..<(MAX_TASK_LIMIT + 1),
+        *freeformTasks.drop(1).toTypedArray(), // Skipping freeformTasks[0]
+        newTask)
   }
 
   @Test
   @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
-  fun moveToDesktop_desktopWallpaperEnabled_bringsTasksOverLimit_dontShowBackTask() {
-    val taskLimit = desktopTasksLimiter.getMaxTaskLimit()
-    val freeformTasks = (1..taskLimit).map { _ -> setUpFreeformTask() }
+  fun moveRunningTaskToDesktop_desktopWallpaperEnabled_bringsTasksOverLimit_dontShowBackTask() {
+    val freeformTasks = (1..MAX_TASK_LIMIT).map { _ -> setUpFreeformTask() }
     val newTask = setUpFullscreenTask()
     val homeTask = setUpHomeTask()
 
-    controller.moveToDesktop(newTask, transitionSource = UNKNOWN)
+    controller.moveRunningTaskToDesktop(newTask, transitionSource = UNKNOWN)
 
     val wct = getLatestEnterDesktopWct()
-    assertThat(wct.hierarchyOps.size).isEqualTo(taskLimit + 2) // tasks + home + wallpaper
+    assertThat(wct.hierarchyOps.size).isEqualTo(MAX_TASK_LIMIT + 2) // tasks + home + wallpaper
     // Move home to front
     wct.assertReorderAt(0, homeTask)
     // Add desktop wallpaper activity
     wct.assertPendingIntentAt(1, desktopWallpaperIntent)
     // Bring freeform tasks to front
     wct.assertReorderSequenceInRange(
-      range = 2..<(taskLimit + 2),
-      *freeformTasks.drop(1).toTypedArray(), // Skipping freeformTasks[0]
-      newTask
-    )
+        range = 2..<(MAX_TASK_LIMIT + 2),
+        *freeformTasks.drop(1).toTypedArray(), // Skipping freeformTasks[0]
+        newTask)
   }
 
   @Test
@@ -832,7 +997,7 @@
     val task = setUpFreeformTask()
     val wallpaperToken = MockToken().token()
 
-    desktopModeTaskRepository.wallpaperActivityToken = wallpaperToken
+    taskRepository.wallpaperActivityToken = wallpaperToken
     assertNotNull(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY))
       .configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN
 
@@ -861,7 +1026,7 @@
     val task = setUpFreeformTask()
     val wallpaperToken = MockToken().token()
 
-    desktopModeTaskRepository.wallpaperActivityToken = wallpaperToken
+    taskRepository.wallpaperActivityToken = wallpaperToken
     assertNotNull(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY))
       .configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FREEFORM
 
@@ -881,7 +1046,7 @@
     setUpFreeformTask()
     val wallpaperToken = MockToken().token()
 
-    desktopModeTaskRepository.wallpaperActivityToken = wallpaperToken
+    taskRepository.wallpaperActivityToken = wallpaperToken
     assertNotNull(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY))
       .configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN
 
@@ -927,9 +1092,8 @@
 
   @Test
   fun moveTaskToFront_bringsTasksOverLimit_minimizesBackTask() {
-    val taskLimit = desktopTasksLimiter.getMaxTaskLimit()
     setUpHomeTask()
-    val freeformTasks = (1..taskLimit + 1).map { _ -> setUpFreeformTask() }
+    val freeformTasks = (1..MAX_TASK_LIMIT + 1).map { _ -> setUpFreeformTask() }
 
     controller.moveTaskToFront(freeformTasks[0])
 
@@ -1023,7 +1187,7 @@
   fun onDesktopWindowClose_singleActiveTask_hasWallpaperActivityToken() {
     val task = setUpFreeformTask()
     val wallpaperToken = MockToken().token()
-    desktopModeTaskRepository.wallpaperActivityToken = wallpaperToken
+    taskRepository.wallpaperActivityToken = wallpaperToken
 
     val wct = WindowContainerTransaction()
     controller.onDesktopWindowClose(wct, displayId = DEFAULT_DISPLAY, taskId = task.taskId)
@@ -1035,8 +1199,8 @@
   fun onDesktopWindowClose_singleActiveTask_isClosing() {
     val task = setUpFreeformTask()
     val wallpaperToken = MockToken().token()
-    desktopModeTaskRepository.wallpaperActivityToken = wallpaperToken
-    desktopModeTaskRepository.addClosingTask(DEFAULT_DISPLAY, task.taskId)
+    taskRepository.wallpaperActivityToken = wallpaperToken
+    taskRepository.addClosingTask(DEFAULT_DISPLAY, task.taskId)
 
     val wct = WindowContainerTransaction()
     controller.onDesktopWindowClose(wct, displayId = DEFAULT_DISPLAY, taskId = task.taskId)
@@ -1048,8 +1212,8 @@
   fun onDesktopWindowClose_singleActiveTask_isMinimized() {
     val task = setUpFreeformTask()
     val wallpaperToken = MockToken().token()
-    desktopModeTaskRepository.wallpaperActivityToken = wallpaperToken
-    desktopModeTaskRepository.minimizeTask(DEFAULT_DISPLAY, task.taskId)
+    taskRepository.wallpaperActivityToken = wallpaperToken
+    taskRepository.minimizeTask(DEFAULT_DISPLAY, task.taskId)
 
     val wct = WindowContainerTransaction()
     controller.onDesktopWindowClose(wct, displayId = DEFAULT_DISPLAY, taskId = task.taskId)
@@ -1062,7 +1226,7 @@
     val task1 = setUpFreeformTask()
     setUpFreeformTask()
     val wallpaperToken = MockToken().token()
-    desktopModeTaskRepository.wallpaperActivityToken = wallpaperToken
+    taskRepository.wallpaperActivityToken = wallpaperToken
 
     val wct = WindowContainerTransaction()
     controller.onDesktopWindowClose(wct, displayId = DEFAULT_DISPLAY, taskId = task1.taskId)
@@ -1075,8 +1239,8 @@
     val task1 = setUpFreeformTask()
     val task2 = setUpFreeformTask()
     val wallpaperToken = MockToken().token()
-    desktopModeTaskRepository.wallpaperActivityToken = wallpaperToken
-    desktopModeTaskRepository.addClosingTask(DEFAULT_DISPLAY, task2.taskId)
+    taskRepository.wallpaperActivityToken = wallpaperToken
+    taskRepository.addClosingTask(DEFAULT_DISPLAY, task2.taskId)
 
     val wct = WindowContainerTransaction()
     controller.onDesktopWindowClose(wct, displayId = DEFAULT_DISPLAY, taskId = task1.taskId)
@@ -1089,8 +1253,8 @@
     val task1 = setUpFreeformTask()
     val task2 = setUpFreeformTask()
     val wallpaperToken = MockToken().token()
-    desktopModeTaskRepository.wallpaperActivityToken = wallpaperToken
-    desktopModeTaskRepository.minimizeTask(DEFAULT_DISPLAY, task2.taskId)
+    taskRepository.wallpaperActivityToken = wallpaperToken
+    taskRepository.minimizeTask(DEFAULT_DISPLAY, task2.taskId)
 
     val wct = WindowContainerTransaction()
     controller.onDesktopWindowClose(wct, displayId = DEFAULT_DISPLAY, taskId = task1.taskId)
@@ -1136,8 +1300,7 @@
   fun handleRequest_fullscreenTaskToFreeform_bringsTasksOverLimit_otherTaskIsMinimized() {
     assumeTrue(ENABLE_SHELL_TRANSITIONS)
 
-    val taskLimit = desktopTasksLimiter.getMaxTaskLimit()
-    val freeformTasks = (1..taskLimit).map { _ -> setUpFreeformTask() }
+    val freeformTasks = (1..MAX_TASK_LIMIT).map { _ -> setUpFreeformTask() }
     freeformTasks.forEach { markTaskVisible(it) }
     val fullscreenTask = createFullscreenTask()
 
@@ -1182,8 +1345,7 @@
   fun handleRequest_freeformTask_freeformVisible_aboveTaskLimit_minimize() {
     assumeTrue(ENABLE_SHELL_TRANSITIONS)
 
-    val taskLimit = desktopTasksLimiter.getMaxTaskLimit()
-    val freeformTasks = (1..taskLimit).map { _ -> setUpFreeformTask() }
+    val freeformTasks = (1..MAX_TASK_LIMIT).map { _ -> setUpFreeformTask() }
     freeformTasks.forEach { markTaskVisible(it) }
     val newFreeformTask = createFreeformTask()
 
@@ -1405,6 +1567,21 @@
   }
 
   @Test
+  fun handleRequest_recentsAnimationRunning_relaunchActiveTask_taskBecomesUndefined() {
+    // Set up a visible freeform task
+    val freeformTask = setUpFreeformTask()
+    markTaskVisible(freeformTask)
+
+    // Mark recents animation running
+    recentsTransitionStateListener.onAnimationStateChanged(true)
+
+    // Should become undefined as the TDA is set to fullscreen. It will inherit from the TDA.
+    val result = controller.handleRequest(Binder(), createTransition(freeformTask))
+    assertThat(result?.changes?.get(freeformTask.token.asBinder())?.windowingMode)
+      .isEqualTo(WINDOWING_MODE_UNDEFINED)
+  }
+
+  @Test
   @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
   fun handleRequest_topActivityTransparentWithStyleFloating_returnSwitchToFreeformWCT() {
     val freeformTask = setUpFreeformTask()
@@ -1498,7 +1675,7 @@
   fun handleRequest_backTransition_singleTaskWithToken_noWallpaper_noBackNav_doesNotHandle() {
     val task = setUpFreeformTask()
 
-    desktopModeTaskRepository.wallpaperActivityToken = MockToken().token()
+    taskRepository.wallpaperActivityToken = MockToken().token()
     val result = controller.handleRequest(Binder(), createTransition(task, type = TRANSIT_TO_BACK))
 
     assertNull(result, "Should not handle request")
@@ -1513,7 +1690,7 @@
     val task = setUpFreeformTask()
     val wallpaperToken = MockToken().token()
 
-    desktopModeTaskRepository.wallpaperActivityToken = wallpaperToken
+    taskRepository.wallpaperActivityToken = wallpaperToken
     val result = controller.handleRequest(Binder(), createTransition(task, type = TRANSIT_TO_BACK))
 
     // Should create remove wallpaper transaction
@@ -1528,7 +1705,7 @@
     val task = setUpFreeformTask()
     val wallpaperToken = MockToken().token()
 
-    desktopModeTaskRepository.wallpaperActivityToken = wallpaperToken
+    taskRepository.wallpaperActivityToken = wallpaperToken
     val result = controller.handleRequest(Binder(), createTransition(task, type = TRANSIT_TO_BACK))
 
     // Should create remove wallpaper transaction
@@ -1544,7 +1721,7 @@
     val task1 = setUpFreeformTask()
     setUpFreeformTask()
 
-    desktopModeTaskRepository.wallpaperActivityToken = MockToken().token()
+    taskRepository.wallpaperActivityToken = MockToken().token()
     val result = controller.handleRequest(Binder(), createTransition(task1, type = TRANSIT_TO_BACK))
 
     assertNull(result, "Should not handle request")
@@ -1559,7 +1736,7 @@
     val task1 = setUpFreeformTask()
     setUpFreeformTask()
 
-    desktopModeTaskRepository.wallpaperActivityToken = MockToken().token()
+    taskRepository.wallpaperActivityToken = MockToken().token()
     val result = controller.handleRequest(Binder(), createTransition(task1, type = TRANSIT_TO_BACK))
 
     assertNotNull(result, "Should handle request").assertRemoveAt(index = 0, task1.token)
@@ -1572,7 +1749,7 @@
     val task1 = setUpFreeformTask()
     setUpFreeformTask()
 
-    desktopModeTaskRepository.wallpaperActivityToken = MockToken().token()
+    taskRepository.wallpaperActivityToken = MockToken().token()
     val result = controller.handleRequest(Binder(), createTransition(task1, type = TRANSIT_TO_BACK))
 
     assertNull(result, "Should not handle request")
@@ -1588,8 +1765,8 @@
     val task2 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
     val wallpaperToken = MockToken().token()
 
-    desktopModeTaskRepository.wallpaperActivityToken = wallpaperToken
-    desktopModeTaskRepository.addClosingTask(displayId = DEFAULT_DISPLAY, taskId = task2.taskId)
+    taskRepository.wallpaperActivityToken = wallpaperToken
+    taskRepository.addClosingTask(displayId = DEFAULT_DISPLAY, taskId = task2.taskId)
     val result = controller.handleRequest(Binder(), createTransition(task1, type = TRANSIT_TO_BACK))
 
     // Should create remove wallpaper transaction
@@ -1605,8 +1782,8 @@
     val task2 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
     val wallpaperToken = MockToken().token()
 
-    desktopModeTaskRepository.wallpaperActivityToken = wallpaperToken
-    desktopModeTaskRepository.addClosingTask(displayId = DEFAULT_DISPLAY, taskId = task2.taskId)
+    taskRepository.wallpaperActivityToken = wallpaperToken
+    taskRepository.addClosingTask(displayId = DEFAULT_DISPLAY, taskId = task2.taskId)
     val result = controller.handleRequest(Binder(), createTransition(task1, type = TRANSIT_TO_BACK))
 
     // Should create remove wallpaper transaction
@@ -1623,8 +1800,8 @@
     val task2 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
     val wallpaperToken = MockToken().token()
 
-    desktopModeTaskRepository.wallpaperActivityToken = wallpaperToken
-    desktopModeTaskRepository.minimizeTask(displayId = DEFAULT_DISPLAY, taskId = task2.taskId)
+    taskRepository.wallpaperActivityToken = wallpaperToken
+    taskRepository.minimizeTask(displayId = DEFAULT_DISPLAY, taskId = task2.taskId)
     val result = controller.handleRequest(Binder(), createTransition(task1, type = TRANSIT_TO_BACK))
 
     // Should create remove wallpaper transaction
@@ -1640,8 +1817,8 @@
     val task2 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
     val wallpaperToken = MockToken().token()
 
-    desktopModeTaskRepository.wallpaperActivityToken = wallpaperToken
-    desktopModeTaskRepository.minimizeTask(displayId = DEFAULT_DISPLAY, taskId = task2.taskId)
+    taskRepository.wallpaperActivityToken = wallpaperToken
+    taskRepository.minimizeTask(displayId = DEFAULT_DISPLAY, taskId = task2.taskId)
     val result = controller.handleRequest(Binder(), createTransition(task1, type = TRANSIT_TO_BACK))
 
     // Should create remove wallpaper transaction
@@ -1658,11 +1835,11 @@
     val task2 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
     val wallpaperToken = MockToken().token()
 
-    desktopModeTaskRepository.wallpaperActivityToken = wallpaperToken
-    desktopModeTaskRepository.minimizeTask(displayId = DEFAULT_DISPLAY, taskId = task2.taskId)
+    taskRepository.wallpaperActivityToken = wallpaperToken
+    taskRepository.minimizeTask(displayId = DEFAULT_DISPLAY, taskId = task2.taskId)
     // Task is being minimized so mark it as not visible.
-    desktopModeTaskRepository
-      .updateVisibleFreeformTasks(displayId = DEFAULT_DISPLAY, task2.taskId, false)
+    taskRepository
+      .updateTaskVisibility(displayId = DEFAULT_DISPLAY, task2.taskId, false)
     val result = controller.handleRequest(Binder(), createTransition(task2, type = TRANSIT_TO_BACK))
 
     assertNull(result, "Should not handle request")
@@ -1713,7 +1890,7 @@
   fun handleRequest_closeTransition_singleTaskWithToken_noWallpaper_noBackNav_doesNotHandle() {
     val task = setUpFreeformTask()
 
-    desktopModeTaskRepository.wallpaperActivityToken = MockToken().token()
+    taskRepository.wallpaperActivityToken = MockToken().token()
     val result = controller.handleRequest(Binder(), createTransition(task, type = TRANSIT_CLOSE))
 
     assertNull(result, "Should not handle request")
@@ -1728,7 +1905,7 @@
     val task = setUpFreeformTask()
     val wallpaperToken = MockToken().token()
 
-    desktopModeTaskRepository.wallpaperActivityToken = wallpaperToken
+    taskRepository.wallpaperActivityToken = wallpaperToken
     val result = controller.handleRequest(Binder(), createTransition(task, type = TRANSIT_CLOSE))
 
     // Should create remove wallpaper transaction
@@ -1743,7 +1920,7 @@
     val task = setUpFreeformTask()
     val wallpaperToken = MockToken().token()
 
-    desktopModeTaskRepository.wallpaperActivityToken = wallpaperToken
+    taskRepository.wallpaperActivityToken = wallpaperToken
     val result = controller.handleRequest(Binder(), createTransition(task, type = TRANSIT_CLOSE))
 
     // Should create remove wallpaper transaction
@@ -1759,7 +1936,7 @@
     val task1 = setUpFreeformTask()
     setUpFreeformTask()
 
-    desktopModeTaskRepository.wallpaperActivityToken = MockToken().token()
+    taskRepository.wallpaperActivityToken = MockToken().token()
     val result = controller.handleRequest(Binder(), createTransition(task1, type = TRANSIT_CLOSE))
 
     assertNull(result, "Should not handle request")
@@ -1774,7 +1951,7 @@
     val task1 = setUpFreeformTask()
     setUpFreeformTask()
 
-    desktopModeTaskRepository.wallpaperActivityToken = MockToken().token()
+    taskRepository.wallpaperActivityToken = MockToken().token()
     val result = controller.handleRequest(Binder(), createTransition(task1, type = TRANSIT_CLOSE))
 
     assertNotNull(result, "Should handle request")
@@ -1788,7 +1965,7 @@
     val task1 = setUpFreeformTask()
     setUpFreeformTask()
 
-    desktopModeTaskRepository.wallpaperActivityToken = MockToken().token()
+    taskRepository.wallpaperActivityToken = MockToken().token()
     val result = controller.handleRequest(Binder(), createTransition(task1, type = TRANSIT_CLOSE))
 
     assertNull(result, "Should not handle request")
@@ -1804,8 +1981,8 @@
     val task2 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
     val wallpaperToken = MockToken().token()
 
-    desktopModeTaskRepository.wallpaperActivityToken = wallpaperToken
-    desktopModeTaskRepository.addClosingTask(displayId = DEFAULT_DISPLAY, taskId = task2.taskId)
+    taskRepository.wallpaperActivityToken = wallpaperToken
+    taskRepository.addClosingTask(displayId = DEFAULT_DISPLAY, taskId = task2.taskId)
     val result = controller.handleRequest(Binder(), createTransition(task1, type = TRANSIT_CLOSE))
 
     // Should create remove wallpaper transaction
@@ -1821,8 +1998,8 @@
     val task2 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
     val wallpaperToken = MockToken().token()
 
-    desktopModeTaskRepository.wallpaperActivityToken = wallpaperToken
-    desktopModeTaskRepository.addClosingTask(displayId = DEFAULT_DISPLAY, taskId = task2.taskId)
+    taskRepository.wallpaperActivityToken = wallpaperToken
+    taskRepository.addClosingTask(displayId = DEFAULT_DISPLAY, taskId = task2.taskId)
     val result = controller.handleRequest(Binder(), createTransition(task1, type = TRANSIT_CLOSE))
 
     // Should create remove wallpaper transaction
@@ -1839,8 +2016,8 @@
     val task2 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
     val wallpaperToken = MockToken().token()
 
-    desktopModeTaskRepository.wallpaperActivityToken = wallpaperToken
-    desktopModeTaskRepository.minimizeTask(displayId = DEFAULT_DISPLAY, taskId = task2.taskId)
+    taskRepository.wallpaperActivityToken = wallpaperToken
+    taskRepository.minimizeTask(displayId = DEFAULT_DISPLAY, taskId = task2.taskId)
     val result = controller.handleRequest(Binder(), createTransition(task1, type = TRANSIT_CLOSE))
 
     // Should create remove wallpaper transaction
@@ -1856,8 +2033,8 @@
     val task2 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
     val wallpaperToken = MockToken().token()
 
-    desktopModeTaskRepository.wallpaperActivityToken = wallpaperToken
-    desktopModeTaskRepository.minimizeTask(displayId = DEFAULT_DISPLAY, taskId = task2.taskId)
+    taskRepository.wallpaperActivityToken = wallpaperToken
+    taskRepository.minimizeTask(displayId = DEFAULT_DISPLAY, taskId = task2.taskId)
     val result = controller.handleRequest(Binder(), createTransition(task1, type = TRANSIT_CLOSE))
 
     // Should create remove wallpaper transaction
@@ -1874,11 +2051,11 @@
     val task2 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
     val wallpaperToken = MockToken().token()
 
-    desktopModeTaskRepository.wallpaperActivityToken = wallpaperToken
-    desktopModeTaskRepository.minimizeTask(displayId = DEFAULT_DISPLAY, taskId = task2.taskId)
+    taskRepository.wallpaperActivityToken = wallpaperToken
+    taskRepository.minimizeTask(displayId = DEFAULT_DISPLAY, taskId = task2.taskId)
     // Task is being minimized so mark it as not visible.
-    desktopModeTaskRepository
-      .updateVisibleFreeformTasks(displayId = DEFAULT_DISPLAY, task2.taskId, false)
+    taskRepository
+      .updateTaskVisibility(displayId = DEFAULT_DISPLAY, task2.taskId, false)
     val result = controller.handleRequest(Binder(), createTransition(task2, type = TRANSIT_TO_BACK))
 
     assertNull(result, "Should not handle request")
@@ -1972,9 +2149,9 @@
     task1.isFocused = false
     task2.isFocused = true
     task3.isFocused = false
-    desktopModeTaskRepository.wallpaperActivityToken = wallpaperToken
-    desktopModeTaskRepository.minimizeTask(DEFAULT_DISPLAY, task1.taskId)
-    desktopModeTaskRepository.updateVisibleFreeformTasks(DEFAULT_DISPLAY, task3.taskId,
+    taskRepository.wallpaperActivityToken = wallpaperToken
+    taskRepository.minimizeTask(DEFAULT_DISPLAY, task1.taskId)
+    taskRepository.updateTaskVisibility(DEFAULT_DISPLAY, task3.taskId,
       visible = false)
 
     controller.enterFullscreen(DEFAULT_DISPLAY, transitionSource = UNKNOWN)
@@ -1995,7 +2172,7 @@
     task1.isFocused = false
     task2.isFocused = true
     task3.isFocused = false
-    desktopModeTaskRepository.wallpaperActivityToken = wallpaperToken
+    taskRepository.wallpaperActivityToken = wallpaperToken
     controller.enterFullscreen(DEFAULT_DISPLAY, transitionSource = UNKNOWN)
 
     val wct = getLatestExitDesktopWct()
@@ -2016,7 +2193,7 @@
     val task = setUpFullscreenTask()
     setUpLandscapeDisplay()
 
-    spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task)
+    spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task, mockSurface)
     val wct = getLatestDragToDesktopWct()
     assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_LANDSCAPE_BOUNDS)
   }
@@ -2032,7 +2209,7 @@
     val task = setUpFullscreenTask(screenOrientation = SCREEN_ORIENTATION_LANDSCAPE)
     setUpLandscapeDisplay()
 
-    spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task)
+    spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task, mockSurface)
     val wct = getLatestDragToDesktopWct()
     assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_LANDSCAPE_BOUNDS)
   }
@@ -2049,7 +2226,7 @@
         setUpFullscreenTask(screenOrientation = SCREEN_ORIENTATION_PORTRAIT, shouldLetterbox = true)
     setUpLandscapeDisplay()
 
-    spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task)
+    spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task, mockSurface)
     val wct = getLatestDragToDesktopWct()
     assertThat(findBoundsChange(wct, task)).isEqualTo(RESIZABLE_PORTRAIT_BOUNDS)
   }
@@ -2066,7 +2243,7 @@
         setUpFullscreenTask(isResizable = false, screenOrientation = SCREEN_ORIENTATION_LANDSCAPE)
     setUpLandscapeDisplay()
 
-    spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task)
+    spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task, mockSurface)
     val wct = getLatestDragToDesktopWct()
     assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_LANDSCAPE_BOUNDS)
   }
@@ -2086,7 +2263,7 @@
             shouldLetterbox = true)
     setUpLandscapeDisplay()
 
-    spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task)
+    spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task, mockSurface)
     val wct = getLatestDragToDesktopWct()
     assertThat(findBoundsChange(wct, task)).isEqualTo(UNRESIZABLE_PORTRAIT_BOUNDS)
   }
@@ -2102,7 +2279,7 @@
     val task = setUpFullscreenTask(deviceOrientation = ORIENTATION_PORTRAIT)
     setUpPortraitDisplay()
 
-    spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task)
+    spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task, mockSurface)
     val wct = getLatestDragToDesktopWct()
     assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_PORTRAIT_BOUNDS)
   }
@@ -2121,7 +2298,7 @@
             screenOrientation = SCREEN_ORIENTATION_PORTRAIT)
     setUpPortraitDisplay()
 
-    spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task)
+    spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task, mockSurface)
     val wct = getLatestDragToDesktopWct()
     assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_PORTRAIT_BOUNDS)
   }
@@ -2141,7 +2318,7 @@
             shouldLetterbox = true)
     setUpPortraitDisplay()
 
-    spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task)
+    spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task, mockSurface)
     val wct = getLatestDragToDesktopWct()
     assertThat(findBoundsChange(wct, task)).isEqualTo(RESIZABLE_LANDSCAPE_BOUNDS)
   }
@@ -2161,7 +2338,7 @@
             screenOrientation = SCREEN_ORIENTATION_PORTRAIT)
     setUpPortraitDisplay()
 
-    spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task)
+    spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task, mockSurface)
     val wct = getLatestDragToDesktopWct()
     assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_PORTRAIT_BOUNDS)
   }
@@ -2182,7 +2359,7 @@
             shouldLetterbox = true)
     setUpPortraitDisplay()
 
-    spyController.onDragPositioningEndThroughStatusBar(PointF(200f, 200f), task)
+    spyController.onDragPositioningEndThroughStatusBar(PointF(200f, 200f), task, mockSurface)
     val wct = getLatestDragToDesktopWct()
     assertThat(findBoundsChange(wct, task)).isEqualTo(UNRESIZABLE_LANDSCAPE_BOUNDS)
   }
@@ -2200,7 +2377,7 @@
         task,
         Point(100, -100), /* position */
         PointF(200f, -200f), /* inputCoordinate */
-        Rect(100, -100, 500, 1000), /* taskBounds */
+        Rect(100, -100, 500, 1000), /* currentDragBounds */
         Rect(0, 50, 2000, 2000) /* validDragArea */)
     val rectAfterEnd = Rect(100, 50, 500, 1150)
     verify(transitions)
@@ -2215,6 +2392,40 @@
   }
 
   @Test
+  fun onDesktopDragEnd_noIndicator_updatesTaskBounds() {
+    val task = setUpFreeformTask()
+    val spyController = spy(controller)
+    val mockSurface = mock(SurfaceControl::class.java)
+    val mockDisplayLayout = mock(DisplayLayout::class.java)
+    whenever(displayController.getDisplayLayout(task.displayId)).thenReturn(mockDisplayLayout)
+    whenever(mockDisplayLayout.stableInsets()).thenReturn(Rect(0, 100, 2000, 2000))
+    spyController.onDragPositioningMove(task, mockSurface, 200f, Rect(100, 200, 500, 1000))
+
+    val currentDragBounds = Rect(100, 200, 500, 1000)
+    whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
+    whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull(), anyOrNull()))
+      .thenReturn(DesktopModeVisualIndicator.IndicatorType.NO_INDICATOR)
+
+    spyController.onDragPositioningEnd(
+      task,
+      Point(100, 200), /* position */
+      PointF(200f, 300f), /* inputCoordinate */
+      currentDragBounds, /* currentDragBounds */
+      Rect(0, 50, 2000, 2000) /* validDragArea */)
+
+
+    verify(transitions)
+      .startTransition(
+        eq(TRANSIT_CHANGE),
+        Mockito.argThat { wct ->
+          return@argThat wct.changes.any { (token, change) ->
+            change.configuration.windowConfiguration.bounds == currentDragBounds
+          }
+        },
+        eq(null))
+  }
+
+  @Test
   fun enterSplit_freeformTaskIsMovedToSplit() {
     val task1 = setUpFreeformTask()
     val task2 = setUpFreeformTask()
@@ -2244,9 +2455,9 @@
     task1.isFocused = false
     task2.isFocused = true
     task3.isFocused = false
-    desktopModeTaskRepository.wallpaperActivityToken = wallpaperToken
-    desktopModeTaskRepository.minimizeTask(DEFAULT_DISPLAY, task1.taskId)
-    desktopModeTaskRepository.updateVisibleFreeformTasks(DEFAULT_DISPLAY, task3.taskId,
+    taskRepository.wallpaperActivityToken = wallpaperToken
+    taskRepository.minimizeTask(DEFAULT_DISPLAY, task1.taskId)
+    taskRepository.updateTaskVisibility(DEFAULT_DISPLAY, task3.taskId,
       visible = false)
 
     controller.enterSplit(DEFAULT_DISPLAY, leftOrTop = false)
@@ -2272,7 +2483,7 @@
     task1.isFocused = false
     task2.isFocused = true
     task3.isFocused = false
-    desktopModeTaskRepository.wallpaperActivityToken = wallpaperToken
+    taskRepository.wallpaperActivityToken = wallpaperToken
 
     controller.enterSplit(DEFAULT_DISPLAY, leftOrTop = false)
 
@@ -2299,6 +2510,28 @@
   }
 
   @Test
+  fun getSnapBounds_calculatesBoundsForResizable() {
+    val bounds = Rect(100, 100, 300, 300)
+    val task = setUpFreeformTask(DEFAULT_DISPLAY, bounds).apply {
+      topActivityInfo = ActivityInfo().apply {
+        screenOrientation = SCREEN_ORIENTATION_LANDSCAPE
+        configuration.windowConfiguration.appBounds = bounds
+      }
+      isResizeable = true
+    }
+
+    val currentDragBounds = Rect(0, 100, 200, 300)
+    val expectedBounds = Rect(
+      STABLE_BOUNDS.left, STABLE_BOUNDS.top, STABLE_BOUNDS.right / 2, STABLE_BOUNDS.bottom
+    )
+
+    controller.snapToHalfScreen(task, currentDragBounds, SnapPosition.LEFT)
+    // Assert bounds set to stable bounds
+    val wct = getLatestToggleResizeDesktopTaskWct(currentDragBounds)
+    assertThat(findBoundsChange(wct, task)).isEqualTo(expectedBounds)
+  }
+
+  @Test
   fun toggleBounds_togglesToCalculatedBoundsForNonResizable() {
     val bounds = Rect(0, 0, 200, 100)
     val task = setUpFreeformTask(DEFAULT_DISPLAY, bounds).apply {
@@ -2324,7 +2557,7 @@
     val task = setUpFreeformTask(DEFAULT_DISPLAY, bounds)
 
     controller.toggleDesktopTaskSize(task)
-    assertThat(desktopModeTaskRepository.removeBoundsBeforeMaximize(task.taskId)).isEqualTo(bounds)
+    assertThat(taskRepository.removeBoundsBeforeMaximize(task.taskId)).isEqualTo(bounds)
   }
 
   @Test
@@ -2397,12 +2630,24 @@
     controller.toggleDesktopTaskSize(task)
 
     // Assert last bounds before maximize removed after use
-    assertThat(desktopModeTaskRepository.removeBoundsBeforeMaximize(task.taskId)).isNull()
+    assertThat(taskRepository.removeBoundsBeforeMaximize(task.taskId)).isNull()
   }
 
   private val desktopWallpaperIntent: Intent
     get() = Intent(context, DesktopWallpaperActivity::class.java)
 
+  private fun addFreeformTaskAtPosition(
+    pos: DesktopTaskPosition,
+    stableBounds: Rect,
+    bounds: Rect = DEFAULT_LANDSCAPE_BOUNDS,
+    offsetPos: Point = Point(0, 0)
+  ): RunningTaskInfo {
+    val offset = pos.getTopLeftCoordinates(stableBounds, bounds)
+    val prevTaskBounds = Rect(bounds)
+    prevTaskBounds.offsetTo(offset.x + offsetPos.x, offset.y + offsetPos.y)
+    return setUpFreeformTask(bounds = prevTaskBounds)
+  }
+
   private fun setUpFreeformTask(
       displayId: Int = DEFAULT_DISPLAY,
       bounds: Rect? = null
@@ -2411,9 +2656,9 @@
     val activityInfo = ActivityInfo()
     task.topActivityInfo = activityInfo
     whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task)
-    desktopModeTaskRepository.addActiveTask(displayId, task.taskId)
-    desktopModeTaskRepository.updateVisibleFreeformTasks(displayId, task.taskId, visible = true)
-    desktopModeTaskRepository.addOrMoveFreeformTaskToTop(displayId, task.taskId)
+    taskRepository.addActiveTask(displayId, task.taskId)
+    taskRepository.updateTaskVisibility(displayId, task.taskId, visible = true)
+    taskRepository.addOrMoveFreeformTaskToTop(displayId, task.taskId)
     runningTasks.add(task)
     return task
   }
@@ -2431,11 +2676,13 @@
       windowingMode: Int = WINDOWING_MODE_FULLSCREEN,
       deviceOrientation: Int = ORIENTATION_LANDSCAPE,
       screenOrientation: Int = SCREEN_ORIENTATION_UNSPECIFIED,
-      shouldLetterbox: Boolean = false
+      shouldLetterbox: Boolean = false,
+      gravity: Int = Gravity.NO_GRAVITY
   ): RunningTaskInfo {
     val task = createFullscreenTask(displayId)
     val activityInfo = ActivityInfo()
     activityInfo.screenOrientation = screenOrientation
+    activityInfo.windowLayout = ActivityInfo.WindowLayout(0, 0F, 0, 0F, gravity, 0, 0)
     with(task) {
       topActivityInfo = activityInfo
       isResizeable = isResizable
@@ -2476,11 +2723,23 @@
   private fun setUpLandscapeDisplay() {
     whenever(displayLayout.width()).thenReturn(DISPLAY_DIMENSION_LONG)
     whenever(displayLayout.height()).thenReturn(DISPLAY_DIMENSION_SHORT)
+    val stableBounds = Rect(0, 0, DISPLAY_DIMENSION_LONG,
+      DISPLAY_DIMENSION_SHORT - Companion.TASKBAR_FRAME_HEIGHT
+    )
+    whenever(displayLayout.getStableBoundsForDesktopMode(any())).thenAnswer { i ->
+      (i.arguments.first() as Rect).set(stableBounds)
+    }
   }
 
   private fun setUpPortraitDisplay() {
     whenever(displayLayout.width()).thenReturn(DISPLAY_DIMENSION_SHORT)
     whenever(displayLayout.height()).thenReturn(DISPLAY_DIMENSION_LONG)
+    val stableBounds = Rect(0, 0, DISPLAY_DIMENSION_SHORT,
+      DISPLAY_DIMENSION_LONG - Companion.TASKBAR_FRAME_HEIGHT
+    )
+    whenever(displayLayout.getStableBoundsForDesktopMode(any())).thenAnswer { i ->
+      (i.arguments.first() as Rect).set(stableBounds)
+    }
   }
 
   private fun setUpSplitScreenTask(displayId: Int = DEFAULT_DISPLAY): RunningTaskInfo {
@@ -2492,12 +2751,12 @@
   }
 
   private fun markTaskVisible(task: RunningTaskInfo) {
-    desktopModeTaskRepository.updateVisibleFreeformTasks(
+    taskRepository.updateTaskVisibility(
         task.displayId, task.taskId, visible = true)
   }
 
   private fun markTaskHidden(task: RunningTaskInfo) {
-    desktopModeTaskRepository.updateVisibleFreeformTasks(
+    taskRepository.updateTaskVisibility(
         task.displayId, task.taskId, visible = false)
   }
 
@@ -2518,11 +2777,14 @@
     return arg.value
   }
 
-  private fun getLatestToggleResizeDesktopTaskWct(): WindowContainerTransaction {
+  private fun getLatestToggleResizeDesktopTaskWct(
+    currentBounds: Rect? = null
+  ): WindowContainerTransaction {
     val arg: ArgumentCaptor<WindowContainerTransaction> =
         ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
     if (ENABLE_SHELL_TRANSITIONS) {
-      verify(toggleResizeDesktopTaskTransitionHandler, atLeastOnce()).startTransition(capture(arg))
+      verify(toggleResizeDesktopTaskTransitionHandler, atLeastOnce())
+        .startTransition(capture(arg), eq(currentBounds))
     } else {
       verify(shellTaskOrganizer).applyTransaction(capture(arg))
     }
@@ -2594,9 +2856,11 @@
     return TransitionRequestInfo(type, task, null /* remoteTransition */)
   }
 
-  companion object {
+  private companion object {
     const val SECOND_DISPLAY = 2
-    private val STABLE_BOUNDS = Rect(0, 0, 1000, 1000)
+    val STABLE_BOUNDS = Rect(0, 0, 1000, 1000)
+    const val MAX_TASK_LIMIT = 6
+    private const val TASKBAR_FRAME_HEIGHT = 200
   }
 }
 
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 8d9fd91..2d0e428 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
@@ -30,6 +30,8 @@
 import com.android.dx.mockito.inline.extended.ExtendedMockito
 import com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn
 import com.android.dx.mockito.inline.extended.StaticMockitoSession
+import com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_MINIMIZE_WINDOW
+import com.android.internal.jank.InteractionJankMonitor
 import com.android.wm.shell.ShellTaskOrganizer
 import com.android.wm.shell.ShellTestCase
 import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFreeformTask
@@ -38,6 +40,7 @@
 import com.android.wm.shell.transition.Transitions
 import com.android.wm.shell.util.StubTransaction
 import com.google.common.truth.Truth.assertThat
+import kotlin.test.assertFailsWith
 import org.junit.After
 import org.junit.Before
 import org.junit.Rule
@@ -46,6 +49,8 @@
 import org.mockito.Mock
 import org.mockito.Mockito.any
 import org.mockito.Mockito.`when`
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.verify
 import org.mockito.quality.Strictness
 
 
@@ -64,6 +69,7 @@
 
     @Mock lateinit var shellTaskOrganizer: ShellTaskOrganizer
     @Mock lateinit var transitions: Transitions
+    @Mock lateinit var interactionJankMonitor: InteractionJankMonitor
 
     private lateinit var mockitoSession: StaticMockitoSession
     private lateinit var desktopTasksLimiter: DesktopTasksLimiter
@@ -77,8 +83,9 @@
 
         desktopTaskRepo = DesktopModeTaskRepository()
 
-        desktopTasksLimiter = DesktopTasksLimiter(
-                transitions, desktopTaskRepo, shellTaskOrganizer)
+        desktopTasksLimiter =
+            DesktopTasksLimiter(transitions, desktopTaskRepo, shellTaskOrganizer, MAX_TASK_LIMIT,
+                interactionJankMonitor, mContext)
     }
 
     @After
@@ -86,12 +93,20 @@
         mockitoSession.finishMocking()
     }
 
-    // Currently, the task limit can be overridden through an adb flag. This test ensures the limit
-    // hasn't been overridden.
     @Test
-    fun getMaxTaskLimit_isSameAsConstant() {
-        assertThat(desktopTasksLimiter.getMaxTaskLimit()).isEqualTo(
-            DesktopModeStatus.DEFAULT_MAX_TASK_LIMIT)
+    fun createDesktopTasksLimiter_withZeroLimit_shouldThrow() {
+        assertFailsWith<IllegalArgumentException> {
+            DesktopTasksLimiter(transitions, desktopTaskRepo, shellTaskOrganizer, 0,
+                interactionJankMonitor, mContext)
+        }
+    }
+
+    @Test
+    fun createDesktopTasksLimiter_withNegativeLimit_shouldThrow() {
+        assertFailsWith<IllegalArgumentException> {
+            DesktopTasksLimiter(transitions, desktopTaskRepo, shellTaskOrganizer, -5,
+                interactionJankMonitor, mContext)
+        }
     }
 
     @Test
@@ -247,8 +262,7 @@
 
     @Test
     fun addAndGetMinimizeTaskChangesIfNeeded_tasksWithinLimit_noTaskMinimized() {
-        val taskLimit = desktopTasksLimiter.getMaxTaskLimit()
-        (1..<taskLimit).forEach { _ -> setUpFreeformTask() }
+        (1..<MAX_TASK_LIMIT).forEach { _ -> setUpFreeformTask() }
 
         val wct = WindowContainerTransaction()
         val minimizedTaskId =
@@ -263,9 +277,8 @@
 
     @Test
     fun addAndGetMinimizeTaskChangesIfNeeded_tasksAboveLimit_backTaskMinimized() {
-        val taskLimit = desktopTasksLimiter.getMaxTaskLimit()
         // The following list will be ordered bottom -> top, as the last task is moved to top last.
-        val tasks = (1..taskLimit).map { setUpFreeformTask() }
+        val tasks = (1..MAX_TASK_LIMIT).map { setUpFreeformTask() }
 
         val wct = WindowContainerTransaction()
         val minimizedTaskId =
@@ -282,8 +295,7 @@
 
     @Test
     fun addAndGetMinimizeTaskChangesIfNeeded_nonMinimizedTasksWithinLimit_noTaskMinimized() {
-        val taskLimit = desktopTasksLimiter.getMaxTaskLimit()
-        val tasks = (1..taskLimit).map { setUpFreeformTask() }
+        val tasks = (1..MAX_TASK_LIMIT).map { setUpFreeformTask() }
         desktopTaskRepo.minimizeTask(displayId = DEFAULT_DISPLAY, taskId = tasks[0].taskId)
 
         val wct = WindowContainerTransaction()
@@ -299,8 +311,7 @@
 
     @Test
     fun getTaskToMinimizeIfNeeded_tasksWithinLimit_returnsNull() {
-        val taskLimit = desktopTasksLimiter.getMaxTaskLimit()
-        val tasks = (1..taskLimit).map { setUpFreeformTask() }
+        val tasks = (1..MAX_TASK_LIMIT).map { setUpFreeformTask() }
 
         val minimizedTask = desktopTasksLimiter.getTaskToMinimizeIfNeeded(
                 visibleFreeformTaskIdsOrderedFrontToBack = tasks.map { it.taskId })
@@ -310,8 +321,7 @@
 
     @Test
     fun getTaskToMinimizeIfNeeded_tasksAboveLimit_returnsBackTask() {
-        val taskLimit = desktopTasksLimiter.getMaxTaskLimit()
-        val tasks = (1..taskLimit + 1).map { setUpFreeformTask() }
+        val tasks = (1..MAX_TASK_LIMIT + 1).map { setUpFreeformTask() }
 
         val minimizedTask = desktopTasksLimiter.getTaskToMinimizeIfNeeded(
                 visibleFreeformTaskIdsOrderedFrontToBack = tasks.map { it.taskId })
@@ -321,9 +331,22 @@
     }
 
     @Test
+    fun getTaskToMinimizeIfNeeded_tasksAboveLimit_otherLimit_returnsBackTask() {
+        desktopTasksLimiter =
+            DesktopTasksLimiter(transitions, desktopTaskRepo, shellTaskOrganizer, MAX_TASK_LIMIT2,
+                interactionJankMonitor, mContext)
+        val tasks = (1..MAX_TASK_LIMIT2 + 1).map { setUpFreeformTask() }
+
+        val minimizedTask = desktopTasksLimiter.getTaskToMinimizeIfNeeded(
+            visibleFreeformTaskIdsOrderedFrontToBack = tasks.map { it.taskId })
+
+        // first == front, last == back
+        assertThat(minimizedTask).isEqualTo(tasks.last())
+    }
+
+    @Test
     fun getTaskToMinimizeIfNeeded_withNewTask_tasksAboveLimit_returnsBackTask() {
-        val taskLimit = desktopTasksLimiter.getMaxTaskLimit()
-        val tasks = (1..taskLimit).map { setUpFreeformTask() }
+        val tasks = (1..MAX_TASK_LIMIT).map { setUpFreeformTask() }
 
         val minimizedTask = desktopTasksLimiter.getTaskToMinimizeIfNeeded(
                 visibleFreeformTaskIdsOrderedFrontToBack = tasks.map { it.taskId },
@@ -333,6 +356,91 @@
         assertThat(minimizedTask).isEqualTo(tasks.last())
     }
 
+    @Test
+    fun minimizeTransitionReadyAndFinished_logsJankInstrumentationBeginAndEnd() {
+        (1..<MAX_TASK_LIMIT).forEach { _ -> setUpFreeformTask() }
+        val transition = Binder()
+        val task = setUpFreeformTask()
+        desktopTasksLimiter.addPendingMinimizeChange(
+            transition, displayId = DEFAULT_DISPLAY, taskId = task.taskId)
+
+        desktopTasksLimiter.getTransitionObserver().onTransitionReady(
+            transition,
+            TransitionInfoBuilder(TRANSIT_OPEN).build(),
+            StubTransaction() /* startTransaction */,
+            StubTransaction() /* finishTransaction */)
+
+        desktopTasksLimiter.getTransitionObserver().onTransitionStarting(transition)
+
+        verify(interactionJankMonitor).begin(
+            any(),
+            eq(mContext),
+            eq(CUJ_DESKTOP_MODE_MINIMIZE_WINDOW))
+
+        desktopTasksLimiter.getTransitionObserver().onTransitionFinished(
+            transition,
+            /* aborted = */ false)
+
+        verify(interactionJankMonitor).end(eq(CUJ_DESKTOP_MODE_MINIMIZE_WINDOW))
+    }
+
+    @Test
+    fun minimizeTransitionReadyAndAborted_logsJankInstrumentationBeginAndCancel() {
+        (1..<MAX_TASK_LIMIT).forEach { _ -> setUpFreeformTask() }
+        val transition = Binder()
+        val task = setUpFreeformTask()
+        desktopTasksLimiter.addPendingMinimizeChange(
+            transition, displayId = DEFAULT_DISPLAY, taskId = task.taskId)
+
+        desktopTasksLimiter.getTransitionObserver().onTransitionReady(
+            transition,
+            TransitionInfoBuilder(TRANSIT_OPEN).build(),
+            StubTransaction() /* startTransaction */,
+            StubTransaction() /* finishTransaction */)
+
+        desktopTasksLimiter.getTransitionObserver().onTransitionStarting(transition)
+
+        verify(interactionJankMonitor).begin(
+            any(),
+            eq(mContext),
+            eq(CUJ_DESKTOP_MODE_MINIMIZE_WINDOW))
+
+        desktopTasksLimiter.getTransitionObserver().onTransitionFinished(
+            transition,
+            /* aborted = */ true)
+
+        verify(interactionJankMonitor).cancel(eq(CUJ_DESKTOP_MODE_MINIMIZE_WINDOW))
+    }
+
+    @Test
+    fun minimizeTransitionReadyAndMerged_logsJankInstrumentationBeginAndEnd() {
+        (1..<MAX_TASK_LIMIT).forEach { _ -> setUpFreeformTask() }
+        val mergedTransition = Binder()
+        val newTransition = Binder()
+        val task = setUpFreeformTask()
+        desktopTasksLimiter.addPendingMinimizeChange(
+            mergedTransition, displayId = DEFAULT_DISPLAY, taskId = task.taskId)
+
+        desktopTasksLimiter.getTransitionObserver().onTransitionReady(
+            mergedTransition,
+            TransitionInfoBuilder(TRANSIT_OPEN).build(),
+            StubTransaction() /* startTransaction */,
+            StubTransaction() /* finishTransaction */)
+
+        desktopTasksLimiter.getTransitionObserver().onTransitionStarting(mergedTransition)
+
+        verify(interactionJankMonitor).begin(
+            any(),
+            eq(mContext),
+            eq(CUJ_DESKTOP_MODE_MINIMIZE_WINDOW))
+
+        desktopTasksLimiter.getTransitionObserver().onTransitionMerged(
+            mergedTransition,
+            newTransition)
+
+        verify(interactionJankMonitor).end(eq(CUJ_DESKTOP_MODE_MINIMIZE_WINDOW))
+    }
+
     private fun setUpFreeformTask(
             displayId: Int = DEFAULT_DISPLAY,
     ): RunningTaskInfo {
@@ -344,7 +452,7 @@
     }
 
     private fun markTaskVisible(task: RunningTaskInfo) {
-        desktopTaskRepo.updateVisibleFreeformTasks(
+        desktopTaskRepo.updateTaskVisibility(
                 task.displayId,
                 task.taskId,
                 visible = true
@@ -352,10 +460,15 @@
     }
 
     private fun markTaskHidden(task: RunningTaskInfo) {
-        desktopTaskRepo.updateVisibleFreeformTasks(
+        desktopTaskRepo.updateTaskVisibility(
                 task.displayId,
                 task.taskId,
                 visible = false
         )
     }
+
+    private companion object {
+        const val MAX_TASK_LIMIT = 6
+        const val MAX_TASK_LIMIT2 = 9
+    }
 }
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 bbf523b..e4e2bd2 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
@@ -15,6 +15,7 @@
 import android.window.TransitionInfo.FLAG_IS_WALLPAPER
 import android.window.WindowContainerTransaction
 import androidx.test.filters.SmallTest
+import com.android.internal.jank.InteractionJankMonitor
 import com.android.wm.shell.RootTaskDisplayAreaOrganizer
 import com.android.wm.shell.ShellTestCase
 import com.android.wm.shell.TestRunningTaskInfoBuilder
@@ -51,6 +52,8 @@
     @Mock private lateinit var taskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer
     @Mock private lateinit var splitScreenController: SplitScreenController
     @Mock private lateinit var dragAnimator: MoveToDesktopAnimator
+    @Mock
+    private lateinit var mockInteractionJankMonitor: InteractionJankMonitor
 
     private val transactionSupplier = Supplier { mock<SurfaceControl.Transaction>() }
 
@@ -63,7 +66,8 @@
                     context,
                     transitions,
                     taskDisplayAreaOrganizer,
-                    transactionSupplier
+                    mockInteractionJankMonitor,
+                    transactionSupplier,
                 )
                 .apply { setSplitScreenController(splitScreenController) }
     }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/TaskStackTransitionObserverTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/TaskStackTransitionObserverTest.kt
index 0e5efa6..bc9b44e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/TaskStackTransitionObserverTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/TaskStackTransitionObserverTest.kt
@@ -18,6 +18,7 @@
 
 import android.app.ActivityManager
 import android.app.WindowConfiguration
+import android.content.Context
 import android.os.IBinder
 import android.platform.test.annotations.EnableFlags
 import android.platform.test.flag.junit.SetFlagsRule
@@ -59,6 +60,7 @@
 
     @JvmField @Rule val setFlagsRule = SetFlagsRule()
 
+    @Mock private lateinit var context: Context
     @Mock private lateinit var shellInit: ShellInit
     @Mock lateinit var testExecutor: ShellExecutor
     @Mock private lateinit var transitionsLazy: Lazy<Transitions>
@@ -72,7 +74,7 @@
         MockitoAnnotations.initMocks(this)
         shellInit = Mockito.spy(ShellInit(testExecutor))
         whenever(transitionsLazy.get()).thenReturn(transitions)
-        transitionObserver = TaskStackTransitionObserver(transitionsLazy, shellInit)
+        transitionObserver = TaskStackTransitionObserver(context, transitionsLazy, shellInit)
         if (Transitions.ENABLE_SHELL_TRANSITIONS) {
             val initRunnableCaptor = ArgumentCaptor.forClass(Runnable::class.java)
             verify(shellInit)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlagsTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlagsTest.kt
index b1d62f4..dd19d76 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlagsTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlagsTest.kt
@@ -184,108 +184,6 @@
   }
 
   @Test
-  @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION)
-  @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
-  fun isEnabled_noSystemProperty_overrideOn_featureFlagOff_returnsTrueAndStoresPropertyOn() {
-    System.clearProperty(SYSTEM_PROPERTY_OVERRIDE_KEY)
-    setOverride(OVERRIDE_ON.setting)
-
-    assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isTrue()
-    // Store System Property if not present
-    assertThat(System.getProperty(SYSTEM_PROPERTY_OVERRIDE_KEY))
-        .isEqualTo(OVERRIDE_ON.setting.toString())
-  }
-
-  @Test
-  @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
-  fun isEnabled_noSystemProperty_overrideUnset_featureFlagOn_returnsTrueAndStoresPropertyUnset() {
-    System.clearProperty(SYSTEM_PROPERTY_OVERRIDE_KEY)
-    setOverride(OVERRIDE_UNSET.setting)
-
-    assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isTrue()
-    // Store System Property if not present
-    assertThat(System.getProperty(SYSTEM_PROPERTY_OVERRIDE_KEY))
-        .isEqualTo(OVERRIDE_UNSET.setting.toString())
-  }
-
-  @Test
-  @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION)
-  @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
-  fun isEnabled_noSystemProperty_overrideUnset_featureFlagOff_returnsFalseAndStoresPropertyUnset() {
-    System.clearProperty(SYSTEM_PROPERTY_OVERRIDE_KEY)
-    setOverride(OVERRIDE_UNSET.setting)
-
-    assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isFalse()
-    // Store System Property if not present
-    assertThat(System.getProperty(SYSTEM_PROPERTY_OVERRIDE_KEY))
-        .isEqualTo(OVERRIDE_UNSET.setting.toString())
-  }
-
-  @Test
-  @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
-  @Suppress("ktlint:standard:max-line-length")
-  fun isEnabled_systemPropertyNotInteger_overrideOff_featureFlagOn_returnsFalseAndStoresPropertyOff() {
-    System.setProperty(SYSTEM_PROPERTY_OVERRIDE_KEY, "abc")
-    setOverride(OVERRIDE_OFF.setting)
-
-    assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isFalse()
-    // Store System Property if currently invalid
-    assertThat(System.getProperty(SYSTEM_PROPERTY_OVERRIDE_KEY))
-        .isEqualTo(OVERRIDE_OFF.setting.toString())
-  }
-
-  @Test
-  @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
-  @Suppress("ktlint:standard:max-line-length")
-  fun isEnabled_systemPropertyInvalidInteger_overrideOff_featureFlagOn_returnsFalseAndStoresPropertyOff() {
-    System.setProperty(SYSTEM_PROPERTY_OVERRIDE_KEY, "-2")
-    setOverride(OVERRIDE_OFF.setting)
-
-    assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isFalse()
-    // Store System Property if currently invalid
-    assertThat(System.getProperty(SYSTEM_PROPERTY_OVERRIDE_KEY))
-        .isEqualTo(OVERRIDE_OFF.setting.toString())
-  }
-
-  @Test
-  @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
-  fun isEnabled_systemPropertyOff_overrideOn_featureFlagOn_returnsFalseAndDoesNotUpdateProperty() {
-    System.setProperty(SYSTEM_PROPERTY_OVERRIDE_KEY, OVERRIDE_OFF.setting.toString())
-    setOverride(OVERRIDE_ON.setting)
-
-    // Have a consistent override until reboot
-    assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isFalse()
-    assertThat(System.getProperty(SYSTEM_PROPERTY_OVERRIDE_KEY))
-        .isEqualTo(OVERRIDE_OFF.setting.toString())
-  }
-
-  @Test
-  @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION)
-  @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
-  fun isEnabled_systemPropertyOn_overrideOff_featureFlagOff_returnsTrueAndDoesNotUpdateProperty() {
-    System.setProperty(SYSTEM_PROPERTY_OVERRIDE_KEY, OVERRIDE_ON.setting.toString())
-    setOverride(OVERRIDE_OFF.setting)
-
-    // Have a consistent override until reboot
-    assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isTrue()
-    assertThat(System.getProperty(SYSTEM_PROPERTY_OVERRIDE_KEY))
-        .isEqualTo(OVERRIDE_ON.setting.toString())
-  }
-
-  @Test
-  @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
-  @Suppress("ktlint:standard:max-line-length")
-  fun isEnabled_systemPropertyUnset_overrideOff_featureFlagOn_returnsTrueAndDoesNotUpdateProperty() {
-    System.setProperty(SYSTEM_PROPERTY_OVERRIDE_KEY, OVERRIDE_UNSET.setting.toString())
-    setOverride(OVERRIDE_OFF.setting)
-
-    // Have a consistent override until reboot
-    assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isTrue()
-    assertThat(System.getProperty(SYSTEM_PROPERTY_OVERRIDE_KEY))
-        .isEqualTo(OVERRIDE_UNSET.setting.toString())
-  }
-
-  @Test
   @EnableFlags(
       FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION,
       FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
@@ -445,12 +343,5 @@
       DesktopModeFlags::class.java.getDeclaredField("cachedToggleOverride")
     cachedToggleOverride.isAccessible = true
     cachedToggleOverride.set(null, null)
-
-    // Clear override cache stored in System property
-    System.clearProperty(SYSTEM_PROPERTY_OVERRIDE_KEY)
-  }
-
-  private companion object {
-    const val SYSTEM_PROPERTY_OVERRIDE_KEY = "sys.wmshell.desktopmode.dev_toggle_override"
   }
 }
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 37ef788..22b408c 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
@@ -51,8 +51,10 @@
 import android.app.ActivityManager;
 import android.os.Handler;
 import android.os.IBinder;
+import android.os.RemoteException;
 import android.view.SurfaceControl;
 import android.view.SurfaceSession;
+import android.window.IRemoteTransition;
 import android.window.RemoteTransition;
 import android.window.TransitionInfo;
 import android.window.TransitionRequestInfo;
@@ -328,6 +330,32 @@
 
     @Test
     @UiThreadTest
+    public void testRemotePassThroughInvoked() throws RemoteException {
+        RemoteTransition remoteWrapper = mock(RemoteTransition.class);
+        IRemoteTransition remoteTransition = mock(IRemoteTransition.class);
+        IBinder remoteBinder = mock(IBinder.class);
+        doReturn(remoteBinder).when(remoteTransition).asBinder();
+        doReturn(remoteTransition).when(remoteWrapper).getRemoteTransition();
+
+        TransitionRequestInfo request = new TransitionRequestInfo(TRANSIT_CHANGE, null,
+                remoteWrapper);
+        IBinder transition = mock(IBinder.class);
+        mMainStage.activate(new WindowContainerTransaction(), false);
+        mStageCoordinator.handleRequest(transition, request);
+        TransitionInfo info = new TransitionInfoBuilder(TRANSIT_CHANGE, 0)
+                .build();
+        boolean accepted = mStageCoordinator.startAnimation(transition, info,
+                mock(SurfaceControl.Transaction.class),
+                mock(SurfaceControl.Transaction.class),
+                mock(Transitions.TransitionFinishCallback.class));
+        assertTrue(accepted);
+
+        verify(remoteTransition, times(1)).startAnimation(any(),
+                any(), any(), any());
+    }
+
+    @Test
+    @UiThreadTest
     public void testEnterRecentsAndRestore() {
         enterSplit();
 
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
index 409b877..81e6d07 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
@@ -442,6 +442,27 @@
     }
 
     @Test
+    public void testTransitionFilterAnimOverride() {
+        TransitionFilter filter = new TransitionFilter();
+        filter.mRequirements =
+                new TransitionFilter.Requirement[]{new TransitionFilter.Requirement()};
+        filter.mRequirements[0].mCustomAnimation = true;
+        filter.mRequirements[0].mModes = new int[]{TRANSIT_OPEN, TRANSIT_TO_FRONT};
+
+        final RunningTaskInfo taskInf = createTaskInfo(1);
+        final TransitionInfo openTask = new TransitionInfoBuilder(TRANSIT_OPEN)
+                .addChange(TRANSIT_OPEN, taskInf).build();
+        assertFalse(filter.matches(openTask));
+
+        final TransitionInfo.AnimationOptions overOpts =
+                TransitionInfo.AnimationOptions.makeCustomAnimOptions("pakname", 0, 0, 0, true);
+        final TransitionInfo openTaskOpts = new TransitionInfoBuilder(TRANSIT_OPEN)
+                .addChange(TRANSIT_OPEN, taskInf).build();
+        openTaskOpts.getChanges().get(0).setAnimationOptions(overOpts);
+        assertTrue(filter.matches(openTaskOpts));
+    }
+
+    @Test
     public void testRegisteredRemoteTransition() {
         Transitions transitions = createTestTransitions();
         transitions.replaceDefaultHandlerForTest(mDefaultHandler);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
index aeae0be..bbf42b5 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
@@ -16,19 +16,23 @@
 package com.android.wm.shell.windowdecor
 
 import android.app.ActivityManager.RunningTaskInfo
-import android.app.WindowConfiguration
 import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD
 import android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED
 import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
 import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN
+import android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW
+import android.app.WindowConfiguration.WINDOWING_MODE_PINNED
 import android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED
+import android.app.WindowConfiguration.WindowingMode
 import android.content.ComponentName
 import android.content.Context
+import android.content.Intent
 import android.content.pm.ActivityInfo
 import android.graphics.Rect
 import android.hardware.display.DisplayManager
 import android.hardware.display.VirtualDisplay
 import android.hardware.input.InputManager
+import android.net.Uri
 import android.os.Handler
 import android.platform.test.annotations.DisableFlags
 import android.platform.test.annotations.EnableFlags
@@ -37,6 +41,7 @@
 import android.platform.test.flag.junit.DeviceFlagsValueProvider
 import android.platform.test.flag.junit.SetFlagsRule
 import android.testing.AndroidTestingRunner
+import android.testing.TestableContext
 import android.testing.TestableLooper.RunWithLooper
 import android.util.SparseArray
 import android.view.Choreographer
@@ -47,11 +52,13 @@
 import android.view.InsetsSource
 import android.view.InsetsState
 import android.view.KeyEvent
+import android.view.Surface
 import android.view.SurfaceControl
 import android.view.SurfaceView
 import android.view.View
 import android.view.WindowInsets.Type.navigationBars
 import android.view.WindowInsets.Type.statusBars
+import android.window.WindowContainerTransaction
 import androidx.test.filters.SmallTest
 import com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn
 import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
@@ -64,11 +71,15 @@
 import com.android.wm.shell.ShellTestCase
 import com.android.wm.shell.TestRunningTaskInfoBuilder
 import com.android.wm.shell.TestShellExecutor
+import com.android.wm.shell.apptoweb.AppToWebGenericLinksParser
+import com.android.wm.shell.common.DisplayChangeController
 import com.android.wm.shell.common.DisplayController
 import com.android.wm.shell.common.DisplayInsetsController
 import com.android.wm.shell.common.DisplayLayout
+import com.android.wm.shell.common.MultiInstanceHelper
 import com.android.wm.shell.common.ShellExecutor
 import com.android.wm.shell.common.SyncTransactionQueue
+import com.android.wm.shell.common.desktopmode.DesktopModeTransitionSource
 import com.android.wm.shell.desktopmode.DesktopTasksController
 import com.android.wm.shell.desktopmode.DesktopTasksController.SnapPosition
 import com.android.wm.shell.freeform.FreeformTaskTransitionStarter
@@ -80,13 +91,17 @@
 import com.android.wm.shell.sysui.ShellInit
 import com.android.wm.shell.transition.Transitions
 import com.android.wm.shell.windowdecor.DesktopModeWindowDecorViewModel.DesktopModeOnInsetsChangedListener
-import com.android.wm.shell.windowdecor.common.OnTaskActionClickListener
+import java.util.Optional
+import java.util.function.Consumer
+import java.util.function.Supplier
+import org.junit.After
 import org.junit.Assert.assertEquals
 import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentCaptor.forClass
 import org.mockito.Mock
 import org.mockito.Mockito
 import org.mockito.Mockito.anyInt
@@ -95,13 +110,16 @@
 import org.mockito.Mockito.times
 import org.mockito.Mockito.verify
 import org.mockito.kotlin.any
+import org.mockito.kotlin.argThat
 import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.doNothing
 import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
 import org.mockito.kotlin.spy
 import org.mockito.kotlin.whenever
 import org.mockito.quality.Strictness
-import java.util.Optional
-import java.util.function.Supplier
+import junit.framework.Assert.assertFalse
+import junit.framework.Assert.assertTrue
 
 /**
  * Tests of [DesktopModeWindowDecorViewModel]
@@ -140,23 +158,38 @@
     @Mock private lateinit var mockShellCommandHandler: ShellCommandHandler
     @Mock private lateinit var mockWindowManager: IWindowManager
     @Mock private lateinit var mockInteractionJankMonitor: InteractionJankMonitor
+    @Mock private lateinit var mockGenericLinksParser: AppToWebGenericLinksParser
     private val bgExecutor = TestShellExecutor()
+    @Mock private lateinit var mockMultiInstanceHelper: MultiInstanceHelper
+    private lateinit var spyContext: TestableContext
 
     private val transactionFactory = Supplier<SurfaceControl.Transaction> {
         SurfaceControl.Transaction()
     }
     private val windowDecorByTaskIdSpy = spy(SparseArray<DesktopModeWindowDecoration>())
 
+    private lateinit var mockitoSession: StaticMockitoSession
     private lateinit var shellInit: ShellInit
     private lateinit var desktopModeOnInsetsChangedListener: DesktopModeOnInsetsChangedListener
+    private lateinit var displayChangingListener: DisplayChangeController.OnDisplayChangingListener
     private lateinit var desktopModeWindowDecorViewModel: DesktopModeWindowDecorViewModel
 
     @Before
     fun setUp() {
+        mockitoSession =
+            mockitoSession()
+                .strictness(Strictness.LENIENT)
+                .spyStatic(DesktopModeStatus::class.java)
+                .spyStatic(DragPositioningCallbackUtility::class.java)
+                .startMocking()
+        doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(Mockito.any()) }
+
+        spyContext = spy(mContext)
+        doNothing().`when`(spyContext).startActivity(any())
         shellInit = ShellInit(mockShellExecutor)
         windowDecorByTaskIdSpy.clear()
         desktopModeWindowDecorViewModel = DesktopModeWindowDecorViewModel(
-                mContext,
+                spyContext,
                 mockShellExecutor,
                 mockMainHandler,
                 mockMainChoreographer,
@@ -171,11 +204,14 @@
                 mockSyncQueue,
                 mockTransitions,
                 Optional.of(mockDesktopTasksController),
+                mockGenericLinksParser,
+                mockMultiInstanceHelper,
                 mockDesktopModeWindowDecorFactory,
                 mockInputMonitorFactory,
                 transactionFactory,
                 mockRootTaskDisplayAreaOrganizer,
-            windowDecorByTaskIdSpy, mockInteractionJankMonitor
+                windowDecorByTaskIdSpy,
+                mockInteractionJankMonitor
         )
         desktopModeWindowDecorViewModel.setSplitScreenController(mockSplitScreenController)
         whenever(mockDisplayController.getDisplayLayout(any())).thenReturn(mockDisplayLayout)
@@ -189,10 +225,22 @@
 
         shellInit.init()
 
-        val listenerCaptor =
-                argumentCaptor<DesktopModeWindowDecorViewModel.DesktopModeOnInsetsChangedListener>()
-        verify(displayInsetsController).addInsetsChangedListener(anyInt(), listenerCaptor.capture())
-        desktopModeOnInsetsChangedListener = listenerCaptor.firstValue
+        val insetListenerCaptor =
+            argumentCaptor<DesktopModeWindowDecorViewModel.DesktopModeOnInsetsChangedListener>()
+        verify(displayInsetsController)
+            .addInsetsChangedListener(anyInt(), insetListenerCaptor.capture())
+        desktopModeOnInsetsChangedListener = insetListenerCaptor.firstValue
+
+        val displayChangingListenerCaptor =
+            argumentCaptor<DisplayChangeController.OnDisplayChangingListener>()
+        verify(mockDisplayController)
+            .addDisplayChangingController(displayChangingListenerCaptor.capture())
+        displayChangingListener = displayChangingListenerCaptor.firstValue
+    }
+
+    @After
+    fun tearDown() {
+        mockitoSession.finishMocking()
     }
 
     @Test
@@ -202,24 +250,13 @@
         val decoration = setUpMockDecorationForTask(task)
 
         onTaskOpening(task, taskSurface)
+        assertTrue(windowDecorByTaskIdSpy.contains(task.taskId))
 
         task.setWindowingMode(WINDOWING_MODE_UNDEFINED)
         task.setActivityType(ACTIVITY_TYPE_UNDEFINED)
         onTaskChanging(task, taskSurface)
 
-        verify(mockDesktopModeWindowDecorFactory).create(
-                mContext,
-                mockDisplayController,
-                mockSplitScreenController,
-                mockTaskOrganizer,
-                task,
-                taskSurface,
-                mockMainHandler,
-                bgExecutor,
-                mockMainChoreographer,
-                mockSyncQueue,
-                mockRootTaskDisplayAreaOrganizer
-        )
+        assertFalse(windowDecorByTaskIdSpy.contains(task.taskId))
         verify(decoration).close()
     }
 
@@ -233,36 +270,12 @@
         setUpMockDecorationForTask(task)
 
         onTaskChanging(task, taskSurface)
-        verify(mockDesktopModeWindowDecorFactory, never()).create(
-                mContext,
-                mockDisplayController,
-                mockSplitScreenController,
-                mockTaskOrganizer,
-                task,
-                taskSurface,
-                mockMainHandler,
-                bgExecutor,
-                mockMainChoreographer,
-                mockSyncQueue,
-                mockRootTaskDisplayAreaOrganizer
-        )
+        assertFalse(windowDecorByTaskIdSpy.contains(task.taskId))
 
         task.setWindowingMode(WINDOWING_MODE_FREEFORM)
         task.setActivityType(ACTIVITY_TYPE_STANDARD)
         onTaskChanging(task, taskSurface)
-        verify(mockDesktopModeWindowDecorFactory, times(1)).create(
-                mContext,
-                mockDisplayController,
-                mockSplitScreenController,
-                mockTaskOrganizer,
-                task,
-                taskSurface,
-                mockMainHandler,
-                bgExecutor,
-                mockMainChoreographer,
-                mockSyncQueue,
-                mockRootTaskDisplayAreaOrganizer
-        )
+        assertTrue(windowDecorByTaskIdSpy.contains(task.taskId))
     }
 
     @Test
@@ -325,7 +338,7 @@
         whenever(view.id).thenReturn(R.id.back_button)
 
         val inputManager = mock(InputManager::class.java)
-        mContext.addMockSystemService(InputManager::class.java, inputManager)
+        spyContext.addMockSystemService(InputManager::class.java, inputManager)
 
         val freeformTaskTransitionStarter = mock(FreeformTaskTransitionStarter::class.java)
         desktopModeWindowDecorViewModel
@@ -357,34 +370,22 @@
         task.setWindowingMode(ACTIVITY_TYPE_UNDEFINED)
         onTaskChanging(task)
 
-        verify(mockDesktopModeWindowDecorFactory, never())
-                .create(any(), any(), any(), any(), eq(task), any(), any(), any(), any(), any(),
-                    any())
+        assertFalse(windowDecorByTaskIdSpy.contains(task.taskId))
     }
 
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
     fun testDecorationIsCreatedForTopTranslucentActivitiesWithStyleFloating() {
-        val mockitoSession: StaticMockitoSession = mockitoSession()
-                .strictness(Strictness.LENIENT)
-                .spyStatic(DesktopModeStatus::class.java)
-                .startMocking()
-        try {
-            val task = createTask(windowingMode = WINDOWING_MODE_FULLSCREEN, focused = true).apply {
-                isTopActivityTransparent = true
-                isTopActivityStyleFloating = true
-                numActivities = 1
-            }
-            doReturn(true).`when` { DesktopModeStatus.canEnterDesktopMode(any()) }
-            setUpMockDecorationsForTasks(task)
-
-            onTaskOpening(task)
-            verify(mockDesktopModeWindowDecorFactory)
-                    .create(any(), any(), any(), any(), eq(task), any(), any(), any(), any(),
-                        any(), any())
-        } finally {
-            mockitoSession.finishMocking()
+        val task = createTask(windowingMode = WINDOWING_MODE_FULLSCREEN, focused = true).apply {
+            isTopActivityTransparent = true
+            isTopActivityStyleFloating = true
+            numActivities = 1
         }
+        doReturn(true).`when` { DesktopModeStatus.canEnterDesktopMode(any()) }
+        setUpMockDecorationsForTasks(task)
+
+        onTaskOpening(task)
+        assertTrue(windowDecorByTaskIdSpy.contains(task.taskId))
     }
 
     @Test
@@ -397,9 +398,7 @@
         }
         onTaskOpening(task)
 
-        verify(mockDesktopModeWindowDecorFactory, never())
-                .create(any(), any(), any(), any(), eq(task), any(), any(), any(), any(), any(),
-                    any())
+        assertFalse(windowDecorByTaskIdSpy.contains(task.taskId))
     }
 
     @Test
@@ -415,9 +414,7 @@
 
         onTaskOpening(task)
 
-        verify(mockDesktopModeWindowDecorFactory, never())
-                .create(any(), any(), any(), any(), eq(task), any(), any(), any(), any(),
-                    any(), any())
+        assertFalse(windowDecorByTaskIdSpy.contains(task.taskId))
     }
 
     @Test
@@ -500,102 +497,70 @@
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
     fun testWindowDecor_desktopModeUnsupportedOnDevice_decorNotCreated() {
-        val mockitoSession: StaticMockitoSession = mockitoSession()
-            .strictness(Strictness.LENIENT)
-            .spyStatic(DesktopModeStatus::class.java)
-            .startMocking()
-        try {
-            // Simulate default enforce device restrictions system property
-            whenever(DesktopModeStatus.enforceDeviceRestrictions()).thenReturn(true)
+        // Simulate default enforce device restrictions system property
+        whenever(DesktopModeStatus.enforceDeviceRestrictions()).thenReturn(true)
 
-            val task = createTask(windowingMode = WINDOWING_MODE_FULLSCREEN, focused = true)
-            // Simulate device that doesn't support desktop mode
-            doReturn(false).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
+        val task = createTask(windowingMode = WINDOWING_MODE_FULLSCREEN, focused = true)
+        // Simulate device that doesn't support desktop mode
+        doReturn(false).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
 
-            onTaskOpening(task)
-            verify(mockDesktopModeWindowDecorFactory, never())
-                .create(any(), any(), any(), any(), eq(task), any(), any(), any(), any(), any(),
-                    any())
-        } finally {
-            mockitoSession.finishMocking()
-        }
+        onTaskOpening(task)
+        assertFalse(windowDecorByTaskIdSpy.contains(task.taskId))
     }
 
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
     fun testWindowDecor_desktopModeUnsupportedOnDevice_deviceRestrictionsOverridden_decorCreated() {
-        val mockitoSession: StaticMockitoSession = mockitoSession()
-            .strictness(Strictness.LENIENT)
-            .spyStatic(DesktopModeStatus::class.java)
-            .startMocking()
-        try {
-            // Simulate enforce device restrictions system property overridden to false
-            whenever(DesktopModeStatus.enforceDeviceRestrictions()).thenReturn(false)
-            // Simulate device that doesn't support desktop mode
-            doReturn(false).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
+        // Simulate enforce device restrictions system property overridden to false
+        whenever(DesktopModeStatus.enforceDeviceRestrictions()).thenReturn(false)
+        // Simulate device that doesn't support desktop mode
+        doReturn(false).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
 
-            val task = createTask(windowingMode = WINDOWING_MODE_FULLSCREEN, focused = true)
-            setUpMockDecorationsForTasks(task)
+        val task = createTask(windowingMode = WINDOWING_MODE_FULLSCREEN, focused = true)
+        setUpMockDecorationsForTasks(task)
 
-            onTaskOpening(task)
-            verify(mockDesktopModeWindowDecorFactory)
-                .create(any(), any(), any(), any(), eq(task), any(), any(), any(), any(), any(),
-                    any())
-        } finally {
-            mockitoSession.finishMocking()
-        }
+        onTaskOpening(task)
+        assertTrue(windowDecorByTaskIdSpy.contains(task.taskId))
     }
 
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
     fun testWindowDecor_deviceSupportsDesktopMode_decorCreated() {
-        val mockitoSession: StaticMockitoSession = mockitoSession()
-            .strictness(Strictness.LENIENT)
-            .spyStatic(DesktopModeStatus::class.java)
-            .startMocking()
-        try {
-            // Simulate default enforce device restrictions system property
-            whenever(DesktopModeStatus.enforceDeviceRestrictions()).thenReturn(true)
+        // Simulate default enforce device restrictions system property
+        whenever(DesktopModeStatus.enforceDeviceRestrictions()).thenReturn(true)
 
-            val task = createTask(windowingMode = WINDOWING_MODE_FULLSCREEN, focused = true)
-            doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
-            setUpMockDecorationsForTasks(task)
+        val task = createTask(windowingMode = WINDOWING_MODE_FULLSCREEN, focused = true)
+        doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
+        setUpMockDecorationsForTasks(task)
 
-            onTaskOpening(task)
-            verify(mockDesktopModeWindowDecorFactory)
-                .create(any(), any(), any(), any(), eq(task), any(), any(), any(), any(), any(),
-                any())
-        } finally {
-            mockitoSession.finishMocking()
-        }
+        onTaskOpening(task)
+        assertTrue(windowDecorByTaskIdSpy.contains(task.taskId))
     }
 
     @Test
     fun testOnDecorMaximizedOrRestored_togglesTaskSize() {
-        val decor = setUpMockDecorationForTask(createTask(windowingMode = WINDOWING_MODE_FREEFORM))
-        onTaskOpening(decor.mTaskInfo)
-        val maxOrRestoreListener = ArgumentCaptor.forClass(OnTaskActionClickListener::class.java)
-            .let { captor ->
-                verify(decor).setOnMaximizeOrRestoreClickListener(captor.capture())
-                return@let captor.value
-            }
+        val maxOrRestoreListenerCaptor = forClass(Function0::class.java)
+                as ArgumentCaptor<Function0<Unit>>
+        val decor = createOpenTaskDecoration(
+            windowingMode = WINDOWING_MODE_FREEFORM,
+            onMaxOrRestoreListenerCaptor = maxOrRestoreListenerCaptor
+        )
 
-        maxOrRestoreListener.onClick(decor.mTaskInfo.taskId, "test")
+        maxOrRestoreListenerCaptor.value.invoke()
 
         verify(mockDesktopTasksController).toggleDesktopTaskSize(decor.mTaskInfo)
     }
 
     @Test
     fun testOnDecorMaximizedOrRestored_closesMenus() {
-        val decor = setUpMockDecorationForTask(createTask(windowingMode = WINDOWING_MODE_FREEFORM))
-        onTaskOpening(decor.mTaskInfo)
-        val maxOrRestoreListener = ArgumentCaptor.forClass(OnTaskActionClickListener::class.java)
-            .let { captor ->
-                verify(decor).setOnMaximizeOrRestoreClickListener(captor.capture())
-                return@let captor.value
-            }
+        val maxOrRestoreListenerCaptor = forClass(Function0::class.java)
+                as ArgumentCaptor<Function0<Unit>>
+        val decor = createOpenTaskDecoration(
+            windowingMode = WINDOWING_MODE_FREEFORM,
+            onMaxOrRestoreListenerCaptor = maxOrRestoreListenerCaptor
+        )
 
-        maxOrRestoreListener.onClick(decor.mTaskInfo.taskId, "test")
+        maxOrRestoreListenerCaptor.value.invoke()
 
         verify(decor).closeHandleMenu()
         verify(decor).closeMaximizeMenu()
@@ -603,30 +568,30 @@
 
     @Test
     fun testOnDecorSnappedLeft_snapResizes() {
-        val decor = setUpMockDecorationForTask(createTask(windowingMode = WINDOWING_MODE_FREEFORM))
-        onTaskOpening(decor.mTaskInfo)
-        val snapLeftListener = ArgumentCaptor.forClass(OnTaskActionClickListener::class.java)
-            .let { captor ->
-                verify(decor).setOnLeftSnapClickListener(captor.capture())
-                return@let captor.value
-            }
+        val onLeftSnapClickListenerCaptor = forClass(Function0::class.java)
+                as ArgumentCaptor<Function0<Unit>>
+        val decor = createOpenTaskDecoration(
+            windowingMode = WINDOWING_MODE_FREEFORM,
+            onLeftSnapClickListenerCaptor = onLeftSnapClickListenerCaptor
+        )
 
-        snapLeftListener.onClick(decor.mTaskInfo.taskId, "test")
+        val currentBounds = decor.mTaskInfo.configuration.windowConfiguration.bounds
+        onLeftSnapClickListenerCaptor.value.invoke()
 
-        verify(mockDesktopTasksController).snapToHalfScreen(decor.mTaskInfo, SnapPosition.LEFT)
+        verify(mockDesktopTasksController)
+            .snapToHalfScreen(decor.mTaskInfo, currentBounds, SnapPosition.LEFT)
     }
 
     @Test
     fun testOnDecorSnappedLeft_closeMenus() {
-        val decor = setUpMockDecorationForTask(createTask(windowingMode = WINDOWING_MODE_FREEFORM))
-        onTaskOpening(decor.mTaskInfo)
-        val snapLeftListener = ArgumentCaptor.forClass(OnTaskActionClickListener::class.java)
-            .let { captor ->
-                verify(decor).setOnLeftSnapClickListener(captor.capture())
-                return@let captor.value
-            }
+        val onLeftSnapClickListenerCaptor = forClass(Function0::class.java)
+                as ArgumentCaptor<Function0<Unit>>
+        val decor = createOpenTaskDecoration(
+            windowingMode = WINDOWING_MODE_FREEFORM,
+            onLeftSnapClickListenerCaptor = onLeftSnapClickListenerCaptor
+        )
 
-        snapLeftListener.onClick(decor.mTaskInfo.taskId, "test")
+        onLeftSnapClickListenerCaptor.value.invoke()
 
         verify(decor).closeHandleMenu()
         verify(decor).closeMaximizeMenu()
@@ -634,35 +599,365 @@
 
     @Test
     fun testOnDecorSnappedRight_snapResizes() {
-        val decor = setUpMockDecorationForTask(createTask(windowingMode = WINDOWING_MODE_FREEFORM))
-        onTaskOpening(decor.mTaskInfo)
-        val snapLeftListener = ArgumentCaptor.forClass(OnTaskActionClickListener::class.java)
-            .let { captor ->
-                verify(decor).setOnRightSnapClickListener(captor.capture())
-                return@let captor.value
-            }
+        val onRightSnapClickListenerCaptor = forClass(Function0::class.java)
+                as ArgumentCaptor<Function0<Unit>>
+        val decor = createOpenTaskDecoration(
+            windowingMode = WINDOWING_MODE_FREEFORM,
+            onRightSnapClickListenerCaptor = onRightSnapClickListenerCaptor
+        )
 
-        snapLeftListener.onClick(decor.mTaskInfo.taskId, "test")
+        val currentBounds = decor.mTaskInfo.configuration.windowConfiguration.bounds
+        onRightSnapClickListenerCaptor.value.invoke()
 
-        verify(mockDesktopTasksController).snapToHalfScreen(decor.mTaskInfo, SnapPosition.RIGHT)
+        verify(mockDesktopTasksController)
+            .snapToHalfScreen(decor.mTaskInfo, currentBounds, SnapPosition.RIGHT)
     }
 
     @Test
     fun testOnDecorSnappedRight_closeMenus() {
-        val decor = setUpMockDecorationForTask(createTask(windowingMode = WINDOWING_MODE_FREEFORM))
-        onTaskOpening(decor.mTaskInfo)
-        val snapLeftListener = ArgumentCaptor.forClass(OnTaskActionClickListener::class.java)
-            .let { captor ->
-                verify(decor).setOnRightSnapClickListener(captor.capture())
-                return@let captor.value
-            }
+        val onRightSnapClickListenerCaptor = forClass(Function0::class.java)
+                as ArgumentCaptor<Function0<Unit>>
+        val decor = createOpenTaskDecoration(
+            windowingMode = WINDOWING_MODE_FREEFORM,
+            onRightSnapClickListenerCaptor = onRightSnapClickListenerCaptor
+        )
 
-        snapLeftListener.onClick(decor.mTaskInfo.taskId, "test")
+        onRightSnapClickListenerCaptor.value.invoke()
 
         verify(decor).closeHandleMenu()
         verify(decor).closeMaximizeMenu()
     }
 
+    @Test
+    fun testDecor_onClickToDesktop_movesToDesktopWithSource() {
+        val toDesktopListenerCaptor = forClass(Consumer::class.java)
+                as ArgumentCaptor<Consumer<DesktopModeTransitionSource>>
+        val decor = createOpenTaskDecoration(
+            windowingMode = WINDOWING_MODE_FULLSCREEN,
+            onToDesktopClickListenerCaptor = toDesktopListenerCaptor
+        )
+
+        toDesktopListenerCaptor.value.accept(DesktopModeTransitionSource.APP_HANDLE_MENU_BUTTON)
+
+        verify(mockDesktopTasksController).moveTaskToDesktop(
+            eq(decor.mTaskInfo.taskId),
+            any(),
+            eq(DesktopModeTransitionSource.APP_HANDLE_MENU_BUTTON)
+        )
+    }
+
+    @Test
+    fun testDecor_onClickToDesktop_addsCaptionInsets() {
+        val toDesktopListenerCaptor = forClass(Consumer::class.java)
+                as ArgumentCaptor<Consumer<DesktopModeTransitionSource>>
+        val decor = createOpenTaskDecoration(
+            windowingMode = WINDOWING_MODE_FULLSCREEN,
+            onToDesktopClickListenerCaptor = toDesktopListenerCaptor
+        )
+
+        toDesktopListenerCaptor.value.accept(DesktopModeTransitionSource.APP_HANDLE_MENU_BUTTON)
+
+        verify(decor).addCaptionInset(any())
+    }
+
+    @Test
+    fun testDecor_onClickToDesktop_closesHandleMenu() {
+        val toDesktopListenerCaptor = forClass(Consumer::class.java)
+                    as ArgumentCaptor<Consumer<DesktopModeTransitionSource>>
+        val decor = createOpenTaskDecoration(
+            windowingMode = WINDOWING_MODE_FULLSCREEN,
+            onToDesktopClickListenerCaptor = toDesktopListenerCaptor
+        )
+
+        toDesktopListenerCaptor.value.accept(DesktopModeTransitionSource.APP_HANDLE_MENU_BUTTON)
+
+        verify(decor).closeHandleMenu()
+    }
+
+    @Test
+    fun testDecor_onClickToFullscreen_closesHandleMenu() {
+        val toFullscreenListenerCaptor = forClass(Function0::class.java)
+                as ArgumentCaptor<Function0<Unit>>
+        val decor = createOpenTaskDecoration(
+            windowingMode = WINDOWING_MODE_FREEFORM,
+            onToFullscreenClickListenerCaptor = toFullscreenListenerCaptor
+        )
+
+        toFullscreenListenerCaptor.value.invoke()
+
+        verify(decor).closeHandleMenu()
+    }
+
+    @Test
+    fun testDecor_onClickToFullscreen_isFreeform_movesToFullscreen() {
+        val toFullscreenListenerCaptor = forClass(Function0::class.java)
+                as ArgumentCaptor<Function0<Unit>>
+        val decor = createOpenTaskDecoration(
+            windowingMode = WINDOWING_MODE_FREEFORM,
+            onToFullscreenClickListenerCaptor = toFullscreenListenerCaptor
+        )
+
+        toFullscreenListenerCaptor.value.invoke()
+
+        verify(mockDesktopTasksController).moveToFullscreen(
+            decor.mTaskInfo.taskId,
+            DesktopModeTransitionSource.APP_HANDLE_MENU_BUTTON
+        )
+    }
+
+    @Test
+    fun testDecor_onClickToFullscreen_isSplit_movesToFullscreen() {
+        val toFullscreenListenerCaptor = forClass(Function0::class.java)
+                as ArgumentCaptor<Function0<Unit>>
+        val decor = createOpenTaskDecoration(
+            windowingMode = WINDOWING_MODE_MULTI_WINDOW,
+            onToFullscreenClickListenerCaptor = toFullscreenListenerCaptor
+        )
+
+        toFullscreenListenerCaptor.value.invoke()
+
+        verify(mockSplitScreenController).moveTaskToFullscreen(
+            decor.mTaskInfo.taskId,
+            SplitScreenController.EXIT_REASON_DESKTOP_MODE
+        )
+    }
+
+    @Test
+    fun testDecor_onClickToSplitScreen_closesHandleMenu() {
+        val toSplitScreenListenerCaptor = forClass(Function0::class.java)
+                as ArgumentCaptor<Function0<Unit>>
+        val decor = createOpenTaskDecoration(
+            windowingMode = WINDOWING_MODE_MULTI_WINDOW,
+            onToSplitScreenClickListenerCaptor = toSplitScreenListenerCaptor
+        )
+
+        toSplitScreenListenerCaptor.value.invoke()
+
+        verify(decor).closeHandleMenu()
+    }
+
+    @Test
+    fun testDecor_onClickToSplitScreen_requestsSplit() {
+        val toSplitScreenListenerCaptor = forClass(Function0::class.java)
+                as ArgumentCaptor<Function0<Unit>>
+        val decor = createOpenTaskDecoration(
+            windowingMode = WINDOWING_MODE_MULTI_WINDOW,
+            onToSplitScreenClickListenerCaptor = toSplitScreenListenerCaptor
+        )
+
+        toSplitScreenListenerCaptor.value.invoke()
+
+        verify(mockDesktopTasksController).requestSplit(decor.mTaskInfo, leftOrTop = false)
+    }
+
+    @Test
+    fun testDecor_onClickToSplitScreen_disposesStatusBarInputLayer() {
+        val toSplitScreenListenerCaptor = forClass(Function0::class.java)
+                as ArgumentCaptor<Function0<Unit>>
+        val decor = createOpenTaskDecoration(
+            windowingMode = WINDOWING_MODE_MULTI_WINDOW,
+            onToSplitScreenClickListenerCaptor = toSplitScreenListenerCaptor
+        )
+
+        toSplitScreenListenerCaptor.value.invoke()
+
+        verify(decor).disposeStatusBarInputLayer()
+    }
+
+    @Test
+    fun testDecor_onClickToOpenBrowser_closeMenus() {
+        val openInBrowserListenerCaptor = forClass(Consumer::class.java)
+                as ArgumentCaptor<Consumer<Uri>>
+        val decor = createOpenTaskDecoration(
+            windowingMode = WINDOWING_MODE_FULLSCREEN,
+            onOpenInBrowserClickListener = openInBrowserListenerCaptor
+        )
+
+        openInBrowserListenerCaptor.value.accept(Uri.EMPTY)
+
+        verify(decor).closeHandleMenu()
+        verify(decor).closeMaximizeMenu()
+    }
+
+    @Test
+    fun testDecor_onClickToOpenBrowser_opensBrowser() {
+        doNothing().whenever(spyContext).startActivity(any())
+        val uri = Uri.parse("https://www.google.com")
+        val openInBrowserListenerCaptor = forClass(Consumer::class.java)
+                as ArgumentCaptor<Consumer<Uri>>
+        createOpenTaskDecoration(
+            windowingMode = WINDOWING_MODE_FULLSCREEN,
+            onOpenInBrowserClickListener = openInBrowserListenerCaptor
+        )
+
+        openInBrowserListenerCaptor.value.accept(uri)
+
+        verify(spyContext).startActivity(argThat { intent ->
+            intent.data == uri
+                    && ((intent.flags and Intent.FLAG_ACTIVITY_NEW_TASK) != 0)
+                    && intent.categories.contains(Intent.CATEGORY_LAUNCHER)
+                    && intent.action == Intent.ACTION_MAIN
+        })
+    }
+
+    @Test
+    fun testOnDisplayRotation_tasksOutOfValidArea_taskBoundsUpdated() {
+        val task = createTask(focused = true, windowingMode = WINDOWING_MODE_FREEFORM)
+        val secondTask =
+            createTask(displayId = task.displayId, windowingMode = WINDOWING_MODE_FREEFORM)
+        val thirdTask =
+            createTask(displayId = task.displayId, windowingMode = WINDOWING_MODE_FREEFORM)
+
+        doReturn(true).`when` {
+            DragPositioningCallbackUtility.snapTaskBoundsIfNecessary(any(), any())
+        }
+        setUpMockDecorationsForTasks(task, secondTask, thirdTask)
+
+        onTaskOpening(task)
+        onTaskOpening(secondTask)
+        onTaskOpening(thirdTask)
+
+        val wct = mock<WindowContainerTransaction>()
+
+        displayChangingListener.onDisplayChange(
+            task.displayId, Surface.ROTATION_0, Surface.ROTATION_90, null, wct
+        )
+
+        verify(wct).setBounds(eq(task.token), any())
+        verify(wct).setBounds(eq(secondTask.token), any())
+        verify(wct).setBounds(eq(thirdTask.token), any())
+    }
+
+    @Test
+    fun testOnDisplayRotation_taskInValidArea_taskBoundsNotUpdated() {
+        val task = createTask(focused = true, windowingMode = WINDOWING_MODE_FREEFORM)
+        val secondTask =
+            createTask(displayId = task.displayId, windowingMode = WINDOWING_MODE_FREEFORM)
+        val thirdTask =
+            createTask(displayId = task.displayId, windowingMode = WINDOWING_MODE_FREEFORM)
+
+        doReturn(false).`when` {
+            DragPositioningCallbackUtility.snapTaskBoundsIfNecessary(any(), any())
+        }
+        setUpMockDecorationsForTasks(task, secondTask, thirdTask)
+
+        onTaskOpening(task)
+        onTaskOpening(secondTask)
+        onTaskOpening(thirdTask)
+
+        val wct = mock<WindowContainerTransaction>()
+        displayChangingListener.onDisplayChange(
+            task.displayId, Surface.ROTATION_0, Surface.ROTATION_90, null, wct
+        )
+
+        verify(wct, never()).setBounds(eq(task.token), any())
+        verify(wct, never()).setBounds(eq(secondTask.token), any())
+        verify(wct, never()).setBounds(eq(thirdTask.token), any())
+    }
+
+    @Test
+    fun testOnDisplayRotation_sameOrientationRotation_taskBoundsNotUpdated() {
+        val task = createTask(focused = true, windowingMode = WINDOWING_MODE_FREEFORM)
+        val secondTask =
+            createTask(displayId = task.displayId, windowingMode = WINDOWING_MODE_FREEFORM)
+        val thirdTask =
+            createTask(displayId = task.displayId, windowingMode = WINDOWING_MODE_FREEFORM)
+
+        setUpMockDecorationsForTasks(task, secondTask, thirdTask)
+
+        onTaskOpening(task)
+        onTaskOpening(secondTask)
+        onTaskOpening(thirdTask)
+
+        val wct = mock<WindowContainerTransaction>()
+        displayChangingListener.onDisplayChange(
+            task.displayId, Surface.ROTATION_0, Surface.ROTATION_180, null, wct
+        )
+
+        verify(wct, never()).setBounds(eq(task.token), any())
+        verify(wct, never()).setBounds(eq(secondTask.token), any())
+        verify(wct, never()).setBounds(eq(thirdTask.token), any())
+    }
+
+    @Test
+    fun testOnDisplayRotation_differentDisplayId_taskBoundsNotUpdated() {
+        val task = createTask(focused = true, windowingMode = WINDOWING_MODE_FREEFORM)
+        val secondTask = createTask(displayId = -2, windowingMode = WINDOWING_MODE_FREEFORM)
+        val thirdTask = createTask(displayId = -3, windowingMode = WINDOWING_MODE_FREEFORM)
+
+        doReturn(true).`when` {
+            DragPositioningCallbackUtility.snapTaskBoundsIfNecessary(any(), any())
+        }
+        setUpMockDecorationsForTasks(task, secondTask, thirdTask)
+
+        onTaskOpening(task)
+        onTaskOpening(secondTask)
+        onTaskOpening(thirdTask)
+
+        val wct = mock<WindowContainerTransaction>()
+        displayChangingListener.onDisplayChange(
+            task.displayId, Surface.ROTATION_0, Surface.ROTATION_90, null, wct
+        )
+
+        verify(wct).setBounds(eq(task.token), any())
+        verify(wct, never()).setBounds(eq(secondTask.token), any())
+        verify(wct, never()).setBounds(eq(thirdTask.token), any())
+    }
+
+    @Test
+    fun testOnDisplayRotation_nonFreeformTask_taskBoundsNotUpdated() {
+        val task = createTask(focused = true, windowingMode = WINDOWING_MODE_FREEFORM)
+        val secondTask = createTask(displayId = -2, windowingMode = WINDOWING_MODE_FULLSCREEN)
+        val thirdTask = createTask(displayId = -3, windowingMode = WINDOWING_MODE_PINNED)
+
+        doReturn(true).`when` {
+            DragPositioningCallbackUtility.snapTaskBoundsIfNecessary(any(), any())
+        }
+        setUpMockDecorationsForTasks(task, secondTask, thirdTask)
+
+        onTaskOpening(task)
+        onTaskOpening(secondTask)
+        onTaskOpening(thirdTask)
+
+        val wct = mock<WindowContainerTransaction>()
+        displayChangingListener.onDisplayChange(
+            task.displayId, Surface.ROTATION_0, Surface.ROTATION_90, null, wct
+        )
+
+        verify(wct).setBounds(eq(task.token), any())
+        verify(wct, never()).setBounds(eq(secondTask.token), any())
+        verify(wct, never()).setBounds(eq(thirdTask.token), any())
+    }
+
+    private fun createOpenTaskDecoration(
+        @WindowingMode windowingMode: Int,
+        onMaxOrRestoreListenerCaptor: ArgumentCaptor<Function0<Unit>> =
+            forClass(Function0::class.java) as ArgumentCaptor<Function0<Unit>>,
+        onLeftSnapClickListenerCaptor: ArgumentCaptor<Function0<Unit>> =
+            forClass(Function0::class.java) as ArgumentCaptor<Function0<Unit>>,
+        onRightSnapClickListenerCaptor: ArgumentCaptor<Function0<Unit>> =
+            forClass(Function0::class.java) as ArgumentCaptor<Function0<Unit>>,
+        onToDesktopClickListenerCaptor: ArgumentCaptor<Consumer<DesktopModeTransitionSource>> =
+            forClass(Consumer::class.java) as ArgumentCaptor<Consumer<DesktopModeTransitionSource>>,
+        onToFullscreenClickListenerCaptor: ArgumentCaptor<Function0<Unit>> =
+            forClass(Function0::class.java) as ArgumentCaptor<Function0<Unit>>,
+        onToSplitScreenClickListenerCaptor: ArgumentCaptor<Function0<Unit>> =
+            forClass(Function0::class.java) as ArgumentCaptor<Function0<Unit>>,
+        onOpenInBrowserClickListener: ArgumentCaptor<Consumer<Uri>> =
+            forClass(Consumer::class.java) as ArgumentCaptor<Consumer<Uri>>,
+    ): DesktopModeWindowDecoration {
+        val decor = setUpMockDecorationForTask(createTask(windowingMode = windowingMode))
+        onTaskOpening(decor.mTaskInfo)
+        verify(decor).setOnMaximizeOrRestoreClickListener(onMaxOrRestoreListenerCaptor.capture())
+        verify(decor).setOnLeftSnapClickListener(onLeftSnapClickListenerCaptor.capture())
+        verify(decor).setOnRightSnapClickListener(onRightSnapClickListenerCaptor.capture())
+        verify(decor).setOnToDesktopClickListener(onToDesktopClickListenerCaptor.capture())
+        verify(decor).setOnToFullscreenClickListener(onToFullscreenClickListenerCaptor.capture())
+        verify(decor).setOnToSplitScreenClickListener(onToSplitScreenClickListenerCaptor.capture())
+        verify(decor).setOpenInBrowserClickListener(onOpenInBrowserClickListener.capture())
+        return decor
+    }
+
     private fun onTaskOpening(task: RunningTaskInfo, leash: SurfaceControl = SurfaceControl()) {
         desktopModeWindowDecorViewModel.onTaskOpening(
                 task,
@@ -683,10 +978,10 @@
 
     private fun createTask(
             displayId: Int = DEFAULT_DISPLAY,
-            @WindowConfiguration.WindowingMode windowingMode: Int,
+            @WindowingMode windowingMode: Int,
             activityType: Int = ACTIVITY_TYPE_STANDARD,
             focused: Boolean = true,
-            activityInfo: ActivityInfo = ActivityInfo()
+            activityInfo: ActivityInfo = ActivityInfo(),
     ): RunningTaskInfo {
         return TestRunningTaskInfoBuilder()
                 .setDisplayId(displayId)
@@ -703,10 +998,16 @@
         val decoration = mock(DesktopModeWindowDecoration::class.java)
         whenever(
             mockDesktopModeWindowDecorFactory.create(
-                any(), any(), any(), any(), eq(task), any(), any(), any(), any(), any(), any())
+                any(), any(), any(), any(), any(), eq(task), any(), any(), any(), any(), any(),
+                any(), any(), any())
         ).thenReturn(decoration)
         decoration.mTaskInfo = task
         whenever(decoration.isFocused).thenReturn(task.isFocused)
+        if (task.windowingMode == WINDOWING_MODE_MULTI_WINDOW) {
+            whenever(mockSplitScreenController.isTaskInSplitScreen(task.taskId))
+                .thenReturn(true)
+        }
+        whenever(decoration.calculateValidDragArea()).thenReturn(Rect(0, 60, 2560, 1600))
         return decoration
     }
 
@@ -727,7 +1028,7 @@
         )
     }
 
-    private fun RunningTaskInfo.setWindowingMode(@WindowConfiguration.WindowingMode mode: Int) {
+    private fun RunningTaskInfo.setWindowingMode(@WindowingMode mode: Int) {
         configuration.windowConfiguration.windowingMode = mode
     }
 
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 412fef3..596adfb 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,6 +20,8 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
 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;
 import static android.view.WindowInsetsController.APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
@@ -31,6 +33,7 @@
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertTrue;
 
+import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.any;
@@ -59,6 +62,7 @@
 import android.platform.test.flag.junit.SetFlagsRule;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableContext;
+import android.testing.TestableLooper;
 import android.view.AttachedSurfaceControl;
 import android.view.Choreographer;
 import android.view.Display;
@@ -83,15 +87,17 @@
 import com.android.wm.shell.ShellTestCase;
 import com.android.wm.shell.TestRunningTaskInfoBuilder;
 import com.android.wm.shell.TestShellExecutor;
+import com.android.wm.shell.apptoweb.AppToWebGenericLinksParser;
 import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.MultiInstanceHelper;
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.SyncTransactionQueue;
 import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
 import com.android.wm.shell.splitscreen.SplitScreenController;
 import com.android.wm.shell.windowdecor.WindowDecoration.RelayoutParams;
-import com.android.wm.shell.windowdecor.common.OnTaskActionClickListener;
 
 import kotlin.Unit;
+import kotlin.jvm.functions.Function0;
 import kotlin.jvm.functions.Function1;
 
 import org.junit.After;
@@ -105,6 +111,7 @@
 import org.mockito.Mock;
 import org.mockito.quality.Strictness;
 
+import java.util.function.Consumer;
 import java.util.function.Supplier;
 
 /**
@@ -114,6 +121,7 @@
  * atest WMShellUnitTests:DesktopModeWindowDecorationTests
  */
 @SmallTest
+@TestableLooper.RunWithLooper
 @RunWith(AndroidTestingRunner.class)
 public class DesktopModeWindowDecorationTests extends ShellTestCase {
     private static final String USE_WINDOW_SHADOWS_SYSPROP_KEY =
@@ -123,7 +131,8 @@
     private static final String USE_ROUNDED_CORNERS_SYSPROP_KEY =
             "persist.wm.debug.desktop_use_rounded_corners";
 
-    private static final Uri TEST_URI = Uri.parse("www.google.com");
+    private static final Uri TEST_URI1 = Uri.parse("https://www.google.com/");
+    private static final Uri TEST_URI2 = Uri.parse("https://docs.google.com/");
 
     @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT);
 
@@ -160,7 +169,14 @@
     @Mock
     private Handler mMockHandler;
     @Mock
-    private DesktopModeWindowDecoration.OpenInBrowserClickListener mMockOpenInBrowserClickListener;
+    private Consumer<Uri> mMockOpenInBrowserClickListener;
+    @Mock
+    private AppToWebGenericLinksParser mMockGenericLinksParser;
+    @Mock
+    private HandleMenu mMockHandleMenu;
+    @Mock
+    private HandleMenuFactory mMockHandleMenuFactory;
+    private MultiInstanceHelper mMockMultiInstanceHelper;
     @Captor
     private ArgumentCaptor<Function1<Boolean, Unit>> mOnMaxMenuHoverChangeListener;
     @Captor
@@ -204,6 +220,9 @@
         final Display defaultDisplay = mock(Display.class);
         doReturn(defaultDisplay).when(mMockDisplayController).getDisplay(Display.DEFAULT_DISPLAY);
         doReturn(mInsetsState).when(mMockDisplayController).getInsetsState(anyInt());
+        when(mMockHandleMenuFactory.create(any(), anyInt(), any(), any(),
+                any(), anyBoolean(), anyBoolean(), any(), anyInt(), anyInt(), anyInt()))
+                .thenReturn(mMockHandleMenu);
     }
 
     @After
@@ -398,6 +417,80 @@
     }
 
     @Test
+    @EnableFlags(Flags.FLAG_ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION)
+    public void updateRelayoutParams_defaultHeader_addsForceConsumingFlag() {
+        final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
+        taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
+        taskInfo.taskDescription.setTopOpaqueSystemBarsAppearance(0);
+        final RelayoutParams relayoutParams = new RelayoutParams();
+
+        DesktopModeWindowDecoration.updateRelayoutParams(
+                relayoutParams,
+                mTestableContext,
+                taskInfo,
+                /* applyStartTransactionOnDraw= */ true,
+                /* shouldSetTaskPositionAndCrop */ false);
+
+        assertThat((relayoutParams.mInsetSourceFlags & FLAG_FORCE_CONSUMING) != 0).isTrue();
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION)
+    public void updateRelayoutParams_customHeader_noForceConsumptionFlag() {
+        final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
+        taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
+        taskInfo.taskDescription.setTopOpaqueSystemBarsAppearance(
+                APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND);
+        final RelayoutParams relayoutParams = new RelayoutParams();
+
+        DesktopModeWindowDecoration.updateRelayoutParams(
+                relayoutParams,
+                mTestableContext,
+                taskInfo,
+                /* applyStartTransactionOnDraw= */ true,
+                /* shouldSetTaskPositionAndCrop */ false);
+
+        assertThat((relayoutParams.mInsetSourceFlags & FLAG_FORCE_CONSUMING) == 0).isTrue();
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION_ALWAYS)
+    public void updateRelayoutParams_header_addsForceConsumingCaptionBar() {
+        final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
+        taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
+        final RelayoutParams relayoutParams = new RelayoutParams();
+
+        DesktopModeWindowDecoration.updateRelayoutParams(
+                relayoutParams,
+                mTestableContext,
+                taskInfo,
+                /* applyStartTransactionOnDraw= */ true,
+                /* shouldSetTaskPositionAndCrop */ false);
+
+        assertThat(
+                (relayoutParams.mInsetSourceFlags & FLAG_FORCE_CONSUMING_OPAQUE_CAPTION_BAR) != 0)
+                .isTrue();
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION_ALWAYS)
+    public void updateRelayoutParams_handle_skipsForceConsumingCaptionBar() {
+        final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
+        taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+        final RelayoutParams relayoutParams = new RelayoutParams();
+
+        DesktopModeWindowDecoration.updateRelayoutParams(
+                relayoutParams,
+                mTestableContext,
+                taskInfo,
+                /* applyStartTransactionOnDraw= */ true,
+                /* shouldSetTaskPositionAndCrop */ false);
+
+        assertThat(
+                (relayoutParams.mInsetSourceFlags & FLAG_FORCE_CONSUMING_OPAQUE_CAPTION_BAR) == 0)
+                .isTrue();
+    }
+
     @DisableFlags(Flags.FLAG_ENABLE_ADDITIONAL_WINDOWS_ABOVE_STATUS_BAR)
     public void relayout_fullscreenTask_appliesTransactionImmediately() {
         final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
@@ -572,72 +665,178 @@
         verify(mMockHandler).removeCallbacks(any());
     }
 
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_TO_WEB)
+    public void capturedLink_handleMenuBrowserLinkSetToCapturedLinkIfValid() {
+        final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(true /* visible */);
+        final DesktopModeWindowDecoration decor = createWindowDecoration(
+                taskInfo, TEST_URI1 /* captured link */, TEST_URI2 /* generic link */);
+
+        // Verify handle menu's browser link set as captured link
+        decor.createHandleMenu(mMockSplitScreenController);
+        verifyHandleMenuCreated(TEST_URI1);
+    }
+
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_TO_WEB)
     public void capturedLink_postsOnCapturedLinkExpiredRunnable() {
         final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(true /* visible */);
+        final DesktopModeWindowDecoration decor = createWindowDecoration(
+                taskInfo, TEST_URI1 /* captured link */, null /* generic link */);
         final ArgumentCaptor<Runnable> runnableArgument = ArgumentCaptor.forClass(Runnable.class);
-        final DesktopModeWindowDecoration decor = createWindowDecoration(taskInfo);
 
-        decor.relayout(taskInfo);
-        // Assert captured link is set
-        assertTrue(decor.browserLinkAvailable());
-        // Asset runnable posted to set captured link to expired
+        // Run runnable to set captured link to expired
         verify(mMockHandler).postDelayed(runnableArgument.capture(), anyLong());
         runnableArgument.getValue().run();
-        assertFalse(decor.browserLinkAvailable());
+
+        // Verify captured link is no longer valid by verifying link is not set as handle menu
+        // browser link.
+        decor.createHandleMenu(mMockSplitScreenController);
+        verifyHandleMenuCreated(null /* uri */);
     }
 
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_TO_WEB)
     public void capturedLink_capturedLinkNotResetToSameLink() {
         final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(true /* visible */);
-        final DesktopModeWindowDecoration decor = createWindowDecoration(taskInfo);
+        final DesktopModeWindowDecoration decor = createWindowDecoration(
+                taskInfo, TEST_URI1 /* captured link */, null /* generic link */);
         final ArgumentCaptor<Runnable> runnableArgument = ArgumentCaptor.forClass(Runnable.class);
 
-        // Set captured link and run on captured link expired runnable
-        decor.relayout(taskInfo);
+        // Run runnable to set captured link to expired
         verify(mMockHandler).postDelayed(runnableArgument.capture(), anyLong());
         runnableArgument.getValue().run();
 
+        // Relayout decor with same captured link
         decor.relayout(taskInfo);
-        // Assert captured link not set to same value twice
-        assertFalse(decor.browserLinkAvailable());
+
+        // Verify handle menu's browser link not set to captured link since link is expired
+        decor.createHandleMenu(mMockSplitScreenController);
+        verifyHandleMenuCreated(null /* uri */);
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_TO_WEB)
+    public void capturedLink_capturedLinkStillUsedIfExpiredAfterHandleMenuCreation() {
+        final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(true /* visible */);
+        final DesktopModeWindowDecoration decor = createWindowDecoration(
+                taskInfo, TEST_URI1 /* captured link */, null /* generic link */);
+        final ArgumentCaptor<Runnable> runnableArgument = ArgumentCaptor.forClass(Runnable.class);
+
+        // Create handle menu before link expires
+        decor.createHandleMenu(mMockSplitScreenController);
+
+        // Run runnable to set captured link to expired
+        verify(mMockHandler).postDelayed(runnableArgument.capture(), anyLong());
+        runnableArgument.getValue().run();
+
+        // Verify handle menu's browser link is set to captured link since menu was opened before
+        // captured link expired
+        decor.createHandleMenu(mMockSplitScreenController);
+        verifyHandleMenuCreated(TEST_URI1);
     }
 
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_TO_WEB)
     public void capturedLink_capturedLinkExpiresAfterClick() {
         final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(true /* visible */);
-        final DesktopModeWindowDecoration decor = createWindowDecoration(taskInfo);
+        final DesktopModeWindowDecoration decor = createWindowDecoration(
+                taskInfo, TEST_URI1 /* captured link */, null /* generic link */);
+        final ArgumentCaptor<Function1<Uri, Unit>> openInBrowserCaptor =
+                ArgumentCaptor.forClass(Function1.class);
 
-        decor.relayout(taskInfo);
-        // Assert captured link is set
-        assertTrue(decor.browserLinkAvailable());
-        decor.onOpenInBrowserClick();
-        //Assert Captured link expires after button is clicked
-        assertFalse(decor.browserLinkAvailable());
+        // Simulate menu opening and clicking open in browser button
+        decor.createHandleMenu(mMockSplitScreenController);
+        verify(mMockHandleMenu).show(
+                any(),
+                any(),
+                any(),
+                any(),
+                openInBrowserCaptor.capture(),
+                any(),
+                any()
+        );
+        openInBrowserCaptor.getValue().invoke(TEST_URI1);
+
+        // Verify handle menu's browser link not set to captured link since link not valid after
+        // open in browser clicked
+        decor.createHandleMenu(mMockSplitScreenController);
+        verifyHandleMenuCreated(null /* uri */);
     }
 
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_TO_WEB)
     public void capturedLink_openInBrowserListenerCalledOnClick() {
         final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(true /* visible */);
-        final DesktopModeWindowDecoration decor = createWindowDecoration(taskInfo);
+        final DesktopModeWindowDecoration decor = createWindowDecoration(
+                taskInfo, TEST_URI1 /* captured link */, null /* generic link */);
+        final ArgumentCaptor<Function1<Uri, Unit>> openInBrowserCaptor =
+                ArgumentCaptor.forClass(Function1.class);
+        decor.createHandleMenu(mMockSplitScreenController);
+        verify(mMockHandleMenu).show(
+                any(),
+                any(),
+                any(),
+                any(),
+                openInBrowserCaptor.capture(),
+                any(),
+                any()
+        );
 
-        decor.relayout(taskInfo);
-        decor.onOpenInBrowserClick();
+        openInBrowserCaptor.getValue().invoke(TEST_URI1);
 
-        verify(mMockOpenInBrowserClickListener).onClick(any(), any());
+        verify(mMockOpenInBrowserClickListener).accept(TEST_URI1);
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_TO_WEB)
+    public void genericLink_genericLinkUsedWhenCapturedLinkUnavailable() {
+        final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(true /* visible */);
+        final DesktopModeWindowDecoration decor = createWindowDecoration(
+                taskInfo, null /* captured link */, TEST_URI2 /* generic link */);
+
+        // Verify handle menu's browser link set as generic link no captured link is available
+        decor.createHandleMenu(mMockSplitScreenController);
+        verifyHandleMenuCreated(TEST_URI2);
+    }
+
+    @Test
+    public void handleMenu_onCloseMenuClick_closesMenu() {
+        final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
+        final DesktopModeWindowDecoration decoration = createWindowDecoration(taskInfo,
+                true /* relayout */);
+        final ArgumentCaptor<Function0<Unit>> closeClickListener =
+                ArgumentCaptor.forClass(Function0.class);
+        decoration.createHandleMenu(mMockSplitScreenController);
+        verify(mMockHandleMenu).show(
+                any(),
+                any(),
+                any(),
+                any(),
+                any(),
+                closeClickListener.capture(),
+                any()
+        );
+
+        closeClickListener.getValue().invoke();
+
+        verify(mMockHandleMenu).close();
+        assertFalse(decoration.isHandleMenuActive());
+    }
+
+    private void verifyHandleMenuCreated(@Nullable Uri uri) {
+        verify(mMockHandleMenuFactory).create(any(), anyInt(), any(), any(),
+                any(), anyBoolean(), anyBoolean(), eq(uri), anyInt(), anyInt(), anyInt());
     }
 
     private void createMaximizeMenu(DesktopModeWindowDecoration decoration, MaximizeMenu menu) {
-        final OnTaskActionClickListener l = (taskId, tag) -> {};
+        final Function0<Unit> l = () -> Unit.INSTANCE;
         decoration.setOnMaximizeOrRestoreClickListener(l);
         decoration.setOnLeftSnapClickListener(l);
         decoration.setOnRightSnapClickListener(l);
         decoration.createMaximizeMenu();
-        verify(menu).show(any(), any(), any(), mOnMaxMenuHoverChangeListener.capture());
+        verify(menu).show(any(), any(), any(), mOnMaxMenuHoverChangeListener.capture(), any());
     }
 
     private void fillRoundedCornersResources(int fillValue) {
@@ -657,26 +856,54 @@
                 R.dimen.rounded_corner_radius_bottom, fillValue);
     }
 
+    private DesktopModeWindowDecoration createWindowDecoration(
+            ActivityManager.RunningTaskInfo taskInfo, @Nullable Uri capturedLink,
+            @Nullable Uri genericLink) {
+        taskInfo.capturedLink = capturedLink;
+        taskInfo.capturedLinkTimestamp = System.currentTimeMillis();
+        final String genericLinkString = genericLink == null ? null : genericLink.toString();
+        doReturn(genericLinkString).when(mMockGenericLinksParser).getGenericLink(any());
+        // Relayout to set captured link
+        return createWindowDecoration(taskInfo, new FakeMaximizeMenuFactory(), true /* relayout */);
+    }
 
     private DesktopModeWindowDecoration createWindowDecoration(
             ActivityManager.RunningTaskInfo taskInfo) {
-        return createWindowDecoration(taskInfo, new FakeMaximizeMenuFactory());
+        return createWindowDecoration(taskInfo, new FakeMaximizeMenuFactory(),
+                false /* relayout */);
+    }
+
+    private DesktopModeWindowDecoration createWindowDecoration(
+            ActivityManager.RunningTaskInfo taskInfo, boolean relayout) {
+        return createWindowDecoration(taskInfo, new FakeMaximizeMenuFactory(), relayout);
     }
 
     private DesktopModeWindowDecoration createWindowDecoration(
             ActivityManager.RunningTaskInfo taskInfo,
             MaximizeMenuFactory maximizeMenuFactory) {
+        return createWindowDecoration(taskInfo, maximizeMenuFactory, false /* relayout */);
+    }
+
+    private DesktopModeWindowDecoration createWindowDecoration(
+            ActivityManager.RunningTaskInfo taskInfo,
+            MaximizeMenuFactory maximizeMenuFactory,
+            boolean relayout) {
         final DesktopModeWindowDecoration windowDecor = new DesktopModeWindowDecoration(mContext,
-                mMockDisplayController, mMockSplitScreenController, mMockShellTaskOrganizer,
-                taskInfo, mMockSurfaceControl, mMockHandler, mBgExecutor, mMockChoreographer,
-                mMockSyncQueue, mMockRootTaskDisplayAreaOrganizer,
-                SurfaceControl.Builder::new, mMockTransactionSupplier,
+                mContext, mMockDisplayController, mMockSplitScreenController,
+                mMockShellTaskOrganizer, taskInfo, mMockSurfaceControl, mMockHandler, mBgExecutor,
+                mMockChoreographer, mMockSyncQueue, mMockRootTaskDisplayAreaOrganizer,
+                mMockGenericLinksParser, SurfaceControl.Builder::new, mMockTransactionSupplier,
                 WindowContainerTransaction::new, SurfaceControl::new,
-                mMockSurfaceControlViewHostFactory, maximizeMenuFactory);
+                mMockSurfaceControlViewHostFactory, maximizeMenuFactory, mMockHandleMenuFactory,
+                        mMockMultiInstanceHelper);
         windowDecor.setCaptionListeners(mMockTouchEventListener, mMockTouchEventListener,
                 mMockTouchEventListener, mMockTouchEventListener);
         windowDecor.setExclusionRegionListener(mMockExclusionRegionListener);
         windowDecor.setOpenInBrowserClickListener(mMockOpenInBrowserClickListener);
+        windowDecor.mDecorWindowContext = mContext;
+        if (relayout) {
+            windowDecor.relayout(taskInfo);
+        }
         return windowDecor;
     }
 
@@ -692,8 +919,6 @@
                 "DesktopModeWindowDecorationTests");
         taskInfo.baseActivity = new ComponentName("com.android.wm.shell.windowdecor",
                 "DesktopModeWindowDecorationTests");
-        taskInfo.capturedLink = TEST_URI;
-        taskInfo.capturedLinkTimestamp = System.currentTimeMillis();
         return taskInfo;
 
     }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragDetectorTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragDetectorTest.kt
index 3fbab0f..56224b4 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragDetectorTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragDetectorTest.kt
@@ -85,6 +85,23 @@
     }
 
     @Test
+    fun testNoMove_mouse_passesDownAndUp() {
+        assertTrue(dragDetector.onMotionEvent(
+            createMotionEvent(MotionEvent.ACTION_DOWN, isTouch = false)))
+        verify(eventHandler).handleMotionEvent(any(), argThat {
+            return@argThat it.action == MotionEvent.ACTION_DOWN && it.x == X && it.y == Y &&
+                    it.source == InputDevice.SOURCE_MOUSE
+        })
+
+        assertTrue(dragDetector.onMotionEvent(
+            createMotionEvent(MotionEvent.ACTION_UP, isTouch = false)))
+        verify(eventHandler).handleMotionEvent(any(), argThat {
+            return@argThat it.action == MotionEvent.ACTION_UP && it.x == X && it.y == Y &&
+                    it.source == InputDevice.SOURCE_MOUSE
+        })
+    }
+
+    @Test
     fun testMoveInSlop_touch_passesDownAndUp() {
         `when`(eventHandler.handleMotionEvent(any(), argThat {
             return@argThat it.action == MotionEvent.ACTION_DOWN
@@ -166,6 +183,52 @@
     }
 
     @Test
+    fun testDownMoveDown_shouldIgnoreTheSecondDownMotion() {
+        assertTrue(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_DOWN)))
+        verify(eventHandler).handleMotionEvent(any(), argThat {
+            return@argThat it.action == MotionEvent.ACTION_DOWN && it.x == X && it.y == Y &&
+                    it.source == InputDevice.SOURCE_TOUCHSCREEN
+        })
+
+        val newX = X + SLOP + 1
+        assertTrue(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_MOVE, newX, Y)))
+        verify(eventHandler).handleMotionEvent(any(), argThat {
+            return@argThat it.action == MotionEvent.ACTION_MOVE && it.x == newX && it.y == Y &&
+                    it.source == InputDevice.SOURCE_TOUCHSCREEN
+        })
+
+        assertTrue(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_DOWN)))
+        verify(eventHandler).handleMotionEvent(any(), argThat {
+            return@argThat it.action == MotionEvent.ACTION_MOVE && it.x == newX && it.y == Y &&
+                    it.source == InputDevice.SOURCE_TOUCHSCREEN
+        })
+    }
+
+    @Test
+    fun testDownMouseMoveDownTouch_shouldIgnoreTheTouchDownMotion() {
+        assertTrue(dragDetector.onMotionEvent(
+            createMotionEvent(MotionEvent.ACTION_DOWN, isTouch = false)))
+        verify(eventHandler).handleMotionEvent(any(), argThat {
+            return@argThat it.action == MotionEvent.ACTION_DOWN && it.x == X && it.y == Y &&
+                    it.source == InputDevice.SOURCE_MOUSE
+        })
+
+        val newX = X + SLOP + 1
+        assertTrue(dragDetector.onMotionEvent(
+            createMotionEvent(MotionEvent.ACTION_MOVE, newX, Y, isTouch = false)))
+        verify(eventHandler).handleMotionEvent(any(), argThat {
+            return@argThat it.action == MotionEvent.ACTION_MOVE && it.x == newX && it.y == Y &&
+                    it.source == InputDevice.SOURCE_MOUSE
+        })
+
+        assertTrue(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_DOWN)))
+        verify(eventHandler).handleMotionEvent(any(), argThat {
+            return@argThat it.action == MotionEvent.ACTION_MOVE && it.x == newX && it.y == Y &&
+                    it.source == InputDevice.SOURCE_MOUSE
+        })
+    }
+
+    @Test
     fun testPassesHoverEnter() {
         `when`(eventHandler.handleMotionEvent(any(), argThat {
             it.action == MotionEvent.ACTION_HOVER_ENTER
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragResizeWindowGeometryTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragResizeWindowGeometryTests.java
index 6a94cd8..77337a0 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragResizeWindowGeometryTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragResizeWindowGeometryTests.java
@@ -25,6 +25,7 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import android.annotation.NonNull;
+import android.content.Context;
 import android.graphics.Point;
 import android.graphics.Region;
 import android.platform.test.annotations.DisableFlags;
@@ -36,6 +37,7 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.window.flags.Flags;
+import com.android.wm.shell.ShellTestCase;
 
 import com.google.common.testing.EqualsTester;
 
@@ -51,7 +53,7 @@
  */
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
-public class DragResizeWindowGeometryTests {
+public class DragResizeWindowGeometryTests extends ShellTestCase {
     private static final Size TASK_SIZE = new Size(500, 1000);
     private static final int TASK_CORNER_RADIUS = 10;
     private static final int EDGE_RESIZE_THICKNESS = 15;
@@ -107,7 +109,7 @@
     @Test
     public void testRegionUnionContainsEdges() {
         Region region = new Region();
-        GEOMETRY.union(region);
+        GEOMETRY.union(mContext, region);
         assertThat(region.isComplex()).isTrue();
         // Region excludes task area. Note that coordinates start from top left.
         assertThat(region.contains(TASK_SIZE.getWidth() / 2, TASK_SIZE.getHeight() / 2)).isFalse();
@@ -147,10 +149,10 @@
     @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_EDGE_DRAG_RESIZE)
     public void testRegionUnion_edgeDragResizeEnabled_containsLargeCorners() {
         Region region = new Region();
-        GEOMETRY.union(region);
+        GEOMETRY.union(mContext, region);
         final int cornerRadius = LARGE_CORNER_SIZE / 2;
 
-        new TestPoints(TASK_SIZE, cornerRadius).validateRegion(region);
+        new TestPoints(mContext, TASK_SIZE, cornerRadius).validateRegion(region);
     }
 
     /**
@@ -161,10 +163,10 @@
     @DisableFlags(Flags.FLAG_ENABLE_WINDOWING_EDGE_DRAG_RESIZE)
     public void testRegionUnion_edgeDragResizeDisabled_containsFineCorners() {
         Region region = new Region();
-        GEOMETRY.union(region);
+        GEOMETRY.union(mContext, region);
         final int cornerRadius = FINE_CORNER_SIZE / 2;
 
-        new TestPoints(TASK_SIZE, cornerRadius).validateRegion(region);
+        new TestPoints(mContext, TASK_SIZE, cornerRadius).validateRegion(region);
     }
 
     @Test
@@ -186,16 +188,16 @@
     }
 
     private void validateCtrlTypeForEdges(boolean isTouchscreen, boolean isEdgeResizePermitted) {
-        assertThat(GEOMETRY.calculateCtrlType(isTouchscreen, isEdgeResizePermitted,
+        assertThat(GEOMETRY.calculateCtrlType(mContext, isTouchscreen, isEdgeResizePermitted,
                 LEFT_EDGE_POINT.x, LEFT_EDGE_POINT.y)).isEqualTo(
                         isEdgeResizePermitted ? CTRL_TYPE_LEFT : CTRL_TYPE_UNDEFINED);
-        assertThat(GEOMETRY.calculateCtrlType(isTouchscreen, isEdgeResizePermitted,
+        assertThat(GEOMETRY.calculateCtrlType(mContext, isTouchscreen, isEdgeResizePermitted,
                 TOP_EDGE_POINT.x, TOP_EDGE_POINT.y)).isEqualTo(
                         isEdgeResizePermitted ? CTRL_TYPE_TOP : CTRL_TYPE_UNDEFINED);
-        assertThat(GEOMETRY.calculateCtrlType(isTouchscreen, isEdgeResizePermitted,
+        assertThat(GEOMETRY.calculateCtrlType(mContext, isTouchscreen, isEdgeResizePermitted,
                 RIGHT_EDGE_POINT.x, RIGHT_EDGE_POINT.y)).isEqualTo(
                         isEdgeResizePermitted ? CTRL_TYPE_RIGHT : CTRL_TYPE_UNDEFINED);
-        assertThat(GEOMETRY.calculateCtrlType(isTouchscreen, isEdgeResizePermitted,
+        assertThat(GEOMETRY.calculateCtrlType(mContext, isTouchscreen, isEdgeResizePermitted,
                 BOTTOM_EDGE_POINT.x, BOTTOM_EDGE_POINT.y)).isEqualTo(
                         isEdgeResizePermitted ? CTRL_TYPE_BOTTOM : CTRL_TYPE_UNDEFINED);
     }
@@ -203,8 +205,9 @@
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_EDGE_DRAG_RESIZE)
     public void testCalculateControlType_edgeDragResizeEnabled_corners() {
-        final TestPoints fineTestPoints = new TestPoints(TASK_SIZE, FINE_CORNER_SIZE / 2);
-        final TestPoints largeCornerTestPoints = new TestPoints(TASK_SIZE, LARGE_CORNER_SIZE / 2);
+        final TestPoints fineTestPoints = new TestPoints(mContext, TASK_SIZE, FINE_CORNER_SIZE / 2);
+        final TestPoints largeCornerTestPoints =
+                new TestPoints(mContext, TASK_SIZE, LARGE_CORNER_SIZE / 2);
 
         // When the flag is enabled, points within fine corners should pass regardless of touch or
         // not. Points outside fine corners should not pass when using a course input (non-touch).
@@ -241,8 +244,10 @@
     @Test
     @DisableFlags(Flags.FLAG_ENABLE_WINDOWING_EDGE_DRAG_RESIZE)
     public void testCalculateControlType_edgeDragResizeDisabled_corners() {
-        final TestPoints fineTestPoints = new TestPoints(TASK_SIZE, FINE_CORNER_SIZE / 2);
-        final TestPoints largeCornerTestPoints = new TestPoints(TASK_SIZE, LARGE_CORNER_SIZE / 2);
+        final TestPoints fineTestPoints =
+                new TestPoints(mContext, TASK_SIZE, FINE_CORNER_SIZE / 2);
+        final TestPoints largeCornerTestPoints =
+                new TestPoints(mContext, TASK_SIZE, LARGE_CORNER_SIZE / 2);
 
         // When the flag is disabled, points within fine corners should pass only from touchscreen.
         // Edge resize permitted (indicating the event is from a cursor/stylus) should have no
@@ -284,6 +289,7 @@
      * <p>Creates points that are both just within the bounds of each corner, and just outside.
      */
     private static final class TestPoints {
+        private final Context mContext;
         private final Point mTopLeftPoint;
         private final Point mTopLeftPointOutside;
         private final Point mTopRightPoint;
@@ -293,7 +299,8 @@
         private final Point mBottomRightPoint;
         private final Point mBottomRightPointOutside;
 
-        TestPoints(@NonNull Size taskSize, int cornerRadius) {
+        TestPoints(@NonNull Context context, @NonNull Size taskSize, int cornerRadius) {
+            mContext = context;
             // Point just inside corner square is included.
             mTopLeftPoint = new Point(-cornerRadius + 1, -cornerRadius + 1);
             // Point just outside corner square is excluded.
@@ -340,17 +347,17 @@
         public void validateCtrlTypeForInnerPoints(@NonNull DragResizeWindowGeometry geometry,
                 boolean isTouchscreen, boolean isEdgeResizePermitted,
                 boolean expectedWithinGeometry) {
-            assertThat(geometry.calculateCtrlType(isTouchscreen, isEdgeResizePermitted,
+            assertThat(geometry.calculateCtrlType(mContext, isTouchscreen, isEdgeResizePermitted,
                     mTopLeftPoint.x, mTopLeftPoint.y)).isEqualTo(
                     expectedWithinGeometry ? CTRL_TYPE_LEFT | CTRL_TYPE_TOP : CTRL_TYPE_UNDEFINED);
-            assertThat(geometry.calculateCtrlType(isTouchscreen, isEdgeResizePermitted,
+            assertThat(geometry.calculateCtrlType(mContext, isTouchscreen, isEdgeResizePermitted,
                     mTopRightPoint.x, mTopRightPoint.y)).isEqualTo(
                     expectedWithinGeometry ? CTRL_TYPE_RIGHT | CTRL_TYPE_TOP : CTRL_TYPE_UNDEFINED);
-            assertThat(geometry.calculateCtrlType(isTouchscreen, isEdgeResizePermitted,
+            assertThat(geometry.calculateCtrlType(mContext, isTouchscreen, isEdgeResizePermitted,
                     mBottomLeftPoint.x, mBottomLeftPoint.y)).isEqualTo(
                     expectedWithinGeometry ? CTRL_TYPE_LEFT | CTRL_TYPE_BOTTOM
                             : CTRL_TYPE_UNDEFINED);
-            assertThat(geometry.calculateCtrlType(isTouchscreen, isEdgeResizePermitted,
+            assertThat(geometry.calculateCtrlType(mContext, isTouchscreen, isEdgeResizePermitted,
                     mBottomRightPoint.x, mBottomRightPoint.y)).isEqualTo(
                     expectedWithinGeometry ? CTRL_TYPE_RIGHT | CTRL_TYPE_BOTTOM
                             : CTRL_TYPE_UNDEFINED);
@@ -363,17 +370,17 @@
         public void validateCtrlTypeForOutsidePoints(@NonNull DragResizeWindowGeometry geometry,
                 boolean isTouchscreen, boolean isEdgeResizePermitted,
                 boolean expectedWithinGeometry) {
-            assertThat(geometry.calculateCtrlType(isTouchscreen, isEdgeResizePermitted,
+            assertThat(geometry.calculateCtrlType(mContext, isTouchscreen, isEdgeResizePermitted,
                     mTopLeftPointOutside.x, mTopLeftPointOutside.y)).isEqualTo(
                     expectedWithinGeometry ? CTRL_TYPE_LEFT | CTRL_TYPE_TOP : CTRL_TYPE_UNDEFINED);
-            assertThat(geometry.calculateCtrlType(isTouchscreen, isEdgeResizePermitted,
+            assertThat(geometry.calculateCtrlType(mContext, isTouchscreen, isEdgeResizePermitted,
                     mTopRightPointOutside.x, mTopRightPointOutside.y)).isEqualTo(
                     expectedWithinGeometry ? CTRL_TYPE_RIGHT | CTRL_TYPE_TOP : CTRL_TYPE_UNDEFINED);
-            assertThat(geometry.calculateCtrlType(isTouchscreen, isEdgeResizePermitted,
+            assertThat(geometry.calculateCtrlType(mContext, isTouchscreen, isEdgeResizePermitted,
                     mBottomLeftPointOutside.x, mBottomLeftPointOutside.y)).isEqualTo(
                     expectedWithinGeometry ? CTRL_TYPE_LEFT | CTRL_TYPE_BOTTOM
                             : CTRL_TYPE_UNDEFINED);
-            assertThat(geometry.calculateCtrlType(isTouchscreen, isEdgeResizePermitted,
+            assertThat(geometry.calculateCtrlType(mContext, isTouchscreen, isEdgeResizePermitted,
                     mBottomRightPointOutside.x, mBottomRightPointOutside.y)).isEqualTo(
                     expectedWithinGeometry ? CTRL_TYPE_RIGHT | CTRL_TYPE_BOTTOM
                             : CTRL_TYPE_UNDEFINED);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositionerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositionerTest.kt
index 6667504..2ce59ff 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositionerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositionerTest.kt
@@ -35,7 +35,6 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.ArgumentMatchers.anyInt
-import org.mockito.ArgumentMatchers.eq
 import org.mockito.Mock
 import org.mockito.Mockito
 import org.mockito.Mockito.any
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt
index e548f8f..a1c7947 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt
@@ -57,6 +57,7 @@
 import org.mockito.Mock
 import org.mockito.Mockito.mock
 import org.mockito.kotlin.any
+import org.mockito.kotlin.mock
 import org.mockito.kotlin.whenever
 
 /**
@@ -109,6 +110,9 @@
         whenever(mockDesktopWindowDecoration.addWindow(
             anyInt(), any(), any(), any(), anyInt(), anyInt(), anyInt(), anyInt())
         ).thenReturn(mockAdditionalViewHostViewContainer)
+        whenever(mockDesktopWindowDecoration.addWindow(
+            any<View>(), any(), any(), any(), anyInt(), anyInt(), anyInt(), anyInt())
+        ).thenReturn(mockAdditionalViewHostViewContainer)
         whenever(mockAdditionalViewHostViewContainer.view).thenReturn(menuView)
         whenever(displayController.getDisplayLayout(anyInt())).thenReturn(displayLayout)
         whenever(displayLayout.width()).thenReturn(DISPLAY_BOUNDS.width())
@@ -226,13 +230,13 @@
             }
             else -> error("Invalid windowing mode")
         }
-        val handleMenu = HandleMenu(mockDesktopWindowDecoration, layoutId,
-                onClickListener, onTouchListener, appIcon, appName, displayController,
-                splitScreenController, shouldShowWindowingPill = true,
-                shouldShowBrowserPill = true, captionWidth = HANDLE_WIDTH, captionHeight = 50,
-                captionX = captionX
+        val handleMenu = HandleMenu(mockDesktopWindowDecoration, layoutId, appIcon, appName,
+            splitScreenController, shouldShowWindowingPill = true,
+            shouldShowNewWindowButton = true,
+            null /* openInBrowserLink */, captionWidth = HANDLE_WIDTH, captionHeight = 50,
+            captionX = captionX
         )
-        handleMenu.show()
+        handleMenu.show(mock(), mock(), mock(), mock(), mock(), mock(), mock())
         return handleMenu
     }
 
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositionerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositionerTest.kt
index 943c313..08a6e1b 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositionerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositionerTest.kt
@@ -200,7 +200,7 @@
         verify(mockTransaction).setPosition(any(), eq(rectAfterMove.left.toFloat()),
                 eq(rectAfterMove.top.toFloat()))
 
-        taskPositioner.onDragPositioningEnd(
+        val endBounds = taskPositioner.onDragPositioningEnd(
             STARTING_BOUNDS.left.toFloat() + 70,
             STARTING_BOUNDS.top.toFloat() + 20
         )
@@ -212,12 +212,7 @@
 
         verify(mockDesktopWindowDecoration, never()).showResizeVeil(any())
         verify(mockDesktopWindowDecoration, never()).hideResizeVeil()
-        verify(mockTransitions).startTransition(eq(TRANSIT_CHANGE), argThat { wct ->
-            return@argThat wct.changes.any { (token, change) ->
-                token == taskBinder &&
-                        (change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0 &&
-                        change.configuration.windowConfiguration.bounds == rectAfterEnd }},
-                eq(taskPositioner))
+        Assert.assertEquals(rectAfterEnd, endBounds)
     }
 
     @Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
index 2d1bf14..e6e2d09 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
@@ -20,6 +20,7 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 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;
 import static android.view.WindowInsets.Type.captionBar;
 import static android.view.WindowInsets.Type.mandatorySystemGestures;
 import static android.view.WindowInsets.Type.statusBars;
@@ -50,13 +51,13 @@
 import static org.mockito.Mockito.when;
 import static org.mockito.quality.Strictness.LENIENT;
 
+import android.annotation.NonNull;
 import android.app.ActivityManager;
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Color;
 import android.graphics.Point;
 import android.graphics.Rect;
-import android.platform.test.annotations.EnableFlags;
 import android.platform.test.flag.junit.SetFlagsRule;
 import android.testing.AndroidTestingRunner;
 import android.util.DisplayMetrics;
@@ -75,7 +76,6 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.dx.mockito.inline.extended.StaticMockitoSession;
-import com.android.window.flags.Flags;
 import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.ShellTestCase;
 import com.android.wm.shell.TestRunningTaskInfoBuilder;
@@ -781,8 +781,7 @@
     }
 
     @Test
-    @EnableFlags(Flags.FLAG_ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION)
-    public void testRelayout_captionInsetForceConsume() {
+    public void testRelayout_captionInsetSourceFlags() {
         final Display defaultDisplay = mock(Display.class);
         doReturn(defaultDisplay).when(mMockDisplayController)
                 .getDisplay(Display.DEFAULT_DISPLAY);
@@ -794,11 +793,14 @@
         final ActivityManager.RunningTaskInfo taskInfo =
                 builder.setToken(token).setBounds(new Rect(0, 0, 1000, 1000)).build();
         final TestWindowDecoration windowDecor = createWindowDecoration(taskInfo);
+        mRelayoutParams.mInsetSourceFlags =
+                FLAG_FORCE_CONSUMING | FLAG_FORCE_CONSUMING_OPAQUE_CAPTION_BAR;
         windowDecor.relayout(taskInfo);
 
-        // Caption inset source should be force-consuming.
+        // Caption inset source should add params' flags.
         verify(mMockWindowContainerTransaction).addInsetsSource(eq(token), any(),
-                eq(0) /* index */, eq(captionBar()), any(), any(), eq(FLAG_FORCE_CONSUMING));
+                eq(0) /* index */, eq(captionBar()), any(), any(),
+                eq(FLAG_FORCE_CONSUMING | FLAG_FORCE_CONSUMING_OPAQUE_CAPTION_BAR));
     }
 
     @Test
@@ -892,8 +894,8 @@
     }
 
     private TestWindowDecoration createWindowDecoration(ActivityManager.RunningTaskInfo taskInfo) {
-        return new TestWindowDecoration(mContext, mMockDisplayController, mMockShellTaskOrganizer,
-                taskInfo, mMockTaskSurface,
+        return new TestWindowDecoration(mContext, mContext, mMockDisplayController,
+                mMockShellTaskOrganizer, taskInfo, mMockTaskSurface,
                 new MockObjectSupplier<>(mMockSurfaceControlBuilders,
                         () -> createMockSurfaceControlBuilder(mock(SurfaceControl.class))),
                 new MockObjectSupplier<>(mMockSurfaceControlTransactions,
@@ -931,7 +933,8 @@
     }
 
     private class TestWindowDecoration extends WindowDecoration<TestView> {
-        TestWindowDecoration(Context context, DisplayController displayController,
+        TestWindowDecoration(Context context, @NonNull Context userContext,
+                DisplayController displayController,
                 ShellTaskOrganizer taskOrganizer, ActivityManager.RunningTaskInfo taskInfo,
                 SurfaceControl taskSurface,
                 Supplier<SurfaceControl.Builder> surfaceControlBuilderSupplier,
@@ -939,7 +942,7 @@
                 Supplier<WindowContainerTransaction> windowContainerTransactionSupplier,
                 Supplier<SurfaceControl> surfaceControlSupplier,
                 SurfaceControlViewHostFactory surfaceControlViewHostFactory) {
-            super(context, displayController, taskOrganizer, taskInfo, taskSurface,
+            super(context, userContext, displayController, taskOrganizer, taskInfo, taskSurface,
                     surfaceControlBuilderSupplier, surfaceControlTransactionSupplier,
                     windowContainerTransactionSupplier, surfaceControlSupplier,
                     surfaceControlViewHostFactory);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/additionalviewcontainer/AdditionalSystemViewContainerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/additionalviewcontainer/AdditionalSystemViewContainerTest.kt
index 3b49055..28b4eb6 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/additionalviewcontainer/AdditionalSystemViewContainerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/additionalviewcontainer/AdditionalSystemViewContainerTest.kt
@@ -29,7 +29,7 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.Mock
-import org.mockito.kotlin.any
+import org.mockito.kotlin.argThat
 import org.mockito.kotlin.eq
 import org.mockito.kotlin.verify
 import org.mockito.kotlin.whenever
@@ -66,6 +66,8 @@
 
     @Test
     fun testReleaseView_ViewRemoved() {
+        val flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or
+                WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
         viewContainer = AdditionalSystemViewContainer(
             mockContext,
             TASK_ID,
@@ -73,9 +75,15 @@
             Y,
             WIDTH,
             HEIGHT,
+            flags,
             R.layout.desktop_mode_window_decor_handle_menu
         )
-        verify(mockWindowManager).addView(eq(mockView), any())
+        verify(mockWindowManager).addView(
+            eq(mockView),
+            argThat {
+                lp -> (lp as WindowManager.LayoutParams).flags == flags
+            }
+        )
         viewContainer.releaseView()
         verify(mockWindowManager).removeViewImmediate(mockView)
     }
diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp
index 2fff4f5..a39f30b 100644
--- a/libs/androidfw/Android.bp
+++ b/libs/androidfw/Android.bp
@@ -44,6 +44,9 @@
         "-Werror",
         "-Wunreachable-code",
     ],
+    header_libs: [
+        "native_headers",
+    ],
     target: {
         windows: {
             // The Windows compiler warns incorrectly for value initialization with {}.
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index e302fa8..d71f3b6 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -736,6 +736,7 @@
 
 cc_test {
     name: "hwui_unit_tests",
+    test_config: "tests/unit/AndroidTest.xml",
     defaults: [
         "hwui_test_defaults",
         "android_graphics_apex",
@@ -803,6 +804,7 @@
 
 cc_benchmark {
     name: "hwuimacro",
+    test_config: "tests/macrobench/AndroidTest.xml",
     defaults: ["hwui_test_defaults"],
 
     static_libs: ["libhwui"],
@@ -822,6 +824,7 @@
 
 cc_benchmark {
     name: "hwuimicro",
+    test_config: "tests/microbench/AndroidTest.xml",
     defaults: ["hwui_test_defaults"],
 
     static_libs: ["libhwui_static"],
diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp
index 5d3bc89..d184f64 100644
--- a/libs/hwui/Properties.cpp
+++ b/libs/hwui/Properties.cpp
@@ -101,6 +101,8 @@
 bool Properties::clipSurfaceViews = false;
 bool Properties::hdr10bitPlus = false;
 
+int Properties::timeoutMultiplier = 1;
+
 StretchEffectBehavior Properties::stretchEffectBehavior = StretchEffectBehavior::ShaderHWUI;
 
 DrawingEnabled Properties::drawingEnabled = DrawingEnabled::NotInitialized;
@@ -174,6 +176,8 @@
             base::GetBoolProperty("debug.hwui.clip_surfaceviews", hwui_flags::clip_surfaceviews());
     hdr10bitPlus = hwui_flags::hdr_10bit_plus();
 
+    timeoutMultiplier = android::base::GetIntProperty("ro.hw_timeout_multiplier", 1);
+
     return (prevDebugLayersUpdates != debugLayersUpdates) || (prevDebugOverdraw != debugOverdraw);
 }
 
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index d3176f6..e264642 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -343,6 +343,8 @@
     static bool clipSurfaceViews;
     static bool hdr10bitPlus;
 
+    static int timeoutMultiplier;
+
     static StretchEffectBehavior getStretchEffectBehavior() {
         return stretchEffectBehavior;
     }
diff --git a/libs/hwui/Readback.cpp b/libs/hwui/Readback.cpp
index afe4c38..2f15722 100644
--- a/libs/hwui/Readback.cpp
+++ b/libs/hwui/Readback.cpp
@@ -91,8 +91,10 @@
 
     {
         ATRACE_NAME("sync_wait");
-        if (sourceFence != -1 && sync_wait(sourceFence.get(), 500 /* ms */) != NO_ERROR) {
-            ALOGE("Timeout (500ms) exceeded waiting for buffer fence, abandoning readback attempt");
+        int syncWaitTimeoutMs = 500 * Properties::timeoutMultiplier;
+        if (sourceFence != -1 && sync_wait(sourceFence.get(), syncWaitTimeoutMs) != NO_ERROR) {
+            ALOGE("Timeout (%dms) exceeded waiting for buffer fence, abandoning readback attempt",
+                  syncWaitTimeoutMs);
             return request->onCopyFinished(CopyResult::Timeout);
         }
     }
@@ -109,9 +111,8 @@
 
     sk_sp<SkColorSpace> colorSpace =
             DataSpaceToColorSpace(static_cast<android_dataspace>(dataspace));
-    sk_sp<SkImage> image =
-            SkImages::DeferredFromAHardwareBuffer(sourceBuffer.get(), kPremul_SkAlphaType, 
-                                                  colorSpace);
+    sk_sp<SkImage> image = SkImages::DeferredFromAHardwareBuffer(sourceBuffer.get(),
+                                                                 kPremul_SkAlphaType, colorSpace);
 
     if (!image.get()) {
         return request->onCopyFinished(CopyResult::UnknownError);
diff --git a/libs/hwui/jni/Bitmap.cpp b/libs/hwui/jni/Bitmap.cpp
index d415700..010c4e8 100644
--- a/libs/hwui/jni/Bitmap.cpp
+++ b/libs/hwui/jni/Bitmap.cpp
@@ -33,9 +33,6 @@
 #define DEBUG_PARCEL 0
 
 static jclass   gBitmap_class;
-static jfieldID gBitmap_nativePtr;
-static jmethodID gBitmap_constructorMethodID;
-static jmethodID gBitmap_reinitMethodID;
 
 namespace android {
 
@@ -183,6 +180,9 @@
 void reinitBitmap(JNIEnv* env, jobject javaBitmap, const SkImageInfo& info,
         bool isPremultiplied)
 {
+    static jmethodID gBitmap_reinitMethodID =
+        GetMethodIDOrDie(env, gBitmap_class, "reinit", "(IIZ)V");
+
     // The caller needs to have already set the alpha type properly, so the
     // native SkBitmap stays in sync with the Java Bitmap.
     assert_premultiplied(info, isPremultiplied);
@@ -194,6 +194,10 @@
 jobject createBitmap(JNIEnv* env, Bitmap* bitmap,
         int bitmapCreateFlags, jbyteArray ninePatchChunk, jobject ninePatchInsets,
         int density) {
+    static jmethodID gBitmap_constructorMethodID =
+        GetMethodIDOrDie(env, gBitmap_class,
+            "<init>", "(JIIIZ[BLandroid/graphics/NinePatch$InsetStruct;Z)V");
+
     bool isMutable = bitmapCreateFlags & kBitmapCreateFlag_Mutable;
     bool isPremultiplied = bitmapCreateFlags & kBitmapCreateFlag_Premultiplied;
     // The caller needs to have already set the alpha type properly, so the
@@ -232,11 +236,17 @@
 using namespace android;
 using namespace android::bitmap;
 
+static inline jlong getNativePtr(JNIEnv* env, jobject bitmap) {
+    static jfieldID gBitmap_nativePtr =
+        GetFieldIDOrDie(env, gBitmap_class, "mNativePtr", "J");
+    return env->GetLongField(bitmap, gBitmap_nativePtr);
+}
+
 Bitmap* GraphicsJNI::getNativeBitmap(JNIEnv* env, jobject bitmap) {
     SkASSERT(env);
     SkASSERT(bitmap);
     SkASSERT(env->IsInstanceOf(bitmap, gBitmap_class));
-    jlong bitmapHandle = env->GetLongField(bitmap, gBitmap_nativePtr);
+    jlong bitmapHandle = getNativePtr(env, bitmap);
     LocalScopedBitmap localBitmap(bitmapHandle);
     return localBitmap.valid() ? &localBitmap->bitmap() : nullptr;
 }
@@ -246,7 +256,7 @@
     SkASSERT(env);
     SkASSERT(bitmap);
     SkASSERT(env->IsInstanceOf(bitmap, gBitmap_class));
-    jlong bitmapHandle = env->GetLongField(bitmap, gBitmap_nativePtr);
+    jlong bitmapHandle = getNativePtr(env, bitmap);
     LocalScopedBitmap localBitmap(bitmapHandle);
     if (outRowBytes) {
         *outRowBytes = localBitmap->rowBytes();
@@ -1269,9 +1279,6 @@
 int register_android_graphics_Bitmap(JNIEnv* env)
 {
     gBitmap_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/Bitmap"));
-    gBitmap_nativePtr = GetFieldIDOrDie(env, gBitmap_class, "mNativePtr", "J");
-    gBitmap_constructorMethodID = GetMethodIDOrDie(env, gBitmap_class, "<init>", "(JIIIZ[BLandroid/graphics/NinePatch$InsetStruct;Z)V");
-    gBitmap_reinitMethodID = GetMethodIDOrDie(env, gBitmap_class, "reinit", "(IIZ)V");
     uirenderer::HardwareBufferHelpers::init();
     return android::RegisterMethodsOrDie(env, "android/graphics/Bitmap", gBitmapMethods,
                                          NELEM(gBitmapMethods));
diff --git a/libs/hwui/jni/ImageDecoder.cpp b/libs/hwui/jni/ImageDecoder.cpp
index 6744c6c..aebc4db 100644
--- a/libs/hwui/jni/ImageDecoder.cpp
+++ b/libs/hwui/jni/ImageDecoder.cpp
@@ -162,8 +162,8 @@
 
 static jobject ImageDecoder_nCreateFd(JNIEnv* env, jobject /*clazz*/,
         jobject fileDescriptor, jlong length, jboolean preferAnimation, jobject source) {
-#ifndef __ANDROID__ // LayoutLib for Windows does not support F_DUPFD_CLOEXEC
-    return throw_exception(env, kSourceException, "Only supported on Android", nullptr, source);
+#ifdef _WIN32  // LayoutLib for Windows does not support F_DUPFD_CLOEXEC
+    return throw_exception(env, kSourceException, "Not supported on Windows", nullptr, source);
 #else
     int descriptor = jniGetFDFromFileDescriptor(env, fileDescriptor);
 
diff --git a/libs/hwui/tests/common/TestUtils.cpp b/libs/hwui/tests/common/TestUtils.cpp
index ad963dd..93118aea 100644
--- a/libs/hwui/tests/common/TestUtils.cpp
+++ b/libs/hwui/tests/common/TestUtils.cpp
@@ -40,6 +40,7 @@
 namespace android {
 namespace uirenderer {
 
+std::mutex TestUtils::sMutex;
 std::unordered_map<int, TestUtils::CallCounts> TestUtils::sMockFunctorCounts{};
 
 SkColor TestUtils::interpolateColor(float fraction, SkColor start, SkColor end) {
diff --git a/libs/hwui/tests/common/TestUtils.h b/libs/hwui/tests/common/TestUtils.h
index 0ede902..8ab2b16 100644
--- a/libs/hwui/tests/common/TestUtils.h
+++ b/libs/hwui/tests/common/TestUtils.h
@@ -305,22 +305,26 @@
                 .onSync =
                         [](int functor, void* client_data, const WebViewSyncData& data) {
                             expectOnRenderThread("onSync");
+                            std::scoped_lock lock(sMutex);
                             sMockFunctorCounts[functor].sync++;
                         },
                 .onContextDestroyed =
                         [](int functor, void* client_data) {
                             expectOnRenderThread("onContextDestroyed");
+                            std::scoped_lock lock(sMutex);
                             sMockFunctorCounts[functor].contextDestroyed++;
                         },
                 .onDestroyed =
                         [](int functor, void* client_data) {
                             expectOnRenderThread("onDestroyed");
+                            std::scoped_lock lock(sMutex);
                             sMockFunctorCounts[functor].destroyed++;
                         },
                 .removeOverlays =
                         [](int functor, void* data,
                            void (*mergeTransaction)(ASurfaceTransaction*)) {
                             expectOnRenderThread("removeOverlays");
+                            std::scoped_lock lock(sMutex);
                             sMockFunctorCounts[functor].removeOverlays++;
                         },
         };
@@ -329,6 +333,7 @@
                 callbacks.gles.draw = [](int functor, void* client_data, const DrawGlInfo& params,
                                          const WebViewOverlayData& overlay_params) {
                     expectOnRenderThread("draw");
+                    std::scoped_lock lock(sMutex);
                     sMockFunctorCounts[functor].glesDraw++;
                 };
                 break;
@@ -336,15 +341,18 @@
                 callbacks.vk.initialize = [](int functor, void* data,
                                              const VkFunctorInitParams& params) {
                     expectOnRenderThread("initialize");
+                    std::scoped_lock lock(sMutex);
                     sMockFunctorCounts[functor].vkInitialize++;
                 };
                 callbacks.vk.draw = [](int functor, void* data, const VkFunctorDrawParams& params,
                                        const WebViewOverlayData& overlayParams) {
                     expectOnRenderThread("draw");
+                    std::scoped_lock lock(sMutex);
                     sMockFunctorCounts[functor].vkDraw++;
                 };
                 callbacks.vk.postDraw = [](int functor, void* data) {
                     expectOnRenderThread("postDraw");
+                    std::scoped_lock lock(sMutex);
                     sMockFunctorCounts[functor].vkPostDraw++;
                 };
                 break;
@@ -352,11 +360,16 @@
         return callbacks;
     }
 
-    static CallCounts& countsForFunctor(int functor) { return sMockFunctorCounts[functor]; }
+    static CallCounts copyCountsForFunctor(int functor) {
+        std::scoped_lock lock(sMutex);
+        return sMockFunctorCounts[functor];
+    }
 
     static SkFont defaultFont();
 
 private:
+    // guards sMockFunctorCounts
+    static std::mutex sMutex;
     static std::unordered_map<int, CallCounts> sMockFunctorCounts;
 
     static void syncHierarchyPropertiesAndDisplayListImpl(RenderNode* node) {
diff --git a/libs/hwui/AndroidTest.xml b/libs/hwui/tests/macrobench/AndroidTest.xml
similarity index 60%
copy from libs/hwui/AndroidTest.xml
copy to libs/hwui/tests/macrobench/AndroidTest.xml
index 75f61f5..5b8576d 100644
--- a/libs/hwui/AndroidTest.xml
+++ b/libs/hwui/tests/macrobench/AndroidTest.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
+<!-- Copyright 2024 The Android Open Source Project
 
      Licensed under the Apache License, Version 2.0 (the "License");
      you may not use this file except in compliance with the License.
@@ -13,24 +13,13 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<configuration description="Config for hwuimicro">
+<configuration description="Config for hwuimacro">
     <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
         <option name="cleanup" value="true" />
-        <option name="push" value="hwui_unit_tests->/data/local/tmp/nativetest/hwui_unit_tests" />
-        <option name="push" value="hwuimicro->/data/local/tmp/benchmarktest/hwuimicro" />
         <option name="push" value="hwuimacro->/data/local/tmp/benchmarktest/hwuimacro" />
     </target_preparer>
     <option name="test-suite-tag" value="apct" />
     <option name="not-shardable" value="true" />
-    <test class="com.android.tradefed.testtype.GTest" >
-        <option name="native-test-device-path" value="/data/local/tmp/nativetest" />
-        <option name="module-name" value="hwui_unit_tests" />
-    </test>
-    <test class="com.android.tradefed.testtype.GoogleBenchmarkTest" >
-        <option name="native-benchmark-device-path" value="/data/local/tmp/benchmarktest" />
-        <option name="benchmark-module-name" value="hwuimicro" />
-        <option name="file-exclusion-filter-regex" value=".*\.config$" />
-    </test>
     <test class="com.android.tradefed.testtype.GoogleBenchmarkTest" >
         <option name="native-benchmark-device-path" value="/data/local/tmp/benchmarktest" />
         <option name="benchmark-module-name" value="hwuimacro" />
diff --git a/libs/hwui/tests/macrobench/how_to_run.txt b/libs/hwui/tests/macrobench/how_to_run.txt
index 3c3d36a..59ef25a 100644
--- a/libs/hwui/tests/macrobench/how_to_run.txt
+++ b/libs/hwui/tests/macrobench/how_to_run.txt
@@ -3,3 +3,7 @@
 adb shell /data/benchmarktest/hwuimacro/hwuimacro shadowgrid2 --onscreen
 
 Pass --help to get help
+
+OR (if you don't need to pass arguments)
+
+atest hwuimacro
diff --git a/libs/hwui/AndroidTest.xml b/libs/hwui/tests/microbench/AndroidTest.xml
similarity index 63%
rename from libs/hwui/AndroidTest.xml
rename to libs/hwui/tests/microbench/AndroidTest.xml
index 75f61f5..d67305df 100644
--- a/libs/hwui/AndroidTest.xml
+++ b/libs/hwui/tests/microbench/AndroidTest.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
+<!-- Copyright 2024 The Android Open Source Project
 
      Licensed under the Apache License, Version 2.0 (the "License");
      you may not use this file except in compliance with the License.
@@ -16,24 +16,13 @@
 <configuration description="Config for hwuimicro">
     <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
         <option name="cleanup" value="true" />
-        <option name="push" value="hwui_unit_tests->/data/local/tmp/nativetest/hwui_unit_tests" />
         <option name="push" value="hwuimicro->/data/local/tmp/benchmarktest/hwuimicro" />
-        <option name="push" value="hwuimacro->/data/local/tmp/benchmarktest/hwuimacro" />
     </target_preparer>
     <option name="test-suite-tag" value="apct" />
     <option name="not-shardable" value="true" />
-    <test class="com.android.tradefed.testtype.GTest" >
-        <option name="native-test-device-path" value="/data/local/tmp/nativetest" />
-        <option name="module-name" value="hwui_unit_tests" />
-    </test>
     <test class="com.android.tradefed.testtype.GoogleBenchmarkTest" >
         <option name="native-benchmark-device-path" value="/data/local/tmp/benchmarktest" />
         <option name="benchmark-module-name" value="hwuimicro" />
         <option name="file-exclusion-filter-regex" value=".*\.config$" />
     </test>
-    <test class="com.android.tradefed.testtype.GoogleBenchmarkTest" >
-        <option name="native-benchmark-device-path" value="/data/local/tmp/benchmarktest" />
-        <option name="benchmark-module-name" value="hwuimacro" />
-        <option name="file-exclusion-filter-regex" value=".*\.config$" />
-    </test>
 </configuration>
diff --git a/libs/hwui/tests/microbench/how_to_run.txt b/libs/hwui/tests/microbench/how_to_run.txt
index 915fe5d..c7ddc1a 100755
--- a/libs/hwui/tests/microbench/how_to_run.txt
+++ b/libs/hwui/tests/microbench/how_to_run.txt
@@ -1,3 +1,7 @@
 mmm -j8 frameworks/base/libs/hwui &&
 adb push $OUT/data/benchmarktest/hwuimicro/hwuimicro /data/benchmarktest/hwuimicro/hwuimicro &&
 adb shell /data/benchmarktest/hwuimicro/hwuimicro
+
+OR
+
+atest hwuimicro
diff --git a/libs/hwui/tests/unit/AndroidTest.xml b/libs/hwui/tests/unit/AndroidTest.xml
new file mode 100644
index 0000000..dc586c9
--- /dev/null
+++ b/libs/hwui/tests/unit/AndroidTest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Config for hwui_unit_tests">
+    <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+        <option name="cleanup" value="true" />
+        <option name="push" value="hwui_unit_tests->/data/local/tmp/nativetest/hwui_unit_tests" />
+    </target_preparer>
+    <option name="test-suite-tag" value="apct" />
+    <option name="not-shardable" value="true" />
+    <test class="com.android.tradefed.testtype.GTest" >
+        <option name="native-test-device-path" value="/data/local/tmp/nativetest" />
+        <option name="module-name" value="hwui_unit_tests" />
+    </test>
+</configuration>
diff --git a/libs/hwui/tests/unit/RenderNodeTests.cpp b/libs/hwui/tests/unit/RenderNodeTests.cpp
index e727ea8..690a60a4 100644
--- a/libs/hwui/tests/unit/RenderNodeTests.cpp
+++ b/libs/hwui/tests/unit/RenderNodeTests.cpp
@@ -239,19 +239,21 @@
     TestUtils::runOnRenderThreadUnmanaged([&] (RenderThread&) {
         TestUtils::syncHierarchyPropertiesAndDisplayList(node);
     });
-    auto& counts = TestUtils::countsForFunctor(functor);
+    auto counts = TestUtils::copyCountsForFunctor(functor);
     EXPECT_EQ(1, counts.sync);
     EXPECT_EQ(0, counts.destroyed);
 
     TestUtils::recordNode(*node, [&](Canvas& canvas) {
         canvas.drawWebViewFunctor(functor);
     });
+    counts = TestUtils::copyCountsForFunctor(functor);
     EXPECT_EQ(1, counts.sync);
     EXPECT_EQ(0, counts.destroyed);
 
     TestUtils::runOnRenderThreadUnmanaged([&] (RenderThread&) {
         TestUtils::syncHierarchyPropertiesAndDisplayList(node);
     });
+    counts = TestUtils::copyCountsForFunctor(functor);
     EXPECT_EQ(2, counts.sync);
     EXPECT_EQ(0, counts.destroyed);
 
@@ -265,6 +267,7 @@
     });
     // Fence on any remaining post'd work
     TestUtils::runOnRenderThreadUnmanaged([] (RenderThread&) {});
+    counts = TestUtils::copyCountsForFunctor(functor);
     EXPECT_EQ(2, counts.sync);
     EXPECT_EQ(1, counts.destroyed);
 }
diff --git a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
index 064d42e..26b4729 100644
--- a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
+++ b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
@@ -101,7 +101,7 @@
     SkCanvas dummyCanvas;
 
     int functor1 = TestUtils::createMockFunctor();
-    auto& counts = TestUtils::countsForFunctor(functor1);
+    auto counts = TestUtils::copyCountsForFunctor(functor1);
     skiaDL.mChildFunctors.push_back(
             skiaDL.allocateDrawable<GLFunctorDrawable>(functor1, &dummyCanvas));
     WebViewFunctor_release(functor1);
@@ -118,6 +118,7 @@
         });
     });
 
+    counts = TestUtils::copyCountsForFunctor(functor1);
     EXPECT_EQ(counts.sync, 1);
     EXPECT_EQ(counts.destroyed, 0);
     EXPECT_EQ(vectorDrawable.mutateProperties()->getBounds(), bounds);
@@ -126,6 +127,7 @@
     TestUtils::runOnRenderThread([](auto&) {
         // Fence
     });
+    counts = TestUtils::copyCountsForFunctor(functor1);
     EXPECT_EQ(counts.destroyed, 1);
 }
 
diff --git a/libs/hwui/tests/unit/WebViewFunctorManagerTests.cpp b/libs/hwui/tests/unit/WebViewFunctorManagerTests.cpp
index 5e8f13d..09ce98a 100644
--- a/libs/hwui/tests/unit/WebViewFunctorManagerTests.cpp
+++ b/libs/hwui/tests/unit/WebViewFunctorManagerTests.cpp
@@ -40,7 +40,7 @@
     TestUtils::runOnRenderThreadUnmanaged([](renderthread::RenderThread&) {
         // Empty, don't care
     });
-    auto& counts = TestUtils::countsForFunctor(functor);
+    auto counts = TestUtils::copyCountsForFunctor(functor);
     // We never initialized, so contextDestroyed == 0
     EXPECT_EQ(0, counts.contextDestroyed);
     EXPECT_EQ(1, counts.destroyed);
@@ -59,7 +59,7 @@
     TestUtils::runOnRenderThreadUnmanaged([](renderthread::RenderThread&) {
         // fence
     });
-    auto& counts = TestUtils::countsForFunctor(functor);
+    auto counts = TestUtils::copyCountsForFunctor(functor);
     EXPECT_EQ(0, counts.sync);
     EXPECT_EQ(0, counts.contextDestroyed);
     EXPECT_EQ(0, counts.destroyed);
@@ -69,6 +69,7 @@
         handle->sync(syncData);
     });
 
+    counts = TestUtils::copyCountsForFunctor(functor);
     EXPECT_EQ(1, counts.sync);
 
     TestUtils::runOnRenderThreadUnmanaged([&](auto&) {
@@ -76,6 +77,7 @@
         handle->sync(syncData);
     });
 
+    counts = TestUtils::copyCountsForFunctor(functor);
     EXPECT_EQ(2, counts.sync);
 
     handle.clear();
@@ -84,6 +86,7 @@
         // fence
     });
 
+    counts = TestUtils::copyCountsForFunctor(functor);
     EXPECT_EQ(2, counts.sync);
     EXPECT_EQ(0, counts.contextDestroyed);
     EXPECT_EQ(1, counts.destroyed);
@@ -98,7 +101,6 @@
     auto handle = WebViewFunctorManager::instance().handleFor(functor);
     ASSERT_TRUE(handle);
     WebViewFunctor_release(functor);
-    auto& counts = TestUtils::countsForFunctor(functor);
     for (int i = 0; i < 5; i++) {
         TestUtils::runOnRenderThreadUnmanaged([&](auto&) {
             WebViewSyncData syncData;
@@ -112,6 +114,7 @@
     TestUtils::runOnRenderThreadUnmanaged([](renderthread::RenderThread&) {
         // fence
     });
+    auto counts = TestUtils::copyCountsForFunctor(functor);
     EXPECT_EQ(5, counts.sync);
     EXPECT_EQ(10, counts.glesDraw);
     EXPECT_EQ(1, counts.contextDestroyed);
@@ -127,13 +130,13 @@
     auto handle = WebViewFunctorManager::instance().handleFor(functor);
     ASSERT_TRUE(handle);
     WebViewFunctor_release(functor);
-    auto& counts = TestUtils::countsForFunctor(functor);
     TestUtils::runOnRenderThreadUnmanaged([&](auto&) {
         WebViewSyncData syncData;
         handle->sync(syncData);
         DrawGlInfo drawInfo;
         handle->drawGl(drawInfo);
     });
+    auto counts = TestUtils::copyCountsForFunctor(functor);
     EXPECT_EQ(1, counts.sync);
     EXPECT_EQ(1, counts.glesDraw);
     EXPECT_EQ(0, counts.contextDestroyed);
@@ -141,6 +144,7 @@
     TestUtils::runOnRenderThreadUnmanaged([](auto& rt) {
         rt.destroyRenderingContext();
     });
+    counts = TestUtils::copyCountsForFunctor(functor);
     EXPECT_EQ(1, counts.sync);
     EXPECT_EQ(1, counts.glesDraw);
     EXPECT_EQ(1, counts.contextDestroyed);
@@ -151,6 +155,7 @@
         DrawGlInfo drawInfo;
         handle->drawGl(drawInfo);
     });
+    counts = TestUtils::copyCountsForFunctor(functor);
     EXPECT_EQ(2, counts.sync);
     EXPECT_EQ(2, counts.glesDraw);
     EXPECT_EQ(1, counts.contextDestroyed);
@@ -159,6 +164,7 @@
     TestUtils::runOnRenderThreadUnmanaged([](renderthread::RenderThread&) {
         // fence
     });
+    counts = TestUtils::copyCountsForFunctor(functor);
     EXPECT_EQ(2, counts.sync);
     EXPECT_EQ(2, counts.glesDraw);
     EXPECT_EQ(2, counts.contextDestroyed);
diff --git a/libs/hwui/tests/unit/how_to_run.txt b/libs/hwui/tests/unit/how_to_run.txt
index c11d6eb3..1a35adf 100755
--- a/libs/hwui/tests/unit/how_to_run.txt
+++ b/libs/hwui/tests/unit/how_to_run.txt
@@ -2,3 +2,11 @@
 adb push $ANDROID_PRODUCT_OUT/data/nativetest/hwui_unit_tests/hwui_unit_tests \
     /data/nativetest/hwui_unit_tests/hwui_unit_tests &&
 adb shell /data/nativetest/hwui_unit_tests/hwui_unit_tests
+
+OR
+
+atest hwui_unit_tests
+
+OR, if you need arguments, they can be passed as native-test-flags, as in:
+
+atest hwui_unit_tests -- --test-arg com.android.tradefed.testtype.GTest:native-test-flag:"--renderer=skiavk"
diff --git a/libs/hwui/tests/unit/main.cpp b/libs/hwui/tests/unit/main.cpp
index 76cbc8a..3fd15c4 100644
--- a/libs/hwui/tests/unit/main.cpp
+++ b/libs/hwui/tests/unit/main.cpp
@@ -15,6 +15,7 @@
  */
 
 #include <getopt.h>
+#include <log/log.h>
 #include <signal.h>
 
 #include "Properties.h"
@@ -65,6 +66,19 @@
     return RenderPipelineType::SkiaGL;
 }
 
+static constexpr const char* renderPipelineTypeName(const RenderPipelineType renderPipelineType) {
+    switch (renderPipelineType) {
+        case RenderPipelineType::SkiaGL:
+            return "SkiaGL";
+        case RenderPipelineType::SkiaVulkan:
+            return "SkiaVulkan";
+        case RenderPipelineType::SkiaCpu:
+            return "SkiaCpu";
+        case RenderPipelineType::NotInitialized:
+            return "NotInitialized";
+    }
+}
+
 struct Options {
     RenderPipelineType renderer = RenderPipelineType::SkiaGL;
 };
@@ -118,6 +132,7 @@
 
     auto opts = parseOptions(argc, argv);
     Properties::overrideRenderPipelineType(opts.renderer);
+    ALOGI("Starting HWUI unit tests with %s pipeline", renderPipelineTypeName(opts.renderer));
 
     // Run the tests
     testing::InitGoogleTest(&argc, argv);
diff --git a/location/java/com/android/internal/location/GpsNetInitiatedHandler.java b/location/java/com/android/internal/location/GpsNetInitiatedHandler.java
index 0d5af50..8b6194f 100644
--- a/location/java/com/android/internal/location/GpsNetInitiatedHandler.java
+++ b/location/java/com/android/internal/location/GpsNetInitiatedHandler.java
@@ -27,6 +27,7 @@
 import android.telephony.emergency.EmergencyNumber;
 import android.util.Log;
 
+import com.android.internal.annotations.KeepForWeakReference;
 import com.android.internal.telephony.flags.Flags;
 
 import java.util.concurrent.TimeUnit;
@@ -94,6 +95,7 @@
 
     // The internal implementation of TelephonyManager uses WeakReference so we have to keep a
     // reference here.
+    @KeepForWeakReference
     private final EmergencyCallListener mEmergencyCallListener = new EmergencyCallListener();
 
     private final EmergencyCallCallback mEmergencyCallCallback;
diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java
index 4217562..1024a55 100644
--- a/media/java/android/media/AudioAttributes.java
+++ b/media/java/android/media/AudioAttributes.java
@@ -578,6 +578,8 @@
     });
 
     private AudioAttributes() {
+        mBundle = null;
+        mFormattedTags = "";
     }
 
     /**
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index 8acaf3b..e575dae 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -5263,6 +5263,8 @@
      *           main thread.)
      */
     public void setCallback(@Nullable /* MediaCodec. */ Callback cb, @Nullable Handler handler) {
+        boolean setCallbackStallFlag =
+            GetFlag(() -> android.media.codec.Flags.setCallbackStall());
         if (cb != null) {
             synchronized (mListenerLock) {
                 EventHandler newHandler = getEventHandlerOn(handler, mCallbackHandler);
@@ -5270,7 +5272,7 @@
                 // even if we were to extend this to be callable dynamically, it must
                 // be called when codec is flushed, so no messages are pending.
                 if (newHandler != mCallbackHandler) {
-                    if (android.media.codec.Flags.setCallbackStall()) {
+                    if (setCallbackStallFlag) {
                         logAndRun(
                                 "[new handler] removeMessages(SET_CALLBACK)",
                                 () -> {
@@ -5289,7 +5291,7 @@
                 }
             }
         } else if (mCallbackHandler != null) {
-            if (android.media.codec.Flags.setCallbackStall()) {
+            if (setCallbackStallFlag) {
                 logAndRun(
                         "[null handler] removeMessages(SET_CALLBACK)",
                         () -> {
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index 0667bfd..1930c3d 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -107,7 +107,8 @@
      * #SCANNING_STATE_WHILE_INTERACTIVE}.
      *
      * <p>Routers requesting unrestricted scanning must hold {@link
-     * Manifest.permission#MEDIA_ROUTING_CONTROL}.
+     * Manifest.permission#MEDIA_ROUTING_CONTROL} or {@link
+     * Manifest.permission#MEDIA_CONTENT_CONTROL}.
      *
      * @hide
      */
@@ -522,11 +523,16 @@
      *
      * <p>{@code scanRequest} specifies relevant scanning options, like whether the system should
      * scan with the screen off. Screen off scanning requires {@link
-     * Manifest.permission#MEDIA_ROUTING_CONTROL}
+     * Manifest.permission#MEDIA_ROUTING_CONTROL} or {@link
+     * Manifest.permission#MEDIA_CONTENT_CONTROL}.
      *
      * <p>Proxy routers use the registered {@link RouteDiscoveryPreference} of their target routers.
      *
      * @return A unique {@link ScanToken} that identifies the scan request.
+     * @throws SecurityException If a {@link ScanRequest} with {@link
+     *     ScanRequest.Builder#setScreenOffScan} true is passed, while not holding {@link
+     *     Manifest.permission#MEDIA_ROUTING_CONTROL} or {@link
+     *     Manifest.permission#MEDIA_CONTENT_CONTROL}.
      */
     @FlaggedApi(FLAG_ENABLE_SCREEN_OFF_SCANNING)
     @NonNull
@@ -1745,8 +1751,9 @@
 
             /**
              * Sets whether the app is requesting to scan even while the screen is off, bypassing
-             * default scanning restrictions. Only companion apps holding {@link
-             * Manifest.permission#MEDIA_ROUTING_CONTROL} should set this to {@code true}.
+             * default scanning restrictions. Only apps holding {@link
+             * Manifest.permission#MEDIA_ROUTING_CONTROL} or {@link
+             * Manifest.permission#MEDIA_CONTENT_CONTROL} should set this to {@code true}.
              *
              * @see #requestScan(ScanRequest)
              */
diff --git a/media/java/android/media/flags/media_better_together.aconfig b/media/java/android/media/flags/media_better_together.aconfig
index 91c4f11..7c41f96 100644
--- a/media/java/android/media/flags/media_better_together.aconfig
+++ b/media/java/android/media/flags/media_better_together.aconfig
@@ -134,3 +134,13 @@
         purpose: PURPOSE_BUGFIX
     }
 }
+
+flag {
+    name: "enable_full_scan_with_media_content_control"
+    namespace: "media_better_together"
+    description: "Allows holders of the MEDIA_CONTENT_CONTROL permission to scan for routes while not in the foreground."
+    bug: "352401364"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java
index 1d6e38d..8a877b8 100644
--- a/media/java/android/media/session/MediaSession.java
+++ b/media/java/android/media/session/MediaSession.java
@@ -165,7 +165,7 @@
 
     /**
      * Creates a new session. The session will automatically be registered with
-     * the system but will not be published until {@link #setActive(boolean)
+     * the system, but will not be published until {@link #setActive(boolean)
      * setActive(true)} is called. You must call {@link #release()} when
      * finished with the session.
      * <p>
diff --git a/media/jni/android_media_ImageWriter.cpp b/media/jni/android_media_ImageWriter.cpp
index f64233f..6776f61 100644
--- a/media/jni/android_media_ImageWriter.cpp
+++ b/media/jni/android_media_ImageWriter.cpp
@@ -24,7 +24,6 @@
 #include <utils/String8.h>
 #include <utils/Thread.h>
 
-#include <gui/IProducerListener.h>
 #include <gui/Surface.h>
 #include <ui/PublicFormat.h>
 #include <android_runtime/AndroidRuntime.h>
@@ -65,15 +64,18 @@
 
 // ----------------------------------------------------------------------------
 
-class JNIImageWriterContext : public BnProducerListener {
+class JNIImageWriterContext : public SurfaceListener {
 public:
     JNIImageWriterContext(JNIEnv* env, jobject weakThiz, jclass clazz);
 
     virtual ~JNIImageWriterContext();
 
-    // Implementation of IProducerListener, used to notify the ImageWriter that the consumer
+    // Implementation of SurfaceListener, used to notify the ImageWriter that the consumer
     // has returned a buffer and it is ready for ImageWriter to dequeue.
     virtual void onBufferReleased();
+    virtual bool needsReleaseNotify() override { return true; };
+    virtual void onBuffersDiscarded(const std::vector<sp<GraphicBuffer>>& /*buffers*/) override {};
+    virtual void onBufferDetached(int /*slot*/) override {};
 
     void setProducer(const sp<Surface>& producer) { mProducer = producer; }
     Surface* getProducer() { return mProducer.get(); }
diff --git a/mms/java/android/telephony/MmsManager.java b/mms/java/android/telephony/MmsManager.java
index b893b45..ac29277 100644
--- a/mms/java/android/telephony/MmsManager.java
+++ b/mms/java/android/telephony/MmsManager.java
@@ -26,6 +26,7 @@
 import android.os.Bundle;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.UserHandle;
 
 import com.android.internal.telephony.IMms;
 
@@ -69,9 +70,9 @@
                 return;
             }
 
-            iMms.sendMessage(subId, ActivityThread.currentPackageName(), contentUri,
-                    locationUrl, configOverrides, sentIntent, messageId,
-                    mContext.getAttributionTag());
+            iMms.sendMessage(subId, /* placeholder callingUser= */ UserHandle.USER_NULL,
+                    ActivityThread.currentPackageName(), contentUri, locationUrl,
+                    configOverrides, sentIntent, messageId, mContext.getAttributionTag());
         } catch (RemoteException e) {
             // Ignore it
         }
@@ -101,9 +102,9 @@
             if (iMms == null) {
                 return;
             }
-            iMms.downloadMessage(subId, ActivityThread.currentPackageName(),
-                    locationUrl, contentUri, configOverrides, downloadedIntent,
-                    messageId, mContext.getAttributionTag());
+            iMms.downloadMessage(subId, /* placeholder callingUser= */ UserHandle.USER_NULL,
+                    ActivityThread.currentPackageName(), locationUrl, contentUri,
+                    configOverrides, downloadedIntent, messageId, mContext.getAttributionTag());
         } catch (RemoteException e) {
             // Ignore it
         }
diff --git a/mms/java/com/android/internal/telephony/IMms.aidl b/mms/java/com/android/internal/telephony/IMms.aidl
index 3cdde10..1c75951 100644
--- a/mms/java/com/android/internal/telephony/IMms.aidl
+++ b/mms/java/com/android/internal/telephony/IMms.aidl
@@ -29,6 +29,7 @@
      * Send an MMS message with attribution tag.
      *
      * @param subId the SIM id
+     * @param callingUser user id of the calling app
      * @param callingPkg the package name of the calling app
      * @param contentUri the content uri from which to read MMS message encoded in standard MMS
      *  PDU format
@@ -40,7 +41,7 @@
      * @param messageId An id that uniquely identifies the message requested to be sent.
      * @param attributionTag a tag that attributes the call to a client App.
      */
-    void sendMessage(int subId, String callingPkg, in Uri contentUri,
+    void sendMessage(int subId, in int callingUser, String callingPkg, in Uri contentUri,
             String locationUrl, in Bundle configOverrides, in PendingIntent sentIntent,
             in long messageId, String attributionTag);
 
@@ -48,6 +49,7 @@
      * Download an MMS message using known location and transaction id
      *
      * @param subId the SIM id
+     * @param callingUser user id of the calling app
      * @param callingPkg the package name of the calling app
      * @param locationUrl the location URL of the MMS message to be downloaded, usually obtained
      *  from the MMS WAP push notification
@@ -60,7 +62,7 @@
      * @param messageId An id that uniquely identifies the message requested to be downloaded.
      * @param attributionTag a tag that attributes the call to a client App.
     */
-    void downloadMessage(int subId, String callingPkg, String locationUrl,
+    void downloadMessage(int subId, in int callingUser, String callingPkg, String locationUrl,
             in Uri contentUri, in Bundle configOverrides,
             in PendingIntent downloadedIntent, in long messageId, String attributionTag);
 
@@ -82,6 +84,7 @@
     /**
       * Import a multimedia message into system's MMS store
       *
+     * @param callingUser user id of the calling app
       * @param callingPkg the package name of the calling app
       * @param contentUri the content uri from which to read PDU of the message to import
       * @param messageId the optional message id
@@ -90,7 +93,7 @@
       * @param read if the message is read
       * @return the message URI, null if failed
       */
-    Uri importMultimediaMessage(String callingPkg, in Uri contentUri, String messageId,
+    Uri importMultimediaMessage(in int callingUser, String callingPkg, in Uri contentUri, String messageId,
             long timestampSecs, boolean seen, boolean read);
 
     /**
@@ -146,11 +149,12 @@
     /**
      * Add a multimedia message draft to system MMS store
      *
+     * @param callingUser user id of the calling app
      * @param callingPkg the package name of the calling app
      * @param contentUri the content Uri from which to read PDU data of the draft MMS
      * @return the URI of the stored draft message
      */
-    Uri addMultimediaMessageDraft(String callingPkg, in Uri contentUri);
+    Uri addMultimediaMessageDraft(in int callingUser, String callingPkg, in Uri contentUri);
 
     /**
      * Send a system stored MMS message
diff --git a/nfc/java/android/nfc/NfcAdapter.java b/nfc/java/android/nfc/NfcAdapter.java
index 395f81d..0ffab4b 100644
--- a/nfc/java/android/nfc/NfcAdapter.java
+++ b/nfc/java/android/nfc/NfcAdapter.java
@@ -1166,10 +1166,11 @@
 
 
     /**
-     * Returns whether the device supports observer mode or not. When observe
-     * mode is enabled, the NFC hardware will listen for NFC readers, but not
-     * respond to them. When observe mode is disabled, the NFC hardware will
-     * resoond to the reader and proceed with the transaction.
+     * Returns whether the device supports observe mode or not. When observe mode is enabled, the
+     * NFC hardware will listen to NFC readers, but not respond to them. While enabled, observed
+     * polling frames will be sent to the APDU service (see {@link #setObserveModeEnabled(boolean)}.
+     * When observe mode is disabled (or if it's not supported), the NFC hardware will automatically
+     * respond to the reader and proceed with the transaction.
      * @return true if the mode is supported, false otherwise.
      */
     @FlaggedApi(Flags.FLAG_NFC_OBSERVE_MODE)
@@ -1193,9 +1194,10 @@
      * and simply observe and notify the APDU service of polling loop frames. See
      * {@link #isObserveModeSupported()} for a description of observe mode. Only the package of the
      * currently preferred service (the service set as preferred by the current foreground
-     * application via {@link CardEmulation#setPreferredService(Activity, ComponentName)} or the
-     * current Default Wallet Role Holder {@link android.app.role.RoleManager#ROLE_WALLET}),
-     * otherwise a call to this method will fail and return false.
+     * application via {@link android.nfc.cardemulation.CardEmulation#setPreferredService(Activity,
+     * android.content.ComponentName)} or the current Default Wallet Role Holder
+     * {@link android.app.role.RoleManager#ROLE_WALLET}), otherwise a call to this method will fail
+     * and return false.
      *
      * @param enabled false disables observe mode to allow the transaction to proceed while true
      *                enables observe mode and does not allow transactions to proceed.
diff --git a/packages/CompanionDeviceManager/res/values-es-rUS/strings.xml b/packages/CompanionDeviceManager/res/values-es-rUS/strings.xml
index 15d97af..5ae0c2b 100644
--- a/packages/CompanionDeviceManager/res/values-es-rUS/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-es-rUS/strings.xml
@@ -39,7 +39,7 @@
     <string name="helper_summary_computer" msgid="8774832742608187072">"<xliff:g id="APP_NAME">%1$s</xliff:g> solicita tu permiso en nombre de <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> para acceder a las fotos, el contenido multimedia y las notificaciones de tu teléfono"</string>
     <string name="title_nearby_device_streaming" msgid="7269956847378799794">"¿Permites que &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; realice esta acción?"</string>
     <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"¿Quieres permitir que &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; transmita funciones del sistema y apps del teléfono?"</string>
-    <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"%1$s tendrá acceso a todo el contenido visible o que se reproduzca en tu teléfono, lo que incluye audio, fotos, información de pago, contraseñas y mensajes.&lt;br/&gt;&lt;br/&gt;%1$s podrá transmitir apps y funciones del sistema, a menos que se quiete el acceso a este permiso."</string>
+    <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"%1$s tendrá acceso a todo el contenido visible o que se reproduzca en tu teléfono, lo que incluye audio, fotos, información de pago, contraseñas y mensajes.&lt;br/&gt;&lt;br/&gt;%1$s podrá transmitir apps y funciones del sistema, a menos que se quite el acceso a este permiso."</string>
     <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> está solicitando permiso en nombre de tu <xliff:g id="DEVICE_NAME">%2$s</xliff:g> para transmitir apps y otras funciones del sistema a dispositivos cercanos"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string>
     <string name="summary_generic" msgid="1761976003668044801">"Esta app podrá sincronizar información, como el nombre de la persona que llama, entre el teléfono y el dispositivo elegido"</string>
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/WearApp.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/WearApp.kt
index a75aeaf..d6e19a6 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/WearApp.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/WearApp.kt
@@ -104,7 +104,6 @@
         scrollable(Screen.MultipleCredentialsScreenFlatten.route) {
             MultiCredentialsFlattenScreen(
                 credentialSelectorUiState = (remember { uiState } as MultipleEntry),
-                columnState = it.columnState,
                 flowEngine = flowEngine,
             )
         }
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/components/CredentialsScreenChip.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/components/CredentialsScreenChip.kt
index 25bc381..e58de64 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/components/CredentialsScreenChip.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/components/CredentialsScreenChip.kt
@@ -19,10 +19,11 @@
 import androidx.compose.foundation.layout.Row
 import androidx.compose.material3.Icon
 import android.graphics.drawable.Drawable
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.BoxScope
 import androidx.compose.foundation.layout.RowScope
 import androidx.compose.foundation.layout.size
-import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.material.icons.Icons
 import androidx.compose.material.icons.outlined.Lock
 import androidx.compose.material.icons.outlined.LockOpen
@@ -62,7 +63,7 @@
                         WearButtonText(
                             text = label,
                             textAlign = textAlign,
-                            maxLines = if (secondaryLabel != null) 1 else 2
+                            maxLines = 2
                         )
                     },
                     secondaryLabel,
@@ -88,7 +89,13 @@
     ) {
     val labelParam: (@Composable RowScope.() -> Unit) =
         {
-            text()
+            var horizontalArrangement = Arrangement.Start
+            if (icon == null) {
+                horizontalArrangement = Arrangement.Center
+            }
+            Row(horizontalArrangement = horizontalArrangement, modifier = modifier.fillMaxWidth()) {
+                text()
+            }
         }
 
     val secondaryLabelParam: (@Composable RowScope.() -> Unit)? =
@@ -97,6 +104,7 @@
                 Row {
                     WearSecondaryLabel(
                         text = secondaryLabel,
+                        color = WearMaterialTheme.colors.onSurfaceVariant
                     )
 
                     if (isAuthenticationEntryLocked != null) {
@@ -178,6 +186,7 @@
             WearButtonText(
                 text = stringResource(R.string.dialog_continue_button),
                 textAlign = TextAlign.Center,
+                color = WearMaterialTheme.colors.surface,
             )
         },
         colors = ChipDefaults.primaryChipColors(),
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/components/SignInHeader.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/components/SignInHeader.kt
index 0afef5e..a82360b 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/components/SignInHeader.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/components/SignInHeader.kt
@@ -16,9 +16,11 @@
 
 package com.android.credentialmanager.ui.components
 
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
 import android.graphics.drawable.Drawable
 import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.size
 import androidx.compose.material3.Icon
 import androidx.compose.runtime.Composable
@@ -36,26 +38,30 @@
     icon: Drawable?,
     title: String,
 ) {
-    Column(
-        modifier = Modifier,
-        horizontalAlignment = Alignment.CenterHorizontally
-    ) {
-        if (icon != null) {
-            Icon(
-                bitmap = icon.toBitmap().asImageBitmap(),
-                modifier = Modifier.size(24.dp),
-                // Decorative purpose only.
-                contentDescription = null,
-                tint = Color.Unspecified,
 
+    Row {
+        Spacer(Modifier.weight(0.073f)) // 7.3% side margin
+        Column(
+            modifier = Modifier.weight(0.854f).fillMaxSize(),
+            horizontalAlignment = Alignment.CenterHorizontally
+        ) {
+            if (icon != null) {
+                Icon(
+                    bitmap = icon.toBitmap().asImageBitmap(),
+                    modifier = Modifier.size(24.dp),
+                    // Decorative purpose only.
+                    contentDescription = null,
+                    tint = Color.Unspecified,
+                    )
+            }
+            Spacer(modifier = Modifier.size(8.dp))
+
+            WearTitleText(
+                text = title,
             )
+
+            Spacer(modifier = Modifier.size(8.dp))
         }
-        Spacer(modifier = Modifier.size(8.dp))
-
-        WearTitleText(
-            text = title,
-        )
-
-        Spacer(modifier = Modifier.size(8.dp))
+        Spacer(Modifier.weight(0.073f)) // 7.3% side margin
     }
 }
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/components/Texts.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/components/Texts.kt
index 282fea0..a7b13ad 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/components/Texts.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/components/Texts.kt
@@ -16,6 +16,7 @@
 
 package com.android.credentialmanager.common.ui.components
 
+import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.wrapContentSize
 import androidx.compose.material3.Text
@@ -43,7 +44,7 @@
     Text(
         modifier = modifier.wrapContentSize(),
         text = text,
-        color = WearMaterialTheme.colors.onSurfaceVariant,
+        color = WearMaterialTheme.colors.onSurface,
         textAlign = TextAlign.Center,
         overflow = TextOverflow.Ellipsis,
         maxLines = 2,
@@ -60,7 +61,7 @@
     Text(
         modifier = modifier.padding(start = 8.dp, end = 8.dp).wrapContentSize(),
         text = text,
-        color = WearMaterialTheme.colors.onSurfaceVariant,
+        color = WearMaterialTheme.colors.onSurface,
         style = WearMaterialTheme.typography.caption1,
         overflow = TextOverflow.Ellipsis,
         textAlign = textAlign,
@@ -91,12 +92,13 @@
 @Composable
 fun WearSecondaryLabel(
     text: String,
-    modifier: Modifier = Modifier,
+    color: Color = WearMaterialTheme.colors.onSurface,
+    modifier: Modifier = Modifier
 ) {
     Text(
-        modifier = modifier.wrapContentSize(),
+        modifier = modifier.fillMaxSize(),
         text = text,
-        color = WearMaterialTheme.colors.onSurfaceVariant,
+        color = color,
         style = WearMaterialTheme.typography.caption1,
         overflow = TextOverflow.Ellipsis,
         textAlign = TextAlign.Start,
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/multiple/MultiCredentialsFlattenScreen.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/multiple/MultiCredentialsFlattenScreen.kt
index 36e9792..2af5be8 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/multiple/MultiCredentialsFlattenScreen.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/multiple/MultiCredentialsFlattenScreen.kt
@@ -15,14 +15,18 @@
  */
 package com.android.credentialmanager.ui.screens.multiple
 
-import com.android.credentialmanager.ui.components.CredentialsScreenChip
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
 import androidx.compose.foundation.layout.fillMaxSize
+import com.android.credentialmanager.ui.components.CredentialsScreenChip
+import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.padding
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.text.style.TextAlign
 import androidx.compose.ui.unit.dp
+import androidx.compose.ui.Alignment
 import com.android.credentialmanager.CredentialSelectorUiState.Get.MultipleEntry
 import com.android.credentialmanager.FlowEngine
 import com.android.credentialmanager.R
@@ -32,13 +36,13 @@
 import com.android.credentialmanager.ui.components.CredentialsScreenChipSpacer
 import com.google.android.horologist.annotations.ExperimentalHorologistApi
 import com.google.android.horologist.compose.layout.ScalingLazyColumn
-import com.google.android.horologist.compose.layout.ScalingLazyColumnState
+import com.google.android.horologist.compose.layout.rememberColumnState
+import com.google.android.horologist.compose.layout.ScalingLazyColumnDefaults
 
 /**
  * Screen that shows multiple credentials to select from, grouped by accounts
  *
  * @param credentialSelectorUiState The app bar view model.
- * @param columnState ScalingLazyColumn configuration to be be applied
  * @param modifier styling for composable
  * @param flowEngine [FlowEngine] that updates ui state for this screen
  */
@@ -46,28 +50,45 @@
 @Composable
 fun MultiCredentialsFlattenScreen(
     credentialSelectorUiState: MultipleEntry,
-    columnState: ScalingLazyColumnState,
     flowEngine: FlowEngine,
 ) {
     val selectEntry = flowEngine.getEntrySelector()
-    ScalingLazyColumn(
-        columnState = columnState,
-        modifier = Modifier.fillMaxSize(),
-    ) {
+    Row {
+        Spacer(Modifier.weight(0.052f)) // 5.2% side margin
+        ScalingLazyColumn(
+            columnState = rememberColumnState(
+                ScalingLazyColumnDefaults.belowTimeText(horizontalAlignment = Alignment.Start),
+            ),
+            modifier = Modifier.weight(0.896f).fillMaxSize(), // 5.2% side margin
+        ) {
+
         item {
-            // make this credential specific if all credentials are same
-            WearButtonText(
-                text = stringResource(R.string.sign_in_options_title),
-                textAlign = TextAlign.Start,
-            )
+            Row {
+                Spacer(Modifier.weight(0.073f)) // 7.3% side margin
+                WearButtonText(
+                    text = stringResource(R.string.sign_in_options_title),
+                    textAlign = TextAlign.Center,
+                    modifier = Modifier.weight(0.854f).fillMaxSize(),
+                )
+                Spacer(Modifier.weight(0.073f)) // 7.3% side margin
+            }
         }
 
         credentialSelectorUiState.accounts.forEach { userNameEntries ->
             item {
-                WearSecondaryLabel(
-                    text = userNameEntries.userName,
-                    modifier = Modifier.padding(top = 12.dp, bottom = 4.dp)
-                )
+                Row {
+                    Spacer(Modifier.weight(0.0624f)) // 6.24% side margin
+                    WearSecondaryLabel(
+                        text = userNameEntries.userName,
+                        modifier = Modifier.padding(
+                            top = 12.dp,
+                            bottom = 4.dp,
+                            start = 0.dp,
+                            end = 0.dp
+                        ).fillMaxWidth(0.87f)
+                    )
+                    Spacer(Modifier.weight(0.0624f)) // 6.24% side margin
+                }
             }
 
             userNameEntries.sortedCredentialEntryList.forEach { credential: CredentialEntryInfo ->
@@ -78,7 +99,7 @@
                         secondaryLabel =
                         credential.credentialTypeDisplayName.ifEmpty {
                             credential.providerDisplayName
-                         },
+                        },
                         icon = credential.icon,
                         textAlign = TextAlign.Start
                     )
@@ -87,14 +108,25 @@
                 }
             }
         }
-        item {
-            WearSecondaryLabel(
-                text = stringResource(R.string.provider_list_title),
-                modifier = Modifier.padding(top = 12.dp, bottom = 4.dp)
-            )
-        }
-        credentialSelectorUiState.actionEntryList.forEach { actionEntry ->
+
+        if (credentialSelectorUiState.actionEntryList.isNotEmpty()) {
             item {
+                Row {
+                    Spacer(Modifier.weight(0.0624f)) // 6.24% side margin
+                    WearSecondaryLabel(
+                        text = stringResource(R.string.provider_list_title),
+                        modifier = Modifier.padding(
+                            top = 12.dp,
+                            bottom = 4.dp,
+                            start = 0.dp,
+                            end = 0.dp
+                        ).fillMaxWidth(0.87f)
+                )
+                    Spacer(Modifier.weight(0.0624f)) // 6.24% side margin
+                }
+            }
+            credentialSelectorUiState.actionEntryList.forEach { actionEntry ->
+                item {
                     CredentialsScreenChip(
                         label = actionEntry.title,
                         onClick = { selectEntry(actionEntry, false) },
@@ -102,7 +134,10 @@
                         icon = actionEntry.icon,
                     )
                     CredentialsScreenChipSpacer()
+                }
             }
         }
     }
+    Spacer(Modifier.weight(0.052f)) // 5.2% side margin
+    }
 }
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/multiple/MultiCredentialsFoldScreen.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/multiple/MultiCredentialsFoldScreen.kt
index ce2bad0..38307b0 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/multiple/MultiCredentialsFoldScreen.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/multiple/MultiCredentialsFoldScreen.kt
@@ -16,10 +16,12 @@
 
 package com.android.credentialmanager.ui.screens.multiple
 
-import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.Row
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.foundation.layout.fillMaxSize
 import com.android.credentialmanager.R
 import androidx.compose.ui.res.stringResource
-import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.Spacer
 import androidx.compose.foundation.layout.size
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Modifier
@@ -53,29 +55,31 @@
     flowEngine: FlowEngine,
 ) {
     val selectEntry = flowEngine.getEntrySelector()
-    ScalingLazyColumn(
-        columnState = columnState,
-        modifier = Modifier.fillMaxSize(),
-    ) {
-        // flatten all credentials into one
-        val credentials = credentialSelectorUiState.sortedEntries
-        item {
-            var title = stringResource(R.string.choose_sign_in_title)
-            if (credentials.isNotEmpty()) {
-                if (credentials.all { it.credentialType == CredentialType.PASSKEY }) {
-                    title = stringResource(R.string.choose_passkey_title)
-                } else if (credentials.all { it.credentialType == CredentialType.PASSWORD }) {
-                    title = stringResource(R.string.choose_password_title)
+    Row {
+        Spacer(Modifier.weight(0.052f)) // 5.2% side margin
+        ScalingLazyColumn(
+            columnState = columnState,
+            modifier = Modifier.weight(0.896f).fillMaxSize(),
+        ) {
+            // flatten all credentials into one
+            val credentials = credentialSelectorUiState.sortedEntries
+            item {
+                var title = stringResource(R.string.choose_sign_in_title)
+                if (credentials.isNotEmpty()) {
+                    if (credentials.all { it.credentialType == CredentialType.PASSKEY }) {
+                        title = stringResource(R.string.choose_passkey_title)
+                    } else if (credentials.all { it.credentialType == CredentialType.PASSWORD }) {
+                        title = stringResource(R.string.choose_password_title)
+                    }
                 }
+
+                SignInHeader(
+                    icon = credentialSelectorUiState.icon,
+                    title = title,
+                )
             }
 
-            SignInHeader(
-                icon = credentialSelectorUiState.icon,
-                title = title,
-            )
-        }
-
-        credentials.forEach { credential: CredentialEntryInfo ->
+            credentials.forEach { credential: CredentialEntryInfo ->
                 item {
                     CredentialsScreenChip(
                         label = credential.userName,
@@ -85,29 +89,32 @@
                             credential.providerDisplayName
                         },
                         icon = credential.icon,
+                        textAlign = TextAlign.Start
                     )
                     CredentialsScreenChipSpacer()
                 }
             }
 
-        credentialSelectorUiState.authenticationEntryList.forEach { authenticationEntryInfo ->
-            item {
-                LockedProviderChip(authenticationEntryInfo) {
-                    selectEntry(authenticationEntryInfo, false)
+            credentialSelectorUiState.authenticationEntryList.forEach { authenticationEntryInfo ->
+                item {
+                    LockedProviderChip(authenticationEntryInfo) {
+                        selectEntry(authenticationEntryInfo, false)
+                    }
+                    CredentialsScreenChipSpacer()
                 }
-                CredentialsScreenChipSpacer()
+            }
+            item {
+                Spacer(modifier = Modifier.size(8.dp))
+            }
+
+            item {
+                SignInOptionsChip { flowEngine.openSecondaryScreen() }
+            }
+            item {
+                DismissChip { flowEngine.cancel() }
+                BottomSpacer()
             }
         }
-        item {
-            Spacer(modifier = Modifier.size(8.dp))
+            Spacer(Modifier.weight(0.052f)) // 5.2% side margin
         }
-
-        item {
-            SignInOptionsChip { flowEngine.openSecondaryScreen() }
-        }
-        item {
-            DismissChip { flowEngine.cancel() }
-            BottomSpacer()
-        }
-    }
 }
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/SingleAccountScreen.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/SingleAccountScreen.kt
index e94dd68..17dd962 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/SingleAccountScreen.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/SingleAccountScreen.kt
@@ -18,6 +18,8 @@
 
 package com.android.credentialmanager.ui.screens.single
 
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Modifier
@@ -31,15 +33,18 @@
     headerContent: @Composable () -> Unit,
     accountContent: @Composable () -> Unit,
     columnState: ScalingLazyColumnState,
-    modifier: Modifier = Modifier,
     content: ScalingLazyListScope.() -> Unit,
 ) {
-    ScalingLazyColumn(
-        columnState = columnState,
-        modifier = modifier.fillMaxSize(),
-    ) {
-        item { headerContent() }
-        item { accountContent() }
-        content()
+    Row {
+        Spacer(Modifier.weight(0.052f)) // 5.2% side margin
+        ScalingLazyColumn(
+            columnState = columnState,
+            modifier = Modifier.weight(0.896f).fillMaxSize(),
+        ) {
+            item { headerContent() }
+            item { accountContent() }
+            content()
+        }
+        Spacer(Modifier.weight(0.052f)) // 5.2% side margin
     }
 }
\ No newline at end of file
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/passkey/SinglePasskeyScreen.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/passkey/SinglePasskeyScreen.kt
index 03608a4..ce243b0 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/passkey/SinglePasskeyScreen.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/passkey/SinglePasskeyScreen.kt
@@ -19,11 +19,8 @@
 package com.android.credentialmanager.ui.screens.single.passkey
 
 import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.padding
 import androidx.compose.runtime.Composable
-import androidx.compose.ui.Modifier
 import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.unit.dp
 import com.android.credentialmanager.FlowEngine
 import com.android.credentialmanager.model.get.CredentialEntryInfo
 import com.android.credentialmanager.R
@@ -75,7 +72,6 @@
             }
         },
         columnState = columnState,
-        modifier = Modifier.padding(horizontal = 10.dp)
     ) {
         item {
             val selectEntry = flowEngine.getEntrySelector()
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreen.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreen.kt
index 818723b..5bc4796 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreen.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreen.kt
@@ -19,11 +19,8 @@
 package com.android.credentialmanager.ui.screens.single.password
 
 import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.padding
 import androidx.compose.runtime.Composable
-import androidx.compose.ui.Modifier
 import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.unit.dp
 import com.android.credentialmanager.FlowEngine
 import com.android.credentialmanager.R
 import com.android.credentialmanager.ui.components.PasswordRow
@@ -67,7 +64,6 @@
             )
         },
         columnState = columnState,
-        modifier = Modifier.padding(horizontal = 10.dp)
     ) {
         item {
             Column {
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/signInWithProvider/SignInWithProviderScreen.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/signInWithProvider/SignInWithProviderScreen.kt
index 34d6e97..fd0fc8c 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/signInWithProvider/SignInWithProviderScreen.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/signInWithProvider/SignInWithProviderScreen.kt
@@ -17,10 +17,7 @@
 package com.android.credentialmanager.ui.screens.single.signInWithProvider
 
 import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.padding
 import androidx.compose.runtime.Composable
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.unit.dp
 import com.android.credentialmanager.FlowEngine
 import com.android.credentialmanager.model.get.CredentialEntryInfo
 import com.android.credentialmanager.ui.components.AccountRow
@@ -47,7 +44,6 @@
 fun SignInWithProviderScreen(
     entry: CredentialEntryInfo,
     columnState: ScalingLazyColumnState,
-    modifier: Modifier = Modifier,
     flowEngine: FlowEngine,
 ) {
     SingleAccountScreen(
@@ -72,7 +68,6 @@
             }
         },
         columnState = columnState,
-        modifier = modifier.padding(horizontal = 10.dp)
     ) {
         item {
             val selectEntry = flowEngine.getEntrySelector()
diff --git a/packages/InputDevices/res/raw/keyboard_layout_arabic.kcm b/packages/InputDevices/res/raw/keyboard_layout_arabic.kcm
index 9c2064c..8c6880b 100644
--- a/packages/InputDevices/res/raw/keyboard_layout_arabic.kcm
+++ b/packages/InputDevices/res/raw/keyboard_layout_arabic.kcm
@@ -58,7 +58,8 @@
     label:                              '5'
     base:                               '\u0665'
     capslock:                           '5'
-    shift:                              '%'
+    shift:                              '\u066a'
+    shift+capslock:                     '%'
 }
 
 key 6 {
diff --git a/packages/InputDevices/res/values-af/strings.xml b/packages/InputDevices/res/values-af/strings.xml
index eafe042..cd9d915 100644
--- a/packages/InputDevices/res/values-af/strings.xml
+++ b/packages/InputDevices/res/values-af/strings.xml
@@ -54,8 +54,6 @@
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Thais (Pattachote)"</string>
     <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"Serwies (Latyns)"</string>
     <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"Montenegryns (Latyns)"</string>
-    <!-- no translation found for keyboard_layout_serbian_cyrillic (7013541044323542196) -->
-    <skip />
-    <!-- no translation found for keyboard_layout_montenegrin_cyrillic (2391253952894077421) -->
-    <skip />
+    <string name="keyboard_layout_serbian_cyrillic" msgid="7013541044323542196">"Serwies (Cyrillies)"</string>
+    <string name="keyboard_layout_montenegrin_cyrillic" msgid="2391253952894077421">"Montenegryns (Cyrillies)"</string>
 </resources>
diff --git a/packages/InputDevices/res/values-gl/strings.xml b/packages/InputDevices/res/values-gl/strings.xml
index 4616168..058dba5 100644
--- a/packages/InputDevices/res/values-gl/strings.xml
+++ b/packages/InputDevices/res/values-gl/strings.xml
@@ -54,8 +54,6 @@
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Tailandés (pattachote)"</string>
     <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"Serbio (alfabeto latino)"</string>
     <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"Montenegrino (alfabeto latino)"</string>
-    <!-- no translation found for keyboard_layout_serbian_cyrillic (7013541044323542196) -->
-    <skip />
-    <!-- no translation found for keyboard_layout_montenegrin_cyrillic (2391253952894077421) -->
-    <skip />
+    <string name="keyboard_layout_serbian_cyrillic" msgid="7013541044323542196">"Serbio (cirílico)"</string>
+    <string name="keyboard_layout_montenegrin_cyrillic" msgid="2391253952894077421">"Montenegrino (cirílico)"</string>
 </resources>
diff --git a/packages/PackageInstaller/TEST_MAPPING b/packages/PackageInstaller/TEST_MAPPING
index 76d7ab1..b3fb1e7 100644
--- a/packages/PackageInstaller/TEST_MAPPING
+++ b/packages/PackageInstaller/TEST_MAPPING
@@ -9,6 +9,9 @@
       ]
     },
     {
+      "name": "CtsUpdateOwnershipEnforcementTestCases"
+    },
+    {
       "name": "CtsNoPermissionTestCases"
     },
     {
diff --git a/packages/PackageInstaller/res/values-ar/strings.xml b/packages/PackageInstaller/res/values-ar/strings.xml
index de91c09..a82f982 100644
--- a/packages/PackageInstaller/res/values-ar/strings.xml
+++ b/packages/PackageInstaller/res/values-ar/strings.xml
@@ -66,9 +66,9 @@
     <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"هل تريد أرشفة هذا التطبيق المحفوظ في المساحة الخاصّة؟ سيتم حفظ بياناتك الشخصية."</string>
     <string name="uninstall_application_text_all_users" msgid="575491774380227119">"هل تريد إزالة هذا التطبيق "<b>"لكل"</b>" المستخدمين؟ ستتم إزالة التطبيق وبياناته من "<b>"كل"</b>" المستخدمين على هذا الجهاز."</string>
     <string name="uninstall_application_text_user" msgid="498072714173920526">"هل تريد إزالة هذا التطبيق للمستخدم <xliff:g id="USERNAME">%1$s</xliff:g>؟"</string>
-    <string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"هل تريد إزالة تثبيت هذا التطبيق من ملفك الشخصي للعمل؟"</string>
+    <string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"هل تريد إزالة تثبيت هذا التطبيق من ملف العمل الخاص بك؟"</string>
     <string name="uninstall_update_text" msgid="863648314632448705">"هل تريد استبدال هذا التطبيق بإصدار المصنع؟ ستتم إزالة جميع البيانات."</string>
-    <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"هل تريد إعادة ضبط هذا التطبيق على الإعدادات الأصلية؟ سؤدي ذلك إلى إزالة جميع البيانات، كما سيؤثر على جميع مستخدمي هذا الجهاز، بما في ذلك من لديهم ملفات شخصية للعمل."</string>
+    <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"هل تريد إعادة ضبط هذا التطبيق على الإعدادات الأصلية؟ سؤدي ذلك إلى إزالة جميع البيانات، كما سيؤثر على جميع مستخدمي هذا الجهاز، بما في ذلك من لديهم ملفات للعمل."</string>
     <string name="uninstall_keep_data" msgid="7002379587465487550">"الاحتفاظ بـ <xliff:g id="SIZE">%1$s</xliff:g> من بيانات التطبيق."</string>
     <string name="uninstall_application_text_current_user_clone_profile" msgid="835170400160011636">"هل تريد حذف هذا التطبيق؟"</string>
     <string name="uninstall_application_text_with_clone_instance" msgid="6944473334273349036">"هل تريد إلغاء تثبيت هذا التطبيق؟ سيتم أيضًا حذف نسخة \"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>\" الطبق الأصل."</string>
diff --git a/packages/PackageInstaller/res/values-fr-rCA-feminine/strings.xml b/packages/PackageInstaller/res/values-fr-rCA-feminine/strings.xml
new file mode 100644
index 0000000..19d3ec7
--- /dev/null
+++ b/packages/PackageInstaller/res/values-fr-rCA-feminine/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Cette utilisatrice n\'est pas autorisée à installer des applis"</string>
+    <string name="user_is_not_allowed_dlg_text" msgid="3468447791330611681">"L\'utilisatrice actuelle n\'est pas autorisée à effectuer cette désinstallation."</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-fr-rCA-masculine/strings.xml b/packages/PackageInstaller/res/values-fr-rCA-masculine/strings.xml
new file mode 100644
index 0000000..2c5234b
--- /dev/null
+++ b/packages/PackageInstaller/res/values-fr-rCA-masculine/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Cet utilisateur n\'est pas autorisé à installer des applis"</string>
+    <string name="user_is_not_allowed_dlg_text" msgid="3468447791330611681">"L\'utilisateur actuel n\'est pas autorisé à effectuer cette désinstallation."</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-fr-rCA-neuter/strings.xml b/packages/PackageInstaller/res/values-fr-rCA-neuter/strings.xml
new file mode 100644
index 0000000..6a818b3
--- /dev/null
+++ b/packages/PackageInstaller/res/values-fr-rCA-neuter/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"L\'utilisateur·trice n\'est pas autorisé·e à installer des applis"</string>
+    <string name="user_is_not_allowed_dlg_text" msgid="3468447791330611681">"L\'utilisateur·trice actuel·le n\'est pas autorisé·e à effectuer cette désinstallation."</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-fr-rCA/strings.xml b/packages/PackageInstaller/res/values-fr-rCA/strings.xml
index 2e42339..64022b9 100644
--- a/packages/PackageInstaller/res/values-fr-rCA/strings.xml
+++ b/packages/PackageInstaller/res/values-fr-rCA/strings.xml
@@ -51,7 +51,7 @@
     <string name="out_of_space_dlg_text" msgid="8727714096031856231">"Impossible d\'installer <xliff:g id="APP_NAME">%1$s</xliff:g>. Veuillez libérer de l\'espace, puis réessayer."</string>
     <string name="app_not_found_dlg_title" msgid="5107924008597470285">"Appli non trouvée"</string>
     <string name="app_not_found_dlg_text" msgid="5219983779377811611">"L\'appli ne figure pas dans la liste des applis installées."</string>
-    <string name="user_is_not_allowed_dlg_title" msgid="6915293433252210232">"Non autorisée"</string>
+    <string name="user_is_not_allowed_dlg_title" msgid="6915293433252210232">"Non autorisé"</string>
     <string name="user_is_not_allowed_dlg_text" msgid="3468447791330611681">"L\'utilisateur actuel n\'est pas autorisé à effectuer cette désinstallation."</string>
     <string name="generic_error_dlg_title" msgid="5863195085927067752">"Erreur"</string>
     <string name="generic_error_dlg_text" msgid="5287861443265795232">"L\'appli n\'a pas pu être désinstallée."</string>
@@ -98,9 +98,9 @@
     <string name="untrusted_external_source_warning" product="tv" msgid="7057271609532508035">"À des fins de sécurité, l\'installation d\'applis inconnues provenant de cette source n\'est pas autorisée sur ce téléviseur. Vous pouvez modifier cette option dans les paramètres."</string>
     <string name="untrusted_external_source_warning" product="watch" msgid="7195163388090818636">"À des fins de sécurité, l\'installation d\'applis inconnues provenant de cette source n\'est pas autorisée sur cette montre. Vous pouvez modifier cette option dans les paramètres."</string>
     <string name="untrusted_external_source_warning" product="default" msgid="8444191224459138919">"À des fins de sécurité, l\'installation d\'applis inconnues provenant de cette source n\'est pas autorisée sur ce téléphone. Vous pouvez modifier cette option dans les paramètres."</string>
-    <string name="anonymous_source_warning" product="default" msgid="2784902545920822500">"Votre téléphone et vos données personnelles sont plus vulnérables aux attaques provenant d\'applis inconnues. En installant cette appli, vous acceptez d\'être le seul responsable de tout dommage causé à votre téléphone ou de toute perte de données pouvant découler de l\'utilisation de telles applis."</string>
-    <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Votre tablette et vos données personnelles sont plus vulnérables aux attaques provenant d\'applis inconnues. En installant cette appli, vous acceptez d\'être le seul responsable de tout dommage causé à votre tablette ou de toute perte de données pouvant découler de l\'utilisation de telles applis."</string>
-    <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"Votre téléviseur et vos données personnelles sont plus vulnérables aux attaques d\'applis inconnues. En installant cette appli, vous acceptez d\'être le seul responsable de tout dommage causé à votre téléviseur ou de toute perte de données pouvant découler de son utilisation."</string>
+    <string name="anonymous_source_warning" product="default" msgid="2784902545920822500">"Votre téléphone et vos données personnelles sont plus vulnérables aux attaques provenant d\'applis inconnues. En installant cette appli, vous acceptez d\'être l\'unique responsable de tout dommage causé à votre téléphone ou de toute perte de données pouvant découler de l\'utilisation de telles applis."</string>
+    <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Votre tablette et vos données personnelles sont plus vulnérables aux attaques provenant d\'applis inconnues. En installant cette appli, vous acceptez d\'être l\'unique responsable de tout dommage causé à votre tablette ou de toute perte de données pouvant découler de l\'utilisation de telles applis."</string>
+    <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"Votre téléviseur et vos données personnelles sont plus vulnérables aux attaques d\'applis inconnues. En installant cette appli, vous acceptez d\'être l\'unique responsable de tout dommage causé à votre téléviseur ou de toute perte de données pouvant découler de son utilisation."</string>
     <string name="cloned_app_label" msgid="7503612829833756160">"Clone de <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
     <string name="archiving_app_label" msgid="1127085259724124725">"Archiver <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>?"</string>
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Continuer"</string>
diff --git a/packages/PackageInstaller/res/values-it-feminine/strings.xml b/packages/PackageInstaller/res/values-it-feminine/strings.xml
new file mode 100644
index 0000000..b1c9179
--- /dev/null
+++ b/packages/PackageInstaller/res/values-it-feminine/strings.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"L\'utente non è autorizzata a installare app"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-it-masculine/strings.xml b/packages/PackageInstaller/res/values-it-masculine/strings.xml
new file mode 100644
index 0000000..598d7e6
--- /dev/null
+++ b/packages/PackageInstaller/res/values-it-masculine/strings.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"L\'utente non è autorizzato a installare app"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-it-neuter/strings.xml b/packages/PackageInstaller/res/values-it-neuter/strings.xml
new file mode 100644
index 0000000..ae1b92e
--- /dev/null
+++ b/packages/PackageInstaller/res/values-it-neuter/strings.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"L\'utente non è autorizzatə a installare app"</string>
+</resources>
diff --git a/packages/PackageInstaller/res/values-it/strings.xml b/packages/PackageInstaller/res/values-it/strings.xml
index 20fc299..1561e79 100644
--- a/packages/PackageInstaller/res/values-it/strings.xml
+++ b/packages/PackageInstaller/res/values-it/strings.xml
@@ -42,7 +42,7 @@
     <string name="launch" msgid="3952550563999890101">"Apri"</string>
     <string name="unknown_apps_admin_dlg_text" msgid="4456572224020176095">"L\'amministratore non consente l\'installazione di app ottenute da origini sconosciute"</string>
     <string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Questo utente non può installare app sconosciute"</string>
-    <string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"L\'utente non è autorizzato a installare app"</string>
+    <string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"L\'utente non dispone dell\'autorizzazione a installare app"</string>
     <string name="ok" msgid="7871959885003339302">"OK"</string>
     <string name="archive" msgid="4447791830199354721">"Archivia"</string>
     <string name="update_anyway" msgid="8792432341346261969">"Aggiorna comunque"</string>
diff --git a/packages/PrintSpooler/res/values-es-rUS/strings.xml b/packages/PrintSpooler/res/values-es-rUS/strings.xml
index 476614b..9f88f14 100644
--- a/packages/PrintSpooler/res/values-es-rUS/strings.xml
+++ b/packages/PrintSpooler/res/values-es-rUS/strings.xml
@@ -56,9 +56,9 @@
     <string name="print_select_printer" msgid="7388760939873368698">"Seleccionar impresora"</string>
     <string name="print_forget_printer" msgid="5035287497291910766">"No recordar impresora"</string>
     <plurals name="print_search_result_count_utterance" formatted="false" msgid="6997663738361080868">
-      <item quantity="many">Se encontraron <xliff:g id="COUNT_1">%1$s</xliff:g> impresoras.</item>
-      <item quantity="other">Se encontraron <xliff:g id="COUNT_1">%1$s</xliff:g> impresoras.</item>
-      <item quantity="one">Se encontró <xliff:g id="COUNT_0">%1$s</xliff:g> impresora.</item>
+      <item quantity="many"><xliff:g id="COUNT_1">%1$s</xliff:g> de impresoras encontradas</item>
+      <item quantity="other"><xliff:g id="COUNT_1">%1$s</xliff:g> impresoras encontradas</item>
+      <item quantity="one"><xliff:g id="COUNT_0">%1$s</xliff:g> impresora encontrada</item>
     </plurals>
     <string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
     <string name="printer_info_desc" msgid="7181988788991581654">"Más información sobre esta impresora"</string>
@@ -77,9 +77,9 @@
     <string name="disabled_services_title" msgid="7313253167968363211">"Servicios inhabilitados"</string>
     <string name="all_services_title" msgid="5578662754874906455">"Todos los servicios"</string>
     <plurals name="print_services_recommendation_subtitle" formatted="false" msgid="5678487708807185138">
-      <item quantity="many">Instala para ver <xliff:g id="COUNT_1">%1$s</xliff:g> impresoras</item>
-      <item quantity="other">Instala para ver <xliff:g id="COUNT_1">%1$s</xliff:g> impresoras</item>
-      <item quantity="one">Instala para ver <xliff:g id="COUNT_0">%1$s</xliff:g> impresora</item>
+      <item quantity="many">Instala para descubrir <xliff:g id="COUNT_1">%1$s</xliff:g> de impresoras</item>
+      <item quantity="other">Instala para descubrir <xliff:g id="COUNT_1">%1$s</xliff:g> impresoras</item>
+      <item quantity="one">Instala para descubrir <xliff:g id="COUNT_0">%1$s</xliff:g> impresora</item>
     </plurals>
     <string name="printing_notification_title_template" msgid="295903957762447362">"Imprimiendo <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"Cancelando <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
diff --git a/packages/PrintSpooler/res/values-es/strings.xml b/packages/PrintSpooler/res/values-es/strings.xml
index 507b2a7..104a1bf 100644
--- a/packages/PrintSpooler/res/values-es/strings.xml
+++ b/packages/PrintSpooler/res/values-es/strings.xml
@@ -56,9 +56,9 @@
     <string name="print_select_printer" msgid="7388760939873368698">"Seleccionar impresora"</string>
     <string name="print_forget_printer" msgid="5035287497291910766">"Olvidar impresora"</string>
     <plurals name="print_search_result_count_utterance" formatted="false" msgid="6997663738361080868">
-      <item quantity="many">Se han encontrado <xliff:g id="COUNT_1">%1$s</xliff:g> impresoras</item>
-      <item quantity="other">Se han encontrado <xliff:g id="COUNT_1">%1$s</xliff:g> impresoras</item>
-      <item quantity="one">Se ha encontrado <xliff:g id="COUNT_0">%1$s</xliff:g> impresora</item>
+      <item quantity="many"><xliff:g id="COUNT_1">%1$s</xliff:g> impresoras encontradas</item>
+      <item quantity="other"><xliff:g id="COUNT_1">%1$s</xliff:g> impresoras encontradas</item>
+      <item quantity="one"><xliff:g id="COUNT_0">%1$s</xliff:g> impresora encontrada</item>
     </plurals>
     <string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
     <string name="printer_info_desc" msgid="7181988788991581654">"Más información sobre esta impresora"</string>
diff --git a/packages/PrintSpooler/res/values-fr-rCA/strings.xml b/packages/PrintSpooler/res/values-fr-rCA/strings.xml
index c2b82af..fb88557 100644
--- a/packages/PrintSpooler/res/values-fr-rCA/strings.xml
+++ b/packages/PrintSpooler/res/values-fr-rCA/strings.xml
@@ -56,9 +56,9 @@
     <string name="print_select_printer" msgid="7388760939873368698">"Sélectionner une imprimante"</string>
     <string name="print_forget_printer" msgid="5035287497291910766">"Supprimer l\'imprimante"</string>
     <plurals name="print_search_result_count_utterance" formatted="false" msgid="6997663738361080868">
-      <item quantity="one"><xliff:g id="COUNT_1">%1$s</xliff:g> imprimante trouvée</item>
-      <item quantity="many"><xliff:g id="COUNT_1">%1$s</xliff:g> imprimante trouvées</item>
-      <item quantity="other"><xliff:g id="COUNT_1">%1$s</xliff:g> imprimante trouvées</item>
+      <item quantity="one"><xliff:g id="COUNT_1">%1$s</xliff:g> imprimante trouvée</item>
+      <item quantity="many"><xliff:g id="COUNT_1">%1$s</xliff:g> d\'imprimantes trouvées</item>
+      <item quantity="other"><xliff:g id="COUNT_1">%1$s</xliff:g> imprimantes trouvées</item>
     </plurals>
     <string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
     <string name="printer_info_desc" msgid="7181988788991581654">"Plus d\'information sur cette imprimante"</string>
@@ -96,7 +96,7 @@
     <item msgid="2762241247228983754">"Couleur"</item>
   </string-array>
   <string-array name="duplex_mode_labels">
-    <item msgid="3882302912790928315">"Aucune"</item>
+    <item msgid="3882302912790928315">"Aucun"</item>
     <item msgid="7296563835355641719">"Bord long"</item>
     <item msgid="79513688117503758">"Bord court"</item>
   </string-array>
diff --git a/packages/PrintSpooler/res/values-it/strings.xml b/packages/PrintSpooler/res/values-it/strings.xml
index 2a64d3d..fc03e64 100644
--- a/packages/PrintSpooler/res/values-it/strings.xml
+++ b/packages/PrintSpooler/res/values-it/strings.xml
@@ -56,7 +56,7 @@
     <string name="print_select_printer" msgid="7388760939873368698">"Seleziona stampante"</string>
     <string name="print_forget_printer" msgid="5035287497291910766">"Elimina stampante"</string>
     <plurals name="print_search_result_count_utterance" formatted="false" msgid="6997663738361080868">
-      <item quantity="many"><xliff:g id="COUNT_1">%1$s</xliff:g> stampanti trovate</item>
+      <item quantity="many"><xliff:g id="COUNT_1">%1$s</xliff:g> di stampanti trovate</item>
       <item quantity="other"><xliff:g id="COUNT_1">%1$s</xliff:g> stampanti trovate</item>
       <item quantity="one"><xliff:g id="COUNT_0">%1$s</xliff:g> stampante trovata</item>
     </plurals>
@@ -77,9 +77,9 @@
     <string name="disabled_services_title" msgid="7313253167968363211">"Servizi disattivati"</string>
     <string name="all_services_title" msgid="5578662754874906455">"Tutti i servizi"</string>
     <plurals name="print_services_recommendation_subtitle" formatted="false" msgid="5678487708807185138">
-      <item quantity="many">Installa per rilevare <xliff:g id="COUNT_1">%1$s</xliff:g> stampanti</item>
-      <item quantity="other">Installa per rilevare <xliff:g id="COUNT_1">%1$s</xliff:g> stampanti</item>
-      <item quantity="one">Installa per rilevare <xliff:g id="COUNT_0">%1$s</xliff:g> stampante</item>
+      <item quantity="many">Installa per individuare <xliff:g id="COUNT_1">%1$s</xliff:g> di stampanti</item>
+      <item quantity="other">Installa per individuare <xliff:g id="COUNT_1">%1$s</xliff:g> stampanti</item>
+      <item quantity="one">Installa per individuare <xliff:g id="COUNT_0">%1$s</xliff:g> stampante</item>
     </plurals>
     <string name="printing_notification_title_template" msgid="295903957762447362">"Stampa di <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"Annullamento di <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -96,7 +96,7 @@
     <item msgid="2762241247228983754">"A colori"</item>
   </string-array>
   <string-array name="duplex_mode_labels">
-    <item msgid="3882302912790928315">"Nessuno"</item>
+    <item msgid="3882302912790928315">"Nessuna"</item>
     <item msgid="7296563835355641719">"Lato lungo"</item>
     <item msgid="79513688117503758">"Lato corto"</item>
   </string-array>
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/EdgeToEdgeUtils.java b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/EdgeToEdgeUtils.java
index 6e53012..062e9b8 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/EdgeToEdgeUtils.java
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/EdgeToEdgeUtils.java
@@ -36,7 +36,7 @@
      * Enable Edge to Edge and handle overlaps using insets. It should be called before
      * setContentView.
      */
-    static void enable(@NonNull ComponentActivity activity) {
+    public static void enable(@NonNull ComponentActivity activity) {
         if (Build.VERSION.SDK_INT < Build.VERSION_CODES.VANILLA_ICE_CREAM) {
             return;
         }
diff --git a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/BackupRestoreStorageManager.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/BackupRestoreStorageManager.kt
index b4a9172..ce02404 100644
--- a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/BackupRestoreStorageManager.kt
+++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/BackupRestoreStorageManager.kt
@@ -129,7 +129,7 @@
             }
         }
 
-        override fun onChanged(reason: Int) = onKeyChanged(null, reason)
+        override fun onChanged(observable: Observable, reason: Int) = onKeyChanged(null, reason)
 
         override fun onKeyChanged(key: Any?, reason: Int) {
             notifyBackupManager(key, reason)
diff --git a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyedObserver.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyedObserver.kt
index ede7c63..4ce1d37 100644
--- a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyedObserver.kt
+++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyedObserver.kt
@@ -103,7 +103,7 @@
 }
 
 /** A thread safe implementation of [KeyedObservable]. */
-class KeyedDataObservable<K> : KeyedObservable<K> {
+open class KeyedDataObservable<K> : KeyedObservable<K> {
     // Instead of @GuardedBy("this"), guarded by itself because KeyedDataObservable object could be
     // synchronized outside by the holder
     @GuardedBy("itself") private val observers = WeakHashMap<KeyedObserver<K?>, Executor>()
diff --git a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/ObservableBackupRestoreStorage.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/ObservableBackupRestoreStorage.kt
index 0e399c0..300d240 100644
--- a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/ObservableBackupRestoreStorage.kt
+++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/ObservableBackupRestoreStorage.kt
@@ -21,5 +21,7 @@
  *
  * This class provides the [Observable] implementations on top of [DataObservable] by delegation.
  */
-abstract class ObservableBackupRestoreStorage :
-    BackupRestoreStorage(), Observable by DataObservable()
+abstract class ObservableBackupRestoreStorage : BackupRestoreStorage(), ObservableDelegation {
+
+    final override val observableDelegate: Observable = DataObservable(this)
+}
diff --git a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/Observer.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/Observer.kt
index 98d0f6e..6af3d1c 100644
--- a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/Observer.kt
+++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/Observer.kt
@@ -32,10 +32,11 @@
      *
      * This callback will run in the given [Executor] when observer is added.
      *
+     * @param observable observable of the change
      * @param reason the reason of change
      * @see [Observable.addObserver] for the notices.
      */
-    fun onChanged(reason: Int)
+    fun onChanged(observable: Observable, reason: Int)
 }
 
 /** An observable object allows to observe change with [Observer]. */
@@ -68,8 +69,21 @@
     fun notifyChange(reason: Int)
 }
 
+/** Delegation of [Observable]. */
+interface ObservableDelegation : Observable {
+    /** [Observable] to delegate. */
+    val observableDelegate: Observable
+
+    override fun addObserver(observer: Observer, executor: Executor) =
+        observableDelegate.addObserver(observer, executor)
+
+    override fun removeObserver(observer: Observer) = observableDelegate.removeObserver(observer)
+
+    override fun notifyChange(reason: Int) = observableDelegate.notifyChange(reason)
+}
+
 /** A thread safe implementation of [Observable]. */
-class DataObservable : Observable {
+class DataObservable(private val observable: Observable) : Observable {
     // Instead of @GuardedBy("this"), guarded by itself because DataObservable object could be
     // synchronized outside by the holder
     @GuardedBy("itself") private val observers = WeakHashMap<Observer, Executor>()
@@ -90,7 +104,7 @@
         val entries = synchronized(observers) { observers.entries.toTypedArray() }
         for (entry in entries) {
             val observer = entry.key // avoid reference "entry"
-            entry.value.execute { observer.onChanged(reason) }
+            entry.value.execute { observer.onChanged(observable, reason) }
         }
     }
 }
diff --git a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SharedPreferencesObservable.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SharedPreferencesObservable.kt
new file mode 100644
index 0000000..e70ec5b
--- /dev/null
+++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SharedPreferencesObservable.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.datastore
+
+import android.content.SharedPreferences
+
+/** [SharedPreferences] based [KeyedDataObservable]. */
+class SharedPreferencesObservable(private val sharedPreferences: SharedPreferences) :
+    KeyedDataObservable<String>(), AutoCloseable {
+
+    private val listener = createSharedPreferenceListener()
+
+    init {
+        sharedPreferences.registerOnSharedPreferenceChangeListener(listener)
+    }
+
+    override fun close() {
+        sharedPreferences.unregisterOnSharedPreferenceChangeListener(listener)
+    }
+}
+
+/** Creates [SharedPreferences.OnSharedPreferenceChangeListener] for [KeyedObservable]. */
+internal fun KeyedObservable<String>.createSharedPreferenceListener() =
+    SharedPreferences.OnSharedPreferenceChangeListener { _, key ->
+        if (key != null) {
+            notifyChange(key, DataChangeReason.UPDATE)
+        } else {
+            // On Android >= R, SharedPreferences.Editor.clear() will trigger this case
+            notifyChange(DataChangeReason.DELETE)
+        }
+    }
diff --git a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SharedPreferencesStorage.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SharedPreferencesStorage.kt
index 20a95d7..0ca91cd 100644
--- a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SharedPreferencesStorage.kt
+++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SharedPreferencesStorage.kt
@@ -80,15 +80,7 @@
             return context.getSharedPreferences(intermediateName, Context.MODE_MULTI_PROCESS)
         }
 
-    private val sharedPreferencesListener =
-        SharedPreferences.OnSharedPreferenceChangeListener { _, key ->
-            if (key != null) {
-                notifyChange(key, DataChangeReason.UPDATE)
-            } else {
-                // On Android >= R, SharedPreferences.Editor.clear() will trigger this case
-                notifyChange(DataChangeReason.DELETE)
-            }
-        }
+    private val sharedPreferencesListener = createSharedPreferenceListener()
 
     init {
         // listener is weakly referenced, so unregister is optional
@@ -191,8 +183,7 @@
                 else -> {
                     Log.e(
                         LOG_TAG,
-                        "[$name] $operation $key=$value, unknown type: ${value?.javaClass}"
-                    )
+                        "[$name] $operation $key=$value, unknown type: ${value?.javaClass}")
                 }
             }
         }
diff --git a/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/BackupRestoreStorageManagerTest.kt b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/BackupRestoreStorageManagerTest.kt
index 19c574a..97b473c 100644
--- a/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/BackupRestoreStorageManagerTest.kt
+++ b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/BackupRestoreStorageManagerTest.kt
@@ -159,7 +159,7 @@
 
         verify(keyedObserver).onKeyChanged("key", DataChangeReason.RESTORE)
         verify(anyKeyObserver).onKeyChanged(null, DataChangeReason.RESTORE)
-        verify(observer).onChanged(DataChangeReason.RESTORE)
+        verify(observer).onChanged(fileStorage, DataChangeReason.RESTORE)
         if (isRobolectric()) {
             Shadows.shadowOf(BackupManager(application)).apply {
                 assertThat(isDataChanged).isFalse()
@@ -187,7 +187,7 @@
         }
 
         fileStorage.notifyChange(DataChangeReason.UPDATE)
-        verify(observer).onChanged(DataChangeReason.UPDATE)
+        verify(observer).onChanged(fileStorage, DataChangeReason.UPDATE)
         verify(keyedObserver, never()).onKeyChanged(any(), any())
         verify(anyKeyObserver, never()).onKeyChanged(any(), any())
         reset(observer)
@@ -197,7 +197,7 @@
         }
 
         keyedStorage.notifyChange("key", DataChangeReason.DELETE)
-        verify(observer, never()).onChanged(any())
+        verify(observer, never()).onChanged(any(), any())
         verify(keyedObserver).onKeyChanged("key", DataChangeReason.DELETE)
         verify(anyKeyObserver).onKeyChanged("key", DataChangeReason.DELETE)
         backupManager?.apply {
@@ -209,7 +209,7 @@
         // backup manager is not notified for restore event
         fileStorage.notifyChange(DataChangeReason.RESTORE)
         keyedStorage.notifyChange("key", DataChangeReason.RESTORE)
-        verify(observer).onChanged(DataChangeReason.RESTORE)
+        verify(observer).onChanged(fileStorage, DataChangeReason.RESTORE)
         verify(keyedObserver).onKeyChanged("key", DataChangeReason.RESTORE)
         verify(anyKeyObserver).onKeyChanged("key", DataChangeReason.RESTORE)
         backupManager?.apply {
@@ -225,7 +225,10 @@
     }
 
     private class FileStorage(override val name: String) :
-        BackupRestoreFileStorage(getApplicationContext(), "file"), Observable by DataObservable()
+        BackupRestoreFileStorage(getApplicationContext(), "file"), ObservableDelegation {
+
+        override val observableDelegate: Observable = DataObservable(this)
+    }
 
     private class DummyBackupAgentHelper : BackupAgentHelper() {
         val backupHelpers = mutableMapOf<String, BackupHelper>()
diff --git a/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/ObserverTest.kt b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/ObserverTest.kt
index 5d0303c..bd114d1 100644
--- a/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/ObserverTest.kt
+++ b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/ObserverTest.kt
@@ -24,6 +24,7 @@
 import org.junit.Assert.assertThrows
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.mockito.kotlin.any
 import org.mockito.kotlin.mock
 import org.mockito.kotlin.never
 import org.mockito.kotlin.reset
@@ -33,10 +34,11 @@
 class ObserverTest {
     private val observer1 = mock<Observer>()
     private val observer2 = mock<Observer>()
+    private val originalObservable = mock<Observable>()
 
     private val executor1: Executor = MoreExecutors.directExecutor()
     private val executor2: Executor = MoreExecutors.newDirectExecutorService()
-    private val observable = DataObservable()
+    private val observable = DataObservable(originalObservable)
 
     @Test
     fun addObserver_sameExecutor() {
@@ -55,7 +57,7 @@
     @Test
     fun addObserver_weaklyReferenced() {
         val counter = AtomicInteger()
-        var observer: Observer? = Observer { counter.incrementAndGet() }
+        var observer: Observer? = Observer { _, _ -> counter.incrementAndGet() }
         observable.addObserver(observer!!, executor1)
 
         observable.notifyChange(DataChangeReason.UPDATE)
@@ -77,21 +79,21 @@
 
         observable.notifyChange(DataChangeReason.DELETE)
 
-        verify(observer1).onChanged(DataChangeReason.DELETE)
-        verify(observer2).onChanged(DataChangeReason.DELETE)
+        verify(observer1).onChanged(originalObservable, DataChangeReason.DELETE)
+        verify(observer2).onChanged(originalObservable, DataChangeReason.DELETE)
 
         reset(observer1, observer2)
         observable.removeObserver(observer2)
 
         observable.notifyChange(DataChangeReason.UPDATE)
-        verify(observer1).onChanged(DataChangeReason.UPDATE)
-        verify(observer2, never()).onChanged(DataChangeReason.UPDATE)
+        verify(observer1).onChanged(originalObservable, DataChangeReason.UPDATE)
+        verify(observer2, never()).onChanged(any(), any())
     }
 
     @Test
     fun notifyChange_addObserverWithinCallback() {
         // ConcurrentModificationException is raised if it is not implemented correctly
-        val observer = Observer { observable.addObserver(observer1, executor1) }
+        val observer = Observer { _, _ -> observable.addObserver(observer1, executor1) }
         observable.addObserver(observer, executor1)
         observable.notifyChange(DataChangeReason.UPDATE)
         observable.removeObserver(observer)
diff --git a/packages/SettingsLib/ProfileSelector/res/values-fr-rCA/strings.xml b/packages/SettingsLib/ProfileSelector/res/values-fr-rCA/strings.xml
index c9ba591..0e74e4d 100644
--- a/packages/SettingsLib/ProfileSelector/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsLib/ProfileSelector/res/values-fr-rCA/strings.xml
@@ -18,6 +18,6 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="settingslib_category_personal" msgid="1142302328104700620">"Personnel"</string>
-    <string name="settingslib_category_work" msgid="4867750733682444676">"Professionnel"</string>
+    <string name="settingslib_category_work" msgid="4867750733682444676">"Profil professionnel"</string>
     <string name="settingslib_category_private" msgid="5039276873477591386">"Privé"</string>
 </resources>
diff --git a/packages/SettingsLib/SearchWidget/res/values-fa/strings.xml b/packages/SettingsLib/SearchWidget/res/values-fa/strings.xml
index 2c9aaa5..2babdd8 100644
--- a/packages/SettingsLib/SearchWidget/res/values-fa/strings.xml
+++ b/packages/SettingsLib/SearchWidget/res/values-fa/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/Spa/build.gradle.kts b/packages/SettingsLib/Spa/build.gradle.kts
index d6345ce..f36344a 100644
--- a/packages/SettingsLib/Spa/build.gradle.kts
+++ b/packages/SettingsLib/Spa/build.gradle.kts
@@ -29,7 +29,7 @@
 
 allprojects {
     extra["androidTop"] = androidTop
-    extra["jetpackComposeVersion"] = "1.7.0-beta02"
+    extra["jetpackComposeVersion"] = "1.7.0-beta05"
 }
 
 subprojects {
diff --git a/packages/SettingsLib/Spa/gradle/libs.versions.toml b/packages/SettingsLib/Spa/gradle/libs.versions.toml
index a842009..1cca73a 100644
--- a/packages/SettingsLib/Spa/gradle/libs.versions.toml
+++ b/packages/SettingsLib/Spa/gradle/libs.versions.toml
@@ -15,7 +15,7 @@
 #
 
 [versions]
-agp = "8.5.0"
+agp = "8.5.1"
 compose-compiler = "1.5.11"
 dexmaker-mockito = "2.28.3"
 jvm = "17"
diff --git a/packages/SettingsLib/Spa/gradle/wrapper/gradle-8.8-bin.zip b/packages/SettingsLib/Spa/gradle/wrapper/gradle-8.8-bin.zip
deleted file mode 100644
index 77e6ad3..0000000
--- a/packages/SettingsLib/Spa/gradle/wrapper/gradle-8.8-bin.zip
+++ /dev/null
Binary files differ
diff --git a/packages/SettingsLib/Spa/gradle/wrapper/gradle-8.9-bin.zip b/packages/SettingsLib/Spa/gradle/wrapper/gradle-8.9-bin.zip
new file mode 100644
index 0000000..9a97e46
--- /dev/null
+++ b/packages/SettingsLib/Spa/gradle/wrapper/gradle-8.9-bin.zip
Binary files differ
diff --git a/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.jar b/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.jar
index e644113..2c35211 100644
--- a/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.jar
+++ b/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.properties b/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.properties
index 91d2a3a..9f29c77 100644
--- a/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.properties
+++ b/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.properties
@@ -16,6 +16,6 @@
 
 distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
-distributionUrl=gradle-8.8-bin.zip
+distributionUrl=gradle-8.9-bin.zip
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
diff --git a/packages/SettingsLib/Spa/gradlew b/packages/SettingsLib/Spa/gradlew
index b740cf1..f5feea6 100755
--- a/packages/SettingsLib/Spa/gradlew
+++ b/packages/SettingsLib/Spa/gradlew
@@ -15,6 +15,8 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 #
+# SPDX-License-Identifier: Apache-2.0
+#
 
 ##############################################################################
 #
@@ -84,7 +86,8 @@
 # shellcheck disable=SC2034
 APP_BASE_NAME=${0##*/}
 # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
-APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
+APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
+' "$PWD" ) || exit
 
 # Use the maximum available, or set MAX_FD != -1 to use that value.
 MAX_FD=maximum
diff --git a/packages/SettingsLib/Spa/spa/build.gradle.kts b/packages/SettingsLib/Spa/spa/build.gradle.kts
index 492d7c0..ce3d96e 100644
--- a/packages/SettingsLib/Spa/spa/build.gradle.kts
+++ b/packages/SettingsLib/Spa/spa/build.gradle.kts
@@ -53,14 +53,14 @@
 
 dependencies {
     api(project(":SettingsLibColor"))
-    api("androidx.appcompat:appcompat:1.7.0-rc01")
-    api("androidx.compose.material3:material3:1.3.0-beta02")
+    api("androidx.appcompat:appcompat:1.7.0")
+    api("androidx.compose.material3:material3:1.3.0-beta04")
     api("androidx.compose.material:material-icons-extended:$jetpackComposeVersion")
     api("androidx.compose.runtime:runtime-livedata:$jetpackComposeVersion")
     api("androidx.compose.ui:ui-tooling-preview:$jetpackComposeVersion")
     api("androidx.lifecycle:lifecycle-livedata-ktx")
     api("androidx.lifecycle:lifecycle-runtime-compose")
-    api("androidx.navigation:navigation-compose:2.8.0-beta02")
+    api("androidx.navigation:navigation-compose:2.8.0-beta05")
     api("com.github.PhilJay:MPAndroidChart:v3.1.0-alpha")
     api("com.google.android.material:material:1.11.0")
     debugApi("androidx.compose.ui:ui-tooling:$jetpackComposeVersion")
diff --git a/packages/SettingsLib/SpaPrivileged/res/values-fr-rCA/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values-fr-rCA/strings.xml
index 7e73c48..433d6d5 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values-fr-rCA/strings.xml
@@ -20,7 +20,7 @@
     <string name="no_applications" msgid="5800789569715871963">"Aucune appli"</string>
     <string name="menu_show_system" msgid="906304605807554788">"Afficher le système"</string>
     <string name="menu_hide_system" msgid="374571689914923020">"Masquer le système"</string>
-    <string name="app_permission_summary_allowed" msgid="6115213465364138103">"Autorisé"</string>
+    <string name="app_permission_summary_allowed" msgid="6115213465364138103">"Autorisée"</string>
     <string name="app_permission_summary_not_allowed" msgid="58396132188553920">"Non autorisée"</string>
     <string name="version_text" msgid="4001669804596458577">"version <xliff:g id="VERSION_NUM">%1$s</xliff:g>"</string>
     <string name="cloned_app_info_label" msgid="1765651167024478391">"Clone de <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/aconfig/settingslib.aconfig b/packages/SettingsLib/aconfig/settingslib.aconfig
index 403e219..754d9423 100644
--- a/packages/SettingsLib/aconfig/settingslib.aconfig
+++ b/packages/SettingsLib/aconfig/settingslib.aconfig
@@ -9,16 +9,6 @@
 }
 
 flag {
-    name: "enable_cached_bluetooth_device_dedup"
-    namespace: "bluetooth"
-    description: "Enable dedup in CachedBluetoothDevice"
-    bug: "319197962"
-    metadata {
-        purpose: PURPOSE_BUGFIX
-    }
-}
-
-flag {
    name: "bluetooth_qs_tile_dialog_auto_on_toggle"
    namespace: "bluetooth"
    description: "Displays the auto on toggle in the bluetooth QS tile dialog"
@@ -112,3 +102,10 @@
       purpose: PURPOSE_BUGFIX
     }
 }
+
+flag {
+    name: "settings_catalyst"
+    namespace: "android_settings"
+    description: "Settings catalyst project migration"
+    bug: "323791114"
+}
\ No newline at end of file
diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml
index 193d0bc..e612229 100644
--- a/packages/SettingsLib/res/values-af/strings.xml
+++ b/packages/SettingsLib/res/values-af/strings.xml
@@ -430,8 +430,7 @@
     <string name="force_allow_on_external_summary" msgid="8525425782530728238">"Maak dat enige program na eksterne berging geskryf kan word, ongeag manifeswaardes"</string>
     <string name="force_resizable_activities" msgid="7143612144399959606">"Dwing aktiwiteite om verstelbaar te wees"</string>
     <string name="force_resizable_activities_summary" msgid="2490382056981583062">"Maak die groottes van alle aktiwiteite verstelbaar vir veelvuldige vensters, ongeag manifeswaardes."</string>
-    <string name="enable_freeform_support" msgid="8409932201445109106">"Aktiveer vryevormvensters (vorige)"</string>
-    <string name="enable_freeform_support_summary" msgid="2242481082356957934">"Aktiveer steun vir eksperimentele vorige vryevormvensters."</string>
+    <string name="enable_freeform_support" msgid="7599125687603914253">"Aktiveer vormvrye vensters"</string>
     <string name="local_backup_password_title" msgid="4631017948933578709">"Werkskerm-rugsteunwagwoord"</string>
     <string name="local_backup_password_summary_none" msgid="7646898032616361714">"Volle rekenaarrugsteune word nie tans beskerm nie"</string>
     <string name="local_backup_password_summary_change" msgid="1707357670383995567">"Tik om die wagwoord vir volledige rekenaarrugsteune te verander of te verwyder"</string>
@@ -496,7 +495,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> oor tot vol"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> oor tot vol"</string>
-    <string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – Laaiproses word geoptimeer"</string>
+    <!-- no translation found for power_charging_limited (4144004473976005214) -->
+    <skip />
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – Laai tans"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATUS">%2$s</xliff:g> – Volgelaai teen <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> – Volgelaai teen <xliff:g id="TIME">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml
index 606a01c..7556de6 100644
--- a/packages/SettingsLib/res/values-am/strings.xml
+++ b/packages/SettingsLib/res/values-am/strings.xml
@@ -430,8 +430,7 @@
     <string name="force_allow_on_external_summary" msgid="8525425782530728238">"የዝርዝር ሰነዶች እሴቶች ግምት ውስጥ ሳያስገባ ማንኛውም መተግበሪያ ወደ ውጫዊ ማከማቻው ለመጻፍ ብቁ ያደርጋል"</string>
     <string name="force_resizable_activities" msgid="7143612144399959606">"እንቅስቃሴዎች ዳግመኛ እንዲመጣጠኑ አስገድድ"</string>
     <string name="force_resizable_activities_summary" msgid="2490382056981583062">"የዝርዝር ሰነድ እሴቶች ምንም ይሁኑ ምን ለበርካታ መስኮቶች ሁሉንም እንቅስቃሴዎች መጠናቸው የሚቀየሩ እንዲሆኑ ያደርጋቸዋል።"</string>
-    <string name="enable_freeform_support" msgid="8409932201445109106">"ነፃ ቅርጽ መስኮቶች (የቆየ) ያንቁ"</string>
-    <string name="enable_freeform_support_summary" msgid="2242481082356957934">"የሙከራ የቆዩ የነፃ ቅርጽ መስኮቶች ድጋፍን ያንቁ።"</string>
+    <string name="enable_freeform_support" msgid="7599125687603914253">"የነጻ ቅርጽ መስኮቶችን ያንቁ"</string>
     <string name="local_backup_password_title" msgid="4631017948933578709">"የዴስክቶፕ መጠባበቂያ ይለፍ ቃል"</string>
     <string name="local_backup_password_summary_none" msgid="7646898032616361714">"ዴስክቶፕ ሙሉ ምትኬዎች በአሁኑ ሰዓት አልተጠበቁም"</string>
     <string name="local_backup_password_summary_change" msgid="1707357670383995567">"የዴስክቶፕ ሙሉ ምትኬዎች የይለፍ ቃሉን ለመለወጥ ወይም ለማስወገድ ነካ ያድርጉ"</string>
@@ -496,7 +495,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"እስኪሞላ ድረስ <xliff:g id="TIME">%1$s</xliff:g> ይቀራል"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - እስኪሞላ ድረስ <xliff:g id="TIME">%2$s</xliff:g> ይቀራል"</string>
-    <string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - ኃይል መሙላት እንዲተባ ተደርጓል"</string>
+    <!-- no translation found for power_charging_limited (4144004473976005214) -->
+    <skip />
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - ኃይል በመሙላት ላይ"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - እስከ <xliff:g id="TIME">%3$s</xliff:g> ድረስ ይሞላል"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - እስከ <xliff:g id="TIME">%2$s</xliff:g> ሙሉ ለሙሉ ይሞላል"</string>
diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml
index 3da23c8..99abb2e 100644
--- a/packages/SettingsLib/res/values-ar/strings.xml
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -408,18 +408,12 @@
     <string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"‏السماح بتحميل طبقات تصحيح أخطاء GPU لتطبيقات تصحيح الأخطاء"</string>
     <string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"تفعيل التسجيل المطوَّل للمورّد"</string>
     <string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"تضمين سجلات المورّدين الإضافية الخاصة بالجهاز في تقارير الخطأ، والتي قد تحتوي على معلومات شخصية و/أو تستهلك المزيد من شحن البطارية و/أو تستهلك المزيد من مساحة التخزين"</string>
-    <!-- no translation found for enable_verbose_vendor_logging_checkbox (3864578373293835530) -->
-    <skip />
-    <!-- no translation found for verbose_vendor_logging_notification_title (6811217272559843592) -->
-    <skip />
-    <!-- no translation found for verbose_vendor_logging_notification_summary (5226524769774370942) -->
-    <skip />
-    <!-- no translation found for verbose_vendor_logging_notification_action (1190831050259046071) -->
-    <skip />
-    <!-- no translation found for verbose_vendor_logging_preference_summary_will_disable (6175431593394522553) -->
-    <skip />
-    <!-- no translation found for verbose_vendor_logging_preference_summary_on (9017757242481762036) -->
-    <skip />
+    <string name="enable_verbose_vendor_logging_checkbox" msgid="3864578373293835530">"الإيقاف بعد يوم واحد"</string>
+    <string name="verbose_vendor_logging_notification_title" msgid="6811217272559843592">"انتهى التسجيل المطوّل للمورِّد"</string>
+    <string name="verbose_vendor_logging_notification_summary" msgid="5226524769774370942">"تم التفعيل لمدة يوم واحد"</string>
+    <string name="verbose_vendor_logging_notification_action" msgid="1190831050259046071">"التفعيل لمدة يوم واحد إضافي"</string>
+    <string name="verbose_vendor_logging_preference_summary_will_disable" msgid="6175431593394522553">"سيتم الإيقاف بعد يوم واحد"</string>
+    <string name="verbose_vendor_logging_preference_summary_on" msgid="9017757242481762036">"تم التفعيل لأجل غير مسمى"</string>
     <string name="window_animation_scale_title" msgid="5236381298376812508">"حجم الرسوم المتحركة للنافذة"</string>
     <string name="transition_animation_scale_title" msgid="1278477690695439337">"حجم الرسوم المتحركة للنقل"</string>
     <string name="animator_duration_scale_title" msgid="7082913931326085176">"طول مدة الرسوم المتحركة"</string>
@@ -436,8 +430,7 @@
     <string name="force_allow_on_external_summary" msgid="8525425782530728238">"تأهيل أي تطبيق بحيث تتم كتابته على وحدة تخزين خارجية، بغض النظر عن قيم البيان"</string>
     <string name="force_resizable_activities" msgid="7143612144399959606">"فرض إمكانية تغيير حجم الأنشطة"</string>
     <string name="force_resizable_activities_summary" msgid="2490382056981583062">"السماح بتغيير حجم جميع الأنشطة لتناسب تعدد النوافذ، بغض النظر عن قيم البيان"</string>
-    <string name="enable_freeform_support" msgid="8409932201445109106">"تفعيل النوافذ الحرة (القديمة)"</string>
-    <string name="enable_freeform_support_summary" msgid="2242481082356957934">"تفعيل عرض النوافذ الحرة التجريبية القديمة"</string>
+    <string name="enable_freeform_support" msgid="7599125687603914253">"تفعيل النوافذ الحرة"</string>
     <string name="local_backup_password_title" msgid="4631017948933578709">"كلمة مرور احتياطية للكمبيوتر"</string>
     <string name="local_backup_password_summary_none" msgid="7646898032616361714">"النُسخ الاحتياطية الكاملة لسطح المكتب غير محمية في الوقت الحالي"</string>
     <string name="local_backup_password_summary_change" msgid="1707357670383995567">"انقر لتغيير كلمة مرور النسخ الاحتياطية الكاملة لسطح المكتب أو إزالتها."</string>
@@ -502,7 +495,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"يتبقّى <xliff:g id="TIME">%1$s</xliff:g> حتى اكتمال شحن البطارية"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - يتبقّى <xliff:g id="TIME">%2$s</xliff:g> حتى اكتمال شحن البطارية."</string>
-    <string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - تم تحسين الشحن"</string>
+    <!-- no translation found for power_charging_limited (4144004473976005214) -->
+    <skip />
     <string name="power_charging_future_paused" msgid="1809543660923642799">"‫<xliff:g id="LEVEL">%1$s</xliff:g>: جارٍ الشحن"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"‏‫‎<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - سيكتمل الشحن بحلول <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"‏‫‎<xliff:g id="LEVEL">%1$s</xliff:g> - سيكتمل الشحن بحلول <xliff:g id="TIME">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml
index da17f80..38600f4 100644
--- a/packages/SettingsLib/res/values-as/strings.xml
+++ b/packages/SettingsLib/res/values-as/strings.xml
@@ -430,8 +430,7 @@
     <string name="force_allow_on_external_summary" msgid="8525425782530728238">"মেনিফেষ্টৰ মান যিয়েই নহওক, বাহ্যিক ষ্ট’ৰেজত লিখিবলৈ যিকোনো এপক উপযুক্ত কৰি তোলে"</string>
     <string name="force_resizable_activities" msgid="7143612144399959606">"বলেৰে কাৰ্যকলাপসমূহৰ আকাৰ সলনি কৰিব পৰা কৰক"</string>
     <string name="force_resizable_activities_summary" msgid="2490382056981583062">"মেনিফেষ্টৰ মান যিয়েই নহওক, মাল্টি-ৱিণ্ডৰ বাবে আটাইবোৰ কাৰ্যকলাপৰ আকাৰ সলনি কৰিব পৰা কৰক।"</string>
-    <string name="enable_freeform_support" msgid="8409932201445109106">"ফ্ৰীফৰ্ম ৱিণ্ড’ সক্ষম কৰ (লিগেচী)"</string>
-    <string name="enable_freeform_support_summary" msgid="2242481082356957934">"পৰীক্ষামূলক লিগেচী ফ্ৰীফৰ্ম ৱিণ্ড’ৰ সমৰ্থন সক্ষম কৰক।"</string>
+    <string name="enable_freeform_support" msgid="7599125687603914253">"ফ্ৰিফৰ্ম ৱিণ্ড\'জ সক্ষম কৰক"</string>
     <string name="local_backup_password_title" msgid="4631017948933578709">"ডেস্কটপ বেকআপ পাছৱৰ্ড"</string>
     <string name="local_backup_password_summary_none" msgid="7646898032616361714">"ডেস্কটপৰ পূৰ্ণ বেকআপ এতিয়ালৈকে সংৰক্ষিত অৱস্থাত নাই"</string>
     <string name="local_backup_password_summary_change" msgid="1707357670383995567">"ডেস্কটপ সম্পূৰ্ণ বেকআপৰ বাবে পাছৱৰ্ডটো সলনি কৰিবলৈ বা আঁতৰাবলৈ টিপক"</string>
@@ -496,7 +495,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"সম্পূৰ্ণ হ’বলৈ <xliff:g id="TIME">%1$s</xliff:g> বাকী আছে"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"সম্পূৰ্ণ হ’বলৈ <xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> বাকী আছে"</string>
-    <string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - চাৰ্জিং অপ্টিমাইজ কৰা হৈছে"</string>
+    <!-- no translation found for power_charging_limited (4144004473976005214) -->
+    <skip />
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> ‑ চাৰ্জ হৈ আছে"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - <xliff:g id="TIME">%3$s</xliff:g>ৰ ভিতৰত সম্পূৰ্ণ হ’ব"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>ৰ ভিতৰত সম্পূৰ্ণৰূপে চাৰ্জ হ’ব"</string>
diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml
index 5ae1c6e..d41cf0e 100644
--- a/packages/SettingsLib/res/values-az/strings.xml
+++ b/packages/SettingsLib/res/values-az/strings.xml
@@ -430,8 +430,7 @@
     <string name="force_allow_on_external_summary" msgid="8525425782530728238">"Manifest dəyərindən asılı olmayaraq tətbiqlərin xarici daşıyıcılarda saxlanmasına icazə verilsin"</string>
     <string name="force_resizable_activities" msgid="7143612144399959606">"Çoxpəncərəli rejimdə ölçü dəyişdirilməsi"</string>
     <string name="force_resizable_activities_summary" msgid="2490382056981583062">"Manifest dəyərindən asılı olmayaraq çoxpəncərəli rejimdə pəncərə ölçüsünün dəyişdirilməsinə icazə verilsin"</string>
-    <string name="enable_freeform_support" msgid="8409932201445109106">"İxtiyari formada pəncərələri (köhnə) aktivləşdirin"</string>
-    <string name="enable_freeform_support_summary" msgid="2242481082356957934">"Təcrübi köhnə ixtiyari formada pəncərələr üçün dəstəyi aktivləşdirin."</string>
+    <string name="enable_freeform_support" msgid="7599125687603914253">"İxtiyari formada pəncərə yaradılsın"</string>
     <string name="local_backup_password_title" msgid="4631017948933578709">"Masaüstü rezerv parolu"</string>
     <string name="local_backup_password_summary_none" msgid="7646898032616361714">"Masaüstü tam rezervlər hazırda qorunmayıblar."</string>
     <string name="local_backup_password_summary_change" msgid="1707357670383995567">"Masaüstünün tam rezerv kopyalanması üçün parolu dəyişmək və ya silmək üçün basın"</string>
@@ -496,7 +495,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"Tam şarj edilənədək <xliff:g id="TIME">%1$s</xliff:g> qalıb"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - tam şarj edilənədək <xliff:g id="TIME">%2$s</xliff:g> qalıb"</string>
-    <string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Şarj optimallaşdırılıb"</string>
+    <!-- no translation found for power_charging_limited (4144004473976005214) -->
+    <skip />
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - Şarj edilir"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - <xliff:g id="TIME">%3$s</xliff:g> radələrinə qədər tam dolacaq"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> radələrinə qədər tam dolacaq"</string>
diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
index 2b92c52..c576af5 100644
--- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
+++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
@@ -430,8 +430,7 @@
     <string name="force_allow_on_external_summary" msgid="8525425782530728238">"Omogućava upisivanje svih aplikacija u spoljnu memoriju, bez obzira na vrednosti manifesta"</string>
     <string name="force_resizable_activities" msgid="7143612144399959606">"Prinudno omogući promenu veličine aktivnosti"</string>
     <string name="force_resizable_activities_summary" msgid="2490382056981583062">"Omogućava promenu veličine svih aktivnosti za režim sa više prozora, bez obzira na vrednosti manifesta."</string>
-    <string name="enable_freeform_support" msgid="8409932201445109106">"Omogući prozore proizvoljnog formata (zastarelo)"</string>
-    <string name="enable_freeform_support_summary" msgid="2242481082356957934">"Omogućava podršku za zastarele eksperimentalne prozore proizvoljnog formata."</string>
+    <string name="enable_freeform_support" msgid="7599125687603914253">"Omogući prozore proizvoljnog formata"</string>
     <string name="local_backup_password_title" msgid="4631017948933578709">"Lozinka rezervne kopije za računar"</string>
     <string name="local_backup_password_summary_none" msgid="7646898032616361714">"Rezervne kopije čitavog sistema trenutno nisu zaštićene"</string>
     <string name="local_backup_password_summary_change" msgid="1707357670383995567">"Dodirnite da biste promenili ili uklonili lozinku za pravljenje rezervnih kopija čitavog sistema na računaru"</string>
@@ -496,7 +495,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> do kraja punjenja"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do kraja punjenja"</string>
-    <string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – punjenje je optimizovano"</string>
+    <!-- no translation found for power_charging_limited (4144004473976005214) -->
+    <skip />
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – Punjenje"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATUS">%2$s</xliff:g> – Potpuno napunjeno do <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> – Potpuno napunjeno do <xliff:g id="TIME">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml
index e676fdf..9fd2af3 100644
--- a/packages/SettingsLib/res/values-be/strings.xml
+++ b/packages/SettingsLib/res/values-be/strings.xml
@@ -430,8 +430,7 @@
     <string name="force_allow_on_external_summary" msgid="8525425782530728238">"Робіць любую праграму даступнай для запісу ў знешняе сховішча, незалежна ад значэнняў маніфеста"</string>
     <string name="force_resizable_activities" msgid="7143612144399959606">"Зрабіць вокны дзеянняў даступнымі для змены памеру"</string>
     <string name="force_resizable_activities_summary" msgid="2490382056981583062">"Зрабіць усе віды дзейнасці даступнымі для змены памеру ў рэжыме некалькіх вокнаў, незалежна ад значэнняў маніфеста."</string>
-    <string name="enable_freeform_support" msgid="8409932201445109106">"Уключыць адвольную форму вокнаў (устарэлая налада)"</string>
-    <string name="enable_freeform_support_summary" msgid="2242481082356957934">"Уключыць падтрымку для эксперыментальнай адвольнай формы вокнаў (устарэлая налада)."</string>
+    <string name="enable_freeform_support" msgid="7599125687603914253">"Уключыць адвольную форму вокнаў"</string>
     <string name="local_backup_password_title" msgid="4631017948933578709">"Пароль для рэз. копіі ПК"</string>
     <string name="local_backup_password_summary_none" msgid="7646898032616361714">"Поўнае рэзервовае капіраванне працоўнага стала зараз не абаронена"</string>
     <string name="local_backup_password_summary_change" msgid="1707357670383995567">"Краніце, каб змяніць або выдаліць пароль для поўнага рэзервовага капіравання працоўнага стала"</string>
@@ -496,7 +495,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"Да поўнай зарадкі засталося <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – да поўнай зарадкі засталося: <xliff:g id="TIME">%2$s</xliff:g>"</string>
-    <string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – Зарадка аптымізавана"</string>
+    <!-- no translation found for power_charging_limited (4144004473976005214) -->
+    <skip />
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – зараджаецца"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATUS">%2$s</xliff:g> – Прылада зарадзіцца поўнасцю да <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> – Прылада зарадзіцца поўнасцю да <xliff:g id="TIME">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml
index c64e2ea..462535b 100644
--- a/packages/SettingsLib/res/values-bg/strings.xml
+++ b/packages/SettingsLib/res/values-bg/strings.xml
@@ -430,8 +430,7 @@
     <string name="force_allow_on_external_summary" msgid="8525425782530728238">"Всички приложения ще отговарят на условията да бъдат записвани във външното хранилище независимо от стойностите в манифеста"</string>
     <string name="force_resizable_activities" msgid="7143612144399959606">"Възможност за преоразмеряване на активностите"</string>
     <string name="force_resizable_activities_summary" msgid="2490382056981583062">"Дава възможност за преоразмеряване на всички активности в режима за няколко прозореца независимо от стойностите в манифеста."</string>
-    <string name="enable_freeform_support" msgid="8409932201445109106">"Актив. на прозорците в свободна форма (наследени)"</string>
-    <string name="enable_freeform_support_summary" msgid="2242481082356957934">"Активиране на поддръжката за експерименталните наследени прозорци в свободна форма."</string>
+    <string name="enable_freeform_support" msgid="7599125687603914253">"Активиране на прозорците в свободна форма"</string>
     <string name="local_backup_password_title" msgid="4631017948933578709">"Парола за резервни копия"</string>
     <string name="local_backup_password_summary_none" msgid="7646898032616361714">"Понастоящем пълните резервни копия за настолен компютър не са защитени"</string>
     <string name="local_backup_password_summary_change" msgid="1707357670383995567">"Докоснете, за да промените или премахнете паролата за пълни резервни копия на настолния компютър"</string>
@@ -496,7 +495,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"Оставащо време до пълно зареждане: <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – Оставащо време до пълно зареждане: <xliff:g id="TIME">%2$s</xliff:g>"</string>
-    <string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – Зареждането е оптимизирано"</string>
+    <!-- no translation found for power_charging_limited (4144004473976005214) -->
+    <skip />
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – зарежда се"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATUS">%2$s</xliff:g> – ще се зареди напълно до <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> – ще се зареди напълно до <xliff:g id="TIME">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml
index 531795a..cd53b67 100644
--- a/packages/SettingsLib/res/values-bn/strings.xml
+++ b/packages/SettingsLib/res/values-bn/strings.xml
@@ -430,8 +430,7 @@
     <string name="force_allow_on_external_summary" msgid="8525425782530728238">"ম্যানিফেস্ট মানের নির্বিশেষে যেকোনও অ্যাপকে এক্সটারনাল স্টোরেজে ইনস্টল করার উপযুক্ত বানায়"</string>
     <string name="force_resizable_activities" msgid="7143612144399959606">"আকার পরিবর্তনযোগ্য করার জন্য ক্রিয়াকলাপগুলিকে জোর করুন"</string>
     <string name="force_resizable_activities_summary" msgid="2490382056981583062">"ম্যানিফেস্ট মানগুলির নির্বিশেষে মাল্টি-উইন্ডোর জন্য সমস্ত ক্রিয়াকলাপগুলির আকার পরিবর্তনযোগ্য করুন৷"</string>
-    <string name="enable_freeform_support" msgid="8409932201445109106">"ফ্রিফর্ম উইন্ডো (লিগ্যাসি) চালু করুন"</string>
-    <string name="enable_freeform_support_summary" msgid="2242481082356957934">"এক্সপেরিমেন্টাল ফ্রিফর্ম উইন্ডোর জন্য সহায়তা চালু করুন।"</string>
+    <string name="enable_freeform_support" msgid="7599125687603914253">"ফ্রি-ফর্ম উইন্ডো চালু করুন"</string>
     <string name="local_backup_password_title" msgid="4631017948933578709">"ডেস্কটপ ব্যাকআপ পাসওয়ার্ড"</string>
     <string name="local_backup_password_summary_none" msgid="7646898032616361714">"ডেস্কটপ পূর্ণ ব্যাকআপ বর্তমানে সুরক্ষিত নয়"</string>
     <string name="local_backup_password_summary_change" msgid="1707357670383995567">"ডেস্কটপের সম্পূর্ণ ব্যাকআপের পাসওয়ার্ডটি পরিবর্তন করতে বা মুছে ফেলতে আলতো চাপুন"</string>
@@ -496,7 +495,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g>-এ ব্যাটারি পুরো চার্জ হয়ে যাবে"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>-এ ব্যাটারি পুরো চার্জ হয়ে যাবে"</string>
-    <string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - চার্জিং অপ্টিমাইজ করা হয়েছে"</string>
+    <!-- no translation found for power_charging_limited (4144004473976005214) -->
+    <skip />
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - চার্জ করা হচ্ছে"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - <xliff:g id="TIME">%3$s</xliff:g>-এর মধ্যে পুরো চার্জ হয়ে যাবে"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>-এর মধ্যে পুরো চার্জ হয়ে যাবে"</string>
diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml
index 93b4ffb..5e35f1a 100644
--- a/packages/SettingsLib/res/values-bs/strings.xml
+++ b/packages/SettingsLib/res/values-bs/strings.xml
@@ -430,8 +430,7 @@
     <string name="force_allow_on_external_summary" msgid="8525425782530728238">"Omogućava upisivanje svih aplikacija u vanjsku pohranu, bez obzira na prikazane vrijednosti"</string>
     <string name="force_resizable_activities" msgid="7143612144399959606">"Nametni aktivnostima mijenjanje veličina"</string>
     <string name="force_resizable_activities_summary" msgid="2490382056981583062">"Omogućava mijenjanje veličine svih aktivnosti za prikaz s više prozora, bez obzira na prikazane vrijednosti"</string>
-    <string name="enable_freeform_support" msgid="8409932201445109106">"Omogući prilagodljive prozore (staro)"</string>
-    <string name="enable_freeform_support_summary" msgid="2242481082356957934">"Omogućite podršku za eksperimentalne stare prilagodljive prozore."</string>
+    <string name="enable_freeform_support" msgid="7599125687603914253">"Omogući prozore nepravilnih oblika"</string>
     <string name="local_backup_password_title" msgid="4631017948933578709">"Lozinka sigurnosne kopije za računar"</string>
     <string name="local_backup_password_summary_none" msgid="7646898032616361714">"Potpune sigurnosne kopije za računare trenutno nisu zaštićene"</string>
     <string name="local_backup_password_summary_change" msgid="1707357670383995567">"Dodirnite da promijenite ili uklonite lozinku za potpune rezervne kopije s radne površine"</string>
@@ -496,7 +495,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> do potpune napunjenosti"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do potpune napunjenosti"</string>
-    <string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – punjenje je optimizirano"</string>
+    <!-- no translation found for power_charging_limited (4144004473976005214) -->
+    <skip />
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – punjenje"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATUS">%2$s</xliff:g> – Napunit će se do <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> – Potpuno napunjeno do <xliff:g id="TIME">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml
index 0e6b3d8..18e8720 100644
--- a/packages/SettingsLib/res/values-ca/strings.xml
+++ b/packages/SettingsLib/res/values-ca/strings.xml
@@ -430,8 +430,7 @@
     <string name="force_allow_on_external_summary" msgid="8525425782530728238">"Permet que qualsevol aplicació es pugui escriure en un dispositiu d’emmagatzematge extern, independentment dels valors del manifest"</string>
     <string name="force_resizable_activities" msgid="7143612144399959606">"Força l\'ajust de la mida de les activitats"</string>
     <string name="force_resizable_activities_summary" msgid="2490382056981583062">"Permet ajustar la mida de totes les activitats per al mode multifinestra, independentment dels valors del manifest"</string>
-    <string name="enable_freeform_support" msgid="8409932201445109106">"Activa les finestres amb format lliure (heretat)"</string>
-    <string name="enable_freeform_support_summary" msgid="2242481082356957934">"Activa la compatibilitat amb les finestres de format lliure heretades i experimentals."</string>
+    <string name="enable_freeform_support" msgid="7599125687603914253">"Activa les finestres amb format lliure"</string>
     <string name="local_backup_password_title" msgid="4631017948933578709">"Contrasenya per a còpies d\'ordinador"</string>
     <string name="local_backup_password_summary_none" msgid="7646898032616361714">"Les còpies de seguretat completes d\'ordinador no estan protegides"</string>
     <string name="local_backup_password_summary_change" msgid="1707357670383995567">"Toca per canviar o suprimir la contrasenya per a les còpies de seguretat completes de l\'ordinador"</string>
@@ -496,7 +495,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> per completar la càrrega"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="TIME">%2$s</xliff:g> per completar la càrrega"</string>
-    <string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g>: càrrega optimitzada"</string>
+    <!-- no translation found for power_charging_limited (4144004473976005214) -->
+    <skip />
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g>: s\'està carregant"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - Càrrega completa a les <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - Càrrega completa a les <xliff:g id="TIME">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml
index b04c908..7ca98f7 100644
--- a/packages/SettingsLib/res/values-cs/strings.xml
+++ b/packages/SettingsLib/res/values-cs/strings.xml
@@ -430,8 +430,7 @@
     <string name="force_allow_on_external_summary" msgid="8525425782530728238">"Každou aplikaci bude možné zapsat do externího úložiště, bez ohledu na hodnoty manifestu"</string>
     <string name="force_resizable_activities" msgid="7143612144399959606">"Vynutit možnost změny velikosti aktivit"</string>
     <string name="force_resizable_activities_summary" msgid="2490382056981583062">"Umožní změnu velikosti všech aktivit na několik oken (bez ohledu na hodnoty manifestu)"</string>
-    <string name="enable_freeform_support" msgid="8409932201445109106">"Aktivovat okna s volným tvarem (starší nastavení)"</string>
-    <string name="enable_freeform_support_summary" msgid="2242481082356957934">"Aktivuje podporu starších experimentálních oken s volným tvarem."</string>
+    <string name="enable_freeform_support" msgid="7599125687603914253">"Aktivovat okna s volným tvarem"</string>
     <string name="local_backup_password_title" msgid="4631017948933578709">"Heslo pro zálohy v počítači"</string>
     <string name="local_backup_password_summary_none" msgid="7646898032616361714">"Úplné zálohy v počítači nejsou v současné době chráněny"</string>
     <string name="local_backup_password_summary_change" msgid="1707357670383995567">"Tuto možnost vyberte, chcete-li změnit nebo odebrat heslo pro úplné zálohy do počítače"</string>
@@ -496,7 +495,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> do úplného nabití"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do úplného nabití"</string>
-    <string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – optimalizované nabíjení"</string>
+    <!-- no translation found for power_charging_limited (4144004473976005214) -->
+    <skip />
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – Nabíjení"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATUS">%2$s</xliff:g> – Úplné nabití: <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> – Úplné nabití: <xliff:g id="TIME">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml
index 6e526db..5564722 100644
--- a/packages/SettingsLib/res/values-da/strings.xml
+++ b/packages/SettingsLib/res/values-da/strings.xml
@@ -430,8 +430,7 @@
     <string name="force_allow_on_external_summary" msgid="8525425782530728238">"Gør det muligt at overføre enhver app til et eksternt lager uafhængigt af manifestværdier"</string>
     <string name="force_resizable_activities" msgid="7143612144399959606">"Gennemtving, at aktiviteter kan tilpasses"</string>
     <string name="force_resizable_activities_summary" msgid="2490382056981583062">"Tillad, at alle aktiviteter kan tilpasses flere vinduer uafhængigt af manifestværdier"</string>
-    <string name="enable_freeform_support" msgid="8409932201445109106">"Aktivér vinduer i frit format (forældet)"</string>
-    <string name="enable_freeform_support_summary" msgid="2242481082356957934">"Aktivér understøttelse af eksperimentelle forældede vinduer i frit format."</string>
+    <string name="enable_freeform_support" msgid="7599125687603914253">"Aktivér vinduer i frit format"</string>
     <string name="local_backup_password_title" msgid="4631017948933578709">"Kode til lokal sikkerhedskopi"</string>
     <string name="local_backup_password_summary_none" msgid="7646898032616361714">"Lokale komplette backups er i øjeblikket ikke beskyttet"</string>
     <string name="local_backup_password_summary_change" msgid="1707357670383995567">"Tryk for at skifte eller fjerne adgangskoden til fuld lokal sikkerhedskopiering"</string>
@@ -496,7 +495,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"Fuldt opladet om <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – fuldt opladet om <xliff:g id="TIME">%2$s</xliff:g>"</string>
-    <string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – opladning er optimeret"</string>
+    <!-- no translation found for power_charging_limited (4144004473976005214) -->
+    <skip />
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – oplades"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATUS">%2$s</xliff:g> – Opladet senest kl. <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> – Fuldt opladet senest kl. <xliff:g id="TIME">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml
index 504a9da..2fd683d 100644
--- a/packages/SettingsLib/res/values-de/strings.xml
+++ b/packages/SettingsLib/res/values-de/strings.xml
@@ -430,8 +430,7 @@
     <string name="force_allow_on_external_summary" msgid="8525425782530728238">"Jede App kann, ungeachtet der Manifestwerte, in den externen Speicher geschrieben werden"</string>
     <string name="force_resizable_activities" msgid="7143612144399959606">"Aktivitätengröße darf immer angepasst werden"</string>
     <string name="force_resizable_activities_summary" msgid="2490382056981583062">"Die Größe aller Aktivitäten darf, ungeachtet der Manifestwerte, für die Mehrfensterdarstellung angepasst werden"</string>
-    <string name="enable_freeform_support" msgid="8409932201445109106">"Freiform-Fenster zulassen (Legacy)"</string>
-    <string name="enable_freeform_support_summary" msgid="2242481082356957934">"Unterstützung für experimentelle Legacy-Freiform-Fenster aktivieren."</string>
+    <string name="enable_freeform_support" msgid="7599125687603914253">"Freiform-Fenster zulassen"</string>
     <string name="local_backup_password_title" msgid="4631017948933578709">"Passwort für Desktop-Sicherung"</string>
     <string name="local_backup_password_summary_none" msgid="7646898032616361714">"Vollständige Desktop-Sicherungen sind momentan nicht passwortgeschützt"</string>
     <string name="local_backup_password_summary_change" msgid="1707357670383995567">"Zum Ändern oder Entfernen des Passworts für vollständige Desktop-Sicherungen tippen"</string>
@@ -496,7 +495,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"Voll in <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – voll in <xliff:g id="TIME">%2$s</xliff:g>"</string>
-    <string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – Laden wird optimiert"</string>
+    <!-- no translation found for power_charging_limited (4144004473976005214) -->
+    <skip />
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – Wird geladen"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATUS">%2$s</xliff:g> – Vollständig geladen in <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> – Vollständig geladen in <xliff:g id="TIME">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml
index cad445f..4fdea21 100644
--- a/packages/SettingsLib/res/values-el/strings.xml
+++ b/packages/SettingsLib/res/values-el/strings.xml
@@ -430,8 +430,7 @@
     <string name="force_allow_on_external_summary" msgid="8525425782530728238">"Κάνει κάθε εφαρμογή κατάλληλη για εγγραφή σε εξωτερικό αποθηκευτικό χώρο, ανεξάρτητα από τις τιμές του μανιφέστου"</string>
     <string name="force_resizable_activities" msgid="7143612144399959606">"Αναγκαστική δυνατότητα αλλαγής μεγέθους δραστηριοτήτων"</string>
     <string name="force_resizable_activities_summary" msgid="2490382056981583062">"Να έχουν όλες οι δραστηριότητες δυνατότητα αλλαγής μεγέθους για την προβολή πολλαπλών παραθύρων, ανεξάρτητα από τις τιμές του μανιφέστου."</string>
-    <string name="enable_freeform_support" msgid="8409932201445109106">"Ενεργ. παραθύρων ελεύθερης μορφής (παλαιού τύπου)"</string>
-    <string name="enable_freeform_support_summary" msgid="2242481082356957934">"Ενεργοποίηση υποστήριξης για πειραματικά παράθυρα ελεύθερης μορφής παλαιού τύπου."</string>
+    <string name="enable_freeform_support" msgid="7599125687603914253">"Ενεργοποίηση παραθύρων ελεύθερης μορφής"</string>
     <string name="local_backup_password_title" msgid="4631017948933578709">"Εφ/κός κωδικός desktop"</string>
     <string name="local_backup_password_summary_none" msgid="7646898032616361714">"Τα πλήρη αντίγραφα ασφαλείας επιφάνειας εργασίας δεν προστατεύονται αυτή τη στιγμή"</string>
     <string name="local_backup_password_summary_change" msgid="1707357670383995567">"Πατήστε για αλλαγή ή κατάργηση του κωδικού πρόσβασης για τα πλήρη αντίγραφα ασφαλείας επιφάνειας εργασίας"</string>
@@ -496,7 +495,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"Απομένουν <xliff:g id="TIME">%1$s</xliff:g> για πλήρη φόρτιση"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - Απομένουν <xliff:g id="TIME">%2$s</xliff:g> για πλήρη φόρτιση"</string>
-    <string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Η φόρτιση βελτιστοποιήθηκε"</string>
+    <!-- no translation found for power_charging_limited (4144004473976005214) -->
+    <skip />
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> ‑ Φόρτιση"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - Πλήρης φόρτιση στις <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - Πλήρης φόρτιση στις <xliff:g id="TIME">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml
index ad43664..bcffe2c 100644
--- a/packages/SettingsLib/res/values-en-rAU/strings.xml
+++ b/packages/SettingsLib/res/values-en-rAU/strings.xml
@@ -430,8 +430,7 @@
     <string name="force_allow_on_external_summary" msgid="8525425782530728238">"Makes any app eligible to be written to external storage, regardless of manifest values"</string>
     <string name="force_resizable_activities" msgid="7143612144399959606">"Force activities to be resizeable"</string>
     <string name="force_resizable_activities_summary" msgid="2490382056981583062">"Make all activities resizeable for multi-window, regardless of manifest values."</string>
-    <string name="enable_freeform_support" msgid="8409932201445109106">"Enable freeform windows (legacy)"</string>
-    <string name="enable_freeform_support_summary" msgid="2242481082356957934">"Enable support for experimental legacy freeform windows."</string>
+    <string name="enable_freeform_support" msgid="7599125687603914253">"Enable freeform windows"</string>
     <string name="local_backup_password_title" msgid="4631017948933578709">"Desktop backup password"</string>
     <string name="local_backup_password_summary_none" msgid="7646898032616361714">"Desktop full backups aren\'t currently protected"</string>
     <string name="local_backup_password_summary_change" msgid="1707357670383995567">"Tap to change or remove the password for desktop full backups"</string>
@@ -496,7 +495,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> left until full"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> left until full"</string>
-    <string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Charging optimised"</string>
+    <!-- no translation found for power_charging_limited (4144004473976005214) -->
+    <skip />
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – charging"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATUS">%2$s</xliff:g> – Full by <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - Fully charged by <xliff:g id="TIME">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-en-rCA/strings.xml b/packages/SettingsLib/res/values-en-rCA/strings.xml
index a77d6c6..df54ac11 100644
--- a/packages/SettingsLib/res/values-en-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-en-rCA/strings.xml
@@ -430,8 +430,7 @@
     <string name="force_allow_on_external_summary" msgid="8525425782530728238">"Makes any app eligible to be written to external storage, regardless of manifest values"</string>
     <string name="force_resizable_activities" msgid="7143612144399959606">"Force activities to be resizable"</string>
     <string name="force_resizable_activities_summary" msgid="2490382056981583062">"Make all activities resizable for multi-window, regardless of manifest values."</string>
-    <string name="enable_freeform_support" msgid="8409932201445109106">"Enable freeform windows (legacy)"</string>
-    <string name="enable_freeform_support_summary" msgid="2242481082356957934">"Enable support for experimental legacy freeform windows."</string>
+    <string name="enable_freeform_support" msgid="7599125687603914253">"Enable freeform windows"</string>
     <string name="local_backup_password_title" msgid="4631017948933578709">"Desktop backup password"</string>
     <string name="local_backup_password_summary_none" msgid="7646898032616361714">"Desktop full backups aren’t currently protected"</string>
     <string name="local_backup_password_summary_change" msgid="1707357670383995567">"Tap to change or remove the password for desktop full backups"</string>
@@ -496,7 +495,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> left until full"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> left until full"</string>
-    <string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Charging optimized"</string>
+    <!-- no translation found for power_charging_limited (4144004473976005214) -->
+    <skip />
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - Charging"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - Full by <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - Fully charged by <xliff:g id="TIME">%2$s</xliff:g>"</string>
@@ -661,7 +661,7 @@
     <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Exit guest mode?"</string>
     <string name="guest_exit_dialog_message" msgid="1743218864242719783">"This will delete apps and data from the current guest session"</string>
     <string name="grant_admin" msgid="4323199171790522574">"Yes, make them an admin"</string>
-    <string name="not_grant_admin" msgid="3557849576157702485">"No, dont make them an admin"</string>
+    <string name="not_grant_admin" msgid="3557849576157702485">"No, don\'t make them an admin"</string>
     <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Exit"</string>
     <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Save guest activity?"</string>
     <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"You can save activity from the current session or delete all apps and data"</string>
diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml
index ad43664..bcffe2c 100644
--- a/packages/SettingsLib/res/values-en-rGB/strings.xml
+++ b/packages/SettingsLib/res/values-en-rGB/strings.xml
@@ -430,8 +430,7 @@
     <string name="force_allow_on_external_summary" msgid="8525425782530728238">"Makes any app eligible to be written to external storage, regardless of manifest values"</string>
     <string name="force_resizable_activities" msgid="7143612144399959606">"Force activities to be resizeable"</string>
     <string name="force_resizable_activities_summary" msgid="2490382056981583062">"Make all activities resizeable for multi-window, regardless of manifest values."</string>
-    <string name="enable_freeform_support" msgid="8409932201445109106">"Enable freeform windows (legacy)"</string>
-    <string name="enable_freeform_support_summary" msgid="2242481082356957934">"Enable support for experimental legacy freeform windows."</string>
+    <string name="enable_freeform_support" msgid="7599125687603914253">"Enable freeform windows"</string>
     <string name="local_backup_password_title" msgid="4631017948933578709">"Desktop backup password"</string>
     <string name="local_backup_password_summary_none" msgid="7646898032616361714">"Desktop full backups aren\'t currently protected"</string>
     <string name="local_backup_password_summary_change" msgid="1707357670383995567">"Tap to change or remove the password for desktop full backups"</string>
@@ -496,7 +495,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> left until full"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> left until full"</string>
-    <string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Charging optimised"</string>
+    <!-- no translation found for power_charging_limited (4144004473976005214) -->
+    <skip />
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – charging"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATUS">%2$s</xliff:g> – Full by <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - Fully charged by <xliff:g id="TIME">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml
index ad43664..bcffe2c 100644
--- a/packages/SettingsLib/res/values-en-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-en-rIN/strings.xml
@@ -430,8 +430,7 @@
     <string name="force_allow_on_external_summary" msgid="8525425782530728238">"Makes any app eligible to be written to external storage, regardless of manifest values"</string>
     <string name="force_resizable_activities" msgid="7143612144399959606">"Force activities to be resizeable"</string>
     <string name="force_resizable_activities_summary" msgid="2490382056981583062">"Make all activities resizeable for multi-window, regardless of manifest values."</string>
-    <string name="enable_freeform_support" msgid="8409932201445109106">"Enable freeform windows (legacy)"</string>
-    <string name="enable_freeform_support_summary" msgid="2242481082356957934">"Enable support for experimental legacy freeform windows."</string>
+    <string name="enable_freeform_support" msgid="7599125687603914253">"Enable freeform windows"</string>
     <string name="local_backup_password_title" msgid="4631017948933578709">"Desktop backup password"</string>
     <string name="local_backup_password_summary_none" msgid="7646898032616361714">"Desktop full backups aren\'t currently protected"</string>
     <string name="local_backup_password_summary_change" msgid="1707357670383995567">"Tap to change or remove the password for desktop full backups"</string>
@@ -496,7 +495,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> left until full"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> left until full"</string>
-    <string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Charging optimised"</string>
+    <!-- no translation found for power_charging_limited (4144004473976005214) -->
+    <skip />
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – charging"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATUS">%2$s</xliff:g> – Full by <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - Fully charged by <xliff:g id="TIME">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-en-rXC/strings.xml b/packages/SettingsLib/res/values-en-rXC/strings.xml
index 8d6520f..fc4c8a0 100644
--- a/packages/SettingsLib/res/values-en-rXC/strings.xml
+++ b/packages/SettingsLib/res/values-en-rXC/strings.xml
@@ -430,8 +430,7 @@
     <string name="force_allow_on_external_summary" msgid="8525425782530728238">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‏‎‏‏‎‎‏‎‏‎‎‎‎‎‏‏‎‎‏‎‏‏‎‎‏‎‏‎‎‎‏‎‏‏‏‏‎‏‎‏‏‎‎‏‏‎‏‏‎‎‎‎‏‎‎‏‎‏‏‏‎‎Makes any app eligible to be written to external storage, regardless of manifest values‎‏‎‎‏‎"</string>
     <string name="force_resizable_activities" msgid="7143612144399959606">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‎‎‎‏‏‎‎‏‎‎‎‏‏‎‎‏‏‎‏‎‏‎‏‏‎‎‏‎‏‏‎‎‏‏‎‎‎‎‎‏‎‎‎‎‎‎‏‎‎‎‏‏‎‎‎‏‏‎‏‏‎‎Force activities to be resizable‎‏‎‎‏‎"</string>
     <string name="force_resizable_activities_summary" msgid="2490382056981583062">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‎‎‎‏‎‏‎‎‎‏‏‏‏‏‎‎‏‏‏‎‏‎‏‎‎‏‎‎‏‎‏‎‎‎‏‎‏‎‏‏‎‎‏‎‎‏‏‎‏‎‏‎‎‏‏‎‏‎‏‏‎‎Make all activities resizable for multi-window, regardless of manifest values.‎‏‎‎‏‎"</string>
-    <string name="enable_freeform_support" msgid="8409932201445109106">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‏‎‏‎‎‏‎‏‏‎‏‏‎‎‎‎‏‎‏‎‎‏‏‎‎‎‏‏‏‏‎‎‎‏‏‎‎‎‏‎‎‎‎‎‏‎‎‏‎‏‏‎‏‎‏‏‏‎‎‏‎‎Enable freeform windows (legacy)‎‏‎‎‏‎"</string>
-    <string name="enable_freeform_support_summary" msgid="2242481082356957934">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‏‏‏‏‏‏‎‎‎‏‏‏‏‎‏‏‏‎‎‏‎‎‏‎‏‎‏‎‎‏‏‏‎‎‏‎‎‏‎‏‏‎‏‎‏‏‏‎‎‎‎‎‏‎‏‏‏‎‏‏‏‎‎Enable support for experimental legacy freeform windows.‎‏‎‎‏‎"</string>
+    <string name="enable_freeform_support" msgid="7599125687603914253">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‎‏‎‎‏‎‏‏‏‎‏‎‏‏‎‎‎‎‏‎‎‏‎‎‎‏‏‎‏‎‎‎‎‏‏‎‏‎‎‏‎‏‏‎‏‎‏‏‎‎‏‏‎‎‎‎‎‏‏‎‏‎Enable freeform windows‎‏‎‎‏‎"</string>
     <string name="local_backup_password_title" msgid="4631017948933578709">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‎‎‎‎‎‎‏‎‎‎‏‎‎‏‎‏‎‏‏‏‎‎‏‎‎‏‎‎‏‏‏‎‏‎‎‏‎‏‎‏‎‎‏‎‎‏‏‎‏‏‏‏‏‏‏‎‏‎‏‎‏‎Desktop backup password‎‏‎‎‏‎"</string>
     <string name="local_backup_password_summary_none" msgid="7646898032616361714">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‎‏‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‏‏‎‏‎‎‏‏‏‏‏‎‏‏‏‎‎‎‏‏‏‎‏‏‏‎‏‏‎‏‎‎‏‏‏‎‏‏‏‏‎‎‏‎‎Desktop full backups aren’t currently protected‎‏‎‎‏‎"</string>
     <string name="local_backup_password_summary_change" msgid="1707357670383995567">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‏‏‎‏‏‏‏‎‏‏‎‎‎‏‏‏‎‎‎‎‎‎‏‏‎‎‎‎‎‏‎‏‎‏‎‏‏‏‏‏‎‎‎‏‏‎‏‏‎‎‏‎‏‎‏‎‏‎‏‏‏‏‎Tap to change or remove the password for desktop full backups‎‏‎‎‏‎"</string>
@@ -496,7 +495,8 @@
     <string name="power_charging" msgid="6727132649743436802">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‏‏‏‎‏‎‏‎‏‏‎‏‏‏‎‎‏‎‎‏‏‏‎‎‎‎‎‎‎‏‏‎‎‏‎‎‎‏‏‏‎‎‎‏‏‏‏‎‎‏‏‎‎‎‎‎‎‎‎‏‎‎‎‏‎‎‏‏‎<xliff:g id="LEVEL">%1$s</xliff:g>‎‏‎‎‏‏‏‎ - ‎‏‎‎‏‏‎<xliff:g id="STATE">%2$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‏‎‎‎‎‎‎‏‏‎‏‎‎‎‎‎‎‏‎‏‎‏‎‏‏‎‏‏‏‎‎‏‎‏‎‎‎‎‏‏‏‏‎‏‏‏‏‏‎‏‎‎‎‎‏‎‎‎‎‏‏‎‎‏‎‎‏‏‎<xliff:g id="TIME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ left until full‎‏‎‎‏‎"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‏‎‏‎‏‎‎‎‎‏‎‎‎‎‎‎‎‎‏‏‎‏‏‏‏‎‏‏‏‎‏‎‏‏‏‎‏‎‎‎‎‎‏‎‏‏‏‏‏‎‎‏‏‏‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎<xliff:g id="LEVEL">%1$s</xliff:g>‎‏‎‎‏‏‏‎ - ‎‏‎‎‏‏‎<xliff:g id="TIME">%2$s</xliff:g>‎‏‎‎‏‏‏‎ left until full‎‏‎‎‏‎"</string>
-    <string name="power_charging_limited" msgid="8202147604844938236">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‏‎‎‎‏‏‏‎‏‎‎‏‏‏‏‏‎‎‎‎‏‏‏‎‎‏‎‏‎‎‎‏‎‏‏‏‎‎‎‎‏‏‎‏‎‏‎‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‏‎‎‏‏‎<xliff:g id="LEVEL">%1$s</xliff:g>‎‏‎‎‏‏‏‎ - Charging optimized‎‏‎‎‏‎"</string>
+    <!-- no translation found for power_charging_limited (4144004473976005214) -->
+    <skip />
     <string name="power_charging_future_paused" msgid="1809543660923642799">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‏‏‏‎‎‏‎‎‎‏‏‏‎‎‏‏‎‎‏‎‏‎‎‏‎‏‏‏‏‏‎‏‎‎‏‎‏‎‏‏‏‎‏‎‎‏‏‏‏‎‎‎‏‏‏‎‏‎‏‏‏‏‎‎‏‎‎‏‏‎<xliff:g id="LEVEL">%1$s</xliff:g>‎‏‎‎‏‏‏‎ - Charging‎‏‎‎‏‎"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‏‎‏‎‎‏‎‏‏‎‏‎‎‎‏‎‎‎‏‎‎‏‏‎‎‎‏‎‎‏‏‏‏‏‏‎‏‏‎‏‎‏‏‎‎‎‎‎‎‏‎‎‎‎‏‎‎‎‎‏‎‎‎‏‎‎‏‏‎<xliff:g id="LEVEL">%1$s</xliff:g>‎‏‎‎‏‏‏‎ - ‎‏‎‎‏‏‎<xliff:g id="STATUS">%2$s</xliff:g>‎‏‎‎‏‏‏‎ - Full by ‎‏‎‎‏‏‎<xliff:g id="TIME">%3$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‎‏‎‎‎‏‏‎‎‏‎‎‏‎‏‏‎‏‎‏‏‎‏‎‏‏‏‏‎‎‎‏‎‏‏‎‏‏‎‏‏‎‏‏‎‏‎‏‎‏‎‏‏‎‎‏‏‎‎‎‎‎‎‏‎‎‏‏‎<xliff:g id="LEVEL">%1$s</xliff:g>‎‏‎‎‏‏‏‎ - Fully charged by ‎‏‎‎‏‏‎<xliff:g id="TIME">%2$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
diff --git a/packages/SettingsLib/res/values-es-rUS-feminine/strings.xml b/packages/SettingsLib/res/values-es-rUS-feminine/strings.xml
new file mode 100644
index 0000000..b2b4859
--- /dev/null
+++ b/packages/SettingsLib/res/values-es-rUS-feminine/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/*
+**
+** Copyright 2015 The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="delete_blob_confirmation_text" msgid="7807446938920827280">"¿Segura que quieres borrar estos datos compartidos?"</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-es-rUS-masculine/strings.xml b/packages/SettingsLib/res/values-es-rUS-masculine/strings.xml
new file mode 100644
index 0000000..f7c792a
--- /dev/null
+++ b/packages/SettingsLib/res/values-es-rUS-masculine/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/*
+**
+** Copyright 2015 The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="delete_blob_confirmation_text" msgid="7807446938920827280">"¿Seguro que quieres borrar estos datos compartidos?"</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-es-rUS-neuter/strings.xml b/packages/SettingsLib/res/values-es-rUS-neuter/strings.xml
new file mode 100644
index 0000000..4bf39a7
--- /dev/null
+++ b/packages/SettingsLib/res/values-es-rUS-neuter/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/*
+**
+** Copyright 2015 The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="delete_blob_confirmation_text" msgid="7807446938920827280">"¿Confirmas que quieres borrar estos datos compartidos?"</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-es-rUS/arrays.xml b/packages/SettingsLib/res/values-es-rUS/arrays.xml
index 4d4d7c9..541f4db 100644
--- a/packages/SettingsLib/res/values-es-rUS/arrays.xml
+++ b/packages/SettingsLib/res/values-es-rUS/arrays.xml
@@ -40,7 +40,7 @@
     <item msgid="8339720953594087771">"Conectando a <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
     <item msgid="3028983857109369308">"Autenticando con <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
     <item msgid="4287401332778341890">"Obteniendo dirección IP de <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
-    <item msgid="1043944043827424501">"Conectado a <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</item>
+    <item msgid="1043944043827424501">"Se estableció conexión con <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</item>
     <item msgid="7445993821842009653">"Suspendido"</item>
     <item msgid="1175040558087735707">"Desconectando de <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
     <item msgid="699832486578171722">"Desconectado"</item>
diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml
index 2cf0fe3..e146f49 100644
--- a/packages/SettingsLib/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/res/values-es-rUS/strings.xml
@@ -62,7 +62,7 @@
     <string name="wifi_no_internet" msgid="1774198889176926299">"No hay acceso a Internet"</string>
     <string name="saved_network" msgid="7143698034077223645">"Guardada por <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="connected_via_network_scorer" msgid="7665725527352893558">"Conexión automática mediante %1$s"</string>
-    <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Conectado automáticamente mediante proveedor de calificación de red"</string>
+    <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Conectado automáticamente mediante proveedor de calificación de redes"</string>
     <string name="connected_via_app" msgid="3532267661404276584">"Conexión a través de <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="tap_to_sign_up" msgid="5356397741063740395">"Presiona para registrarte"</string>
     <string name="wifi_connected_no_internet" msgid="5087420713443350646">"Sin Internet"</string>
@@ -137,7 +137,7 @@
     <string name="bluetooth_profile_hearing_aid" msgid="2607867572569689732">"Audífonos"</string>
     <string name="bluetooth_profile_le_audio" msgid="1725521360076451751">"audio de bajo consumo"</string>
     <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="5757754050938807276">"Conectado a audífonos"</string>
-    <string name="bluetooth_le_audio_profile_summary_connected" msgid="6916226974453480650">"Conectado a audio de bajo consumo"</string>
+    <string name="bluetooth_le_audio_profile_summary_connected" msgid="6916226974453480650">"Conectado a LE Audio"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Conectado al audio multimedia"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Conectado al audio del dispositivo"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"Conectado al servidor de transferencia de archivo"</string>
@@ -163,7 +163,7 @@
     <string name="bluetooth_pairing_error_message" msgid="6626399020672335565">"No se pudo vincular con <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
     <string name="bluetooth_pairing_pin_error_message" msgid="264422127613704940">"No se pudo vincular con <xliff:g id="DEVICE_NAME">%1$s</xliff:g>: llave de acceso o PIN incorrectos."</string>
     <string name="bluetooth_pairing_device_down_error_message" msgid="2554424863101358857">"No se puede establecer la comunicación con <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
-    <string name="bluetooth_pairing_rejected_error_message" msgid="5943444352777314442">"Vínculo rechazado por <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="bluetooth_pairing_rejected_error_message" msgid="5943444352777314442">"Vinculación rechazada por <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_talkback_computer" msgid="3736623135703893773">"Computadora"</string>
     <string name="bluetooth_talkback_headset" msgid="3406852564400882682">"Auriculares"</string>
     <string name="bluetooth_talkback_phone" msgid="868393783858123880">"Teléfono"</string>
@@ -189,7 +189,7 @@
     <string name="tether_settings_title_wifi" msgid="4803402057533895526">"Hotspot portátil"</string>
     <string name="tether_settings_title_bluetooth" msgid="916519902721399656">"Conexión Bluetooth"</string>
     <string name="tether_settings_title_usb_bluetooth" msgid="1727111807207577322">"Compartir conexión"</string>
-    <string name="tether_settings_title_all" msgid="8910259483383010470">"Conexión móvil y hotspot"</string>
+    <string name="tether_settings_title_all" msgid="8910259483383010470">"Conexión/hotspot portable"</string>
     <string name="managed_user_title" msgid="449081789742645723">"Todas las apps de trabajo"</string>
     <string name="unknown" msgid="3544487229740637809">"Desconocido"</string>
     <string name="running_process_item_user_label" msgid="3988506293099805796">"Usuario: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
@@ -279,7 +279,7 @@
     <string name="keywords_adb_wireless" msgid="6507505581882171240">"adb, debug, dev"</string>
     <string name="bugreport_in_power" msgid="8664089072534638709">"Acceso directo para informes de errores"</string>
     <string name="bugreport_in_power_summary" msgid="1885529649381831775">"Muestra un botón en el menú de encendido para realizar un informe de errores"</string>
-    <string name="keep_screen_on" msgid="1187161672348797558">"Permanecer activo"</string>
+    <string name="keep_screen_on" msgid="1187161672348797558">"No desactivar"</string>
     <string name="keep_screen_on_summary" msgid="1510731514101925829">"La pantalla nunca quedará inactiva mientras el dispositivo se esté cargando"</string>
     <string name="bt_hci_snoop_log" msgid="7291287955649081448">"Registro de Bluetooth HCI"</string>
     <string name="bt_hci_snoop_log_summary" msgid="6808538971394092284">"Capturar paquetes de Bluetooth (activa/desactiva el Bluetooth después de cambiar esta configuración)"</string>
@@ -379,12 +379,12 @@
     <string name="show_touches" msgid="8437666942161289025">"Mostrar presiones"</string>
     <string name="show_touches_summary" msgid="3692861665994502193">"Muestra la ubicación de las presiones en la pantalla"</string>
     <string name="show_key_presses" msgid="6360141722735900214">"Ver pulsaciones de teclas"</string>
-    <string name="show_key_presses_summary" msgid="725387457373015024">"Ver coment. visual para pulsac. de teclas físicas"</string>
-    <string name="show_screen_updates" msgid="2078782895825535494">"Ver actualiz. de superficie"</string>
+    <string name="show_key_presses_summary" msgid="725387457373015024">"Ver comentario visual para pulsaciones de teclas físicas"</string>
+    <string name="show_screen_updates" msgid="2078782895825535494">"Mostrar actualizaciones"</string>
     <string name="show_screen_updates_summary" msgid="2126932969682087406">"Destello en superficie por actualización"</string>
     <string name="show_hw_screen_updates" msgid="2021286231267747506">"Mostrar cambios de vista"</string>
     <string name="show_hw_screen_updates_summary" msgid="3539770072741435691">"Mostrar vistas de ventanas procesadas"</string>
-    <string name="show_hw_layers_updates" msgid="5268370750002509767">"Ver actualiz. de capas de hardware"</string>
+    <string name="show_hw_layers_updates" msgid="5268370750002509767">"Act. de capas de hardware"</string>
     <string name="show_hw_layers_updates_summary" msgid="5850955890493054618">"Luz verde en capas de hardware al actualizarse"</string>
     <string name="debug_hw_overdraw" msgid="8944851091008756796">"Depurar superpos. de GPU"</string>
     <string name="disable_overlays" msgid="4206590799671557143">"Desactivar superposición de hardware"</string>
@@ -430,8 +430,7 @@
     <string name="force_allow_on_external_summary" msgid="8525425782530728238">"Cualquier app puede escribirse en un almacenamiento externo, sin importar los valores del manifiesto"</string>
     <string name="force_resizable_activities" msgid="7143612144399959606">"Forzar actividades para que cambien de tamaño"</string>
     <string name="force_resizable_activities_summary" msgid="2490382056981583062">"Permitir que todas las actividades puedan cambiar de tamaño para el modo multiventana, sin importar los valores del manifiesto."</string>
-    <string name="enable_freeform_support" msgid="8409932201445109106">"Habilitar ventanas de formato libre (heredado)"</string>
-    <string name="enable_freeform_support_summary" msgid="2242481082356957934">"Permitir la compatibilidad con ventanas de formato libre experimentales (heredado)"</string>
+    <string name="enable_freeform_support" msgid="7599125687603914253">"Habilitar ventanas de forma libre"</string>
     <string name="local_backup_password_title" msgid="4631017948933578709">"Contraseñas"</string>
     <string name="local_backup_password_summary_none" msgid="7646898032616361714">"Tus copias de seguridad de escritorio no están protegidas por contraseña"</string>
     <string name="local_backup_password_summary_change" msgid="1707357670383995567">"Presiona para cambiar o quitar la contraseña de las copias de seguridad completas de tu escritorio."</string>
@@ -496,7 +495,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> para completar"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> para completar"</string>
-    <string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Carga optimizada"</string>
+    <!-- no translation found for power_charging_limited (4144004473976005214) -->
+    <skip />
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - Cargando"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - Carga completa: <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - Carga completa: <xliff:g id="TIME">%2$s</xliff:g>"</string>
@@ -617,7 +617,7 @@
     <string name="accessor_no_description_text" msgid="7510967452505591456">"La app no proporcionó una descripción."</string>
     <string name="accessor_expires_text" msgid="4625619273236786252">"La asignación de tiempo vence el <xliff:g id="DATE">%s</xliff:g>"</string>
     <string name="delete_blob_text" msgid="2819192607255625697">"Borrar datos compartidos"</string>
-    <string name="delete_blob_confirmation_text" msgid="7807446938920827280">"¿Seguro que quieres borrar estos datos compartidos?"</string>
+    <string name="delete_blob_confirmation_text" msgid="7807446938920827280">"¿Confirmas que quieres borrar estos datos compartidos?"</string>
     <string name="user_add_user_item_summary" msgid="5748424612724703400">"Los usuarios tienen sus propias aplicaciones y contenidos."</string>
     <string name="user_add_profile_item_summary" msgid="5418602404308968028">"Desde tu cuenta, puedes restringir el acceso a las aplicaciones y al contenido."</string>
     <string name="user_add_user_item_title" msgid="2394272381086965029">"Usuario"</string>
@@ -636,7 +636,7 @@
     <string name="user_add_user_type_title" msgid="551279664052914497">"Agregar"</string>
     <string name="user_new_user_name" msgid="60979820612818840">"Usuario nuevo"</string>
     <string name="user_new_profile_name" msgid="2405500423304678841">"Perfil nuevo"</string>
-    <string name="user_info_settings_title" msgid="6351390762733279907">"Datos del usuario"</string>
+    <string name="user_info_settings_title" msgid="6351390762733279907">"Datos de usuario"</string>
     <string name="profile_info_settings_title" msgid="105699672534365099">"Datos del perfil"</string>
     <string name="user_need_lock_message" msgid="4311424336209509301">"Para poder crear un perfil restringido, debes configurar un bloqueo de pantalla que proteja tus aplicaciones y datos personales."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Configurar bloqueo"</string>
diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml
index e012a418..fdfb844 100644
--- a/packages/SettingsLib/res/values-es/strings.xml
+++ b/packages/SettingsLib/res/values-es/strings.xml
@@ -81,7 +81,7 @@
     <string name="speed_label_fast" msgid="2677719134596044051">"Rápida"</string>
     <string name="speed_label_very_fast" msgid="8215718029533182439">"Muy rápida"</string>
     <string name="wifi_passpoint_expired" msgid="6540867261754427561">"Caducada"</string>
-    <string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
+    <string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
     <string name="bluetooth_disconnected" msgid="7739366554710388701">"Desconectado"</string>
     <string name="bluetooth_disconnecting" msgid="7638892134401574338">"Desconectando…"</string>
     <string name="bluetooth_connecting" msgid="5871702668260192755">"Estableciendo conexión…"</string>
@@ -386,7 +386,7 @@
     <string name="show_hw_screen_updates_summary" msgid="3539770072741435691">"Hacer parpadear las vistas dentro de las ventanas cuando se dibujan"</string>
     <string name="show_hw_layers_updates" msgid="5268370750002509767">"Ver actualizaciones de capas de hardware"</string>
     <string name="show_hw_layers_updates_summary" msgid="5850955890493054618">"Hacer parpadear las capas de hardware en verde cuando se actualizan"</string>
-    <string name="debug_hw_overdraw" msgid="8944851091008756796">"Depurar overdraw de GPU"</string>
+    <string name="debug_hw_overdraw" msgid="8944851091008756796">"Depurar sobredibujos de GPU"</string>
     <string name="disable_overlays" msgid="4206590799671557143">"Inhabilitar superposiciones de hardware"</string>
     <string name="disable_overlays_summary" msgid="1954852414363338166">"Usa siempre la GPU para componer pantallas"</string>
     <string name="simulate_color_space" msgid="1206503300335835151">"Simular espacio de color"</string>
@@ -420,7 +420,7 @@
     <string name="overlay_display_devices_title" msgid="5411894622334469607">"Simular pantallas secundarias"</string>
     <string name="debug_applications_category" msgid="5394089406638954196">"Aplicaciones"</string>
     <string name="immediately_destroy_activities" msgid="1826287490705167403">"No mantener actividades"</string>
-    <string name="immediately_destroy_activities_summary" msgid="6289590341144557614">"Destruye actividades cuando el usuario deja de usarlas"</string>
+    <string name="immediately_destroy_activities_summary" msgid="6289590341144557614">"Destruir actividades cuando el usuario salga de ellas"</string>
     <string name="app_process_limit_title" msgid="8361367869453043007">"Límitar procesos en segundo plano"</string>
     <string name="show_all_anrs" msgid="9160563836616468726">"Mostrar ANR en segundo plano"</string>
     <string name="show_all_anrs_summary" msgid="8562788834431971392">"Muestra un cuadro de diálogo que informa de que la aplicación no responde en aplicaciones en segundo plano"</string>
@@ -430,8 +430,7 @@
     <string name="force_allow_on_external_summary" msgid="8525425782530728238">"Permite que cualquier aplicación se pueda escribir en un almacenamiento externo independientemente de los valores de manifiesto"</string>
     <string name="force_resizable_activities" msgid="7143612144399959606">"Forzar que las actividades puedan cambiar de tamaño"</string>
     <string name="force_resizable_activities_summary" msgid="2490382056981583062">"Permite que todas las actividades puedan cambiar de tamaño en multiventana independientemente de los valores de manifiesto"</string>
-    <string name="enable_freeform_support" msgid="8409932201445109106">"Habilitar ventanas de forma libre (antiguo)"</string>
-    <string name="enable_freeform_support_summary" msgid="2242481082356957934">"Habilita la compatibilidad con ventanas de forma libre antiguas y experimentales."</string>
+    <string name="enable_freeform_support" msgid="7599125687603914253">"Habilitar ventanas de forma libre"</string>
     <string name="local_backup_password_title" msgid="4631017948933578709">"Contraseña para copias de ordenador"</string>
     <string name="local_backup_password_summary_none" msgid="7646898032616361714">"Las copias de seguridad completas de ordenador no están protegidas"</string>
     <string name="local_backup_password_summary_change" msgid="1707357670383995567">"Toca para cambiar o quitar la contraseña de las copias de seguridad completas del escritorio"</string>
@@ -496,7 +495,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> hasta la carga completa"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> hasta la carga completa"</string>
-    <string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Carga optimizada"</string>
+    <!-- no translation found for power_charging_limited (4144004473976005214) -->
+    <skip />
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> ‑ Cargar"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - Carga completa a las <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - Carga completa a las <xliff:g id="TIME">%2$s</xliff:g>"</string>
@@ -623,7 +623,7 @@
     <string name="user_add_user_item_title" msgid="2394272381086965029">"Usuario"</string>
     <string name="user_add_profile_item_title" msgid="3111051717414643029">"Perfil restringido"</string>
     <string name="user_add_user_title" msgid="5457079143694924885">"¿Añadir nuevo usuario?"</string>
-    <string name="user_add_user_message_long" msgid="1527434966294733380">"Puedes compartir este dispositivo si creas más usuarios. Cada uno tendrá su propio espacio y podrá personalizarlo con aplicaciones, un fondo de pantalla y mucho más. Los usuarios también pueden ajustar opciones del dispositivo, como la conexión Wi‑Fi, que afectan a todos los usuarios.\n\nCuando añadas un usuario, tendrá que configurar su espacio.\n\nCualquier usuario puede actualizar aplicaciones de todos los usuarios. Es posible que no se transfieran los servicios y opciones de accesibilidad al nuevo usuario."</string>
+    <string name="user_add_user_message_long" msgid="1527434966294733380">"Puedes compartir este dispositivo si creas más usuarios. Cada uno tendrá su propio espacio y podrá personalizarlo con aplicaciones, un fondo de pantalla y más. Los usuarios también pueden ajustar opciones del dispositivo, como la conexión Wi‑Fi, que afectan a todos los usuarios.\n\nCuando añadas un usuario, tendrá que configurar su espacio.\n\nCualquier usuario puede actualizar aplicaciones de todos los usuarios. Es posible que no se transfieran los servicios y opciones de accesibilidad al nuevo usuario."</string>
     <string name="user_add_user_message_short" msgid="3295959985795716166">"Al añadir un nuevo usuario, dicha persona debe configurar su espacio.\n\nCualquier usuario puede actualizar las aplicaciones del resto de los usuarios."</string>
     <string name="user_grant_admin_title" msgid="5157031020083343984">"¿Convertir a este usuario en administrador?"</string>
     <string name="user_grant_admin_message" msgid="1673791931033486709">"Los administradores tienen privilegios especiales que otros usuarios no tienen. Los administradores pueden gestionar todos los usuarios, actualizar o restablecer este dispositivo, modificar los ajustes, ver todas las aplicaciones instaladas y conceder o revocar privilegios de administrador a otros usuarios."</string>
@@ -641,7 +641,7 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Para poder crear un perfil restringido, debes configurar una pantalla de bloqueo que proteja tus aplicaciones y datos personales."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Establecer bloqueo"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Cambiar a <xliff:g id="USER_NAME">%s</xliff:g>"</string>
-    <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Creando nuevo usuario…"</string>
+    <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Creando usuario…"</string>
     <string name="creating_new_guest_dialog_message" msgid="1114905602181350690">"Creando nuevo invitado…"</string>
     <string name="add_user_failed" msgid="4809887794313944872">"No se ha podido crear el usuario"</string>
     <string name="add_guest_failed" msgid="8074548434469843443">"No se ha podido crear un nuevo invitado"</string>
diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml
index 70b436c..cff38eb 100644
--- a/packages/SettingsLib/res/values-et/strings.xml
+++ b/packages/SettingsLib/res/values-et/strings.xml
@@ -430,8 +430,7 @@
     <string name="force_allow_on_external_summary" msgid="8525425782530728238">"Lubab mis tahes rakendusi kirjutada välisesse salvestusruumi manifesti väärtustest olenemata"</string>
     <string name="force_resizable_activities" msgid="7143612144399959606">"Muuda tegevuste suurused muudetavaks"</string>
     <string name="force_resizable_activities_summary" msgid="2490382056981583062">"Muudetakse kõigi tegevuste suurused mitme aknaga vaates muudetavaks (manifesti väärtustest olenemata)."</string>
-    <string name="enable_freeform_support" msgid="8409932201445109106">"Luba vabas vormis aknad (pärand)"</string>
-    <string name="enable_freeform_support_summary" msgid="2242481082356957934">"Lubatakse katseliste pärand vabas vormis akende tugi."</string>
+    <string name="enable_freeform_support" msgid="7599125687603914253">"Luba vabas vormis aknad"</string>
     <string name="local_backup_password_title" msgid="4631017948933578709">"Arvutivarunduse parool"</string>
     <string name="local_backup_password_summary_none" msgid="7646898032616361714">"Täielikud arvutivarundused pole praegu kaitstud"</string>
     <string name="local_backup_password_summary_change" msgid="1707357670383995567">"Puudutage täielike arvutivarunduste parooli muutmiseks või eemaldamiseks"</string>
@@ -496,7 +495,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"Täislaadimiseks kulub <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – täislaadimiseks kulub <xliff:g id="TIME">%2$s</xliff:g>"</string>
-    <string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – laadimine on optimeeritud"</string>
+    <!-- no translation found for power_charging_limited (4144004473976005214) -->
+    <skip />
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – laadimine"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATUS">%2$s</xliff:g> – täis kell <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> – aku saab täis kell <xliff:g id="TIME">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml
index 1c80af8..622055e 100644
--- a/packages/SettingsLib/res/values-eu/strings.xml
+++ b/packages/SettingsLib/res/values-eu/strings.xml
@@ -430,8 +430,7 @@
     <string name="force_allow_on_external_summary" msgid="8525425782530728238">"Aplikazioek kanpoko memorian idatz dezakete, ezarritako balioak kontuan izan gabe"</string>
     <string name="force_resizable_activities" msgid="7143612144399959606">"Behartu jardueren tamaina doitu ahal izatera"</string>
     <string name="force_resizable_activities_summary" msgid="2490382056981583062">"Eman aukera jarduera guztien tamaina doitzeko, leiho batean baino gehiagotan erabili ahal izan daitezen, ezarritako balioak kontuan izan gabe"</string>
-    <string name="enable_freeform_support" msgid="8409932201445109106">"Gaitu estilo libreko leihoak (aurreko bertsiokoak)"</string>
-    <string name="enable_freeform_support_summary" msgid="2242481082356957934">"Onartu aurreko bertsioko estilo libreko leiho esperimentalak."</string>
+    <string name="enable_freeform_support" msgid="7599125687603914253">"Gaitu estilo libreko leihoak"</string>
     <string name="local_backup_password_title" msgid="4631017948933578709">"Babeskopien pasahitz lokala"</string>
     <string name="local_backup_password_summary_none" msgid="7646898032616361714">"Une honetan, ordenagailuko babeskopia osoak ez daude babestuta"</string>
     <string name="local_backup_password_summary_change" msgid="1707357670383995567">"Ordenagailuko eduki guztiaren babeskopia egiteko erabiltzen den pasahitza aldatzeko edo kentzeko, sakatu hau"</string>
@@ -496,7 +495,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> guztiz kargatu arte"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> guztiz kargatu arte"</string>
-    <string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Kargatzeko modu optimizatua"</string>
+    <!-- no translation found for power_charging_limited (4144004473976005214) -->
+    <skip />
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> ‑ Kargatzen"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - Ordu honetan kargatuko da guztiz: <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - Ordu honetan kargatuko da guztiz: <xliff:g id="TIME">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml
index 5e4d29a..f7279b1 100644
--- a/packages/SettingsLib/res/values-fa/strings.xml
+++ b/packages/SettingsLib/res/values-fa/strings.xml
@@ -430,8 +430,7 @@
     <string name="force_allow_on_external_summary" msgid="8525425782530728238">"بدون توجه به مقادیر آشکار، هر برنامه‌ای را برای نوشتن در حافظه خارجی واجد شرایط می‌کند"</string>
     <string name="force_resizable_activities" msgid="7143612144399959606">"اجبار فعالیت‌ها به قابل تغییر اندازه بودن"</string>
     <string name="force_resizable_activities_summary" msgid="2490382056981583062">"بدون توجه به مقادیر مانیفست، اندازه همه فعالیت‌ها برای حالت چند پنجره‌ای می‌تواند تغییر کند."</string>
-    <string name="enable_freeform_support" msgid="8409932201445109106">"فعال کردن پنجره‌های با قالب آزاد (قدیمی)"</string>
-    <string name="enable_freeform_support_summary" msgid="2242481082356957934">"فعال کردن پشتیبانی از پنجره‌های با قالب آزاد قدیمی آزمایشی."</string>
+    <string name="enable_freeform_support" msgid="7599125687603914253">"فعال کردن پنجره‌های آزاد"</string>
     <string name="local_backup_password_title" msgid="4631017948933578709">"گذرواژه پشتیبان‌گیری محلی"</string>
     <string name="local_backup_password_summary_none" msgid="7646898032616361714">"پشتیبان‌گیری کامل رایانه درحال حاضر محافظت نمی‌شود"</string>
     <string name="local_backup_password_summary_change" msgid="1707357670383995567">"برای تغییر یا حذف گذرواژه برای نسخه‌های پشتیبان کامل رایانه‌ای تک‌ضرب بزنید"</string>
@@ -496,7 +495,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - ‏<xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> تا شارژ کامل باقی مانده است"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> تا شارژ کامل باقی مانده است"</string>
-    <string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - شارژ بهینه شده است"</string>
+    <!-- no translation found for power_charging_limited (4144004473976005214) -->
+    <skip />
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - درحال شارژ"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"‫<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - تا <xliff:g id="TIME">%3$s</xliff:g> کامل می‌شود"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"‫<xliff:g id="LEVEL">%1$s</xliff:g> - شارژ تا <xliff:g id="TIME">%2$s</xliff:g> کامل می‌شود"</string>
diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml
index ad0cab3..03f6b0a 100644
--- a/packages/SettingsLib/res/values-fi/strings.xml
+++ b/packages/SettingsLib/res/values-fi/strings.xml
@@ -430,8 +430,7 @@
     <string name="force_allow_on_external_summary" msgid="8525425782530728238">"Mahdollistaa sovelluksen tietojen tallentamisen ulkoiseen tallennustilaan luetteloarvoista riippumatta"</string>
     <string name="force_resizable_activities" msgid="7143612144399959606">"Pakota kaikki toiminnot hyväksymään koon muutos"</string>
     <string name="force_resizable_activities_summary" msgid="2490382056981583062">"Pakota kaikki toiminnot hyväksymään koon muuttaminen usean ikkunan tilassa luettelon arvoista riippumatta"</string>
-    <string name="enable_freeform_support" msgid="8409932201445109106">"Ota vapaamuotoiset ikkunat käyttöön (vanha)"</string>
-    <string name="enable_freeform_support_summary" msgid="2242481082356957934">"Ota kokeellinen vapaamuotoisten ikkunoiden tuki käyttöön (vanha)."</string>
+    <string name="enable_freeform_support" msgid="7599125687603914253">"Ota käyttöön vapaamuotoiset ikkunat"</string>
     <string name="local_backup_password_title" msgid="4631017948933578709">"Varmuuskop. salasana"</string>
     <string name="local_backup_password_summary_none" msgid="7646898032616361714">"Tietokoneen kaikkien tietojen varmuuskopiointia ei ole tällä hetkellä suojattu"</string>
     <string name="local_backup_password_summary_change" msgid="1707357670383995567">"Vaihda tai poista tietokoneen kaikkien tietojen varmuuskopioinnin salasana koskettamalla."</string>
@@ -496,7 +495,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> kunnes täynnä"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> kunnes täynnä"</string>
-    <string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – Lataus optimoitu"</string>
+    <!-- no translation found for power_charging_limited (4144004473976005214) -->
+    <skip />
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – Ladataan"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATUS">%2$s</xliff:g> – Täynnä klo <xliff:g id="TIME">%3$s</xliff:g> mennessä"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> – Ladattu täyteen klo <xliff:g id="TIME">%2$s</xliff:g> mennessä"</string>
diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml
index 45e70f8..04bcb6c 100644
--- a/packages/SettingsLib/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml
@@ -62,7 +62,7 @@
     <string name="wifi_no_internet" msgid="1774198889176926299">"Aucun accès à Internet"</string>
     <string name="saved_network" msgid="7143698034077223645">"Enregistrés par <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatiquement connecté par %1$s"</string>
-    <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Connecté automatiquement par le fournisseur d\'avis sur le réseau"</string>
+    <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Connecté automatiquement par l\'utilitaire d\'évaluation des réseaux"</string>
     <string name="connected_via_app" msgid="3532267661404276584">"Connecté sur le réseau <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="tap_to_sign_up" msgid="5356397741063740395">"Toucher pour vous connecter"</string>
     <string name="wifi_connected_no_internet" msgid="5087420713443350646">"Aucune connexion Internet"</string>
@@ -145,7 +145,7 @@
     <string name="bluetooth_sap_profile_summary_connected" msgid="1280297388033001037">"Connecté au point d\'accès au service"</string>
     <string name="bluetooth_opp_profile_summary_not_connected" msgid="3959741824627764954">"Connexion au serveur de transfert de fichiers non établie"</string>
     <string name="bluetooth_hid_profile_summary_connected" msgid="3923653977051684833">"Connecté au périphérique d\'entrée"</string>
-    <string name="bluetooth_pan_user_profile_summary_connected" msgid="380469653827505727">"Connecté à l\'appareil pour accès Internet"</string>
+    <string name="bluetooth_pan_user_profile_summary_connected" msgid="380469653827505727">"Connecté à l\'appareil pour un accès Internet"</string>
     <string name="bluetooth_pan_nap_profile_summary_connected" msgid="3744773111299503493">"Connexion Internet locale partagée avec appareil"</string>
     <string name="bluetooth_pan_profile_summary_use_for" msgid="7422039765025340313">"Utiliser pour l\'accès à Internet"</string>
     <string name="bluetooth_map_profile_summary_use_for" msgid="4453622103977592583">"Utiliser pour la carte"</string>
@@ -159,7 +159,7 @@
     <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Associer"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"ASSOCIER"</string>
     <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"Annuler"</string>
-    <string name="bluetooth_pairing_will_share_phonebook" msgid="3064334458659165176">"L\'association vous permet d\'accéder à vos contacts et à l\'historique des appels lorsque vous êtes connecté."</string>
+    <string name="bluetooth_pairing_will_share_phonebook" msgid="3064334458659165176">"L\'association vous permet d\'accéder à vos contacts et à l\'historique des appels lorsque votre connexion est active."</string>
     <string name="bluetooth_pairing_error_message" msgid="6626399020672335565">"Impossible d\'associer à <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
     <string name="bluetooth_pairing_pin_error_message" msgid="264422127613704940">"Impossible d\'établir l\'association avec <xliff:g id="DEVICE_NAME">%1$s</xliff:g> en raison d\'un NIP ou d\'une clé d\'accès incorrects."</string>
     <string name="bluetooth_pairing_device_down_error_message" msgid="2554424863101358857">"Impossible d\'établir la communication avec <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
@@ -183,7 +183,7 @@
     <string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Réseau sécurisé"</string>
     <string name="process_kernel_label" msgid="950292573930336765">"Système d\'exploitation Android"</string>
     <string name="data_usage_uninstalled_apps" msgid="1933665711856171491">"Applis supprimées"</string>
-    <string name="data_usage_uninstalled_apps_users" msgid="5533981546921913295">"Applis et utilisateurs supprimés"</string>
+    <string name="data_usage_uninstalled_apps_users" msgid="5533981546921913295">"Applis et utilisateurs retirés"</string>
     <string name="data_usage_ota" msgid="7984667793701597001">"Mises à jour du système"</string>
     <string name="tether_settings_title_usb" msgid="3728686573430917722">"Partage de connexion par USB"</string>
     <string name="tether_settings_title_wifi" msgid="4803402057533895526">"Point d\'accès Wi-Fi mobile"</string>
@@ -242,7 +242,7 @@
     <string name="development_settings_title" msgid="140296922921597393">"Options pour les développeurs"</string>
     <string name="development_settings_enable" msgid="4285094651288242183">"Activer les options pour les développeurs"</string>
     <string name="development_settings_summary" msgid="8718917813868735095">"Définir les options pour le développement de l\'appli"</string>
-    <string name="development_settings_not_available" msgid="355070198089140951">"Les options proposées aux développeurs ne sont pas disponibles pour cet utilisateur."</string>
+    <string name="development_settings_not_available" msgid="355070198089140951">"Les options pour les développeurs ne sont pas disponibles pour cet utilisateur"</string>
     <string name="vpn_settings_not_available" msgid="2894137119965668920">"Les paramètres de RPV ne sont pas disponibles pour cet utilisateur"</string>
     <string name="tethering_settings_not_available" msgid="266821736434699780">"Les paramètres de partage de connexion ne sont pas disponibles pour cet utilisateur"</string>
     <string name="apn_settings_not_available" msgid="1147111671403342300">"Les paramètres de point d\'accès ne sont pas disponibles pour cet utilisateur"</string>
@@ -430,8 +430,7 @@
     <string name="force_allow_on_external_summary" msgid="8525425782530728238">"Rend possible l\'enregistrement de toute appli sur un espace de stockage externe, indépendamment des valeurs du fichier manifeste"</string>
     <string name="force_resizable_activities" msgid="7143612144399959606">"Forcer les activités à être redimensionnables"</string>
     <string name="force_resizable_activities_summary" msgid="2490382056981583062">"Permet de redimensionner toutes les activités pour le mode multi-fenêtre, indépendamment des valeurs du fichier manifeste."</string>
-    <string name="enable_freeform_support" msgid="8409932201445109106">"Activer fenêtres de forme libre (patrimoniales)"</string>
-    <string name="enable_freeform_support_summary" msgid="2242481082356957934">"Activer la prise en charge des fenêtres de forme libre patrimoniales expérimentales."</string>
+    <string name="enable_freeform_support" msgid="7599125687603914253">"Activer les fenêtres de forme libre"</string>
     <string name="local_backup_password_title" msgid="4631017948933578709">"Mot de passe sauvegarde PC"</string>
     <string name="local_backup_password_summary_none" msgid="7646898032616361714">"Les sauvegardes complètes sur PC ne sont pas protégées actuellement"</string>
     <string name="local_backup_password_summary_change" msgid="1707357670383995567">"Touchez pour modifier ou supprimer le mot de passe utilisé pour les sauvegardes complètes sur ordinateur."</string>
@@ -496,7 +495,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> jusqu\'à la recharge complète"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> (<xliff:g id="TIME">%2$s</xliff:g> jusqu\'à la recharge complète)"</string>
-    <string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Recharge optimisée"</string>
+    <!-- no translation found for power_charging_limited (4144004473976005214) -->
+    <skip />
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – Recharge en cours…"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATUS">%2$s</xliff:g> – Recharge complète d\'ici <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> – Recharge complète d\'ici <xliff:g id="TIME">%2$s</xliff:g>"</string>
@@ -529,9 +529,9 @@
   </string-array>
     <string name="charge_length_format" msgid="6941645744588690932">"Il y a <xliff:g id="ID_1">%1$s</xliff:g>"</string>
     <string name="remaining_length_format" msgid="4310625772926171089">"Temps restant : <xliff:g id="ID_1">%1$s</xliff:g>"</string>
-    <string name="screen_zoom_summary_small" msgid="6050633151263074260">"Petite"</string>
+    <string name="screen_zoom_summary_small" msgid="6050633151263074260">"Petit"</string>
     <string name="screen_zoom_summary_default" msgid="1888865694033865408">"Par défaut"</string>
-    <string name="screen_zoom_summary_large" msgid="4706951482598978984">"Grande"</string>
+    <string name="screen_zoom_summary_large" msgid="4706951482598978984">"Grand"</string>
     <string name="screen_zoom_summary_very_large" msgid="7317423942896999029">"Plus grande"</string>
     <string name="screen_zoom_summary_extremely_large" msgid="1438045624562358554">"La plus grande"</string>
     <string name="screen_zoom_summary_custom" msgid="3468154096832912210">"Personnalisée (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
@@ -553,7 +553,7 @@
     <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"Plus longtemps."</string>
     <string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"Moins longtemps."</string>
     <string name="cancel" msgid="5665114069455378395">"Annuler"</string>
-    <string name="next" msgid="2699398661093607009">"Suivant"</string>
+    <string name="next" msgid="2699398661093607009">"Suivante"</string>
     <string name="back" msgid="5554327870352703710">"Retour"</string>
     <string name="save" msgid="3745809743277153149">"Enregistrer"</string>
     <string name="okay" msgid="949938843324579502">"OK"</string>
diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml
index c60e8ce..455fd23 100644
--- a/packages/SettingsLib/res/values-fr/strings.xml
+++ b/packages/SettingsLib/res/values-fr/strings.xml
@@ -430,8 +430,7 @@
     <string name="force_allow_on_external_summary" msgid="8525425782530728238">"Autoriser l\'enregistrement de toute application sur un espace de stockage externe, indépendamment des valeurs du fichier manifeste"</string>
     <string name="force_resizable_activities" msgid="7143612144399959606">"Forcer le redimensionnement des activités"</string>
     <string name="force_resizable_activities_summary" msgid="2490382056981583062">"Rendre toutes les activités redimensionnables pour le mode multifenêtre, indépendamment des valeurs du fichier manifeste"</string>
-    <string name="enable_freeform_support" msgid="8409932201445109106">"Activer les anciennes fenêtres de forme libre"</string>
-    <string name="enable_freeform_support_summary" msgid="2242481082356957934">"Permet d\'activer la compatibilité avec les anciennes fenêtres de forme libre expérimentales."</string>
+    <string name="enable_freeform_support" msgid="7599125687603914253">"Activer les fenêtres de forme libre"</string>
     <string name="local_backup_password_title" msgid="4631017948933578709">"Mot de passe de sauvegarde ordi"</string>
     <string name="local_backup_password_summary_none" msgid="7646898032616361714">"Les sauvegardes complètes sur ordi ne sont actuellement pas protégées"</string>
     <string name="local_backup_password_summary_change" msgid="1707357670383995567">"Appuyez pour modifier ou supprimer le mot de passe des sauvegardes complètes sur ordi."</string>
@@ -496,7 +495,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"Chargée à 100 %% dans <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - chargée à 100 %% dans <xliff:g id="TIME">%2$s</xliff:g>"</string>
-    <string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Recharge optimisée"</string>
+    <!-- no translation found for power_charging_limited (4144004473976005214) -->
+    <skip />
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - En charge"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - Complètement chargé d\'ici <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - Complètement chargé dans <xliff:g id="TIME">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml
index 4f82af3..7127ca8 100644
--- a/packages/SettingsLib/res/values-gl/strings.xml
+++ b/packages/SettingsLib/res/values-gl/strings.xml
@@ -408,18 +408,12 @@
     <string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"Permite cargar capas de depuración da GPU para aplicacións de depuración"</string>
     <string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"Activar rexistro de provedores"</string>
     <string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"Inclúe outros rexistros de provedores específicos do dispositivo en informes de erros; pode conter información privada, consumir máis batería e ocupar máis espazo de almacenamento"</string>
-    <!-- no translation found for enable_verbose_vendor_logging_checkbox (3864578373293835530) -->
-    <skip />
-    <!-- no translation found for verbose_vendor_logging_notification_title (6811217272559843592) -->
-    <skip />
-    <!-- no translation found for verbose_vendor_logging_notification_summary (5226524769774370942) -->
-    <skip />
-    <!-- no translation found for verbose_vendor_logging_notification_action (1190831050259046071) -->
-    <skip />
-    <!-- no translation found for verbose_vendor_logging_preference_summary_will_disable (6175431593394522553) -->
-    <skip />
-    <!-- no translation found for verbose_vendor_logging_preference_summary_on (9017757242481762036) -->
-    <skip />
+    <string name="enable_verbose_vendor_logging_checkbox" msgid="3864578373293835530">"Desactivar despois dun día"</string>
+    <string name="verbose_vendor_logging_notification_title" msgid="6811217272559843592">"Finalizou o rexistro detallado do provedor"</string>
+    <string name="verbose_vendor_logging_notification_summary" msgid="5226524769774370942">"Activado durante un día"</string>
+    <string name="verbose_vendor_logging_notification_action" msgid="1190831050259046071">"Activar durante un día máis"</string>
+    <string name="verbose_vendor_logging_preference_summary_will_disable" msgid="6175431593394522553">"Desactívase despois dun día"</string>
+    <string name="verbose_vendor_logging_preference_summary_on" msgid="9017757242481762036">"Desactivado indefinidamente"</string>
     <string name="window_animation_scale_title" msgid="5236381298376812508">"Escala de animación da ventá"</string>
     <string name="transition_animation_scale_title" msgid="1278477690695439337">"Escala animación-transición"</string>
     <string name="animator_duration_scale_title" msgid="7082913931326085176">"Escala duración animador"</string>
@@ -436,8 +430,7 @@
     <string name="force_allow_on_external_summary" msgid="8525425782530728238">"Permite que calquera aplicación compatible se poida escribir nun almacenamento externo, independentemente dos valores do manifesto"</string>
     <string name="force_resizable_activities" msgid="7143612144399959606">"Forzar o axuste do tamaño das actividades"</string>
     <string name="force_resizable_activities_summary" msgid="2490382056981583062">"Permite axustar o tamaño de todas as actividades para o modo multiventá, independentemente dos valores do manifesto"</string>
-    <string name="enable_freeform_support" msgid="8409932201445109106">"Activar ventás de forma libre (antigas)"</string>
-    <string name="enable_freeform_support_summary" msgid="2242481082356957934">"Activa a compatibilidade coas ventás de forma libre experimentais antigas."</string>
+    <string name="enable_freeform_support" msgid="7599125687603914253">"Activar ventás de forma libre"</string>
     <string name="local_backup_password_title" msgid="4631017948933578709">"Contrasinal para copias"</string>
     <string name="local_backup_password_summary_none" msgid="7646898032616361714">"As copias de seguranza de ordenador completas non están protexidas"</string>
     <string name="local_backup_password_summary_change" msgid="1707357670383995567">"Toca para cambiar ou quitar o contrasinal para as copias de seguranza completas de ordenador"</string>
@@ -502,7 +495,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> para completar a carga"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> (<xliff:g id="TIME">%2$s</xliff:g> para completar a carga)"</string>
-    <string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> (carga optimizada)"</string>
+    <!-- no translation found for power_charging_limited (4144004473976005214) -->
+    <skip />
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> (cargando)"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - Completarase á/s <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - Carga completa á/s <xliff:g id="TIME">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml
index b44f0c7..f89e148 100644
--- a/packages/SettingsLib/res/values-gu/strings.xml
+++ b/packages/SettingsLib/res/values-gu/strings.xml
@@ -430,8 +430,7 @@
     <string name="force_allow_on_external_summary" msgid="8525425782530728238">"મેનિફેસ્ટ મૂલ્યોને ધ્યાનમાં લીધા સિવાય, કોઈપણ ઍપને બાહ્ય સ્ટોરેજ પર લખાવા માટે લાયક બનાવે છે"</string>
     <string name="force_resizable_activities" msgid="7143612144399959606">"પ્રવૃત્તિઓને ફરીથી કદ યોગ્ય થવા માટે ફરજ પાડો"</string>
     <string name="force_resizable_activities_summary" msgid="2490382056981583062">"મૅનિફેસ્ટ મૂલ્યોને ધ્યાનમાં લીધા સિવાય, તમામ પ્રવૃત્તિઓને મલ્ટી-વિન્ડો માટે ફરીથી કદ બદલી શકે તેવી બનાવો."</string>
-    <string name="enable_freeform_support" msgid="8409932201445109106">"ફ્રીફોર્મ વિન્ડો ચાલુ કરો (જૂની)"</string>
-    <string name="enable_freeform_support_summary" msgid="2242481082356957934">"પ્રાયોગિક જૂની ફ્રીફોર્મ વિન્ડો માટે સપોર્ટ મેળવવાની સુવિધા ચાલુ કરો."</string>
+    <string name="enable_freeform_support" msgid="7599125687603914253">"ફ્રીફોર્મ વિન્ડો ચાલુ કરો"</string>
     <string name="local_backup_password_title" msgid="4631017948933578709">"ડેસ્કટૉપ બૅકઅપ પાસવર્ડ"</string>
     <string name="local_backup_password_summary_none" msgid="7646898032616361714">"ડેસ્કટૉપ સંપૂર્ણ બૅકઅપ હાલમાં સુરક્ષિત નથી"</string>
     <string name="local_backup_password_summary_change" msgid="1707357670383995567">"ડેસ્કટૉપ સંપૂર્ણ બેકઅપ્સ માટેનો પાસવર્ડ બદલવા અથવા દૂર કરવા માટે ટૅચ કરો"</string>
@@ -496,7 +495,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"પૂર્ણ ચાર્જ થવામાં <xliff:g id="TIME">%1$s</xliff:g> બાકી છે"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - પૂર્ણ ચાર્જ થવામાં <xliff:g id="TIME">%2$s</xliff:g> બાકી છે"</string>
-    <string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - ચાર્જિંગ ઑપ્ટિમાઇઝ કરવામાં આવ્યું છે"</string>
+    <!-- no translation found for power_charging_limited (4144004473976005214) -->
+    <skip />
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - ચાર્જિંગ"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - <xliff:g id="TIME">%3$s</xliff:g> સુધીમાં સંપૂર્ણ ચાર્જ થશે"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> સુધીમાં સંપૂર્ણ ચાર્જ થશે"</string>
diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml
index 246f66e..adf8b5f 100644
--- a/packages/SettingsLib/res/values-hi/strings.xml
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -430,8 +430,7 @@
     <string name="force_allow_on_external_summary" msgid="8525425782530728238">"इससे कोई भी ऐप्लिकेशन बाहरी स्टोरेज में रखने लायक बन जाता है, चाहे उसकी मेनिफ़ेस्ट वैल्यू कुछ भी हो"</string>
     <string name="force_resizable_activities" msgid="7143612144399959606">"विंडो के हिसाब से गतिविधियों का साइज़ बदल दें"</string>
     <string name="force_resizable_activities_summary" msgid="2490382056981583062">"सभी गतिविधियों को मल्टी-विंडो (एक से ज़्यादा ऐप्लिकेशन, एक साथ) के लिए साइज़ बदलने लायक बनाएं, चाहे उनकी मेनिफ़ेस्ट वैल्यू कुछ भी हो."</string>
-    <string name="enable_freeform_support" msgid="8409932201445109106">"फ़्रीफ़ॉर्म विंडो वाला लेगसी मोड चालू करें"</string>
-    <string name="enable_freeform_support_summary" msgid="2242481082356957934">"एक्सपेरिमेंट के तौर पर उपलब्ध फ़्रीफ़ॉर्म विंडो वाला लेगसी मोड चालू करें."</string>
+    <string name="enable_freeform_support" msgid="7599125687603914253">"फ़्रीफ़ॉर्म विंडो (एक साथ कई विंडो दिखाना) चालू करें"</string>
     <string name="local_backup_password_title" msgid="4631017948933578709">"डेस्‍कटॉप बैकअप पासवर्ड"</string>
     <string name="local_backup_password_summary_none" msgid="7646898032616361714">"डेस्‍कटॉप का पूरा बैकअप फ़िलहाल सुरक्षित नहीं है"</string>
     <string name="local_backup_password_summary_change" msgid="1707357670383995567">"डेस्कटॉप के पूरे बैक अप का पासवर्ड बदलने या हटाने के लिए टैप करें"</string>
@@ -496,7 +495,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> में बैटरी पूरी चार्ज हो जाएगी"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> में बैटरी पूरी चार्ज हो जाएगी"</string>
-    <string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - चार्जिंग को ऑप्टिमाइज़ किया गया"</string>
+    <!-- no translation found for power_charging_limited (4144004473976005214) -->
+    <skip />
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - चार्ज हो रही है"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - बैटरी <xliff:g id="TIME">%3$s</xliff:g> में पूरी चार्ज हो जाएगी"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - बैटरी <xliff:g id="TIME">%2$s</xliff:g> में पूरी चार्ज हो जाएगी"</string>
diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml
index f4bb6ea..3c20bf4 100644
--- a/packages/SettingsLib/res/values-hr/strings.xml
+++ b/packages/SettingsLib/res/values-hr/strings.xml
@@ -430,8 +430,7 @@
     <string name="force_allow_on_external_summary" msgid="8525425782530728238">"Aplikacije se mogu zapisivati u vanjsku pohranu neovisno o vrijednostima manifesta"</string>
     <string name="force_resizable_activities" msgid="7143612144399959606">"Nametni mogućnost promjene veličine za aktivnosti"</string>
     <string name="force_resizable_activities_summary" msgid="2490382056981583062">"Omogući mijenjanje veličine svih aktivnosti za više prozora, neovisno o vrijednostima manifesta."</string>
-    <string name="enable_freeform_support" msgid="8409932201445109106">"Omogući prozore slobodnog oblika (naslijeđeno)"</string>
-    <string name="enable_freeform_support_summary" msgid="2242481082356957934">"Omogući podršku za eksperimentalne naslijeđene prozore slobodnog oblika."</string>
+    <string name="enable_freeform_support" msgid="7599125687603914253">"Omogući prozore slobodnog oblika"</string>
     <string name="local_backup_password_title" msgid="4631017948933578709">"Zaporka sigurnosne kopije"</string>
     <string name="local_backup_password_summary_none" msgid="7646898032616361714">"Potpune sigurnosne kopije na stolnom računalu trenutačno nisu zaštićene"</string>
     <string name="local_backup_password_summary_change" msgid="1707357670383995567">"Dodirnite da biste promijenili ili uklonili zaporku za potpune sigurnosne kopije na računalu"</string>
@@ -496,7 +495,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> do napunjenosti"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do napunjenosti"</string>
-    <string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – punjenje se optimizira"</string>
+    <!-- no translation found for power_charging_limited (4144004473976005214) -->
+    <skip />
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – punjenje"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATUS">%2$s</xliff:g> – bit će pun do <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> – potpuno napunjeno do <xliff:g id="TIME">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml
index db739d5..fd938b9 100644
--- a/packages/SettingsLib/res/values-hu/strings.xml
+++ b/packages/SettingsLib/res/values-hu/strings.xml
@@ -430,8 +430,7 @@
     <string name="force_allow_on_external_summary" msgid="8525425782530728238">"Lehetővé teszi bármely alkalmazás külső tárhelyre való írását a jegyzékértékektől függetlenül"</string>
     <string name="force_resizable_activities" msgid="7143612144399959606">"Tevékenységek átméretezésének kényszerítése"</string>
     <string name="force_resizable_activities_summary" msgid="2490382056981583062">"Legyen az összes tevékenység átméretezhető a többablakos megjelenítés érdekében a jegyzékértékektől függetlenül."</string>
-    <string name="enable_freeform_support" msgid="8409932201445109106">"Szabad formájú ablakok engedélyezése (régi)"</string>
-    <string name="enable_freeform_support_summary" msgid="2242481082356957934">"Kísérleti, régi szabad formájú ablakok támogatásának engedélyezése."</string>
+    <string name="enable_freeform_support" msgid="7599125687603914253">"Szabad formájú ablakok engedélyezése"</string>
     <string name="local_backup_password_title" msgid="4631017948933578709">"Asztali mentés jelszava"</string>
     <string name="local_backup_password_summary_none" msgid="7646898032616361714">"Az asztali teljes biztonsági mentések jelenleg nem védettek."</string>
     <string name="local_backup_password_summary_change" msgid="1707357670383995567">"Koppintson ide az asztali teljes mentések jelszavának módosításához vagy eltávolításához"</string>
@@ -496,7 +495,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> a teljes töltöttségig"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> a teljes töltöttségig"</string>
-    <string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – Optimalizált töltés"</string>
+    <!-- no translation found for power_charging_limited (4144004473976005214) -->
+    <skip />
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – Töltés…"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATUS">%2$s</xliff:g> – Teljes feltöltés eddig: <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> – Teljes feltöltés eddig: <xliff:g id="TIME">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml
index 717313fb..389aad2 100644
--- a/packages/SettingsLib/res/values-hy/strings.xml
+++ b/packages/SettingsLib/res/values-hy/strings.xml
@@ -430,8 +430,7 @@
     <string name="force_allow_on_external_summary" msgid="8525425782530728238">"Թույլ է տալիս ցանկացած հավելված պահել արտաքին սարքում՝ մանիֆեստի արժեքներից անկախ"</string>
     <string name="force_resizable_activities" msgid="7143612144399959606">"Չափերի փոփոխում բազմապատուհան ռեժիմում"</string>
     <string name="force_resizable_activities_summary" msgid="2490382056981583062">"Բոլոր ակտիվությունների չափերը բազմապատուհան ռեժիմի համար դարձնել փոփոխելի՝ մանիֆեստի արժեքներից անկախ:"</string>
-    <string name="enable_freeform_support" msgid="8409932201445109106">"Միացնել կամայական ձևի պատուհանները (հնացած)"</string>
-    <string name="enable_freeform_support_summary" msgid="2242481082356957934">"Միացնել կամայական ձևի պատուհանների ստեղծման հնացած փորձնական գործառույթի աջակցումը:"</string>
+    <string name="enable_freeform_support" msgid="7599125687603914253">"Ակտիվացնել կամայական ձևի պատուհանները"</string>
     <string name="local_backup_password_title" msgid="4631017948933578709">"Աշխատասեղանի պահուստավորման գաղտնաբառ"</string>
     <string name="local_backup_password_summary_none" msgid="7646898032616361714">"Աշխատասեղանի ամբողջական պահուստավորումները այժմ պաշտպանված չեն"</string>
     <string name="local_backup_password_summary_change" msgid="1707357670383995567">"Հպեք՝ աշխատասեղանի ամբողջական պահուստավորման գաղտնաբառը փոխելու կամ հեռացնելու համար"</string>
@@ -496,7 +495,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> մինչև լրիվ լիցքավորումը"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> մինչև լրիվ լիցքավորումը"</string>
-    <string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – Լիցքավորումն օպտիմալացված է"</string>
+    <!-- no translation found for power_charging_limited (4144004473976005214) -->
+    <skip />
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> — Լիցքավորում"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATUS">%2$s</xliff:g> – Ամբողջովին կլիցքավորվի մինչև <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> – Ամբողջովին կլիցքավորվի մինչև <xliff:g id="TIME">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml
index 748d5ed..c6bd3e9 100644
--- a/packages/SettingsLib/res/values-in/strings.xml
+++ b/packages/SettingsLib/res/values-in/strings.xml
@@ -430,8 +430,7 @@
     <string name="force_allow_on_external_summary" msgid="8525425782530728238">"Membuat semua aplikasi dapat ditulis ke penyimpanan eksternal, terlepas dari nilai manifes"</string>
     <string name="force_resizable_activities" msgid="7143612144399959606">"Paksa aktivitas agar ukurannya dapat diubah"</string>
     <string name="force_resizable_activities_summary" msgid="2490382056981583062">"Membuat semua aktivitas dapat diubah ukurannya untuk banyak jendela, terlepas dari nilai manifes."</string>
-    <string name="enable_freeform_support" msgid="8409932201445109106">"Aktifkan jendela freeform (versi lama)"</string>
-    <string name="enable_freeform_support_summary" msgid="2242481082356957934">"Aktifkan dukungan untuk jendela freeform eksperimental versi lama."</string>
+    <string name="enable_freeform_support" msgid="7599125687603914253">"Aktifkan jendela berformat bebas"</string>
     <string name="local_backup_password_title" msgid="4631017948933578709">"Sandi cadangan desktop"</string>
     <string name="local_backup_password_summary_none" msgid="7646898032616361714">"Saat ini cadangan desktop penuh tidak dilindungi"</string>
     <string name="local_backup_password_summary_change" msgid="1707357670383995567">"Ketuk guna mengubah atau menghapus sandi untuk cadangan lengkap desktop"</string>
@@ -496,7 +495,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> lagi sampai penuh"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> lagi sampai penuh"</string>
-    <string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Pengisian daya dioptimalkan"</string>
+    <!-- no translation found for power_charging_limited (4144004473976005214) -->
+    <skip />
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - Mengisi daya"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - Penuh pukul <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - Baterai terisi penuh dalam <xliff:g id="TIME">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml
index 7871cb2..2575af0 100644
--- a/packages/SettingsLib/res/values-is/strings.xml
+++ b/packages/SettingsLib/res/values-is/strings.xml
@@ -430,8 +430,7 @@
     <string name="force_allow_on_external_summary" msgid="8525425782530728238">"Gerir öll forrit skrifanleg í ytra geymslurými, óháð gildum í upplýsingaskrá"</string>
     <string name="force_resizable_activities" msgid="7143612144399959606">"Þvinga breytanlega stærð virkni"</string>
     <string name="force_resizable_activities_summary" msgid="2490382056981583062">"Gera stærð allrar virkni breytanlega svo að hún henti fyrir marga glugga, óháð gildum í upplýsingaskrá."</string>
-    <string name="enable_freeform_support" msgid="8409932201445109106">"Virkja glugga á frjálsu sniði (eldra)"</string>
-    <string name="enable_freeform_support_summary" msgid="2242481082356957934">"Virkja stuðning við eldri tilraunaglugga á frjálsu sniði."</string>
+    <string name="enable_freeform_support" msgid="7599125687603914253">"Virkja glugga með frjálsu sniði"</string>
     <string name="local_backup_password_title" msgid="4631017948933578709">"Aðgangsorð tölvuafritunar"</string>
     <string name="local_backup_password_summary_none" msgid="7646898032616361714">"Heildarafritun á tölvu er ekki varin sem stendur."</string>
     <string name="local_backup_password_summary_change" msgid="1707357670383995567">"Ýttu til að breyta eða fjarlægja aðgangsorðið fyrir heildarafritun á tölvu"</string>
@@ -496,7 +495,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> fram að fullri hleðslu"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> fram að fullri hleðslu"</string>
-    <string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – Hleðsla fínstillt"</string>
+    <!-- no translation found for power_charging_limited (4144004473976005214) -->
+    <skip />
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> ‑ Í hleðslu"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - Fullt kl. <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - Fullhlaðið kl. <xliff:g id="TIME">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml
index 26b67b4..deae4a5 100644
--- a/packages/SettingsLib/res/values-it/strings.xml
+++ b/packages/SettingsLib/res/values-it/strings.xml
@@ -85,7 +85,7 @@
     <string name="bluetooth_disconnected" msgid="7739366554710388701">"Disconnesso"</string>
     <string name="bluetooth_disconnecting" msgid="7638892134401574338">"Disconnessione…"</string>
     <string name="bluetooth_connecting" msgid="5871702668260192755">"Connessione…"</string>
-    <string name="bluetooth_connected" msgid="8065345572198502293">"<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> Connesso"</string>
+    <string name="bluetooth_connected" msgid="8065345572198502293">"Connessione a <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
     <string name="bluetooth_pairing" msgid="4269046942588193600">"Accoppiamento in corso…"</string>
     <string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> connesso (telefono escluso)"</string>
     <string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> connesso (contenuti multimediali esclusi)"</string>
@@ -143,9 +143,9 @@
     <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"Collegato al server di trasferimento file"</string>
     <string name="bluetooth_map_profile_summary_connected" msgid="4141725591784669181">"Connesso alla mappa"</string>
     <string name="bluetooth_sap_profile_summary_connected" msgid="1280297388033001037">"Collegato al SAP"</string>
-    <string name="bluetooth_opp_profile_summary_not_connected" msgid="3959741824627764954">"Non collegato al server di trasferimento file"</string>
+    <string name="bluetooth_opp_profile_summary_not_connected" msgid="3959741824627764954">"Non connesso al server di trasferimento file"</string>
     <string name="bluetooth_hid_profile_summary_connected" msgid="3923653977051684833">"Connesso a dispositivo di input"</string>
-    <string name="bluetooth_pan_user_profile_summary_connected" msgid="380469653827505727">"Connesso a dispositivo per accesso Internet"</string>
+    <string name="bluetooth_pan_user_profile_summary_connected" msgid="380469653827505727">"Connesso per internet"</string>
     <string name="bluetooth_pan_nap_profile_summary_connected" msgid="3744773111299503493">"Connessione Internet locale condivisa con dispositivo"</string>
     <string name="bluetooth_pan_profile_summary_use_for" msgid="7422039765025340313">"Usa per accesso a Internet"</string>
     <string name="bluetooth_map_profile_summary_use_for" msgid="4453622103977592583">"Utilizza per la mappa"</string>
@@ -430,8 +430,7 @@
     <string name="force_allow_on_external_summary" msgid="8525425782530728238">"Consente l\'installazione di qualsiasi app su memoria esterna, indipendentemente dai valori manifest"</string>
     <string name="force_resizable_activities" msgid="7143612144399959606">"Imponi formato modificabile alle attività"</string>
     <string name="force_resizable_activities_summary" msgid="2490382056981583062">"Rendi il formato di tutte le attività modificabile per la modalità multi-finestra, indipendentemente dai valori manifest"</string>
-    <string name="enable_freeform_support" msgid="8409932201445109106">"Attiva finestre a forma libera (legacy)"</string>
-    <string name="enable_freeform_support_summary" msgid="2242481082356957934">"Attiva il supporto delle finestre a forma libera sperimentali legacy."</string>
+    <string name="enable_freeform_support" msgid="7599125687603914253">"Attiva finestre a forma libera"</string>
     <string name="local_backup_password_title" msgid="4631017948933578709">"Password di backup desktop"</string>
     <string name="local_backup_password_summary_none" msgid="7646898032616361714">"I backup desktop completi non sono attualmente protetti"</string>
     <string name="local_backup_password_summary_change" msgid="1707357670383995567">"Tocca per modificare o rimuovere la password per i backup desktop completi"</string>
@@ -465,9 +464,9 @@
     <string name="select_webview_provider_dialog_title" msgid="2444261109877277714">"Imposta l\'implementazione di WebView"</string>
     <string name="select_webview_provider_toast_text" msgid="8512254949169359848">"La selezione non è più valida. Riprova."</string>
     <string name="picture_color_mode" msgid="1013807330552931903">"Modalità colori immagini"</string>
-    <string name="picture_color_mode_desc" msgid="151780973768136200">"Use sRGB"</string>
+    <string name="picture_color_mode_desc" msgid="151780973768136200">"Usa sRGB"</string>
     <string name="daltonizer_mode_disabled" msgid="403424372812399228">"Disattivato"</string>
-    <string name="daltonizer_mode_monochromacy" msgid="362060873835885014">"Monocromìa"</string>
+    <string name="daltonizer_mode_monochromacy" msgid="362060873835885014">"Monocromatismo"</string>
     <string name="daltonizer_mode_deuteranomaly" msgid="3507284319584683963">"Deuteranomalìa (rosso-verde)"</string>
     <string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomalìa (rosso-verde)"</string>
     <string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomalìa (blu-giallo)"</string>
@@ -496,7 +495,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> alla ricarica completa"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> alla ricarica completa"</string>
-    <string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Ricarica ottimizzata"</string>
+    <!-- no translation found for power_charging_limited (4144004473976005214) -->
+    <skip />
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> ‑ In carica"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - Ricarica completa entro <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - Batteria completamente carica entro <xliff:g id="TIME">%2$s</xliff:g>"</string>
@@ -600,7 +600,7 @@
     <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Connessione tramite eARC"</string>
     <string name="tv_media_transfer_default" msgid="5403053145185843843">"TV predefinita"</string>
     <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"Uscita HDMI"</string>
-    <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Speaker interni"</string>
+    <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Altoparlanti interni"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problema di connessione. Spegni e riaccendi il dispositivo"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Dispositivo audio cablato"</string>
     <string name="help_label" msgid="3528360748637781274">"Guida e feedback"</string>
diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml
index f7c9684..310b046 100644
--- a/packages/SettingsLib/res/values-iw/strings.xml
+++ b/packages/SettingsLib/res/values-iw/strings.xml
@@ -430,8 +430,7 @@
     <string name="force_allow_on_external_summary" msgid="8525425782530728238">"מאפשר כתיבה של כל אפליקציה באחסון חיצוני, ללא התחשבות בערכי המניפסט"</string>
     <string name="force_resizable_activities" msgid="7143612144399959606">"אילוץ יכולת קביעת גודל של הפעילויות"</string>
     <string name="force_resizable_activities_summary" msgid="2490382056981583062">"מאפשר יכולת קביעת גודל של כל הפעילויות לריבוי חלונות, ללא התחשבות בערכי המניפסט."</string>
-    <string name="enable_freeform_support" msgid="8409932201445109106">"הפעלת שינוי הגודל והמיקום של החלונות (מדור קודם)"</string>
-    <string name="enable_freeform_support_summary" msgid="2242481082356957934">"הפעלת תמיכה בתכונה הניסיונית מדור קודם של שינוי הגודל והמיקום של החלונות."</string>
+    <string name="enable_freeform_support" msgid="7599125687603914253">"הפעלת האפשרות לשנות את הגודל והמיקום של החלונות"</string>
     <string name="local_backup_password_title" msgid="4631017948933578709">"סיסמת גיבוי שולחן העבודה"</string>
     <string name="local_backup_password_summary_none" msgid="7646898032616361714">"גיבויים מלאים בשולחן העבודה אינם מוגנים כעת"</string>
     <string name="local_backup_password_summary_change" msgid="1707357670383995567">"יש להקיש כדי לשנות או להסיר את הסיסמה לגיבויים מלאים בשולחן העבודה"</string>
@@ -496,7 +495,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g>‏ – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"הזמן הנותר לטעינה מלאה: <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – הזמן הנותר לטעינה מלאה: <xliff:g id="TIME">%2$s</xliff:g>"</string>
-    <string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – הטעינה עברה אופטימיזציה"</string>
+    <!-- no translation found for power_charging_limited (4144004473976005214) -->
+    <skip />
     <string name="power_charging_future_paused" msgid="1809543660923642799">"‫<xliff:g id="LEVEL">%1$s</xliff:g> – בטעינה"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"‫<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATUS">%2$s</xliff:g> – טעינה מלאה עד <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"‫<xliff:g id="LEVEL">%1$s</xliff:g> – טעינה מלאה עד <xliff:g id="TIME">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml
index cafa95d..4dc0580 100644
--- a/packages/SettingsLib/res/values-ja/strings.xml
+++ b/packages/SettingsLib/res/values-ja/strings.xml
@@ -430,8 +430,7 @@
     <string name="force_allow_on_external_summary" msgid="8525425782530728238">"マニフェストの値に関係なく、すべてのアプリを外部ストレージに書き込めるようになります"</string>
     <string name="force_resizable_activities" msgid="7143612144399959606">"アクティビティをサイズ変更可能にする"</string>
     <string name="force_resizable_activities_summary" msgid="2490382056981583062">"マニフェストの値に関係なく、マルチウィンドウですべてのアクティビティのサイズを変更できるようにします。"</string>
-    <string name="enable_freeform_support" msgid="8409932201445109106">"フリーフォーム ウィンドウを有効にする(従来版)"</string>
-    <string name="enable_freeform_support_summary" msgid="2242481082356957934">"従来の試験運用版のフリーフォーム ウィンドウのサポートを有効にします。"</string>
+    <string name="enable_freeform_support" msgid="7599125687603914253">"フリーフォーム ウィンドウを有効にする"</string>
     <string name="local_backup_password_title" msgid="4631017948933578709">"PC バックアップ パスワード"</string>
     <string name="local_backup_password_summary_none" msgid="7646898032616361714">"デスクトップのフルバックアップは現在保護されていません"</string>
     <string name="local_backup_password_summary_change" msgid="1707357670383995567">"デスクトップのフルバックアップ用のパスワードを変更または削除する場合にタップします"</string>
@@ -496,7 +495,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"完了まであと <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - 完了まであと <xliff:g id="TIME">%2$s</xliff:g>"</string>
-    <string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - 充電が最適化されています"</string>
+    <!-- no translation found for power_charging_limited (4144004473976005214) -->
+    <skip />
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - 充電中"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - <xliff:g id="TIME">%3$s</xliff:g>までに完了"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>までに充電完了"</string>
diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml
index 10a660b..a196844 100644
--- a/packages/SettingsLib/res/values-ka/strings.xml
+++ b/packages/SettingsLib/res/values-ka/strings.xml
@@ -434,8 +434,7 @@
     <string name="force_allow_on_external_summary" msgid="8525425782530728238">"აპები ჩაიწერება გარე მეხსიერებაზე აღწერის ფაილების მნიშვნელობების მიუხედავად"</string>
     <string name="force_resizable_activities" msgid="7143612144399959606">"ზომაცვლადი აქტივობების იძულება"</string>
     <string name="force_resizable_activities_summary" msgid="2490382056981583062">"მანიფესტის მნიშვნელობების მიუხედავად, მრავალი ფანჯრის რეჟიმისთვის ყველა აქტივობის ზომაცვლადად გადაქცევა."</string>
-    <string name="enable_freeform_support" msgid="8409932201445109106">"თავისუფალი ფორმის ფანჯრების ჩართვა (მოძველებული)"</string>
-    <string name="enable_freeform_support_summary" msgid="2242481082356957934">"თავისუფალი ფორმის ექსპერიმენტული მოძველებული ფანჯრების ჩართვა."</string>
+    <string name="enable_freeform_support" msgid="7599125687603914253">"თავისუფალი ფორმის მქონე ფანჯრების ჩართვა"</string>
     <string name="local_backup_password_title" msgid="4631017948933578709">"დესკტოპის სარეზერვო ასლის პაროლი"</string>
     <string name="local_backup_password_summary_none" msgid="7646898032616361714">"დესკტოპის სრული სარეზერვო ასლები ამჟამად დაცული არ არის"</string>
     <string name="local_backup_password_summary_change" msgid="1707357670383995567">"შეეხეთ დესკტოპის სრული სარეზერვო ასლების პაროლის შესაცვლელად ან წასაშლელად"</string>
@@ -500,7 +499,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"სრულ დატენვამდე დარჩენილია <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> — სრულ დატენვამდე დარჩენილია <xliff:g id="TIME">%2$s</xliff:g>"</string>
-    <string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - დატენვა ოპტიმიზირებულია"</string>
+    <!-- no translation found for power_charging_limited (4144004473976005214) -->
+    <skip />
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – იტენება"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - სრულად დატენის დრო: <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - სრულად დატენის დრო: <xliff:g id="TIME">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-kk/arrays.xml b/packages/SettingsLib/res/values-kk/arrays.xml
index 6c8d59f..6669820 100644
--- a/packages/SettingsLib/res/values-kk/arrays.xml
+++ b/packages/SettingsLib/res/values-kk/arrays.xml
@@ -31,7 +31,7 @@
     <item msgid="7852381437933824454">"Ажыратуда…"</item>
     <item msgid="5046795712175415059">"Ажыратылған"</item>
     <item msgid="2473654476624070462">"Сәтсіз"</item>
-    <item msgid="9146847076036105115">"Бөгелген"</item>
+    <item msgid="9146847076036105115">"Блокталған"</item>
     <item msgid="4543924085816294893">"Нашар байланысты уақытша қолданбау"</item>
   </string-array>
   <string-array name="wifi_status_with_ssid">
@@ -45,7 +45,7 @@
     <item msgid="1175040558087735707">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> байланысынан ажыратылуда…"</item>
     <item msgid="699832486578171722">"Ажыратылған"</item>
     <item msgid="522383512264986901">"Сәтсіз"</item>
-    <item msgid="3602596701217484364">"Бөгелген"</item>
+    <item msgid="3602596701217484364">"Блокталған"</item>
     <item msgid="1999413958589971747">"Нашар байланысты уақытша қолданбау"</item>
   </string-array>
   <string-array name="hdcp_checking_titles">
diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml
index 536b624..b0b71ef 100644
--- a/packages/SettingsLib/res/values-kk/strings.xml
+++ b/packages/SettingsLib/res/values-kk/strings.xml
@@ -430,8 +430,7 @@
     <string name="force_allow_on_external_summary" msgid="8525425782530728238">"Манифест мәндеріне қарамастан, кез келген қолданбаны сыртқы жадқа жазуға рұқсат беру"</string>
     <string name="force_resizable_activities" msgid="7143612144399959606">"Әрекеттердің өлшемін өзгертуге рұқсат ету"</string>
     <string name="force_resizable_activities_summary" msgid="2490382056981583062">"Манифест мәндеріне қарамастан, бірнеше терезе режимінде барлық әрекеттердің өлшемін өзгертуге рұқсат беру"</string>
-    <string name="enable_freeform_support" msgid="8409932201445109106">"Еркін пішінді терезелерге рұқсат беру (бұрынғы)"</string>
-    <string name="enable_freeform_support_summary" msgid="2242481082356957934">"Эксперименттік бұрынғы еркін пішінді терезелерді қолдауға рұқсат береді."</string>
+    <string name="enable_freeform_support" msgid="7599125687603914253">"Еркін пішінді терезелерге рұқсат беру"</string>
     <string name="local_backup_password_title" msgid="4631017948933578709">"Компьютердегі сақтық көшірме құпия сөзі"</string>
     <string name="local_backup_password_summary_none" msgid="7646898032616361714">"Компьютердегі толық сақтық көшірмелер қазір қорғалмаған."</string>
     <string name="local_backup_password_summary_change" msgid="1707357670383995567">"Үстелдік компьютердің толық сақтық көшірмелерінің кілтсөзін өзгерту немесе жою үшін түртіңіз"</string>
@@ -496,7 +495,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"Толық зарядталғанға дейін <xliff:g id="TIME">%1$s</xliff:g> қалды."</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g>: толық зарядталуға <xliff:g id="TIME">%2$s</xliff:g> қалды"</string>
-    <string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – зарядтау оңтайландырылды"</string>
+    <!-- no translation found for power_charging_limited (4144004473976005214) -->
+    <skip />
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – Зарядталып жатыр"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - Зарядталып болады: <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - Толық заряд алуға қалған уақыт: <xliff:g id="TIME">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml
index baef5d8..577a2ae 100644
--- a/packages/SettingsLib/res/values-km/strings.xml
+++ b/packages/SettingsLib/res/values-km/strings.xml
@@ -430,8 +430,7 @@
     <string name="force_allow_on_external_summary" msgid="8525425782530728238">"ធ្វើឲ្យកម្មវិធីទាំងឡាយមានសិទ្ធិសរសេរទៅកាន់ឧបករណ៍ផ្ទុកខាងក្រៅ ដោយមិនគិតពីតម្លៃមេនីហ្វេសថ៍"</string>
     <string name="force_resizable_activities" msgid="7143612144399959606">"បង្ខំឲ្យសកម្មភាពអាចប្តូរទំហំបាន"</string>
     <string name="force_resizable_activities_summary" msgid="2490382056981583062">"ធ្វើឲ្យសកម្មភាពទាំងអស់អាចប្តូរទំហំបានសម្រាប់ពហុវិនដូ ដោយមិនគិតពីតម្លៃមេនីហ្វេសថ៍។"</string>
-    <string name="enable_freeform_support" msgid="8409932201445109106">"បើកវិនដូទម្រង់សេរី (ចាស់)"</string>
-    <string name="enable_freeform_support_summary" msgid="2242481082356957934">"បើកជំនួយសម្រាប់វិនដូទម្រង់សេរីចាស់ក្នុងដំណាក់កាលពិសោធន៍។"</string>
+    <string name="enable_freeform_support" msgid="7599125687603914253">"បើកដំណើរការផ្ទាំងវិនដូទម្រង់សេរី"</string>
     <string name="local_backup_password_title" msgid="4631017948933578709">"ពាក្យ​សម្ងាត់​បម្រុង​ទុក​លើកុំព្យូទ័រ"</string>
     <string name="local_backup_password_summary_none" msgid="7646898032616361714">"បច្ចុប្បន្ន ការ​បម្រុង​ទុក​ពេញលេញនៅលើកុំព្យូទ័រមិន​ត្រូវ​បាន​ការពារ​ទេ"</string>
     <string name="local_backup_password_summary_change" msgid="1707357670383995567">"ប៉ះដើម្បីប្ដូរ ឬយកពាក្យសម្ងាត់ចេញសម្រាប់ការបម្រុងទុកពេញលេញលើកុំព្យូទ័រ"</string>
@@ -496,7 +495,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> ទៀតទើបពេញ"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - នៅសល់ <xliff:g id="TIME">%2$s</xliff:g> ទៀតទើបពេញ"</string>
-    <string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - បានបង្កើនប្រសិទ្ធភាពនៃការសាក"</string>
+    <!-- no translation found for power_charging_limited (4144004473976005214) -->
+    <skip />
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - កំពុងសាកថ្ម"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - ពេញនៅម៉ោង <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - សាកថ្មពេញនៅម៉ោង <xliff:g id="TIME">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml
index a203ea7..69e8bff 100644
--- a/packages/SettingsLib/res/values-kn/strings.xml
+++ b/packages/SettingsLib/res/values-kn/strings.xml
@@ -430,8 +430,7 @@
     <string name="force_allow_on_external_summary" msgid="8525425782530728238">"ಮ್ಯಾನಿಫೆಸ್ಟ್ ಮೌಲ್ಯಗಳು ಯಾವುದೇ ಆಗಿದ್ದರೂ, ಬಾಹ್ಯ ಸಂಗ್ರಹಣೆಗೆ ಬರೆಯಲು ಯಾವುದೇ ಅಪ್ಲಿಕೇಶನ್‌ ಅನ್ನು ಅರ್ಹಗೊಳಿಸುತ್ತದೆ"</string>
     <string name="force_resizable_activities" msgid="7143612144399959606">"ಚಟುವಟಿಕೆಗಳನ್ನು ಮರುಗಾತ್ರಗೊಳಿಸುವಂತೆ ಒತ್ತಾಯ ಮಾಡಿ"</string>
     <string name="force_resizable_activities_summary" msgid="2490382056981583062">"ಮ್ಯಾನಿಫೆಸ್ಟ್ ಮೌಲ್ಯಗಳನ್ನು ಪರಿಗಣಿಸದೇ, ಬಹು-ವಿಂಡೊಗೆ ಎಲ್ಲಾ ಚಟುವಟಿಕೆಗಳನ್ನು ಮರುಗಾತ್ರಗೊಳಿಸುವಂತೆ ಮಾಡಿ."</string>
-    <string name="enable_freeform_support" msgid="8409932201445109106">"ಫ್ರೀಫಾರ್ಮ್ ವಿಂಡೋಗಳನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಿ (ಲೆಗಸಿ)"</string>
-    <string name="enable_freeform_support_summary" msgid="2242481082356957934">"ಪ್ರಾಯೋಗಿಕ ಲೆಗಸಿ ಫ್ರೀಫಾರ್ಮ್ ವಿಂಡೋಗಳಿಗೆ ಬೆಂಬಲವನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಿ."</string>
+    <string name="enable_freeform_support" msgid="7599125687603914253">"ಮುಕ್ತಸ್ವರೂಪದ ವಿಂಡೊಗಳನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಿ"</string>
     <string name="local_backup_password_title" msgid="4631017948933578709">"ಡೆಸ್ಕ್‌ಟಾಪ್ ಬ್ಯಾಕಪ್ ಪಾಸ್‌ವರ್ಡ್"</string>
     <string name="local_backup_password_summary_none" msgid="7646898032616361714">"ಡೆಸ್ಕ್‌ಟಾಪ್‌‌ ಪೂರ್ಣ ಬ್ಯಾಕಪ್‌‌ಗಳನ್ನು ಪ್ರಸ್ತುತ ರಕ್ಷಿಸಲಾಗಿಲ್ಲ"</string>
     <string name="local_backup_password_summary_change" msgid="1707357670383995567">"ಡೆಸ್ಕ್‌ಟಾಪ್‌ನ ಪೂರ್ಣ ಬ್ಯಾಕಪ್‌ಗಳಿಗೆ ಪಾಸ್‌ವರ್ಡ್‌ ಬದಲಾಯಿಸಲು ಅಥವಾ ತೆಗೆದುಹಾಕಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
@@ -496,7 +495,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> - ಸಮಯದಲ್ಲಿ ಪೂರ್ತಿ ಚಾರ್ಜ್ ಆಗುತ್ತದೆ"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ಸಮಯದಲ್ಲಿ ಪೂರ್ತಿ ಚಾರ್ಜ್ ಆಗುತ್ತದೆ"</string>
-    <string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - ಚಾರ್ಜಿಂಗ್ ಅನ್ನು ಆಪ್ಟಿಮೈಸ್ ಮಾಡಲಾಗಿದೆ"</string>
+    <!-- no translation found for power_charging_limited (4144004473976005214) -->
+    <skip />
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - ಚಾರ್ಜಿಂಗ್ ಆಗುತ್ತಿದೆ"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - <xliff:g id="TIME">%3$s</xliff:g> ವೇಳೆಗೆ ಭರ್ತಿಯಾಗುತ್ತದೆ"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ವೇಳೆಗೆ ಸಂಪೂರ್ಣವಾಗಿ ಚಾರ್ಜ್ ಆಗುತ್ತದೆ"</string>
diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml
index 67f1ecd..af4f37b 100644
--- a/packages/SettingsLib/res/values-ko/strings.xml
+++ b/packages/SettingsLib/res/values-ko/strings.xml
@@ -430,8 +430,7 @@
     <string name="force_allow_on_external_summary" msgid="8525425782530728238">"매니페스트 값과 관계없이 모든 앱이 외부 저장소에 작성되도록 허용"</string>
     <string name="force_resizable_activities" msgid="7143612144399959606">"활동의 크기가 조정 가능하도록 설정"</string>
     <string name="force_resizable_activities_summary" msgid="2490382056981583062">"모든 활동을 매니페스트 값에 관계없이 멀티 윈도우용으로 크기 조정 가능하도록 설정"</string>
-    <string name="enable_freeform_support" msgid="8409932201445109106">"자유 형식 창 사용(레거시)"</string>
-    <string name="enable_freeform_support_summary" msgid="2242481082356957934">"실험적인 레거시 자유 형식 창에 대한 지원을 사용하도록 설정합니다."</string>
+    <string name="enable_freeform_support" msgid="7599125687603914253">"자유 형식 창 사용"</string>
     <string name="local_backup_password_title" msgid="4631017948933578709">"데스크톱 백업 비밀번호"</string>
     <string name="local_backup_password_summary_none" msgid="7646898032616361714">"데스크톱 전체 백업에 비밀번호가 설정되어 있지 않음"</string>
     <string name="local_backup_password_summary_change" msgid="1707357670383995567">"데스크톱 전체 백업에 대한 비밀번호를 변경하거나 삭제하려면 탭하세요."</string>
@@ -496,7 +495,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> 후 충전 완료"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="TIME">%2$s</xliff:g> 후 충전 완료"</string>
-    <string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - 충전 최적화됨"</string>
+    <!-- no translation found for power_charging_limited (4144004473976005214) -->
+    <skip />
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> ‑ 충전 중"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - <xliff:g id="TIME">%3$s</xliff:g>에 완전히 충전됨"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>에 완전히 충전됨"</string>
diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml
index 7353264..95d35f3 100644
--- a/packages/SettingsLib/res/values-ky/strings.xml
+++ b/packages/SettingsLib/res/values-ky/strings.xml
@@ -430,8 +430,7 @@
     <string name="force_allow_on_external_summary" msgid="8525425782530728238">"Манифест маанилерине карабастан бардык колдонмолорду тышкы сактагычка сактоого уруксат берет"</string>
     <string name="force_resizable_activities" msgid="7143612144399959606">"Бир нече терезе режиминде өлчөмдү өзгөртүү"</string>
     <string name="force_resizable_activities_summary" msgid="2490382056981583062">"Бир нече терезе режиминде өлчөмдү өзгөртүүгө уруксат берет (манифесттин маанилерине карабастан)"</string>
-    <string name="enable_freeform_support" msgid="8409932201445109106">"Эркин формадагы терезелерди түзүүнү иштетүү (эски)"</string>
-    <string name="enable_freeform_support_summary" msgid="2242481082356957934">"Эркин формадагы эски терезелерди түзүү боюнча сынамык функциясы иштетилет."</string>
+    <string name="enable_freeform_support" msgid="7599125687603914253">"Эркин формадагы терезелерди түзүүнү иштетүү"</string>
     <string name="local_backup_password_title" msgid="4631017948933578709">"Камдык көчүрмөнүн сырсөзү"</string>
     <string name="local_backup_password_summary_none" msgid="7646898032616361714">"Толук камдык көчүрмөлөр учурда корголгон эмес"</string>
     <string name="local_backup_password_summary_change" msgid="1707357670383995567">"Иш тактасынын камдалган сырсөзүн өзгөртүү же алып салуу үчүн таптап коюңуз"</string>
@@ -496,7 +495,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> кийин толук кубатталат"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> кийин толук кубатталат"</string>
-    <string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> — Кубаттоо жакшыртылды"</string>
+    <!-- no translation found for power_charging_limited (4144004473976005214) -->
+    <skip />
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - Кубатталууда"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - Саат <xliff:g id="TIME">%3$s</xliff:g> кубатталып бүтөт"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> чейин толук кубатталат"</string>
diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml
index 3680e8d..2bf96ad 100644
--- a/packages/SettingsLib/res/values-lo/strings.xml
+++ b/packages/SettingsLib/res/values-lo/strings.xml
@@ -430,8 +430,7 @@
     <string name="force_allow_on_external_summary" msgid="8525425782530728238">"ເຮັດໃຫ້ທຸກແອັບມີສິດໄດ້ຮັບການຂຽນໃສ່ພື້ນທີ່ຈັດເກັບຂໍ້ມູນພາຍນອກ, ໂດຍບໍ່ຄຳນຶງເຖິງຄ່າ manifest"</string>
     <string name="force_resizable_activities" msgid="7143612144399959606">"ບັງ​ຄັງ​ໃຫ້​ການ​ເຄື່ອນ​ໄຫວ​ປ່ຽນ​ຂະ​ໜາດ​ໄດ້"</string>
     <string name="force_resizable_activities_summary" msgid="2490382056981583062">"ເຮັດໃຫ້ທຸກການ​ເຄື່ອນ​ໄຫວສາມາດປັບຂະໜາດໄດ້ສຳລັບຫຼາຍໜ້າຈໍ, ໂດຍບໍ່ຄຳນຶງເຖິງຄ່າ manifest."</string>
-    <string name="enable_freeform_support" msgid="8409932201445109106">"ເປີດການນຳໃຊ້ໜ້າຈໍຮູບແບບອິດສະຫຼະ (ແບບເກົ່າ)"</string>
-    <string name="enable_freeform_support_summary" msgid="2242481082356957934">"ເປີດການນຳໃຊ້ການຮອງຮັບໜ້າຈໍຮູບແບບອິດສະຫຼະແບບເກົ່າເວີຊັນທົດລອງ."</string>
+    <string name="enable_freeform_support" msgid="7599125687603914253">"ເປີດໃຊ້ໜ້າຈໍຮູບແບບອິດສະຫຼະ"</string>
     <string name="local_backup_password_title" msgid="4631017948933578709">"ລະຫັດຜ່ານການສຳຮອງຂໍ້ມູນເດັສທັອບ"</string>
     <string name="local_backup_password_summary_none" msgid="7646898032616361714">"ການ​ສຳຮອງ​ຂໍ້ມູນ​ເຕັມຮູບແບບ​ໃນ​ເດັສທັອບ​ຍັງ​ບໍ່​ໄດ້​ຮັບ​ການ​ປ້ອງກັນ​ໃນ​ເວລາ​ນີ້"</string>
     <string name="local_backup_password_summary_change" msgid="1707357670383995567">"ແຕະເພື່ອປ່ຽນ ຫຼື ລຶບລະຫັດຂອງການສຳຮອງຂໍ້ມູນເຕັມຮູບແບບໃນເດັສທັອບ"</string>
@@ -496,7 +495,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"ຍັງເຫຼືອອີກ <xliff:g id="TIME">%1$s</xliff:g> ຈຶ່ງຈະສາກເຕັມ"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"ຍັງເຫຼືອອີກ <xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ຈຶ່ງຈະສາກເຕັມ"</string>
-    <string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - ການສາກຖືກປັບໃຫ້ເໝາະສົມແລ້ວ"</string>
+    <!-- no translation found for power_charging_limited (4144004473976005214) -->
+    <skip />
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - ກຳລັງສາກໄຟ"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - ຈະເຕັມພາຍໃນ <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - ຈະສາກເຕັມພາຍໃນ <xliff:g id="TIME">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml
index 4f0d6b0..6363cdb 100644
--- a/packages/SettingsLib/res/values-lt/strings.xml
+++ b/packages/SettingsLib/res/values-lt/strings.xml
@@ -430,8 +430,7 @@
     <string name="force_allow_on_external_summary" msgid="8525425782530728238">"Nustatoma, kad visas programas būtų galima įrašyti į išorinę saugyklą, nepaisant aprašo verčių"</string>
     <string name="force_resizable_activities" msgid="7143612144399959606">"Priv. nust., kad veiksm. b. g. atl. kelių d. lang."</string>
     <string name="force_resizable_activities_summary" msgid="2490382056981583062">"Nustatyti, kad visus veiksmus būtų galima atlikti kelių dydžių languose, nepaisant aprašo verčių."</string>
-    <string name="enable_freeform_support" msgid="8409932201445109106">"Įgalinti laisvos formos langus (pasenę)"</string>
-    <string name="enable_freeform_support_summary" msgid="2242481082356957934">"Įgalinti eksperimentinių pasenusių laisvos formos langų palaikymą."</string>
+    <string name="enable_freeform_support" msgid="7599125687603914253">"Įgalinti laisvos formos langus"</string>
     <string name="local_backup_password_title" msgid="4631017948933578709">"Viet. atsrg. kop. slapt."</string>
     <string name="local_backup_password_summary_none" msgid="7646898032616361714">"Šiuo metu visos vietinės atsarginės kopijos neapsaugotos"</string>
     <string name="local_backup_password_summary_change" msgid="1707357670383995567">"Jei norite pakeisti ar pašalinti visų stalinio kompiuterio atsarginių kopijų slaptažodį, palieskite"</string>
@@ -496,7 +495,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"Liko <xliff:g id="TIME">%1$s</xliff:g>, kol bus visiškai įkrauta"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – liko <xliff:g id="TIME">%2$s</xliff:g>, kol bus visiškai įkrauta"</string>
-    <string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – įkrovimas optimizuotas"</string>
+    <!-- no translation found for power_charging_limited (4144004473976005214) -->
+    <skip />
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – įkraunama"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATUS">%2$s</xliff:g> – bus visiškai įkrauta <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> – bus visiškai įkrauta <xliff:g id="TIME">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml
index e571b84..2b82cdb 100644
--- a/packages/SettingsLib/res/values-lv/strings.xml
+++ b/packages/SettingsLib/res/values-lv/strings.xml
@@ -430,8 +430,7 @@
     <string name="force_allow_on_external_summary" msgid="8525425782530728238">"Ļauj jebkuru lietotni ierakstīt ārējā krātuvē neatkarīgi no manifesta vērtības."</string>
     <string name="force_resizable_activities" msgid="7143612144399959606">"Pielāgot darbības"</string>
     <string name="force_resizable_activities_summary" msgid="2490382056981583062">"Pielāgot visas darbības vairāku logu režīmam neatkarīgi no vērtībām manifestā."</string>
-    <string name="enable_freeform_support" msgid="8409932201445109106">"Iespējot brīvās formas logus (mantots režīms)"</string>
-    <string name="enable_freeform_support_summary" msgid="2242481082356957934">"Iespējot atbalstu eksperimentālajam, mantotajam brīvās formas logu režīmam."</string>
+    <string name="enable_freeform_support" msgid="7599125687603914253">"Iespējot brīvās formas logus"</string>
     <string name="local_backup_password_title" msgid="4631017948933578709">"Datora dublējuma parole"</string>
     <string name="local_backup_password_summary_none" msgid="7646898032616361714">"Darbvirsmas pilnie dublējumi pašlaik nav aizsargāti."</string>
     <string name="local_backup_password_summary_change" msgid="1707357670383995567">"Pieskarieties, lai mainītu vai noņemtu paroli pilniem datora dublējumiem."</string>
@@ -496,7 +495,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> — <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> līdz pilnai uzlādei"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> — <xliff:g id="TIME">%2$s</xliff:g> līdz pilnai uzlādei"</string>
-    <string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> — uzlāde optimizēta"</string>
+    <!-- no translation found for power_charging_limited (4144004473976005214) -->
+    <skip />
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> — notiek uzlāde"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> — <xliff:g id="STATUS">%2$s</xliff:g>. Tiks pilnībā uzlādēts līdz plkst. <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> — tiks pilnībā uzlādēts līdz plkst. <xliff:g id="TIME">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml
index ab30556..b135afa 100644
--- a/packages/SettingsLib/res/values-mk/strings.xml
+++ b/packages/SettingsLib/res/values-mk/strings.xml
@@ -430,8 +430,7 @@
     <string name="force_allow_on_external_summary" msgid="8525425782530728238">"Сите апликации ќе бидат подобни за запишување во надворешната меморија, независно од вредностите на манифестот"</string>
     <string name="force_resizable_activities" msgid="7143612144399959606">"Наметни променлива големина на активностите"</string>
     <string name="force_resizable_activities_summary" msgid="2490382056981583062">"Сите активности ќе имаат променлива големина во режимот со повеќе прозорци, независно од вредностите на манифестот."</string>
-    <string name="enable_freeform_support" msgid="8409932201445109106">"Овозможи прозорци со менлива големина (застарено)"</string>
-    <string name="enable_freeform_support_summary" msgid="2242481082356957934">"Овозможете поддршка за експериментални застарени прозорци со менлива големина."</string>
+    <string name="enable_freeform_support" msgid="7599125687603914253">"Овозможи прозорци со слободна форма"</string>
     <string name="local_backup_password_title" msgid="4631017948933578709">"Лозинка за бекап на компјутер"</string>
     <string name="local_backup_password_summary_none" msgid="7646898032616361714">"Целосниот бекап на компјутерот во моментов не е заштитен"</string>
     <string name="local_backup_password_summary_change" msgid="1707357670383995567">"Допрете за да се промени или отстрани лозинката за целосен бекап на компјутерот"</string>
@@ -496,7 +495,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> до полна батерија"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до полна батерија"</string>
-    <string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – полнењето е оптимизирано"</string>
+    <!-- no translation found for power_charging_limited (4144004473976005214) -->
+    <skip />
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – се полни"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATUS">%2$s</xliff:g> – Ќе се наполни целосно до <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> – Ќе се наполни целосно до <xliff:g id="TIME">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml
index d190691..5fa8961 100644
--- a/packages/SettingsLib/res/values-ml/strings.xml
+++ b/packages/SettingsLib/res/values-ml/strings.xml
@@ -430,8 +430,7 @@
     <string name="force_allow_on_external_summary" msgid="8525425782530728238">"മാനിഫെസ്റ്റ് മൂല്യങ്ങൾ പരിഗണിക്കാതെ, ബാഹ്യ സ്റ്റോറേജിലേക്ക് എഴുതപ്പെടുന്നതിന് ഏതൊരു ആപ്പിനെയും യോഗ്യമാക്കുന്നു"</string>
     <string name="force_resizable_activities" msgid="7143612144399959606">"വലുപ്പം മാറ്റാൻ പ്രവർത്തനങ്ങളെ നിർബന്ധിക്കുക"</string>
     <string name="force_resizable_activities_summary" msgid="2490382056981583062">"മാനിഫെസ്റ്റ് മൂല്യങ്ങൾ പരിഗണിക്കാതെ, എല്ലാ ആക്ടിവിറ്റികളെയും മൾട്ടി-വിൻഡോയ്ക്കായി വലുപ്പം മാറ്റുക."</string>
-    <string name="enable_freeform_support" msgid="8409932201445109106">"ഫ്രീഫോം വിൻഡോകൾ പ്രവർത്തനക്ഷമമാക്കുക (ലെഗസി)"</string>
-    <string name="enable_freeform_support_summary" msgid="2242481082356957934">"പരീക്ഷണാത്മക ലെഗസി ഫ്രീഫോം വിൻഡോകൾക്കുള്ള പിന്തുണ പ്രവർത്തനക്ഷമമാക്കുക."</string>
+    <string name="enable_freeform_support" msgid="7599125687603914253">"ഫ്രീഫോം വിൻഡോകൾ പ്രവർത്തനക്ഷമമാക്കുക"</string>
     <string name="local_backup_password_title" msgid="4631017948933578709">"ഡെ‌സ്‌ക്ടോപ്പ് ബാക്കപ്പ് പാസ്‌വേഡ്"</string>
     <string name="local_backup_password_summary_none" msgid="7646898032616361714">"ഡെസ്‌ക്‌ടോപ്പ് പൂർണ്ണ ബാക്കപ്പുകൾ നിലവിൽ പരിരക്ഷിച്ചിട്ടില്ല"</string>
     <string name="local_backup_password_summary_change" msgid="1707357670383995567">"ഡെസ്‌ക്‌ടോപ്പ് പൂർണ്ണ ബാക്കപ്പുകൾക്കായി പാസ്‌വേഡുകൾ മാറ്റാനോ നീക്കംചെയ്യാനോ ടാപ്പുചെയ്യുക"</string>
@@ -496,7 +495,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"പൂർണ്ണമാകാൻ <xliff:g id="TIME">%1$s</xliff:g> ശേഷിക്കുന്നു"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - പൂർണ്ണമാകാൻ <xliff:g id="TIME">%2$s</xliff:g> ശേഷിക്കുന്നു"</string>
-    <string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - ചാർജിംഗ് ഒപ്റ്റിമൈസ് ചെയ്തു"</string>
+    <!-- no translation found for power_charging_limited (4144004473976005214) -->
+    <skip />
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> ‑ ചാർജ് ചെയ്യുന്നു"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - <xliff:g id="TIME">%3$s</xliff:g>-നകം പൂർണ്ണമാകും"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>-നകം പൂർണ്ണമായി ചാർജ് ചെയ്യും"</string>
diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml
index c0a7939..388ea6f 100644
--- a/packages/SettingsLib/res/values-mn/strings.xml
+++ b/packages/SettingsLib/res/values-mn/strings.xml
@@ -430,8 +430,7 @@
     <string name="force_allow_on_external_summary" msgid="8525425782530728238">"Манифест утгыг нь үл хамааран дурын апп-г гадаад санах ойд бичих боломжтой болгодог"</string>
     <string name="force_resizable_activities" msgid="7143612144399959606">"Үйл ажиллагааны хэмжээг өөрчилж болохуйц болгох"</string>
     <string name="force_resizable_activities_summary" msgid="2490382056981583062">"Тодорхойлогч файлын утгыг үл хамааран, бүх үйл ажиллагааны хэмжээг олон цонхонд өөрчилж болохуйц болгоно уу."</string>
-    <string name="enable_freeform_support" msgid="8409932201445109106">"Чөлөөт хэлбэрийн цонхыг идэвхжүүлэх (уламжлалт)"</string>
-    <string name="enable_freeform_support_summary" msgid="2242481082356957934">"Туршилтын чөлөөт хэлбэрийн уламжлалт цонхны дэмжлэгийг идэвхжүүлнэ үү."</string>
+    <string name="enable_freeform_support" msgid="7599125687603914253">"Чөлөөт хэлбэрийн цонхыг идэвхжүүлэх"</string>
     <string name="local_backup_password_title" msgid="4631017948933578709">"Компьютерын нөөцлөлтийн нууц үг"</string>
     <string name="local_backup_password_summary_none" msgid="7646898032616361714">"Компьютерын бүрэн нөөцлөлт одоогоор хамгаалалтгүй байна"</string>
     <string name="local_backup_password_summary_change" msgid="1707357670383995567">"Компьютерийн бүтэн нөөцлөлтийн нууц үгийг өөрчлөх, устгах бол дарна уу"</string>
@@ -496,7 +495,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"Дүүрэх хүртэл <xliff:g id="TIME">%1$s</xliff:g> үлдсэн"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - дүүрэх хүртэл <xliff:g id="TIME">%2$s</xliff:g> үлдсэн"</string>
-    <string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Цэнэглэх явцыг оновчилсон"</string>
+    <!-- no translation found for power_charging_limited (4144004473976005214) -->
+    <skip />
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - Цэнэглэж байна"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - <xliff:g id="TIME">%3$s</xliff:g> гэхэд дүүрнэ"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> гэхэд бүрэн цэнэглэгдэнэ"</string>
diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml
index 4842a99..063b7d1 100644
--- a/packages/SettingsLib/res/values-mr/strings.xml
+++ b/packages/SettingsLib/res/values-mr/strings.xml
@@ -430,8 +430,7 @@
     <string name="force_allow_on_external_summary" msgid="8525425782530728238">"मॅनिफेस्‍ट मूल्ये काहीही असू देत, कोणत्याही अ‍ॅपला बाह्य स्टोरेजवर राइट करण्यासाठी पात्र बनविते"</string>
     <string name="force_resizable_activities" msgid="7143612144399959606">"ॲक्टिव्हिटीचा आकार बदलण्यायोग्य होण्याची सक्ती करा"</string>
     <string name="force_resizable_activities_summary" msgid="2490382056981583062">"मॅनिफेस्‍ट मूल्ये काहीही असू देत, एकाहून अधिक विंडोसाठी सर्व अ‍ॅक्टिव्हिटीचा आकार बदलण्यायोग्य करा."</string>
-    <string name="enable_freeform_support" msgid="8409932201445109106">"फ्रीफॉर्म विंडो सुरू करा (लेगसी)"</string>
-    <string name="enable_freeform_support_summary" msgid="2242481082356957934">"प्रायोगिक लेगसी फ्रीफॉर्म विंडोसाठी सपोर्ट सुरू करा."</string>
+    <string name="enable_freeform_support" msgid="7599125687603914253">"freeform windows सुरू करा"</string>
     <string name="local_backup_password_title" msgid="4631017948933578709">"डेस्कटॉप बॅकअप पासवर्ड"</string>
     <string name="local_backup_password_summary_none" msgid="7646898032616361714">"डेस्कटॉप पूर्ण बॅक अप सध्या संरक्षित नाहीत"</string>
     <string name="local_backup_password_summary_change" msgid="1707357670383995567">"डेस्कटॉपच्या पूर्ण बॅकअपसाठी असलेला पासवर्ड बदलण्यासाठी किंवा काढण्यासाठी टॅप  करा"</string>
@@ -495,8 +494,9 @@
     <string name="power_remaining_only_more_than_subtext" msgid="4873750633368888062">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> पेक्षा जास्त शिल्लक आहे"</string>
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"पूर्ण चार्ज होण्यासाठी <xliff:g id="TIME">%1$s</xliff:g> शिल्लक आहेत"</string>
-    <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - पूर्ण चार्ज होण्यासाठी <xliff:g id="TIME">%2$s</xliff:g> शिल्लक आहे"</string>
-    <string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - चार्जिंग ऑप्टिमाइझ केले"</string>
+    <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> या वेळेत बॅटरी पूर्ण चार्ज होईल"</string>
+    <!-- no translation found for power_charging_limited (4144004473976005214) -->
+    <skip />
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> ‑ चार्ज होत आहे"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - <xliff:g id="TIME">%3$s</xliff:g> पर्यंत पूर्ण होईल"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> पर्यंत पूर्णपणे चार्ज होईल"</string>
diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml
index 01d6409..48ed79d 100644
--- a/packages/SettingsLib/res/values-ms/strings.xml
+++ b/packages/SettingsLib/res/values-ms/strings.xml
@@ -430,8 +430,7 @@
     <string name="force_allow_on_external_summary" msgid="8525425782530728238">"Menjadikan sebarang apl layak ditulis ke storan luaran, tanpa mengambil kira nilai manifes"</string>
     <string name="force_resizable_activities" msgid="7143612144399959606">"Paksa aktiviti supaya boleh diubah saiz"</string>
     <string name="force_resizable_activities_summary" msgid="2490382056981583062">"Bolehkan semua saiz aktiviti diubah untuk berbilang tetingkap, tanpa mengambil kira nilai manifes."</string>
-    <string name="enable_freeform_support" msgid="8409932201445109106">"Dayakan tetingkap bentuk bebas (lama)"</string>
-    <string name="enable_freeform_support_summary" msgid="2242481082356957934">"Dayakan sokongan untuk tetingkap bentuk bebas lama yang bersifat percubaan."</string>
+    <string name="enable_freeform_support" msgid="7599125687603914253">"Dayakan tetingkap bentuk bebas"</string>
     <string name="local_backup_password_title" msgid="4631017948933578709">"Kata laluan sandaran desktop"</string>
     <string name="local_backup_password_summary_none" msgid="7646898032616361714">"Sandaran penuh desktop tidak dilindungi pada masa ini"</string>
     <string name="local_backup_password_summary_change" msgid="1707357670383995567">"Ketik untuk menukar atau mengalih keluar kata laluan untuk sandaran penuh desktop"</string>
@@ -496,7 +495,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> lagi sebelum penuh"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> lagi sebelum penuh"</string>
-    <string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Pengecasan dioptimumkan"</string>
+    <!-- no translation found for power_charging_limited (4144004473976005214) -->
+    <skip />
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - Mengecas"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - Penuh pada <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - Dicas sepenuhnya pada <xliff:g id="TIME">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml
index 92b630d..d15a0be 100644
--- a/packages/SettingsLib/res/values-my/strings.xml
+++ b/packages/SettingsLib/res/values-my/strings.xml
@@ -430,8 +430,7 @@
     <string name="force_allow_on_external_summary" msgid="8525425782530728238">"သတ်မှတ်တန်ဖိုးများ မည်သို့ပင်ရှိစေ ပြင်ပသိုလှောင်ခန်းများသို့ မည်သည့်အက်ပ်ကိုမဆို ဝင်ရောက်ခွင့်ပြုသည်"</string>
     <string name="force_resizable_activities" msgid="7143612144399959606">"လုပ်ဆောင်ချက်များ အရွယ်ပြောင်းနိုင်ခြင်း"</string>
     <string name="force_resizable_activities_summary" msgid="2490382056981583062">"သတ်မှတ်တန်ဖိုး မည်သို့ပင်ရှိစေ ဝင်းဒိုးများ၏ လုပ်ဆောင်မှုအားလုံးကို အရွယ်အစားပြင်သည်။"</string>
-    <string name="enable_freeform_support" msgid="8409932201445109106">"အလွတ်ပုံစံ ဝင်းဒိုးများ ဖွင့်ပါ (အဟောင်း)"</string>
-    <string name="enable_freeform_support_summary" msgid="2242481082356957934">"အလွတ်ပုံစံဝင်းဒိုးအဟောင်းများ စမ်းသပ်ရန် ပံ့ပိုးမှုရယူပါ။"</string>
+    <string name="enable_freeform_support" msgid="7599125687603914253">"ပုံစံမျိုးစုံ ဝင်းဒိုးများ ဖွင့်ရန်"</string>
     <string name="local_backup_password_title" msgid="4631017948933578709">"ဒက်စ်တော့ အရန်စကားဝှက်"</string>
     <string name="local_backup_password_summary_none" msgid="7646898032616361714">"ဒက်စ်တော့ တစ်ခုလုံး အရန်သိမ်းဆည်းခြင်းကို လက်ရှိတွင် ကာကွယ်မထားပါ"</string>
     <string name="local_backup_password_summary_change" msgid="1707357670383995567">"ဒက်စ်တော့ အပြည့်အဝ အရန်သိမ်းခြင်းအတွက် စကားဝှက်ကို ပြောင်းရန် သို့မဟုတ် ဖယ်ရှားရန် တို့ပါ။"</string>
@@ -496,7 +495,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"အားပြည့်ရန် <xliff:g id="TIME">%1$s</xliff:g> လိုသည်"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - အားပြည့်ရန် <xliff:g id="TIME">%2$s</xliff:g> လိုသည်"</string>
-    <string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - အားသွင်းခြင်းကို အကောင်းဆုံးပြင်ဆင်ထားသည်"</string>
+    <!-- no translation found for power_charging_limited (4144004473976005214) -->
+    <skip />
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - အားသွင်းနေသည်"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - <xliff:g id="TIME">%3$s</xliff:g> တွင် အားပြည့်မည်"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> တွင် အားပြည့်မည်"</string>
diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml
index 75b04ad..8f5663a 100644
--- a/packages/SettingsLib/res/values-nb/strings.xml
+++ b/packages/SettingsLib/res/values-nb/strings.xml
@@ -430,8 +430,7 @@
     <string name="force_allow_on_external_summary" msgid="8525425782530728238">"Dette gjør at alle apper kan lagres på eksterne lagringsmedier – uavhengig av manifestverdier"</string>
     <string name="force_resizable_activities" msgid="7143612144399959606">"Alle aktiviteter kan endre størrelse"</string>
     <string name="force_resizable_activities_summary" msgid="2490382056981583062">"Gjør at alle aktiviteter kan endre størrelse for flervindusmodus, uavhengig av manifestverdier."</string>
-    <string name="enable_freeform_support" msgid="8409932201445109106">"Slå på vinduer i fritt format (eldre versjon)"</string>
-    <string name="enable_freeform_support_summary" msgid="2242481082356957934">"Slå på støtte for eldre versjon av vinduer i eksperimentelt fritt format."</string>
+    <string name="enable_freeform_support" msgid="7599125687603914253">"Slå på vinduer i fritt format"</string>
     <string name="local_backup_password_title" msgid="4631017948933578709">"Passord for sikkerhetskopiering på datamaskin"</string>
     <string name="local_backup_password_summary_none" msgid="7646898032616361714">"Fullstendig sikkerhetskopiering på datamaskin er ikke beskyttet"</string>
     <string name="local_backup_password_summary_change" msgid="1707357670383995567">"Trykk for å endre eller fjerne passordet for fullstendige sikkerhetskopier på datamaskinen"</string>
@@ -496,7 +495,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"Fulladet om <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – Fulladet om <xliff:g id="TIME">%2$s</xliff:g>"</string>
-    <string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – Ladingen er optimalisert"</string>
+    <!-- no translation found for power_charging_limited (4144004473976005214) -->
+    <skip />
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – lader"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATUS">%2$s</xliff:g> – Fulladet innen <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> – Fulladet innen <xliff:g id="TIME">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml
index b252854..ddb58d5 100644
--- a/packages/SettingsLib/res/values-ne/strings.xml
+++ b/packages/SettingsLib/res/values-ne/strings.xml
@@ -430,8 +430,7 @@
     <string name="force_allow_on_external_summary" msgid="8525425782530728238">"तोकिएको नियमको ख्याल नगरी एपलाई बाह्य भण्डारणमा चल्ने बनाउनुहोस्"</string>
     <string name="force_resizable_activities" msgid="7143612144399959606">"बलपूर्वक एपहरूको आकार मिलाउन मिल्ने बनाउनुहोस्"</string>
     <string name="force_resizable_activities_summary" msgid="2490382056981583062">"तोकिएको नियमको ख्याल नगरी एपलाई एकभन्दा बढी विन्डोमा रिसाइज गर्न सकिने बनाउनुहोस्।"</string>
-    <string name="enable_freeform_support" msgid="8409932201445109106">"फ्रिफर्म विन्डोहरू अन गर्नुहोस् (लिगेसी)"</string>
-    <string name="enable_freeform_support_summary" msgid="2242481082356957934">"प्रयोगात्मक लिगेसी फ्रिफर्म विन्डोहरू चल्ने बनाउनुहोस्"</string>
+    <string name="enable_freeform_support" msgid="7599125687603914253">"फ्रिफर्म विन्डोहरू अन गर्नुहोस्"</string>
     <string name="local_backup_password_title" msgid="4631017948933578709">"डेस्कटप ब्याकअप पासवर्ड"</string>
     <string name="local_backup_password_summary_none" msgid="7646898032616361714">"हाल डेस्कटपका सबै ब्याकअप पासवर्ड सुरक्षित छैनन्"</string>
     <string name="local_backup_password_summary_change" msgid="1707357670383995567">"डेस्कटप पूर्ण ब्याकअपको लागि पासवर्ड बदल्न वा हटाउन ट्याप गर्नुहोस्"</string>
@@ -496,7 +495,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"पूरा चार्ज हुन <xliff:g id="TIME">%1$s</xliff:g> लाग्ने छ"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - पूरा चार्ज हुन <xliff:g id="TIME">%2$s</xliff:g> लाग्ने छ"</string>
-    <string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - चार्ज गर्ने प्रक्रिया अप्टिमाइज गरिएको छ"</string>
+    <!-- no translation found for power_charging_limited (4144004473976005214) -->
+    <skip />
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - चार्ज गरिँदै छ"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - <xliff:g id="TIME">%3$s</xliff:g> बजेसम्ममा पूरा चार्ज हुने छ"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> बजेसम्ममा पूरा चार्ज हुने छ"</string>
diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml
index 8577f48..abf00f0 100644
--- a/packages/SettingsLib/res/values-nl/strings.xml
+++ b/packages/SettingsLib/res/values-nl/strings.xml
@@ -430,8 +430,7 @@
     <string name="force_allow_on_external_summary" msgid="8525425782530728238">"Hiermee komt elke app in aanmerking voor schrijven naar externe opslag, ongeacht de manifestwaarden"</string>
     <string name="force_resizable_activities" msgid="7143612144399959606">"Formaat activiteiten geforceerd aanpasbaar maken"</string>
     <string name="force_resizable_activities_summary" msgid="2490382056981583062">"Maak het formaat van alle activiteiten aanpasbaar, ongeacht de manifestwaarden"</string>
-    <string name="enable_freeform_support" msgid="8409932201445109106">"Vensters met vrije vorm aanzetten (verouderd)"</string>
-    <string name="enable_freeform_support_summary" msgid="2242481082356957934">"Zet ondersteuning voor verouderde vensters met experimentele vrije vorm aan."</string>
+    <string name="enable_freeform_support" msgid="7599125687603914253">"Vensters met vrije vorm aanzetten"</string>
     <string name="local_backup_password_title" msgid="4631017948933578709">"Wachtwoord desktopback-up"</string>
     <string name="local_backup_password_summary_none" msgid="7646898032616361714">"Volledige back-ups naar desktops zijn momenteel niet beveiligd"</string>
     <string name="local_backup_password_summary_change" msgid="1707357670383995567">"Tik om het wachtwoord voor volledige back-ups naar desktops te wijzigen of te verwijderen"</string>
@@ -496,7 +495,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"Vol over <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - vol over <xliff:g id="TIME">%2$s</xliff:g>"</string>
-    <string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Opladen geoptimaliseerd"</string>
+    <!-- no translation found for power_charging_limited (4144004473976005214) -->
+    <skip />
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> ‑ Opladen"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - Vol om <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - Volledig opgeladen om <xliff:g id="TIME">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml
index 9fdb99b..941a627 100644
--- a/packages/SettingsLib/res/values-or/strings.xml
+++ b/packages/SettingsLib/res/values-or/strings.xml
@@ -430,8 +430,7 @@
     <string name="force_allow_on_external_summary" msgid="8525425782530728238">"ଯେକୌଣସି ଆପ୍‌କୁ ଏକ୍ସଟର୍ନଲ୍ ଷ୍ଟୋରେଜ୍‌ରେ ଲେଖାଯୋଗ୍ୟ କରନ୍ତୁ, ମେନିଫେଷ୍ଟ ମୂଲ୍ୟ ଯାହା ହୋଇଥାଉ ନା କାହିଁକି"</string>
     <string name="force_resizable_activities" msgid="7143612144399959606">"ୱିଣ୍ଡୋ ହିସାବରେ କାର୍ଯ୍ୟକଳାପର ଆକାର ବଦଳାନ୍ତୁ"</string>
     <string name="force_resizable_activities_summary" msgid="2490382056981583062">"ମାନିଫେଷ୍ଟ ମୂଲ୍ୟ ଯାହା ହୋଇଥାଉ ନା କାହିଁକି, ଏକାଧିକ-ୱିଣ୍ଡୋ ପାଇଁ ସମସ୍ତ କାର୍ଯ୍ୟକଳାପକୁ ରିସାଇଜ କରନ୍ତୁ।"</string>
-    <string name="enable_freeform_support" msgid="8409932201445109106">"ଫ୍ରିଫର୍ମ ୱିଣ୍ଡୋ ସକ୍ଷମ କରନ୍ତୁ (ଲିଗାସି)"</string>
-    <string name="enable_freeform_support_summary" msgid="2242481082356957934">"ପରୀକ୍ଷାମୂଳକ ଲିଗାସି ଫ୍ରିଫର୍ମ ୱିଣ୍ଡୋ ପାଇଁ ସପୋର୍ଟ ସକ୍ଷମ କରନ୍ତୁ।"</string>
+    <string name="enable_freeform_support" msgid="7599125687603914253">"ଫ୍ରିଫର୍ମ ୱିଣ୍ଡୋକୁ ସକ୍ଷମ କରନ୍ତୁ"</string>
     <string name="local_backup_password_title" msgid="4631017948933578709">"ଡେସ୍କଟପ ବେକଅପ ପାସୱାର୍ଡ"</string>
     <string name="local_backup_password_summary_none" msgid="7646898032616361714">"ଡେସ୍କଟପ୍‌ର ସମ୍ପୂର୍ଣ୍ଣ ବ୍ୟାକଅପ୍‌ଗୁଡ଼ିକ ବର୍ତ୍ତମାନ ସୁରକ୍ଷିତ ନୁହେଁ"</string>
     <string name="local_backup_password_summary_change" msgid="1707357670383995567">"ଡେସ୍କଟପ୍‌ର ସମ୍ପୂର୍ଣ୍ଣ ବ୍ୟାକ୍‌ଅପ୍‌ ପାଇଁ ପାସ୍‌ୱର୍ଡ ବଦଳାଇବା କିମ୍ୱା କାଢ଼ିଦେବା ନିମନ୍ତେ ଟାପ୍‌ କରନ୍ତୁ"</string>
@@ -496,7 +495,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"ପୂର୍ଣ୍ଣ ହେବାକୁ ଆଉ <xliff:g id="TIME">%1$s</xliff:g> ବାକି ଅଛି"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - ପୂର୍ଣ୍ଣ ହେବାକୁ ଆଉ <xliff:g id="TIME">%2$s</xliff:g> ବାକି ଅଛି"</string>
-    <string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - ଚାର୍ଜିଂକୁ ଅପ୍ଟିମାଇଜ କରାଯାଇଛି"</string>
+    <!-- no translation found for power_charging_limited (4144004473976005214) -->
+    <skip />
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> ‑ ଚାର୍ଜିଂ"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - <xliff:g id="TIME">%3$s</xliff:g> ସୁଦ୍ଧା ସମ୍ପୂର୍ଣ୍ଣ ଚାର୍ଜ ହେବ"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> • <xliff:g id="TIME">%2$s</xliff:g> ସୁଦ୍ଧା ସମ୍ପୂର୍ଣ୍ଣ ଭାବେ ଚାର୍ଜ ହୋଇଯିବ"</string>
diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml
index 64981a7..09fd778 100644
--- a/packages/SettingsLib/res/values-pa/strings.xml
+++ b/packages/SettingsLib/res/values-pa/strings.xml
@@ -430,8 +430,7 @@
     <string name="force_allow_on_external_summary" msgid="8525425782530728238">"ਮੈਨੀਫੈਸਟ ਮੁੱਲਾਂ ਦੀ ਪਰਵਾਹ ਕੀਤੇ ਬਿਨਾਂ, ਕਿਸੇ ਵੀ ਐਪ ਨੂੰ ਬਾਹਰੀ ਸਟੋਰੇਜ \'ਤੇ ਲਿਖਣ ਦੇ ਯੋਗ ਬਣਾਉਂਦੀ ਹੈ"</string>
     <string name="force_resizable_activities" msgid="7143612144399959606">"ਸਰਗਰਮੀਆਂ ਨੂੰ ਜ਼ਬਰਦਸਤੀ ਆਕਾਰ ਬਦਲਣਯੋਗ ਬਣਾਓ"</string>
     <string name="force_resizable_activities_summary" msgid="2490382056981583062">"ਮੈਨੀਫ਼ੈਸਟ ਮੁੱਲਾਂ ਦੀ ਪਰਵਾਹ ਕੀਤੇ ਬਿਨਾਂ, ਮਲਟੀ-ਵਿੰਡੋ ਲਈ ਸਾਰੀਆਂ ਸਰਗਰਮੀਆਂ ਨੂੰ ਆਕਾਰ ਬਦਲਣਯੋਗ ਬਣਾਓ।"</string>
-    <string name="enable_freeform_support" msgid="8409932201445109106">"ਫ੍ਰੀਫਾਰਮ ਵਿੰਡੋਆਂ ਚਾਲੂ ਕਰੋ (ਵਿਰਾਸਤੀ)"</string>
-    <string name="enable_freeform_support_summary" msgid="2242481082356957934">"ਪ੍ਰਯੋਗਮਈ ਵਿਰਾਸਤੀ ਫ੍ਰੀਫਾਰਮ ਵਿੰਡੋਆਂ ਲਈ ਸਹਾਇਤਾ ਚਾਲੂ ਕਰੋ।"</string>
+    <string name="enable_freeform_support" msgid="7599125687603914253">"ਫ੍ਰੀਫਾਰਮ ਵਿੰਡੋਜ਼ ਨੂੰ ਚਾਲੂ ਕਰੋ"</string>
     <string name="local_backup_password_title" msgid="4631017948933578709">"ਡੈਸਕਟਾਪ ਬੈਕਅੱਪ ਪਾਸਵਰਡ"</string>
     <string name="local_backup_password_summary_none" msgid="7646898032616361714">"ਡੈਸਕਟਾਪ ਦੇ ਪੂਰੇ ਬੈਕਅੱਪ ਇਸ ਵੇਲੇ ਸੁਰੱਖਿਅਤ ਨਹੀਂ ਹਨ"</string>
     <string name="local_backup_password_summary_change" msgid="1707357670383995567">"ਡੈਸਕਟਾਪ ਦੇ ਮੁਕੰਮਲ ਬੈਕਅੱਪਾਂ ਲਈ ਪਾਸਵਰਡ ਨੂੰ ਬਦਲਣ ਜਾਂ ਹਟਾਉਣ ਲਈ ਟੈਪ ਕਰੋ"</string>
@@ -496,7 +495,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"ਬੈਟਰੀ ਪੂਰੀ ਚਾਰਜ ਹੋਣ ਵਿੱਚ <xliff:g id="TIME">%1$s</xliff:g> ਬਾਕੀ"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - ਬੈਟਰੀ ਪੂਰੀ ਚਾਰਜ ਹੋਣ ਵਿੱਚ <xliff:g id="TIME">%2$s</xliff:g> ਬਾਕੀ"</string>
-    <string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - ਚਾਰਜਿੰਗ ਨੂੰ ਸੁਯੋਗ ਬਣਾਇਆ ਗਿਆ"</string>
+    <!-- no translation found for power_charging_limited (4144004473976005214) -->
+    <skip />
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - ਚਾਰਜ ਹੋ ਰਹੀ ਹੈ"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - <xliff:g id="TIME">%3$s</xliff:g> ਤੱਕ ਪੂਰੀ ਤਰ੍ਹਾਂ ਚਾਰਜ ਹੋ ਜਾਵੇਗੀ"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ਤੱਕ ਪੂਰੀ ਤਰ੍ਹਾਂ ਚਾਰਜ ਹੋ ਜਾਵੇਗੀ"</string>
diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml
index a1ca4a6..7840cb0 100644
--- a/packages/SettingsLib/res/values-pl/strings.xml
+++ b/packages/SettingsLib/res/values-pl/strings.xml
@@ -430,8 +430,7 @@
     <string name="force_allow_on_external_summary" msgid="8525425782530728238">"Pozwala na zapis aplikacji w pamięci zewnętrznej niezależnie od wartości w pliku manifestu"</string>
     <string name="force_resizable_activities" msgid="7143612144399959606">"Wymuś zmianę rozmiaru okien aktywności"</string>
     <string name="force_resizable_activities_summary" msgid="2490382056981583062">"Zezwalaj na zmianę rozmiaru wszystkich okien aktywności w trybie wielu okien niezależnie od ustawień w pliku manifestu"</string>
-    <string name="enable_freeform_support" msgid="8409932201445109106">"Włącz dowolny rozmiar okien (starsza wersja)"</string>
-    <string name="enable_freeform_support_summary" msgid="2242481082356957934">"Włącz obsługę eksperymentalnej funkcji dowolnego rozmiaru okien w starszej wersji"</string>
+    <string name="enable_freeform_support" msgid="7599125687603914253">"Włącz dowolny rozmiar okien"</string>
     <string name="local_backup_password_title" msgid="4631017948933578709">"Hasło kopii zapasowej"</string>
     <string name="local_backup_password_summary_none" msgid="7646898032616361714">"Pełne kopie zapasowe na komputerze nie są obecnie chronione"</string>
     <string name="local_backup_password_summary_change" msgid="1707357670383995567">"Dotknij, by zmienić lub usunąć hasło pełnych kopii zapasowych na komputerze."</string>
@@ -496,7 +495,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> do pełnego naładowania"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do pełnego naładowania"</string>
-    <string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – ładowanie zoptymalizowane"</string>
+    <!-- no translation found for power_charging_limited (4144004473976005214) -->
+    <skip />
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – ładowanie"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATUS">%2$s</xliff:g> – Bateria będzie pełna do <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> – Pełne naładowanie do <xliff:g id="TIME">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml
index 13f9c40..9209d62 100644
--- a/packages/SettingsLib/res/values-pt-rBR/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml
@@ -430,8 +430,7 @@
     <string name="force_allow_on_external_summary" msgid="8525425782530728238">"Qualificar apps para gravação em armazenamento externo, independentemente dos valores do manifesto"</string>
     <string name="force_resizable_activities" msgid="7143612144399959606">"Forçar atividades a serem redimensionáveis"</string>
     <string name="force_resizable_activities_summary" msgid="2490382056981583062">"Tornar todas as atividades redimensionáveis para várias janelas, independentemente dos valores do manifesto"</string>
-    <string name="enable_freeform_support" msgid="8409932201445109106">"Ativar janelas de forma livre (legado)"</string>
-    <string name="enable_freeform_support_summary" msgid="2242481082356957934">"Ativar a compatibilidade com janelas experimentais legadas de forma livre."</string>
+    <string name="enable_freeform_support" msgid="7599125687603914253">"Ativar janelas de forma livre"</string>
     <string name="local_backup_password_title" msgid="4631017948933578709">"Senha de backup local"</string>
     <string name="local_backup_password_summary_none" msgid="7646898032616361714">"Os backups completos não estão protegidos no momento"</string>
     <string name="local_backup_password_summary_change" msgid="1707357670383995567">"Toque para alterar ou remover a senha de backups completos do desktop"</string>
@@ -496,7 +495,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> até a conclusão"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="TIME">%2$s</xliff:g> até a conclusão"</string>
-    <string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Carregamento otimizado"</string>
+    <!-- no translation found for power_charging_limited (4144004473976005214) -->
+    <skip />
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> (carregando)"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - Carregado até <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - Totalmente carregado até <xliff:g id="TIME">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml
index 260e58e..2cd6811 100644
--- a/packages/SettingsLib/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml
@@ -430,8 +430,7 @@
     <string name="force_allow_on_external_summary" msgid="8525425782530728238">"Torna qualquer aplicação elegível para ser gravada no armazenamento externo, independentemente dos valores do manifesto"</string>
     <string name="force_resizable_activities" msgid="7143612144399959606">"Forçar as atividades a serem redimensionáveis"</string>
     <string name="force_resizable_activities_summary" msgid="2490382056981583062">"Tornar todas as atividades redimensionáveis para várias janelas, independentemente dos valores do manifesto."</string>
-    <string name="enable_freeform_support" msgid="8409932201445109106">"Ativar janelas de forma livre (antigo)"</string>
-    <string name="enable_freeform_support_summary" msgid="2242481082356957934">"Ativar compatibilidade com janelas de forma livre antigas experimentais."</string>
+    <string name="enable_freeform_support" msgid="7599125687603914253">"Ativar janelas de forma livre"</string>
     <string name="local_backup_password_title" msgid="4631017948933578709">"Palavra-passe cópia do computador"</string>
     <string name="local_backup_password_summary_none" msgid="7646898032616361714">"As cópias de segurança completas no ambiente de trabalho não estão atualmente protegidas"</string>
     <string name="local_backup_password_summary_change" msgid="1707357670383995567">"Tocar para alterar ou remover a palavra-passe para cópias de segurança completas no ambiente de trabalho"</string>
@@ -496,7 +495,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> até à carga máxima"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> até à carga máxima"</string>
-    <string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g>: carregamento otimizado"</string>
+    <!-- no translation found for power_charging_limited (4144004473976005214) -->
+    <skip />
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – A carregar"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATUS">%2$s</xliff:g> – Completo à(s) <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> – Completamente carregado à(s) <xliff:g id="TIME">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml
index 13f9c40..9209d62 100644
--- a/packages/SettingsLib/res/values-pt/strings.xml
+++ b/packages/SettingsLib/res/values-pt/strings.xml
@@ -430,8 +430,7 @@
     <string name="force_allow_on_external_summary" msgid="8525425782530728238">"Qualificar apps para gravação em armazenamento externo, independentemente dos valores do manifesto"</string>
     <string name="force_resizable_activities" msgid="7143612144399959606">"Forçar atividades a serem redimensionáveis"</string>
     <string name="force_resizable_activities_summary" msgid="2490382056981583062">"Tornar todas as atividades redimensionáveis para várias janelas, independentemente dos valores do manifesto"</string>
-    <string name="enable_freeform_support" msgid="8409932201445109106">"Ativar janelas de forma livre (legado)"</string>
-    <string name="enable_freeform_support_summary" msgid="2242481082356957934">"Ativar a compatibilidade com janelas experimentais legadas de forma livre."</string>
+    <string name="enable_freeform_support" msgid="7599125687603914253">"Ativar janelas de forma livre"</string>
     <string name="local_backup_password_title" msgid="4631017948933578709">"Senha de backup local"</string>
     <string name="local_backup_password_summary_none" msgid="7646898032616361714">"Os backups completos não estão protegidos no momento"</string>
     <string name="local_backup_password_summary_change" msgid="1707357670383995567">"Toque para alterar ou remover a senha de backups completos do desktop"</string>
@@ -496,7 +495,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> até a conclusão"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="TIME">%2$s</xliff:g> até a conclusão"</string>
-    <string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Carregamento otimizado"</string>
+    <!-- no translation found for power_charging_limited (4144004473976005214) -->
+    <skip />
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> (carregando)"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - Carregado até <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - Totalmente carregado até <xliff:g id="TIME">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml
index 5f8f367..e33c02f 100644
--- a/packages/SettingsLib/res/values-ro/strings.xml
+++ b/packages/SettingsLib/res/values-ro/strings.xml
@@ -430,8 +430,7 @@
     <string name="force_allow_on_external_summary" msgid="8525425782530728238">"Permite scrierea oricărei aplicații eligibile în stocarea externă, indiferent de valorile manifestului"</string>
     <string name="force_resizable_activities" msgid="7143612144399959606">"Forțează redimensionarea activităților"</string>
     <string name="force_resizable_activities_summary" msgid="2490382056981583062">"Permite redimensionarea tuturor activităților pentru modul cu ferestre multiple, indiferent de valorile manifestului."</string>
-    <string name="enable_freeform_support" msgid="8409932201445109106">"Activează ferestrele cu formă liberă (vechi)"</string>
-    <string name="enable_freeform_support_summary" msgid="2242481082356957934">"Activează compatibilitatea pentru ferestrele experimentale vechi cu formă liberă."</string>
+    <string name="enable_freeform_support" msgid="7599125687603914253">"Activează ferestrele cu formă liberă"</string>
     <string name="local_backup_password_title" msgid="4631017948933578709">"Parolă backup computer"</string>
     <string name="local_backup_password_summary_none" msgid="7646898032616361714">"În prezent, backupurile complete pe computer nu sunt protejate"</string>
     <string name="local_backup_password_summary_change" msgid="1707357670383995567">"Atinge ca să modifici sau să elimini parola pentru backupurile complete pe desktop"</string>
@@ -496,7 +495,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> până la finalizare"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> până la finalizare"</string>
-    <string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – Încărcare optimizată"</string>
+    <!-- no translation found for power_charging_limited (4144004473976005214) -->
+    <skip />
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – Se încarcă"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - Timp rămas <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - Complet încărcat în <xliff:g id="TIME">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml
index f492b65..4de495a 100644
--- a/packages/SettingsLib/res/values-ru/strings.xml
+++ b/packages/SettingsLib/res/values-ru/strings.xml
@@ -430,8 +430,7 @@
     <string name="force_allow_on_external_summary" msgid="8525425782530728238">"Разрешить сохранение приложений на внешних накопителях (независимо от значений в манифесте)"</string>
     <string name="force_resizable_activities" msgid="7143612144399959606">"Изменение размера в многооконном режиме"</string>
     <string name="force_resizable_activities_summary" msgid="2490382056981583062">"Разрешить изменение размера окон в многооконном режиме (независимо от значений в манифесте)"</string>
-    <string name="enable_freeform_support" msgid="8409932201445109106">"Разрешить окна произвольной формы (устаревшее)"</string>
-    <string name="enable_freeform_support_summary" msgid="2242481082356957934">"Включить устаревшую экспериментальную функцию для создания окон произвольной формы"</string>
+    <string name="enable_freeform_support" msgid="7599125687603914253">"Разрешить создание окон произвольной формы"</string>
     <string name="local_backup_password_title" msgid="4631017948933578709">"Пароль для резервного копирования"</string>
     <string name="local_backup_password_summary_none" msgid="7646898032616361714">"Полные локальные резервные копии в настоящее время не защищены"</string>
     <string name="local_backup_password_summary_change" msgid="1707357670383995567">"Нажмите, чтобы изменить или удалить пароль для резервного копирования"</string>
@@ -496,7 +495,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> до полной зарядки"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до полной зарядки"</string>
-    <string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – зарядка оптимизирована"</string>
+    <!-- no translation found for power_charging_limited (4144004473976005214) -->
+    <skip />
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – заряжается"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g>. <xliff:g id="STATUS">%2$s</xliff:g> – завершится к <xliff:g id="TIME">%3$s</xliff:g>."</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> – полностью зарядится к <xliff:g id="TIME">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml
index 42cde8b..9a4ca8e 100644
--- a/packages/SettingsLib/res/values-si/strings.xml
+++ b/packages/SettingsLib/res/values-si/strings.xml
@@ -430,8 +430,7 @@
     <string name="force_allow_on_external_summary" msgid="8525425782530728238">"මැනිෆෙස්ට් අගයන් නොසලකා, ඕනෑම යෙදුමක් බාහිර ගබඩාවට ලිවීමට සුදුසුකම් ලබා දෙයි"</string>
     <string name="force_resizable_activities" msgid="7143612144399959606">"ක්‍රියාකාරකම් ප්‍රතිප්‍රමාණ කළ හැකි බවට බල කරන්න"</string>
     <string name="force_resizable_activities_summary" msgid="2490382056981583062">"මැනිෆෙස්ට් අගයන් නොසලකා, සියලු ක්‍රියාකාරකම් බහු-කවුළුව සඳහා ප්‍රතිප්‍රමාණ කළ හැකි බවට පත් කරන්න."</string>
-    <string name="enable_freeform_support" msgid="8409932201445109106">"නිදහස් ආකෘති කවුළු සබල කරන්න (ලෙගසිය)"</string>
-    <string name="enable_freeform_support_summary" msgid="2242481082356957934">"පරීක්ෂණාත්මක ලෙගසි නිදහස් ආකෘති කවුළු සඳහා සහාය සබල කරන්න."</string>
+    <string name="enable_freeform_support" msgid="7599125687603914253">"අනියම් හැඩැති කවුළු සබල කරන්න"</string>
     <string name="local_backup_password_title" msgid="4631017948933578709">"ඩෙස්ක්ටොප් උපස්ථ මුරපදය"</string>
     <string name="local_backup_password_summary_none" msgid="7646898032616361714">"ඩෙස්ක්ටොප් සම්පූර්ණ උපස්ථ දැනට ආරක්ෂා කර නොමැත"</string>
     <string name="local_backup_password_summary_change" msgid="1707357670383995567">"ඩෙස්ක්ටොප් සම්පූර්ණ උපස්ථ සඳහා මුරපදය වෙනස් කිරීමට හෝ ඉවත් කිරීමට තට්ටු කරන්න"</string>
@@ -496,7 +495,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"සම්පූර්ණ වීමට <xliff:g id="TIME">%1$s</xliff:g>ක් ඉතිරියි"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - සම්පූර්ණ වීමට <xliff:g id="TIME">%2$s</xliff:g>ක් ඉතිරියි"</string>
-    <string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - ආරෝපණය ප්‍රශස්ත කර ඇත"</string>
+    <!-- no translation found for power_charging_limited (4144004473976005214) -->
+    <skip />
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - ආරෝපණය වේ"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - <xliff:g id="TIME">%3$s</xliff:g> හට පෙර සම්පූර්ණයි"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> හට පෙර සම්පූර්ණයෙන් ආරෝපණ වෙයි"</string>
diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml
index 6bf0cdb..189e729 100644
--- a/packages/SettingsLib/res/values-sk/strings.xml
+++ b/packages/SettingsLib/res/values-sk/strings.xml
@@ -430,8 +430,7 @@
     <string name="force_allow_on_external_summary" msgid="8525425782530728238">"Umožniť zapísať akúkoľvek aplikáciu do externého úložiska bez ohľadu na hodnoty v manifeste"</string>
     <string name="force_resizable_activities" msgid="7143612144399959606">"Vynútiť možnosť zmeny veľkosti aktivít"</string>
     <string name="force_resizable_activities_summary" msgid="2490382056981583062">"Umožniť zmeniť veľkosť všetkých aktivít na niekoľko okien (bez ohľadu na hodnoty manifestu)"</string>
-    <string name="enable_freeform_support" msgid="8409932201445109106">"Povolenie meniteľných okien (starých)"</string>
-    <string name="enable_freeform_support_summary" msgid="2242481082356957934">"Povoľte podporu pre experimentálne staré meniteľné okná."</string>
+    <string name="enable_freeform_support" msgid="7599125687603914253">"Povoliť okná s voľným tvarom"</string>
     <string name="local_backup_password_title" msgid="4631017948933578709">"Heslo pre zálohy v počítači"</string>
     <string name="local_backup_password_summary_none" msgid="7646898032616361714">"Úplné zálohy v počítači nie sú momentálne chránené"</string>
     <string name="local_backup_password_summary_change" msgid="1707357670383995567">"Klepnutím zmeníte alebo odstránite heslo pre úplné zálohy do počítača"</string>
@@ -496,7 +495,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> do úplného nabitia"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do úplného nabitia"</string>
-    <string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Nabíjanie je optimalizované"</string>
+    <!-- no translation found for power_charging_limited (4144004473976005214) -->
+    <skip />
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – nabíja sa"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATUS">%2$s</xliff:g> – čas do nabitia: <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> – čas do úplného nabitia: <xliff:g id="TIME">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml
index 869958f..a7367ad 100644
--- a/packages/SettingsLib/res/values-sl/strings.xml
+++ b/packages/SettingsLib/res/values-sl/strings.xml
@@ -430,8 +430,7 @@
     <string name="force_allow_on_external_summary" msgid="8525425782530728238">"Poskrbi, da je ne glede na vrednosti v manifestu mogoče vsako aplikacijo zapisati v zunanjo shrambo."</string>
     <string name="force_resizable_activities" msgid="7143612144399959606">"Vsili spremembo velikosti za aktivnosti"</string>
     <string name="force_resizable_activities_summary" msgid="2490382056981583062">"Poskrbi, da je ne glede na vrednosti v manifestu mogoče vsem aktivnostim spremeniti velikost za način z več okni."</string>
-    <string name="enable_freeform_support" msgid="8409932201445109106">"Omogoči okna svobodne oblike (starejše)"</string>
-    <string name="enable_freeform_support_summary" msgid="2242481082356957934">"Omogoči podporo za poskusna okna svobodne oblike starejše različice."</string>
+    <string name="enable_freeform_support" msgid="7599125687603914253">"Omogoči okna svobodne oblike"</string>
     <string name="local_backup_password_title" msgid="4631017948933578709">"Geslo za varnostno kopijo namizja"</string>
     <string name="local_backup_password_summary_none" msgid="7646898032616361714">"Popolne varnostne kopije namizja trenutno niso zaščitene."</string>
     <string name="local_backup_password_summary_change" msgid="1707357670383995567">"Dotaknite se, če želite spremeniti ali odstraniti geslo za popolno varnostno kopiranje namizja"</string>
@@ -496,7 +495,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"Še <xliff:g id="TIME">%1$s</xliff:g> do napolnjenosti"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – še <xliff:g id="TIME">%2$s</xliff:g> do napolnjenosti"</string>
-    <string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – polnjenje je optimizirano"</string>
+    <!-- no translation found for power_charging_limited (4144004473976005214) -->
+    <skip />
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – polnjenje"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATUS">%2$s</xliff:g> – popolnoma napolnjena do <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> – popolnoma napolnjena do <xliff:g id="TIME">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml
index 0c1bf6c..688a098 100644
--- a/packages/SettingsLib/res/values-sq/strings.xml
+++ b/packages/SettingsLib/res/values-sq/strings.xml
@@ -430,8 +430,7 @@
     <string name="force_allow_on_external_summary" msgid="8525425782530728238">"Bën që çdo aplikacion të jetë i përshtatshëm për t\'u shkruar në hapësirën ruajtëse të jashtme, pavarësisht nga vlerat e manifestit"</string>
     <string name="force_resizable_activities" msgid="7143612144399959606">"Detyro madhësinë e ndryshueshme për aktivitetet"</string>
     <string name="force_resizable_activities_summary" msgid="2490382056981583062">"Bëj që të gjitha aktivitetet të kenë madhësi të ndryshueshme për përdorimin me shumë dritare, pavarësisht vlerave të manifestit."</string>
-    <string name="enable_freeform_support" msgid="8409932201445109106">"Aktivizo dritaret me formë të lirë (të vjetra)"</string>
-    <string name="enable_freeform_support_summary" msgid="2242481082356957934">"Aktivizo mbështetjen për dritaret eksperimentale me formë të lirë të versionit të vjetër."</string>
+    <string name="enable_freeform_support" msgid="7599125687603914253">"Aktivizo dritaret me formë të lirë"</string>
     <string name="local_backup_password_title" msgid="4631017948933578709">"Fjalëkalimi i rezervimit të desktopit"</string>
     <string name="local_backup_password_summary_none" msgid="7646898032616361714">"Rezervimet e plota të desktopit nuk janë të mbrojtura aktualisht"</string>
     <string name="local_backup_password_summary_change" msgid="1707357670383995567">"Trokit për të ndryshuar ose hequr fjalëkalimin për rezervime të plota të desktopit"</string>
@@ -496,7 +495,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> derisa të mbushet"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> derisa të mbushet"</string>
-    <string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Karikimi u optimizua"</string>
+    <!-- no translation found for power_charging_limited (4144004473976005214) -->
+    <skip />
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - Po karikohet"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - Plot deri në <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - Karikohet plotësisht deri në <xliff:g id="TIME">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml
index 2b004cb..17f5c2a 100644
--- a/packages/SettingsLib/res/values-sr/strings.xml
+++ b/packages/SettingsLib/res/values-sr/strings.xml
@@ -430,8 +430,7 @@
     <string name="force_allow_on_external_summary" msgid="8525425782530728238">"Омогућава уписивање свих апликација у спољну меморију, без обзира на вредности манифеста"</string>
     <string name="force_resizable_activities" msgid="7143612144399959606">"Принудно омогући промену величине активности"</string>
     <string name="force_resizable_activities_summary" msgid="2490382056981583062">"Омогућава промену величине свих активности за режим са више прозора, без обзира на вредности манифеста."</string>
-    <string name="enable_freeform_support" msgid="8409932201445109106">"Омогући прозоре произвољног формата (застарело)"</string>
-    <string name="enable_freeform_support_summary" msgid="2242481082356957934">"Омогућава подршку за застареле експерименталне прозоре произвољног формата."</string>
+    <string name="enable_freeform_support" msgid="7599125687603914253">"Омогући прозоре произвољног формата"</string>
     <string name="local_backup_password_title" msgid="4631017948933578709">"Лозинка резервне копије за рачунар"</string>
     <string name="local_backup_password_summary_none" msgid="7646898032616361714">"Резервне копије читавог система тренутно нису заштићене"</string>
     <string name="local_backup_password_summary_change" msgid="1707357670383995567">"Додирните да бисте променили или уклонили лозинку за прављење резервних копија читавог система на рачунару"</string>
@@ -496,7 +495,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> до краја пуњења"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до краја пуњења"</string>
-    <string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – пуњење је оптимизовано"</string>
+    <!-- no translation found for power_charging_limited (4144004473976005214) -->
+    <skip />
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – Пуњење"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATUS">%2$s</xliff:g> – Потпуно напуњено до <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> – Потпуно напуњено до <xliff:g id="TIME">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml
index 06ed90e..c50d3c3 100644
--- a/packages/SettingsLib/res/values-sv/strings.xml
+++ b/packages/SettingsLib/res/values-sv/strings.xml
@@ -430,8 +430,7 @@
     <string name="force_allow_on_external_summary" msgid="8525425782530728238">"Allar appar kan skrivas till extern lagring, oavsett manifestvärden"</string>
     <string name="force_resizable_activities" msgid="7143612144399959606">"Framtvinga storleksanpassning för aktiviteter"</string>
     <string name="force_resizable_activities_summary" msgid="2490382056981583062">"Gör det möjligt att ändra storleken på alla aktiviteter i flerfönsterläge, oavsett manifestvärden."</string>
-    <string name="enable_freeform_support" msgid="8409932201445109106">"Aktivera frihandsfönster (äldre)"</string>
-    <string name="enable_freeform_support_summary" msgid="2242481082356957934">"Aktivera stöd för äldre experimentella frihandsfönster."</string>
+    <string name="enable_freeform_support" msgid="7599125687603914253">"Aktivera frihandsfönster"</string>
     <string name="local_backup_password_title" msgid="4631017948933578709">"Lösenord för säkerhetskopia av datorn"</string>
     <string name="local_backup_password_summary_none" msgid="7646898032616361714">"De fullständiga säkerhetskopiorna av datorn är för närvarande inte skyddade"</string>
     <string name="local_backup_password_summary_change" msgid="1707357670383995567">"Tryck om du vill ändra eller ta bort lösenordet för fullständig säkerhetskopiering av datorn"</string>
@@ -496,7 +495,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> kvar tills fulladdat"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> kvar tills fulladdat"</string>
-    <string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Laddningen har optimerats"</string>
+    <!-- no translation found for power_charging_limited (4144004473976005214) -->
+    <skip />
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – laddas"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATUS">%2$s</xliff:g> – fulladdad till <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> – fulladdad till <xliff:g id="TIME">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml
index 08aa784..0c71dd1 100644
--- a/packages/SettingsLib/res/values-sw/strings.xml
+++ b/packages/SettingsLib/res/values-sw/strings.xml
@@ -430,8 +430,7 @@
     <string name="force_allow_on_external_summary" msgid="8525425782530728238">"Huruhusu programu yoyote iwekwe kwenye hifadhi ya nje, bila kujali thamani za faili ya maelezo"</string>
     <string name="force_resizable_activities" msgid="7143612144399959606">"Lazimisha shughuli ziweze kubadilishwa ukubwa"</string>
     <string name="force_resizable_activities_summary" msgid="2490382056981583062">"Fanya shughuli zote ziweze kubadilishwa ukubwa kwenye madirisha mengi, bila kuzingatia thamani za faili ya maelezo."</string>
-    <string name="enable_freeform_support" msgid="8409932201445109106">"Washa madirisha yenye muundo huru (yaliyopitwa na wakati)"</string>
-    <string name="enable_freeform_support_summary" msgid="2242481082356957934">"Ruhusu matumizi ya madirisha ya majaribio yenye muundo huru yaliyopitwa na wakati."</string>
+    <string name="enable_freeform_support" msgid="7599125687603914253">"Washa madirisha yenye muundo huru"</string>
     <string name="local_backup_password_title" msgid="4631017948933578709">"Nenosiri la hifadhi rudufu ya eneo kazi"</string>
     <string name="local_backup_password_summary_none" msgid="7646898032616361714">"Hifadhi rudufu kamili za eneo kazi hazijalindwa kwa sasa"</string>
     <string name="local_backup_password_summary_change" msgid="1707357670383995567">"Gusa ili ubadilishe au uondoe nenosiri la hifadhi rudufu kamili za eneo kazi"</string>
@@ -496,7 +495,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"Zimesalia <xliff:g id="TIME">%1$s</xliff:g> ijae chaji"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> zimesalia ijae chaji"</string>
-    <string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Hali ya kuchaji imeboreshwa"</string>
+    <!-- no translation found for power_charging_limited (4144004473976005214) -->
+    <skip />
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - Inachaji"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - Itajaa kufikia <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - Betri itajaa chaji kufikia <xliff:g id="TIME">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml
index 5972756..2908b63 100644
--- a/packages/SettingsLib/res/values-ta/strings.xml
+++ b/packages/SettingsLib/res/values-ta/strings.xml
@@ -430,8 +430,7 @@
     <string name="force_allow_on_external_summary" msgid="8525425782530728238">"மேனிஃபெஸ்ட் மதிப்புகளைப் பொருட்படுத்தாமல், எல்லா ஆப்ஸையும் வெளிப்புறச் சேமிப்பிடத்தில் எழுத அனுமதிக்கும்"</string>
     <string name="force_resizable_activities" msgid="7143612144399959606">"செயல்பாடுகளை அளவுமாறக்கூடியதாக அமை"</string>
     <string name="force_resizable_activities_summary" msgid="2490382056981583062">"மேனிஃபெஸ்ட் மதிப்புகளைப் பொருட்படுத்தாமல், பல சாளரத்திற்கு எல்லா செயல்பாடுகளையும் அளவுமாறக்கூடியதாக அமை."</string>
-    <string name="enable_freeform_support" msgid="8409932201445109106">"குறிப்பிட்ட வடிவமில்லாத சாளரத்தை இயக்குதல் (லெகஸி)"</string>
-    <string name="enable_freeform_support_summary" msgid="2242481082356957934">"பரிசோதனைக்குரிய, குறிப்பிட்ட வடிவமில்லாத பழைய சாளரங்களுக்கான ஆதரவை இயக்குதல்."</string>
+    <string name="enable_freeform_support" msgid="7599125687603914253">"குறிப்பிட்ட வடிவமில்லாத சாளரங்களை இயக்கு"</string>
     <string name="local_backup_password_title" msgid="4631017948933578709">"டெஸ்க்டாப் காப்புப்பிரதி கடவுச்சொல்"</string>
     <string name="local_backup_password_summary_none" msgid="7646898032616361714">"டெஸ்க்டாப்பின் முழு காப்புப்பிரதிகள் தற்போது பாதுகாக்கப்படவில்லை"</string>
     <string name="local_backup_password_summary_change" msgid="1707357670383995567">"டெஸ்க்டாப்பின் முழுக் காப்புப் பிரதிகளுக்கான கடவுச்சொல்லை மாற்ற அல்லது அகற்ற, தட்டவும்"</string>
@@ -496,7 +495,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"முழுவதும் சார்ஜாக <xliff:g id="TIME">%1$s</xliff:g> ஆகும்"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - முழுவதும் சார்ஜாக <xliff:g id="TIME">%2$s</xliff:g> ஆகும்"</string>
-    <string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - சார்ஜிங் மேம்படுத்தப்பட்டது"</string>
+    <!-- no translation found for power_charging_limited (4144004473976005214) -->
+    <skip />
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> ‑ சார்ஜாகிறது"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - <xliff:g id="TIME">%3$s</xliff:g>க்கு முழுமையாகச் சார்ஜாகிவிடும்"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>க்கு முழுமையாகச் சார்ஜாகிவிடும்"</string>
diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml
index 1df1823..c188bdb 100644
--- a/packages/SettingsLib/res/values-te/strings.xml
+++ b/packages/SettingsLib/res/values-te/strings.xml
@@ -430,8 +430,7 @@
     <string name="force_allow_on_external_summary" msgid="8525425782530728238">"ఏ యాప్‌ను అయినా మానిఫెస్ట్ విలువలతో సంబంధం లేకుండా బాహ్య స్టోరేజ్‌లో సేవ్ చేయడానికి అనుమతిస్తుంది"</string>
     <string name="force_resizable_activities" msgid="7143612144399959606">"యాక్టివిటీ విండోల సైజ్‌ మార్చ‌గ‌లిగేలా నిర్బంధించు"</string>
     <string name="force_resizable_activities_summary" msgid="2490382056981583062">"మానిఫెస్ట్ విలువలతో సంబంధం లేకుండా అన్ని యాక్టివిటీస్‌ను పలు రకాల విండోల్లో సరిపోయేటట్లు సైజ్‌ మార్చగలిగేలా చేస్తుంది."</string>
-    <string name="enable_freeform_support" msgid="8409932201445109106">"ఫ్రీఫార్మ్ విండోలను ఎనేబుల్ చేయండి (లెగసీ)"</string>
-    <string name="enable_freeform_support_summary" msgid="2242481082356957934">"ప్రయోగాత్మక లెగసీ ఫ్రీఫార్మ్ విండోలకు సంబంధించిన సపోర్ట్‌ను ఎనేబుల్ చేయండి."</string>
+    <string name="enable_freeform_support" msgid="7599125687603914253">"స్వతంత్ర రూప విండోలను ఎనేబుల్ చేయండి"</string>
     <string name="local_backup_password_title" msgid="4631017948933578709">"డెస్క్‌టాప్ బ్యాకప్ పాస్‌వర్డ్"</string>
     <string name="local_backup_password_summary_none" msgid="7646898032616361714">"డెస్క్‌టాప్ పూర్తి బ్యాకప్‌లు ప్రస్తుతం రక్షించబడలేదు"</string>
     <string name="local_backup_password_summary_change" msgid="1707357670383995567">"డెస్క్‌టాప్ పూర్తి బ్యాకప్‌ల కోసం పాస్‌వర్డ్‌ను మార్చడానికి లేదా తీసివేయడానికి నొక్కండి"</string>
@@ -496,7 +495,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g>లో పూర్తిగా ఛార్జ్ అవుతుంది"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>లో పూర్తిగా ఛార్జ్ అవుతుంది"</string>
-    <string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - ఛార్జింగ్ ఆప్టిమైజ్ చేయబడింది"</string>
+    <!-- no translation found for power_charging_limited (4144004473976005214) -->
+    <skip />
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - ఛార్జ్ అవుతోంది"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - <xliff:g id="TIME">%3$s</xliff:g>కు పూర్తవుతుంది"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>కు పూర్తిగా ఛార్జ్ అవుతుంది"</string>
diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml
index c26f02d..b9510a8 100644
--- a/packages/SettingsLib/res/values-th/strings.xml
+++ b/packages/SettingsLib/res/values-th/strings.xml
@@ -430,8 +430,7 @@
     <string name="force_allow_on_external_summary" msgid="8525425782530728238">"ทำให้เขียนแอปในที่จัดเก็บข้อมูลภายนอกได้ โดยไม่คำนึงถึงค่าในไฟล์ Manifest"</string>
     <string name="force_resizable_activities" msgid="7143612144399959606">"บังคับให้กิจกรรมปรับขนาดได้"</string>
     <string name="force_resizable_activities_summary" msgid="2490382056981583062">"ทำให้กิจกรรมทั้งหมดปรับขนาดได้สำหรับหน้าต่างหลายบาน โดยไม่คำนึงถึงค่าในไฟล์ Manifest"</string>
-    <string name="enable_freeform_support" msgid="8409932201445109106">"เปิดใช้หน้าต่างรูปแบบอิสระ (แบบเดิม)"</string>
-    <string name="enable_freeform_support_summary" msgid="2242481082356957934">"เปิดใช้การรองรับหน้าต่างรูปแบบอิสระแบบเดิมเวอร์ชันทดลอง"</string>
+    <string name="enable_freeform_support" msgid="7599125687603914253">"เปิดใช้หน้าต่างรูปแบบอิสระ"</string>
     <string name="local_backup_password_title" msgid="4631017948933578709">"รหัสผ่านการสำรองข้อมูลในเดสก์ท็อป"</string>
     <string name="local_backup_password_summary_none" msgid="7646898032616361714">"การสำรองข้อมูลเต็มรูปแบบในเดสก์ท็อปไม่ได้รับการป้องกันในขณะนี้"</string>
     <string name="local_backup_password_summary_change" msgid="1707357670383995567">"แตะเพื่อเปลี่ยนแปลงหรือลบรหัสผ่านสำหรับการสำรองข้อมูลเต็มรูปแบบในเดสก์ท็อป"</string>
@@ -496,7 +495,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"อีก <xliff:g id="TIME">%1$s</xliff:g>จึงจะเต็ม"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - อีก <xliff:g id="TIME">%2$s</xliff:g> จึงจะเต็ม"</string>
-    <string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - ปรับการชาร์จให้เหมาะสมแล้ว"</string>
+    <!-- no translation found for power_charging_limited (4144004473976005214) -->
+    <skip />
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> ‑ กำลังชาร์จ"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - จะเต็มภายใน <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - จะชาร์จเต็มภายใน <xliff:g id="TIME">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml
index 1c038f6..32cc961 100644
--- a/packages/SettingsLib/res/values-tl/strings.xml
+++ b/packages/SettingsLib/res/values-tl/strings.xml
@@ -430,8 +430,7 @@
     <string name="force_allow_on_external_summary" msgid="8525425782530728238">"Ginagawang kwalipikado ang anumang app na mailagay sa external na storage, anuman ang mga value ng manifest"</string>
     <string name="force_resizable_activities" msgid="7143612144399959606">"Sapilitang gawing resizable ang mga aktibidad"</string>
     <string name="force_resizable_activities_summary" msgid="2490382056981583062">"Gawing nare-resize ang lahat ng aktibidad para sa multi-window, anuman ang mga value ng manifest."</string>
-    <string name="enable_freeform_support" msgid="8409932201445109106">"I-enable ang mga freeform window (legacy)"</string>
-    <string name="enable_freeform_support_summary" msgid="2242481082356957934">"I-enable ang suporta para sa mga pang-eksperimentong legacy na freeform window."</string>
+    <string name="enable_freeform_support" msgid="7599125687603914253">"I-enable ang mga freeform window"</string>
     <string name="local_backup_password_title" msgid="4631017948933578709">"Password ng pag-backup ng desktop"</string>
     <string name="local_backup_password_summary_none" msgid="7646898032616361714">"Kasalukuyang hindi pinoprotektahan ang mga buong pag-backup ng desktop"</string>
     <string name="local_backup_password_summary_change" msgid="1707357670383995567">"I-tap upang baguhin o alisin ang password para sa mga kumpletong pag-back up sa desktop"</string>
@@ -496,7 +495,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> na lang bago mapuno"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> na lang bago mapuno"</string>
-    <string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Naka-optimize ang pag-charge"</string>
+    <!-- no translation found for power_charging_limited (4144004473976005214) -->
+    <skip />
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - Nagcha-charge"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - Mapupuno sa <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - Mafu-full charge sa <xliff:g id="TIME">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml
index 79ace3f..6bfc1ee 100644
--- a/packages/SettingsLib/res/values-tr/strings.xml
+++ b/packages/SettingsLib/res/values-tr/strings.xml
@@ -430,8 +430,7 @@
     <string name="force_allow_on_external_summary" msgid="8525425782530728238">"Manifest değerlerinden bağımsız olarak uygulamaları harici depolamaya yazmak için uygun hale getirir"</string>
     <string name="force_resizable_activities" msgid="7143612144399959606">"Etkinlikleri yeniden boyutlandırılabilmeye zorla"</string>
     <string name="force_resizable_activities_summary" msgid="2490382056981583062">"Manifest değerlerinden bağımsız olarak, tüm etkinlikleri birden fazla pencerede yeniden boyutlandırılabilir yap."</string>
-    <string name="enable_freeform_support" msgid="8409932201445109106">"Serbest biçimli pencereleri (eski) etkinleştir"</string>
-    <string name="enable_freeform_support_summary" msgid="2242481082356957934">"Deneysel eski serbest biçimli pencere desteğini etkinleştir."</string>
+    <string name="enable_freeform_support" msgid="7599125687603914253">"Serbest biçimli pencereleri etkinleştir"</string>
     <string name="local_backup_password_title" msgid="4631017948933578709">"Masaüstü yedekleme şifresi"</string>
     <string name="local_backup_password_summary_none" msgid="7646898032616361714">"Masaüstü tam yedeklemeleri şu an korunmuyor"</string>
     <string name="local_backup_password_summary_change" msgid="1707357670383995567">"Masaüstü tam yedeklemelerinin şifresini değiştirmek veya kaldırmak için dokunun"</string>
@@ -496,7 +495,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"Tamamen şarj olmasına <xliff:g id="TIME">%1$s</xliff:g> kaldı"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - Tamamen şarj olmasına <xliff:g id="TIME">%2$s</xliff:g> kaldı"</string>
-    <string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Şarj işlemi optimize edildi"</string>
+    <!-- no translation found for power_charging_limited (4144004473976005214) -->
+    <skip />
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> ‑ Şarj ediliyor"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - Tamamen dolacağı zaman: <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - Tamamen şarj olacağı zaman: <xliff:g id="TIME">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml
index de416d2..083a130 100644
--- a/packages/SettingsLib/res/values-uk/strings.xml
+++ b/packages/SettingsLib/res/values-uk/strings.xml
@@ -430,8 +430,7 @@
     <string name="force_allow_on_external_summary" msgid="8525425782530728238">"Можна записувати додатки в зовнішню пам’ять, незалежно від значень у маніфесті"</string>
     <string name="force_resizable_activities" msgid="7143612144399959606">"Примусово масштабувати активність"</string>
     <string name="force_resizable_activities_summary" msgid="2490382056981583062">"Масштабувати активність на кілька вікон, незалежно від значень у файлі маніфесту."</string>
-    <string name="enable_freeform_support" msgid="8409932201445109106">"Увімкнути старий формат вікон змінного розміру"</string>
-    <string name="enable_freeform_support_summary" msgid="2242481082356957934">"Увімкнути підтримку експериментального старого формату вікон змінного розміру."</string>
+    <string name="enable_freeform_support" msgid="7599125687603914253">"Увімкнути вікна довільного формату"</string>
     <string name="local_backup_password_title" msgid="4631017948933578709">"Пароль рез. копії на ПК"</string>
     <string name="local_backup_password_summary_none" msgid="7646898032616361714">"Повні резервні копії на комп’ютері наразі не захищені"</string>
     <string name="local_backup_password_summary_change" msgid="1707357670383995567">"Торкніться, щоб змінити або видалити пароль для повного резервного копіювання на комп’ютер"</string>
@@ -496,7 +495,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> до повного заряду"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до повного заряду"</string>
-    <string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – заряджання оптимізовано"</string>
+    <!-- no translation found for power_charging_limited (4144004473976005214) -->
+    <skip />
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – заряджається"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - Завершиться до <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - Повністю зарядиться до <xliff:g id="TIME">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml
index 3092832..6508d51 100644
--- a/packages/SettingsLib/res/values-ur/strings.xml
+++ b/packages/SettingsLib/res/values-ur/strings.xml
@@ -430,8 +430,7 @@
     <string name="force_allow_on_external_summary" msgid="8525425782530728238">"‏manifest اقدار سے قطع نظر، کسی بھی ایپ کو بیرونی اسٹوریج پر لکھے جانے کا اہل بناتا ہے"</string>
     <string name="force_resizable_activities" msgid="7143612144399959606">"سرگرمیوں کو ری سائز ایبل بنائیں"</string>
     <string name="force_resizable_activities_summary" msgid="2490382056981583062">"مینی فیسٹ اقدار سے قطع نظر، ملٹی ونڈو کیلئے تمام سرگرمیوں کو ری سائز ایبل بنائیں۔"</string>
-    <string name="enable_freeform_support" msgid="8409932201445109106">"‏فریفارم ونڈوز کو فعال کریں (legacy)"</string>
-    <string name="enable_freeform_support_summary" msgid="2242481082356957934">"‏تجرباتی legacy فری فارم ونڈوز کیلئے سپورٹ فعال کریں۔"</string>
+    <string name="enable_freeform_support" msgid="7599125687603914253">"‏freeform ونڈوز فعال کریں"</string>
     <string name="local_backup_password_title" msgid="4631017948933578709">"ڈیسک ٹاپ کا بیک اپ پاس ورڈ"</string>
     <string name="local_backup_password_summary_none" msgid="7646898032616361714">"ڈیسک ٹاپ کے مکمل بیک اپس فی الحال محفوظ کیے ہوئے نہیں ہیں"</string>
     <string name="local_backup_password_summary_change" msgid="1707357670383995567">"ڈیسک ٹاپ کے مکمل بیک اپس کیلئے پاس ورڈ کو تبدیل کرنے یا ہٹانے کیلئے تھپتھپائیں"</string>
@@ -496,7 +495,8 @@
     <string name="power_charging" msgid="6727132649743436802">"‎<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>‎"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"مکمل چارج ہونے میں <xliff:g id="TIME">%1$s</xliff:g> باقی ہے"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"مکمل چارج ہونے میں <xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> باقی ہے"</string>
-    <string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - چارجنگ کو بہتر بنایا گیا"</string>
+    <!-- no translation found for power_charging_limited (4144004473976005214) -->
+    <skip />
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - چارج ہو رہی ہے"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - <xliff:g id="TIME">%3$s</xliff:g> تک مکمل ہو جائے گی"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> تک مکمل چارج ہو جائے گی"</string>
diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml
index 7502412..738c815 100644
--- a/packages/SettingsLib/res/values-uz/strings.xml
+++ b/packages/SettingsLib/res/values-uz/strings.xml
@@ -430,8 +430,7 @@
     <string name="force_allow_on_external_summary" msgid="8525425782530728238">"Manifest qiymatidan qat’i nazar istalgan ilovani tashqi xotiraga saqlash imkonini beradi"</string>
     <string name="force_resizable_activities" msgid="7143612144399959606">"Harakatlarni moslashuvchan o‘lchamga keltirish"</string>
     <string name="force_resizable_activities_summary" msgid="2490382056981583062">"Manifest qiymatidan qat’i nazar barcha harakatlarni ko‘p oynali rejimga moslashtirish."</string>
-    <string name="enable_freeform_support" msgid="8409932201445109106">"Erkin shakldagi oynalarni (eskirgan) yoqish"</string>
-    <string name="enable_freeform_support_summary" msgid="2242481082356957934">"Eskirgan erkin shakldagi oynalar yaratish uchun moʻljallangan tajribaviy funksiyani yoqish."</string>
+    <string name="enable_freeform_support" msgid="7599125687603914253">"Erkin shakldagi oynalarni yoqish"</string>
     <string name="local_backup_password_title" msgid="4631017948933578709">"Zaxira nusxa uchun parol"</string>
     <string name="local_backup_password_summary_none" msgid="7646898032616361714">"Kompyuterdagi zaxira nusxalar hozirgi vaqtda himoyalanmagan"</string>
     <string name="local_backup_password_summary_change" msgid="1707357670383995567">"Ish stoli to‘liq zaxira nusxalari parolini o‘zgartirish yoki o‘chirish uchun bu yerni bosing"</string>
@@ -496,7 +495,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"Toʻlishiga <xliff:g id="TIME">%1$s</xliff:g> qoldi"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – Toʻlishiga <xliff:g id="TIME">%2$s</xliff:g> qoldi"</string>
-    <string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Quvvatlash optimallashtirildi"</string>
+    <!-- no translation found for power_charging_limited (4144004473976005214) -->
+    <skip />
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - Quvvatlanmoqda"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - Toʻladi: <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - Toʻliq quvvatlanadi: <xliff:g id="TIME">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml
index 02f0aa1..38b4e4f 100644
--- a/packages/SettingsLib/res/values-vi/strings.xml
+++ b/packages/SettingsLib/res/values-vi/strings.xml
@@ -430,8 +430,7 @@
     <string name="force_allow_on_external_summary" msgid="8525425782530728238">"Cho phép ghi mọi ứng dụng đủ điều kiện vào bộ nhớ ngoài, bất kể giá trị tệp kê khai là gì"</string>
     <string name="force_resizable_activities" msgid="7143612144399959606">"Buộc các hoạt động có thể thay đổi kích thước"</string>
     <string name="force_resizable_activities_summary" msgid="2490382056981583062">"Cho phép thay đổi kích thước của tất cả các hoạt động cho nhiều cửa sổ, bất kể giá trị tệp kê khai là gì."</string>
-    <string name="enable_freeform_support" msgid="8409932201445109106">"Bật cửa sổ có thể đổi kích thước (cũ)"</string>
-    <string name="enable_freeform_support_summary" msgid="2242481082356957934">"Bật tính năng hỗ trợ cửa sổ có thể đổi kích thước thử nghiệm (cũ)."</string>
+    <string name="enable_freeform_support" msgid="7599125687603914253">"Bật cửa sổ dạng tự do"</string>
     <string name="local_backup_password_title" msgid="4631017948933578709">"Mật khẩu cho bản sao lưu qua máy tính"</string>
     <string name="local_backup_password_summary_none" msgid="7646898032616361714">"Các bản sao lưu đầy đủ qua máy tính hiện không được bảo vệ"</string>
     <string name="local_backup_password_summary_change" msgid="1707357670383995567">"Nhấn để thay đổi hoặc xóa mật khẩu dành cho các bản sao lưu đầy đủ vào máy tính"</string>
@@ -496,7 +495,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> nữa là pin đầy"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> nữa là pin đầy"</string>
-    <string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – Quá trình sạc được tối ưu hoá"</string>
+    <!-- no translation found for power_charging_limited (4144004473976005214) -->
+    <skip />
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – Đang sạc"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATUS">%2$s</xliff:g> – Đến <xliff:g id="TIME">%3$s</xliff:g> pin sẽ đầy"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> – Đến <xliff:g id="TIME">%2$s</xliff:g> pin sẽ đầy"</string>
diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml
index a551121..21cd458 100644
--- a/packages/SettingsLib/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml
@@ -430,8 +430,7 @@
     <string name="force_allow_on_external_summary" msgid="8525425782530728238">"允许将任何应用写入外部存储设备(无论清单值是什么)"</string>
     <string name="force_resizable_activities" msgid="7143612144399959606">"强制将 activity 设为可调整大小"</string>
     <string name="force_resizable_activities_summary" msgid="2490382056981583062">"将所有 activity 设为可配合多窗口环境调整大小(无论清单值是什么)。"</string>
-    <string name="enable_freeform_support" msgid="8409932201445109106">"启用可自由调整的窗口(旧版)"</string>
-    <string name="enable_freeform_support_summary" msgid="2242481082356957934">"实现对实验性旧版可自由调整的窗口的支持。"</string>
+    <string name="enable_freeform_support" msgid="7599125687603914253">"启用可自由调整的窗口"</string>
     <string name="local_backup_password_title" msgid="4631017948933578709">"桌面备份密码"</string>
     <string name="local_backup_password_summary_none" msgid="7646898032616361714">"桌面完整备份当前未设置密码保护"</string>
     <string name="local_backup_password_summary_change" msgid="1707357670383995567">"点按即可更改或移除用于保护桌面完整备份的密码"</string>
@@ -496,7 +495,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"还需<xliff:g id="TIME">%1$s</xliff:g>充满"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - 还需<xliff:g id="TIME">%2$s</xliff:g>充满"</string>
-    <string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - 充电方式已优化"</string>
+    <!-- no translation found for power_charging_limited (4144004473976005214) -->
+    <skip />
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - 正在充电"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - <xliff:g id="TIME">%3$s</xliff:g> 前充满"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> 前充满"</string>
diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml
index 6f8c537..8ad35e6 100644
--- a/packages/SettingsLib/res/values-zh-rHK/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml
@@ -430,8 +430,7 @@
     <string name="force_allow_on_external_summary" msgid="8525425782530728238">"在任何資訊清單值下,允許將所有符合資格的應用程式寫入到外部儲存完間"</string>
     <string name="force_resizable_activities" msgid="7143612144399959606">"強制將活動設為可調整尺寸"</string>
     <string name="force_resizable_activities_summary" msgid="2490382056981583062">"在任何資訊清單值下,允許系統配合多重視窗環境調整所有活動的尺寸。"</string>
-    <string name="enable_freeform_support" msgid="8409932201445109106">"啟用自由形態視窗 (舊的)"</string>
-    <string name="enable_freeform_support_summary" msgid="2242481082356957934">"啟用舊的實驗版自由形態視窗的支援功能。"</string>
+    <string name="enable_freeform_support" msgid="7599125687603914253">"啟用自由形態視窗"</string>
     <string name="local_backup_password_title" msgid="4631017948933578709">"桌面電腦備份密碼"</string>
     <string name="local_backup_password_summary_none" msgid="7646898032616361714">"桌面電腦的完整備份目前未受保護"</string>
     <string name="local_backup_password_summary_change" msgid="1707357670383995567">"輕按即可變更或移除桌面電腦完整備份的密碼"</string>
@@ -496,7 +495,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g>後充滿電"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>後充滿電"</string>
-    <string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - 已優化充電"</string>
+    <!-- no translation found for power_charging_limited (4144004473976005214) -->
+    <skip />
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> ‑ 充電中"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - 在 <xliff:g id="TIME">%3$s</xliff:g>前充滿電"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> • 在 <xliff:g id="TIME">%2$s</xliff:g>前充滿電"</string>
diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml
index 2806b02..8265bba 100644
--- a/packages/SettingsLib/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml
@@ -430,8 +430,7 @@
     <string name="force_allow_on_external_summary" msgid="8525425782530728238">"允許將任何應用程式寫入外部儲存空間 (無論資訊清單值為何)"</string>
     <string name="force_resizable_activities" msgid="7143612144399959606">"將活動強制設為可調整大小"</string>
     <string name="force_resizable_activities_summary" msgid="2490382056981583062">"將所有活動設為可配合多重視窗環境調整大小 (無論資訊清單值為何)。"</string>
-    <string name="enable_freeform_support" msgid="8409932201445109106">"啟用自由形式視窗 (舊版)"</string>
-    <string name="enable_freeform_support_summary" msgid="2242481082356957934">"啟用實驗性舊版自由形式視窗支援功能。"</string>
+    <string name="enable_freeform_support" msgid="7599125687603914253">"啟用自由形式視窗"</string>
     <string name="local_backup_password_title" msgid="4631017948933578709">"電腦備份密碼"</string>
     <string name="local_backup_password_summary_none" msgid="7646898032616361714">"尚未設定密碼保護電腦的完整備份"</string>
     <string name="local_backup_password_summary_change" msgid="1707357670383995567">"輕觸即可變更或移除電腦完整備份的密碼"</string>
@@ -496,7 +495,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g>後充飽"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>後充飽"</string>
-    <string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - 充電效能已最佳化"</string>
+    <!-- no translation found for power_charging_limited (4144004473976005214) -->
+    <skip />
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - 充電中"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - <xliff:g id="TIME">%3$s</xliff:g> 前充飽"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> 前充飽"</string>
diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml
index 570c424..b2f46b5 100644
--- a/packages/SettingsLib/res/values-zu/strings.xml
+++ b/packages/SettingsLib/res/values-zu/strings.xml
@@ -430,8 +430,7 @@
     <string name="force_allow_on_external_summary" msgid="8525425782530728238">"Yenza noma uluphi uhlelo lokusebenza lifaneleke ukuthi libhalwe kusitoreji sangaphandle, ngaphandle kwamavelu we-manifest"</string>
     <string name="force_resizable_activities" msgid="7143612144399959606">"Imisebenzi yamandla izonikezwa usayizi omusha"</string>
     <string name="force_resizable_activities_summary" msgid="2490382056981583062">"Yenza yonke imisebenzi ibe nosayizi abasha kumawindi amaningi, ngokunganaki amavelu e-manifest."</string>
-    <string name="enable_freeform_support" msgid="8409932201445109106">"Vumela amawindi efreeform (ifa)"</string>
-    <string name="enable_freeform_support_summary" msgid="2242481082356957934">"Nika amandla amawindi efreeform efa elisahlolwa."</string>
+    <string name="enable_freeform_support" msgid="7599125687603914253">"Nika amandla amawindi e-freeform"</string>
     <string name="local_backup_password_title" msgid="4631017948933578709">"Iphasiwedi yokusekela ngokulondoloza ye-Desktop"</string>
     <string name="local_backup_password_summary_none" msgid="7646898032616361714">"Ukusekela ngokulondoloza okugcwele kwe-Desktop akuvikelekile okwamanje."</string>
     <string name="local_backup_password_summary_change" msgid="1707357670383995567">"Thepha ukushintsha noma ukususa iphasiwedi yokwenziwa kwezipele ngokugcwele kwideskithophu"</string>
@@ -496,7 +495,8 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> okusele kuze kugcwale"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> okusele kuze kugcwale"</string>
-    <string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Ukushaja kuthuthukisiwe"</string>
+    <!-- no translation found for power_charging_limited (4144004473976005214) -->
+    <skip />
     <string name="power_charging_future_paused" msgid="1809543660923642799">"Iku-<xliff:g id="LEVEL">%1$s</xliff:g> ‑ Iyashaja"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - Igcwala ngo-<xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> • Ishajwe ngokugcwele ngo-<xliff:g id="TIME">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index cfd74d4..687c728 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -881,6 +881,11 @@
     <!-- UI debug setting: show physical key presses summary [CHAR LIMIT=150] -->
     <string name="show_key_presses_summary">Show visual feedback for physical key presses</string>
 
+    <!-- UI debug setting: Title text for a debug setting that enables a visualization of touchpad input in a window on the screen  [CHAR LIMIT=50] -->
+    <string name="touchpad_visualizer">Show touchpad input</string>
+    <!-- UI debug setting: Summary text for a debug setting that enables a visualization of touchpad input in a window on the screen [CHAR LIMIT=150] -->
+    <string name="touchpad_visualizer_summary">Screen overlay displaying touchpad input data and recognized gestures</string>
+
     <!-- UI debug setting: show where surface updates happen? [CHAR LIMIT=25] -->
     <string name="show_screen_updates">Show surface updates</string>
     <!-- UI debug setting: show surface updates summary [CHAR LIMIT=50] -->
@@ -1010,10 +1015,8 @@
     <!-- UI debug setting: force allow on external summary [CHAR LIMIT=150] -->
     <string name="force_resizable_activities_summary">Make all activities resizable for multi-window, regardless of manifest values.</string>
 
-    <!-- UI debug setting: enable legacy freeform window support [CHAR LIMIT=50] -->
-    <string name="enable_freeform_support">Enable freeform windows (legacy)</string>
-    <!-- UI debug setting: enable legacy freeform window support summary [CHAR LIMIT=150] -->
-    <string name="enable_freeform_support_summary">Enable support for experimental legacy freeform windows.</string>
+    <!-- Title for a toggle that enables support for windows to be in freeform. Freeform windows enables users to freely arrange and resize overlapping apps. [CHAR LIMIT=50] -->
+    <string name="enable_freeform_support">Enable freeform windows</string>
 
     <!-- Local (desktop) backup password menu title [CHAR LIMIT=25] -->
     <string name="local_backup_password_title">Desktop backup password</string>
@@ -1166,7 +1169,7 @@
     <!-- [CHAR_LIMIT=40] Label for battery level chart when charging with duration -->
     <string name="power_charging_duration"><xliff:g id="level">%1$s</xliff:g> - <xliff:g id="time">%2$s</xliff:g> left until full</string>
     <!-- [CHAR_LIMIT=80] Label for battery level chart when charge been limited -->
-    <string name="power_charging_limited"><xliff:g id="level">%1$s</xliff:g> - Charging optimized</string>
+    <string name="power_charging_limited"><xliff:g id="level">%1$s</xliff:g> - Charging on hold to protect battery</string>
     <!-- [CHAR_LIMIT=80] Label for battery charging future pause -->
     <string name="power_charging_future_paused"><xliff:g id="level">%1$s</xliff:g> - Charging</string>
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
index e926b16..0dc772a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
@@ -16,8 +16,6 @@
 
 package com.android.settingslib.bluetooth;
 
-import static com.android.settingslib.flags.Flags.enableCachedBluetoothDeviceDedup;
-
 import android.bluetooth.BluetoothA2dp;
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothCsipSetCoordinator;
@@ -403,7 +401,7 @@
                 cachedDevice = mDeviceManager.addDevice(device);
             }
 
-            if (enableCachedBluetoothDeviceDedup() && bondState == BluetoothDevice.BOND_BONDED) {
+            if (bondState == BluetoothDevice.BOND_BONDED) {
                 mDeviceManager.removeDuplicateInstanceForIdentityAddress(device);
             }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
index 53441c0..de60fdc2 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
@@ -11,6 +11,7 @@
 import android.bluetooth.BluetoothProfile;
 import android.bluetooth.BluetoothStatusCodes;
 import android.content.ComponentName;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
@@ -475,14 +476,26 @@
     }
 
     /**
+     * Gets string metadata from Fast Pair customized fields.
+     *
+     * @param bluetoothDevice the BluetoothDevice to get metadata
+     * @return the string metadata
+     */
+    @Nullable
+    public static String getFastPairCustomizedField(
+            @Nullable BluetoothDevice bluetoothDevice, @NonNull String key) {
+        String data = getStringMetaData(bluetoothDevice, METADATA_FAST_PAIR_CUSTOMIZED_FIELDS);
+        return extraTagValue(key, data);
+    }
+
+    /**
      * Get URI Bluetooth metadata for extra control
      *
      * @param bluetoothDevice the BluetoothDevice to get metadata
      * @return the URI metadata
      */
     public static String getControlUriMetaData(BluetoothDevice bluetoothDevice) {
-        String data = getStringMetaData(bluetoothDevice, METADATA_FAST_PAIR_CUSTOMIZED_FIELDS);
-        return extraTagValue(KEY_HEARABLE_CONTROL_SLICE, data);
+        return getFastPairCustomizedField(bluetoothDevice, KEY_HEARABLE_CONTROL_SLICE);
     }
 
     /**
@@ -565,7 +578,7 @@
     }
 
     /**
-     * Check if {@link CachedBluetoothDevice} has connected to a broadcast source.
+     * Check if {@link CachedBluetoothDevice} (lead or member) has connected to a broadcast source.
      *
      * @param cachedDevice The cached bluetooth device to check.
      * @param localBtManager The BT manager to provide BT functions.
@@ -573,20 +586,10 @@
      */
     @WorkerThread
     public static boolean hasConnectedBroadcastSource(
-            CachedBluetoothDevice cachedDevice, LocalBluetoothManager localBtManager) {
-        if (localBtManager == null) {
-            Log.d(TAG, "Skip check hasConnectedBroadcastSource due to bt manager is null");
-            return false;
-        }
-        LocalBluetoothLeBroadcastAssistant assistant =
-                localBtManager.getProfileManager().getLeAudioBroadcastAssistantProfile();
-        if (assistant == null) {
-            Log.d(TAG, "Skip check hasConnectedBroadcastSource due to assistant profile is null");
-            return false;
-        }
-        List<BluetoothLeBroadcastReceiveState> sourceList =
-                assistant.getAllSources(cachedDevice.getDevice());
-        if (!sourceList.isEmpty() && sourceList.stream().anyMatch(BluetoothUtils::isConnected)) {
+            @Nullable CachedBluetoothDevice cachedDevice,
+            @Nullable LocalBluetoothManager localBtManager) {
+        if (cachedDevice == null) return false;
+        if (hasConnectedBroadcastSourceForBtDevice(cachedDevice.getDevice(), localBtManager)) {
             Log.d(
                     TAG,
                     "Lead device has connected broadcast source, device = "
@@ -595,9 +598,7 @@
         }
         // Return true if member device is in broadcast.
         for (CachedBluetoothDevice device : cachedDevice.getMemberDevice()) {
-            List<BluetoothLeBroadcastReceiveState> list =
-                    assistant.getAllSources(device.getDevice());
-            if (!list.isEmpty() && list.stream().anyMatch(BluetoothUtils::isConnected)) {
+            if (hasConnectedBroadcastSourceForBtDevice(device.getDevice(), localBtManager)) {
                 Log.d(
                         TAG,
                         "Member device has connected broadcast source, device = "
@@ -608,6 +609,28 @@
         return false;
     }
 
+    /**
+     * Check if {@link BluetoothDevice} has connected to a broadcast source.
+     *
+     * @param device The bluetooth device to check.
+     * @param localBtManager The BT manager to provide BT functions.
+     * @return Whether the device has connected to a broadcast source.
+     */
+    @WorkerThread
+    public static boolean hasConnectedBroadcastSourceForBtDevice(
+            @Nullable BluetoothDevice device, @Nullable LocalBluetoothManager localBtManager) {
+        LocalBluetoothLeBroadcastAssistant assistant =
+                localBtManager == null
+                        ? null
+                        : localBtManager.getProfileManager().getLeAudioBroadcastAssistantProfile();
+        if (device == null || assistant == null) {
+            Log.d(TAG, "Skip check hasConnectedBroadcastSourceForBtDevice due to arg is null");
+            return false;
+        }
+        List<BluetoothLeBroadcastReceiveState> sourceList = assistant.getAllSources(device);
+        return !sourceList.isEmpty() && sourceList.stream().anyMatch(BluetoothUtils::isConnected);
+    }
+
     /** Checks the connectivity status based on the provided broadcast receive state. */
     @WorkerThread
     public static boolean isConnected(BluetoothLeBroadcastReceiveState state) {
@@ -792,8 +815,10 @@
 
         ComponentName exclusiveManagerComponent =
                 ComponentName.unflattenFromString(exclusiveManagerName);
-        String exclusiveManagerPackage = exclusiveManagerComponent != null
-                ? exclusiveManagerComponent.getPackageName() : exclusiveManagerName;
+        String exclusiveManagerPackage =
+                exclusiveManagerComponent != null
+                        ? exclusiveManagerComponent.getPackageName()
+                        : exclusiveManagerName;
 
         if (!isPackageInstalledAndEnabled(context, exclusiveManagerPackage)) {
             return false;
@@ -835,9 +860,9 @@
 
     /** Get primary device group id in broadcast. */
     @WorkerThread
-    public static int getPrimaryGroupIdForBroadcast(@NonNull Context context) {
+    public static int getPrimaryGroupIdForBroadcast(@NonNull ContentResolver contentResolver) {
         return Settings.Secure.getInt(
-                context.getContentResolver(),
+                contentResolver,
                 getPrimaryGroupIdUriForBroadcast(),
                 BluetoothCsipSetCoordinator.GROUP_ID_INVALID);
     }
@@ -846,9 +871,13 @@
     @Nullable
     @WorkerThread
     public static CachedBluetoothDevice getSecondaryDeviceForBroadcast(
-            @NonNull Context context, @Nullable LocalBluetoothManager localBtManager) {
+            @NonNull ContentResolver contentResolver,
+            @Nullable LocalBluetoothManager localBtManager) {
         if (localBtManager == null) return null;
-        int primaryGroupId = getPrimaryGroupIdForBroadcast(context);
+        LocalBluetoothLeBroadcast broadcast =
+                localBtManager.getProfileManager().getLeAudioBroadcastProfile();
+        if (broadcast == null || !broadcast.isEnabled(null)) return null;
+        int primaryGroupId = getPrimaryGroupIdForBroadcast(contentResolver);
         if (primaryGroupId == BluetoothCsipSetCoordinator.GROUP_ID_INVALID) return null;
         LocalBluetoothLeBroadcastAssistant assistant =
                 localBtManager.getProfileManager().getLeAudioBroadcastAssistantProfile();
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
index cb6a930..8b6351e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
@@ -40,7 +40,7 @@
     private static final String TAG = "CachedBluetoothDeviceManager";
     private static final boolean DEBUG = BluetoothUtils.D;
 
-    @VisibleForTesting static int sLateBondingTimeoutMillis = 5000; // 5s
+    @VisibleForTesting static int sLateBondingTimeoutMillis = 10000; // 10s
 
     private Context mContext;
     private final LocalBluetoothManager mBtManager;
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcastCallbackExt.kt b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcastCallbackExt.kt
new file mode 100644
index 0000000..0bcf7fe
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcastCallbackExt.kt
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.bluetooth
+
+import android.bluetooth.BluetoothLeBroadcast
+import android.bluetooth.BluetoothLeBroadcastMetadata
+import com.android.internal.util.ConcurrentUtils
+import kotlinx.coroutines.channels.Channel
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.buffer
+import kotlinx.coroutines.flow.callbackFlow
+import kotlinx.coroutines.launch
+
+/** [Flow] for [BluetoothLeBroadcast.Callback] source start/stop events */
+val LocalBluetoothLeBroadcast.onBroadcastStartedOrStopped: Flow<Unit>
+    get() =
+        callbackFlow {
+                val listener =
+                    object : BluetoothLeBroadcast.Callback {
+                        override fun onBroadcastStarted(reason: Int, broadcastId: Int) {
+                            launch { trySend(Unit) }
+                        }
+
+                        override fun onBroadcastStartFailed(reason: Int) {
+                            launch { trySend(Unit) }
+                        }
+
+                        override fun onBroadcastStopped(reason: Int, broadcastId: Int) {
+                            launch { trySend(Unit) }
+                        }
+
+                        override fun onBroadcastStopFailed(reason: Int) {
+                            launch { trySend(Unit) }
+                        }
+
+                        override fun onPlaybackStarted(reason: Int, broadcastId: Int) {}
+
+                        override fun onPlaybackStopped(reason: Int, broadcastId: Int) {}
+
+                        override fun onBroadcastUpdated(reason: Int, broadcastId: Int) {}
+
+                        override fun onBroadcastUpdateFailed(reason: Int, broadcastId: Int) {}
+
+                        override fun onBroadcastMetadataChanged(
+                            broadcastId: Int,
+                            metadata: BluetoothLeBroadcastMetadata
+                        ) {}
+                    }
+                registerServiceCallBack(
+                    ConcurrentUtils.DIRECT_EXECUTOR,
+                    listener,
+                )
+                awaitClose { unregisterServiceCallBack(listener) }
+            }
+            .buffer(capacity = Channel.CONFLATED)
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/ActionSwitchPreferenceState.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/ActionSwitchPreferenceState.java
index 91c1a59..eefceae 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/ActionSwitchPreferenceState.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/ActionSwitchPreferenceState.java
@@ -21,6 +21,9 @@
 import android.os.Parcelable;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import java.util.Objects;
 
 /** A data class representing the state of an action/switch preference. */
 public class ActionSwitchPreferenceState extends DeviceSettingPreferenceState
@@ -133,4 +136,15 @@
     public Bundle getExtras() {
         return mExtras;
     }
+
+    @Override
+    public boolean equals(@Nullable Object obj) {
+        if (!(obj instanceof ActionSwitchPreferenceState other)) return false;
+        return mChecked == other.mChecked;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mChecked);
+    }
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceInfo.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceInfo.java
index 52e520e..bfd1d1a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceInfo.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceInfo.java
@@ -21,6 +21,7 @@
 import android.os.Parcelable;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 
 import java.util.Objects;
 
@@ -134,4 +135,15 @@
     public Bundle getExtras() {
         return mExtras;
     }
+
+    @Override
+    public boolean equals(@Nullable Object obj) {
+        if (!(obj instanceof DeviceInfo other)) return false;
+        return Objects.equals(mBluetoothAddress, other.getBluetoothAddress());
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mBluetoothAddress);
+    }
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingState.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingState.java
index 63fd4eb..b505f27 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingState.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingState.java
@@ -21,6 +21,7 @@
 import android.os.Parcelable;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 
 import java.util.Objects;
 
@@ -162,4 +163,16 @@
     public Bundle getExtras() {
         return mExtras;
     }
+
+    @Override
+    public boolean equals(@Nullable Object obj) {
+        if (!(obj instanceof DeviceSettingState other)) return false;
+        return mSettingId == other.mSettingId
+                && Objects.equals(mPreferenceState, other.mPreferenceState);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mSettingId, mPreferenceState);
+    }
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/PartitionedGridLayoutKosmos.kt b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/IDeviceSettingsConfigProviderService.aidl
similarity index 63%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/PartitionedGridLayoutKosmos.kt
copy to packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/IDeviceSettingsConfigProviderService.aidl
index 37c9552..647611e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/PartitionedGridLayoutKosmos.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/IDeviceSettingsConfigProviderService.aidl
@@ -14,11 +14,11 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.panels.domain.interactor
+package com.android.settingslib.bluetooth.devicesettings;
 
-import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.qs.panels.ui.compose.PartitionedGridLayout
-import com.android.systemui.qs.panels.ui.viewmodel.partitionedGridViewModel
+import com.android.settingslib.bluetooth.devicesettings.DeviceInfo;
+import com.android.settingslib.bluetooth.devicesettings.DeviceSettingsConfig;
 
-val Kosmos.partitionedGridLayout by
-    Kosmos.Fixture { PartitionedGridLayout(partitionedGridViewModel) }
+interface IDeviceSettingsConfigProviderService {
+   DeviceSettingsConfig getDeviceSettingsConfig(in DeviceInfo device);
+}
\ No newline at end of file
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/MultiTogglePreferenceState.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/MultiTogglePreferenceState.java
index 239df0b..d9ce4c5 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/MultiTogglePreferenceState.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/MultiTogglePreferenceState.java
@@ -21,6 +21,9 @@
 import android.os.Parcelable;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import java.util.Objects;
 
 /** A data class representing a multi-toggle preference state. */
 public class MultiTogglePreferenceState extends DeviceSettingPreferenceState implements Parcelable {
@@ -123,4 +126,15 @@
     public Bundle getExtras() {
         return mExtras;
     }
+
+    @Override
+    public boolean equals(@Nullable Object obj) {
+        if (!(obj instanceof MultiTogglePreferenceState other)) return false;
+        return mState == other.mState;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mState);
+    }
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingRepository.kt b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingRepository.kt
new file mode 100644
index 0000000..9ff5c43
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingRepository.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.settingslib.bluetooth.devicesettings.data.repository
+
+import android.bluetooth.BluetoothAdapter
+import android.content.Context
+import com.android.settingslib.bluetooth.CachedBluetoothDevice
+import com.android.settingslib.bluetooth.devicesettings.DeviceSetting
+import com.android.settingslib.bluetooth.devicesettings.DeviceSettingId
+import com.android.settingslib.bluetooth.devicesettings.DeviceSettingPreferenceState
+import com.android.settingslib.bluetooth.devicesettings.DeviceSettingsConfig
+import java.util.concurrent.ConcurrentHashMap
+import kotlin.coroutines.CoroutineContext
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.Flow
+
+/** Provides functionality to control bluetooth device settings. */
+interface DeviceSettingRepository {
+    /** Gets config for the bluetooth device, returns null if failed. */
+    suspend fun getDeviceSettingsConfig(cachedDevice: CachedBluetoothDevice): DeviceSettingsConfig?
+
+    /** Gets all device settings for the bluetooth device. */
+    fun getDeviceSettingList(
+        cachedDevice: CachedBluetoothDevice,
+    ): Flow<List<DeviceSetting>?>
+
+    /** Gets device setting for the bluetooth device. */
+    fun getDeviceSetting(
+        cachedDevice: CachedBluetoothDevice,
+        @DeviceSettingId settingId: Int
+    ): Flow<DeviceSetting?>
+
+    /** Updates device setting for the bluetooth device. */
+    suspend fun updateDeviceSettingState(
+        cachedDevice: CachedBluetoothDevice,
+        @DeviceSettingId deviceSettingId: Int,
+        deviceSettingPreferenceState: DeviceSettingPreferenceState,
+    )
+}
+
+class DeviceSettingRepositoryImpl(
+    private val context: Context,
+    private val bluetoothAdaptor: BluetoothAdapter,
+    private val coroutineScope: CoroutineScope,
+    private val backgroundCoroutineContext: CoroutineContext,
+) : DeviceSettingRepository {
+    private val deviceSettings =
+        ConcurrentHashMap<CachedBluetoothDevice, DeviceSettingServiceConnection>()
+
+    override suspend fun getDeviceSettingsConfig(
+        cachedDevice: CachedBluetoothDevice
+    ): DeviceSettingsConfig? = createConnectionIfAbsent(cachedDevice).getDeviceSettingsConfig()
+
+    override fun getDeviceSettingList(
+        cachedDevice: CachedBluetoothDevice
+    ): Flow<List<DeviceSetting>?> = createConnectionIfAbsent(cachedDevice).getDeviceSettingList()
+
+    override fun getDeviceSetting(
+        cachedDevice: CachedBluetoothDevice,
+        settingId: Int
+    ): Flow<DeviceSetting?> = createConnectionIfAbsent(cachedDevice).getDeviceSetting(settingId)
+
+    override suspend fun updateDeviceSettingState(
+        cachedDevice: CachedBluetoothDevice,
+        @DeviceSettingId deviceSettingId: Int,
+        deviceSettingPreferenceState: DeviceSettingPreferenceState,
+    ) =
+        createConnectionIfAbsent(cachedDevice)
+            .updateDeviceSettings(deviceSettingId, deviceSettingPreferenceState)
+
+    private fun createConnectionIfAbsent(
+        cachedDevice: CachedBluetoothDevice
+    ): DeviceSettingServiceConnection =
+        deviceSettings.computeIfAbsent(cachedDevice) {
+            DeviceSettingServiceConnection(
+                cachedDevice,
+                context,
+                bluetoothAdaptor,
+                coroutineScope,
+                backgroundCoroutineContext,
+            )
+        }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingServiceConnection.kt b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingServiceConnection.kt
new file mode 100644
index 0000000..d6b2862
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingServiceConnection.kt
@@ -0,0 +1,285 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.bluetooth.devicesettings.data.repository
+
+import android.bluetooth.BluetoothAdapter
+import android.content.ComponentName
+import android.content.Context
+import android.content.Intent
+import android.content.ServiceConnection
+import android.os.IBinder
+import com.android.internal.util.ConcurrentUtils
+import com.android.settingslib.bluetooth.BluetoothUtils
+import com.android.settingslib.bluetooth.CachedBluetoothDevice
+import com.android.settingslib.bluetooth.devicesettings.DeviceInfo
+import com.android.settingslib.bluetooth.devicesettings.DeviceSetting
+import com.android.settingslib.bluetooth.devicesettings.DeviceSettingId
+import com.android.settingslib.bluetooth.devicesettings.DeviceSettingPreferenceState
+import com.android.settingslib.bluetooth.devicesettings.DeviceSettingState
+import com.android.settingslib.bluetooth.devicesettings.DeviceSettingsConfig
+import com.android.settingslib.bluetooth.devicesettings.IDeviceSettingsConfigProviderService
+import com.android.settingslib.bluetooth.devicesettings.IDeviceSettingsListener
+import com.android.settingslib.bluetooth.devicesettings.IDeviceSettingsProviderService
+import java.util.concurrent.ConcurrentHashMap
+import java.util.concurrent.atomic.AtomicReference
+import kotlin.coroutines.CoroutineContext
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.callbackFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.emitAll
+import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onStart
+import kotlinx.coroutines.flow.shareIn
+import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+
+@OptIn(ExperimentalCoroutinesApi::class)
+class DeviceSettingServiceConnection(
+    private val cachedDevice: CachedBluetoothDevice,
+    private val context: Context,
+    private val bluetoothAdaptor: BluetoothAdapter,
+    private val coroutineScope: CoroutineScope,
+    private val backgroundCoroutineContext: CoroutineContext,
+) {
+    data class EndPoint(
+        private val packageName: String,
+        private val className: String?,
+        private val intentAction: String,
+    ) {
+        fun toIntent(): Intent =
+            Intent().apply {
+                if (className.isNullOrBlank()) {
+                    setPackage(packageName)
+                } else {
+                    setClassName(packageName, className)
+                }
+                setAction(intentAction)
+            }
+    }
+
+    private var config = AtomicReference<DeviceSettingsConfig?>(null)
+    private var idToSetting = AtomicReference<Flow<Map<Int, DeviceSetting>>?>(null)
+
+    /** Gets [DeviceSettingsConfig] for the device, return null when failed. */
+    suspend fun getDeviceSettingsConfig(): DeviceSettingsConfig? =
+        config.computeIfAbsent {
+            getConfigServiceBindingIntent(cachedDevice)
+                .flatMapLatest { getService(it) }
+                .map { it?.let { IDeviceSettingsConfigProviderService.Stub.asInterface(it) } }
+                .map {
+                    it?.getDeviceSettingsConfig(
+                        deviceInfo { setBluetoothAddress(cachedDevice.address) }
+                    )
+                }
+                .first()
+        }
+
+    /** Gets all device settings for the device. */
+    fun getDeviceSettingList(): Flow<List<DeviceSetting>> =
+        getSettingIdToItemMapping().map { it.values.toList() }
+
+    /** Gets the device settings with the ID for the device. */
+    fun getDeviceSetting(@DeviceSettingId deviceSettingId: Int): Flow<DeviceSetting?> =
+        getSettingIdToItemMapping().map { it[deviceSettingId] }
+
+    /** Updates the device setting state for the device. */
+    suspend fun updateDeviceSettings(
+        @DeviceSettingId deviceSettingId: Int,
+        deviceSettingPreferenceState: DeviceSettingPreferenceState,
+    ) {
+        getDeviceSettingsConfig()?.let { config ->
+            (config.mainContentItems + config.moreSettingsItems)
+                .find { it.settingId == deviceSettingId }
+                ?.let {
+                    getSettingsProviderServices()
+                        ?.get(EndPoint(it.packageName, it.className, it.intentAction))
+                        ?.filterNotNull()
+                        ?.first()
+                }
+                ?.updateDeviceSettings(
+                    deviceInfo { setBluetoothAddress(cachedDevice.address) },
+                    DeviceSettingState.Builder()
+                        .setSettingId(deviceSettingId)
+                        .setPreferenceState(deviceSettingPreferenceState)
+                        .build()
+                )
+        }
+    }
+
+    private suspend fun getSettingsProviderServices():
+        Map<EndPoint, StateFlow<IDeviceSettingsProviderService?>>? =
+        getDeviceSettingsConfig()
+            ?.let { config ->
+                (config.mainContentItems + config.moreSettingsItems).map {
+                    EndPoint(
+                        packageName = it.packageName,
+                        className = it.className,
+                        intentAction = it.intentAction
+                    )
+                }
+            }
+            ?.distinct()
+            ?.associateBy(
+                { it },
+                { endpoint ->
+                    services.computeIfAbsent(endpoint) {
+                        getService(endpoint.toIntent())
+                            .map { service ->
+                                IDeviceSettingsProviderService.Stub.asInterface(service)
+                            }
+                            .stateIn(coroutineScope, SharingStarted.WhileSubscribed(), null)
+                    }
+                }
+            )
+
+    private fun getSettingIdToItemMapping(): Flow<Map<Int, DeviceSetting>> =
+        idToSetting.computeIfAbsent {
+            flow {
+                    getSettingsProviderServices()
+                        ?.values
+                        ?.map {
+                            it.flatMapLatest { service ->
+                                if (service != null) {
+                                    getDeviceSettingsFromService(cachedDevice, service)
+                                } else {
+                                    flowOf(emptyList())
+                                }
+                            }
+                        }
+                        ?.let { items -> combine(items) { it.toList().flatten() } }
+                        ?.map { items -> items.associateBy { it.settingId } }
+                        ?.let { emitAll(it) }
+                }
+                .shareIn(
+                    scope = coroutineScope,
+                    started = SharingStarted.WhileSubscribed(),
+                    replay = 1
+                )
+        }!!
+
+    private fun getDeviceSettingsFromService(
+        cachedDevice: CachedBluetoothDevice,
+        service: IDeviceSettingsProviderService
+    ): Flow<List<DeviceSetting>> {
+        return callbackFlow {
+                val listener =
+                    object : IDeviceSettingsListener.Stub() {
+                        override fun onDeviceSettingsChanged(settings: List<DeviceSetting>) {
+                            launch { send(settings) }
+                        }
+                    }
+                val deviceInfo = deviceInfo { setBluetoothAddress(cachedDevice.address) }
+                service.registerDeviceSettingsListener(deviceInfo, listener)
+                awaitClose { service.unregisterDeviceSettingsListener(deviceInfo, listener) }
+            }
+            .stateIn(coroutineScope, SharingStarted.WhileSubscribed(), emptyList())
+    }
+
+    private fun getService(intent: Intent): Flow<IBinder?> {
+        return callbackFlow {
+            val serviceConnection =
+                object : ServiceConnection {
+                    override fun onServiceConnected(name: ComponentName, service: IBinder) {
+                        launch { send(service) }
+                    }
+
+                    override fun onServiceDisconnected(name: ComponentName?) {
+                        launch { send(null) }
+                    }
+                }
+            if (!context.bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE)) {
+                launch { send(null) }
+            }
+            awaitClose { context.unbindService(serviceConnection) }
+        }
+    }
+
+    private fun getConfigServiceBindingIntent(cachedDevice: CachedBluetoothDevice): Flow<Intent> {
+        return callbackFlow {
+                val listener =
+                    BluetoothAdapter.OnMetadataChangedListener { device, key, _ ->
+                        if (
+                            key == METADATA_FAST_PAIR_CUSTOMIZED_FIELDS &&
+                                cachedDevice.device == device
+                        ) {
+                            launch { tryGetEndpointFromMetadata(cachedDevice)?.let { send(it) } }
+                        }
+                    }
+                bluetoothAdaptor.addOnMetadataChangedListener(
+                    cachedDevice.device,
+                    ConcurrentUtils.DIRECT_EXECUTOR,
+                    listener,
+                )
+                awaitClose {
+                    bluetoothAdaptor.removeOnMetadataChangedListener(cachedDevice.device, listener)
+                }
+            }
+            .onStart { tryGetEndpointFromMetadata(cachedDevice)?.let { emit(it) } }
+            .distinctUntilChanged()
+            .map { it.toIntent() }
+            .flowOn(backgroundCoroutineContext)
+    }
+
+    private suspend fun tryGetEndpointFromMetadata(cachedDevice: CachedBluetoothDevice): EndPoint? =
+        withContext(backgroundCoroutineContext) {
+            val packageName =
+                BluetoothUtils.getFastPairCustomizedField(
+                    cachedDevice.device,
+                    CONFIG_SERVICE_PACKAGE_NAME,
+                ) ?: return@withContext null
+            val className =
+                BluetoothUtils.getFastPairCustomizedField(
+                    cachedDevice.device,
+                    CONFIG_SERVICE_CLASS_NAME
+                ) ?: return@withContext null
+            val intentAction =
+                BluetoothUtils.getFastPairCustomizedField(
+                    cachedDevice.device,
+                    CONFIG_SERVICE_INTENT_ACTION
+                ) ?: return@withContext null
+            EndPoint(packageName, className, intentAction)
+        }
+
+    private inline fun <T> AtomicReference<T?>.computeIfAbsent(producer: () -> T): T? =
+        get() ?: producer().let { compareAndExchange(null, it) ?: it }
+
+    private inline fun deviceInfo(block: DeviceInfo.Builder.() -> Unit): DeviceInfo {
+        return DeviceInfo.Builder().apply { block() }.build()
+    }
+
+    companion object {
+        const val METADATA_FAST_PAIR_CUSTOMIZED_FIELDS: Int = 25
+        const val CONFIG_SERVICE_PACKAGE_NAME = "DEVICE_SETTINGS_CONFIG_PACKAGE_NAME"
+        const val CONFIG_SERVICE_CLASS_NAME = "DEVICE_SETTINGS_CONFIG_CLASS"
+        const val CONFIG_SERVICE_INTENT_ACTION = "DEVICE_SETTINGS_CONFIG_ACTION"
+
+        val services = ConcurrentHashMap<EndPoint, StateFlow<IDeviceSettingsProviderService?>>()
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/shared/model/DeviceSettingModel.kt b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/shared/model/DeviceSettingModel.kt
new file mode 100644
index 0000000..db78280
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/shared/model/DeviceSettingModel.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.settingslib.bluetooth.devicesettings.shared.model
+
+import android.content.Intent
+import android.graphics.Bitmap
+import com.android.settingslib.bluetooth.CachedBluetoothDevice
+import com.android.settingslib.bluetooth.devicesettings.DeviceSettingId
+
+/** Models a device setting. */
+sealed interface DeviceSettingModel {
+    val cachedDevice: CachedBluetoothDevice
+    @DeviceSettingId val id: Int
+
+    /** Models a device setting which should be displayed as an action/switch preference. */
+    data class ActionSwitchPreference(
+        override val cachedDevice: CachedBluetoothDevice,
+        @DeviceSettingId override val id: Int,
+        val title: String,
+        val summary: String? = null,
+        val icon: Bitmap? = null,
+        val intent: Intent? = null,
+        val switchState: DeviceSettingStateModel.ActionSwitchPreferenceState? = null,
+        val isAllowedChangingState: Boolean = true,
+        val updateState: ((DeviceSettingStateModel.ActionSwitchPreferenceState) -> Unit)? = null,
+    ) : DeviceSettingModel
+
+    /** Models a device setting which should be displayed as a multi-toggle preference. */
+    data class MultiTogglePreference(
+        override val cachedDevice: CachedBluetoothDevice,
+        @DeviceSettingId override val id: Int,
+        val title: String,
+        val toggles: List<ToggleModel>,
+        val isActive: Boolean,
+        val state: DeviceSettingStateModel.MultiTogglePreferenceState,
+        val isAllowedChangingState: Boolean,
+        val updateState: (DeviceSettingStateModel.MultiTogglePreferenceState) -> Unit
+    ) : DeviceSettingModel
+
+    /** Models an unknown preference. */
+    data class Unknown(
+        override val cachedDevice: CachedBluetoothDevice,
+        @DeviceSettingId override val id: Int
+    ) : DeviceSettingModel
+}
+
+/** Models a toggle in [DeviceSettingModel.MultiTogglePreference]. */
+data class ToggleModel(val label: String, val icon: Bitmap)
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/shared/model/DeviceSettingStateModel.kt b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/shared/model/DeviceSettingStateModel.kt
new file mode 100644
index 0000000..b404bb9
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/shared/model/DeviceSettingStateModel.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.bluetooth.devicesettings.shared.model
+
+import com.android.settingslib.bluetooth.devicesettings.DeviceSettingPreferenceState
+
+/** Models a device setting state. */
+sealed interface DeviceSettingStateModel {
+    fun toParcelable(): DeviceSettingPreferenceState
+
+    /** Models a device setting state for action/switch preference. */
+    data class ActionSwitchPreferenceState(val checked: Boolean) : DeviceSettingStateModel {
+        override fun toParcelable() =
+            com.android.settingslib.bluetooth.devicesettings.ActionSwitchPreferenceState.Builder()
+                .setChecked(checked)
+                .build()
+    }
+
+    /** Models a device setting state for multi-toggle preference. */
+    data class MultiTogglePreferenceState(val selectedIndex: Int) : DeviceSettingStateModel {
+        override fun toParcelable() =
+            com.android.settingslib.bluetooth.devicesettings.MultiTogglePreferenceState.Builder()
+                .setState(selectedIndex)
+                .build()
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryUtils.java b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryUtils.java
index 8571360..0ed0386 100644
--- a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryUtils.java
@@ -25,6 +25,7 @@
 import android.os.UserManager;
 import android.provider.Settings;
 import android.util.ArraySet;
+import android.util.Log;
 import android.view.accessibility.AccessibilityManager;
 
 import androidx.annotation.Nullable;
@@ -33,6 +34,7 @@
 import java.util.List;
 
 public final class BatteryUtils {
+    private static final String TAG = "BatteryUtils";
 
     /** The key to get the time to full from Settings.Global */
     public static final String GLOBAL_TIME_TO_FULL_MILLIS = "time_to_full_millis";
@@ -89,6 +91,18 @@
         return userManager.isPrivateProfile();
     }
 
+    /** Returns true if current user is an additional profile user. */
+    public static boolean isAdditionalProfile(Context context) {
+        if (isWorkProfile(context)) {
+            Log.d(TAG, "Current user is a work profile user.");
+            return true;
+        } else if (isPrivateProfile(context)) {
+            Log.d(TAG, "Current user is a private profile user.");
+            return true;
+        }
+        return false;
+    }
+
     private static Boolean sChargingStringV2Enabled = null;
 
     /** Returns {@code true} if the charging string v2 is enabled. */
diff --git a/packages/SettingsLib/src/com/android/settingslib/notification/data/repository/FakeZenModeRepository.kt b/packages/SettingsLib/src/com/android/settingslib/notification/data/repository/FakeZenModeRepository.kt
index 49b974f..e5d79a1 100644
--- a/packages/SettingsLib/src/com/android/settingslib/notification/data/repository/FakeZenModeRepository.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/notification/data/repository/FakeZenModeRepository.kt
@@ -40,6 +40,8 @@
     override val modes: Flow<List<ZenMode>>
         get() = mutableModesFlow.asStateFlow()
 
+    private val activeModesDurations = mutableMapOf<String, Duration?>()
+
     init {
         updateNotificationPolicy()
     }
@@ -64,8 +66,22 @@
         mutableModesFlow.value = mutableModesFlow.value.filter { it.id != id }
     }
 
+    fun getMode(id: String): ZenMode? {
+        return mutableModesFlow.value.find { it.id == id }
+    }
+
     override fun activateMode(zenMode: ZenMode, duration: Duration?) {
         activateMode(zenMode.id)
+        activeModesDurations[zenMode.id] = duration
+    }
+
+    fun getModeActiveDuration(id: String): Duration? {
+        if (!activeModesDurations.containsKey(id)) {
+            throw IllegalArgumentException(
+                "mode $id not manually activated, you need to call activateMode"
+            )
+        }
+        return activeModesDurations[id]
     }
 
     override fun deactivateMode(zenMode: ZenMode) {
@@ -73,15 +89,23 @@
     }
 
     fun activateMode(id: String) {
-        val oldMode = mutableModesFlow.value.find { it.id == id } ?: return
-        removeMode(id)
-        mutableModesFlow.value += TestModeBuilder(oldMode).setActive(true).build()
+        updateModeActiveState(id = id, isActive = true)
     }
 
     fun deactivateMode(id: String) {
-        val oldMode = mutableModesFlow.value.find { it.id == id } ?: return
-        removeMode(id)
-        mutableModesFlow.value += TestModeBuilder(oldMode).setActive(false).build()
+        updateModeActiveState(id = id, isActive = false)
+        activeModesDurations.remove(id)
+    }
+
+    // Update the active state while maintaining the mode's position in the list
+    private fun updateModeActiveState(id: String, isActive: Boolean) {
+        val modes = mutableModesFlow.value.toMutableList()
+        val index = modes.indexOfFirst { it.id == id }
+        if (index < 0) {
+            throw IllegalArgumentException("mode $id not found")
+        }
+        modes[index] = TestModeBuilder(modes[index]).setActive(isActive).build()
+        mutableModesFlow.value = modes
     }
 }
 
@@ -101,7 +125,8 @@
             suppressedVisualEffects,
             state,
             priorityConversationSenders,
-        ))
+        )
+    )
 
 private fun newMode(id: String, active: Boolean = false): ZenMode {
     return TestModeBuilder().setId(id).setName("Mode $id").setActive(active).build()
diff --git a/packages/SettingsLib/src/com/android/settingslib/notification/modes/EnableZenModeDialog.java b/packages/SettingsLib/src/com/android/settingslib/notification/modes/EnableZenModeDialog.java
index 054c849..c48694c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/notification/modes/EnableZenModeDialog.java
+++ b/packages/SettingsLib/src/com/android/settingslib/notification/modes/EnableZenModeDialog.java
@@ -159,9 +159,9 @@
                         });
 
         if (mCancelIsNeutral) {
-            builder.setNeutralButton(R.string.cancel, null);
+            builder.setNeutralButton(android.R.string.cancel, null);
         } else {
-            builder.setNegativeButton(R.string.cancel, null);
+            builder.setNegativeButton(android.R.string.cancel, null);
         }
 
         View contentView = getContentView();
diff --git a/packages/SettingsLib/src/com/android/settingslib/notification/modes/TestModeBuilder.java b/packages/SettingsLib/src/com/android/settingslib/notification/modes/TestModeBuilder.java
index 2f7cdd6..a06f084 100644
--- a/packages/SettingsLib/src/com/android/settingslib/notification/modes/TestModeBuilder.java
+++ b/packages/SettingsLib/src/com/android/settingslib/notification/modes/TestModeBuilder.java
@@ -16,6 +16,9 @@
 
 package com.android.settingslib.notification.modes;
 
+import static android.service.notification.ZenModeConfig.UPDATE_ORIGIN_UNKNOWN;
+import static android.service.notification.ZenModeConfig.UPDATE_ORIGIN_USER;
+
 import android.app.AutomaticZenRule;
 import android.app.NotificationManager;
 import android.content.ComponentName;
@@ -144,8 +147,15 @@
     }
 
     public TestModeBuilder setEnabled(boolean enabled) {
+        return setEnabled(enabled, /* byUser= */ false);
+    }
+
+    public TestModeBuilder setEnabled(boolean enabled, boolean byUser) {
         mRule.setEnabled(enabled);
         mConfigZenRule.enabled = enabled;
+        if (!enabled) {
+            mConfigZenRule.disabledOrigin = byUser ? UPDATE_ORIGIN_USER : UPDATE_ORIGIN_UNKNOWN;
+        }
         return this;
     }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenIconLoader.java b/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenIconLoader.java
index 960df63..271d5c4 100644
--- a/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenIconLoader.java
+++ b/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenIconLoader.java
@@ -35,7 +35,6 @@
 
 import androidx.annotation.NonNull;
 import androidx.annotation.VisibleForTesting;
-import androidx.appcompat.content.res.AppCompatResources;
 
 import com.google.common.util.concurrent.FluentFuture;
 import com.google.common.util.concurrent.Futures;
@@ -104,7 +103,7 @@
                 return context.getDrawable(iconResId);
             } else {
                 Context appContext = context.createPackageContext(pkg, 0);
-                Drawable appDrawable = AppCompatResources.getDrawable(appContext, iconResId);
+                Drawable appDrawable = appContext.getDrawable(iconResId);
                 return getMonochromeIconIfPresent(appDrawable);
             }
         })).catching(Exception.class, ex -> {
diff --git a/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenMode.java b/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenMode.java
index 03a2b75..88af7ee 100644
--- a/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenMode.java
+++ b/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenMode.java
@@ -16,6 +16,8 @@
 
 package com.android.settingslib.notification.modes;
 
+import static android.app.AutomaticZenRule.TYPE_SCHEDULE_CALENDAR;
+import static android.app.AutomaticZenRule.TYPE_SCHEDULE_TIME;
 import static android.app.NotificationManager.INTERRUPTION_FILTER_ALL;
 import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY;
 import static android.service.notification.SystemZenRules.getTriggerDescriptionForScheduleEvent;
@@ -156,6 +158,11 @@
         mIsManualDnd = isManualDnd;
     }
 
+    /** Creates a deep copy of this object. */
+    public ZenMode copy() {
+        return new ZenMode(mId, new AutomaticZenRule.Builder(mRule).build(), mStatus, mIsManualDnd);
+    }
+
     @NonNull
     public String getId() {
         return mId;
@@ -298,6 +305,21 @@
         return mIsManualDnd;
     }
 
+    /**
+     * A <em>custom manual</em> mode is a mode created by the user, and not yet assigned an
+     * automatic trigger condition (neither time schedule nor a calendar).
+     */
+    public boolean isCustomManual() {
+        return isSystemOwned()
+                && getType() != TYPE_SCHEDULE_TIME
+                && getType() != TYPE_SCHEDULE_CALENDAR
+                && !isManualDnd();
+    }
+
+    public boolean isEnabled() {
+        return mRule.isEnabled();
+    }
+
     public boolean isActive() {
         return mStatus == Status.ENABLED_AND_ACTIVE;
     }
diff --git a/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenModesBackend.java b/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenModesBackend.java
index d36e2fe..f533c95 100644
--- a/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenModesBackend.java
+++ b/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenModesBackend.java
@@ -28,6 +28,7 @@
 import android.util.Log;
 
 import androidx.annotation.DrawableRes;
+import androidx.annotation.VisibleForTesting;
 
 import com.android.settingslib.R;
 
@@ -61,6 +62,12 @@
         return sInstance;
     }
 
+    /** Replaces the singleton instance of {@link ZenModesBackend} by the provided one. */
+    @VisibleForTesting(otherwise = VisibleForTesting.NONE)
+    public static void setInstance(@Nullable ZenModesBackend backend) {
+        sInstance = backend;
+    }
+
     ZenModesBackend(Context context) {
         mContext = context;
         mNotificationManager = context.getSystemService(NotificationManager.class);
diff --git a/packages/SettingsLib/src/com/android/settingslib/satellite/SatelliteDialogUtils.kt b/packages/SettingsLib/src/com/android/settingslib/satellite/SatelliteDialogUtils.kt
index d69c87b..4b6bcf9 100644
--- a/packages/SettingsLib/src/com/android/settingslib/satellite/SatelliteDialogUtils.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/satellite/SatelliteDialogUtils.kt
@@ -21,6 +21,7 @@
 import android.content.Intent
 import android.os.OutcomeReceiver
 import android.telephony.satellite.SatelliteManager
+import android.telephony.satellite.SatelliteModemStateCallback
 import android.util.Log
 import android.view.WindowManager
 import androidx.lifecycle.LifecycleOwner
@@ -31,12 +32,19 @@
 import kotlinx.coroutines.Dispatchers.Default
 import kotlinx.coroutines.Job
 import kotlinx.coroutines.asExecutor
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.callbackFlow
+import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.suspendCancellableCoroutine
 import kotlinx.coroutines.withContext
 import java.util.concurrent.ExecutionException
 import java.util.concurrent.TimeoutException
 import kotlin.coroutines.resume
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.conflate
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.flow.flowOn
 
 /** A util for Satellite dialog */
 object SatelliteDialogUtils {
@@ -70,7 +78,7 @@
             coroutineScope.launch {
                 var isSatelliteModeOn = false
                 try {
-                    isSatelliteModeOn = requestIsEnabled(context)
+                    isSatelliteModeOn = requestIsSessionStarted(context)
                 } catch (e: InterruptedException) {
                     Log.w(TAG, "Error to get satellite status : $e")
                 } catch (e: ExecutionException) {
@@ -118,19 +126,107 @@
         }
 
         suspendCancellableCoroutine {continuation ->
-            satelliteManager?.requestIsEnabled(Default.asExecutor(),
-                    object : OutcomeReceiver<Boolean, SatelliteManager.SatelliteException> {
-                        override fun onResult(result: Boolean) {
-                            Log.i(TAG, "Satellite modem enabled status: $result")
-                            continuation.resume(result)
-                        }
+            try {
+                satelliteManager?.requestIsEnabled(Default.asExecutor(),
+                        object : OutcomeReceiver<Boolean, SatelliteManager.SatelliteException> {
+                            override fun onResult(result: Boolean) {
+                                Log.i(TAG, "Satellite modem enabled status: $result")
+                                continuation.resume(result)
+                            }
 
-                        override fun onError(error: SatelliteManager.SatelliteException) {
-                            super.onError(error)
-                            Log.w(TAG, "Can't get satellite modem enabled status", error)
-                            continuation.resume(false)
-                        }
-                    })
+                            override fun onError(error: SatelliteManager.SatelliteException) {
+                                super.onError(error)
+                                Log.w(TAG, "Can't get satellite modem enabled status", error)
+                                continuation.resume(false)
+                            }
+                        })
+            } catch (e: IllegalStateException) {
+                Log.w(TAG, "IllegalStateException: $e")
+                continuation.resume(false)
+            }
+        }
+    }
+
+    private suspend fun requestIsSessionStarted(
+            context: Context
+    ): Boolean = withContext(Default) {
+        val satelliteManager: SatelliteManager? =
+                context.getSystemService(SatelliteManager::class.java)
+        if (satelliteManager == null) {
+            Log.w(TAG, "SatelliteManager is null")
+            return@withContext false
+        }
+
+        getIsSessionStartedFlow(context).conflate().first()
+    }
+
+    /**
+     * Provides a Flow that emits the session state of the satellite modem. Updates are triggered
+     * when the modem state changes.
+     *
+     * @param defaultDispatcher The CoroutineDispatcher to use (Defaults to `Dispatchers.Default`).
+     * @return A Flow emitting `true` when the session is started and `false` otherwise.
+     */
+    private fun getIsSessionStartedFlow(
+            context: Context
+    ): Flow<Boolean> {
+        val satelliteManager: SatelliteManager? =
+                context.getSystemService(SatelliteManager::class.java)
+        if (satelliteManager == null) {
+            Log.w(TAG, "SatelliteManager is null")
+            return flowOf(false)
+        }
+
+        return callbackFlow {
+            val callback = SatelliteModemStateCallback { state ->
+                val isSessionStarted = isSatelliteSessionStarted(state)
+                Log.i(TAG, "Satellite modem state changed: state=$state"
+                        + ", isSessionStarted=$isSessionStarted")
+                trySend(isSessionStarted)
+            }
+
+            var registerResult = SatelliteManager.SATELLITE_MODEM_STATE_UNKNOWN
+            try {
+                registerResult = satelliteManager.registerForModemStateChanged(
+                        Default.asExecutor(),
+                        callback
+                )
+            } catch (e: IllegalStateException) {
+                Log.w(TAG, "IllegalStateException: $e")
+            }
+
+
+            if (registerResult != SatelliteManager.SATELLITE_RESULT_SUCCESS) {
+                // If the registration failed (e.g., device doesn't support satellite),
+                // SatelliteManager will not emit the current state by callback.
+                // We send `false` value by ourself to make sure the flow has initial value.
+                Log.w(TAG, "Failed to register for satellite modem state change: $registerResult")
+                trySend(false)
+            }
+
+            awaitClose {
+                try {
+                    satelliteManager.unregisterForModemStateChanged(callback)
+                } catch (e: IllegalStateException) {
+                    Log.w(TAG, "IllegalStateException: $e")
+                }
+            }
+        }.flowOn(Default)
+    }
+
+
+    /**
+     * Check if the modem is in a satellite session.
+     *
+     * @param state The SatelliteModemState provided by the SatelliteManager.
+     * @return `true` if the modem is in a satellite session, `false` otherwise.
+     */
+    fun isSatelliteSessionStarted(@SatelliteManager.SatelliteModemState state: Int): Boolean {
+        return when (state) {
+            SatelliteManager.SATELLITE_MODEM_STATE_OFF,
+            SatelliteManager.SATELLITE_MODEM_STATE_UNAVAILABLE,
+            SatelliteManager.SATELLITE_MODEM_STATE_UNKNOWN -> false
+            else -> true
         }
     }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioSharingRepository.kt b/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioSharingRepository.kt
index eb33a7a..99d5891 100644
--- a/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioSharingRepository.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioSharingRepository.kt
@@ -19,21 +19,18 @@
 import android.bluetooth.BluetoothAdapter
 import android.bluetooth.BluetoothCsipSetCoordinator
 import android.bluetooth.BluetoothDevice
-import android.bluetooth.BluetoothLeBroadcast
-import android.bluetooth.BluetoothLeBroadcastMetadata
 import android.bluetooth.BluetoothProfile
 import android.bluetooth.BluetoothVolumeControl
 import android.content.ContentResolver
-import android.content.Context
 import android.database.ContentObserver
 import android.provider.Settings
 import androidx.annotation.IntRange
 import com.android.internal.util.ConcurrentUtils
 import com.android.settingslib.bluetooth.BluetoothUtils
 import com.android.settingslib.bluetooth.LocalBluetoothManager
+import com.android.settingslib.bluetooth.onBroadcastStartedOrStopped
 import com.android.settingslib.bluetooth.onProfileConnectionStateChanged
 import com.android.settingslib.bluetooth.onSourceConnectedOrRemoved
-import com.android.settingslib.flags.Flags
 import com.android.settingslib.volume.data.repository.AudioSharingRepository.Companion.AUDIO_SHARING_VOLUME_MAX
 import com.android.settingslib.volume.data.repository.AudioSharingRepository.Companion.AUDIO_SHARING_VOLUME_MIN
 import kotlin.coroutines.CoroutineContext
@@ -41,10 +38,10 @@
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.callbackFlow
-import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.emptyFlow
 import kotlinx.coroutines.flow.filter
 import kotlinx.coroutines.flow.flatMapLatest
@@ -65,6 +62,9 @@
     /** Whether the device is in audio sharing. */
     val inAudioSharing: Flow<Boolean>
 
+    /** The primary headset groupId in audio sharing. */
+    val primaryGroupId: StateFlow<Int>
+
     /** The secondary headset groupId in audio sharing. */
     val secondaryGroupId: StateFlow<Int>
 
@@ -85,63 +85,18 @@
 
 @OptIn(ExperimentalCoroutinesApi::class)
 class AudioSharingRepositoryImpl(
-    private val context: Context,
     private val contentResolver: ContentResolver,
-    private val btManager: LocalBluetoothManager?,
+    private val btManager: LocalBluetoothManager,
     private val coroutineScope: CoroutineScope,
     private val backgroundCoroutineContext: CoroutineContext,
 ) : AudioSharingRepository {
     override val inAudioSharing: Flow<Boolean> =
-        if (Flags.enableLeAudioSharing()) {
-            btManager?.profileManager?.leAudioBroadcastProfile?.let { leBroadcast ->
-                callbackFlow {
-                        val listener =
-                            object : BluetoothLeBroadcast.Callback {
-                                override fun onBroadcastStarted(reason: Int, broadcastId: Int) {
-                                    launch { send(isBroadcasting()) }
-                                }
-
-                                override fun onBroadcastStartFailed(reason: Int) {
-                                    launch { send(isBroadcasting()) }
-                                }
-
-                                override fun onBroadcastStopped(reason: Int, broadcastId: Int) {
-                                    launch { send(isBroadcasting()) }
-                                }
-
-                                override fun onBroadcastStopFailed(reason: Int) {
-                                    launch { send(isBroadcasting()) }
-                                }
-
-                                override fun onPlaybackStarted(reason: Int, broadcastId: Int) {}
-
-                                override fun onPlaybackStopped(reason: Int, broadcastId: Int) {}
-
-                                override fun onBroadcastUpdated(reason: Int, broadcastId: Int) {}
-
-                                override fun onBroadcastUpdateFailed(
-                                    reason: Int,
-                                    broadcastId: Int
-                                ) {}
-
-                                override fun onBroadcastMetadataChanged(
-                                    broadcastId: Int,
-                                    metadata: BluetoothLeBroadcastMetadata
-                                ) {}
-                            }
-
-                        leBroadcast.registerServiceCallBack(
-                            ConcurrentUtils.DIRECT_EXECUTOR,
-                            listener,
-                        )
-                        awaitClose { leBroadcast.unregisterServiceCallBack(listener) }
-                    }
-                    .onStart { emit(isBroadcasting()) }
-                    .flowOn(backgroundCoroutineContext)
-            } ?: flowOf(false)
-        } else {
-            flowOf(false)
-        }
+        btManager.profileManager.leAudioBroadcastProfile?.let { broadcast ->
+            broadcast.onBroadcastStartedOrStopped
+                .map { isBroadcasting() }
+                .onStart { emit(isBroadcasting()) }
+                .flowOn(backgroundCoroutineContext)
+        } ?: flowOf(false)
 
     private val primaryChange: Flow<Unit> = callbackFlow {
         val callback =
@@ -157,35 +112,35 @@
         awaitClose { contentResolver.unregisterContentObserver(callback) }
     }
 
+    override val primaryGroupId: StateFlow<Int> =
+        primaryChange
+            .map { BluetoothUtils.getPrimaryGroupIdForBroadcast(contentResolver) }
+            .onStart { emit(BluetoothUtils.getPrimaryGroupIdForBroadcast(contentResolver)) }
+            .flowOn(backgroundCoroutineContext)
+            .stateIn(
+                coroutineScope,
+                SharingStarted.WhileSubscribed(),
+                BluetoothUtils.getPrimaryGroupIdForBroadcast(contentResolver))
+
     override val secondaryGroupId: StateFlow<Int> =
-        if (Flags.volumeDialogAudioSharingFix()) {
-                merge(
-                        btManager
-                            ?.profileManager
-                            ?.leAudioBroadcastAssistantProfile
-                            ?.onSourceConnectedOrRemoved
-                            ?.map { getSecondaryGroupId() } ?: emptyFlow(),
-                        btManager
-                            ?.eventManager
-                            ?.onProfileConnectionStateChanged
-                            ?.filter { profileConnection ->
-                                profileConnection.state == BluetoothAdapter.STATE_DISCONNECTED &&
-                                    profileConnection.bluetoothProfile ==
-                                        BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT
-                            }
-                            ?.map { getSecondaryGroupId() } ?: emptyFlow(),
-                        primaryChange.map { getSecondaryGroupId() })
-                    .onStart { emit(getSecondaryGroupId()) }
-                    .distinctUntilChanged()
-                    .flowOn(backgroundCoroutineContext)
-            } else {
-                emptyFlow()
-            }
+        merge(
+                btManager.profileManager.leAudioBroadcastAssistantProfile
+                    ?.onSourceConnectedOrRemoved
+                    ?.map { getSecondaryGroupId() } ?: emptyFlow(),
+                btManager.eventManager.onProfileConnectionStateChanged
+                    .filter { profileConnection ->
+                        profileConnection.state == BluetoothAdapter.STATE_DISCONNECTED &&
+                            profileConnection.bluetoothProfile ==
+                                BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT
+                    }
+                    .map { getSecondaryGroupId() },
+                primaryGroupId.map { getSecondaryGroupId() })
+            .onStart { emit(getSecondaryGroupId()) }
+            .flowOn(backgroundCoroutineContext)
             .stateIn(coroutineScope, SharingStarted.WhileSubscribed(), getSecondaryGroupId())
 
     override val volumeMap: StateFlow<GroupIdToVolumes> =
-        if (Flags.volumeDialogAudioSharingFix()) {
-            btManager?.profileManager?.volumeControlProfile?.let { volumeControl ->
+        (btManager.profileManager.volumeControlProfile?.let { volumeControl ->
                 inAudioSharing.flatMapLatest { isSharing ->
                     if (isSharing) {
                         callbackFlow {
@@ -210,50 +165,55 @@
                             .runningFold(emptyMap<Int, Int>()) { acc, value ->
                                 val groupId =
                                     BluetoothUtils.getGroupId(
-                                        btManager.cachedDeviceManager?.findDevice(value.first))
+                                        btManager.cachedDeviceManager.findDevice(value.first))
                                 if (groupId != BluetoothCsipSetCoordinator.GROUP_ID_INVALID) {
                                     acc + Pair(groupId, value.second)
                                 } else {
                                     acc
                                 }
                             }
-                            .distinctUntilChanged()
                             .flowOn(backgroundCoroutineContext)
                     } else {
                         emptyFlow()
                     }
                 }
-            } ?: emptyFlow()
-        } else {
-            emptyFlow()
-        }
-        .stateIn(coroutineScope, SharingStarted.WhileSubscribed(), emptyMap())
+            } ?: emptyFlow())
+            .stateIn(coroutineScope, SharingStarted.WhileSubscribed(), emptyMap())
 
     override suspend fun setSecondaryVolume(
         @IntRange(from = AUDIO_SHARING_VOLUME_MIN.toLong(), to = AUDIO_SHARING_VOLUME_MAX.toLong())
         volume: Int
     ) {
         withContext(backgroundCoroutineContext) {
-            if (Flags.volumeDialogAudioSharingFix()) {
-                btManager?.profileManager?.volumeControlProfile?.let {
-                    // Find secondary headset and set volume.
-                    val cachedDevice =
-                        BluetoothUtils.getSecondaryDeviceForBroadcast(context, btManager)
-                    if (cachedDevice != null) {
-                        it.setDeviceVolume(cachedDevice.device, volume, /* isGroupOp= */ true)
-                    }
+            btManager.profileManager.volumeControlProfile?.let {
+                // Find secondary headset and set volume.
+                val cachedDevice =
+                    BluetoothUtils.getSecondaryDeviceForBroadcast(contentResolver, btManager)
+                if (cachedDevice != null) {
+                    it.setDeviceVolume(cachedDevice.device, volume, /* isGroupOp= */ true)
                 }
             }
         }
     }
 
-    private fun isBroadcasting(): Boolean {
-        return Flags.enableLeAudioSharing() &&
-            (btManager?.profileManager?.leAudioBroadcastProfile?.isEnabled(null) ?: false)
-    }
+    private fun isBroadcasting(): Boolean =
+        btManager.profileManager.leAudioBroadcastProfile?.isEnabled(null) ?: false
 
-    private fun getSecondaryGroupId(): Int {
-        return BluetoothUtils.getGroupId(
-            BluetoothUtils.getSecondaryDeviceForBroadcast(context, btManager))
-    }
+    private fun getSecondaryGroupId(): Int =
+        BluetoothUtils.getGroupId(
+            BluetoothUtils.getSecondaryDeviceForBroadcast(contentResolver, btManager))
+}
+
+class AudioSharingRepositoryEmptyImpl : AudioSharingRepository {
+    override val inAudioSharing: Flow<Boolean> = flowOf(false)
+    override val primaryGroupId: StateFlow<Int> =
+        MutableStateFlow(BluetoothCsipSetCoordinator.GROUP_ID_INVALID)
+    override val secondaryGroupId: StateFlow<Int> =
+        MutableStateFlow(BluetoothCsipSetCoordinator.GROUP_ID_INVALID)
+    override val volumeMap: StateFlow<GroupIdToVolumes> = MutableStateFlow(emptyMap())
+
+    override suspend fun setSecondaryVolume(
+        @IntRange(from = AUDIO_SHARING_VOLUME_MIN.toLong(), to = AUDIO_SHARING_VOLUME_MAX.toLong())
+        volume: Int
+    ) {}
 }
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/AudioSharingRepositoryEmptyImplTest.kt b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/AudioSharingRepositoryEmptyImplTest.kt
new file mode 100644
index 0000000..2601592
--- /dev/null
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/AudioSharingRepositoryEmptyImplTest.kt
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.volume.data.repository
+
+import android.bluetooth.BluetoothDevice
+import android.bluetooth.BluetoothLeBroadcastReceiveState
+import android.content.Context
+import android.platform.test.flag.junit.SetFlagsRule
+import android.provider.Settings
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.settingslib.bluetooth.BluetoothEventManager
+import com.android.settingslib.bluetooth.BluetoothUtils
+import com.android.settingslib.bluetooth.CachedBluetoothDevice
+import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager
+import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast
+import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant
+import com.android.settingslib.bluetooth.LocalBluetoothManager
+import com.android.settingslib.bluetooth.LocalBluetoothProfileManager
+import com.android.settingslib.bluetooth.VolumeControlProfile
+import com.google.common.truth.Truth
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.anyBoolean
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.Mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
+import org.mockito.junit.MockitoJUnit
+import org.mockito.junit.MockitoRule
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class AudioSharingRepositoryEmptyImplTest {
+    @get:Rule val mockito: MockitoRule = MockitoJUnit.rule()
+
+    @get:Rule val setFlagsRule: SetFlagsRule = SetFlagsRule()
+
+    @Mock private lateinit var btManager: LocalBluetoothManager
+
+    @Mock private lateinit var profileManager: LocalBluetoothProfileManager
+
+    @Mock private lateinit var broadcast: LocalBluetoothLeBroadcast
+
+    @Mock private lateinit var assistant: LocalBluetoothLeBroadcastAssistant
+
+    @Mock private lateinit var volumeControl: VolumeControlProfile
+
+    @Mock private lateinit var eventManager: BluetoothEventManager
+
+    @Mock private lateinit var deviceManager: CachedBluetoothDeviceManager
+
+    @Mock private lateinit var device1: BluetoothDevice
+
+    @Mock private lateinit var device2: BluetoothDevice
+
+    @Mock private lateinit var cachedDevice1: CachedBluetoothDevice
+
+    @Mock private lateinit var cachedDevice2: CachedBluetoothDevice
+
+    @Mock private lateinit var receiveState: BluetoothLeBroadcastReceiveState
+
+    private val testScope = TestScope()
+    private val context: Context = ApplicationProvider.getApplicationContext()
+    private lateinit var underTest: AudioSharingRepository
+
+    @Before
+    fun setup() {
+        `when`(btManager.profileManager).thenReturn(profileManager)
+        `when`(profileManager.leAudioBroadcastProfile).thenReturn(broadcast)
+        `when`(profileManager.leAudioBroadcastAssistantProfile).thenReturn(assistant)
+        `when`(profileManager.volumeControlProfile).thenReturn(volumeControl)
+        `when`(btManager.eventManager).thenReturn(eventManager)
+        `when`(btManager.cachedDeviceManager).thenReturn(deviceManager)
+        `when`(broadcast.isEnabled(null)).thenReturn(true)
+        `when`(cachedDevice1.groupId).thenReturn(TEST_GROUP_ID1)
+        `when`(cachedDevice1.device).thenReturn(device1)
+        `when`(deviceManager.findDevice(device1)).thenReturn(cachedDevice1)
+        `when`(cachedDevice2.groupId).thenReturn(TEST_GROUP_ID2)
+        `when`(cachedDevice2.device).thenReturn(device2)
+        `when`(deviceManager.findDevice(device2)).thenReturn(cachedDevice2)
+        `when`(receiveState.bisSyncState).thenReturn(arrayListOf(TEST_RECEIVE_STATE_CONTENT))
+        `when`(assistant.getAllSources(any())).thenReturn(listOf(receiveState))
+        underTest = AudioSharingRepositoryEmptyImpl()
+    }
+
+    @Test
+    fun inAudioSharing_returnFalse() {
+        testScope.runTest {
+            val states = mutableListOf<Boolean?>()
+            underTest.inAudioSharing.onEach { states.add(it) }.launchIn(backgroundScope)
+            runCurrent()
+
+            Truth.assertThat(states).containsExactly(false)
+            verify(broadcast, never()).registerServiceCallBack(any(), any())
+            verify(broadcast, never()).isEnabled(any())
+        }
+    }
+
+    @Test
+    fun secondaryGroupIdChange_returnFalse() {
+        testScope.runTest {
+            val groupIds = mutableListOf<Int?>()
+            underTest.secondaryGroupId.onEach { groupIds.add(it) }.launchIn(backgroundScope)
+            runCurrent()
+
+            Truth.assertThat(groupIds).containsExactly(TEST_GROUP_ID_INVALID)
+            verify(assistant, never()).registerServiceCallBack(any(), any())
+            verify(eventManager, never()).registerCallback(any())
+        }
+    }
+
+    @Test
+    fun volumeMapChange_returnFalse() {
+        testScope.runTest {
+            val volumeMaps = mutableListOf<GroupIdToVolumes?>()
+            underTest.volumeMap.onEach { volumeMaps.add(it) }.launchIn(backgroundScope)
+            runCurrent()
+
+            Truth.assertThat(volumeMaps).containsExactly(emptyMap<Int, Int>())
+            verify(broadcast, never()).registerServiceCallBack(any(), any())
+            verify(volumeControl, never()).registerCallback(any(), any())
+        }
+    }
+
+    @Test
+    fun setSecondaryVolume_doNothing() {
+        testScope.runTest {
+            Settings.Secure.putInt(
+                context.contentResolver,
+                BluetoothUtils.getPrimaryGroupIdUriForBroadcast(),
+                TEST_GROUP_ID2)
+            `when`(assistant.allConnectedDevices).thenReturn(listOf(device1, device2))
+            underTest.setSecondaryVolume(TEST_VOLUME1)
+
+            runCurrent()
+            verify(volumeControl, never()).setDeviceVolume(any(), anyInt(), anyBoolean())
+        }
+    }
+
+    private companion object {
+        const val TEST_GROUP_ID_INVALID = -1
+        const val TEST_GROUP_ID1 = 1
+        const val TEST_GROUP_ID2 = 2
+        const val TEST_RECEIVE_STATE_CONTENT = 1L
+        const val TEST_VOLUME1 = 10
+    }
+}
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/AudioSharingRepositoryTest.kt b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/AudioSharingRepositoryTest.kt
index 000664d..c54a2e4 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/AudioSharingRepositoryTest.kt
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/AudioSharingRepositoryTest.kt
@@ -26,8 +26,6 @@
 import android.content.ContentResolver
 import android.content.Context
 import android.database.ContentObserver
-import android.platform.test.annotations.DisableFlags
-import android.platform.test.annotations.EnableFlags
 import android.platform.test.flag.junit.SetFlagsRule
 import android.provider.Settings
 import androidx.test.core.app.ApplicationProvider
@@ -43,7 +41,6 @@
 import com.android.settingslib.bluetooth.LocalBluetoothManager
 import com.android.settingslib.bluetooth.LocalBluetoothProfileManager
 import com.android.settingslib.bluetooth.VolumeControlProfile
-import com.android.settingslib.flags.Flags
 import com.google.common.truth.Truth
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.launchIn
@@ -57,14 +54,12 @@
 import org.junit.runner.RunWith
 import org.mockito.ArgumentCaptor
 import org.mockito.ArgumentMatchers.any
-import org.mockito.ArgumentMatchers.anyBoolean
-import org.mockito.ArgumentMatchers.anyInt
 import org.mockito.ArgumentMatchers.eq
 import org.mockito.Captor
 import org.mockito.Mock
-import org.mockito.Mockito.never
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.`when`
+import org.mockito.Spy
 import org.mockito.junit.MockitoJUnit
 import org.mockito.junit.MockitoRule
 
@@ -100,8 +95,6 @@
 
     @Mock private lateinit var receiveState: BluetoothLeBroadcastReceiveState
 
-    @Mock private lateinit var contentResolver: ContentResolver
-
     @Captor
     private lateinit var broadcastCallbackCaptor: ArgumentCaptor<BluetoothLeBroadcast.Callback>
 
@@ -118,6 +111,7 @@
 
     private val testScope = TestScope()
     private val context: Context = ApplicationProvider.getApplicationContext()
+    @Spy private val contentResolver: ContentResolver = context.contentResolver
     private lateinit var underTest: AudioSharingRepository
 
     @Before
@@ -137,9 +131,12 @@
         `when`(deviceManager.findDevice(device2)).thenReturn(cachedDevice2)
         `when`(receiveState.bisSyncState).thenReturn(arrayListOf(TEST_RECEIVE_STATE_CONTENT))
         `when`(assistant.getAllSources(any())).thenReturn(listOf(receiveState))
+        Settings.Secure.putInt(
+            contentResolver,
+            BluetoothUtils.getPrimaryGroupIdUriForBroadcast(),
+            TEST_GROUP_ID_INVALID)
         underTest =
             AudioSharingRepositoryImpl(
-                context,
                 contentResolver,
                 btManager,
                 testScope.backgroundScope,
@@ -148,7 +145,6 @@
     }
 
     @Test
-    @EnableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
     fun audioSharingStateChange_emitValues() {
         testScope.runTest {
             val states = mutableListOf<Boolean?>()
@@ -164,21 +160,22 @@
     }
 
     @Test
-    @DisableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
-    fun audioSharingFlagOff_returnFalse() {
+    fun primaryGroupIdChange_emitValues() {
         testScope.runTest {
-            val states = mutableListOf<Boolean?>()
-            underTest.inAudioSharing.onEach { states.add(it) }.launchIn(backgroundScope)
+            val groupIds = mutableListOf<Int?>()
+            underTest.primaryGroupId.onEach { groupIds.add(it) }.launchIn(backgroundScope)
+            runCurrent()
+            triggerContentObserverChange()
             runCurrent()
 
-            Truth.assertThat(states).containsExactly(false)
-            verify(broadcast, never()).registerServiceCallBack(any(), any())
-            verify(broadcast, never()).isEnabled(any())
+            Truth.assertThat(groupIds)
+                .containsExactly(
+                    TEST_GROUP_ID_INVALID,
+                    TEST_GROUP_ID2)
         }
     }
 
     @Test
-    @EnableFlags(Flags.FLAG_VOLUME_DIALOG_AUDIO_SHARING_FIX)
     fun secondaryGroupIdChange_emitValues() {
         testScope.runTest {
             val groupIds = mutableListOf<Int?>()
@@ -214,21 +211,6 @@
     }
 
     @Test
-    @DisableFlags(Flags.FLAG_VOLUME_DIALOG_AUDIO_SHARING_FIX)
-    fun secondaryGroupIdChange_audioSharingFlagOff_returnFalse() {
-        testScope.runTest {
-            val groupIds = mutableListOf<Int?>()
-            underTest.secondaryGroupId.onEach { groupIds.add(it) }.launchIn(backgroundScope)
-            runCurrent()
-
-            Truth.assertThat(groupIds).containsExactly(TEST_GROUP_ID_INVALID)
-            verify(assistant, never()).registerServiceCallBack(any(), any())
-            verify(eventManager, never()).registerCallback(any())
-        }
-    }
-
-    @Test
-    @EnableFlags(Flags.FLAG_VOLUME_DIALOG_AUDIO_SHARING_FIX)
     fun volumeMapChange_emitValues() {
         testScope.runTest {
             val volumeMaps = mutableListOf<GroupIdToVolumes?>()
@@ -252,25 +234,10 @@
     }
 
     @Test
-    @DisableFlags(Flags.FLAG_VOLUME_DIALOG_AUDIO_SHARING_FIX)
-    fun volumeMapChange_audioSharingFlagOff_returnFalse() {
-        testScope.runTest {
-            val volumeMaps = mutableListOf<GroupIdToVolumes?>()
-            underTest.volumeMap.onEach { volumeMaps.add(it) }.launchIn(backgroundScope)
-            runCurrent()
-
-            Truth.assertThat(volumeMaps).isEmpty()
-            verify(broadcast, never()).registerServiceCallBack(any(), any())
-            verify(volumeControl, never()).registerCallback(any(), any())
-        }
-    }
-
-    @Test
-    @EnableFlags(Flags.FLAG_VOLUME_DIALOG_AUDIO_SHARING_FIX)
     fun setSecondaryVolume_setValue() {
         testScope.runTest {
             Settings.Secure.putInt(
-                context.contentResolver,
+                contentResolver,
                 BluetoothUtils.getPrimaryGroupIdUriForBroadcast(),
                 TEST_GROUP_ID2)
             `when`(assistant.allConnectedDevices).thenReturn(listOf(device1, device2))
@@ -281,22 +248,6 @@
         }
     }
 
-    @Test
-    @DisableFlags(Flags.FLAG_VOLUME_DIALOG_AUDIO_SHARING_FIX)
-    fun setSecondaryVolume_audioSharingFlagOff_doNothing() {
-        testScope.runTest {
-            Settings.Secure.putInt(
-                context.contentResolver,
-                BluetoothUtils.getPrimaryGroupIdUriForBroadcast(),
-                TEST_GROUP_ID2)
-            `when`(assistant.allConnectedDevices).thenReturn(listOf(device1, device2))
-            underTest.setSecondaryVolume(TEST_VOLUME1)
-
-            runCurrent()
-            verify(volumeControl, never()).setDeviceVolume(any(), anyInt(), anyBoolean())
-        }
-    }
-
     private fun triggerAudioSharingStateChange(
         type: TriggerType,
         broadcastAction: BluetoothLeBroadcast.Callback.() -> Unit
@@ -317,7 +268,7 @@
     private fun triggerSourceAdded() {
         verify(assistant).registerServiceCallBack(any(), assistantCallbackCaptor.capture())
         Settings.Secure.putInt(
-            context.contentResolver,
+            contentResolver,
             BluetoothUtils.getPrimaryGroupIdUriForBroadcast(),
             TEST_GROUP_ID1)
         `when`(assistant.allConnectedDevices).thenReturn(listOf(device1, device2))
@@ -328,7 +279,7 @@
         verify(assistant).registerServiceCallBack(any(), assistantCallbackCaptor.capture())
         `when`(assistant.allConnectedDevices).thenReturn(listOf(device1))
         Settings.Secure.putInt(
-            context.contentResolver,
+            contentResolver,
             BluetoothUtils.getPrimaryGroupIdUriForBroadcast(),
             TEST_GROUP_ID1)
         assistantCallbackCaptor.value.sourceRemoved(device2)
@@ -338,7 +289,7 @@
         verify(eventManager).registerCallback(btCallbackCaptor.capture())
         `when`(assistant.allConnectedDevices).thenReturn(listOf(device1))
         Settings.Secure.putInt(
-            context.contentResolver,
+            contentResolver,
             BluetoothUtils.getPrimaryGroupIdUriForBroadcast(),
             TEST_GROUP_ID1)
         btCallbackCaptor.value.onProfileConnectionStateChanged(cachedDevice2, state, profile)
@@ -352,7 +303,7 @@
                 contentObserverCaptor.capture())
         `when`(assistant.allConnectedDevices).thenReturn(listOf(device1, device2))
         Settings.Secure.putInt(
-            context.contentResolver,
+            contentResolver,
             BluetoothUtils.getPrimaryGroupIdUriForBroadcast(),
             TEST_GROUP_ID2)
         contentObserverCaptor.value.primaryChanged()
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 28bf348..4551f1e 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
@@ -56,29 +56,25 @@
 import org.robolectric.RuntimeEnvironment;
 
 import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
 @RunWith(RobolectricTestRunner.class)
 public class BluetoothUtilsTest {
 
     @Mock(answer = Answers.RETURNS_DEEP_STUBS)
     private CachedBluetoothDevice mCachedBluetoothDevice;
-    @Mock
-    private BluetoothDevice mBluetoothDevice;
-    @Mock
-    private AudioManager mAudioManager;
-    @Mock
-    private PackageManager mPackageManager;
-    @Mock
-    private LocalBluetoothLeBroadcast mBroadcast;
-    @Mock
-    private LocalBluetoothProfileManager mProfileManager;
-    @Mock
-    private LocalBluetoothManager mLocalBluetoothManager;
-    @Mock
-    private LocalBluetoothLeBroadcastAssistant mAssistant;
-    @Mock
-    private BluetoothLeBroadcastReceiveState mLeBroadcastReceiveState;
+
+    @Mock private BluetoothDevice mBluetoothDevice;
+    @Mock private AudioManager mAudioManager;
+    @Mock private PackageManager mPackageManager;
+    @Mock private LocalBluetoothLeBroadcast mBroadcast;
+    @Mock private LocalBluetoothProfileManager mProfileManager;
+    @Mock private LocalBluetoothManager mLocalBluetoothManager;
+    @Mock private LocalBluetoothLeBroadcastAssistant mAssistant;
+    @Mock private BluetoothLeBroadcastReceiveState mLeBroadcastReceiveState;
 
     private Context mContext;
     private static final String STRING_METADATA = "string_metadata";
@@ -87,13 +83,13 @@
     private static final int METADATA_FAST_PAIR_CUSTOMIZED_FIELDS = 25;
     private static final String KEY_HEARABLE_CONTROL_SLICE = "HEARABLE_CONTROL_SLICE_WITH_WIDTH";
     private static final String CONTROL_METADATA =
-            "<HEARABLE_CONTROL_SLICE_WITH_WIDTH>" + STRING_METADATA
+            "<HEARABLE_CONTROL_SLICE_WITH_WIDTH>"
+                    + STRING_METADATA
                     + "</HEARABLE_CONTROL_SLICE_WITH_WIDTH>";
     private static final String TEST_EXCLUSIVE_MANAGER_PACKAGE = "com.test.manager";
     private static final String TEST_EXCLUSIVE_MANAGER_COMPONENT = "com.test.manager/.component";
 
-    @Rule
-    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+    @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
 
     @Before
     public void setUp() {
@@ -108,20 +104,20 @@
 
     @Test
     public void getBtClassDrawableWithDescription_typePhone_returnPhoneDrawable() {
-        when(mCachedBluetoothDevice.getBtClass().getMajorDeviceClass()).thenReturn(
-                BluetoothClass.Device.Major.PHONE);
-        final Pair<Drawable, String> pair = BluetoothUtils.getBtClassDrawableWithDescription(
-                mContext, mCachedBluetoothDevice);
+        when(mCachedBluetoothDevice.getBtClass().getMajorDeviceClass())
+                .thenReturn(BluetoothClass.Device.Major.PHONE);
+        final Pair<Drawable, String> pair =
+                BluetoothUtils.getBtClassDrawableWithDescription(mContext, mCachedBluetoothDevice);
 
         verify(mContext).getDrawable(com.android.internal.R.drawable.ic_phone);
     }
 
     @Test
     public void getBtClassDrawableWithDescription_typeComputer_returnComputerDrawable() {
-        when(mCachedBluetoothDevice.getBtClass().getMajorDeviceClass()).thenReturn(
-                BluetoothClass.Device.Major.COMPUTER);
-        final Pair<Drawable, String> pair = BluetoothUtils.getBtClassDrawableWithDescription(
-                mContext, mCachedBluetoothDevice);
+        when(mCachedBluetoothDevice.getBtClass().getMajorDeviceClass())
+                .thenReturn(BluetoothClass.Device.Major.COMPUTER);
+        final Pair<Drawable, String> pair =
+                BluetoothUtils.getBtClassDrawableWithDescription(mContext, mCachedBluetoothDevice);
 
         verify(mContext).getDrawable(com.android.internal.R.drawable.ic_bt_laptop);
     }
@@ -136,133 +132,146 @@
 
     @Test
     public void getBtRainbowDrawableWithDescription_normalHeadset_returnAdaptiveIcon() {
-        when(mBluetoothDevice.getMetadata(
-                BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)).thenReturn("false".getBytes());
+        when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET))
+                .thenReturn("false".getBytes());
         when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
         when(mCachedBluetoothDevice.getAddress()).thenReturn("1f:aa:bb");
 
-        assertThat(BluetoothUtils.getBtRainbowDrawableWithDescription(
-                RuntimeEnvironment.application,
-                mCachedBluetoothDevice).first).isInstanceOf(AdaptiveIcon.class);
+        assertThat(
+                        BluetoothUtils.getBtRainbowDrawableWithDescription(
+                                        RuntimeEnvironment.application, mCachedBluetoothDevice)
+                                .first)
+                .isInstanceOf(AdaptiveIcon.class);
     }
 
     @Test
     public void getStringMetaData_hasMetaData_getCorrectMetaData() {
-        when(mBluetoothDevice.getMetadata(
-                BluetoothDevice.METADATA_UNTETHERED_LEFT_ICON)).thenReturn(
-                STRING_METADATA.getBytes());
+        when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_UNTETHERED_LEFT_ICON))
+                .thenReturn(STRING_METADATA.getBytes());
 
-        assertThat(BluetoothUtils.getStringMetaData(mBluetoothDevice,
-                BluetoothDevice.METADATA_UNTETHERED_LEFT_ICON)).isEqualTo(STRING_METADATA);
+        assertThat(
+                        BluetoothUtils.getStringMetaData(
+                                mBluetoothDevice, BluetoothDevice.METADATA_UNTETHERED_LEFT_ICON))
+                .isEqualTo(STRING_METADATA);
     }
 
     @Test
     public void getIntMetaData_hasMetaData_getCorrectMetaData() {
-        when(mBluetoothDevice.getMetadata(
-                BluetoothDevice.METADATA_UNTETHERED_LEFT_BATTERY)).thenReturn(
-                INT_METADATA.getBytes());
+        when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_UNTETHERED_LEFT_BATTERY))
+                .thenReturn(INT_METADATA.getBytes());
 
-        assertThat(BluetoothUtils.getIntMetaData(mBluetoothDevice,
-                BluetoothDevice.METADATA_UNTETHERED_LEFT_BATTERY))
+        assertThat(
+                        BluetoothUtils.getIntMetaData(
+                                mBluetoothDevice, BluetoothDevice.METADATA_UNTETHERED_LEFT_BATTERY))
                 .isEqualTo(Integer.parseInt(INT_METADATA));
     }
 
     @Test
     public void getIntMetaData_invalidMetaData_getErrorCode() {
-        when(mBluetoothDevice.getMetadata(
-                BluetoothDevice.METADATA_UNTETHERED_LEFT_BATTERY)).thenReturn(null);
+        when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_UNTETHERED_LEFT_BATTERY))
+                .thenReturn(null);
 
-        assertThat(BluetoothUtils.getIntMetaData(mBluetoothDevice,
-                BluetoothDevice.METADATA_UNTETHERED_LEFT_ICON))
+        assertThat(
+                        BluetoothUtils.getIntMetaData(
+                                mBluetoothDevice, BluetoothDevice.METADATA_UNTETHERED_LEFT_ICON))
                 .isEqualTo(BluetoothUtils.META_INT_ERROR);
     }
 
     @Test
     public void getBooleanMetaData_hasMetaData_getCorrectMetaData() {
-        when(mBluetoothDevice.getMetadata(
-                BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)).thenReturn(
-                BOOL_METADATA.getBytes());
+        when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET))
+                .thenReturn(BOOL_METADATA.getBytes());
 
-        assertThat(BluetoothUtils.getBooleanMetaData(mBluetoothDevice,
-                BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)).isEqualTo(true);
+        assertThat(
+                        BluetoothUtils.getBooleanMetaData(
+                                mBluetoothDevice, BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET))
+                .isTrue();
     }
 
     @Test
     public void getUriMetaData_hasMetaData_getCorrectMetaData() {
-        when(mBluetoothDevice.getMetadata(
-                BluetoothDevice.METADATA_MAIN_ICON)).thenReturn(
-                STRING_METADATA.getBytes());
+        when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_MAIN_ICON))
+                .thenReturn(STRING_METADATA.getBytes());
 
-        assertThat(BluetoothUtils.getUriMetaData(mBluetoothDevice,
-                BluetoothDevice.METADATA_MAIN_ICON)).isEqualTo(Uri.parse(STRING_METADATA));
+        assertThat(
+                        BluetoothUtils.getUriMetaData(
+                                mBluetoothDevice, BluetoothDevice.METADATA_MAIN_ICON))
+                .isEqualTo(Uri.parse(STRING_METADATA));
     }
 
     @Test
     public void getUriMetaData_nullMetaData_getNullUri() {
-        when(mBluetoothDevice.getMetadata(
-                BluetoothDevice.METADATA_MAIN_ICON)).thenReturn(null);
+        when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_MAIN_ICON)).thenReturn(null);
 
-        assertThat(BluetoothUtils.getUriMetaData(mBluetoothDevice,
-                BluetoothDevice.METADATA_MAIN_ICON)).isNull();
+        assertThat(
+                        BluetoothUtils.getUriMetaData(
+                                mBluetoothDevice, BluetoothDevice.METADATA_MAIN_ICON))
+                .isNull();
     }
 
     @Test
     public void getControlUriMetaData_hasMetaData_returnsCorrectMetaData() {
-        when(mBluetoothDevice.getMetadata(METADATA_FAST_PAIR_CUSTOMIZED_FIELDS)).thenReturn(
-                CONTROL_METADATA.getBytes());
+        when(mBluetoothDevice.getMetadata(METADATA_FAST_PAIR_CUSTOMIZED_FIELDS))
+                .thenReturn(CONTROL_METADATA.getBytes());
 
-        assertThat(BluetoothUtils.getControlUriMetaData(mBluetoothDevice)).isEqualTo(
-                STRING_METADATA);
+        assertThat(BluetoothUtils.getControlUriMetaData(mBluetoothDevice))
+                .isEqualTo(STRING_METADATA);
+    }
+
+    @Test
+    public void getFastPairCustomizedField_hasMetaData_returnsCorrectMetaData() {
+        when(mBluetoothDevice.getMetadata(METADATA_FAST_PAIR_CUSTOMIZED_FIELDS))
+                .thenReturn(CONTROL_METADATA.getBytes());
+
+        assertThat(
+                        BluetoothUtils.getFastPairCustomizedField(
+                                mBluetoothDevice, KEY_HEARABLE_CONTROL_SLICE))
+                .isEqualTo(STRING_METADATA);
     }
 
     @Test
     public void isAdvancedDetailsHeader_untetheredHeadset_returnTrue() {
-        when(mBluetoothDevice.getMetadata(
-                BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)).thenReturn(
-                BOOL_METADATA.getBytes());
+        when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET))
+                .thenReturn(BOOL_METADATA.getBytes());
 
-        assertThat(BluetoothUtils.isAdvancedDetailsHeader(mBluetoothDevice)).isEqualTo(true);
+        assertThat(BluetoothUtils.isAdvancedDetailsHeader(mBluetoothDevice)).isTrue();
     }
 
     @Test
     public void isAdvancedDetailsHeader_deviceTypeUntetheredHeadset_returnTrue() {
-        when(mBluetoothDevice.getMetadata(
-                BluetoothDevice.METADATA_DEVICE_TYPE)).thenReturn(
-                BluetoothDevice.DEVICE_TYPE_UNTETHERED_HEADSET.getBytes());
+        when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_DEVICE_TYPE))
+                .thenReturn(BluetoothDevice.DEVICE_TYPE_UNTETHERED_HEADSET.getBytes());
 
-        assertThat(BluetoothUtils.isAdvancedDetailsHeader(mBluetoothDevice)).isEqualTo(true);
+        assertThat(BluetoothUtils.isAdvancedDetailsHeader(mBluetoothDevice)).isTrue();
     }
 
     @Test
     public void isAdvancedDetailsHeader_deviceTypeWatch_returnTrue() {
-        when(mBluetoothDevice.getMetadata(
-                BluetoothDevice.METADATA_DEVICE_TYPE)).thenReturn(
-                BluetoothDevice.DEVICE_TYPE_WATCH.getBytes());
+        when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_DEVICE_TYPE))
+                .thenReturn(BluetoothDevice.DEVICE_TYPE_WATCH.getBytes());
 
-        assertThat(BluetoothUtils.isAdvancedDetailsHeader(mBluetoothDevice)).isEqualTo(true);
+        assertThat(BluetoothUtils.isAdvancedDetailsHeader(mBluetoothDevice)).isTrue();
     }
 
     @Test
     public void isAdvancedDetailsHeader_deviceTypeStylus_returnTrue() {
-        when(mBluetoothDevice.getMetadata(
-                BluetoothDevice.METADATA_DEVICE_TYPE)).thenReturn(
-                BluetoothDevice.DEVICE_TYPE_STYLUS.getBytes());
+        when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_DEVICE_TYPE))
+                .thenReturn(BluetoothDevice.DEVICE_TYPE_STYLUS.getBytes());
 
-        assertThat(BluetoothUtils.isAdvancedDetailsHeader(mBluetoothDevice)).isEqualTo(true);
+        assertThat(BluetoothUtils.isAdvancedDetailsHeader(mBluetoothDevice)).isTrue();
     }
 
     @Test
     public void isAdvancedDetailsHeader_deviceTypeDefault_returnTrue() {
-        when(mBluetoothDevice.getMetadata(
-                BluetoothDevice.METADATA_DEVICE_TYPE)).thenReturn(
-                BluetoothDevice.DEVICE_TYPE_DEFAULT.getBytes());
+        when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_DEVICE_TYPE))
+                .thenReturn(BluetoothDevice.DEVICE_TYPE_DEFAULT.getBytes());
 
-        assertThat(BluetoothUtils.isAdvancedDetailsHeader(mBluetoothDevice)).isEqualTo(true);
+        assertThat(BluetoothUtils.isAdvancedDetailsHeader(mBluetoothDevice)).isTrue();
     }
 
     @Test
     public void isAdvancedDetailsHeader_noMetadata_returnFalse() {
-        assertThat(BluetoothUtils.isAdvancedDetailsHeader(mBluetoothDevice)).isEqualTo(false);
+        assertThat(BluetoothUtils.isAdvancedDetailsHeader(mBluetoothDevice)).isFalse();
     }
 
     @Test
@@ -286,43 +295,39 @@
 
     @Test
     public void isAdvancedUntetheredDevice_untetheredHeadset_returnTrue() {
-        when(mBluetoothDevice.getMetadata(
-                BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)).thenReturn(
-                BOOL_METADATA.getBytes());
+        when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET))
+                .thenReturn(BOOL_METADATA.getBytes());
 
-        assertThat(BluetoothUtils.isAdvancedUntetheredDevice(mBluetoothDevice)).isEqualTo(true);
+        assertThat(BluetoothUtils.isAdvancedUntetheredDevice(mBluetoothDevice)).isTrue();
     }
 
     @Test
     public void isAdvancedUntetheredDevice_deviceTypeUntetheredHeadset_returnTrue() {
-        when(mBluetoothDevice.getMetadata(
-                BluetoothDevice.METADATA_DEVICE_TYPE)).thenReturn(
-                BluetoothDevice.DEVICE_TYPE_UNTETHERED_HEADSET.getBytes());
+        when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_DEVICE_TYPE))
+                .thenReturn(BluetoothDevice.DEVICE_TYPE_UNTETHERED_HEADSET.getBytes());
 
-        assertThat(BluetoothUtils.isAdvancedUntetheredDevice(mBluetoothDevice)).isEqualTo(true);
+        assertThat(BluetoothUtils.isAdvancedUntetheredDevice(mBluetoothDevice)).isTrue();
     }
 
     @Test
     public void isAdvancedUntetheredDevice_deviceTypeWatch_returnFalse() {
-        when(mBluetoothDevice.getMetadata(
-                BluetoothDevice.METADATA_DEVICE_TYPE)).thenReturn(
-                BluetoothDevice.DEVICE_TYPE_WATCH.getBytes());
+        when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_DEVICE_TYPE))
+                .thenReturn(BluetoothDevice.DEVICE_TYPE_WATCH.getBytes());
 
-        assertThat(BluetoothUtils.isAdvancedUntetheredDevice(mBluetoothDevice)).isEqualTo(false);
+        assertThat(BluetoothUtils.isAdvancedUntetheredDevice(mBluetoothDevice)).isFalse();
     }
 
     @Test
     public void isAdvancedUntetheredDevice_deviceTypeDefault_returnFalse() {
-        when(mBluetoothDevice.getMetadata(
-                BluetoothDevice.METADATA_DEVICE_TYPE)).thenReturn(
-                BluetoothDevice.DEVICE_TYPE_DEFAULT.getBytes());
+        when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_DEVICE_TYPE))
+                .thenReturn(BluetoothDevice.DEVICE_TYPE_DEFAULT.getBytes());
 
-        assertThat(BluetoothUtils.isAdvancedUntetheredDevice(mBluetoothDevice)).isEqualTo(false);
+        assertThat(BluetoothUtils.isAdvancedUntetheredDevice(mBluetoothDevice)).isFalse();
     }
 
     @Test
     public void isAdvancedUntetheredDevice_noMetadata_returnFalse() {
-        assertThat(BluetoothUtils.isAdvancedUntetheredDevice(mBluetoothDevice)).isEqualTo(false);
+        assertThat(BluetoothUtils.isAdvancedUntetheredDevice(mBluetoothDevice)).isFalse();
     }
 
     @Test
@@ -344,8 +349,10 @@
         when(mBluetoothDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
         when(mBluetoothDevice.isConnected()).thenReturn(true);
 
-        assertThat(BluetoothUtils.isAvailableMediaBluetoothDevice(mCachedBluetoothDevice,
-                mAudioManager)).isEqualTo(true);
+        assertThat(
+                        BluetoothUtils.isAvailableMediaBluetoothDevice(
+                                mCachedBluetoothDevice, mAudioManager))
+                .isTrue();
     }
 
     @Test
@@ -356,8 +363,10 @@
         when(mBluetoothDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
         when(mBluetoothDevice.isConnected()).thenReturn(true);
 
-        assertThat(BluetoothUtils.isAvailableMediaBluetoothDevice(mCachedBluetoothDevice,
-                mAudioManager)).isEqualTo(false);
+        assertThat(
+                        BluetoothUtils.isAvailableMediaBluetoothDevice(
+                                mCachedBluetoothDevice, mAudioManager))
+                .isFalse();
     }
 
     @Test
@@ -368,8 +377,10 @@
         when(mBluetoothDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
         when(mBluetoothDevice.isConnected()).thenReturn(true);
 
-        assertThat(BluetoothUtils.isAvailableMediaBluetoothDevice(mCachedBluetoothDevice,
-                mAudioManager)).isEqualTo(true);
+        assertThat(
+                        BluetoothUtils.isAvailableMediaBluetoothDevice(
+                                mCachedBluetoothDevice, mAudioManager))
+                .isTrue();
     }
 
     @Test
@@ -380,8 +391,10 @@
         when(mBluetoothDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
         when(mBluetoothDevice.isConnected()).thenReturn(true);
 
-        assertThat(BluetoothUtils.isAvailableMediaBluetoothDevice(mCachedBluetoothDevice,
-                mAudioManager)).isEqualTo(true);
+        assertThat(
+                        BluetoothUtils.isAvailableMediaBluetoothDevice(
+                                mCachedBluetoothDevice, mAudioManager))
+                .isTrue();
     }
 
     @Test
@@ -391,8 +404,8 @@
         when(mBluetoothDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
         when(mBluetoothDevice.isConnected()).thenReturn(true);
 
-        assertThat(BluetoothUtils.isConnectedBluetoothDevice(mCachedBluetoothDevice,
-                mAudioManager)).isEqualTo(false);
+        assertThat(BluetoothUtils.isConnectedBluetoothDevice(mCachedBluetoothDevice, mAudioManager))
+                .isFalse();
     }
 
     @Test
@@ -403,8 +416,8 @@
         when(mBluetoothDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
         when(mBluetoothDevice.isConnected()).thenReturn(true);
 
-        assertThat(BluetoothUtils.isConnectedBluetoothDevice(mCachedBluetoothDevice,
-                mAudioManager)).isEqualTo(true);
+        assertThat(BluetoothUtils.isConnectedBluetoothDevice(mCachedBluetoothDevice, mAudioManager))
+                .isTrue();
     }
 
     @Test
@@ -415,8 +428,8 @@
         when(mBluetoothDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
         when(mBluetoothDevice.isConnected()).thenReturn(true);
 
-        assertThat(BluetoothUtils.isConnectedBluetoothDevice(mCachedBluetoothDevice,
-                mAudioManager)).isEqualTo(false);
+        assertThat(BluetoothUtils.isConnectedBluetoothDevice(mCachedBluetoothDevice, mAudioManager))
+                .isFalse();
     }
 
     @Test
@@ -427,8 +440,8 @@
         when(mBluetoothDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
         when(mBluetoothDevice.isConnected()).thenReturn(true);
 
-        assertThat(BluetoothUtils.isConnectedBluetoothDevice(mCachedBluetoothDevice,
-                mAudioManager)).isEqualTo(false);
+        assertThat(BluetoothUtils.isConnectedBluetoothDevice(mCachedBluetoothDevice, mAudioManager))
+                .isFalse();
     }
 
     @Test
@@ -439,58 +452,61 @@
         when(mBluetoothDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
         when(mBluetoothDevice.isConnected()).thenReturn(false);
 
-        assertThat(BluetoothUtils.isConnectedBluetoothDevice(mCachedBluetoothDevice,
-                mAudioManager)).isEqualTo(false);
+        assertThat(BluetoothUtils.isConnectedBluetoothDevice(mCachedBluetoothDevice, mAudioManager))
+                .isFalse();
     }
 
     @Test
     public void isExclusivelyManaged_hasNoManager_returnFalse() {
-        when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_EXCLUSIVE_MANAGER)).thenReturn(
-                null);
+        when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_EXCLUSIVE_MANAGER))
+                .thenReturn(null);
 
-        assertThat(BluetoothUtils.isExclusivelyManagedBluetoothDevice(mContext,
-                mBluetoothDevice)).isEqualTo(false);
+        assertThat(BluetoothUtils.isExclusivelyManagedBluetoothDevice(mContext, mBluetoothDevice))
+                .isFalse();
     }
 
     @Test
     public void isExclusivelyManaged_hasPackageName_packageNotInstalled_returnFalse()
             throws Exception {
-        when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_EXCLUSIVE_MANAGER)).thenReturn(
-                TEST_EXCLUSIVE_MANAGER_PACKAGE.getBytes());
+        when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_EXCLUSIVE_MANAGER))
+                .thenReturn(TEST_EXCLUSIVE_MANAGER_PACKAGE.getBytes());
         when(mContext.getPackageManager()).thenReturn(mPackageManager);
-        doThrow(new PackageManager.NameNotFoundException()).when(mPackageManager)
+        doThrow(new PackageManager.NameNotFoundException())
+                .when(mPackageManager)
                 .getApplicationInfo(TEST_EXCLUSIVE_MANAGER_PACKAGE, 0);
 
-        assertThat(BluetoothUtils.isExclusivelyManagedBluetoothDevice(mContext,
-                mBluetoothDevice)).isEqualTo(false);
+        assertThat(BluetoothUtils.isExclusivelyManagedBluetoothDevice(mContext, mBluetoothDevice))
+                .isFalse();
     }
 
     @Test
     public void isExclusivelyManaged_hasComponentName_packageNotInstalled_returnFalse()
             throws Exception {
-        when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_EXCLUSIVE_MANAGER)).thenReturn(
-                TEST_EXCLUSIVE_MANAGER_COMPONENT.getBytes());
+        when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_EXCLUSIVE_MANAGER))
+                .thenReturn(TEST_EXCLUSIVE_MANAGER_COMPONENT.getBytes());
         when(mContext.getPackageManager()).thenReturn(mPackageManager);
-        doThrow(new PackageManager.NameNotFoundException()).when(mPackageManager)
+        doThrow(new PackageManager.NameNotFoundException())
+                .when(mPackageManager)
                 .getApplicationInfo(TEST_EXCLUSIVE_MANAGER_PACKAGE, 0);
 
-        assertThat(BluetoothUtils.isExclusivelyManagedBluetoothDevice(mContext,
-            mBluetoothDevice)).isEqualTo(false);
+        assertThat(BluetoothUtils.isExclusivelyManagedBluetoothDevice(mContext, mBluetoothDevice))
+                .isFalse();
     }
 
     @Test
     public void isExclusivelyManaged_hasPackageName_packageNotEnabled_returnFalse()
-             throws Exception {
+            throws Exception {
         ApplicationInfo appInfo = new ApplicationInfo();
         appInfo.enabled = false;
         when(mContext.getPackageManager()).thenReturn(mPackageManager);
-        doReturn(appInfo).when(mPackageManager).getApplicationInfo(
-                TEST_EXCLUSIVE_MANAGER_PACKAGE, 0);
-        when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_EXCLUSIVE_MANAGER)).thenReturn(
-                TEST_EXCLUSIVE_MANAGER_PACKAGE.getBytes());
+        doReturn(appInfo)
+                .when(mPackageManager)
+                .getApplicationInfo(TEST_EXCLUSIVE_MANAGER_PACKAGE, 0);
+        when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_EXCLUSIVE_MANAGER))
+                .thenReturn(TEST_EXCLUSIVE_MANAGER_PACKAGE.getBytes());
 
-        assertThat(BluetoothUtils.isExclusivelyManagedBluetoothDevice(mContext,
-            mBluetoothDevice)).isEqualTo(false);
+        assertThat(BluetoothUtils.isExclusivelyManagedBluetoothDevice(mContext, mBluetoothDevice))
+                .isFalse();
     }
 
     @Test
@@ -499,50 +515,59 @@
         ApplicationInfo appInfo = new ApplicationInfo();
         appInfo.enabled = false;
         when(mContext.getPackageManager()).thenReturn(mPackageManager);
-        doReturn(appInfo).when(mPackageManager).getApplicationInfo(
-                TEST_EXCLUSIVE_MANAGER_PACKAGE, 0);
-        when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_EXCLUSIVE_MANAGER)).thenReturn(
-                TEST_EXCLUSIVE_MANAGER_COMPONENT.getBytes());
+        doReturn(appInfo)
+                .when(mPackageManager)
+                .getApplicationInfo(TEST_EXCLUSIVE_MANAGER_PACKAGE, 0);
+        when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_EXCLUSIVE_MANAGER))
+                .thenReturn(TEST_EXCLUSIVE_MANAGER_COMPONENT.getBytes());
 
-        assertThat(BluetoothUtils.isExclusivelyManagedBluetoothDevice(mContext,
-            mBluetoothDevice)).isEqualTo(false);
+        assertThat(BluetoothUtils.isExclusivelyManagedBluetoothDevice(mContext, mBluetoothDevice))
+                .isFalse();
     }
 
     @Test
     public void isExclusivelyManaged_hasPackageName_packageInstalledAndEnabled_returnTrue()
             throws Exception {
-        when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_EXCLUSIVE_MANAGER)).thenReturn(
-                TEST_EXCLUSIVE_MANAGER_PACKAGE.getBytes());
+        when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_EXCLUSIVE_MANAGER))
+                .thenReturn(TEST_EXCLUSIVE_MANAGER_PACKAGE.getBytes());
         when(mContext.getPackageManager()).thenReturn(mPackageManager);
-        doReturn(new ApplicationInfo()).when(mPackageManager).getApplicationInfo(
-                TEST_EXCLUSIVE_MANAGER_PACKAGE, 0);
+        doReturn(new ApplicationInfo())
+                .when(mPackageManager)
+                .getApplicationInfo(TEST_EXCLUSIVE_MANAGER_PACKAGE, 0);
 
-        assertThat(BluetoothUtils.isExclusivelyManagedBluetoothDevice(mContext,
-            mBluetoothDevice)).isEqualTo(true);
+        assertThat(BluetoothUtils.isExclusivelyManagedBluetoothDevice(mContext, mBluetoothDevice))
+                .isTrue();
     }
 
     @Test
     public void isExclusivelyManaged_hasComponentName_packageInstalledAndEnabled_returnTrue()
             throws Exception {
-        when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_EXCLUSIVE_MANAGER)).thenReturn(
-                TEST_EXCLUSIVE_MANAGER_COMPONENT.getBytes());
+        when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_EXCLUSIVE_MANAGER))
+                .thenReturn(TEST_EXCLUSIVE_MANAGER_COMPONENT.getBytes());
         when(mContext.getPackageManager()).thenReturn(mPackageManager);
-        doReturn(new ApplicationInfo()).when(mPackageManager).getApplicationInfo(
-                TEST_EXCLUSIVE_MANAGER_PACKAGE, 0);
+        doReturn(new ApplicationInfo())
+                .when(mPackageManager)
+                .getApplicationInfo(TEST_EXCLUSIVE_MANAGER_PACKAGE, 0);
 
-        assertThat(BluetoothUtils.isExclusivelyManagedBluetoothDevice(mContext,
-            mBluetoothDevice)).isEqualTo(true);
+        assertThat(BluetoothUtils.isExclusivelyManagedBluetoothDevice(mContext, mBluetoothDevice))
+                .isTrue();
     }
 
     @Test
     public void testIsBroadcasting_broadcastEnabled_returnTrue() {
         when(mBroadcast.isEnabled(any())).thenReturn(true);
-        assertThat(BluetoothUtils.isBroadcasting(mLocalBluetoothManager)).isEqualTo(true);
+        assertThat(BluetoothUtils.isBroadcasting(mLocalBluetoothManager)).isTrue();
     }
 
     @Test
-    public void testHasConnectedBroadcastSource_deviceConnectedToBroadcastSource() {
+    public void testHasConnectedBroadcastSource_leadDeviceConnectedToBroadcastSource() {
         when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
+        CachedBluetoothDevice memberCachedDevice = mock(CachedBluetoothDevice.class);
+        BluetoothDevice memberDevice = mock(BluetoothDevice.class);
+        when(memberCachedDevice.getDevice()).thenReturn(memberDevice);
+        Set<CachedBluetoothDevice> memberCachedDevices = new HashSet<>();
+        memberCachedDevices.add(memberCachedDevice);
+        when(mCachedBluetoothDevice.getMemberDevice()).thenReturn(memberCachedDevices);
 
         List<Long> bisSyncState = new ArrayList<>();
         bisSyncState.add(1L);
@@ -550,12 +575,86 @@
 
         List<BluetoothLeBroadcastReceiveState> sourceList = new ArrayList<>();
         sourceList.add(mLeBroadcastReceiveState);
-        when(mAssistant.getAllSources(any())).thenReturn(sourceList);
+        when(mAssistant.getAllSources(mBluetoothDevice)).thenReturn(sourceList);
+        when(mAssistant.getAllSources(memberDevice)).thenReturn(Collections.emptyList());
 
         assertThat(
-                BluetoothUtils.hasConnectedBroadcastSource(
-                        mCachedBluetoothDevice, mLocalBluetoothManager))
-                .isEqualTo(true);
+                        BluetoothUtils.hasConnectedBroadcastSource(
+                                mCachedBluetoothDevice, mLocalBluetoothManager))
+                .isTrue();
+    }
+
+    @Test
+    public void testHasConnectedBroadcastSource_memberDeviceConnectedToBroadcastSource() {
+        when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
+        CachedBluetoothDevice memberCachedDevice = mock(CachedBluetoothDevice.class);
+        BluetoothDevice memberDevice = mock(BluetoothDevice.class);
+        when(memberCachedDevice.getDevice()).thenReturn(memberDevice);
+        Set<CachedBluetoothDevice> memberCachedDevices = new HashSet<>();
+        memberCachedDevices.add(memberCachedDevice);
+        when(mCachedBluetoothDevice.getMemberDevice()).thenReturn(memberCachedDevices);
+
+        List<Long> bisSyncState = new ArrayList<>();
+        bisSyncState.add(1L);
+        when(mLeBroadcastReceiveState.getBisSyncState()).thenReturn(bisSyncState);
+
+        List<BluetoothLeBroadcastReceiveState> sourceList = new ArrayList<>();
+        sourceList.add(mLeBroadcastReceiveState);
+        when(mAssistant.getAllSources(memberDevice)).thenReturn(sourceList);
+        when(mAssistant.getAllSources(mBluetoothDevice)).thenReturn(Collections.emptyList());
+
+        assertThat(
+                        BluetoothUtils.hasConnectedBroadcastSource(
+                                mCachedBluetoothDevice, mLocalBluetoothManager))
+                .isTrue();
+    }
+
+    @Test
+    public void testHasConnectedBroadcastSource_deviceNotConnectedToBroadcastSource() {
+        when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
+
+        List<Long> bisSyncState = new ArrayList<>();
+        when(mLeBroadcastReceiveState.getBisSyncState()).thenReturn(bisSyncState);
+
+        List<BluetoothLeBroadcastReceiveState> sourceList = new ArrayList<>();
+        sourceList.add(mLeBroadcastReceiveState);
+        when(mAssistant.getAllSources(mBluetoothDevice)).thenReturn(sourceList);
+
+        assertThat(
+                        BluetoothUtils.hasConnectedBroadcastSource(
+                                mCachedBluetoothDevice, mLocalBluetoothManager))
+                .isFalse();
+    }
+
+    @Test
+    public void testHasConnectedBroadcastSourceForBtDevice_deviceConnectedToBroadcastSource() {
+        List<Long> bisSyncState = new ArrayList<>();
+        bisSyncState.add(1L);
+        when(mLeBroadcastReceiveState.getBisSyncState()).thenReturn(bisSyncState);
+
+        List<BluetoothLeBroadcastReceiveState> sourceList = new ArrayList<>();
+        sourceList.add(mLeBroadcastReceiveState);
+        when(mAssistant.getAllSources(mBluetoothDevice)).thenReturn(sourceList);
+
+        assertThat(
+                        BluetoothUtils.hasConnectedBroadcastSourceForBtDevice(
+                                mBluetoothDevice, mLocalBluetoothManager))
+                .isTrue();
+    }
+
+    @Test
+    public void testHasConnectedBroadcastSourceForBtDevice_deviceNotConnectedToBroadcastSource() {
+        List<Long> bisSyncState = new ArrayList<>();
+        when(mLeBroadcastReceiveState.getBisSyncState()).thenReturn(bisSyncState);
+
+        List<BluetoothLeBroadcastReceiveState> sourceList = new ArrayList<>();
+        sourceList.add(mLeBroadcastReceiveState);
+        when(mAssistant.getAllSources(mBluetoothDevice)).thenReturn(sourceList);
+
+        assertThat(
+                        BluetoothUtils.hasConnectedBroadcastSourceForBtDevice(
+                                mBluetoothDevice, mLocalBluetoothManager))
+                .isFalse();
     }
 
     @Test
@@ -565,7 +664,7 @@
         when(mBluetoothDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
         when(mBluetoothDevice.isConnected()).thenReturn(true);
 
-        assertThat(BluetoothUtils.isAvailableHearingDevice(mCachedBluetoothDevice)).isEqualTo(true);
+        assertThat(BluetoothUtils.isAvailableHearingDevice(mCachedBluetoothDevice)).isTrue();
     }
 
     @Test
@@ -588,8 +687,19 @@
     }
 
     @Test
-    public void getSecondaryDeviceForBroadcast_errorState_returnNull() {
-        assertThat(BluetoothUtils.getSecondaryDeviceForBroadcast(mContext, mLocalBluetoothManager))
+    public void getSecondaryDeviceForBroadcast_noBroadcast_returnNull() {
+        assertThat(
+                        BluetoothUtils.getSecondaryDeviceForBroadcast(
+                                mContext.getContentResolver(), mLocalBluetoothManager))
+                .isNull();
+    }
+
+    @Test
+    public void getSecondaryDeviceForBroadcast_nullProfile_returnNull() {
+        when(mProfileManager.getLeAudioBroadcastProfile()).thenReturn(null);
+        assertThat(
+                        BluetoothUtils.getSecondaryDeviceForBroadcast(
+                                mContext.getContentResolver(), mLocalBluetoothManager))
                 .isNull();
     }
 
@@ -599,6 +709,7 @@
                 mContext.getContentResolver(),
                 BluetoothUtils.getPrimaryGroupIdUriForBroadcast(),
                 1);
+        when(mBroadcast.isEnabled(any())).thenReturn(true);
         CachedBluetoothDeviceManager deviceManager = mock(CachedBluetoothDeviceManager.class);
         when(mLocalBluetoothManager.getCachedDeviceManager()).thenReturn(deviceManager);
         when(deviceManager.findDevice(mBluetoothDevice)).thenReturn(mCachedBluetoothDevice);
@@ -608,7 +719,9 @@
         when(mAssistant.getAllSources(mBluetoothDevice)).thenReturn(ImmutableList.of(state));
         when(mAssistant.getAllConnectedDevices()).thenReturn(ImmutableList.of(mBluetoothDevice));
 
-        assertThat(BluetoothUtils.getSecondaryDeviceForBroadcast(mContext, mLocalBluetoothManager))
+        assertThat(
+                        BluetoothUtils.getSecondaryDeviceForBroadcast(
+                                mContext.getContentResolver(), mLocalBluetoothManager))
                 .isNull();
     }
 
@@ -618,6 +731,7 @@
                 mContext.getContentResolver(),
                 BluetoothUtils.getPrimaryGroupIdUriForBroadcast(),
                 1);
+        when(mBroadcast.isEnabled(any())).thenReturn(true);
         CachedBluetoothDeviceManager deviceManager = mock(CachedBluetoothDeviceManager.class);
         when(mLocalBluetoothManager.getCachedDeviceManager()).thenReturn(deviceManager);
         CachedBluetoothDevice cachedBluetoothDevice = mock(CachedBluetoothDevice.class);
@@ -637,7 +751,9 @@
         when(mAssistant.getAllConnectedDevices())
                 .thenReturn(ImmutableList.of(mBluetoothDevice, bluetoothDevice));
 
-        assertThat(BluetoothUtils.getSecondaryDeviceForBroadcast(mContext, mLocalBluetoothManager))
+        assertThat(
+                        BluetoothUtils.getSecondaryDeviceForBroadcast(
+                                mContext.getContentResolver(), mLocalBluetoothManager))
                 .isEqualTo(mCachedBluetoothDevice);
     }
 }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingRepositoryTest.kt b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingRepositoryTest.kt
new file mode 100644
index 0000000..b5457c5
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingRepositoryTest.kt
@@ -0,0 +1,394 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.bluetooth.devicesettings.data.repository
+
+import android.bluetooth.BluetoothAdapter
+import android.bluetooth.BluetoothDevice
+import android.content.ComponentName
+import android.content.Context
+import android.content.Intent
+import android.content.ServiceConnection
+import com.android.settingslib.bluetooth.CachedBluetoothDevice
+import com.android.settingslib.bluetooth.devicesettings.ActionSwitchPreference
+import com.android.settingslib.bluetooth.devicesettings.ActionSwitchPreferenceState
+import com.android.settingslib.bluetooth.devicesettings.DeviceInfo
+import com.android.settingslib.bluetooth.devicesettings.DeviceSetting
+import com.android.settingslib.bluetooth.devicesettings.DeviceSettingId
+import com.android.settingslib.bluetooth.devicesettings.DeviceSettingItem
+import com.android.settingslib.bluetooth.devicesettings.DeviceSettingState
+import com.android.settingslib.bluetooth.devicesettings.DeviceSettingsConfig
+import com.android.settingslib.bluetooth.devicesettings.IDeviceSettingsConfigProviderService
+import com.android.settingslib.bluetooth.devicesettings.IDeviceSettingsListener
+import com.android.settingslib.bluetooth.devicesettings.IDeviceSettingsProviderService
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.anyString
+import org.mockito.ArgumentMatchers.eq
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito.doReturn
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
+import org.mockito.junit.MockitoJUnit
+import org.mockito.junit.MockitoRule
+import org.robolectric.RobolectricTestRunner
+
+@RunWith(RobolectricTestRunner::class)
+@OptIn(ExperimentalCoroutinesApi::class)
+class DeviceSettingRepositoryTest {
+    @get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule()
+
+    @Mock private lateinit var cachedDevice: CachedBluetoothDevice
+    @Mock private lateinit var bluetoothDevice: BluetoothDevice
+    @Mock private lateinit var context: Context
+    @Mock private lateinit var bluetoothAdapter: BluetoothAdapter
+    @Mock private lateinit var configService: IDeviceSettingsConfigProviderService.Stub
+    @Mock private lateinit var settingProviderService1: IDeviceSettingsProviderService.Stub
+    @Mock private lateinit var settingProviderService2: IDeviceSettingsProviderService.Stub
+    @Captor
+    private lateinit var metadataChangeCaptor:
+        ArgumentCaptor<BluetoothAdapter.OnMetadataChangedListener>
+
+    private lateinit var underTest: DeviceSettingRepository
+    private val testScope = TestScope()
+
+    @Before
+    fun setUp() {
+        `when`(cachedDevice.device).thenReturn(bluetoothDevice)
+        `when`(cachedDevice.address).thenReturn(BLUETOOTH_ADDRESS)
+        `when`(
+                bluetoothDevice.getMetadata(
+                    DeviceSettingServiceConnection.METADATA_FAST_PAIR_CUSTOMIZED_FIELDS
+                )
+            )
+            .thenReturn(BLUETOOTH_DEVICE_METADATA.toByteArray())
+
+        `when`(configService.queryLocalInterface(anyString())).thenReturn(configService)
+        `when`(settingProviderService1.queryLocalInterface(anyString()))
+            .thenReturn(settingProviderService1)
+        `when`(settingProviderService2.queryLocalInterface(anyString()))
+            .thenReturn(settingProviderService2)
+
+        `when`(context.bindService(any(), any(), anyInt())).then { input ->
+            val intent = input.getArgument<Intent?>(0)
+            val connection = input.getArgument<ServiceConnection>(1)
+
+            when (intent?.action) {
+                CONFIG_SERVICE_INTENT_ACTION ->
+                    connection.onServiceConnected(
+                        ComponentName(CONFIG_SERVICE_PACKAGE_NAME, CONFIG_SERVICE_CLASS_NAME),
+                        configService,
+                    )
+                SETTING_PROVIDER_SERVICE_INTENT_ACTION_1 ->
+                    connection.onServiceConnected(
+                        ComponentName(
+                            SETTING_PROVIDER_SERVICE_PACKAGE_NAME_1,
+                            SETTING_PROVIDER_SERVICE_CLASS_NAME_1
+                        ),
+                        settingProviderService1,
+                    )
+                SETTING_PROVIDER_SERVICE_INTENT_ACTION_2 ->
+                    connection.onServiceConnected(
+                        ComponentName(
+                            SETTING_PROVIDER_SERVICE_PACKAGE_NAME_2,
+                            SETTING_PROVIDER_SERVICE_CLASS_NAME_2,
+                        ),
+                        settingProviderService2,
+                    )
+            }
+            true
+        }
+        underTest =
+            DeviceSettingRepositoryImpl(
+                context,
+                bluetoothAdapter,
+                testScope.backgroundScope,
+                testScope.testScheduler,
+            )
+    }
+
+    @After
+    fun clean() {
+        DeviceSettingServiceConnection.services.clear()
+    }
+
+    @Test
+    fun getDeviceSettingsConfig_withMetadata_success() {
+        testScope.runTest {
+            `when`(configService.getDeviceSettingsConfig(any())).thenReturn(DEVICE_SETTING_CONFIG)
+
+            val config = underTest.getDeviceSettingsConfig(cachedDevice)
+
+            assertThat(config).isSameInstanceAs(DEVICE_SETTING_CONFIG)
+        }
+    }
+
+    @Test
+    fun getDeviceSettingsConfig_waitMetadataChange_success() {
+        testScope.runTest {
+            `when`(configService.getDeviceSettingsConfig(any())).thenReturn(DEVICE_SETTING_CONFIG)
+            `when`(
+                    bluetoothDevice.getMetadata(
+                        DeviceSettingServiceConnection.METADATA_FAST_PAIR_CUSTOMIZED_FIELDS
+                    )
+                )
+                .thenReturn("".toByteArray())
+
+            var config: DeviceSettingsConfig? = null
+            val job = launch { config = underTest.getDeviceSettingsConfig(cachedDevice) }
+            delay(1000)
+            verify(bluetoothAdapter)
+                .addOnMetadataChangedListener(
+                    eq(bluetoothDevice),
+                    any(),
+                    metadataChangeCaptor.capture()
+                )
+            metadataChangeCaptor.value.onMetadataChanged(
+                bluetoothDevice,
+                DeviceSettingServiceConnection.METADATA_FAST_PAIR_CUSTOMIZED_FIELDS,
+                BLUETOOTH_DEVICE_METADATA.toByteArray(),
+            )
+            `when`(
+                    bluetoothDevice.getMetadata(
+                        DeviceSettingServiceConnection.METADATA_FAST_PAIR_CUSTOMIZED_FIELDS
+                    )
+                )
+                .thenReturn(BLUETOOTH_DEVICE_METADATA.toByteArray())
+
+            job.join()
+            assertThat(config).isSameInstanceAs(DEVICE_SETTING_CONFIG)
+        }
+    }
+
+    @Test
+    fun getDeviceSettingsConfig_bindingServiceFail_returnNull() {
+        testScope.runTest {
+            `when`(configService.getDeviceSettingsConfig(any())).thenReturn(DEVICE_SETTING_CONFIG)
+            doReturn(false).`when`(context).bindService(any(), any(), anyInt())
+
+            val config = underTest.getDeviceSettingsConfig(cachedDevice)
+
+            assertThat(config).isNull()
+        }
+    }
+
+    @Test
+    fun getDeviceSettingList_success() {
+        testScope.runTest {
+            `when`(configService.getDeviceSettingsConfig(any())).thenReturn(DEVICE_SETTING_CONFIG)
+            `when`(settingProviderService1.registerDeviceSettingsListener(any(), any())).then {
+                input ->
+                input
+                    .getArgument<IDeviceSettingsListener>(1)
+                    .onDeviceSettingsChanged(listOf(DEVICE_SETTING_1))
+            }
+            `when`(settingProviderService2.registerDeviceSettingsListener(any(), any())).then {
+                input ->
+                input
+                    .getArgument<IDeviceSettingsListener>(1)
+                    .onDeviceSettingsChanged(listOf(DEVICE_SETTING_2))
+            }
+            var settings: List<DeviceSetting>? = null
+
+            underTest
+                .getDeviceSettingList(cachedDevice)
+                .onEach { settings = it }
+                .launchIn(backgroundScope)
+            runCurrent()
+
+            assertThat(settings?.map { it.settingId })
+                .containsExactly(
+                    DeviceSettingId.DEVICE_SETTING_ID_HEADER,
+                    DeviceSettingId.DEVICE_SETTING_ID_ANC
+                )
+            assertThat(settings?.map { (it.preference as ActionSwitchPreference).title })
+                .containsExactly(
+                    "title1",
+                    "title2",
+                )
+        }
+    }
+
+    @Test
+    fun getDeviceSetting_oneServiceFailed_returnPartialResult() {
+        testScope.runTest {
+            `when`(configService.getDeviceSettingsConfig(any())).thenReturn(DEVICE_SETTING_CONFIG)
+            `when`(settingProviderService1.registerDeviceSettingsListener(any(), any())).then {
+                input ->
+                input
+                    .getArgument<IDeviceSettingsListener>(1)
+                    .onDeviceSettingsChanged(listOf(DEVICE_SETTING_1))
+            }
+            var settings: List<DeviceSetting>? = null
+
+            underTest
+                .getDeviceSettingList(cachedDevice)
+                .onEach { settings = it }
+                .launchIn(backgroundScope)
+            runCurrent()
+
+            assertThat(settings?.map { it.settingId })
+                .containsExactly(
+                    DeviceSettingId.DEVICE_SETTING_ID_HEADER,
+                )
+            assertThat(settings?.map { (it.preference as ActionSwitchPreference).title })
+                .containsExactly(
+                    "title1",
+                )
+        }
+    }
+
+    @Test
+    fun getDeviceSetting_success() {
+        testScope.runTest {
+            `when`(configService.getDeviceSettingsConfig(any())).thenReturn(DEVICE_SETTING_CONFIG)
+            `when`(settingProviderService1.registerDeviceSettingsListener(any(), any())).then {
+                input ->
+                input
+                    .getArgument<IDeviceSettingsListener>(1)
+                    .onDeviceSettingsChanged(listOf(DEVICE_SETTING_1))
+            }
+            var setting: DeviceSetting? = null
+
+            underTest
+                .getDeviceSetting(cachedDevice, DeviceSettingId.DEVICE_SETTING_ID_HEADER)
+                .onEach { setting = it }
+                .launchIn(backgroundScope)
+            runCurrent()
+
+            assertThat(setting?.settingId).isEqualTo(DeviceSettingId.DEVICE_SETTING_ID_HEADER)
+            assertThat((setting?.preference as ActionSwitchPreference).title).isEqualTo("title1")
+        }
+    }
+
+    @Test
+    fun updateDeviceSetting_success() {
+        testScope.runTest {
+            `when`(configService.getDeviceSettingsConfig(any())).thenReturn(DEVICE_SETTING_CONFIG)
+            `when`(settingProviderService1.registerDeviceSettingsListener(any(), any())).then {
+                input ->
+                input
+                    .getArgument<IDeviceSettingsListener>(1)
+                    .onDeviceSettingsChanged(listOf(DEVICE_SETTING_1))
+            }
+
+            underTest.updateDeviceSettingState(
+                cachedDevice,
+                DeviceSettingId.DEVICE_SETTING_ID_HEADER,
+                ActionSwitchPreferenceState.Builder().build()
+            )
+            runCurrent()
+
+            verify(settingProviderService1)
+                .updateDeviceSettings(
+                    DEVICE_INFO,
+                    DeviceSettingState.Builder()
+                        .setSettingId(DeviceSettingId.DEVICE_SETTING_ID_HEADER)
+                        .setPreferenceState(ActionSwitchPreferenceState.Builder().build())
+                        .build()
+                )
+        }
+    }
+
+    private companion object {
+        const val BLUETOOTH_ADDRESS = "12:34:56:78"
+        const val CONFIG_SERVICE_PACKAGE_NAME = "com.android.fake.configservice"
+        const val CONFIG_SERVICE_CLASS_NAME = "com.android.fake.configservice.Service"
+        const val CONFIG_SERVICE_INTENT_ACTION = "com.android.fake.configservice.BIND"
+        const val SETTING_PROVIDER_SERVICE_PACKAGE_NAME_1 =
+            "com.android.fake.settingproviderservice1"
+        const val SETTING_PROVIDER_SERVICE_CLASS_NAME_1 =
+            "com.android.fake.settingproviderservice1.Service"
+        const val SETTING_PROVIDER_SERVICE_INTENT_ACTION_1 =
+            "com.android.fake.settingproviderservice1.BIND"
+        const val SETTING_PROVIDER_SERVICE_PACKAGE_NAME_2 =
+            "com.android.fake.settingproviderservice2"
+        const val SETTING_PROVIDER_SERVICE_CLASS_NAME_2 =
+            "com.android.fake.settingproviderservice2.Service"
+        const val SETTING_PROVIDER_SERVICE_INTENT_ACTION_2 =
+            "com.android.fake.settingproviderservice2.BIND"
+        const val BLUETOOTH_DEVICE_METADATA =
+            "<DEVICE_SETTINGS_CONFIG_PACKAGE_NAME>" +
+                CONFIG_SERVICE_PACKAGE_NAME +
+                "</DEVICE_SETTINGS_CONFIG_PACKAGE_NAME>" +
+                "<DEVICE_SETTINGS_CONFIG_CLASS>" +
+                CONFIG_SERVICE_CLASS_NAME +
+                "</DEVICE_SETTINGS_CONFIG_CLASS>" +
+                "<DEVICE_SETTINGS_CONFIG_ACTION>" +
+                CONFIG_SERVICE_INTENT_ACTION +
+                "</DEVICE_SETTINGS_CONFIG_ACTION>"
+        val DEVICE_INFO = DeviceInfo.Builder().setBluetoothAddress(BLUETOOTH_ADDRESS).build()
+
+        val DEVICE_SETTING_ITEM_1 =
+            DeviceSettingItem(
+                DeviceSettingId.DEVICE_SETTING_ID_HEADER,
+                SETTING_PROVIDER_SERVICE_PACKAGE_NAME_1,
+                SETTING_PROVIDER_SERVICE_CLASS_NAME_1,
+                SETTING_PROVIDER_SERVICE_INTENT_ACTION_1
+            )
+        val DEVICE_SETTING_ITEM_2 =
+            DeviceSettingItem(
+                DeviceSettingId.DEVICE_SETTING_ID_ANC,
+                SETTING_PROVIDER_SERVICE_PACKAGE_NAME_2,
+                SETTING_PROVIDER_SERVICE_CLASS_NAME_2,
+                SETTING_PROVIDER_SERVICE_INTENT_ACTION_2
+            )
+        val DEVICE_SETTING_1 =
+            DeviceSetting.Builder()
+                .setSettingId(DeviceSettingId.DEVICE_SETTING_ID_HEADER)
+                .setPreference(
+                    ActionSwitchPreference.Builder()
+                        .setTitle("title1")
+                        .setHasSwitch(true)
+                        .setAllowedChangingState(true)
+                        .build()
+                )
+                .build()
+        val DEVICE_SETTING_2 =
+            DeviceSetting.Builder()
+                .setSettingId(DeviceSettingId.DEVICE_SETTING_ID_ANC)
+                .setPreference(
+                    ActionSwitchPreference.Builder()
+                        .setTitle("title2")
+                        .setHasSwitch(true)
+                        .setAllowedChangingState(true)
+                        .build()
+                )
+                .build()
+        val DEVICE_SETTING_CONFIG =
+            DeviceSettingsConfig(
+                listOf(DEVICE_SETTING_ITEM_1),
+                listOf(DEVICE_SETTING_ITEM_2),
+                "footer"
+            )
+    }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/BatteryUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/BatteryUtilsTest.java
index 465798c..4aec0d6 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/BatteryUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/BatteryUtilsTest.java
@@ -127,6 +127,25 @@
         assertThat(BatteryUtils.isPrivateProfile(mContext)).isTrue();
     }
 
+    @Test
+    public void isAdditionalProfile_defaultValue_returnFalse() {
+        assertThat(BatteryUtils.isAdditionalProfile(mContext)).isFalse();
+    }
+
+    @Test
+    public void isAdditionalProfile_workProfileMode_returnTrue() {
+        doReturn(true).when(mUserManager).isManagedProfile();
+
+        assertThat(BatteryUtils.isAdditionalProfile(mContext)).isTrue();
+    }
+
+    @Test
+    public void isAdditionalProfile_privateProfileMode_returnTrue() {
+        doReturn(true).when(mUserManager).isPrivateProfile();
+
+        assertThat(BatteryUtils.isAdditionalProfile(mContext)).isTrue();
+    }
+
     private void setTtsPackageName(String defaultTtsPackageName) {
         Settings.Secure.putString(mContext.getContentResolver(),
                 Settings.Secure.TTS_DEFAULT_SYNTH, defaultTtsPackageName);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/modes/ZenModeTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/modes/ZenModeTest.java
index e705f97..651e57c 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/modes/ZenModeTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/modes/ZenModeTest.java
@@ -27,6 +27,7 @@
 import android.net.Uri;
 import android.os.Parcel;
 import android.service.notification.Condition;
+import android.service.notification.SystemZenRules;
 import android.service.notification.ZenModeConfig;
 import android.service.notification.ZenPolicy;
 
@@ -109,6 +110,61 @@
     }
 
     @Test
+    public void isCustomManual_customManualMode() {
+        AutomaticZenRule rule = new AutomaticZenRule.Builder("Mode", Uri.parse("x"))
+                .setPackage(SystemZenRules.PACKAGE_ANDROID)
+                .setType(AutomaticZenRule.TYPE_OTHER)
+                .build();
+        ZenMode mode = new ZenMode("id", rule, zenConfigRuleFor(rule, false));
+
+        assertThat(mode.isCustomManual()).isTrue();
+    }
+
+    @Test
+    public void isCustomManual_scheduleTime_false() {
+        AutomaticZenRule rule = new AutomaticZenRule.Builder("Mode", Uri.parse("x"))
+                .setPackage(SystemZenRules.PACKAGE_ANDROID)
+                .setType(AutomaticZenRule.TYPE_SCHEDULE_TIME)
+                .build();
+        ZenMode mode = new ZenMode("id", rule, zenConfigRuleFor(rule, false));
+
+        assertThat(mode.isCustomManual()).isFalse();
+    }
+
+    @Test
+    public void isCustomManual_scheduleCalendar_false() {
+        AutomaticZenRule rule = new AutomaticZenRule.Builder("Mode", Uri.parse("x"))
+                .setPackage(SystemZenRules.PACKAGE_ANDROID)
+                .setType(AutomaticZenRule.TYPE_SCHEDULE_CALENDAR)
+                .build();
+        ZenMode mode = new ZenMode("id", rule, zenConfigRuleFor(rule, false));
+
+        assertThat(mode.isCustomManual()).isFalse();
+    }
+
+    @Test
+    public void isCustomManual_appProvidedMode_false() {
+        AutomaticZenRule rule = new AutomaticZenRule.Builder("Mode", Uri.parse("x"))
+                .setPackage("com.some.package")
+                .setType(AutomaticZenRule.TYPE_OTHER)
+                .build();
+        ZenMode mode = new ZenMode("id", rule, zenConfigRuleFor(rule, false));
+
+        assertThat(mode.isCustomManual()).isFalse();
+    }
+
+    @Test
+    public void isCustomManual_manualDnd_false() {
+        AutomaticZenRule dndRule = new AutomaticZenRule.Builder("Mode", Uri.parse("x"))
+                .setPackage(SystemZenRules.PACKAGE_ANDROID)
+                .setType(AutomaticZenRule.TYPE_OTHER)
+                .build();
+        ZenMode mode = ZenMode.manualDndMode(dndRule, false);
+
+        assertThat(mode.isCustomManual()).isFalse();
+    }
+
+    @Test
     public void getPolicy_interruptionFilterPriority_returnsZenPolicy() {
         AutomaticZenRule azr = new AutomaticZenRule.Builder("Rule", Uri.EMPTY)
                 .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/satellite/SatelliteDialogUtilsTest.kt b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/satellite/SatelliteDialogUtilsTest.kt
index aeda1ed6..2078b36 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/satellite/SatelliteDialogUtilsTest.kt
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/satellite/SatelliteDialogUtilsTest.kt
@@ -17,11 +17,12 @@
 package com.android.settingslib.satellite
 
 import android.content.Context
-import android.content.Intent
-import android.os.OutcomeReceiver
 import android.platform.test.annotations.RequiresFlagsEnabled
 import android.telephony.satellite.SatelliteManager
-import android.telephony.satellite.SatelliteManager.SatelliteException
+import android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_ENABLING_SATELLITE
+import android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_OFF
+import android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_MODEM_ERROR
+import android.telephony.satellite.SatelliteModemStateCallback
 import android.util.AndroidRuntimeException
 import androidx.test.core.app.ApplicationProvider
 import com.android.internal.telephony.flags.Flags
@@ -67,26 +68,19 @@
     @Test
     @RequiresFlagsEnabled(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
     fun mayStartSatelliteWarningDialog_satelliteIsOn_showWarningDialog() = runBlocking {
-        `when`(
-                satelliteManager.requestIsEnabled(
-                        any(), any<OutcomeReceiver<Boolean, SatelliteManager.SatelliteException>>()
-                )
-        )
+        `when`(satelliteManager.registerForModemStateChanged(any(), any()))
                 .thenAnswer { invocation ->
-                    val receiver = invocation
-                            .getArgument<
-                                    OutcomeReceiver<Boolean, SatelliteManager.SatelliteException>>(
-                                    1
-                            )
-                    receiver.onResult(true)
+                    val callback = invocation
+                            .getArgument<SatelliteModemStateCallback>(1)
+                    callback.onSatelliteModemStateChanged(SATELLITE_MODEM_STATE_ENABLING_SATELLITE)
                     null
                 }
 
         try {
             SatelliteDialogUtils.mayStartSatelliteWarningDialog(
                     context, coroutineScope, TYPE_IS_WIFI, allowClick = {
-                        assertTrue(it)
-                })
+                assertTrue(it)
+            })
         } catch (e: AndroidRuntimeException) {
             // Catch exception of starting activity .
         }
@@ -95,68 +89,61 @@
     @Test
     @RequiresFlagsEnabled(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
     fun mayStartSatelliteWarningDialog_satelliteIsOff_notShowWarningDialog() = runBlocking {
-        `when`(
-                satelliteManager.requestIsEnabled(
-                        any(), any<OutcomeReceiver<Boolean, SatelliteManager.SatelliteException>>()
-                )
-        )
+        `when`(satelliteManager.registerForModemStateChanged(any(), any()))
                 .thenAnswer { invocation ->
-                    val receiver = invocation
-                            .getArgument<
-                                    OutcomeReceiver<Boolean, SatelliteManager.SatelliteException>>(
-                                    1
-                            )
-                    receiver.onResult(false)
+                    val callback = invocation
+                            .getArgument<SatelliteModemStateCallback>(1)
+                    callback.onSatelliteModemStateChanged(SATELLITE_MODEM_STATE_OFF)
                     null
                 }
 
 
         SatelliteDialogUtils.mayStartSatelliteWarningDialog(
-            context, coroutineScope, TYPE_IS_WIFI, allowClick = {
-                assertFalse(it)
-            })
+                context, coroutineScope, TYPE_IS_WIFI, allowClick = {
+            assertFalse(it)
+        })
 
-        verify(context, Times(0)).startActivity(any<Intent>())
+        verify(context, Times(0)).startActivity(any())
     }
 
     @Test
     @RequiresFlagsEnabled(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
     fun mayStartSatelliteWarningDialog_noSatelliteManager_notShowWarningDialog() = runBlocking {
-        `when`(context.getSystemService(SatelliteManager::class.java))
-                .thenReturn(null)
+        `when`(context.getSystemService(SatelliteManager::class.java)).thenReturn(null)
 
         SatelliteDialogUtils.mayStartSatelliteWarningDialog(
-            context, coroutineScope, TYPE_IS_WIFI, allowClick = {
-                assertFalse(it)
-            })
+                context, coroutineScope, TYPE_IS_WIFI, allowClick = {
+            assertFalse(it)
+        })
 
-        verify(context, Times(0)).startActivity(any<Intent>())
+        verify(context, Times(0)).startActivity(any())
     }
 
     @Test
     @RequiresFlagsEnabled(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
     fun mayStartSatelliteWarningDialog_satelliteErrorResult_notShowWarningDialog() = runBlocking {
-        `when`(
-                satelliteManager.requestIsEnabled(
-                        any(), any<OutcomeReceiver<Boolean, SatelliteManager.SatelliteException>>()
-                )
-        )
-                .thenAnswer { invocation ->
-                    val receiver = invocation
-                            .getArgument<
-                                    OutcomeReceiver<Boolean, SatelliteManager.SatelliteException>>(
-                                    1
-                            )
-                    receiver.onError(SatelliteException(SatelliteManager.SATELLITE_RESULT_ERROR))
-                    null
-                }
-
+        `when`(satelliteManager.registerForModemStateChanged(any(), any()))
+                .thenReturn(SATELLITE_RESULT_MODEM_ERROR)
 
         SatelliteDialogUtils.mayStartSatelliteWarningDialog(
-            context, coroutineScope, TYPE_IS_WIFI, allowClick = {
-                assertFalse(it)
-            })
+                context, coroutineScope, TYPE_IS_WIFI, allowClick = {
+            assertFalse(it)
+        })
 
-        verify(context, Times(0)).startActivity(any<Intent>())
+        verify(context, Times(0)).startActivity(any())
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    fun mayStartSatelliteWarningDialog_phoneCrash_notShowWarningDialog() = runBlocking {
+        `when`(satelliteManager.registerForModemStateChanged(any(), any()))
+                .thenThrow(IllegalStateException("Telephony is null!!!"))
+
+        SatelliteDialogUtils.mayStartSatelliteWarningDialog(
+                context, coroutineScope, TYPE_IS_WIFI, allowClick = {
+            assertFalse(it)
+        })
+
+        verify(context, Times(0)).startActivity(any())
     }
 }
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index 5f23651..40a8199 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -253,6 +253,7 @@
         Settings.Secure.CUSTOM_BUGREPORT_HANDLER_APP,
         Settings.Secure.CUSTOM_BUGREPORT_HANDLER_USER,
         Settings.Secure.CONTEXTUAL_SCREEN_TIMEOUT_ENABLED,
+        Settings.Secure.HINGE_ANGLE_LIDEVENT_ENABLED,
         Settings.Secure.LOCK_SCREEN_WEATHER_ENABLED,
         Settings.Secure.HEARING_AID_RINGTONE_ROUTING,
         Settings.Secure.HEARING_AID_CALL_ROUTING,
@@ -282,5 +283,6 @@
         Settings.Secure.ON_DEVICE_INFERENCE_UNBIND_TIMEOUT_MS,
         Settings.Secure.ON_DEVICE_INTELLIGENCE_IDLE_TIMEOUT_MS,
         Settings.Secure.MANDATORY_BIOMETRICS,
+        Settings.Secure.MANDATORY_BIOMETRICS_REQUIREMENTS_SATISFIED,
     };
 }
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index c8da8af..3b9c683 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -406,6 +406,7 @@
         VALIDATORS.put(Secure.CUSTOM_BUGREPORT_HANDLER_USER, ANY_INTEGER_VALIDATOR);
         VALIDATORS.put(Secure.LOCK_SCREEN_WEATHER_ENABLED, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.CONTEXTUAL_SCREEN_TIMEOUT_ENABLED, BOOLEAN_VALIDATOR);
+        VALIDATORS.put(Secure.HINGE_ANGLE_LIDEVENT_ENABLED, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.HEARING_AID_RINGTONE_ROUTING,
                 new DiscreteValueValidator(new String[] {"0", "1", "2"}));
         VALIDATORS.put(Secure.HEARING_AID_CALL_ROUTING,
@@ -441,5 +442,7 @@
         VALIDATORS.put(Secure.ON_DEVICE_INTELLIGENCE_IDLE_TIMEOUT_MS, NONE_NEGATIVE_LONG_VALIDATOR);
         VALIDATORS.put(Secure.ACCESSIBILITY_MOUSE_KEYS_ENABLED, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.MANDATORY_BIOMETRICS, new InclusiveIntegerRangeValidator(0, 1));
+        VALIDATORS.put(Secure.MANDATORY_BIOMETRICS_REQUIREMENTS_SATISFIED,
+                new InclusiveIntegerRangeValidator(0, 1));
     }
 }
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
index 7b927d7..2823277 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
@@ -200,6 +200,7 @@
         VALIDATORS.put(System.POINTER_LOCATION, BOOLEAN_VALIDATOR);
         VALIDATORS.put(System.SHOW_TOUCHES, BOOLEAN_VALIDATOR);
         VALIDATORS.put(System.SHOW_KEY_PRESSES, BOOLEAN_VALIDATOR);
+        VALIDATORS.put(System.TOUCHPAD_VISUALIZER, BOOLEAN_VALIDATOR);
         VALIDATORS.put(System.SHOW_ROTARY_INPUT, BOOLEAN_VALIDATOR);
         VALIDATORS.put(System.WINDOW_ORIENTATION_LISTENER_LOG, BOOLEAN_VALIDATOR);
         VALIDATORS.put(System.LOCKSCREEN_SOUNDS_ENABLED, BOOLEAN_VALIDATOR);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index cd37ad1..3c24f5c 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -2831,6 +2831,9 @@
                 Settings.System.SHOW_KEY_PRESSES,
                 SystemSettingsProto.DevOptions.SHOW_KEY_PRESSES);
         dumpSetting(s, p,
+                Settings.System.TOUCHPAD_VISUALIZER,
+                SystemSettingsProto.DevOptions.TOUCHPAD_VISUALIZER);
+        dumpSetting(s, p,
                 Settings.System.POINTER_LOCATION,
                 SystemSettingsProto.DevOptions.POINTER_LOCATION);
         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 e8ef620..ba59ce8 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -3264,6 +3264,24 @@
 
             if (forceNotify || success) {
                 notifyForSettingsChange(key, name);
+
+                // If this is an aconfig flag, it will be written as a staged flag.
+                // Notify that its staged flag value will be updated.
+                if (Flags.notifyIndividualAconfigSyspropChanged() && type == SETTINGS_TYPE_CONFIG) {
+                    int slashIndex = name.indexOf('/');
+                    boolean validSlashIndex = slashIndex != -1
+                            && slashIndex != 0
+                            && slashIndex != name.length();
+                    if (validSlashIndex) {
+                        String namespace = name.substring(0, slashIndex);
+                        String flagName = name.substring(slashIndex + 1);
+                        if (settingsState.getAconfigDefaultFlags().containsKey(flagName)) {
+                            String stagedName = "staged/" + namespace + "*" + flagName;
+                            notifyForSettingsChange(key, stagedName);
+                        }
+                    }
+                }
+
                 if (wasUnsetNonPredefinedSetting) {
                     // Increment the generation number for all non-predefined, unset settings,
                     // because a new non-predefined setting has been inserted
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/device_config_service.aconfig b/packages/SettingsProvider/src/com/android/providers/settings/device_config_service.aconfig
index 4f5955b..f53dec6 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/device_config_service.aconfig
+++ b/packages/SettingsProvider/src/com/android/providers/settings/device_config_service.aconfig
@@ -52,3 +52,14 @@
         purpose: PURPOSE_BUGFIX
     }
 }
+
+flag {
+    name: "notify_individual_aconfig_sysprop_changed"
+    namespace: "core_experiments_team_internal"
+    description: "When enabled, propagate individual aconfig sys props on flag stage."
+    bug: "331963764"
+    is_fixed_read_only: true
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 411decd..8c96484 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -918,6 +918,7 @@
                         Settings.System.SHOW_GTALK_SERVICE_STATUS, // candidate for backup?
                         Settings.System.SHOW_TOUCHES,
                         Settings.System.SHOW_KEY_PRESSES,
+                        Settings.System.TOUCHPAD_VISUALIZER,
                         Settings.System.SHOW_ROTARY_INPUT,
                         Settings.System.SIP_ADDRESS_ONLY, // value, not a setting
                         Settings.System.SIP_ALWAYS, // value, not a setting
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 04d30ed..0b5187c 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -65,6 +65,7 @@
     <uses-permission android:name="com.android.voicemail.permission.ADD_VOICEMAIL" />
     <uses-permission android:name="android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS" />
     <uses-permission android:name="android.permission.GET_PROCESS_STATE_AND_OOM_SCORE" />
+    <uses-permission android:name="android.permission.READ_DROPBOX_DATA" />
     <uses-permission android:name="android.permission.READ_LOGS" />
     <uses-permission android:name="android.permission.BRIGHTNESS_SLIDER_USAGE" />
     <uses-permission android:name="android.permission.ACCESS_AMBIENT_LIGHT_STATS" />
diff --git a/packages/SoundPicker/src/com/android/soundpicker/RingtonePickerActivity.java b/packages/SoundPicker/src/com/android/soundpicker/RingtonePickerActivity.java
index 0dd9275..8c3cce4 100644
--- a/packages/SoundPicker/src/com/android/soundpicker/RingtonePickerActivity.java
+++ b/packages/SoundPicker/src/com/android/soundpicker/RingtonePickerActivity.java
@@ -255,8 +255,8 @@
                 p.mTitle = getString(com.android.internal.R.string.ringtone_picker_title);
             }
         } else {
-            // Make sure intents don't inject HTML elements.
-            p.mTitle = Html.escapeHtml(p.mTitle.toString());
+            // Make sure intents don't inject spannable elements.
+            p.mTitle = p.mTitle.toString();
         }
 
         setupAlert();
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 4e01a71..c2e8c37 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -258,6 +258,7 @@
         "tests/src/**/systemui/statusbar/policy/LocationControllerImplTest.java",
         "tests/src/**/systemui/statusbar/policy/RemoteInputViewTest.java",
         "tests/src/**/systemui/statusbar/policy/SmartReplyViewTest.java",
+        "tests/src/**/systemui/statusbar/policy/ui/dialog/ModesDialogDelegateTest.kt",
         "tests/src/**/systemui/statusbar/StatusBarStateControllerImplTest.kt",
         "tests/src/**/systemui/theme/ThemeOverlayApplierTest.java",
         "tests/src/**/systemui/touch/TouchInsetManagerTest.java",
@@ -570,9 +571,12 @@
         "jsr305",
         "jsr330",
         "lottie",
+        "lottie_compose",
         "LowLightDreamLib",
         "TraceurCommon",
+        "Traceur-res",
         "//frameworks/libs/systemui:motion_tool_lib",
+        "//frameworks/libs/systemui:contextualeducationlib",
         "notification_flags_lib",
         "PlatformComposeCore",
         "PlatformComposeSceneTransitionLayout",
@@ -727,11 +731,13 @@
         "truth",
         "monet",
         "libmonet",
+        "lottie_compose",
         "dagger2",
         "jsr330",
         "WindowManager-Shell",
         "LowLightDreamLib",
         "//frameworks/libs/systemui:motion_tool_lib",
+        "//frameworks/libs/systemui:contextualeducationlib",
         "androidx.core_core-animation-testing",
         "androidx.compose.ui_ui",
         "flag-junit",
@@ -747,6 +753,7 @@
         "androidx.compose.animation_animation-graphics",
         "androidx.lifecycle_lifecycle-viewmodel-compose",
         "TraceurCommon",
+        "Traceur-res",
     ],
 }
 
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 666d939..9f3c2bf 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -482,7 +482,7 @@
             android:exported="true"
             android:theme="@style/Theme.AppCompat.NoActionBar">
             <intent-filter>
-                <action android:name="com.android.systemui.action.TOUCHPAD_TUTORIAL"/>
+                <action android:name="com.android.systemui.action.TOUCHPAD_KEYBOARD_TUTORIAL"/>
                 <category android:name="android.intent.category.DEFAULT"/>
             </intent-filter>
         </activity>
@@ -1062,6 +1062,14 @@
             </intent-filter>
         </receiver>
 
+        <receiver android:name=".accessibility.extradim.ExtraDimDialogReceiver"
+            android:singleUser="true"
+            android:exported="false">
+            <intent-filter android:priority="1">
+                <action android:name="com.android.systemui.action.LAUNCH_REMOVE_EXTRA_DIM_DIALOG" />
+            </intent-filter>
+        </receiver>
+
         <activity android:name=".logcat.LogAccessDialogActivity"
                   android:theme="@android:style/Theme.Translucent.NoTitleBar"
                   android:excludeFromRecents="true"
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/aconfig/accessibility.aconfig b/packages/SystemUI/accessibility/accessibilitymenu/aconfig/accessibility.aconfig
index ba84287..c1e43c9 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/aconfig/accessibility.aconfig
+++ b/packages/SystemUI/accessibility/accessibilitymenu/aconfig/accessibility.aconfig
@@ -19,3 +19,13 @@
         purpose: PURPOSE_BUGFIX
     }
 }
+
+flag {
+    name: "action_bar_wrap_content"
+    namespace: "accessibility"
+    description: "Applies WRAP_CONTENT to the action bar in A11yMenu settings to better fit large fonts"
+    bug: "347911378"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/activity/A11yMenuSettingsActivity.java b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/activity/A11yMenuSettingsActivity.java
index ab8f97a..c71ef83 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/activity/A11yMenuSettingsActivity.java
+++ b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/activity/A11yMenuSettingsActivity.java
@@ -26,6 +26,7 @@
 import android.provider.Browser;
 import android.provider.Settings;
 import android.view.View;
+import android.view.ViewGroup;
 import android.widget.TextView;
 import android.window.OnBackInvokedCallback;
 
@@ -35,6 +36,7 @@
 import androidx.preference.PreferenceFragmentCompat;
 import androidx.preference.PreferenceManager;
 
+import com.android.systemui.accessibility.accessibilitymenu.Flags;
 import com.android.systemui.accessibility.accessibilitymenu.R;
 
 /**
@@ -60,6 +62,18 @@
         ((TextView) findViewById(R.id.action_bar_title)).setText(
                 getResources().getString(R.string.accessibility_menu_settings_name)
         );
+        if (Flags.actionBarWrapContent()) {
+            setHeightWrapContent(findViewById(com.android.internal.R.id.action_bar));
+            setHeightWrapContent(findViewById(com.android.internal.R.id.action_bar_container));
+        }
+    }
+
+    private void setHeightWrapContent(View view) {
+        if (view != null) {
+            ViewGroup.LayoutParams params = view.getLayoutParams();
+            params.height = ViewGroup.LayoutParams.WRAP_CONTENT;
+            view.setLayoutParams(params);
+        }
     }
 
     @Override
diff --git a/packages/SystemUI/aconfig/accessibility.aconfig b/packages/SystemUI/aconfig/accessibility.aconfig
index 0861454..8860452 100644
--- a/packages/SystemUI/aconfig/accessibility.aconfig
+++ b/packages/SystemUI/aconfig/accessibility.aconfig
@@ -90,6 +90,16 @@
 }
 
 flag {
+    name: "update_corner_radius_on_display_changed"
+    namespace: "accessibility"
+    description: "Updates the corner radius to the magnification fullscreen border when the display changes."
+    bug: "335113174"
+    metadata {
+      purpose: PURPOSE_BUGFIX
+    }
+}
+
+flag {
     name: "hearing_devices_dialog_related_tools"
     namespace: "accessibility"
     description: "Shows the related tools for hearing devices dialog."
diff --git a/packages/SystemUI/aconfig/biometrics_framework.aconfig b/packages/SystemUI/aconfig/biometrics_framework.aconfig
index bd1a442..e81d5d5 100644
--- a/packages/SystemUI/aconfig/biometrics_framework.aconfig
+++ b/packages/SystemUI/aconfig/biometrics_framework.aconfig
@@ -4,13 +4,6 @@
 # NOTE: Keep alphabetized to help limit merge conflicts from multiple simultaneous editors.
 
 flag {
-    name: "bp_talkback"
-    namespace: "biometrics_framework"
-    description: "Adds talkback directional guidance when using UDFPS with biometric prompt"
-    bug: "310044658"
-}
-
-flag {
     name: "constraint_bp"
     namespace: "biometrics_framework"
     description: "Refactors Biometric Prompt to use a ConstraintLayout"
diff --git a/packages/SystemUI/aconfig/communal.aconfig b/packages/SystemUI/aconfig/communal.aconfig
index afcd8a9..f2b5efa 100644
--- a/packages/SystemUI/aconfig/communal.aconfig
+++ b/packages/SystemUI/aconfig/communal.aconfig
@@ -8,12 +8,3 @@
     bug: "304584416"
 }
 
-flag {
-    name: "enable_widget_picker_size_filter"
-    namespace: "communal"
-    description: "Enables passing a size filter to the widget picker"
-    bug: "345482907"
-   metadata {
-        purpose: PURPOSE_BUGFIX
-   }
-}
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 02e0423..f2c4a7f 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -392,6 +392,24 @@
 }
 
 flag {
+    name: "status_bar_use_repos_for_call_chip"
+    namespace: "systemui"
+    description: "Use repositories as the source of truth for call notifications shown as a chip in"
+        "the status bar"
+    bug: "328584859"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
+
+flag {
+    name: "status_bar_call_chip_notification_icon"
+    namespace: "systemui"
+    description: "Use the small icon set on the notification for the status bar call chip"
+    bug: "354930838"
+}
+
+flag {
     name: "compose_bouncer"
     namespace: "systemui"
     description: "Use the new compose bouncer in SystemUI"
@@ -571,16 +589,6 @@
 }
 
 flag {
-    name: "screenshot_private_profile_accessibility_announcement_fix"
-    namespace: "systemui"
-    description: "Modified a11y announcement for private space screenshots"
-    bug: "326941376"
-    metadata {
-        purpose: PURPOSE_BUGFIX
-    }
-}
-
-flag {
     name: "screenshot_private_profile_behavior_fix"
     namespace: "systemui"
     description: "Private profile support for screenshots"
@@ -601,6 +609,13 @@
 }
 
 flag {
+    name: "screenshot_ui_controller_refactor"
+    namespace: "systemui"
+    description: "Simplify and refactor ScreenshotController"
+    bug: "354711957"
+}
+
+flag {
    name: "run_fingerprint_detect_on_dismissible_keyguard"
    namespace: "systemui"
    description: "Run fingerprint detect instead of authenticate if the keyguard is dismissible."
@@ -998,6 +1013,16 @@
 }
 
 flag {
+  name: "communal_timer_flicker_fix"
+  namespace: "systemui"
+  description: "fixes timers on the hub flickering when pausing"
+  bug: "353801573"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
+
+flag {
   name: "app_clips_backlinks"
   namespace: "systemui"
   description: "Enables Backlinks improvement feature in App Clips"
@@ -1206,6 +1231,16 @@
 }
 
 flag {
+  name: "hubmode_fullscreen_vertical_swipe"
+  namespace: "systemui"
+  description: "Enables fullscreen vertical swiping in hub mode to bring up and down the bouncer and shade"
+  bug: "340177049"
+  metadata {
+      purpose: PURPOSE_BUGFIX
+  }
+}
+
+flag {
    namespace: "systemui"
    name: "remove_update_listener_in_qs_icon_view_impl"
    description: "Remove update listeners in QsIconViewImpl class to avoid memory leak."
@@ -1236,6 +1271,16 @@
 }
 
 flag {
+   name: "use_transitions_for_keyguard_occluded"
+   namespace: "systemui"
+   description: "Use Keyguard Transitions to set Notification Shade occlusion state"
+   bug: "344716537"
+   metadata {
+        purpose: PURPOSE_BUGFIX
+   }
+}
+
+flag {
    name: "lockscreen_preview_renderer_create_on_main_thread"
    namespace: "systemui"
    description: "Force preview renderer to be created on the main thread"
@@ -1243,4 +1288,13 @@
    metadata {
         purpose: PURPOSE_BUGFIX
    }
-}
\ No newline at end of file
+}
+flag {
+   name: "classic_flags_multi_user"
+   namespace: "systemui"
+   description: "Make the classic feature flag loading multi user aware."
+   bug: "345443431"
+   metadata {
+        purpose: PURPOSE_BUGFIX
+   }
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt
index f73b6cd..9fd30b4 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt
@@ -33,7 +33,7 @@
 import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.scene.ui.composable.ComposableScene
 import javax.inject.Inject
-import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.Flow
 
 object Bouncer {
     object Elements {
@@ -56,7 +56,7 @@
 ) : ComposableScene {
     override val key = Scenes.Bouncer
 
-    override val destinationScenes: StateFlow<Map<UserAction, UserActionResult>> =
+    override val destinationScenes: Flow<Map<UserAction, UserActionResult>> =
         viewModel.destinationScenes
 
     @Composable
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
index 35db9e0..d4bad23 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
@@ -7,6 +7,7 @@
 import androidx.compose.animation.core.rememberInfiniteTransition
 import androidx.compose.animation.core.tween
 import androidx.compose.foundation.background
+import androidx.compose.foundation.focusable
 import androidx.compose.foundation.gestures.Orientation
 import androidx.compose.foundation.isSystemInDarkTheme
 import androidx.compose.foundation.layout.Box
@@ -25,13 +26,17 @@
 import androidx.compose.ui.graphics.Brush
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.semantics.clearAndSetSemantics
+import androidx.compose.ui.semantics.contentDescription
+import androidx.compose.ui.semantics.semantics
 import androidx.compose.ui.unit.dp
 import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.compose.animation.scene.Back
+import com.android.compose.animation.scene.ContentKey
 import com.android.compose.animation.scene.Edge
 import com.android.compose.animation.scene.ElementKey
 import com.android.compose.animation.scene.ElementMatcher
-import com.android.compose.animation.scene.LowestZIndexScenePicker
+import com.android.compose.animation.scene.LowestZIndexContentPicker
 import com.android.compose.animation.scene.MutableSceneTransitionLayoutState
 import com.android.compose.animation.scene.SceneKey
 import com.android.compose.animation.scene.SceneScope
@@ -41,6 +46,7 @@
 import com.android.compose.animation.scene.observableTransitionState
 import com.android.compose.animation.scene.transitions
 import com.android.compose.theme.LocalAndroidColorScheme
+import com.android.internal.R.attr.focusable
 import com.android.systemui.Flags.glanceableHubBackGesture
 import com.android.systemui.communal.shared.model.CommunalBackgroundType
 import com.android.systemui.communal.shared.model.CommunalScenes
@@ -56,7 +62,7 @@
 
 object Communal {
     object Elements {
-        val Scrim = ElementKey("Scrim", scenePicker = LowestZIndexScenePicker)
+        val Scrim = ElementKey("Scrim", contentPicker = LowestZIndexContentPicker)
         val Grid = ElementKey("CommunalContent")
         val LockIcon = ElementKey("CommunalLockIcon")
         val IndicationArea = ElementKey("CommunalIndicationArea")
@@ -65,7 +71,7 @@
 }
 
 object AllElements : ElementMatcher {
-    override fun matches(key: ElementKey, scene: SceneKey) = true
+    override fun matches(key: ElementKey, content: ContentKey) = true
 }
 
 private object TransitionDuration {
@@ -88,12 +94,12 @@
     }
     to(CommunalScenes.Communal) {
         spec = tween(durationMillis = 1000)
-        translate(Communal.Elements.Grid, Edge.Right)
+        translate(Communal.Elements.Grid, Edge.End)
         timestampRange(startMillis = 167, endMillis = 334) { fade(AllElements) }
     }
     to(CommunalScenes.Blank) {
         spec = tween(durationMillis = 1000)
-        translate(Communal.Elements.Grid, Edge.Right)
+        translate(Communal.Elements.Grid, Edge.End)
         timestampRange(endMillis = 167) {
             fade(Communal.Elements.Grid)
             fade(Communal.Elements.IndicationArea)
@@ -128,7 +134,7 @@
     }
     // Disable horizontal overscroll. If the scene is overscrolled too soon after showing, this
     // can lead to inconsistent KeyguardState changes.
-    overscroll(CommunalScenes.Communal, Orientation.Horizontal) {}
+    overscrollDisabled(CommunalScenes.Communal, Orientation.Horizontal)
 }
 
 /**
@@ -186,9 +192,7 @@
         scene(
             CommunalScenes.Blank,
             userActions =
-                mapOf(
-                    Swipe(SwipeDirection.Left, fromSource = Edge.Right) to CommunalScenes.Communal
-                )
+                mapOf(Swipe(SwipeDirection.Start, fromSource = Edge.End) to CommunalScenes.Communal)
         ) {
             // This scene shows nothing only allowing for transitions to the communal scene.
             Box(modifier = Modifier.fillMaxSize())
@@ -197,11 +201,11 @@
         val userActions =
             if (glanceableHubBackGesture()) {
                 mapOf(
-                    Swipe(SwipeDirection.Right) to CommunalScenes.Blank,
+                    Swipe(SwipeDirection.End) to CommunalScenes.Blank,
                     Back to CommunalScenes.Blank,
                 )
             } else {
-                mapOf(Swipe(SwipeDirection.Right) to CommunalScenes.Blank)
+                mapOf(Swipe(SwipeDirection.End) to CommunalScenes.Blank)
             }
 
         scene(CommunalScenes.Communal, userActions = userActions) {
@@ -209,6 +213,8 @@
                 backgroundType = backgroundType,
                 colors = colors,
                 content = content,
+                viewModel = viewModel,
+                modifier = Modifier.horizontalNestedScrollToScene(),
             )
         }
     }
@@ -224,17 +230,41 @@
     backgroundType: CommunalBackgroundType,
     colors: CommunalColors,
     content: CommunalContent,
+    viewModel: CommunalViewModel,
     modifier: Modifier = Modifier,
 ) {
-    Box(modifier = Modifier.element(Communal.Elements.Scrim).fillMaxSize()) {
+    val isFocusable by viewModel.isFocusable.collectAsStateWithLifecycle(initialValue = false)
+
+    Box(
+        modifier =
+            Modifier.element(Communal.Elements.Scrim)
+                .fillMaxSize()
+                .then(
+                    if (isFocusable) {
+                        Modifier.focusable()
+                    } else {
+                        Modifier.semantics { contentDescription = "" }.clearAndSetSemantics {}
+                    }
+                )
+    ) {
         when (backgroundType) {
             CommunalBackgroundType.STATIC -> DefaultBackground(colors = colors)
             CommunalBackgroundType.STATIC_GRADIENT -> StaticLinearGradient()
             CommunalBackgroundType.ANIMATED -> AnimatedLinearGradient()
             CommunalBackgroundType.NONE -> BackgroundTopScrim()
         }
+
+        with(content) {
+            Content(
+                modifier =
+                    modifier.focusable(isFocusable).semantics {
+                        if (!isFocusable) {
+                            contentDescription = ""
+                        }
+                    }
+            )
+        }
     }
-    with(content) { Content(modifier = modifier) }
 }
 
 /** Default background of the hub, a single color */
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt
index 1ce51af..8c38253 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt
@@ -98,13 +98,7 @@
                         bottom = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Bottom],
                     )
 
-                val bottomAreaPlaceable =
-                    bottomAreaMeasurable.measure(
-                        noMinConstraints.copy(
-                            maxHeight =
-                                (constraints.maxHeight - lockIconBounds.bottom).coerceAtLeast(0)
-                        )
-                    )
+                val bottomAreaPlaceable = bottomAreaMeasurable.measure(noMinConstraints)
 
                 val communalGridPlaceable =
                     communalGridMeasurable.measure(
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
index 1c02d3f..69f1174 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
@@ -42,6 +42,7 @@
 import androidx.compose.foundation.background
 import androidx.compose.foundation.clickable
 import androidx.compose.foundation.focusable
+import androidx.compose.foundation.gestures.awaitFirstDown
 import androidx.compose.foundation.interaction.MutableInteractionSource
 import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Box
@@ -51,6 +52,7 @@
 import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.RowScope
 import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.calculateStartPadding
 import androidx.compose.foundation.layout.fillMaxHeight
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.fillMaxWidth
@@ -67,8 +69,10 @@
 import androidx.compose.foundation.lazy.grid.LazyGridState
 import androidx.compose.foundation.lazy.grid.LazyHorizontalGrid
 import androidx.compose.foundation.lazy.grid.rememberLazyGridState
+import androidx.compose.foundation.rememberScrollState
 import androidx.compose.foundation.selection.selectable
 import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.foundation.verticalScroll
 import androidx.compose.material.icons.Icons
 import androidx.compose.material.icons.filled.Add
 import androidx.compose.material.icons.filled.Check
@@ -90,6 +94,7 @@
 import androidx.compose.material3.Text
 import androidx.compose.material3.rememberModalBottomSheetState
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
 import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.State
 import androidx.compose.runtime.derivedStateOf
@@ -112,6 +117,11 @@
 import androidx.compose.ui.graphics.SolidColor
 import androidx.compose.ui.graphics.drawscope.Stroke
 import androidx.compose.ui.graphics.graphicsLayer
+import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
+import androidx.compose.ui.input.nestedscroll.NestedScrollSource
+import androidx.compose.ui.input.nestedscroll.nestedScroll
+import androidx.compose.ui.input.pointer.changedToUp
+import androidx.compose.ui.input.pointer.changedToUpIgnoreConsumed
 import androidx.compose.ui.input.pointer.pointerInput
 import androidx.compose.ui.layout.LayoutCoordinates
 import androidx.compose.ui.layout.boundsInWindow
@@ -121,6 +131,7 @@
 import androidx.compose.ui.platform.LocalConfiguration
 import androidx.compose.ui.platform.LocalContext
 import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.platform.LocalLayoutDirection
 import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.res.dimensionResource
 import androidx.compose.ui.res.stringResource
@@ -137,8 +148,11 @@
 import androidx.compose.ui.unit.IntSize
 import androidx.compose.ui.unit.LayoutDirection
 import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
 import androidx.compose.ui.unit.times
+import androidx.compose.ui.util.fastAll
 import androidx.compose.ui.viewinterop.AndroidView
+import androidx.compose.ui.viewinterop.NoOpUpdate
 import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import androidx.window.layout.WindowMetricsCalculator
 import com.android.compose.animation.Easings.Emphasized
@@ -146,6 +160,7 @@
 import com.android.compose.theme.LocalAndroidColorScheme
 import com.android.compose.ui.graphics.painter.rememberDrawablePainter
 import com.android.internal.R.dimen.system_app_widget_background_radius
+import com.android.systemui.Flags.communalTimerFlickerFix
 import com.android.systemui.communal.domain.model.CommunalContentModel
 import com.android.systemui.communal.shared.model.CommunalContentSize
 import com.android.systemui.communal.shared.model.CommunalScenes
@@ -156,6 +171,8 @@
 import com.android.systemui.communal.ui.viewmodel.BaseCommunalViewModel
 import com.android.systemui.communal.ui.viewmodel.CommunalEditModeViewModel
 import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
+import com.android.systemui.communal.util.DensityUtils.Companion.adjustedDp
+import com.android.systemui.communal.util.DensityUtils.Companion.scalingAdjustment
 import com.android.systemui.communal.widgets.SmartspaceAppWidgetHostView
 import com.android.systemui.communal.widgets.WidgetConfigurator
 import com.android.systemui.res.R
@@ -200,24 +217,86 @@
 
     ObserveScrollEffect(gridState, viewModel)
 
-    if (!viewModel.isEditMode) {
+    val context = LocalContext.current
+    val windowMetrics = WindowMetricsCalculator.getOrCreate().computeCurrentWindowMetrics(context)
+    val screenWidth = windowMetrics.bounds.width()
+    val layoutDirection = LocalLayoutDirection.current
+
+    if (viewModel.isEditMode) {
+        ObserveNewWidgetAddedEffect(communalContent, gridState, viewModel)
+    } else {
         ScrollOnUpdatedLiveContentEffect(communalContent, gridState)
     }
 
+    val nestedScrollConnection = remember {
+        object : NestedScrollConnection {
+            override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
+                // Begin tracking nested scrolling
+                viewModel.onNestedScrolling()
+                return super.onPreScroll(available, source)
+            }
+        }
+    }
+
     Box(
         modifier =
             modifier
                 .semantics { testTagsAsResourceId = true }
                 .testTag(COMMUNAL_HUB_TEST_TAG)
                 .fillMaxSize()
-                .pointerInput(gridState, contentOffset, contentListState) {
-                    // If not in edit mode, don't allow selecting items.
-                    if (!viewModel.isEditMode) return@pointerInput
-                    observeTaps { offset ->
-                        val adjustedOffset = offset - contentOffset
-                        val index = firstIndexAtOffset(gridState, adjustedOffset)
-                        val key = index?.let { keyAtIndexIfEditable(contentListState.list, index) }
-                        viewModel.setSelectedKey(key)
+                // Observe taps for selecting items
+                .thenIf(viewModel.isEditMode) {
+                    Modifier.pointerInput(
+                        layoutDirection,
+                        gridState,
+                        contentOffset,
+                        contentListState,
+                    ) {
+                        observeTaps { offset ->
+                            // if RTL, flip offset direction from Left side to Right
+                            val adjustedOffset =
+                                Offset(
+                                    if (layoutDirection == LayoutDirection.Rtl)
+                                        screenWidth - offset.x
+                                    else offset.x,
+                                    offset.y
+                                ) - contentOffset
+                            val index = firstIndexAtOffset(gridState, adjustedOffset)
+                            val key =
+                                index?.let { keyAtIndexIfEditable(contentListState.list, index) }
+                            viewModel.setSelectedKey(key)
+                        }
+                    }
+                }
+                // Nested scroll for full screen swipe to get to shade and bouncer
+                .thenIf(!viewModel.isEditMode) {
+                    Modifier.nestedScroll(nestedScrollConnection).pointerInput(viewModel) {
+                        awaitPointerEventScope {
+                            while (true) {
+                                val firstDownEvent = awaitFirstDown(requireUnconsumed = false)
+                                // Reset touch on first event.
+                                viewModel.onResetTouchState()
+
+                                // Process down event in case it's consumed immediately
+                                if (firstDownEvent.isConsumed) {
+                                    viewModel.onHubTouchConsumed()
+                                }
+
+                                do {
+                                    val event = awaitPointerEvent()
+                                    for (change in event.changes) {
+                                        if (change.isConsumed) {
+                                            // Signal touch consumption on any consumed event.
+                                            viewModel.onHubTouchConsumed()
+                                        }
+                                    }
+                                } while (
+                                    !event.changes.fastAll {
+                                        it.changedToUp() || it.changedToUpIgnoreConsumed()
+                                    }
+                                )
+                            }
+                        }
                     }
                 }
                 .thenIf(!viewModel.isEditMode && !isEmptyState) {
@@ -225,14 +304,19 @@
                         gridState,
                         contentOffset,
                         communalContent,
-                        gridCoordinates
+                        gridCoordinates,
                     ) {
                         detectLongPressGesture { offset ->
                             // Deduct both grid offset relative to its container and content
                             // offset.
                             val adjustedOffset =
                                 gridCoordinates?.let {
-                                    offset - it.positionInWindow() - contentOffset
+                                    Offset(
+                                        if (layoutDirection == LayoutDirection.Rtl)
+                                            screenWidth - offset.x
+                                        else offset.x,
+                                        offset.y
+                                    ) - it.positionInWindow() - contentOffset
                                 }
                             val index = adjustedOffset?.let { firstIndexAtOffset(gridState, it) }
                             val key = index?.let { keyAtIndexIfEditable(communalContent, index) }
@@ -283,6 +367,7 @@
                             viewModel = viewModel,
                             contentPadding = contentPadding,
                             contentOffset = contentOffset,
+                            screenWidth = screenWidth,
                             setGridCoordinates = { gridCoordinates = it },
                             updateDragPositionForRemove = { offset ->
                                 isPointerWithinEnabledRemoveButton(
@@ -481,6 +566,56 @@
     }
 }
 
+/**
+ * Observes communal content and determines whether a new widget has been added, upon which case:
+ * - Announce for accessibility
+ * - Scroll if the new widget is not visible
+ */
+@Composable
+private fun ObserveNewWidgetAddedEffect(
+    communalContent: List<CommunalContentModel>,
+    gridState: LazyGridState,
+    viewModel: BaseCommunalViewModel,
+) {
+    val coroutineScope = rememberCoroutineScope()
+    val widgetKeys = remember { mutableListOf<String>() }
+    var communalContentPending by remember { mutableStateOf(true) }
+
+    LaunchedEffect(communalContent) {
+        // Do nothing until any communal content comes in
+        if (communalContentPending && communalContent.isEmpty()) {
+            return@LaunchedEffect
+        }
+
+        val oldWidgetKeys = widgetKeys.toList()
+        val widgets = communalContent.filterIsInstance<CommunalContentModel.WidgetContent.Widget>()
+        widgetKeys.clear()
+        widgetKeys.addAll(widgets.map { it.key })
+
+        // Do nothing on first communal content since we don't have a delta
+        if (communalContentPending) {
+            communalContentPending = false
+            return@LaunchedEffect
+        }
+
+        // Do nothing if there is no new widget
+        val indexOfFirstNewWidget = widgetKeys.indexOfFirst { !oldWidgetKeys.contains(it) }
+        if (indexOfFirstNewWidget < 0) {
+            return@LaunchedEffect
+        }
+
+        viewModel.onNewWidgetAdded(widgets[indexOfFirstNewWidget].providerInfo)
+
+        // Scroll if the new widget is not visible
+        val lastVisibleItemIndex = gridState.layoutInfo.visibleItemsInfo.lastOrNull()?.index
+        if (lastVisibleItemIndex != null && indexOfFirstNewWidget > lastVisibleItemIndex) {
+            // Launching with a scope to prevent the job from being canceled in the case of a
+            // recomposition during scrolling
+            coroutineScope.launch { gridState.animateScrollToItem(indexOfFirstNewWidget) }
+        }
+    }
+}
+
 @OptIn(ExperimentalFoundationApi::class)
 @Composable
 private fun BoxScope.CommunalHubLazyGrid(
@@ -488,6 +623,7 @@
     viewModel: BaseCommunalViewModel,
     contentPadding: PaddingValues,
     selectedKey: State<String?>,
+    screenWidth: Int,
     contentOffset: Offset,
     gridState: LazyGridState,
     contentListState: ContentListState,
@@ -510,7 +646,15 @@
                 updateDragPositionForRemove = updateDragPositionForRemove
             )
         gridModifier =
-            gridModifier.fillMaxSize().dragContainer(dragDropState, contentOffset, viewModel)
+            gridModifier
+                .fillMaxSize()
+                .dragContainer(
+                    dragDropState,
+                    LocalLayoutDirection.current,
+                    screenWidth,
+                    contentOffset,
+                    viewModel
+                )
         // for widgets dropped from other activities
         val dragAndDropTargetState =
             rememberDragAndDropTargetState(
@@ -604,11 +748,11 @@
     Card(
         modifier = Modifier.height(hubDimensions.GridHeight).padding(contentPadding),
         colors = CardDefaults.cardColors(containerColor = Color.Transparent),
-        border = BorderStroke(3.dp, colors.secondary),
-        shape = RoundedCornerShape(size = 80.dp)
+        border = BorderStroke(3.adjustedDp, colors.secondary),
+        shape = RoundedCornerShape(size = 80.adjustedDp)
     ) {
         Column(
-            modifier = Modifier.fillMaxSize().padding(horizontal = 110.dp),
+            modifier = Modifier.fillMaxSize().padding(horizontal = 110.adjustedDp),
             verticalArrangement =
                 Arrangement.spacedBy(Dimensions.Spacing, Alignment.CenterVertically),
             horizontalAlignment = Alignment.CenterHorizontally,
@@ -791,7 +935,7 @@
             onClick = onClick,
             colors =
                 ButtonDefaults.outlinedButtonColors(
-                    contentColor = colors.primary,
+                    contentColor = colors.onPrimaryContainer,
                 ),
             border = BorderStroke(width = 2.0.dp, color = colors.primary),
             contentPadding = Dimensions.ButtonPadding,
@@ -855,22 +999,22 @@
 /** Creates an empty card used to highlight a particular spot on the grid. */
 @Composable
 fun HighlightedItem(modifier: Modifier = Modifier, alpha: Float = 1.0f) {
-    val brush = SolidColor(LocalAndroidColorScheme.current.primaryFixed)
+    val brush = SolidColor(LocalAndroidColorScheme.current.primary)
     Box(
         modifier =
             // drawBehind lets us draw outside the bounds of the widgets so that we don't need to
             // resize grid items to account for the border.
             modifier.drawBehind {
                 // 8dp of padding between the widget and the highlight on every side.
-                val padding = 8.dp.toPx()
+                val padding = 8.adjustedDp.toPx()
                 drawRoundRect(
                     brush,
                     alpha = alpha,
                     topLeft = Offset(-padding, -padding),
                     size =
                         Size(width = size.width + padding * 2, height = size.height + padding * 2),
-                    cornerRadius = CornerRadius(37.dp.toPx()),
-                    style = Stroke(width = 3.dp.toPx())
+                    cornerRadius = CornerRadius(37.adjustedDp.toPx()),
+                    style = Stroke(width = 3.adjustedDp.toPx())
                 )
             }
     )
@@ -890,10 +1034,12 @@
                 containerColor = colors.primary,
                 contentColor = colors.onPrimary,
             ),
-        shape = RoundedCornerShape(68.dp, 34.dp, 68.dp, 34.dp)
+        shape = RoundedCornerShape(68.adjustedDp, 34.adjustedDp, 68.adjustedDp, 34.adjustedDp)
     ) {
         Column(
-            modifier = Modifier.fillMaxSize().padding(vertical = 32.dp, horizontal = 50.dp),
+            modifier =
+                Modifier.fillMaxSize()
+                    .padding(vertical = 32.adjustedDp, horizontal = 50.adjustedDp),
             verticalArrangement = Arrangement.Center,
             horizontalAlignment = Alignment.CenterHorizontally,
         ) {
@@ -902,47 +1048,57 @@
                 contentDescription = stringResource(R.string.cta_label_to_open_widget_picker),
                 modifier = Modifier.size(Dimensions.IconSize).clearAndSetSemantics {},
             )
-            Spacer(modifier = Modifier.size(6.dp))
+            Spacer(modifier = Modifier.size(6.adjustedDp))
             Text(
                 text = stringResource(R.string.cta_label_to_edit_widget),
                 style = MaterialTheme.typography.titleLarge,
                 fontSize = nonScalableTextSize(22.dp),
                 lineHeight = nonScalableTextSize(28.dp),
+                modifier = Modifier.verticalScroll(rememberScrollState()).weight(1F)
             )
-            Spacer(modifier = Modifier.size(16.dp))
+            Spacer(modifier = Modifier.size(16.adjustedDp))
             Row(
-                modifier = Modifier.fillMaxWidth().height(56.dp),
-                horizontalArrangement = Arrangement.spacedBy(16.dp, Alignment.CenterHorizontally),
+                modifier = Modifier.fillMaxWidth().height(56.adjustedDp),
+                horizontalArrangement =
+                    Arrangement.spacedBy(16.adjustedDp, Alignment.CenterHorizontally),
             ) {
-                OutlinedButton(
-                    modifier = Modifier.fillMaxHeight(),
-                    colors =
-                        ButtonDefaults.buttonColors(
-                            contentColor = colors.onPrimary,
-                        ),
-                    border = BorderStroke(width = 1.0.dp, color = colors.primaryContainer),
-                    contentPadding = PaddingValues(26.dp, 8.dp),
-                    onClick = viewModel::onDismissCtaTile,
+                CompositionLocalProvider(
+                    LocalDensity provides
+                        Density(
+                            LocalDensity.current.density,
+                            LocalDensity.current.fontScale.coerceIn(0f, 1.25f)
+                        )
                 ) {
-                    Text(
-                        text = stringResource(R.string.cta_tile_button_to_dismiss),
-                        fontSize = nonScalableTextSize(14.dp),
-                    )
-                }
-                Button(
-                    modifier = Modifier.fillMaxHeight(),
-                    colors =
-                        ButtonDefaults.buttonColors(
-                            containerColor = colors.primaryContainer,
-                            contentColor = colors.onPrimaryContainer,
-                        ),
-                    contentPadding = PaddingValues(26.dp, 8.dp),
-                    onClick = viewModel::onOpenWidgetEditor
-                ) {
-                    Text(
-                        text = stringResource(R.string.cta_tile_button_to_open_widget_editor),
-                        fontSize = nonScalableTextSize(14.dp),
-                    )
+                    OutlinedButton(
+                        modifier = Modifier.fillMaxHeight().weight(1F),
+                        colors =
+                            ButtonDefaults.buttonColors(
+                                contentColor = colors.onPrimary,
+                            ),
+                        border = BorderStroke(width = 1.0.dp, color = colors.primaryContainer),
+                        onClick = viewModel::onDismissCtaTile,
+                        contentPadding = PaddingValues(0.dp, 0.dp, 0.dp, 0.dp),
+                    ) {
+                        Text(
+                            text = stringResource(R.string.cta_tile_button_to_dismiss),
+                            fontSize = 14.sp,
+                        )
+                    }
+                    Button(
+                        modifier = Modifier.fillMaxHeight().weight(1F),
+                        colors =
+                            ButtonDefaults.buttonColors(
+                                containerColor = colors.primaryContainer,
+                                contentColor = colors.onPrimaryContainer,
+                            ),
+                        onClick = viewModel::onOpenWidgetEditor,
+                        contentPadding = PaddingValues(0.dp, 0.dp, 0.dp, 0.dp),
+                    ) {
+                        Text(
+                            text = stringResource(R.string.cta_tile_button_to_open_widget_editor),
+                            fontSize = 14.sp,
+                        )
+                    }
                 }
             }
         }
@@ -993,6 +1149,11 @@
         modifier =
             modifier
                 .then(selectableModifier)
+                .thenIf(!viewModel.isEditMode && !model.inQuietMode) {
+                    Modifier.pointerInput(Unit) {
+                        observeTaps { viewModel.onTapWidget(model.componentName, model.priority) }
+                    }
+                }
                 .thenIf(!viewModel.isEditMode && model.inQuietMode) {
                     Modifier.pointerInput(Unit) {
                         // consume tap to prevent the child view from triggering interactions with
@@ -1004,6 +1165,7 @@
                 }
                 .thenIf(viewModel.isEditMode) {
                     Modifier.semantics {
+                        onClick(clickActionLabel, null)
                         contentDescription = accessibilityLabel
                         val deleteAction =
                             CustomAccessibilityAction(removeWidgetActionLabel) {
@@ -1101,11 +1263,11 @@
         visible = visible,
         enter = fadeIn(),
         exit = fadeOut(),
-        modifier = modifier.padding(16.dp),
+        modifier = modifier.padding(16.adjustedDp),
     ) {
         FilledIconButton(
-            shape = RoundedCornerShape(16.dp),
-            modifier = Modifier.size(48.dp),
+            shape = RoundedCornerShape(16.adjustedDp),
+            modifier = Modifier.size(48.adjustedDp),
             colors =
                 IconButtonColors(
                     containerColor = colors.primary,
@@ -1118,7 +1280,7 @@
             Icon(
                 imageVector = Icons.Outlined.Edit,
                 contentDescription = stringResource(id = R.string.edit_widget),
-                modifier = Modifier.padding(12.dp)
+                modifier = Modifier.padding(12.adjustedDp)
             )
         }
     }
@@ -1181,7 +1343,9 @@
         modifier =
             modifier.background(
                 MaterialTheme.colorScheme.surfaceVariant,
-                RoundedCornerShape(dimensionResource(system_app_widget_background_radius))
+                RoundedCornerShape(
+                    dimensionResource(system_app_widget_background_radius) * scalingAdjustment
+                )
             ),
         verticalArrangement = Arrangement.Center,
         horizontalAlignment = Alignment.CenterHorizontally,
@@ -1205,9 +1369,15 @@
         factory = { context ->
             SmartspaceAppWidgetHostView(context).apply {
                 interactionHandler?.let { setInteractionHandler(it) }
-                updateAppWidget(model.remoteViews)
+                if (!communalTimerFlickerFix()) {
+                    updateAppWidget(model.remoteViews)
+                }
             }
         },
+        update =
+            if (communalTimerFlickerFix()) {
+                { view: SmartspaceAppWidgetHostView -> view.updateAppWidget(model.remoteViews) }
+            } else NoOpUpdate,
         // For reusing composition in lazy lists.
         onReset = {},
     )
@@ -1316,7 +1486,7 @@
 private fun beforeContentPadding(paddingValues: PaddingValues): ContentPaddingInPx {
     return with(LocalDensity.current) {
         ContentPaddingInPx(
-            start = paddingValues.calculateLeftPadding(LayoutDirection.Ltr).toPx(),
+            start = paddingValues.calculateStartPadding(LocalLayoutDirection.current).toPx(),
             top = paddingValues.calculateTopPadding().toPx()
         )
     }
@@ -1362,11 +1532,11 @@
     val GridTopSpacing: Dp
         get() {
             if (config.orientation == Configuration.ORIENTATION_LANDSCAPE) {
-                return 114.dp
+                return 114.adjustedDp
             } else {
                 val windowMetrics =
                     WindowMetricsCalculator.getOrCreate().computeCurrentWindowMetrics(context)
-                val screenHeight = with(density) { windowMetrics.bounds.height().toDp() }
+                val screenHeight = with(density) { windowMetrics.bounds.height().adjustedDp }
 
                 return (screenHeight - CardHeightFull) / 2
             }
@@ -1375,26 +1545,47 @@
     val GridHeight = CardHeightFull + GridTopSpacing
 
     companion object {
-        val CardHeightFull = 530.dp
-        val ItemSpacing = 50.dp
-        val CardHeightHalf = (CardHeightFull - ItemSpacing) / 2
-        val CardHeightThird = (CardHeightFull - (2 * ItemSpacing)) / 3
-        val CardWidth = 360.dp
-        val CardOutlineWidth = 3.dp
-        val Spacing = ItemSpacing / 2
+        val CardHeightFull
+            get() = 530.adjustedDp
+
+        val ItemSpacing
+            get() = 50.adjustedDp
+
+        val CardHeightHalf
+            get() = (CardHeightFull - ItemSpacing) / 2
+
+        val CardHeightThird
+            get() = (CardHeightFull - (2 * ItemSpacing)) / 3
+
+        val CardWidth
+            get() = 360.adjustedDp
+
+        val CardOutlineWidth
+            get() = 3.adjustedDp
+
+        val Spacing
+            get() = ItemSpacing / 2
 
         // The sizing/padding of the toolbar in glanceable hub edit mode
-        val ToolbarPaddingTop = 27.dp
-        val ToolbarPaddingHorizontal = ItemSpacing
-        val ToolbarButtonPaddingHorizontal = 24.dp
-        val ToolbarButtonPaddingVertical = 16.dp
+        val ToolbarPaddingTop
+            get() = 27.adjustedDp
+
+        val ToolbarPaddingHorizontal
+            get() = ItemSpacing
+
+        val ToolbarButtonPaddingHorizontal
+            get() = 24.adjustedDp
+
+        val ToolbarButtonPaddingVertical
+            get() = 16.adjustedDp
+
         val ButtonPadding =
             PaddingValues(
                 vertical = ToolbarButtonPaddingVertical,
                 horizontal = ToolbarButtonPaddingHorizontal,
             )
-        val IconSize = 40.dp
-        val SlideOffsetY = 30.dp
+        val IconSize = 40.adjustedDp
+        val SlideOffsetY = 30.adjustedDp
     }
 }
 
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalScene.kt
index 94018bb..5886d7d 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalScene.kt
@@ -30,8 +30,8 @@
 import com.android.systemui.scene.ui.composable.ComposableScene
 import com.android.systemui.statusbar.phone.SystemUIDialogFactory
 import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.asStateFlow
 
 /** The communal scene shows glanceable hub when the device is locked and docked. */
@@ -45,10 +45,10 @@
 ) : ComposableScene {
     override val key = Scenes.Communal
 
-    override val destinationScenes: StateFlow<Map<UserAction, UserActionResult>> =
+    override val destinationScenes: Flow<Map<UserAction, UserActionResult>> =
         MutableStateFlow<Map<UserAction, UserActionResult>>(
                 mapOf(
-                    Swipe(SwipeDirection.Right) to UserActionResult(Scenes.Lockscreen),
+                    Swipe(SwipeDirection.End) to UserActionResult(Scenes.Lockscreen),
                 )
             )
             .asStateFlow()
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ContentListState.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ContentListState.kt
index c5dab33..38a3474 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ContentListState.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ContentListState.kt
@@ -58,7 +58,7 @@
     communalContent: List<CommunalContentModel>,
     private val onAddWidget:
         (componentName: ComponentName, user: UserHandle, priority: Int) -> Unit,
-    private val onDeleteWidget: (id: Int) -> Unit,
+    private val onDeleteWidget: (id: Int, componentName: ComponentName, priority: Int) -> Unit,
     private val onReorderWidgets: (widgetIdToPriorityMap: Map<Int, Int>) -> Unit,
 ) {
     var list = communalContent.toMutableStateList()
@@ -74,7 +74,7 @@
         if (list[indexToRemove].isWidgetContent()) {
             val widget = list[indexToRemove] as CommunalContentModel.WidgetContent
             list.apply { removeAt(indexToRemove) }
-            onDeleteWidget(widget.appWidgetId)
+            onDeleteWidget(widget.appWidgetId, widget.componentName, widget.priority)
         }
     }
 
@@ -110,7 +110,7 @@
         // reorder and then add the new widget
         onReorderWidgets(widgetIdToPriorityMap)
         if (newItemComponentName != null && newItemUser != null && newItemIndex != null) {
-            onAddWidget(newItemComponentName, newItemUser, /*priority=*/ list.size - newItemIndex)
+            onAddWidget(newItemComponentName, newItemUser, /* priority= */ list.size - newItemIndex)
         }
     }
 
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt
index 07898b0..20ee131 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt
@@ -37,7 +37,9 @@
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.graphics.graphicsLayer
 import androidx.compose.ui.input.pointer.pointerInput
+import androidx.compose.ui.platform.LocalLayoutDirection
 import androidx.compose.ui.unit.IntOffset
+import androidx.compose.ui.unit.LayoutDirection
 import androidx.compose.ui.unit.toOffset
 import androidx.compose.ui.unit.toSize
 import com.android.systemui.communal.ui.compose.extensions.firstItemAtOffset
@@ -47,6 +49,9 @@
 import kotlinx.coroutines.channels.Channel
 import kotlinx.coroutines.launch
 
+private fun Float.directional(origin: LayoutDirection, current: LayoutDirection): Float =
+    if (origin == current) this else -this
+
 @Composable
 fun rememberGridDragDropState(
     gridState: LazyGridState,
@@ -113,14 +118,24 @@
      *
      * @return {@code True} if dragging a grid item, {@code False} otherwise.
      */
-    internal fun onDragStart(offset: Offset, contentOffset: Offset): Boolean {
+    internal fun onDragStart(
+        offset: Offset,
+        screenWidth: Int,
+        layoutDirection: LayoutDirection,
+        contentOffset: Offset
+    ): Boolean {
+        val normalizedOffset =
+            Offset(
+                if (layoutDirection == LayoutDirection.Ltr) offset.x else screenWidth - offset.x,
+                offset.y
+            )
         state.layoutInfo.visibleItemsInfo
             .filter { item -> contentListState.isItemEditable(item.index) }
             // grid item offset is based off grid content container so we need to deduct
             // before content padding from the initial pointer position
-            .firstItemAtOffset(offset - contentOffset)
+            .firstItemAtOffset(normalizedOffset - contentOffset)
             ?.apply {
-                dragStartPointerOffset = offset - this.offset.toOffset()
+                dragStartPointerOffset = normalizedOffset - this.offset.toOffset()
                 draggingItemIndex = index
                 draggingItemInitialOffset = this.offset.toOffset()
                 return true
@@ -145,8 +160,10 @@
         dragStartPointerOffset = Offset.Zero
     }
 
-    internal fun onDrag(offset: Offset) {
-        draggingItemDraggedDelta += offset
+    internal fun onDrag(offset: Offset, layoutDirection: LayoutDirection) {
+        // Adjust offset to match the layout direction
+        draggingItemDraggedDelta +=
+            Offset(offset.x.directional(LayoutDirection.Ltr, layoutDirection), offset.y)
 
         val draggingItem = draggingItemLayoutInfo ?: return
         val startOffset = draggingItem.offset.toOffset() + draggingItemOffset
@@ -213,6 +230,8 @@
 
 fun Modifier.dragContainer(
     dragDropState: GridDragDropState,
+    layoutDirection: LayoutDirection,
+    screenWidth: Int,
     contentOffset: Offset,
     viewModel: BaseCommunalViewModel,
 ): Modifier {
@@ -221,10 +240,17 @@
             detectDragGesturesAfterLongPress(
                 onDrag = { change, offset ->
                     change.consume()
-                    dragDropState.onDrag(offset = offset)
+                    dragDropState.onDrag(offset, layoutDirection)
                 },
                 onDragStart = { offset ->
-                    if (dragDropState.onDragStart(offset, contentOffset)) {
+                    if (
+                        dragDropState.onDragStart(
+                            offset,
+                            screenWidth,
+                            layoutDirection,
+                            contentOffset
+                        )
+                    ) {
                         viewModel.onReorderWidgetStart()
                     }
                 },
@@ -262,10 +288,12 @@
             targetValue = if (dragDropState.isDraggingToRemove) 0.5f else 1f,
             label = "DraggableItemAlpha"
         )
+    val direction = LocalLayoutDirection.current
     val draggingModifier =
         if (dragging) {
             Modifier.graphicsLayer {
-                translationX = dragDropState.draggingItemOffset.x
+                translationX =
+                    dragDropState.draggingItemOffset.x.directional(LayoutDirection.Ltr, direction)
                 translationY = dragDropState.draggingItemOffset.y
                 alpha = itemAlpha
             }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/section/CommunalPopupSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/section/CommunalPopupSection.kt
index 620892a..b4c1a2e 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/section/CommunalPopupSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/section/CommunalPopupSection.kt
@@ -50,7 +50,6 @@
 import androidx.compose.ui.focus.focusRequester
 import androidx.compose.ui.graphics.TransformOrigin
 import androidx.compose.ui.graphics.graphicsLayer
-import androidx.compose.ui.platform.LocalContext
 import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.unit.IntOffset
 import androidx.compose.ui.unit.dp
@@ -102,8 +101,6 @@
         val interactionSource = remember { MutableInteractionSource() }
         val focusRequester = remember { FocusRequester() }
 
-        val context = LocalContext.current
-
         LaunchedEffect(Unit) {
             // Adding a delay to ensure the animation completes before requesting focus
             delay(250)
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt
index c241f9c..b077e18 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt
@@ -30,7 +30,7 @@
 import com.android.systemui.scene.ui.composable.ComposableScene
 import dagger.Lazy
 import javax.inject.Inject
-import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.Flow
 
 /** The lock screen scene shows when the device is locked. */
 @SysUISingleton
@@ -42,7 +42,7 @@
 ) : ComposableScene {
     override val key = Scenes.Lockscreen
 
-    override val destinationScenes: StateFlow<Map<UserAction, UserActionResult>> =
+    override val destinationScenes: Flow<Map<UserAction, UserActionResult>> =
         viewModel.destinationScenes
 
     @Composable
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 7400711..a9e63c6 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
@@ -31,6 +31,7 @@
 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
@@ -94,9 +95,12 @@
                             with(topAreaSection) {
                                 DefaultClockLayout(
                                     modifier =
-                                        Modifier.graphicsLayer {
-                                            translationX = unfoldTranslations.start
-                                        }
+                                        Modifier.thenIf(isShadeLayoutWide) {
+                                                Modifier.fillMaxWidth(0.5f)
+                                            }
+                                            .graphicsLayer {
+                                                translationX = unfoldTranslations.start
+                                            }
                                 )
                             }
                             if (isShadeLayoutWide) {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/BottomAreaSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/BottomAreaSection.kt
index 86639fa..9c72d93 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/BottomAreaSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/BottomAreaSection.kt
@@ -32,6 +32,7 @@
 import androidx.core.content.res.ResourcesCompat
 import com.android.compose.animation.scene.ElementKey
 import com.android.compose.animation.scene.SceneScope
+import com.android.keyguard.logging.KeyguardQuickAffordancesLogger
 import com.android.systemui.animation.view.LaunchableImageView
 import com.android.systemui.keyguard.ui.binder.KeyguardIndicationAreaBinder
 import com.android.systemui.keyguard.ui.binder.KeyguardQuickAffordanceViewBinder
@@ -55,6 +56,7 @@
     private val vibratorHelper: VibratorHelper,
     private val indicationController: KeyguardIndicationController,
     private val indicationAreaViewModel: KeyguardIndicationAreaViewModel,
+    private val shortcutsLogger: KeyguardQuickAffordancesLogger,
 ) {
     /**
      * Renders a single lockscreen shortcut.
@@ -69,7 +71,7 @@
         applyPadding: Boolean,
         modifier: Modifier = Modifier,
     ) {
-        MovableElement(
+        Element(
             key = if (isStart) StartButtonElementKey else EndButtonElementKey,
             modifier = modifier,
         ) {
@@ -96,9 +98,9 @@
     fun SceneScope.IndicationArea(
         modifier: Modifier = Modifier,
     ) {
-        MovableElement(
+        Element(
             key = IndicationAreaElementKey,
-            modifier = modifier.shortcutPadding(),
+            modifier = modifier.indicationAreaPadding(),
         ) {
             content {
                 IndicationArea(
@@ -162,6 +164,7 @@
                         transitionAlpha,
                         falsingManager,
                         vibratorHelper,
+                        shortcutsLogger,
                     ) {
                         indicationController.showTransientIndication(it)
                     }
@@ -210,6 +213,11 @@
             )
             .padding(bottom = dimensionResource(R.dimen.keyguard_affordance_vertical_offset))
     }
+
+    @Composable
+    private fun Modifier.indicationAreaPadding(): Modifier {
+        return this.padding(bottom = dimensionResource(R.dimen.keyguard_indication_margin_bottom))
+    }
 }
 
 private val StartButtonElementKey = ElementKey("StartButton")
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 218779d..bcdb259 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
@@ -120,7 +120,7 @@
             )
         }
 
-        MovableElement(key = largeClockElementKey, modifier = modifier) {
+        Element(key = largeClockElementKey, modifier = modifier) {
             content {
                 AndroidView(
                     factory = { context ->
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt
index 859c036..df068c4 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt
@@ -92,7 +92,7 @@
     fun SceneScope.Notifications(burnInParams: BurnInParameters?, modifier: Modifier = Modifier) {
         val areNotificationsVisible by
             lockscreenContentViewModel
-                .areNotificationsVisible(sceneKey)
+                .areNotificationsVisible(contentKey)
                 .collectAsStateWithLifecycle(initialValue = false)
         if (!areNotificationsVisible) {
             return
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/SmartSpaceSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/SmartSpaceSection.kt
index 44bda95..33ed14b 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/SmartSpaceSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/SmartSpaceSection.kt
@@ -65,68 +65,74 @@
     ) {
         val resources = LocalContext.current.resources
 
-        MovableElement(key = ClockElementKeys.smartspaceElementKey, modifier = modifier) {
-            Column(
-                modifier =
-                    modifier
-                        .onTopPlacementChanged(onTopChanged)
-                        .padding(
-                            top = { lockscreenContentViewModel.getSmartSpacePaddingTop(resources) },
-                            bottom = {
-                                resources.getDimensionPixelSize(
-                                    R.dimen.keyguard_status_view_bottom_margin
-                                )
-                            }
-                        )
-            ) {
-                if (!keyguardSmartspaceViewModel.isSmartspaceEnabled) {
-                    return@Column
-                }
+        Element(key = ClockElementKeys.smartspaceElementKey, modifier = modifier) {
+            content {
+                Column(
+                    modifier =
+                        modifier
+                            .onTopPlacementChanged(onTopChanged)
+                            .padding(
+                                top = {
+                                    lockscreenContentViewModel.getSmartSpacePaddingTop(resources)
+                                },
+                                bottom = {
+                                    resources.getDimensionPixelSize(
+                                        R.dimen.keyguard_status_view_bottom_margin
+                                    )
+                                }
+                            )
+                ) {
+                    if (!keyguardSmartspaceViewModel.isSmartspaceEnabled) {
+                        return@Column
+                    }
 
-                val paddingBelowClockStart = dimensionResource(R.dimen.below_clock_padding_start)
-                val paddingBelowClockEnd = dimensionResource(R.dimen.below_clock_padding_end)
+                    val paddingBelowClockStart =
+                        dimensionResource(R.dimen.below_clock_padding_start)
+                    val paddingBelowClockEnd = dimensionResource(R.dimen.below_clock_padding_end)
 
-                if (keyguardSmartspaceViewModel.isDateWeatherDecoupled) {
-                    Row(
-                        verticalAlignment = Alignment.CenterVertically,
+                    if (keyguardSmartspaceViewModel.isDateWeatherDecoupled) {
+                        Row(
+                            verticalAlignment = Alignment.CenterVertically,
+                            modifier =
+                                Modifier.fillMaxWidth()
+                                    // All items will be constrained to be as tall as the shortest
+                                    // item.
+                                    .height(IntrinsicSize.Min)
+                                    .padding(
+                                        start = paddingBelowClockStart,
+                                    ),
+                        ) {
+                            Date(
+                                modifier =
+                                    Modifier.burnInAware(
+                                        viewModel = aodBurnInViewModel,
+                                        params = burnInParams,
+                                    ),
+                            )
+                            Spacer(modifier = Modifier.width(4.dp))
+                            Weather(
+                                modifier =
+                                    Modifier.burnInAware(
+                                        viewModel = aodBurnInViewModel,
+                                        params = burnInParams,
+                                    ),
+                            )
+                        }
+                    }
+
+                    Card(
                         modifier =
                             Modifier.fillMaxWidth()
-                                // All items will be constrained to be as tall as the shortest item.
-                                .height(IntrinsicSize.Min)
                                 .padding(
                                     start = paddingBelowClockStart,
-                                ),
-                    ) {
-                        Date(
-                            modifier =
-                                Modifier.burnInAware(
+                                    end = paddingBelowClockEnd,
+                                )
+                                .burnInAware(
                                     viewModel = aodBurnInViewModel,
                                     params = burnInParams,
                                 ),
-                        )
-                        Spacer(modifier = Modifier.width(4.dp))
-                        Weather(
-                            modifier =
-                                Modifier.burnInAware(
-                                    viewModel = aodBurnInViewModel,
-                                    params = burnInParams,
-                                ),
-                        )
-                    }
+                    )
                 }
-
-                Card(
-                    modifier =
-                        Modifier.fillMaxWidth()
-                            .padding(
-                                start = paddingBelowClockStart,
-                                end = paddingBelowClockEnd,
-                            )
-                            .burnInAware(
-                                viewModel = aodBurnInViewModel,
-                                params = burnInParams,
-                            ),
-                )
             }
         }
     }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/TopAreaSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/TopAreaSection.kt
index 0cd4b68..c0832d9 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/TopAreaSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/TopAreaSection.kt
@@ -60,7 +60,7 @@
     private val clockInteractor: KeyguardClockInteractor,
 ) {
     @Composable
-    fun DefaultClockLayout(
+    fun SceneScope.DefaultClockLayout(
         modifier: Modifier = Modifier,
     ) {
         val currentClockLayout by clockViewModel.currentClockLayout.collectAsStateWithLifecycle()
@@ -95,26 +95,29 @@
             }
         }
 
-        SceneTransitionLayout(state, modifier) {
-            scene(splitShadeLargeClockScene) {
-                LargeClockWithSmartSpace(
-                    shouldOffSetClockToOneHalf = !hasCustomPositionUpdatedAnimation
-                )
+        Column(modifier) {
+            SceneTransitionLayout(state) {
+                scene(splitShadeLargeClockScene) {
+                    LargeClockWithSmartSpace(
+                        shouldOffSetClockToOneHalf = !hasCustomPositionUpdatedAnimation
+                    )
+                }
+
+                scene(splitShadeSmallClockScene) {
+                    SmallClockWithSmartSpace(modifier = Modifier.fillMaxWidth(0.5f))
+                }
+
+                scene(smallClockScene) { SmallClockWithSmartSpace() }
+
+                scene(largeClockScene) { LargeClockWithSmartSpace() }
+
+                scene(WeatherClockScenes.largeClockScene) { WeatherLargeClockWithSmartSpace() }
+
+                scene(WeatherClockScenes.splitShadeLargeClockScene) {
+                    WeatherLargeClockWithSmartSpace(modifier = Modifier.fillMaxWidth(0.5f))
+                }
             }
-
-            scene(splitShadeSmallClockScene) {
-                SmallClockWithSmartSpace(modifier = Modifier.fillMaxWidth(0.5f))
-            }
-
-            scene(smallClockScene) { SmallClockWithSmartSpace() }
-
-            scene(largeClockScene) { LargeClockWithSmartSpace() }
-
-            scene(WeatherClockScenes.largeClockScene) { WeatherLargeClockWithSmartSpace() }
-
-            scene(WeatherClockScenes.splitShadeLargeClockScene) {
-                WeatherLargeClockWithSmartSpace(modifier = Modifier.fillMaxWidth(0.5f))
-            }
+            with(mediaCarouselSection) { KeyguardMediaCarousel() }
         }
     }
 
@@ -136,7 +139,6 @@
                     onTopChanged = burnIn.onSmartspaceTopChanged,
                 )
             }
-            with(mediaCarouselSection) { KeyguardMediaCarousel() }
         }
     }
 
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/WeatherClockSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/WeatherClockSection.kt
index 9a82da2..2e39524 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/WeatherClockSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/WeatherClockSection.kt
@@ -128,7 +128,7 @@
         elementKey: ElementKey,
         modifier: Modifier = Modifier,
     ) {
-        MovableElement(key = elementKey, modifier) {
+        Element(key = elementKey, modifier) {
             content {
                 AndroidView(
                     factory = {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaCarousel.kt b/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaCarousel.kt
index f8bd633..26ab10b 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaCarousel.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaCarousel.kt
@@ -28,7 +28,7 @@
 import androidx.compose.ui.platform.LocalDensity
 import androidx.compose.ui.res.dimensionResource
 import androidx.compose.ui.viewinterop.AndroidView
-import com.android.compose.animation.scene.ElementKey
+import com.android.compose.animation.scene.MovableElementKey
 import com.android.compose.animation.scene.SceneScope
 import com.android.systemui.media.controls.ui.controller.MediaCarouselController
 import com.android.systemui.media.controls.ui.view.MediaHost
@@ -38,7 +38,10 @@
 object MediaCarousel {
     object Elements {
         internal val Content =
-            ElementKey(debugName = "MediaCarouselContent", scenePicker = MediaScenePicker)
+            MovableElementKey(
+                debugName = "MediaCarouselContent",
+                contentPicker = MediaContentPicker,
+            )
     }
 }
 
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaContentPicker.kt b/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaContentPicker.kt
new file mode 100644
index 0000000..3f04f37
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaContentPicker.kt
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.media.controls.ui.composable
+
+import com.android.compose.animation.scene.ContentKey
+import com.android.compose.animation.scene.ElementContentPicker
+import com.android.compose.animation.scene.ElementKey
+import com.android.compose.animation.scene.SceneTransitionLayoutState
+import com.android.compose.animation.scene.StaticElementContentPicker
+import com.android.compose.animation.scene.content.state.ContentState
+import com.android.systemui.scene.shared.model.Scenes
+
+/** [ElementContentPicker] implementation for the media carousel object. */
+object MediaContentPicker : StaticElementContentPicker {
+
+    const val SHADE_FRACTION = 0.66f
+    override val contents =
+        setOf(
+            Scenes.Lockscreen,
+            Scenes.Shade,
+            Scenes.QuickSettings,
+            Scenes.QuickSettingsShade,
+            Scenes.Communal
+        )
+
+    override fun contentDuringTransition(
+        element: ElementKey,
+        transition: ContentState.Transition<*>,
+        fromContentZIndex: Float,
+        toContentZIndex: Float
+    ): ContentKey {
+        return when {
+            shouldElevateMedia(transition) -> {
+                Scenes.Shade
+            }
+            transition.isTransitioningBetween(Scenes.Lockscreen, Scenes.Communal) -> {
+                Scenes.Lockscreen
+            }
+            transition.isTransitioningBetween(Scenes.QuickSettings, Scenes.Shade) -> {
+                Scenes.QuickSettings
+            }
+            transition.toContent in contents -> transition.toContent
+            else -> {
+                check(transition.fromContent in contents) {
+                    "Media player should not be composed for the transition from " +
+                        "${transition.fromContent} to ${transition.toContent}"
+                }
+                transition.fromContent
+            }
+        }
+    }
+
+    /** Returns true when the media should be laid on top of the rest for the given [transition]. */
+    fun shouldElevateMedia(transition: ContentState.Transition<*>): Boolean {
+        return transition.isTransitioningBetween(Scenes.Lockscreen, Scenes.Shade)
+    }
+}
+
+fun MediaContentPicker.shouldElevateMedia(layoutState: SceneTransitionLayoutState): Boolean {
+    return layoutState.currentTransition?.let { shouldElevateMedia(it) } ?: false
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaScenePicker.kt b/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaScenePicker.kt
deleted file mode 100644
index a22bc34..0000000
--- a/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaScenePicker.kt
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.media.controls.ui.composable
-
-import com.android.compose.animation.scene.ElementKey
-import com.android.compose.animation.scene.ElementScenePicker
-import com.android.compose.animation.scene.SceneKey
-import com.android.compose.animation.scene.TransitionState
-import com.android.systemui.scene.shared.model.Scenes
-
-/** [ElementScenePicker] implementation for the media carousel object. */
-object MediaScenePicker : ElementScenePicker {
-
-    const val SHADE_FRACTION = 0.66f
-    private val scenes =
-        setOf(
-            Scenes.Lockscreen,
-            Scenes.Shade,
-            Scenes.QuickSettings,
-            Scenes.QuickSettingsShade,
-            Scenes.Communal
-        )
-
-    override fun sceneDuringTransition(
-        element: ElementKey,
-        transition: TransitionState.Transition,
-        fromSceneZIndex: Float,
-        toSceneZIndex: Float
-    ): SceneKey? {
-        return when {
-            // TODO: 352052894 - update with the actual scene picking
-            transition.isTransitioning(from = Scenes.Lockscreen, to = Scenes.Shade) -> {
-                if (transition.progress < SHADE_FRACTION) {
-                    Scenes.Lockscreen
-                } else {
-                    Scenes.Shade
-                }
-            }
-
-            // TODO: 345467290 - update with the actual scene picking
-            transition.isTransitioning(from = Scenes.Shade, to = Scenes.Lockscreen) -> {
-                if (transition.progress < 1f - SHADE_FRACTION) {
-                    Scenes.Shade
-                } else {
-                    Scenes.Lockscreen
-                }
-            }
-
-            // TODO: 345467290 - update with the actual scene picking
-            transition.isTransitioningBetween(Scenes.QuickSettings, Scenes.Shade) -> {
-                Scenes.QuickSettings
-            }
-
-            // TODO: 340216785 - update with the actual scene picking
-            else -> pickSingleSceneIn(scenes, transition, element)
-        }
-    }
-}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationStackNestedScrollConnection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationStackNestedScrollConnection.kt
new file mode 100644
index 0000000..4b3a39b
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationStackNestedScrollConnection.kt
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.notifications.ui.composable
+
+import androidx.compose.animation.core.Animatable
+import androidx.compose.animation.core.tween
+import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.foundation.layout.offset
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.input.nestedscroll.NestedScrollSource
+import androidx.compose.ui.input.nestedscroll.nestedScroll
+import androidx.compose.ui.unit.IntOffset
+import com.android.compose.nestedscroll.PriorityNestedScrollConnection
+import kotlin.math.roundToInt
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+
+@Composable
+fun Modifier.stackVerticalOverscroll(
+    coroutineScope: CoroutineScope,
+    canScrollForward: () -> Boolean
+): Modifier {
+    val overscrollOffset = remember { Animatable(0f) }
+    val stackNestedScrollConnection = remember {
+        NotificationStackNestedScrollConnection(
+            stackOffset = { overscrollOffset.value },
+            canScrollForward = canScrollForward,
+            onScroll = { offsetAvailable ->
+                coroutineScope.launch {
+                    overscrollOffset.snapTo(overscrollOffset.value + offsetAvailable * 0.3f)
+                }
+            },
+            onStop = { velocityAvailable ->
+                coroutineScope.launch {
+                    overscrollOffset.animateTo(
+                        targetValue = 0f,
+                        initialVelocity = velocityAvailable,
+                        animationSpec = tween()
+                    )
+                }
+            }
+        )
+    }
+
+    return this.then(
+        Modifier.nestedScroll(stackNestedScrollConnection).offset {
+            IntOffset(x = 0, y = overscrollOffset.value.roundToInt())
+        }
+    )
+}
+
+fun NotificationStackNestedScrollConnection(
+    stackOffset: () -> Float,
+    canScrollForward: () -> Boolean,
+    onStart: (Float) -> Unit = {},
+    onScroll: (Float) -> Unit,
+    onStop: (Float) -> Unit = {},
+): PriorityNestedScrollConnection {
+    return PriorityNestedScrollConnection(
+        orientation = Orientation.Vertical,
+        canStartPreScroll = { _, _ -> false },
+        canStartPostScroll = { offsetAvailable, offsetBeforeStart ->
+            offsetAvailable < 0f && offsetBeforeStart < 0f && !canScrollForward()
+        },
+        canStartPostFling = { velocityAvailable -> velocityAvailable < 0f && !canScrollForward() },
+        canContinueScroll = { source ->
+            if (source == NestedScrollSource.SideEffect) {
+                stackOffset() > STACK_OVERSCROLL_FLING_MIN_OFFSET
+            } else {
+                true
+            }
+        },
+        canScrollOnFling = true,
+        onStart = { offsetAvailable -> onStart(offsetAvailable) },
+        onScroll = { offsetAvailable ->
+            onScroll(offsetAvailable)
+            offsetAvailable
+        },
+        onStop = { velocityAvailable ->
+            onStop(velocityAvailable)
+            velocityAvailable
+        },
+    )
+}
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 c4970c5..84782fd 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
@@ -78,7 +78,7 @@
 import androidx.compose.ui.util.lerp
 import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.compose.animation.scene.ElementKey
-import com.android.compose.animation.scene.LowestZIndexScenePicker
+import com.android.compose.animation.scene.LowestZIndexContentPicker
 import com.android.compose.animation.scene.NestedScrollBehavior
 import com.android.compose.animation.scene.SceneScope
 import com.android.compose.modifiers.thenIf
@@ -105,7 +105,7 @@
         val NotificationScrim = ElementKey("NotificationScrim")
         val NotificationStackPlaceholder = ElementKey("NotificationStackPlaceholder")
         val HeadsUpNotificationPlaceholder =
-            ElementKey("HeadsUpNotificationPlaceholder", scenePicker = LowestZIndexScenePicker)
+            ElementKey("HeadsUpNotificationPlaceholder", contentPicker = LowestZIndexContentPicker)
         val ShelfSpace = ElementKey("ShelfSpace")
         val NotificationStackCutoffGuideline = ElementKey("NotificationStackCutoffGuideline")
     }
@@ -290,6 +290,7 @@
     val isCurrentGestureOverscroll =
         viewModel.isCurrentGestureOverscroll.collectAsStateWithLifecycle(false)
     val expansionFraction by viewModel.expandFraction.collectAsStateWithLifecycle(0f)
+    val shadeToQsFraction by viewModel.shadeToQsFraction.collectAsStateWithLifecycle(0f)
 
     val topPadding = dimensionResource(id = R.dimen.notification_side_paddings)
     val navBarHeight = WindowInsets.systemBars.asPaddingValues().calculateBottomPadding()
@@ -385,14 +386,26 @@
             modifier
                 .element(Notifications.Elements.NotificationScrim)
                 .offset {
-                    // if scrim is expanded while transitioning to Gone scene, increase the offset
-                    // in step with the transition so that it is 0 when it completes.
+                    // if scrim is expanded while transitioning to Gone or QS scene, increase the
+                    // offset in step with the corresponding transition so that it is 0 when it
+                    // completes.
                     if (
                         scrimOffset.value < 0 &&
                             layoutState.isTransitioning(from = Scenes.Shade, to = Scenes.Gone) ||
                             layoutState.isTransitioning(from = Scenes.Shade, to = Scenes.Lockscreen)
                     ) {
                         IntOffset(x = 0, y = (scrimOffset.value * expansionFraction).roundToInt())
+                    } else if (
+                        scrimOffset.value < 0 &&
+                            layoutState.isTransitioning(
+                                from = Scenes.Shade,
+                                to = Scenes.QuickSettings
+                            )
+                    ) {
+                        IntOffset(
+                            x = 0,
+                            y = (scrimOffset.value * (1 - shadeToQsFraction)).roundToInt()
+                        )
                     } else {
                         IntOffset(x = 0, y = scrimOffset.value.roundToInt())
                     }
@@ -461,6 +474,7 @@
                         .thenIf(shadeMode == ShadeMode.Single) {
                             Modifier.nestedScroll(scrimNestedScrollConnection)
                         }
+                        .stackVerticalOverscroll(coroutineScope) { scrollState.canScrollForward }
                         .verticalScroll(scrollState)
                         .padding(top = topPadding)
                         .fillMaxWidth()
@@ -658,3 +672,4 @@
 private val DEBUG_BOX_COLOR = Color(0f, 1f, 0f, 0.2f)
 private const val HUN_SNOOZE_POSITIONAL_THRESHOLD_FRACTION = 0.25f
 private const val HUN_SNOOZE_VELOCITY_THRESHOLD = -70f
+internal const val STACK_OVERSCROLL_FLING_MIN_OFFSET = -100f
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeScene.kt
index db98bc8f..7159def 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeScene.kt
@@ -44,7 +44,7 @@
 import dagger.Lazy
 import java.util.Optional
 import javax.inject.Inject
-import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.Flow
 
 @SysUISingleton
 class NotificationsShadeScene
@@ -64,7 +64,7 @@
 
     override val key = Scenes.NotificationsShade
 
-    override val destinationScenes: StateFlow<Map<UserAction, UserActionResult>> =
+    override val destinationScenes: Flow<Map<UserAction, UserActionResult>> =
         sceneViewModel.destinationScenes
 
     @Composable
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt
index 8058dcd..f399436 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt
@@ -33,10 +33,11 @@
 import androidx.compose.ui.viewinterop.AndroidView
 import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.compose.animation.scene.ElementKey
-import com.android.compose.animation.scene.MovableElementScenePicker
+import com.android.compose.animation.scene.MovableElementContentPicker
+import com.android.compose.animation.scene.MovableElementKey
 import com.android.compose.animation.scene.SceneScope
-import com.android.compose.animation.scene.TransitionState
 import com.android.compose.animation.scene.ValueKey
+import com.android.compose.animation.scene.content.state.TransitionState
 import com.android.compose.modifiers.thenIf
 import com.android.systemui.compose.modifiers.sysuiResTag
 import com.android.systemui.qs.ui.adapter.QSSceneAdapter
@@ -55,7 +56,10 @@
 
     object Elements {
         val Content =
-            ElementKey("QuickSettingsContent", scenePicker = MovableElementScenePicker(SCENES))
+            MovableElementKey(
+                "QuickSettingsContent",
+                contentPicker = MovableElementContentPicker(SCENES)
+            )
         val QuickQuickSettings = ElementKey("QuickQuickSettings")
         val SplitShadeQuickSettings = ElementKey("SplitShadeQuickSettings")
         val FooterActions = ElementKey("QuickSettingsFooterActions")
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt
index 3cf8e70..cdcd840 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt
@@ -68,9 +68,11 @@
 import androidx.compose.ui.unit.dp
 import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.compose.animation.scene.SceneScope
-import com.android.compose.animation.scene.TransitionState
+import com.android.compose.animation.scene.UserAction
+import com.android.compose.animation.scene.UserActionResult
 import com.android.compose.animation.scene.animateSceneDpAsState
 import com.android.compose.animation.scene.animateSceneFloatAsState
+import com.android.compose.animation.scene.content.state.TransitionState
 import com.android.compose.modifiers.thenIf
 import com.android.compose.windowsizeclass.LocalWindowSizeClass
 import com.android.systemui.battery.BatteryMeterViewController
@@ -79,7 +81,6 @@
 import com.android.systemui.common.ui.compose.windowinsets.LocalRawScreenHeight
 import com.android.systemui.compose.modifiers.sysuiResTag
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.media.controls.ui.composable.MediaCarousel
 import com.android.systemui.media.controls.ui.controller.MediaCarouselController
 import com.android.systemui.media.controls.ui.view.MediaHost
@@ -109,16 +110,13 @@
 import javax.inject.Inject
 import javax.inject.Named
 import kotlin.math.roundToInt
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.flow.SharingStarted
-import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.flow.Flow
 
 /** The Quick Settings (AKA "QS") scene shows the quick setting tiles. */
 @SysUISingleton
 class QuickSettingsScene
 @Inject
 constructor(
-    @Application private val applicationScope: CoroutineScope,
     private val shadeSession: SaveableSession,
     private val notificationStackScrollView: Lazy<NotificationScrollView>,
     private val viewModel: QuickSettingsSceneViewModel,
@@ -131,12 +129,8 @@
 ) : ComposableScene {
     override val key = Scenes.QuickSettings
 
-    override val destinationScenes =
-        viewModel.destinationScenes.stateIn(
-            scope = applicationScope,
-            started = SharingStarted.WhileSubscribed(),
-            initialValue = emptyMap(),
-        )
+    override val destinationScenes: Flow<Map<UserAction, UserActionResult>> =
+        viewModel.destinationScenes
 
     @Composable
     override fun SceneScope.Content(
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeScene.kt
index 422c9f6..f6d1283 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeScene.kt
@@ -60,7 +60,7 @@
 import dagger.Lazy
 import java.util.Optional
 import javax.inject.Inject
-import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.Flow
 
 @SysUISingleton
 class QuickSettingsShadeScene
@@ -76,7 +76,7 @@
 
     override val key = Scenes.QuickSettingsShade
 
-    override val destinationScenes: StateFlow<Map<UserAction, UserActionResult>> =
+    override val destinationScenes: Flow<Map<UserAction, UserActionResult>> =
         viewModel.destinationScenes
 
     @Composable
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 a44041a..a9ddf84 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
@@ -36,7 +36,7 @@
 import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationsPlaceholderViewModel
 import dagger.Lazy
 import javax.inject.Inject
-import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.Flow
 
 /**
  * "Gone" is not a real scene but rather the absence of scenes when we want to skip showing any
@@ -52,7 +52,7 @@
 ) : ComposableScene {
     override val key = Scenes.Gone
 
-    override val destinationScenes: StateFlow<Map<UserAction, UserActionResult>> =
+    override val destinationScenes: Flow<Map<UserAction, UserActionResult>> =
         viewModel.destinationScenes
 
     @Composable
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 9c9e6c6..d5874d1 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
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-@file:OptIn(ExperimentalComposeUiApi::class)
-
 package com.android.systemui.scene.ui.composable
 
 import androidx.compose.foundation.layout.Box
@@ -23,14 +21,13 @@
 import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.DisposableEffect
-import androidx.compose.runtime.getValue
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.mutableStateMapOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.rememberCoroutineScope
 import androidx.compose.ui.Alignment
-import androidx.compose.ui.ExperimentalComposeUiApi
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
-import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.compose.animation.scene.MutableSceneTransitionLayoutState
 import com.android.compose.animation.scene.SceneKey
 import com.android.compose.animation.scene.SceneTransitionLayout
@@ -40,6 +37,7 @@
 import com.android.systemui.ribbon.ui.composable.BottomRightCornerRibbon
 import com.android.systemui.scene.shared.model.SceneDataSourceDelegator
 import com.android.systemui.scene.ui.viewmodel.SceneContainerViewModel
+import kotlinx.coroutines.flow.collectLatest
 
 /**
  * Renders a container of a collection of "scenes" that the user can switch between using certain
@@ -62,19 +60,20 @@
 fun SceneContainer(
     viewModel: SceneContainerViewModel,
     sceneByKey: Map<SceneKey, ComposableScene>,
+    initialSceneKey: SceneKey,
     dataSourceDelegator: SceneDataSourceDelegator,
     modifier: Modifier = Modifier,
 ) {
     val coroutineScope = rememberCoroutineScope()
-    val currentSceneKey: SceneKey by viewModel.currentScene.collectAsStateWithLifecycle()
     val state: MutableSceneTransitionLayoutState = remember {
         MutableSceneTransitionLayoutState(
-            initialScene = currentSceneKey,
+            initialScene = initialSceneKey,
             canChangeScene = { toScene -> viewModel.canChangeScene(toScene) },
             transitions = SceneContainerTransitions,
             enableInterruptions = false,
         )
     }
+    val currentSceneKey = state.transitionState.currentScene
 
     DisposableEffect(state) {
         val dataSource = SceneTransitionLayoutDataSource(state, coroutineScope)
@@ -87,19 +86,32 @@
         onDispose { viewModel.setTransitionState(null) }
     }
 
-    val userActionsBySceneKey: Map<SceneKey, Map<UserAction, UserActionResult>> =
-        sceneByKey.values.associate { scene ->
-            val userActions by scene.destinationScenes.collectAsStateWithLifecycle(emptyMap())
-            val resolvedUserActions = viewModel.resolveSceneFamilies(userActions)
-            scene.key to resolvedUserActions
+    val userActionsBySceneKey: MutableMap<SceneKey, Map<UserAction, UserActionResult>> = remember {
+        mutableStateMapOf()
+    }
+    LaunchedEffect(currentSceneKey) {
+        try {
+            sceneByKey[currentSceneKey]?.destinationScenes?.collectLatest { userActions ->
+                userActionsBySceneKey[currentSceneKey] = viewModel.resolveSceneFamilies(userActions)
+            }
+        } finally {
+            userActionsBySceneKey[currentSceneKey] = emptyMap()
         }
+    }
 
     Box(
         modifier = Modifier.fillMaxSize(),
     ) {
         SceneTransitionLayout(state = state, modifier = modifier.fillMaxSize()) {
             sceneByKey.forEach { (sceneKey, composableScene) ->
-                scene(key = sceneKey, userActions = checkNotNull(userActionsBySceneKey[sceneKey])) {
+                scene(
+                    key = sceneKey,
+                    userActions = userActionsBySceneKey.getOrDefault(sceneKey, emptyMap())
+                ) {
+                    // Activate the scene.
+                    LaunchedEffect(composableScene) { composableScene.activate() }
+
+                    // Render the scene.
                     with(composableScene) {
                         this@scene.Content(
                             modifier = Modifier.element(sceneKey.rootElementKey).fillMaxSize(),
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 e433d32..8751ca7 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
@@ -87,7 +87,7 @@
 
     // Scene overscroll
 
-    overscroll(Scenes.Gone, Orientation.Vertical) {}
+    overscrollDisabled(Scenes.Gone, Orientation.Vertical)
     overscroll(Scenes.Bouncer, Orientation.Vertical) {
         translate(Bouncer.Elements.Content, y = { absoluteDistance })
     }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromGoneToSplitShadeTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromGoneToSplitShadeTransition.kt
index f14ff76..a9da733 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromGoneToSplitShadeTransition.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromGoneToSplitShadeTransition.kt
@@ -16,10 +16,59 @@
 
 package com.android.systemui.scene.ui.composable.transitions
 
+import androidx.compose.animation.core.Spring
+import androidx.compose.animation.core.spring
+import androidx.compose.animation.core.tween
+import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.ui.unit.IntSize
 import com.android.compose.animation.scene.TransitionBuilder
+import com.android.compose.animation.scene.UserActionDistance
+import com.android.compose.animation.scene.UserActionDistanceScope
+import com.android.systemui.media.controls.ui.composable.MediaCarousel
+import com.android.systemui.media.controls.ui.composable.MediaContentPicker
+import com.android.systemui.notifications.ui.composable.Notifications
+import com.android.systemui.qs.ui.composable.QuickSettings
+import com.android.systemui.shade.ui.composable.Shade
+import com.android.systemui.shade.ui.composable.ShadeHeader
+import kotlin.time.Duration.Companion.milliseconds
 
 fun TransitionBuilder.goneToSplitShadeTransition(
     durationScale: Double = 1.0,
 ) {
-    toSplitShadeTransition(durationScale)
+    spec = tween(durationMillis = (DefaultDuration * durationScale).inWholeMilliseconds.toInt())
+    swipeSpec =
+        spring(
+            stiffness = Spring.StiffnessMediumLow,
+            visibilityThreshold = Shade.Dimensions.ScrimVisibilityThreshold,
+        )
+    distance =
+        object : UserActionDistance {
+            override fun UserActionDistanceScope.absoluteDistance(
+                fromSceneSize: IntSize,
+                orientation: Orientation,
+            ): Float {
+                return fromSceneSize.height.toFloat() * 2 / 3f
+            }
+        }
+
+    fractionRange(end = .33f) { fade(Shade.Elements.BackgroundScrim) }
+
+    fractionRange(start = .33f) {
+        val qsTranslation =
+            ShadeHeader.Dimensions.CollapsedHeight * MediaContentPicker.SHADE_FRACTION
+        val qsExpansionDiff =
+            ShadeHeader.Dimensions.ExpandedHeight - ShadeHeader.Dimensions.CollapsedHeight
+        translate(MediaCarousel.Elements.Content, y = -(qsExpansionDiff + qsTranslation))
+        fade(MediaCarousel.Elements.Content)
+
+        fade(ShadeHeader.Elements.Clock)
+        fade(ShadeHeader.Elements.CollapsedContentStart)
+        fade(ShadeHeader.Elements.CollapsedContentEnd)
+        fade(ShadeHeader.Elements.PrivacyChip)
+        fade(QuickSettings.Elements.SplitShadeQuickSettings)
+        fade(QuickSettings.Elements.FooterActions)
+        fade(Notifications.Elements.NotificationScrim)
+    }
 }
+
+private val DefaultDuration = 500.milliseconds
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToCommunalTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToCommunalTransition.kt
index 0021bf5..5401936 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToCommunalTransition.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToCommunalTransition.kt
@@ -25,8 +25,8 @@
     spec = tween(durationMillis = 500)
 
     // Translate lockscreen to the left.
-    translate(Scenes.Lockscreen.rootElementKey, Edge.Left)
+    translate(Scenes.Lockscreen.rootElementKey, Edge.Start)
 
     // Translate communal from the right.
-    translate(Scenes.Communal.rootElementKey, Edge.Right)
+    translate(Scenes.Communal.rootElementKey, Edge.End)
 }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToSplitShadeTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToSplitShadeTransition.kt
index 70c343c..1486ea7 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToSplitShadeTransition.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToSplitShadeTransition.kt
@@ -16,10 +16,50 @@
 
 package com.android.systemui.scene.ui.composable.transitions
 
+import androidx.compose.animation.core.Spring
+import androidx.compose.animation.core.spring
+import androidx.compose.animation.core.tween
+import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.ui.unit.IntSize
 import com.android.compose.animation.scene.TransitionBuilder
+import com.android.compose.animation.scene.UserActionDistance
+import com.android.compose.animation.scene.UserActionDistanceScope
+import com.android.systemui.notifications.ui.composable.Notifications
+import com.android.systemui.qs.ui.composable.QuickSettings
+import com.android.systemui.shade.ui.composable.Shade
+import com.android.systemui.shade.ui.composable.ShadeHeader
+import kotlin.time.Duration.Companion.milliseconds
 
 fun TransitionBuilder.lockscreenToSplitShadeTransition(
     durationScale: Double = 1.0,
 ) {
-    toSplitShadeTransition(durationScale = durationScale)
+    spec = tween(durationMillis = (DefaultDuration * durationScale).inWholeMilliseconds.toInt())
+    swipeSpec =
+        spring(
+            stiffness = Spring.StiffnessMediumLow,
+            visibilityThreshold = Shade.Dimensions.ScrimVisibilityThreshold,
+        )
+    distance =
+        object : UserActionDistance {
+            override fun UserActionDistanceScope.absoluteDistance(
+                fromSceneSize: IntSize,
+                orientation: Orientation,
+            ): Float {
+                return fromSceneSize.height.toFloat() * 2 / 3f
+            }
+        }
+
+    fractionRange(end = .33f) { fade(Shade.Elements.BackgroundScrim) }
+
+    fractionRange(start = .33f) {
+        fade(ShadeHeader.Elements.Clock)
+        fade(ShadeHeader.Elements.CollapsedContentStart)
+        fade(ShadeHeader.Elements.CollapsedContentEnd)
+        fade(ShadeHeader.Elements.PrivacyChip)
+        fade(QuickSettings.Elements.SplitShadeQuickSettings)
+        fade(QuickSettings.Elements.FooterActions)
+        fade(Notifications.Elements.NotificationScrim)
+    }
 }
+
+private val DefaultDuration = 500.milliseconds
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToShadeTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToShadeTransition.kt
index 7d46c75..21dfc49 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToShadeTransition.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToShadeTransition.kt
@@ -26,7 +26,7 @@
 import com.android.compose.animation.scene.UserActionDistance
 import com.android.compose.animation.scene.UserActionDistanceScope
 import com.android.systemui.media.controls.ui.composable.MediaCarousel
-import com.android.systemui.media.controls.ui.composable.MediaScenePicker
+import com.android.systemui.media.controls.ui.composable.MediaContentPicker
 import com.android.systemui.notifications.ui.composable.Notifications
 import com.android.systemui.qs.ui.composable.QuickSettings
 import com.android.systemui.scene.shared.model.Scenes
@@ -62,7 +62,7 @@
         fade(QuickSettings.Elements.FooterActions)
     }
 
-    val qsTranslation = ShadeHeader.Dimensions.CollapsedHeight * MediaScenePicker.SHADE_FRACTION
+    val qsTranslation = ShadeHeader.Dimensions.CollapsedHeight * MediaContentPicker.SHADE_FRACTION
     val qsExpansionDiff =
         ShadeHeader.Dimensions.ExpandedHeight - ShadeHeader.Dimensions.CollapsedHeight
 
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToSplitShadeTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToSplitShadeTransition.kt
deleted file mode 100644
index a8315c0..0000000
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToSplitShadeTransition.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.scene.ui.composable.transitions
-
-import androidx.compose.animation.core.Spring
-import androidx.compose.animation.core.spring
-import androidx.compose.animation.core.tween
-import androidx.compose.foundation.gestures.Orientation
-import androidx.compose.ui.unit.IntSize
-import com.android.compose.animation.scene.TransitionBuilder
-import com.android.compose.animation.scene.UserActionDistance
-import com.android.compose.animation.scene.UserActionDistanceScope
-import com.android.systemui.notifications.ui.composable.Notifications
-import com.android.systemui.qs.ui.composable.QuickSettings
-import com.android.systemui.shade.ui.composable.Shade
-import com.android.systemui.shade.ui.composable.ShadeHeader
-import kotlin.time.Duration.Companion.milliseconds
-
-fun TransitionBuilder.toSplitShadeTransition(
-    durationScale: Double = 1.0,
-) {
-    spec = tween(durationMillis = (DefaultDuration * durationScale).inWholeMilliseconds.toInt())
-    swipeSpec =
-        spring(
-            stiffness = Spring.StiffnessMediumLow,
-            visibilityThreshold = Shade.Dimensions.ScrimVisibilityThreshold,
-        )
-    distance =
-        object : UserActionDistance {
-            override fun UserActionDistanceScope.absoluteDistance(
-                fromSceneSize: IntSize,
-                orientation: Orientation,
-            ): Float {
-                return fromSceneSize.height.toFloat() * 2 / 3f
-            }
-        }
-
-    fractionRange(end = .33f) { fade(Shade.Elements.BackgroundScrim) }
-
-    fractionRange(start = .33f) {
-        fade(ShadeHeader.Elements.Clock)
-        fade(ShadeHeader.Elements.CollapsedContentStart)
-        fade(ShadeHeader.Elements.CollapsedContentEnd)
-        fade(ShadeHeader.Elements.PrivacyChip)
-        fade(QuickSettings.Elements.SplitShadeQuickSettings)
-        fade(QuickSettings.Elements.FooterActions)
-        fade(Notifications.Elements.NotificationScrim)
-    }
-}
-
-private val DefaultDuration = 500.milliseconds
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 8656223..facbcaf 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
@@ -49,7 +49,7 @@
 import androidx.compose.ui.unit.dp
 import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.compose.animation.scene.ElementKey
-import com.android.compose.animation.scene.LowestZIndexScenePicker
+import com.android.compose.animation.scene.LowestZIndexContentPicker
 import com.android.compose.animation.scene.SceneScope
 import com.android.compose.windowsizeclass.LocalWindowSizeClass
 import com.android.systemui.keyguard.ui.composable.LockscreenContent
@@ -186,10 +186,10 @@
 
 object OverlayShade {
     object Elements {
-        val Scrim = ElementKey("OverlayShadeScrim", scenePicker = LowestZIndexScenePicker)
-        val Panel = ElementKey("OverlayShadePanel", scenePicker = LowestZIndexScenePicker)
+        val Scrim = ElementKey("OverlayShadeScrim", contentPicker = LowestZIndexContentPicker)
+        val Panel = ElementKey("OverlayShadePanel", contentPicker = LowestZIndexContentPicker)
         val PanelBackground =
-            ElementKey("OverlayShadePanelBackground", scenePicker = LowestZIndexScenePicker)
+            ElementKey("OverlayShadePanelBackground", contentPicker = LowestZIndexContentPicker)
     }
 
     object Colors {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt
index b5a10ca..1cd48bf 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt
@@ -59,11 +59,11 @@
 import androidx.compose.ui.viewinterop.AndroidView
 import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.compose.animation.scene.ElementKey
-import com.android.compose.animation.scene.LowestZIndexScenePicker
+import com.android.compose.animation.scene.LowestZIndexContentPicker
 import com.android.compose.animation.scene.SceneScope
-import com.android.compose.animation.scene.TransitionState
 import com.android.compose.animation.scene.ValueKey
 import com.android.compose.animation.scene.animateElementFloatAsState
+import com.android.compose.animation.scene.content.state.TransitionState
 import com.android.compose.modifiers.thenIf
 import com.android.compose.windowsizeclass.LocalWindowSizeClass
 import com.android.settingslib.Utils
@@ -93,8 +93,8 @@
         val ExpandedContent = ElementKey("ShadeHeaderExpandedContent")
         val CollapsedContentStart = ElementKey("ShadeHeaderCollapsedContentStart")
         val CollapsedContentEnd = ElementKey("ShadeHeaderCollapsedContentEnd")
-        val PrivacyChip = ElementKey("PrivacyChip", scenePicker = LowestZIndexScenePicker)
-        val Clock = ElementKey("ShadeHeaderClock", scenePicker = LowestZIndexScenePicker)
+        val PrivacyChip = ElementKey("PrivacyChip", contentPicker = LowestZIndexContentPicker)
+        val Clock = ElementKey("ShadeHeaderClock", contentPicker = LowestZIndexContentPicker)
         val ShadeCarrierGroup = ElementKey("ShadeCarrierGroup")
     }
 
@@ -110,6 +110,7 @@
     object Colors {
         val ColorScheme.shadeHeaderText: Color
             get() = Color.White
+
         val ColorScheme.onScrimDim: Color
             get() = Color.DarkGray
     }
@@ -148,8 +149,8 @@
         }
 
     val isLargeScreenLayout =
-            LocalWindowSizeClass.current.widthSizeClass == WindowWidthSizeClass.Medium ||
-                    LocalWindowSizeClass.current.widthSizeClass == WindowWidthSizeClass.Expanded
+        LocalWindowSizeClass.current.widthSizeClass == WindowWidthSizeClass.Medium ||
+            LocalWindowSizeClass.current.widthSizeClass == WindowWidthSizeClass.Expanded
 
     val isPrivacyChipVisible by viewModel.isPrivacyChipVisible.collectAsStateWithLifecycle()
 
@@ -197,8 +198,8 @@
                         ) {
                             if (isLargeScreenLayout) {
                                 ShadeCarrierGroup(
-                                        viewModel = viewModel,
-                                        modifier = Modifier.align(Alignment.CenterVertically),
+                                    viewModel = viewModel,
+                                    modifier = Modifier.align(Alignment.CenterVertically),
                                 )
                             }
                             SystemIconContainer(
@@ -552,19 +553,20 @@
     val interactionSource = remember { MutableInteractionSource() }
     val isHovered by interactionSource.collectIsHoveredAsState()
 
-    val hoverModifier = Modifier
-            .clip(RoundedCornerShape(CollapsedHeight / 4))
+    val hoverModifier =
+        Modifier.clip(RoundedCornerShape(CollapsedHeight / 4))
             .background(MaterialTheme.colorScheme.onScrimDim)
 
     Row(
-        modifier = modifier
+        modifier =
+            modifier
                 .height(CollapsedHeight)
                 .padding(vertical = CollapsedHeight / 4)
                 .thenIf(isClickable) {
                     Modifier.clickable(
-                            interactionSource = interactionSource,
-                            indication = null,
-                            onClick = { viewModel.onSystemIconContainerClicked() },
+                        interactionSource = interactionSource,
+                        indication = null,
+                        onClick = { viewModel.onSystemIconContainerClicked() },
                     )
                 }
                 .thenIf(isHovered) { hoverModifier },
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt
index ece8b40..77b48d3 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt
@@ -60,16 +60,17 @@
 import androidx.compose.ui.platform.LocalLifecycleOwner
 import androidx.compose.ui.res.colorResource
 import androidx.compose.ui.unit.dp
+import androidx.compose.ui.zIndex
 import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.compose.animation.scene.ElementKey
-import com.android.compose.animation.scene.LowestZIndexScenePicker
+import com.android.compose.animation.scene.LowestZIndexContentPicker
 import com.android.compose.animation.scene.NestedScrollBehavior
 import com.android.compose.animation.scene.SceneScope
-import com.android.compose.animation.scene.TransitionState
 import com.android.compose.animation.scene.UserAction
 import com.android.compose.animation.scene.UserActionResult
 import com.android.compose.animation.scene.animateSceneDpAsState
 import com.android.compose.animation.scene.animateSceneFloatAsState
+import com.android.compose.animation.scene.content.state.TransitionState
 import com.android.compose.modifiers.padding
 import com.android.compose.modifiers.thenIf
 import com.android.compose.windowsizeclass.LocalWindowSizeClass
@@ -80,10 +81,13 @@
 import com.android.systemui.compose.modifiers.sysuiResTag
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.media.controls.ui.composable.MediaCarousel
+import com.android.systemui.media.controls.ui.composable.MediaContentPicker
+import com.android.systemui.media.controls.ui.composable.shouldElevateMedia
 import com.android.systemui.media.controls.ui.controller.MediaCarouselController
 import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager
 import com.android.systemui.media.controls.ui.view.MediaHost
 import com.android.systemui.media.controls.ui.view.MediaHostState
+import com.android.systemui.media.dagger.MediaModule.QS_PANEL
 import com.android.systemui.media.dagger.MediaModule.QUICK_QS_PANEL
 import com.android.systemui.notifications.ui.composable.NotificationScrollingStack
 import com.android.systemui.notifications.ui.composable.NotificationStackCutoffGuideline
@@ -108,13 +112,13 @@
 import javax.inject.Inject
 import javax.inject.Named
 import kotlin.math.roundToInt
-import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.Flow
 
 object Shade {
     object Elements {
         val MediaCarousel = ElementKey("ShadeMediaCarousel")
         val BackgroundScrim =
-            ElementKey("ShadeBackgroundScrim", scenePicker = LowestZIndexScenePicker)
+            ElementKey("ShadeBackgroundScrim", contentPicker = LowestZIndexContentPicker)
         val SplitShadeStartColumn = ElementKey("SplitShadeStartColumn")
     }
 
@@ -147,12 +151,17 @@
     private val batteryMeterViewControllerFactory: BatteryMeterViewController.Factory,
     private val statusBarIconController: StatusBarIconController,
     private val mediaCarouselController: MediaCarouselController,
-    @Named(QUICK_QS_PANEL) private val mediaHost: MediaHost,
+    @Named(QUICK_QS_PANEL) private val qqsMediaHost: MediaHost,
+    @Named(QS_PANEL) private val qsMediaHost: MediaHost,
 ) : ComposableScene {
 
     override val key = Scenes.Shade
 
-    override val destinationScenes: StateFlow<Map<UserAction, UserActionResult>> =
+    override suspend fun activate() {
+        viewModel.activate()
+    }
+
+    override val destinationScenes: Flow<Map<UserAction, UserActionResult>> =
         viewModel.destinationScenes
 
     @Composable
@@ -167,15 +176,20 @@
             createBatteryMeterViewController = batteryMeterViewControllerFactory::create,
             statusBarIconController = statusBarIconController,
             mediaCarouselController = mediaCarouselController,
-            mediaHost = mediaHost,
+            qqsMediaHost = qqsMediaHost,
+            qsMediaHost = qsMediaHost,
             modifier = modifier,
             shadeSession = shadeSession,
         )
 
     init {
-        mediaHost.expansion = MediaHostState.EXPANDED
-        mediaHost.showsOnlyActiveMedia = true
-        mediaHost.init(MediaHierarchyManager.LOCATION_QQS)
+        qqsMediaHost.expansion = MediaHostState.EXPANDED
+        qqsMediaHost.showsOnlyActiveMedia = true
+        qqsMediaHost.init(MediaHierarchyManager.LOCATION_QQS)
+
+        qsMediaHost.expansion = MediaHostState.EXPANDED
+        qsMediaHost.showsOnlyActiveMedia = false
+        qsMediaHost.init(MediaHierarchyManager.LOCATION_QS)
     }
 }
 
@@ -188,7 +202,8 @@
     createBatteryMeterViewController: (ViewGroup, StatusBarLocation) -> BatteryMeterViewController,
     statusBarIconController: StatusBarIconController,
     mediaCarouselController: MediaCarouselController,
-    mediaHost: MediaHost,
+    qqsMediaHost: MediaHost,
+    qsMediaHost: MediaHost,
     modifier: Modifier = Modifier,
     shadeSession: SaveableSession,
 ) {
@@ -203,7 +218,7 @@
                 createBatteryMeterViewController = createBatteryMeterViewController,
                 statusBarIconController = statusBarIconController,
                 mediaCarouselController = mediaCarouselController,
-                mediaHost = mediaHost,
+                mediaHost = qqsMediaHost,
                 modifier = modifier,
                 shadeSession = shadeSession,
             )
@@ -216,7 +231,7 @@
                 createBatteryMeterViewController = createBatteryMeterViewController,
                 statusBarIconController = statusBarIconController,
                 mediaCarouselController = mediaCarouselController,
-                mediaHost = mediaHost,
+                mediaHost = qsMediaHost,
                 modifier = modifier,
                 shadeSession = shadeSession,
             )
@@ -360,7 +375,13 @@
             maxNotifScrimTop.value = quickSettingsPlaceable.height.toFloat()
 
             layout(constraints.maxWidth, constraints.maxHeight) {
-                quickSettingsPlaceable.placeRelative(x = 0, y = 0)
+                val qsZIndex =
+                    if (MediaContentPicker.shouldElevateMedia(layoutState)) {
+                        1f
+                    } else {
+                        0f
+                    }
+                quickSettingsPlaceable.placeRelative(x = 0, y = 0, zIndex = qsZIndex)
                 notificationsPlaceable.placeRelative(x = 0, y = maxNotifScrimTop.value.roundToInt())
             }
         }
@@ -464,17 +485,20 @@
 
     val brightnessMirrorShowingModifier = Modifier.graphicsLayer { alpha = contentAlpha }
 
-    Box(
-        modifier =
-            modifier
-                .fillMaxSize()
-                .element(Shade.Elements.BackgroundScrim)
-                // Cannot set the alpha of the whole element to 0, because the mirror should be
-                // in the QS column.
-                .background(
-                    colorResource(R.color.shade_scrim_background_dark).copy(alpha = contentAlpha)
-                )
-    ) {
+    Box {
+        Box(
+            modifier =
+                modifier
+                    .fillMaxSize()
+                    .element(Shade.Elements.BackgroundScrim)
+                    // Cannot set the alpha of the whole element to 0, because the mirror should be
+                    // in the QS column.
+                    .background(
+                        colorResource(R.color.shade_scrim_background_dark)
+                            .copy(alpha = contentAlpha)
+                    )
+        )
+
         Column(
             modifier = Modifier.fillMaxSize(),
         ) {
@@ -534,11 +558,15 @@
                                     squishiness = { tileSquishiness },
                                 )
                             }
-
                             MediaCarousel(
                                 isVisible = isMediaVisible,
                                 mediaHost = mediaHost,
-                                modifier = Modifier.fillMaxWidth(),
+                                modifier =
+                                    Modifier.fillMaxWidth().thenIf(
+                                        MediaContentPicker.shouldElevateMedia(layoutState)
+                                    ) {
+                                        Modifier.zIndex(1f)
+                                    },
                                 carouselController = mediaCarouselController,
                             )
                         }
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt
index 114dcf4..e13ca391 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt
@@ -32,6 +32,7 @@
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.util.fastCoerceIn
 import androidx.compose.ui.util.fastLastOrNull
+import com.android.compose.animation.scene.content.state.TransitionState
 import kotlin.math.roundToInt
 
 /**
@@ -67,15 +68,15 @@
 /**
  * Animate a scene Int value.
  *
- * @see SceneScope.animateSceneValueAsState
+ * @see ContentScope.animateContentValueAsState
  */
 @Composable
-fun SceneScope.animateSceneIntAsState(
+fun ContentScope.animateContentIntAsState(
     value: Int,
     key: ValueKey,
     canOverflow: Boolean = true,
 ): AnimatedState<Int> {
-    return animateSceneValueAsState(value, key, SharedIntType, canOverflow)
+    return animateContentValueAsState(value, key, SharedIntType, canOverflow)
 }
 
 /**
@@ -107,17 +108,28 @@
 /**
  * Animate a scene Float value.
  *
- * @see SceneScope.animateSceneValueAsState
+ * @see ContentScope.animateContentValueAsState
  */
 @Composable
-fun SceneScope.animateSceneFloatAsState(
+fun ContentScope.animateContentFloatAsState(
     value: Float,
     key: ValueKey,
     canOverflow: Boolean = true,
 ): AnimatedState<Float> {
-    return animateSceneValueAsState(value, key, SharedFloatType, canOverflow)
+    return animateContentValueAsState(value, key, SharedFloatType, canOverflow)
 }
 
+@Deprecated(
+    "Use animateSceneFloatAsState() instead",
+    replaceWith = ReplaceWith("animateContentFloatAsState(value, key, canOverflow)")
+)
+@Composable
+fun ContentScope.animateSceneFloatAsState(
+    value: Float,
+    key: ValueKey,
+    canOverflow: Boolean = true,
+) = animateContentFloatAsState(value, key, canOverflow)
+
 /**
  * Animate a shared element Float value.
  *
@@ -147,17 +159,28 @@
 /**
  * Animate a scene Dp value.
  *
- * @see SceneScope.animateSceneValueAsState
+ * @see ContentScope.animateContentValueAsState
  */
 @Composable
-fun SceneScope.animateSceneDpAsState(
+fun ContentScope.animateContentDpAsState(
     value: Dp,
     key: ValueKey,
     canOverflow: Boolean = true,
 ): AnimatedState<Dp> {
-    return animateSceneValueAsState(value, key, SharedDpType, canOverflow)
+    return animateContentValueAsState(value, key, SharedDpType, canOverflow)
 }
 
+@Deprecated(
+    "Use animateSceneDpAsState() instead",
+    replaceWith = ReplaceWith("animateContentDpAsState(value, key, canOverflow)")
+)
+@Composable
+fun ContentScope.animateSceneDpAsState(
+    value: Dp,
+    key: ValueKey,
+    canOverflow: Boolean = true,
+) = animateContentDpAsState(value, key, canOverflow)
+
 /**
  * Animate a shared element Dp value.
  *
@@ -188,14 +211,14 @@
 /**
  * Animate a scene Color value.
  *
- * @see SceneScope.animateSceneValueAsState
+ * @see ContentScope.animateContentValueAsState
  */
 @Composable
-fun SceneScope.animateSceneColorAsState(
+fun ContentScope.animateContentColorAsState(
     value: Color,
     key: ValueKey,
 ): AnimatedState<Color> {
-    return animateSceneValueAsState(value, key, SharedColorType, canOverflow = false)
+    return animateContentValueAsState(value, key, SharedColorType, canOverflow = false)
 }
 
 /**
@@ -261,24 +284,24 @@
 @Composable
 internal fun <T> animateSharedValueAsState(
     layoutImpl: SceneTransitionLayoutImpl,
-    scene: SceneKey,
+    content: ContentKey,
     element: ElementKey?,
     key: ValueKey,
     value: T,
     type: SharedValueType<T, *>,
     canOverflow: Boolean,
 ): AnimatedState<T> {
-    DisposableEffect(layoutImpl, scene, element, key) {
-        // Create the associated maps that hold the current value for each (element, scene) pair.
+    DisposableEffect(layoutImpl, content, element, key) {
+        // Create the associated maps that hold the current value for each (element, content) pair.
         val valueMap = layoutImpl.sharedValues.getOrPut(key) { mutableMapOf() }
         val sharedValue = valueMap.getOrPut(element) { SharedValue(type) } as SharedValue<T, *>
         val targetValues = sharedValue.targetValues
-        targetValues[scene] = value
+        targetValues[content] = value
 
         onDispose {
             // Remove the value associated to the current scene, and eventually remove the maps if
             // they are empty.
-            targetValues.remove(scene)
+            targetValues.remove(content)
 
             if (targetValues.isEmpty() && valueMap[element] === sharedValue) {
                 valueMap.remove(element)
@@ -297,11 +320,11 @@
             error("value is equal to $value, which is the undefined value for this type.")
         }
 
-        sharedValue<T, Any>(layoutImpl, key, element).targetValues[scene] = value
+        sharedValue<T, Any>(layoutImpl, key, element).targetValues[content] = value
     }
 
-    return remember(layoutImpl, scene, element, canOverflow) {
-        AnimatedStateImpl<T, Any>(layoutImpl, scene, element, key, canOverflow)
+    return remember(layoutImpl, content, element, canOverflow) {
+        AnimatedStateImpl<T, Any>(layoutImpl, content, element, key, canOverflow)
     }
 }
 
@@ -322,8 +345,8 @@
 internal class SharedValue<T, Delta>(
     val type: SharedValueType<T, Delta>,
 ) {
-    /** The target value of this shared value for each scene. */
-    val targetValues = SnapshotStateMap<SceneKey, T>()
+    /** The target value of this shared value for each content. */
+    val targetValues = SnapshotStateMap<ContentKey, T>()
 
     /** The last value of this shared value. */
     var lastValue: T = type.unspecifiedValue
@@ -340,7 +363,7 @@
 
 private class AnimatedStateImpl<T, Delta>(
     private val layoutImpl: SceneTransitionLayoutImpl,
-    private val scene: SceneKey,
+    private val content: ContentKey,
     private val element: ElementKey?,
     private val key: ValueKey,
     private val canOverflow: Boolean,
@@ -356,14 +379,14 @@
                 // TODO(b/311600838): Remove this. We should not have to fallback to the current
                 // scene value, but we have to because code of removed nodes can still run if they
                 // are placed with a graphics layer.
-                ?: sharedValue[scene]
+                ?: sharedValue[content]
                 ?: error(valueReadTooEarlyMessage(key))
         val interruptedValue = computeInterruptedValue(sharedValue, transition, value)
         sharedValue.lastValue = interruptedValue
         return interruptedValue
     }
 
-    private operator fun SharedValue<T, *>.get(scene: SceneKey): T? = targetValues[scene]
+    private operator fun SharedValue<T, *>.get(content: ContentKey): T? = targetValues[content]
 
     private fun valueOrNull(
         sharedValue: SharedValue<T, *>,
@@ -401,7 +424,7 @@
         val targetValues = sharedValue.targetValues
         val transition =
             if (element != null) {
-                layoutImpl.elements[element]?.sceneStates?.let { sceneStates ->
+                layoutImpl.elements[element]?.stateByContent?.let { sceneStates ->
                     layoutImpl.state.currentTransitions.fastLastOrNull { transition ->
                         transition.fromScene in sceneStates || transition.toScene in sceneStates
                     }
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateToScene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateToScene.kt
index 82c85d1..1fc1f98 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateToScene.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateToScene.kt
@@ -19,6 +19,7 @@
 import androidx.compose.animation.core.Animatable
 import androidx.compose.animation.core.AnimationVector1D
 import androidx.compose.animation.core.SpringSpec
+import com.android.compose.animation.scene.content.state.TransitionState
 import kotlin.math.absoluteValue
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.CoroutineStart
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 665be53..a43028a 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
@@ -29,7 +29,11 @@
 import androidx.compose.ui.unit.IntSize
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.round
-import com.android.compose.animation.scene.TransitionState.HasOverscrollProperties.Companion.DistanceUnspecified
+import androidx.compose.ui.util.fastCoerceIn
+import com.android.compose.animation.scene.content.Scene
+import com.android.compose.animation.scene.content.state.ContentState
+import com.android.compose.animation.scene.content.state.ContentState.HasOverscrollProperties.Companion.DistanceUnspecified
+import com.android.compose.animation.scene.content.state.TransitionState
 import com.android.compose.nestedscroll.PriorityNestedScrollConnection
 import kotlin.math.absoluteValue
 import kotlinx.coroutines.CoroutineScope
@@ -52,11 +56,19 @@
  * and [onStop] methods.
  */
 internal interface DragController {
-    /** Drag the current scene by [delta] pixels. */
-    fun onDrag(delta: Float)
+    /**
+     * Drag the current scene by [delta] pixels.
+     *
+     * @return the consumed [delta]
+     */
+    fun onDrag(delta: Float): Float
 
-    /** Starts a transition to a target scene. */
-    fun onStop(velocity: Float, canChangeScene: Boolean)
+    /**
+     * Starts a transition to a target scene.
+     *
+     * @return the consumed [velocity]
+     */
+    fun onStop(velocity: Float, canChangeScene: Boolean): Float
 }
 
 internal class DraggableHandlerImpl(
@@ -272,89 +284,107 @@
      *
      * @return the consumed delta
      */
-    override fun onDrag(delta: Float) {
-        if (delta == 0f || !isDrivingTransition || swipeTransition.isFinishing) return
-        swipeTransition.dragOffset += delta
+    override fun onDrag(delta: Float): Float {
+        if (delta == 0f || !isDrivingTransition || swipeTransition.isFinishing) {
+            return 0f
+        }
 
-        val (fromScene, acceleratedOffset) =
-            computeFromSceneConsideringAcceleratedSwipe(swipeTransition)
+        val toScene = swipeTransition._toScene
+        val distance = swipeTransition.distance()
+        val previousOffset = swipeTransition.dragOffset
+        val desiredOffset = previousOffset + delta
 
-        val isNewFromScene = fromScene.key != swipeTransition.fromScene
+        fun hasReachedToSceneUpOrLeft() =
+            distance < 0 &&
+                desiredOffset <= distance &&
+                swipes.upOrLeftResult?.toScene == toScene.key
+
+        fun hasReachedToSceneDownOrRight() =
+            distance > 0 &&
+                desiredOffset >= distance &&
+                swipes.downOrRightResult?.toScene == toScene.key
+
+        // Considering accelerated swipe: Change fromScene in the case where the user quickly swiped
+        // multiple times in the same direction to accelerate the transition from A => B then B => C
+        //
+        // TODO(b/290184746): the second drag needs to pass B to work. Add support for flinging
+        //  twice before B has been reached
+        val hasReachedToScene =
+            swipeTransition._currentScene == toScene &&
+                (hasReachedToSceneUpOrLeft() || hasReachedToSceneDownOrRight())
+
+        val fromScene: Scene
+        val currentTransitionOffset: Float
+        val newOffset: Float
+        val consumedDelta: Float
+        if (hasReachedToScene) {
+            // The new transition will start from the current toScene
+            fromScene = toScene
+            // The current transition is completed (we have reached the distance)
+            currentTransitionOffset = distance
+            // The next transition will start with the remaining offset
+            newOffset = desiredOffset - distance
+            consumedDelta = delta
+        } else {
+            fromScene = swipeTransition._fromScene
+            val desiredProgress = swipeTransition.computeProgress(desiredOffset)
+            // note: the distance could be negative if fromScene is aboveOrLeft of toScene.
+            currentTransitionOffset =
+                when {
+                    distance == DistanceUnspecified ||
+                        swipeTransition.isWithinProgressRange(desiredProgress) -> desiredOffset
+                    distance > 0f -> desiredOffset.fastCoerceIn(0f, distance)
+                    else -> desiredOffset.fastCoerceIn(distance, 0f)
+                }
+            // If there is a new transition, we will use the same offset
+            newOffset = currentTransitionOffset
+            consumedDelta = newOffset - previousOffset
+        }
+
+        swipeTransition.dragOffset = currentTransitionOffset
+
         val result =
             swipes.findUserActionResult(
                 fromScene = fromScene,
-                directionOffset = swipeTransition.dragOffset,
-                updateSwipesResults = isNewFromScene,
+                directionOffset = newOffset,
+                updateSwipesResults = hasReachedToScene
             )
 
         if (result == null) {
             onStop(velocity = delta, canChangeScene = true)
-            return
+            return 0f
         }
 
-        if (
-            isNewFromScene ||
+        val needNewTransition =
+            hasReachedToScene ||
                 result.toScene != swipeTransition.toScene ||
                 result.transitionKey != swipeTransition.key
-        ) {
+
+        if (needNewTransition) {
             // Make sure the current transition will finish to the right current scene.
             swipeTransition._currentScene = fromScene
 
-            val swipeTransition =
+            val newSwipeTransition =
                 SwipeTransition(
-                        layoutState = layoutState,
-                        coroutineScope = draggableHandler.coroutineScope,
-                        fromScene = fromScene,
-                        result = result,
-                        swipes = swipes,
-                        layoutImpl = draggableHandler.layoutImpl,
-                        orientation = draggableHandler.orientation,
-                    )
-                    .apply { dragOffset = swipeTransition.dragOffset + acceleratedOffset }
-
-            updateTransition(swipeTransition)
+                    layoutState = layoutState,
+                    coroutineScope = draggableHandler.coroutineScope,
+                    fromScene = fromScene,
+                    result = result,
+                    swipes = swipes,
+                    layoutImpl = draggableHandler.layoutImpl,
+                    orientation = draggableHandler.orientation,
+                )
+            newSwipeTransition.dragOffset = newOffset
+            updateTransition(newSwipeTransition)
         }
+
+        return consumedDelta
     }
 
-    /**
-     * Change fromScene in the case where the user quickly swiped multiple times in the same
-     * direction to accelerate the transition from A => B then B => C.
-     *
-     * @return the new fromScene and a dragOffset to be added in case the scene has changed
-     *
-     * TODO(b/290184746): the second drag needs to pass B to work. Add support for flinging twice
-     *   before B has been reached
-     */
-    private inline fun computeFromSceneConsideringAcceleratedSwipe(
-        swipeTransition: SwipeTransition,
-    ): Pair<Scene, Float> {
-        val toScene = swipeTransition._toScene
-        val fromScene = swipeTransition._fromScene
-        val distance = swipeTransition.distance()
-
-        // If the swipe was not committed or if the swipe distance is not computed yet, don't do
-        // anything.
-        if (swipeTransition._currentScene != toScene || distance == DistanceUnspecified) {
-            return fromScene to 0f
-        }
-
-        // If the offset is past the distance then let's change fromScene so that the user can swipe
-        // to the next screen or go back to the previous one.
-        val offset = swipeTransition.dragOffset
-        val absoluteDistance = distance.absoluteValue
-        return if (offset <= -absoluteDistance && swipes.upOrLeftResult?.toScene == toScene.key) {
-            toScene to absoluteDistance
-        } else if (offset >= absoluteDistance && swipes.downOrRightResult?.toScene == toScene.key) {
-            toScene to -absoluteDistance
-        } else {
-            fromScene to 0f
-        }
-    }
-
-    override fun onStop(velocity: Float, canChangeScene: Boolean) {
+    override fun onStop(velocity: Float, canChangeScene: Boolean): Float {
         // The state was changed since the drag started; don't do anything.
         if (!isDrivingTransition || swipeTransition.isFinishing) {
-            return
+            return 0f
         }
 
         // Important: Make sure that all the code here references the current transition when
@@ -440,7 +470,7 @@
                 if (result == null) {
                     // We will not animate
                     swipeTransition.snapToScene(fromScene.key)
-                    return
+                    return 0f
                 }
 
                 val newSwipeTransition =
@@ -462,6 +492,9 @@
                 animateTo(targetScene = fromScene, targetOffset = 0f)
             }
         }
+
+        // The onStop animation consumes any remaining velocity.
+        return velocity
     }
 
     /**
@@ -577,7 +610,7 @@
     var lastDistance: Float = DistanceUnspecified,
 ) :
     TransitionState.Transition(_fromScene.key, _toScene.key, replacedTransition),
-    TransitionState.HasOverscrollProperties {
+    ContentState.HasOverscrollProperties {
     var _currentScene by mutableStateOf(_fromScene)
     override val currentScene: SceneKey
         get() = _currentScene.key
@@ -589,14 +622,17 @@
             // subscribes to the offset value.
             val offset = offsetAnimation?.animatable?.value ?: dragOffset
 
-            val distance = distance()
-            if (distance == DistanceUnspecified) {
-                return 0f
-            }
-
-            return offset / distance
+            return computeProgress(offset)
         }
 
+    fun computeProgress(offset: Float): Float {
+        val distance = distance()
+        if (distance == DistanceUnspecified) {
+            return 0f
+        }
+        return offset / distance
+    }
+
     override val progressVelocity: Float
         get() {
             val animatable = offsetAnimation?.animatable ?: return 0f
@@ -611,7 +647,7 @@
 
     override val isInitiatedByUserInput = true
 
-    override var bouncingScene: SceneKey? = null
+    override var bouncingContent: SceneKey? = null
 
     /** The current offset caused by the drag gesture. */
     var dragOffset by mutableFloatStateOf(0f)
@@ -692,20 +728,19 @@
         targetOffset: Float,
         targetScene: SceneKey,
     ): OffsetAnimation {
+        val initialProgress = progress
         // Skip the animation if we have already reached the target scene and the overscroll does
         // not animate anything.
         val hasReachedTargetScene =
-            (targetScene == toScene && progress >= 1f) ||
-                (targetScene == fromScene && progress <= 0f)
-        val skipAnimation =
-            hasReachedTargetScene &&
-                currentOverscrollSpec?.transformationSpec?.transformations?.isEmpty() == true
+            (targetScene == toScene && initialProgress >= 1f) ||
+                (targetScene == fromScene && initialProgress <= 0f)
+        val skipAnimation = hasReachedTargetScene && !isWithinProgressRange(initialProgress)
 
         return startOffsetAnimation {
             val animatable = Animatable(dragOffset, OffsetVisibilityThreshold)
             val isTargetGreater = targetOffset > animatable.value
             val startedWhenOvercrollingTargetScene =
-                if (targetScene == fromScene) progress < 0f else progress > 1f
+                if (targetScene == fromScene) initialProgress < 0f else initialProgress > 1f
             val job =
                 coroutineScope
                     // Important: We start atomically to make sure that we start the coroutine even
@@ -731,7 +766,7 @@
                                 animationSpec = swipeSpec,
                                 initialVelocity = initialVelocity,
                             ) {
-                                if (bouncingScene == null) {
+                                if (bouncingContent == null) {
                                     val isBouncing =
                                         if (isTargetGreater) {
                                             if (startedWhenOvercrollingTargetScene) {
@@ -748,16 +783,11 @@
                                         }
 
                                     if (isBouncing) {
-                                        bouncingScene = targetScene
+                                        bouncingContent = targetScene
 
                                         // Immediately stop this transition if we are bouncing on a
                                         // scene that does not bounce.
-                                        val overscrollSpec = currentOverscrollSpec
-                                        if (
-                                            overscrollSpec != null &&
-                                                overscrollSpec.transformationSpec.transformations
-                                                    .isEmpty()
-                                        ) {
+                                        if (!isWithinProgressRange(progress)) {
                                             snapToScene(targetScene)
                                         }
                                     }
@@ -1081,17 +1111,13 @@
                 // TODO(b/297842071) We should handle the overscroll or slow drag if the gesture is
                 // initiated in a nested child.
                 controller.onDrag(delta = offsetAvailable)
-
-                offsetAvailable
             },
             onStop = { velocityAvailable ->
                 val controller = dragController ?: error("Should be called after onStart")
 
-                controller.onStop(velocity = velocityAvailable, canChangeScene = canChangeScene)
-
-                dragController = null
-                // The onDragStopped animation consumes any remaining velocity.
-                velocityAvailable
+                controller
+                    .onStop(velocity = velocityAvailable, canChangeScene = canChangeScene)
+                    .also { dragController = null }
             },
         )
     }
@@ -1106,7 +1132,7 @@
 internal const val OffsetVisibilityThreshold = 0.5f
 
 private object NoOpDragController : DragController {
-    override fun onDrag(delta: Float) {}
+    override fun onDrag(delta: Float) = 0f
 
-    override fun onStop(velocity: Float, canChangeScene: Boolean) {}
+    override fun onStop(velocity: Float, canChangeScene: Boolean) = 0f
 }
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
index 377b02b..ec7c77b 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
@@ -48,50 +48,55 @@
 import androidx.compose.ui.util.fastCoerceIn
 import androidx.compose.ui.util.fastLastOrNull
 import androidx.compose.ui.util.lerp
+import com.android.compose.animation.scene.content.Content
+import com.android.compose.animation.scene.content.state.ContentState
+import com.android.compose.animation.scene.content.state.TransitionState
 import com.android.compose.animation.scene.transformation.PropertyTransformation
 import com.android.compose.animation.scene.transformation.SharedElementTransformation
 import com.android.compose.ui.util.lerp
 import kotlin.math.roundToInt
 import kotlinx.coroutines.launch
 
-/** An element on screen, that can be composed in one or more scenes. */
+/** An element on screen, that can be composed in one or more contents. */
 @Stable
 internal class Element(val key: ElementKey) {
-    /** The mapping between a scene and the state this element has in that scene, if any. */
+    /** The mapping between a content and the state this element has in that content, if any. */
     // TODO(b/316901148): Make this a normal map instead once we can make sure that new transitions
     // are first seen by composition then layout/drawing code. See b/316901148#comment2 for details.
-    val sceneStates = SnapshotStateMap<SceneKey, SceneState>()
+    val stateByContent = SnapshotStateMap<ContentKey, State>()
 
     /**
      * The last transition that was used when computing the state (size, position and alpha) of this
-     * element in any scene, or `null` if it was last laid out when idle.
+     * element in any content, or `null` if it was last laid out when idle.
      */
-    var lastTransition: TransitionState.Transition? = null
+    var lastTransition: ContentState.Transition<*>? = null
 
-    /** Whether this element was ever drawn in a scene. */
-    var wasDrawnInAnyScene = false
+    /** Whether this element was ever drawn in a content. */
+    var wasDrawnInAnyContent = false
 
     override fun toString(): String {
         return "Element(key=$key)"
     }
 
-    /** The last and target state of this element in a given scene. */
+    /** The last and target state of this element in a given content. */
     @Stable
-    class SceneState(val scene: SceneKey) {
+    class State(val content: ContentKey) {
         /**
-         * The *target* state of this element in this scene, i.e. the state of this element when we
-         * are idle on this scene.
+         * The *target* state of this element in this content, i.e. the state of this element when
+         * we are idle on this content.
          */
         var targetSize by mutableStateOf(SizeUnspecified)
         var targetOffset by mutableStateOf(Offset.Unspecified)
 
-        /** The last state this element had in this scene. */
+        /** The last state this element had in this content. */
         var lastOffset = Offset.Unspecified
         var lastSize = SizeUnspecified
         var lastScale = Scale.Unspecified
         var lastAlpha = AlphaUnspecified
 
-        /** The state of this element in this scene right before the last interruption (if any). */
+        /**
+         * The state of this element in this content right before the last interruption (if any).
+         */
         var offsetBeforeInterruption = Offset.Unspecified
         var sizeBeforeInterruption = SizeUnspecified
         var scaleBeforeInterruption = Scale.Unspecified
@@ -100,7 +105,7 @@
         /**
          * The delta values to add to this element state to have smoother interruptions. These
          * should be multiplied by the
-         * [current interruption progress][TransitionState.Transition.interruptionProgress] so that
+         * [current interruption progress][ContentState.Transition.interruptionProgress] so that
          * they nicely animate from their values down to 0.
          */
         var offsetInterruptionDelta = Offset.Zero
@@ -109,7 +114,7 @@
         var alphaInterruptionDelta = 0f
 
         /**
-         * The attached [ElementNode] a Modifier.element() for a given element and scene. During
+         * The attached [ElementNode] a Modifier.element() for a given element and content. During
          * composition, this set could have 0 to 2 elements. After composition and after all
          * modifier nodes have been attached/detached, this set should contain exactly 1 element.
          */
@@ -130,19 +135,19 @@
     }
 }
 
-/** The implementation of [SceneScope.element]. */
+/** The implementation of [ContentScope.element]. */
 @Stable
 internal fun Modifier.element(
     layoutImpl: SceneTransitionLayoutImpl,
-    scene: Scene,
+    content: Content,
     key: ElementKey,
 ): Modifier {
     // Make sure that we read the current transitions during composition and not during
     // layout/drawing.
     // TODO(b/341072461): Revert this and read the current transitions in ElementNode directly once
-    // we can ensure that SceneTransitionLayoutImpl will compose new scenes first.
+    // we can ensure that SceneTransitionLayoutImpl will compose new contents first.
     val currentTransitions = layoutImpl.state.currentTransitions
-    return then(ElementModifier(layoutImpl, currentTransitions, scene, key)).testTag(key.testTag)
+    return then(ElementModifier(layoutImpl, currentTransitions, content, key)).testTag(key.testTag)
 }
 
 /**
@@ -152,92 +157,92 @@
 private data class ElementModifier(
     private val layoutImpl: SceneTransitionLayoutImpl,
     private val currentTransitions: List<TransitionState.Transition>,
-    private val scene: Scene,
+    private val content: Content,
     private val key: ElementKey,
 ) : ModifierNodeElement<ElementNode>() {
-    override fun create(): ElementNode = ElementNode(layoutImpl, currentTransitions, scene, key)
+    override fun create(): ElementNode = ElementNode(layoutImpl, currentTransitions, content, key)
 
     override fun update(node: ElementNode) {
-        node.update(layoutImpl, currentTransitions, scene, key)
+        node.update(layoutImpl, currentTransitions, content, key)
     }
 }
 
 internal class ElementNode(
     private var layoutImpl: SceneTransitionLayoutImpl,
     private var currentTransitions: List<TransitionState.Transition>,
-    private var scene: Scene,
+    private var content: Content,
     private var key: ElementKey,
 ) : Modifier.Node(), DrawModifierNode, ApproachLayoutModifierNode, TraversableNode {
     private var _element: Element? = null
     private val element: Element
         get() = _element!!
 
-    private var _sceneState: Element.SceneState? = null
-    private val sceneState: Element.SceneState
-        get() = _sceneState!!
+    private var _stateInContent: Element.State? = null
+    private val stateInContent: Element.State
+        get() = _stateInContent!!
 
     override val traverseKey: Any = ElementTraverseKey
 
     override fun onAttach() {
         super.onAttach()
-        updateElementAndSceneValues()
-        addNodeToSceneState()
+        updateElementAndContentValues()
+        addNodeToContentState()
     }
 
-    private fun updateElementAndSceneValues() {
+    private fun updateElementAndContentValues() {
         val element =
             layoutImpl.elements[key] ?: Element(key).also { layoutImpl.elements[key] = it }
         _element = element
-        _sceneState =
-            element.sceneStates[scene.key]
-                ?: Element.SceneState(scene.key).also { element.sceneStates[scene.key] = it }
+        _stateInContent =
+            element.stateByContent[content.key]
+                ?: Element.State(content.key).also { element.stateByContent[content.key] = it }
     }
 
-    private fun addNodeToSceneState() {
-        sceneState.nodes.add(this)
+    private fun addNodeToContentState() {
+        stateInContent.nodes.add(this)
 
         coroutineScope.launch {
             // At this point all [CodeLocationNode] have been attached or detached, which means that
-            // [sceneState.codeLocations] should have exactly 1 element, otherwise this means that
-            // this element was composed multiple times in the same scene.
-            val nCodeLocations = sceneState.nodes.size
-            if (nCodeLocations != 1 || !sceneState.nodes.contains(this@ElementNode)) {
-                error("$key was composed $nCodeLocations times in ${sceneState.scene}")
+            // [elementState.codeLocations] should have exactly 1 element, otherwise this means that
+            // this element was composed multiple times in the same content.
+            val nCodeLocations = stateInContent.nodes.size
+            if (nCodeLocations != 1 || !stateInContent.nodes.contains(this@ElementNode)) {
+                error("$key was composed $nCodeLocations times in ${stateInContent.content}")
             }
         }
     }
 
     override fun onDetach() {
         super.onDetach()
-        removeNodeFromSceneState()
-        maybePruneMaps(layoutImpl, element, sceneState)
+        removeNodeFromContentState()
+        maybePruneMaps(layoutImpl, element, stateInContent)
 
         _element = null
-        _sceneState = null
+        _stateInContent = null
     }
 
-    private fun removeNodeFromSceneState() {
-        sceneState.nodes.remove(this)
+    private fun removeNodeFromContentState() {
+        stateInContent.nodes.remove(this)
     }
 
     fun update(
         layoutImpl: SceneTransitionLayoutImpl,
         currentTransitions: List<TransitionState.Transition>,
-        scene: Scene,
+        content: Content,
         key: ElementKey,
     ) {
-        check(layoutImpl == this.layoutImpl && scene == this.scene)
+        check(layoutImpl == this.layoutImpl && content == this.content)
         this.currentTransitions = currentTransitions
 
-        removeNodeFromSceneState()
+        removeNodeFromContentState()
 
         val prevElement = this.element
-        val prevSceneState = this.sceneState
+        val prevElementState = this.stateInContent
         this.key = key
-        updateElementAndSceneValues()
+        updateElementAndContentValues()
 
-        addNodeToSceneState()
-        maybePruneMaps(layoutImpl, prevElement, prevSceneState)
+        addNodeToContentState()
+        maybePruneMaps(layoutImpl, prevElement, prevElementState)
     }
 
     override fun isMeasurementApproachInProgress(lookaheadSize: IntSize): Boolean {
@@ -262,15 +267,15 @@
         check(isLookingAhead)
 
         return measurable.measure(constraints).run {
-            // Update the size this element has in this scene when idle.
-            sceneState.targetSize = size()
+            // Update the size this element has in this content when idle.
+            stateInContent.targetSize = size()
 
             layout(width, height) {
                 // Update the offset (relative to the SceneTransitionLayout) this element has in
-                // this scene when idle.
+                // this content when idle.
                 coordinates?.let { coords ->
                     with(layoutImpl.lookaheadScope) {
-                        sceneState.targetOffset =
+                        stateInContent.targetOffset =
                             lookaheadScopeCoordinates.localLookaheadPositionOf(coords)
                     }
                 }
@@ -287,37 +292,37 @@
         val transition = elementTransition(layoutImpl, element, transitions)
 
         // If this element is not supposed to be laid out now, either because it is not part of any
-        // ongoing transition or the other scene of its transition is overscrolling, then lay out
+        // ongoing transition or the other content of its transition is overscrolling, then lay out
         // the element normally and don't place it.
         val overscrollScene = transition?.currentOverscrollSpec?.scene
-        val isOtherSceneOverscrolling = overscrollScene != null && overscrollScene != scene.key
+        val isOtherSceneOverscrolling = overscrollScene != null && overscrollScene != content.key
         val isNotPartOfAnyOngoingTransitions = transitions.isNotEmpty() && transition == null
         if (isNotPartOfAnyOngoingTransitions || isOtherSceneOverscrolling) {
             recursivelyClearPlacementValues()
-            sceneState.lastSize = Element.SizeUnspecified
+            stateInContent.lastSize = Element.SizeUnspecified
 
             val placeable = measurable.measure(constraints)
             return layout(placeable.width, placeable.height) { /* Do not place */ }
         }
 
         val placeable =
-            measure(layoutImpl, element, transition, sceneState, measurable, constraints)
-        sceneState.lastSize = placeable.size()
+            measure(layoutImpl, element, transition, stateInContent, measurable, constraints)
+        stateInContent.lastSize = placeable.size()
         return layout(placeable.width, placeable.height) { place(transition, placeable) }
     }
 
     private fun Placeable.PlacementScope.place(
-        transition: TransitionState.Transition?,
+        transition: ContentState.Transition<*>?,
         placeable: Placeable,
     ) {
         with(layoutImpl.lookaheadScope) {
             // Update the offset (relative to the SceneTransitionLayout) this element has in this
-            // scene when idle.
+            // content when idle.
             val coords =
                 coordinates ?: error("Element ${element.key} does not have any coordinates")
 
-            // No need to place the element in this scene if we don't want to draw it anyways.
-            if (!shouldPlaceElement(layoutImpl, scene.key, element, transition)) {
+            // No need to place the element in this content if we don't want to draw it anyways.
+            if (!shouldPlaceElement(layoutImpl, content.key, element, transition)) {
                 recursivelyClearPlacementValues()
                 return
             }
@@ -326,10 +331,10 @@
             val targetOffset =
                 computeValue(
                     layoutImpl,
-                    sceneState,
+                    stateInContent,
                     element,
                     transition,
-                    sceneValue = { it.targetOffset },
+                    contentValue = { it.targetOffset },
                     transformation = { it.offset },
                     currentValue = { currentOffset },
                     isSpecified = { it != Offset.Unspecified },
@@ -343,17 +348,17 @@
                     value = targetOffset,
                     unspecifiedValue = Offset.Unspecified,
                     zeroValue = Offset.Zero,
-                    getValueBeforeInterruption = { sceneState.offsetBeforeInterruption },
-                    setValueBeforeInterruption = { sceneState.offsetBeforeInterruption = it },
-                    getInterruptionDelta = { sceneState.offsetInterruptionDelta },
+                    getValueBeforeInterruption = { stateInContent.offsetBeforeInterruption },
+                    setValueBeforeInterruption = { stateInContent.offsetBeforeInterruption = it },
+                    getInterruptionDelta = { stateInContent.offsetInterruptionDelta },
                     setInterruptionDelta = { delta ->
                         setPlacementInterruptionDelta(
                             element = element,
-                            sceneState = sceneState,
+                            stateInContent = stateInContent,
                             transition = transition,
                             delta = delta,
-                            setter = { sceneState, delta ->
-                                sceneState.offsetInterruptionDelta = delta
+                            setter = { stateInContent, delta ->
+                                stateInContent.offsetInterruptionDelta = delta
                             },
                         )
                     },
@@ -361,14 +366,15 @@
                     add = { a, b, bProgress -> a + b * bProgress },
                 )
 
-            sceneState.lastOffset = interruptedOffset
+            stateInContent.lastOffset = interruptedOffset
 
             val offset = (interruptedOffset - currentOffset).round()
             if (
-                isElementOpaque(scene, element, transition) &&
-                    interruptedAlpha(layoutImpl, element, transition, sceneState, alpha = 1f) == 1f
+                isElementOpaque(content, element, transition) &&
+                    interruptedAlpha(layoutImpl, element, transition, stateInContent, alpha = 1f) ==
+                        1f
             ) {
-                sceneState.lastAlpha = 1f
+                stateInContent.lastAlpha = 1f
 
                 // TODO(b/291071158): Call placeWithLayer() if offset != IntOffset.Zero and size is
                 // not animated once b/305195729 is fixed. Test that drawing is not invalidated in
@@ -387,11 +393,11 @@
                     }
 
                     val transition = elementTransition(layoutImpl, element, currentTransitions)
-                    if (!shouldPlaceElement(layoutImpl, scene.key, element, transition)) {
+                    if (!shouldPlaceElement(layoutImpl, content.key, element, transition)) {
                         return@placeWithLayer
                     }
 
-                    alpha = elementAlpha(layoutImpl, element, transition, sceneState)
+                    alpha = elementAlpha(layoutImpl, element, transition, stateInContent)
                     compositingStrategy = CompositingStrategy.ModulateAlpha
                 }
             }
@@ -404,24 +410,24 @@
      * for the descendants for which approachMeasure() won't be called.
      */
     private fun recursivelyClearPlacementValues() {
-        fun Element.SceneState.clearLastPlacementValues() {
+        fun Element.State.clearLastPlacementValues() {
             lastOffset = Offset.Unspecified
             lastScale = Scale.Unspecified
             lastAlpha = Element.AlphaUnspecified
         }
 
-        sceneState.clearLastPlacementValues()
+        stateInContent.clearLastPlacementValues()
         traverseDescendants(ElementTraverseKey) { node ->
-            (node as ElementNode)._sceneState?.clearLastPlacementValues()
+            (node as ElementNode)._stateInContent?.clearLastPlacementValues()
             TraversableNode.Companion.TraverseDescendantsAction.ContinueTraversal
         }
     }
 
     override fun ContentDrawScope.draw() {
-        element.wasDrawnInAnyScene = true
+        element.wasDrawnInAnyContent = true
 
         val transition = elementTransition(layoutImpl, element, currentTransitions)
-        val drawScale = getDrawScale(layoutImpl, element, transition, sceneState)
+        val drawScale = getDrawScale(layoutImpl, element, transition, stateInContent)
         if (drawScale == Scale.Default) {
             drawContent()
         } else {
@@ -441,16 +447,21 @@
         private fun maybePruneMaps(
             layoutImpl: SceneTransitionLayoutImpl,
             element: Element,
-            sceneState: Element.SceneState,
+            stateInContent: Element.State,
         ) {
-            // If element is not composed from this scene anymore, remove the scene values. This
+            // If element is not composed in this content anymore, remove the content values. This
             // works because [onAttach] is called before [onDetach], so if an element is moved from
             // the UI tree we will first add the new code location then remove the old one.
-            if (sceneState.nodes.isEmpty() && element.sceneStates[sceneState.scene] == sceneState) {
-                element.sceneStates.remove(sceneState.scene)
+            if (
+                stateInContent.nodes.isEmpty() &&
+                    element.stateByContent[stateInContent.content] == stateInContent
+            ) {
+                element.stateByContent.remove(stateInContent.content)
 
-                // If the element is not composed in any scene, remove it from the elements map.
-                if (element.sceneStates.isEmpty() && layoutImpl.elements[element.key] == element) {
+                // If the element is not composed in any content, remove it from the elements map.
+                if (
+                    element.stateByContent.isEmpty() && layoutImpl.elements[element.key] == element
+                ) {
                     layoutImpl.elements.remove(element.key)
                 }
             }
@@ -460,16 +471,17 @@
 
 /**
  * The transition that we should consider for [element]. This is the last transition where one of
- * its scenes contains the element.
+ * its contents contains the element.
  */
 private fun elementTransition(
     layoutImpl: SceneTransitionLayoutImpl,
     element: Element,
     transitions: List<TransitionState.Transition>,
-): TransitionState.Transition? {
+): ContentState.Transition<*>? {
     val transition =
         transitions.fastLastOrNull { transition ->
-            transition.fromScene in element.sceneStates || transition.toScene in element.sceneStates
+            transition.fromScene in element.stateByContent ||
+                transition.toScene in element.stateByContent
         }
 
     val previousTransition = element.lastTransition
@@ -480,7 +492,7 @@
         prepareInterruption(layoutImpl, element, transition, previousTransition)
     } else if (transition == null && previousTransition != null) {
         // The transition was just finished.
-        element.sceneStates.values.forEach {
+        element.stateByContent.values.forEach {
             it.clearValuesBeforeInterruption()
             it.clearInterruptionDeltas()
         }
@@ -492,39 +504,39 @@
 private fun prepareInterruption(
     layoutImpl: SceneTransitionLayoutImpl,
     element: Element,
-    transition: TransitionState.Transition,
-    previousTransition: TransitionState.Transition,
+    transition: ContentState.Transition<*>,
+    previousTransition: ContentState.Transition<*>,
 ) {
     if (transition.replacedTransition == previousTransition) {
         return
     }
 
-    val sceneStates = element.sceneStates
-    fun updatedSceneState(key: SceneKey): Element.SceneState? {
-        return sceneStates[key]?.also { it.selfUpdateValuesBeforeInterruption() }
+    val stateByContent = element.stateByContent
+    fun updateStateInContent(key: ContentKey): Element.State? {
+        return stateByContent[key]?.also { it.selfUpdateValuesBeforeInterruption() }
     }
 
-    val previousFromState = updatedSceneState(previousTransition.fromScene)
-    val previousToState = updatedSceneState(previousTransition.toScene)
-    val fromState = updatedSceneState(transition.fromScene)
-    val toState = updatedSceneState(transition.toScene)
+    val previousFromState = updateStateInContent(previousTransition.fromContent)
+    val previousToState = updateStateInContent(previousTransition.toContent)
+    val fromState = updateStateInContent(transition.fromContent)
+    val toState = updateStateInContent(transition.toContent)
 
     reconcileStates(element, previousTransition)
     reconcileStates(element, transition)
 
-    // Remove the interruption values to all scenes but the scene(s) where the element will be
+    // Remove the interruption values to all contents but the content(s) where the element will be
     // placed, to make sure that interruption deltas are computed only right after this interruption
     // is prepared.
-    fun cleanInterruptionValues(sceneState: Element.SceneState) {
-        sceneState.sizeInterruptionDelta = IntSize.Zero
-        sceneState.offsetInterruptionDelta = Offset.Zero
-        sceneState.alphaInterruptionDelta = 0f
-        sceneState.scaleInterruptionDelta = Scale.Zero
+    fun cleanInterruptionValues(stateInContent: Element.State) {
+        stateInContent.sizeInterruptionDelta = IntSize.Zero
+        stateInContent.offsetInterruptionDelta = Offset.Zero
+        stateInContent.alphaInterruptionDelta = 0f
+        stateInContent.scaleInterruptionDelta = Scale.Zero
 
-        if (!shouldPlaceElement(layoutImpl, sceneState.scene, element, transition)) {
-            sceneState.offsetBeforeInterruption = Offset.Unspecified
-            sceneState.alphaBeforeInterruption = Element.AlphaUnspecified
-            sceneState.scaleBeforeInterruption = Scale.Unspecified
+        if (!shouldPlaceElement(layoutImpl, stateInContent.content, element, transition)) {
+            stateInContent.offsetBeforeInterruption = Offset.Unspecified
+            stateInContent.alphaBeforeInterruption = Element.AlphaUnspecified
+            stateInContent.scaleBeforeInterruption = Scale.Unspecified
         }
     }
 
@@ -535,35 +547,35 @@
 }
 
 /**
- * Reconcile the state of [element] in the fromScene and toScene of [transition] so that the values
- * before interruption have their expected values, taking shared transitions into account.
+ * Reconcile the state of [element] in the formContent and toContent of [transition] so that the
+ * values before interruption have their expected values, taking shared transitions into account.
  */
 private fun reconcileStates(
     element: Element,
-    transition: TransitionState.Transition,
+    transition: ContentState.Transition<*>,
 ) {
-    val fromSceneState = element.sceneStates[transition.fromScene] ?: return
-    val toSceneState = element.sceneStates[transition.toScene] ?: return
+    val fromContentState = element.stateByContent[transition.fromContent] ?: return
+    val toContentState = element.stateByContent[transition.toContent] ?: return
     if (!isSharedElementEnabled(element.key, transition)) {
         return
     }
 
     if (
-        fromSceneState.offsetBeforeInterruption != Offset.Unspecified &&
-            toSceneState.offsetBeforeInterruption == Offset.Unspecified
+        fromContentState.offsetBeforeInterruption != Offset.Unspecified &&
+            toContentState.offsetBeforeInterruption == Offset.Unspecified
     ) {
-        // Element is shared and placed in fromScene only.
-        toSceneState.updateValuesBeforeInterruption(fromSceneState)
+        // Element is shared and placed in fromContent only.
+        toContentState.updateValuesBeforeInterruption(fromContentState)
     } else if (
-        toSceneState.offsetBeforeInterruption != Offset.Unspecified &&
-            fromSceneState.offsetBeforeInterruption == Offset.Unspecified
+        toContentState.offsetBeforeInterruption != Offset.Unspecified &&
+            fromContentState.offsetBeforeInterruption == Offset.Unspecified
     ) {
-        // Element is shared and placed in toScene only.
-        fromSceneState.updateValuesBeforeInterruption(toSceneState)
+        // Element is shared and placed in toContent only.
+        fromContentState.updateValuesBeforeInterruption(toContentState)
     }
 }
 
-private fun Element.SceneState.selfUpdateValuesBeforeInterruption() {
+private fun Element.State.selfUpdateValuesBeforeInterruption() {
     sizeBeforeInterruption = lastSize
 
     if (lastAlpha > 0f) {
@@ -571,7 +583,7 @@
         scaleBeforeInterruption = lastScale
         alphaBeforeInterruption = lastAlpha
     } else {
-        // Consider the element as not placed in this scene if it was fully transparent.
+        // Consider the element as not placed in this content if it was fully transparent.
         // TODO(b/290930950): Look into using derived state inside place() instead to not even place
         // the element at all when alpha == 0f.
         offsetBeforeInterruption = Offset.Unspecified
@@ -580,7 +592,7 @@
     }
 }
 
-private fun Element.SceneState.updateValuesBeforeInterruption(lastState: Element.SceneState) {
+private fun Element.State.updateValuesBeforeInterruption(lastState: Element.State) {
     offsetBeforeInterruption = lastState.offsetBeforeInterruption
     sizeBeforeInterruption = lastState.sizeBeforeInterruption
     scaleBeforeInterruption = lastState.scaleBeforeInterruption
@@ -589,14 +601,14 @@
     clearInterruptionDeltas()
 }
 
-private fun Element.SceneState.clearInterruptionDeltas() {
+private fun Element.State.clearInterruptionDeltas() {
     offsetInterruptionDelta = Offset.Zero
     sizeInterruptionDelta = IntSize.Zero
     scaleInterruptionDelta = Scale.Zero
     alphaInterruptionDelta = 0f
 }
 
-private fun Element.SceneState.clearValuesBeforeInterruption() {
+private fun Element.State.clearValuesBeforeInterruption() {
     offsetBeforeInterruption = Offset.Unspecified
     scaleBeforeInterruption = Scale.Unspecified
     alphaBeforeInterruption = Element.AlphaUnspecified
@@ -604,12 +616,12 @@
 
 /**
  * Compute what [value] should be if we take the
- * [interruption progress][TransitionState.Transition.interruptionProgress] of [transition] into
+ * [interruption progress][ContentState.Transition.interruptionProgress] of [transition] into
  * account.
  */
 private inline fun <T> computeInterruptedValue(
     layoutImpl: SceneTransitionLayoutImpl,
-    transition: TransitionState.Transition?,
+    transition: ContentState.Transition<*>?,
     value: T,
     unspecifiedValue: T,
     zeroValue: T,
@@ -650,53 +662,55 @@
 
 /**
  * Set the interruption delta of a *placement/drawing*-related value (offset, alpha, scale). This
- * ensures that the delta is also set on the other scene in the transition for shared elements, so
- * that there is no jump cut if the scene where the element is placed has changed.
+ * ensures that the delta is also set on the other content in the transition for shared elements, so
+ * that there is no jump cut if the content where the element is placed has changed.
  */
 private inline fun <T> setPlacementInterruptionDelta(
     element: Element,
-    sceneState: Element.SceneState,
-    transition: TransitionState.Transition?,
+    stateInContent: Element.State,
+    transition: ContentState.Transition<*>?,
     delta: T,
-    setter: (Element.SceneState, T) -> Unit,
+    setter: (Element.State, T) -> Unit,
 ) {
-    // Set the interruption delta on the current scene.
-    setter(sceneState, delta)
+    // Set the interruption delta on the current content.
+    setter(stateInContent, delta)
 
     if (transition == null) {
         return
     }
 
-    // If the element is shared, also set the delta on the other scene so that it is used by that
-    // scene if we start overscrolling it and change the scene where the element is placed.
-    val otherScene =
-        if (sceneState.scene == transition.fromScene) transition.toScene else transition.fromScene
-    val otherSceneState = element.sceneStates[otherScene] ?: return
+    // If the element is shared, also set the delta on the other content so that it is used by that
+    // content if we start overscrolling it and change the content where the element is placed.
+    val otherContent =
+        if (stateInContent.content == transition.fromContent) transition.toContent
+        else transition.fromContent
+    val otherContentState = element.stateByContent[otherContent] ?: return
     if (isSharedElementEnabled(element.key, transition)) {
-        setter(otherSceneState, delta)
+        setter(otherContentState, delta)
     }
 }
 
 private fun shouldPlaceElement(
     layoutImpl: SceneTransitionLayoutImpl,
-    scene: SceneKey,
+    content: ContentKey,
     element: Element,
-    transition: TransitionState.Transition?,
+    transition: ContentState.Transition<*>?,
 ): Boolean {
     // Always place the element if we are idle.
     if (transition == null) {
         return true
     }
 
-    // Don't place the element in this scene if this scene is not part of the current element
+    // Don't place the element in this content if this content is not part of the current element
     // transition.
-    if (scene != transition.fromScene && scene != transition.toScene) {
+    if (content != transition.fromContent && content != transition.toContent) {
         return false
     }
 
     // Place the element if it is not shared.
     if (
-        transition.fromScene !in element.sceneStates || transition.toScene !in element.sceneStates
+        transition.fromContent !in element.stateByContent ||
+            transition.toContent !in element.stateByContent
     ) {
         return true
     }
@@ -708,7 +722,7 @@
 
     return shouldPlaceOrComposeSharedElement(
         layoutImpl,
-        scene,
+        content,
         element.key,
         transition,
     )
@@ -716,55 +730,57 @@
 
 internal fun shouldPlaceOrComposeSharedElement(
     layoutImpl: SceneTransitionLayoutImpl,
-    scene: SceneKey,
+    content: ContentKey,
     element: ElementKey,
-    transition: TransitionState.Transition,
+    transition: ContentState.Transition<*>,
 ): Boolean {
     // If we are overscrolling, only place/compose the element in the overscrolling scene.
     val overscrollScene = transition.currentOverscrollSpec?.scene
     if (overscrollScene != null) {
-        return scene == overscrollScene
+        return content == overscrollScene
     }
 
-    val scenePicker = element.scenePicker
-    val fromScene = transition.fromScene
-    val toScene = transition.toScene
-
+    val scenePicker = element.contentPicker
     val pickedScene =
-        scenePicker.sceneDuringTransition(
-            element = element,
-            transition = transition,
-            fromSceneZIndex = layoutImpl.scenes.getValue(fromScene).zIndex,
-            toSceneZIndex = layoutImpl.scenes.getValue(toScene).zIndex,
-        ) ?: return false
+        when (transition) {
+            is TransitionState.Transition -> {
+                scenePicker.contentDuringTransition(
+                    element = element,
+                    transition = transition,
+                    fromContentZIndex = layoutImpl.scene(transition.fromScene).zIndex,
+                    toContentZIndex = layoutImpl.scene(transition.toScene).zIndex,
+                )
+            }
+        }
 
-    return pickedScene == scene
+    return pickedScene == content
 }
 
 private fun isSharedElementEnabled(
     element: ElementKey,
-    transition: TransitionState.Transition,
+    transition: ContentState.Transition<*>,
 ): Boolean {
     return sharedElementTransformation(element, transition)?.enabled ?: true
 }
 
 internal fun sharedElementTransformation(
     element: ElementKey,
-    transition: TransitionState.Transition,
+    transition: ContentState.Transition<*>,
 ): SharedElementTransformation? {
     val transformationSpec = transition.transformationSpec
-    val sharedInFromScene = transformationSpec.transformations(element, transition.fromScene).shared
-    val sharedInToScene = transformationSpec.transformations(element, transition.toScene).shared
+    val sharedInFromContent =
+        transformationSpec.transformations(element, transition.fromContent).shared
+    val sharedInToContent = transformationSpec.transformations(element, transition.toContent).shared
 
-    // The sharedElement() transformation must either be null or be the same in both scenes.
-    if (sharedInFromScene != sharedInToScene) {
+    // The sharedElement() transformation must either be null or be the same in both contents.
+    if (sharedInFromContent != sharedInToContent) {
         error(
-            "Different sharedElement() transformations matched $element (from=$sharedInFromScene " +
-                "to=$sharedInToScene)"
+            "Different sharedElement() transformations matched $element " +
+                "(from=$sharedInFromContent to=$sharedInToContent)"
         )
     }
 
-    return sharedInFromScene
+    return sharedInFromContent
 }
 
 /**
@@ -775,18 +791,16 @@
  * placement and we don't want to read the transition progress in that phase.
  */
 private fun isElementOpaque(
-    scene: Scene,
+    content: Content,
     element: Element,
-    transition: TransitionState.Transition?,
+    transition: ContentState.Transition<*>?,
 ): Boolean {
     if (transition == null) {
         return true
     }
 
-    val fromScene = transition.fromScene
-    val toScene = transition.toScene
-    val fromState = element.sceneStates[fromScene]
-    val toState = element.sceneStates[toScene]
+    val fromState = element.stateByContent[transition.fromContent]
+    val toState = element.stateByContent[transition.toContent]
 
     if (fromState == null && toState == null) {
         // TODO(b/311600838): Throw an exception instead once layers of disposed elements are not
@@ -799,7 +813,7 @@
         return true
     }
 
-    return transition.transformationSpec.transformations(element.key, scene.key).alpha == null
+    return transition.transformationSpec.transformations(element.key, content.key).alpha == null
 }
 
 /**
@@ -813,16 +827,16 @@
 private fun elementAlpha(
     layoutImpl: SceneTransitionLayoutImpl,
     element: Element,
-    transition: TransitionState.Transition?,
-    sceneState: Element.SceneState,
+    transition: ContentState.Transition<*>?,
+    stateInContent: Element.State,
 ): Float {
     val alpha =
         computeValue(
                 layoutImpl,
-                sceneState,
+                stateInContent,
                 element,
                 transition,
-                sceneValue = { 1f },
+                contentValue = { 1f },
                 transformation = { it.alpha },
                 currentValue = { 1f },
                 isSpecified = { true },
@@ -832,20 +846,20 @@
 
     // If the element is fading during this transition and that it is drawn for the first time, make
     // sure that it doesn't instantly appear on screen.
-    if (!element.wasDrawnInAnyScene && alpha > 0f) {
-        element.sceneStates.forEach { it.value.alphaBeforeInterruption = 0f }
+    if (!element.wasDrawnInAnyContent && alpha > 0f) {
+        element.stateByContent.forEach { it.value.alphaBeforeInterruption = 0f }
     }
 
-    val interruptedAlpha = interruptedAlpha(layoutImpl, element, transition, sceneState, alpha)
-    sceneState.lastAlpha = interruptedAlpha
+    val interruptedAlpha = interruptedAlpha(layoutImpl, element, transition, stateInContent, alpha)
+    stateInContent.lastAlpha = interruptedAlpha
     return interruptedAlpha
 }
 
 private fun interruptedAlpha(
     layoutImpl: SceneTransitionLayoutImpl,
     element: Element,
-    transition: TransitionState.Transition?,
-    sceneState: Element.SceneState,
+    transition: ContentState.Transition<*>?,
+    stateInContent: Element.State,
     alpha: Float,
 ): Float {
     return computeInterruptedValue(
@@ -854,16 +868,16 @@
         value = alpha,
         unspecifiedValue = Element.AlphaUnspecified,
         zeroValue = 0f,
-        getValueBeforeInterruption = { sceneState.alphaBeforeInterruption },
-        setValueBeforeInterruption = { sceneState.alphaBeforeInterruption = it },
-        getInterruptionDelta = { sceneState.alphaInterruptionDelta },
+        getValueBeforeInterruption = { stateInContent.alphaBeforeInterruption },
+        setValueBeforeInterruption = { stateInContent.alphaBeforeInterruption = it },
+        getInterruptionDelta = { stateInContent.alphaInterruptionDelta },
         setInterruptionDelta = { delta ->
             setPlacementInterruptionDelta(
                 element = element,
-                sceneState = sceneState,
+                stateInContent = stateInContent,
                 transition = transition,
                 delta = delta,
-                setter = { sceneState, delta -> sceneState.alphaInterruptionDelta = delta },
+                setter = { stateInContent, delta -> stateInContent.alphaInterruptionDelta = delta },
             )
         },
         diff = { a, b -> a - b },
@@ -874,8 +888,8 @@
 private fun measure(
     layoutImpl: SceneTransitionLayoutImpl,
     element: Element,
-    transition: TransitionState.Transition?,
-    sceneState: Element.SceneState,
+    transition: ContentState.Transition<*>?,
+    stateInContent: Element.State,
     measurable: Measurable,
     constraints: Constraints,
 ): Placeable {
@@ -887,10 +901,10 @@
     val targetSize =
         computeValue(
             layoutImpl,
-            sceneState,
+            stateInContent,
             element,
             transition,
-            sceneValue = { it.targetSize },
+            contentValue = { it.targetSize },
             transformation = { it.size },
             currentValue = { measurable.measure(constraints).also { maybePlaceable = it }.size() },
             isSpecified = { it != Element.SizeUnspecified },
@@ -900,8 +914,8 @@
     // The measurable was already measured, so we can't take interruptions into account here given
     // that we are not allowed to measure the same measurable twice.
     maybePlaceable?.let { placeable ->
-        sceneState.sizeBeforeInterruption = Element.SizeUnspecified
-        sceneState.sizeInterruptionDelta = IntSize.Zero
+        stateInContent.sizeBeforeInterruption = Element.SizeUnspecified
+        stateInContent.sizeInterruptionDelta = IntSize.Zero
         return placeable
     }
 
@@ -912,10 +926,10 @@
             value = targetSize,
             unspecifiedValue = Element.SizeUnspecified,
             zeroValue = IntSize.Zero,
-            getValueBeforeInterruption = { sceneState.sizeBeforeInterruption },
-            setValueBeforeInterruption = { sceneState.sizeBeforeInterruption = it },
-            getInterruptionDelta = { sceneState.sizeInterruptionDelta },
-            setInterruptionDelta = { sceneState.sizeInterruptionDelta = it },
+            getValueBeforeInterruption = { stateInContent.sizeBeforeInterruption },
+            setValueBeforeInterruption = { stateInContent.sizeBeforeInterruption = it },
+            getInterruptionDelta = { stateInContent.sizeInterruptionDelta },
+            setInterruptionDelta = { stateInContent.sizeInterruptionDelta = it },
             diff = { a, b -> IntSize(a.width - b.width, a.height - b.height) },
             add = { a, b, bProgress ->
                 IntSize(
@@ -938,16 +952,16 @@
 private fun ContentDrawScope.getDrawScale(
     layoutImpl: SceneTransitionLayoutImpl,
     element: Element,
-    transition: TransitionState.Transition?,
-    sceneState: Element.SceneState,
+    transition: ContentState.Transition<*>?,
+    stateInContent: Element.State,
 ): Scale {
     val scale =
         computeValue(
             layoutImpl,
-            sceneState,
+            stateInContent,
             element,
             transition,
-            sceneValue = { Scale.Default },
+            contentValue = { Scale.Default },
             transformation = { it.drawScale },
             currentValue = { Scale.Default },
             isSpecified = { true },
@@ -965,16 +979,18 @@
             value = scale,
             unspecifiedValue = Scale.Unspecified,
             zeroValue = Scale.Zero,
-            getValueBeforeInterruption = { sceneState.scaleBeforeInterruption },
-            setValueBeforeInterruption = { sceneState.scaleBeforeInterruption = it },
-            getInterruptionDelta = { sceneState.scaleInterruptionDelta },
+            getValueBeforeInterruption = { stateInContent.scaleBeforeInterruption },
+            setValueBeforeInterruption = { stateInContent.scaleBeforeInterruption = it },
+            getInterruptionDelta = { stateInContent.scaleInterruptionDelta },
             setInterruptionDelta = { delta ->
                 setPlacementInterruptionDelta(
                     element = element,
-                    sceneState = sceneState,
+                    stateInContent = stateInContent,
                     transition = transition,
                     delta = delta,
-                    setter = { sceneState, delta -> sceneState.scaleInterruptionDelta = delta },
+                    setter = { stateInContent, delta ->
+                        stateInContent.scaleInterruptionDelta = delta
+                    },
                 )
             },
             diff = { a, b ->
@@ -1003,7 +1019,7 @@
             }
         )
 
-    sceneState.lastScale = interruptedScale
+    stateInContent.lastScale = interruptedScale
     return interruptedScale
 }
 
@@ -1015,11 +1031,11 @@
  * Measurable.
  *
  * @param layoutImpl the [SceneTransitionLayoutImpl] associated to [element].
- * @param currentSceneState the scene state of the scene for which we are computing the value. Note
- *   that during interruptions, this could be the state of a scene that is neither
- *   [transition.toScene] nor [transition.fromScene].
+ * @param currentContentState the content state of the content for which we are computing the value.
+ *   Note that during interruptions, this could be the state of a content that is neither
+ *   [transition.toContent] nor [transition.fromContent].
  * @param element the element being animated.
- * @param sceneValue the value being animated.
+ * @param contentValue the value being animated.
  * @param transformation the transformation associated to the value being animated.
  * @param currentValue the value that would be used if it is not transformed. Note that this is
  *   different than [idleValue] even if the value is not transformed directly because it could be
@@ -1030,10 +1046,10 @@
  */
 private inline fun <T> computeValue(
     layoutImpl: SceneTransitionLayoutImpl,
-    currentSceneState: Element.SceneState,
+    currentContentState: Element.State,
     element: Element,
-    transition: TransitionState.Transition?,
-    sceneValue: (Element.SceneState) -> T,
+    transition: ContentState.Transition<*>?,
+    contentValue: (Element.State) -> T,
     transformation: (ElementTransformations) -> PropertyTransformation<T>?,
     currentValue: () -> T,
     isSpecified: (T) -> Boolean,
@@ -1047,31 +1063,32 @@
         return currentValue()
     }
 
-    val fromScene = transition.fromScene
-    val toScene = transition.toScene
+    val fromContent = transition.fromContent
+    val toContent = transition.toContent
 
-    val fromState = element.sceneStates[fromScene]
-    val toState = element.sceneStates[toScene]
+    val fromState = element.stateByContent[fromContent]
+    val toState = element.stateByContent[toContent]
 
     if (fromState == null && toState == null) {
         // TODO(b/311600838): Throw an exception instead once layers of disposed elements are not
         // run anymore.
-        return sceneValue(currentSceneState)
+        return contentValue(currentContentState)
     }
 
-    val currentScene = currentSceneState.scene
-    if (transition is TransitionState.HasOverscrollProperties) {
+    val currentContent = currentContentState.content
+    if (transition is ContentState.HasOverscrollProperties) {
         val overscroll = transition.currentOverscrollSpec
-        if (overscroll?.scene == currentScene) {
+        if (overscroll?.scene == currentContent) {
             val elementSpec =
-                overscroll.transformationSpec.transformations(element.key, currentScene)
+                overscroll.transformationSpec.transformations(element.key, currentContent)
             val propertySpec = transformation(elementSpec) ?: return currentValue()
-            val overscrollState = checkNotNull(if (currentScene == toScene) toState else fromState)
-            val idleValue = sceneValue(overscrollState)
+            val overscrollState =
+                checkNotNull(if (currentContent == toContent) toState else fromState)
+            val idleValue = contentValue(overscrollState)
             val targetValue =
                 propertySpec.transform(
                     layoutImpl,
-                    currentScene,
+                    currentContent,
                     element,
                     overscrollState,
                     transition,
@@ -1087,8 +1104,8 @@
             // TODO(b/290184746): Make sure that we don't overflow transformations associated to a
             // range.
             val directionSign = if (transition.isUpOrLeft) -1 else 1
-            val isToScene = overscroll.scene == transition.toScene
-            val linearProgress = transition.progress.let { if (isToScene) it - 1f else it }
+            val isToContent = overscroll.scene == transition.toContent
+            val linearProgress = transition.progress.let { if (isToContent) it - 1f else it }
             val progress = directionSign * overscroll.progressConverter(linearProgress)
             val rangeProgress = propertySpec.range?.progress(progress) ?: progress
 
@@ -1097,13 +1114,14 @@
         }
     }
 
-    // The element is shared: interpolate between the value in fromScene and the value in toScene.
+    // The element is shared: interpolate between the value in fromContent and the value in
+    // toContent.
     // TODO(b/290184746): Support non linear shared paths as well as a way to make sure that shared
     // elements follow the finger direction.
     val isSharedElement = fromState != null && toState != null
     if (isSharedElement && isSharedElementEnabled(element.key, transition)) {
-        val start = sceneValue(fromState!!)
-        val end = sceneValue(toState!!)
+        val start = contentValue(fromState!!)
+        val end = contentValue(toState!!)
 
         // TODO(b/316901148): Remove checks to isSpecified() once the lookahead pass runs for all
         // nodes before the intermediate layout pass.
@@ -1117,34 +1135,122 @@
 
     // Get the transformed value, i.e. the target value at the beginning (for entering elements) or
     // end (for leaving elements) of the transition.
-    val sceneState =
+    val contentState =
         checkNotNull(
             when {
-                isSharedElement && currentScene == fromScene -> fromState
+                isSharedElement && currentContent == fromContent -> fromState
                 isSharedElement -> toState
                 else -> fromState ?: toState
             }
         )
 
-    // The scene for which we compute the transformation. Note that this is not necessarily
-    // [currentScene] because [currentScene] could be a different scene than the transition
-    // fromScene or toScene during interruptions.
-    val scene = sceneState.scene
+    // The content for which we compute the transformation. Note that this is not necessarily
+    // [currentContent] because [currentContent] could be a different content than the transition
+    // fromContent or toContent during interruptions.
+    val content = contentState.content
 
     val transformation =
-        transformation(transition.transformationSpec.transformations(element.key, scene))
-            // If there is no transformation explicitly associated to this element value, let's use
-            // the value given by the system (like the current position and size given by the layout
-            // pass).
-            ?: return currentValue()
+        transformation(transition.transformationSpec.transformations(element.key, content))
 
-    val idleValue = sceneValue(sceneState)
+    val previewTransformation =
+        transition.previewTransformationSpec?.let {
+            transformation(it.transformations(element.key, content))
+        }
+    if (previewTransformation != null) {
+        val isInPreviewStage = transition.isInPreviewStage
+
+        val idleValue = contentValue(contentState)
+        val isEntering = content == toContent
+        val previewTargetValue =
+            previewTransformation.transform(
+                layoutImpl,
+                content,
+                element,
+                contentState,
+                transition,
+                idleValue,
+            )
+
+        val targetValueOrNull =
+            transformation?.transform(
+                layoutImpl,
+                content,
+                element,
+                contentState,
+                transition,
+                idleValue,
+            )
+
+        // Make sure we don't read progress if values are the same and we don't need to interpolate,
+        // so we don't invalidate the phase where this is read.
+        when {
+            isInPreviewStage && isEntering && previewTargetValue == targetValueOrNull ->
+                return previewTargetValue
+            isInPreviewStage && !isEntering && idleValue == previewTargetValue -> return idleValue
+            previewTargetValue == targetValueOrNull && idleValue == previewTargetValue ->
+                return idleValue
+            else -> {}
+        }
+
+        val previewProgress = transition.previewProgress
+        // progress is not needed for all cases of the below when block, therefore read it lazily
+        // TODO(b/290184746): Make sure that we don't overflow transformations associated to a range
+        val previewRangeProgress =
+            previewTransformation.range?.progress(previewProgress) ?: previewProgress
+
+        if (isInPreviewStage) {
+            // if we're in the preview stage of the transition, interpolate between start state and
+            // preview target state:
+            return if (isEntering) {
+                // i.e. in the entering case between previewTargetValue and targetValue (or
+                // idleValue if no transformation is defined in the second stage transition)...
+                lerp(previewTargetValue, targetValueOrNull ?: idleValue, previewRangeProgress)
+            } else {
+                // ...and in the exiting case between the idleValue and the previewTargetValue.
+                lerp(idleValue, previewTargetValue, previewRangeProgress)
+            }
+        }
+
+        // if we're in the second stage of the transition, interpolate between the state the
+        // element was left at the end of the preview-phase and the target state:
+        return if (isEntering) {
+            // i.e. in the entering case between preview-end-state and the idleValue...
+            lerp(
+                lerp(previewTargetValue, targetValueOrNull ?: idleValue, previewRangeProgress),
+                idleValue,
+                transformation?.range?.progress(transition.progress) ?: transition.progress
+            )
+        } else {
+            if (targetValueOrNull == null) {
+                // ... and in the exiting case, the element should remain in the preview-end-state
+                // if no further transformation is defined in the second-stage transition...
+                lerp(idleValue, previewTargetValue, previewRangeProgress)
+            } else {
+                // ...and otherwise it should be interpolated between preview-end-state and
+                // targetValue
+                lerp(
+                    lerp(idleValue, previewTargetValue, previewRangeProgress),
+                    targetValueOrNull,
+                    transformation.range?.progress(transition.progress) ?: transition.progress
+                )
+            }
+        }
+    }
+
+    if (transformation == null) {
+        // If there is no transformation explicitly associated to this element value, let's use
+        // the value given by the system (like the current position and size given by the layout
+        // pass).
+        return currentValue()
+    }
+
+    val idleValue = contentValue(contentState)
     val targetValue =
         transformation.transform(
             layoutImpl,
-            scene,
+            content,
             element,
-            sceneState,
+            contentState,
             transition,
             idleValue,
         )
@@ -1160,7 +1266,7 @@
     val rangeProgress = transformation.range?.progress(progress) ?: progress
 
     // Interpolate between the value at rest and the value before entering/after leaving.
-    val isEntering = scene == toScene
+    val isEntering = content == toContent
     return if (isEntering) {
         lerp(targetValue, idleValue, rangeProgress)
     } else {
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ElementMatcher.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ElementMatcher.kt
index 98dbb67..ca68c25 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ElementMatcher.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ElementMatcher.kt
@@ -18,20 +18,23 @@
 
 /** An interface to match one or more elements. */
 interface ElementMatcher {
-    /** Whether the element with key [key] in scene [scene] matches this matcher. */
-    fun matches(key: ElementKey, scene: SceneKey): Boolean
+    /** Whether the element with key [key] in scene [content] matches this matcher. */
+    fun matches(key: ElementKey, content: ContentKey): Boolean
 }
 
 /**
- * Returns an [ElementMatcher] that matches elements in [scene] also matching [this]
+ * Returns an [ElementMatcher] that matches elements in [content] also matching [this]
  * [ElementMatcher].
  */
-fun ElementMatcher.inScene(scene: SceneKey): ElementMatcher {
+fun ElementMatcher.inContent(content: ContentKey): ElementMatcher {
     val delegate = this
-    val matcherScene = scene
+    val matcherScene = content
     return object : ElementMatcher {
-        override fun matches(key: ElementKey, scene: SceneKey): Boolean {
-            return scene == matcherScene && delegate.matches(key, scene)
+        override fun matches(key: ElementKey, content: ContentKey): Boolean {
+            return content == matcherScene && delegate.matches(key, content)
         }
     }
 }
+
+@Deprecated("Use inContent() instead", replaceWith = ReplaceWith("inContent(scene)"))
+fun ElementMatcher.inScene(scene: SceneKey) = inContent(scene)
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/InterruptionHandler.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/InterruptionHandler.kt
index 54c64fd..bf70ca9 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/InterruptionHandler.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/InterruptionHandler.kt
@@ -16,6 +16,8 @@
 
 package com.android.compose.animation.scene
 
+import com.android.compose.animation.scene.content.state.TransitionState
+
 /**
  * A handler to specify how a transition should be interrupted.
  *
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Key.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Key.kt
index 9770399..3cd5553 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Key.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Key.kt
@@ -40,15 +40,20 @@
     }
 }
 
+/** The key for a content (scene or overlay). */
+sealed class ContentKey(debugName: String, identity: Any) : Key(debugName, identity) {
+    @VisibleForTesting
+    // TODO(b/240432457): Make internal once PlatformComposeSceneTransitionLayoutTestsUtils can
+    // access internal members.
+    abstract val testTag: String
+}
+
 /** Key for a scene. */
 class SceneKey(
     debugName: String,
     identity: Any = Object(),
-) : Key(debugName, identity) {
-    @VisibleForTesting
-    // TODO(b/240432457): Make internal once PlatformComposeSceneTransitionLayoutTestsUtils can
-    // access internal members.
-    val testTag: String = "scene:$debugName"
+) : ContentKey(debugName, identity) {
+    override val testTag: String = "scene:$debugName"
 
     /** The unique [ElementKey] identifying this scene's root element. */
     val rootElementKey = ElementKey(debugName, identity)
@@ -59,22 +64,22 @@
 }
 
 /** Key for an element. */
-class ElementKey(
+open class ElementKey(
     debugName: String,
     identity: Any = Object(),
 
     /**
-     * The [ElementScenePicker] to use when deciding in which scene we should draw shared Elements
+     * The [ElementContentPicker] to use when deciding in which scene we should draw shared Elements
      * or compose MovableElements.
      */
-    val scenePicker: ElementScenePicker = DefaultElementScenePicker,
+    open val contentPicker: ElementContentPicker = DefaultElementContentPicker,
 ) : Key(debugName, identity), ElementMatcher {
     @VisibleForTesting
     // TODO(b/240432457): Make internal once PlatformComposeSceneTransitionLayoutTestsUtils can
     // access internal members.
     val testTag: String = "element:$debugName"
 
-    override fun matches(key: ElementKey, scene: SceneKey): Boolean {
+    override fun matches(key: ElementKey, content: ContentKey): Boolean {
         return key == this
     }
 
@@ -86,7 +91,7 @@
         /** Matches any element whose [key identity][ElementKey.identity] matches [predicate]. */
         fun withIdentity(predicate: (Any) -> Boolean): ElementMatcher {
             return object : ElementMatcher {
-                override fun matches(key: ElementKey, scene: SceneKey): Boolean {
+                override fun matches(key: ElementKey, content: ContentKey): Boolean {
                     return predicate(key.identity)
                 }
             }
@@ -94,6 +99,32 @@
     }
 }
 
+/** Key for a movable element. */
+class MovableElementKey(
+    debugName: String,
+
+    /**
+     * The [StaticElementContentPicker] to use when deciding in which scene we should draw shared
+     * Elements or compose MovableElements.
+     *
+     * @see MovableElementContentPicker
+     */
+    override val contentPicker: StaticElementContentPicker,
+    identity: Any = Object(),
+) : ElementKey(debugName, identity, contentPicker) {
+    constructor(
+        debugName: String,
+
+        /** The exhaustive list of contents (scenes or overlays) that can contain this element. */
+        contents: Set<ContentKey>,
+        identity: Any = Object(),
+    ) : this(debugName, MovableElementContentPicker(contents), identity)
+
+    override fun toString(): String {
+        return "MovableElementKey(debugName=$debugName)"
+    }
+}
+
 /** Key for a shared value of an element. */
 class ValueKey(debugName: String, identity: Any = Object()) : Key(debugName, identity) {
     override fun toString(): String {
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MovableElement.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MovableElement.kt
index 32eadde..abecdd7 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MovableElement.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MovableElement.kt
@@ -27,21 +27,23 @@
 import androidx.compose.ui.layout.Layout
 import androidx.compose.ui.unit.IntSize
 import androidx.compose.ui.util.fastLastOrNull
+import com.android.compose.animation.scene.content.Content
+import com.android.compose.animation.scene.content.state.TransitionState
 
 @Composable
 internal fun Element(
     layoutImpl: SceneTransitionLayoutImpl,
-    scene: Scene,
+    sceneOrOverlay: Content,
     key: ElementKey,
     modifier: Modifier,
     content: @Composable ElementScope<ElementContentScope>.() -> Unit,
 ) {
-    Box(modifier.element(layoutImpl, scene, key)) {
-        val sceneScope = scene.scope
+    Box(modifier.element(layoutImpl, sceneOrOverlay, key)) {
+        val contentScope = sceneOrOverlay.scope
         val boxScope = this
         val elementScope =
-            remember(layoutImpl, key, scene, sceneScope, boxScope) {
-                ElementScopeImpl(layoutImpl, key, scene, sceneScope, boxScope)
+            remember(layoutImpl, key, sceneOrOverlay, contentScope, boxScope) {
+                ElementScopeImpl(layoutImpl, key, sceneOrOverlay, contentScope, boxScope)
             }
 
         content(elementScope)
@@ -51,17 +53,17 @@
 @Composable
 internal fun MovableElement(
     layoutImpl: SceneTransitionLayoutImpl,
-    scene: Scene,
-    key: ElementKey,
+    sceneOrOverlay: Content,
+    key: MovableElementKey,
     modifier: Modifier,
     content: @Composable ElementScope<MovableElementContentScope>.() -> Unit,
 ) {
-    Box(modifier.element(layoutImpl, scene, key)) {
-        val sceneScope = scene.scope
+    Box(modifier.element(layoutImpl, sceneOrOverlay, key)) {
+        val contentScope = sceneOrOverlay.scope
         val boxScope = this
         val elementScope =
-            remember(layoutImpl, key, scene, sceneScope, boxScope) {
-                MovableElementScopeImpl(layoutImpl, key, scene, sceneScope, boxScope)
+            remember(layoutImpl, key, sceneOrOverlay, contentScope, boxScope) {
+                MovableElementScopeImpl(layoutImpl, key, sceneOrOverlay, contentScope, boxScope)
             }
 
         content(elementScope)
@@ -71,7 +73,7 @@
 private abstract class BaseElementScope<ContentScope>(
     private val layoutImpl: SceneTransitionLayoutImpl,
     private val element: ElementKey,
-    private val scene: Scene,
+    private val sceneOrOverlay: Content,
 ) : ElementScope<ContentScope> {
     @Composable
     override fun <T> animateElementValueAsState(
@@ -82,7 +84,7 @@
     ): AnimatedState<T> {
         return animateSharedValueAsState(
             layoutImpl,
-            scene.key,
+            sceneOrOverlay.key,
             element,
             key,
             value,
@@ -95,12 +97,12 @@
 private class ElementScopeImpl(
     layoutImpl: SceneTransitionLayoutImpl,
     element: ElementKey,
-    scene: Scene,
-    private val sceneScope: SceneScope,
+    content: Content,
+    private val delegateContentScope: ContentScope,
     private val boxScope: BoxScope,
-) : BaseElementScope<ElementContentScope>(layoutImpl, element, scene) {
+) : BaseElementScope<ElementContentScope>(layoutImpl, element, content) {
     private val contentScope =
-        object : ElementContentScope, SceneScope by sceneScope, BoxScope by boxScope {}
+        object : ElementContentScope, ContentScope by delegateContentScope, BoxScope by boxScope {}
 
     @Composable
     override fun content(content: @Composable ElementContentScope.() -> Unit) {
@@ -110,13 +112,16 @@
 
 private class MovableElementScopeImpl(
     private val layoutImpl: SceneTransitionLayoutImpl,
-    private val element: ElementKey,
-    private val scene: Scene,
-    private val sceneScope: BaseSceneScope,
+    private val element: MovableElementKey,
+    private val content: Content,
+    private val baseContentScope: BaseContentScope,
     private val boxScope: BoxScope,
-) : BaseElementScope<MovableElementContentScope>(layoutImpl, element, scene) {
+) : BaseElementScope<MovableElementContentScope>(layoutImpl, element, content) {
     private val contentScope =
-        object : MovableElementContentScope, BaseSceneScope by sceneScope, BoxScope by boxScope {}
+        object :
+            MovableElementContentScope,
+            BaseContentScope by baseContentScope,
+            BoxScope by boxScope {}
 
     @Composable
     override fun content(content: @Composable MovableElementContentScope.() -> Unit) {
@@ -126,9 +131,10 @@
         // during the transition.
         // TODO(b/317026105): Use derivedStateOf only if the scene picker reads the progress in its
         // logic.
+        val contentKey = this@MovableElementScopeImpl.content.key
         val shouldComposeMovableElement by
-            remember(layoutImpl, scene.key, element) {
-                derivedStateOf { shouldComposeMovableElement(layoutImpl, scene.key, element) }
+            remember(layoutImpl, contentKey, element) {
+                derivedStateOf { shouldComposeMovableElement(layoutImpl, contentKey, element) }
             }
 
         if (shouldComposeMovableElement) {
@@ -152,7 +158,7 @@
                 val size =
                     placeholderContentSize(
                         layoutImpl,
-                        scene.key,
+                        contentKey,
                         layoutImpl.elements.getValue(element),
                     )
                 layout(size.width, size.height) {}
@@ -163,33 +169,29 @@
 
 private fun shouldComposeMovableElement(
     layoutImpl: SceneTransitionLayoutImpl,
-    scene: SceneKey,
-    element: ElementKey,
+    content: ContentKey,
+    element: MovableElementKey,
 ): Boolean {
     val transitions = layoutImpl.state.currentTransitions
     if (transitions.isEmpty()) {
         // If we are idle, there is only one [scene] that is composed so we can compose our
         // movable content here. We still check that [scene] is equal to the current idle scene, to
         // make sure we only compose it there.
-        return layoutImpl.state.transitionState.currentScene == scene
+        return layoutImpl.state.transitionState.currentScene == content
     }
 
     // The current transition for this element is the last transition in which either fromScene or
     // toScene contains the element.
+    val contents = element.contentPicker.contents
     val transition =
         transitions.fastLastOrNull { transition ->
-            element.scenePicker.sceneDuringTransition(
-                element = element,
-                transition = transition,
-                fromSceneZIndex = layoutImpl.scenes.getValue(transition.fromScene).zIndex,
-                toSceneZIndex = layoutImpl.scenes.getValue(transition.toScene).zIndex,
-            ) != null
+            transition.fromScene in contents || transition.toScene in contents
         } ?: return false
 
     // Always compose movable elements in the scene picked by their scene picker.
     return shouldPlaceOrComposeSharedElement(
         layoutImpl,
-        scene,
+        content,
         element,
         transition,
     )
@@ -201,12 +203,12 @@
  */
 private fun placeholderContentSize(
     layoutImpl: SceneTransitionLayoutImpl,
-    scene: SceneKey,
+    content: ContentKey,
     element: Element,
 ): IntSize {
     // If the content of the movable element was already composed in this scene before, use that
     // target size.
-    val targetValueInScene = element.sceneStates.getValue(scene).targetSize
+    val targetValueInScene = element.stateByContent.getValue(content).targetSize
     if (targetValueInScene != Element.SizeUnspecified) {
         return targetValueInScene
     }
@@ -219,8 +221,9 @@
     // doesn't change between scenes.
     // TODO(b/317026105): Provide a way to give a hint size/content for cases where this is not
     // true.
-    val otherScene = if (transition.fromScene == scene) transition.toScene else transition.fromScene
-    val targetValueInOtherScene = element.sceneStates[otherScene]?.targetSize
+    val otherScene =
+        if (transition.fromScene == content) transition.toScene else transition.fromScene
+    val targetValueInOtherScene = element.stateByContent[otherScene]?.targetSize
     if (targetValueInOtherScene != null && targetValueInOtherScene != Element.SizeUnspecified) {
         return targetValueInOtherScene
     }
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 2b78b5a..b329534 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
@@ -16,12 +16,16 @@
 
 package com.android.compose.animation.scene
 
+import androidx.annotation.VisibleForTesting
 import androidx.compose.foundation.gestures.Orientation
 import androidx.compose.foundation.gestures.awaitHorizontalTouchSlopOrCancellation
 import androidx.compose.foundation.gestures.awaitVerticalTouchSlopOrCancellation
 import androidx.compose.runtime.Stable
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
+import androidx.compose.ui.input.nestedscroll.NestedScrollDispatcher
+import androidx.compose.ui.input.nestedscroll.NestedScrollSource
 import androidx.compose.ui.input.pointer.AwaitPointerEventScope
 import androidx.compose.ui.input.pointer.PointerEvent
 import androidx.compose.ui.input.pointer.PointerEventPass
@@ -36,13 +40,11 @@
 import androidx.compose.ui.input.pointer.util.VelocityTracker
 import androidx.compose.ui.input.pointer.util.addPointerInputChange
 import androidx.compose.ui.node.CompositionLocalConsumerModifierNode
-import androidx.compose.ui.node.DelegatableNode
 import androidx.compose.ui.node.DelegatingNode
 import androidx.compose.ui.node.ModifierNodeElement
 import androidx.compose.ui.node.ObserverModifierNode
 import androidx.compose.ui.node.PointerInputModifierNode
 import androidx.compose.ui.node.currentValueOf
-import androidx.compose.ui.node.findNearestAncestor
 import androidx.compose.ui.node.observeReads
 import androidx.compose.ui.platform.LocalViewConfiguration
 import androidx.compose.ui.unit.IntSize
@@ -51,6 +53,7 @@
 import androidx.compose.ui.util.fastAny
 import androidx.compose.ui.util.fastFirstOrNull
 import androidx.compose.ui.util.fastSumBy
+import com.android.compose.ui.util.SpaceVectorConverter
 import kotlin.coroutines.cancellation.CancellationException
 import kotlin.math.sign
 import kotlinx.coroutines.coroutineScope
@@ -71,6 +74,7 @@
  * dragged) and a second pointer is down and dragged. This is an implementation detail that might
  * change in the future.
  */
+@VisibleForTesting
 @Stable
 internal fun Modifier.multiPointerDraggable(
     orientation: Orientation,
@@ -78,6 +82,7 @@
     startDragImmediately: (startedPosition: Offset) -> Boolean,
     onDragStarted: (startedPosition: Offset, overSlop: Float, pointersDown: Int) -> DragController,
     swipeDetector: SwipeDetector = DefaultSwipeDetector,
+    dispatcher: NestedScrollDispatcher,
 ): Modifier =
     this.then(
         MultiPointerDraggableElement(
@@ -86,6 +91,7 @@
             startDragImmediately,
             onDragStarted,
             swipeDetector,
+            dispatcher,
         )
     )
 
@@ -96,6 +102,7 @@
     private val onDragStarted:
         (startedPosition: Offset, overSlop: Float, pointersDown: Int) -> DragController,
     private val swipeDetector: SwipeDetector,
+    private val dispatcher: NestedScrollDispatcher,
 ) : ModifierNodeElement<MultiPointerDraggableNode>() {
     override fun create(): MultiPointerDraggableNode =
         MultiPointerDraggableNode(
@@ -104,6 +111,7 @@
             startDragImmediately = startDragImmediately,
             onDragStarted = onDragStarted,
             swipeDetector = swipeDetector,
+            dispatcher = dispatcher,
         )
 
     override fun update(node: MultiPointerDraggableNode) {
@@ -115,18 +123,6 @@
     }
 }
 
-private val TRAVERSE_KEY = Any()
-
-/** Find the nearest [PointersInfoOwner] ancestor or throw. */
-internal fun DelegatableNode.requireAncestorPointersInfoOwner(): PointersInfoOwner {
-    val ancestorNode =
-        checkNotNull(findNearestAncestor(TRAVERSE_KEY)) {
-            "This should never happen! Couldn't find a MultiPointerDraggableNode. " +
-                "Are we inside an SceneTransitionLayout?"
-        }
-    return ancestorNode as PointersInfoOwner
-}
-
 internal class MultiPointerDraggableNode(
     orientation: Orientation,
     enabled: () -> Boolean,
@@ -134,11 +130,13 @@
     var onDragStarted:
         (startedPosition: Offset, overSlop: Float, pointersDown: Int) -> DragController,
     var swipeDetector: SwipeDetector = DefaultSwipeDetector,
+    private val dispatcher: NestedScrollDispatcher,
 ) :
     DelegatingNode(),
     PointerInputModifierNode,
     CompositionLocalConsumerModifierNode,
-    ObserverModifierNode {
+    ObserverModifierNode,
+    SpaceVectorConverter {
     private val pointerInputHandler: suspend PointerInputScope.() -> Unit = { pointerInput() }
     private val delegate = delegate(SuspendingPointerInputModifierNode(pointerInputHandler))
     private val velocityTracker = VelocityTracker()
@@ -153,26 +151,22 @@
             }
         }
 
-    private var _toFloat = orientation.toFunctionOffsetToFloat()
+    private var converter = SpaceVectorConverter(orientation)
 
-    private fun Offset.toFloat(): Float = _toFloat(this)
+    override fun Offset.toFloat(): Float = with(converter) { this@toFloat.toFloat() }
 
-    private fun Orientation.toFunctionOffsetToFloat(): (Offset) -> Float =
-        when (this) {
-            Orientation.Vertical -> {
-                { it.y }
-            }
-            Orientation.Horizontal -> {
-                { it.x }
-            }
-        }
+    override fun Velocity.toFloat(): Float = with(converter) { this@toFloat.toFloat() }
+
+    override fun Float.toOffset(): Offset = with(converter) { this@toOffset.toOffset() }
+
+    override fun Float.toVelocity(): Velocity = with(converter) { this@toVelocity.toVelocity() }
 
     var orientation: Orientation = orientation
         set(value) {
             // Reset the pointer input whenever orientation changed.
             if (value != field) {
                 field = value
-                _toFloat = field.toFunctionOffsetToFloat()
+                converter = SpaceVectorConverter(value)
                 delegate.resetPointerInputHandler()
             }
         }
@@ -252,28 +246,32 @@
                                 },
                                 onDrag = { controller, change, amount ->
                                     velocityTracker.addPointerInputChange(change)
-                                    controller.onDrag(amount)
+                                    dispatchScrollEvents(
+                                        availableOnPreScroll = amount,
+                                        onScroll = { controller.onDrag(it) },
+                                        source = NestedScrollSource.UserInput,
+                                    )
                                 },
                                 onDragEnd = { controller ->
-                                    val viewConfiguration = currentValueOf(LocalViewConfiguration)
-                                    val maxVelocity =
-                                        viewConfiguration.maximumFlingVelocity.let {
-                                            Velocity(it, it)
-                                        }
-                                    val velocity = velocityTracker.calculateVelocity(maxVelocity)
-                                    controller.onStop(
-                                        velocity =
-                                            when (orientation) {
-                                                Orientation.Horizontal -> velocity.x
-                                                Orientation.Vertical -> velocity.y
-                                            },
-                                        canChangeScene = true,
+                                    startFlingGesture(
+                                        initialVelocity =
+                                            currentValueOf(LocalViewConfiguration)
+                                                .maximumFlingVelocity
+                                                .let {
+                                                    val maxVelocity = Velocity(it, it)
+                                                    velocityTracker.calculateVelocity(maxVelocity)
+                                                }
+                                                .toFloat(),
+                                        onFling = { controller.onStop(it, canChangeScene = true) }
                                     )
                                 },
                                 onDragCancel = { controller ->
-                                    controller.onStop(velocity = 0f, canChangeScene = true)
+                                    startFlingGesture(
+                                        initialVelocity = 0f,
+                                        onFling = { controller.onStop(it, canChangeScene = true) }
+                                    )
                                 },
-                                swipeDetector = swipeDetector
+                                swipeDetector = swipeDetector,
                             )
                         } catch (exception: CancellationException) {
                             // If the coroutine scope is active, we can just restart the drag cycle.
@@ -288,6 +286,101 @@
     }
 
     /**
+     * Start a fling gesture in another CoroutineScope, this is to ensure that even when the pointer
+     * input scope is reset we will continue any coroutine scope that we started from these methods
+     * while the pointer input scope was active.
+     *
+     * Note: Inspired by [androidx.compose.foundation.gestures.ScrollableNode.onDragStopped]
+     */
+    private fun startFlingGesture(initialVelocity: Float, onFling: (velocity: Float) -> Float) {
+        // Note: [AwaitPointerEventScope] is annotated as @RestrictsSuspension, we need another
+        // CoroutineScope to run the fling gestures.
+        // We do not need to cancel this [Job], the source will take care of emitting an
+        // [onPostFling] before starting a new gesture.
+        dispatcher.coroutineScope.launch {
+            dispatchFlingEvents(availableOnPreFling = initialVelocity, onFling = onFling)
+        }
+    }
+
+    /**
+     * Use the nested scroll system to fire scroll events. This allows us to consume events from our
+     * ancestors during the pre-scroll and post-scroll phases.
+     *
+     * @param availableOnPreScroll amount available before the scroll, this can be partially
+     *   consumed by our ancestors.
+     * @param onScroll function that returns the amount consumed during a scroll given the amount
+     *   available after the [NestedScrollConnection.onPreScroll].
+     * @param source the source of the scroll event
+     * @return Total offset consumed.
+     */
+    private inline fun dispatchScrollEvents(
+        availableOnPreScroll: Float,
+        onScroll: (delta: Float) -> Float,
+        source: NestedScrollSource,
+    ): Float {
+        // PreScroll phase
+        val consumedByPreScroll =
+            dispatcher
+                .dispatchPreScroll(
+                    available = availableOnPreScroll.toOffset(),
+                    source = source,
+                )
+                .toFloat()
+
+        // Scroll phase
+        val availableOnScroll = availableOnPreScroll - consumedByPreScroll
+        val consumedBySelfScroll = onScroll(availableOnScroll)
+
+        // PostScroll phase
+        val availableOnPostScroll = availableOnScroll - consumedBySelfScroll
+        val consumedByPostScroll =
+            dispatcher
+                .dispatchPostScroll(
+                    consumed = consumedBySelfScroll.toOffset(),
+                    available = availableOnPostScroll.toOffset(),
+                    source = source,
+                )
+                .toFloat()
+
+        return consumedByPreScroll + consumedBySelfScroll + consumedByPostScroll
+    }
+
+    /**
+     * Use the nested scroll system to fire fling events. This allows us to consume events from our
+     * ancestors during the pre-fling and post-fling phases.
+     *
+     * @param availableOnPreFling velocity available before the fling, this can be partially
+     *   consumed by our ancestors.
+     * @param onFling function that returns the velocity consumed during the fling given the
+     *   velocity available after the [NestedScrollConnection.onPreFling].
+     * @return Total velocity consumed.
+     */
+    private suspend inline fun dispatchFlingEvents(
+        availableOnPreFling: Float,
+        onFling: (velocity: Float) -> Float,
+    ): Float {
+        // PreFling phase
+        val consumedByPreFling =
+            dispatcher.dispatchPreFling(available = availableOnPreFling.toVelocity()).toFloat()
+
+        // Fling phase
+        val availableOnFling = availableOnPreFling - consumedByPreFling
+        val consumedBySelfFling = onFling(availableOnFling)
+
+        // PostFling phase
+        val availableOnPostFling = availableOnFling - consumedBySelfFling
+        val consumedByPostFling =
+            dispatcher
+                .dispatchPostFling(
+                    consumed = consumedBySelfFling.toVelocity(),
+                    available = availableOnPostFling.toVelocity(),
+                )
+                .toFloat()
+
+        return consumedByPreFling + consumedBySelfFling + consumedByPostFling
+    }
+
+    /**
      * Detect drag gestures in the given [orientation].
      *
      * This function is a mix of [androidx.compose.foundation.gestures.awaitDownAndSlop] and
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ObservableTransitionState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ObservableTransitionState.kt
index f1b2249..ae5344f 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ObservableTransitionState.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ObservableTransitionState.kt
@@ -17,6 +17,7 @@
 package com.android.compose.animation.scene
 
 import androidx.compose.runtime.snapshotFlow
+import com.android.compose.animation.scene.content.state.TransitionState
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.flowOf
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/PredictiveBackHandler.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/PredictiveBackHandler.kt
index d3e2a1c..fe16ef751 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/PredictiveBackHandler.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/PredictiveBackHandler.kt
@@ -25,6 +25,7 @@
 import androidx.compose.runtime.mutableFloatStateOf
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.setValue
+import com.android.compose.animation.scene.content.state.TransitionState
 import kotlin.coroutines.cancellation.CancellationException
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.CoroutineStart
@@ -77,8 +78,17 @@
     private var progressAnimatable by mutableStateOf<Animatable<Float, AnimationVector1D>?>(null)
     var dragProgress: Float by mutableFloatStateOf(0f)
 
+    override val previewProgress: Float
+        get() = dragProgress
+
+    override val previewProgressVelocity: Float
+        get() = 0f // Currently, velocity is not exposed by predictive back API
+
+    override val isInPreviewStage: Boolean
+        get() = progressAnimatable == null && previewTransformationSpec != null
+
     override val progress: Float
-        get() = progressAnimatable?.value ?: dragProgress
+        get() = progressAnimatable?.value ?: previewTransformationSpec?.let { 0f } ?: dragProgress
 
     override val progressVelocity: Float
         get() = progressAnimatable?.velocity ?: 0f
@@ -109,8 +119,8 @@
                 toScene -> 1f
                 else -> error("scene $currentScene should be either $fromScene or $toScene")
             }
-
-        val animatable = Animatable(dragProgress).also { progressAnimatable = it }
+        val startProgress = if (previewTransformationSpec != null) 0f else dragProgress
+        val animatable = Animatable(startProgress).also { progressAnimatable = it }
 
         // Important: We start atomically to make sure that we start the coroutine even if it is
         // cancelled right after it is launched, so that finishTransition() is correctly called.
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 2fc4526..65a7367 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
@@ -34,7 +34,6 @@
 import androidx.compose.ui.unit.IntOffset
 import androidx.compose.ui.unit.IntSize
 import androidx.compose.ui.unit.LayoutDirection
-import com.android.compose.animation.scene.UserAction.Resolved
 
 /**
  * [SceneTransitionLayout] is a container that automatically animates its content whenever its state
@@ -85,7 +84,7 @@
     fun scene(
         key: SceneKey,
         userActions: Map<UserAction, UserActionResult> = emptyMap(),
-        content: @Composable SceneScope.() -> Unit,
+        content: @Composable ContentScope.() -> Unit,
     )
 }
 
@@ -118,25 +117,25 @@
 
 @Stable
 @ElementDsl
-interface BaseSceneScope : ElementStateScope {
-    /** The key of this scene. */
-    val sceneKey: SceneKey
+interface BaseContentScope : ElementStateScope {
+    /** The key of this content. */
+    val contentKey: ContentKey
 
-    /** The state of the [SceneTransitionLayout] in which this scene is contained. */
+    /** The state of the [SceneTransitionLayout] in which this content is contained. */
     val layoutState: SceneTransitionLayoutState
 
     /**
      * Tag an element identified by [key].
      *
      * Tagging an element will allow you to reference that element when defining transitions, so
-     * that the element can be transformed and animated when the scene transitions in or out.
+     * that the element can be transformed and animated when the content transitions in or out.
      *
-     * Additionally, this [key] will be used to detect elements that are shared between scenes to
+     * Additionally, this [key] will be used to detect elements that are shared between contents to
      * automatically interpolate their size and offset. If you need to animate shared element values
-     * (i.e. values associated to this element that change depending on which scene it is composed
+     * (i.e. values associated to this element that change depending on which content it is composed
      * in), use [Element] instead.
      *
-     * Note that shared elements tagged using this function will be duplicated in each scene they
+     * Note that shared elements tagged using this function will be duplicated in each content they
      * are part of, so any **internal** state (e.g. state created using `remember {
      * mutableStateOf(...) }`) will be lost. If you need to preserve internal state, you should use
      * [MovableElement] instead.
@@ -150,7 +149,7 @@
      * Create an element identified by [key].
      *
      * Similar to [element], this creates an element that will be automatically shared when present
-     * in multiple scenes and that can be transformed during transitions, the same way that
+     * in multiple contents and that can be transformed during transitions, the same way that
      * [element] does.
      *
      * The only difference with [element] is that the provided [ElementScope] allows you to
@@ -177,7 +176,7 @@
      * Create a *movable* element identified by [key].
      *
      * Similar to [Element], this creates an element that will be automatically shared when present
-     * in multiple scenes and that can be transformed during transitions, and you can also use the
+     * in multiple contents and that can be transformed during transitions, and you can also use the
      * provided [ElementScope] to [animate element values][ElementScope.animateElementValueAsState].
      *
      * The important difference with [element] and [Element] is that this element
@@ -190,7 +189,7 @@
      */
     @Composable
     fun MovableElement(
-        key: ElementKey,
+        key: MovableElementKey,
         modifier: Modifier,
 
         // TODO(b/317026105): As discussed in http://shortn/_gJVdltF8Si, remove the @Composable
@@ -232,24 +231,26 @@
     fun Modifier.noResizeDuringTransitions(): Modifier
 }
 
+typealias SceneScope = ContentScope
+
 @Stable
 @ElementDsl
-interface SceneScope : BaseSceneScope {
+interface ContentScope : BaseContentScope {
     /**
-     * Animate some value at the scene level.
+     * Animate some value at the content level.
      *
      * @param value the value of this shared value in the current scene.
      * @param key the key of this shared value.
      * @param type the [SharedValueType] of this animated value.
      * @param canOverflow whether this value can overflow past the values it is interpolated
      *   between, for instance because the transition is animated using a bouncy spring.
-     * @see animateSceneIntAsState
-     * @see animateSceneFloatAsState
-     * @see animateSceneDpAsState
-     * @see animateSceneColorAsState
+     * @see animateContentIntAsState
+     * @see animateContentFloatAsState
+     * @see animateContentDpAsState
+     * @see animateContentColorAsState
      */
     @Composable
-    fun <T> animateSceneValueAsState(
+    fun <T> animateContentValueAsState(
         value: T,
         key: ValueKey,
         type: SharedValueType<T, *>,
@@ -259,7 +260,7 @@
 
 /**
  * The type of a shared value animated using [ElementScope.animateElementValueAsState] or
- * [SceneScope.animateSceneValueAsState].
+ * [ContentScope.animateContentValueAsState].
  */
 @Stable
 interface SharedValueType<T, Delta> {
@@ -321,8 +322,9 @@
  * The exact same scope as [androidx.compose.foundation.layout.BoxScope].
  *
  * We can't reuse BoxScope directly because of the @LayoutScopeMarker annotation on it, which would
- * prevent us from calling Modifier.element() and other methods of [SceneScope] inside any Box {} in
- * the [content][ElementScope.content] of a [SceneScope.Element] or a [SceneScope.MovableElement].
+ * prevent us from calling Modifier.element() and other methods of [ContentScope] inside any Box {}
+ * in the [content][ElementScope.content] of a [ContentScope.Element] or a
+ * [ContentScope.MovableElement].
  */
 @Stable
 @ElementDsl
@@ -335,16 +337,16 @@
 }
 
 /** The scope for "normal" (not movable) elements. */
-@Stable @ElementDsl interface ElementContentScope : SceneScope, ElementBoxScope
+@Stable @ElementDsl interface ElementContentScope : ContentScope, ElementBoxScope
 
 /**
  * The scope for the content of movable elements.
  *
- * Note that it extends [BaseSceneScope] and not [SceneScope] because movable elements should not
- * call [SceneScope.animateSceneValueAsState], given that their content is not composed in all
- * scenes.
+ * Note that it extends [BaseContentScope] and not [ContentScope] because movable elements should
+ * not call [ContentScope.animateContentValueAsState], given that their content is not composed in
+ * all scenes.
  */
-@Stable @ElementDsl interface MovableElementContentScope : BaseSceneScope, ElementBoxScope
+@Stable @ElementDsl interface MovableElementContentScope : BaseContentScope, ElementBoxScope
 
 /** An action performed by the user. */
 sealed class UserAction {
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt
index 32db0b7..062d553 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt
@@ -36,6 +36,8 @@
 import androidx.compose.ui.unit.LayoutDirection
 import androidx.compose.ui.util.fastForEach
 import androidx.compose.ui.util.fastForEachReversed
+import com.android.compose.animation.scene.content.Content
+import com.android.compose.animation.scene.content.Scene
 import com.android.compose.ui.util.lerp
 import kotlinx.coroutines.CoroutineScope
 
@@ -84,7 +86,7 @@
 
     /**
      * The different values of a shared value keyed by a a [ValueKey] and the different elements and
-     * scenes it is associated to.
+     * contents it is associated to.
      */
     private var _sharedValues: MutableMap<ValueKey, MutableMap<ElementKey?, SharedValue<*, *>>>? =
         null
@@ -149,6 +151,12 @@
         return scenes[key] ?: error("Scene $key is not configured")
     }
 
+    internal fun content(key: ContentKey): Content {
+        return when (key) {
+            is SceneKey -> scene(key)
+        }
+    }
+
     internal fun updateScenes(
         builder: SceneTransitionLayoutScope.() -> Unit,
         layoutDirection: LayoutDirection,
@@ -164,7 +172,7 @@
                 override fun scene(
                     key: SceneKey,
                     userActions: Map<UserAction, UserActionResult>,
-                    content: @Composable SceneScope.() -> Unit,
+                    content: @Composable ContentScope.() -> Unit,
                 ) {
                     scenesToRemove.remove(key)
 
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 48bc251..a6c6a80 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
@@ -18,10 +18,6 @@
 
 import android.util.Log
 import androidx.annotation.VisibleForTesting
-import androidx.compose.animation.core.Animatable
-import androidx.compose.animation.core.AnimationVector1D
-import androidx.compose.animation.core.spring
-import androidx.compose.foundation.gestures.Orientation
 import androidx.compose.runtime.Stable
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
@@ -29,12 +25,12 @@
 import androidx.compose.ui.util.fastAll
 import androidx.compose.ui.util.fastFilter
 import androidx.compose.ui.util.fastForEach
+import com.android.compose.animation.scene.content.state.ContentState
+import com.android.compose.animation.scene.content.state.TransitionState
 import com.android.compose.animation.scene.transition.link.LinkedTransition
 import com.android.compose.animation.scene.transition.link.StateLink
 import kotlin.math.absoluteValue
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.Job
-import kotlinx.coroutines.launch
 
 /**
  * The state of a [SceneTransitionLayout].
@@ -146,192 +142,6 @@
     )
 }
 
-@Stable
-sealed interface TransitionState {
-    /**
-     * The current effective scene. If a new transition was triggered, it would start from this
-     * scene.
-     *
-     * For instance, when swiping from scene A to scene B, the [currentScene] is A when the swipe
-     * gesture starts, but then if the user flings their finger and commits the transition to scene
-     * B, then [currentScene] becomes scene B even if the transition is not finished yet and is
-     * still animating to settle to scene B.
-     */
-    val currentScene: SceneKey
-
-    /** No transition/animation is currently running. */
-    data class Idle(override val currentScene: SceneKey) : TransitionState
-
-    /** There is a transition animating between two scenes. */
-    abstract class Transition(
-        /** The scene this transition is starting from. Can't be the same as toScene */
-        val fromScene: SceneKey,
-
-        /** The scene this transition is going to. Can't be the same as fromScene */
-        val toScene: SceneKey,
-
-        /** The transition that `this` transition is replacing, if any. */
-        internal val replacedTransition: Transition? = null,
-    ) : TransitionState {
-        /**
-         * The key of this transition. This should usually be null, but it can be specified to use a
-         * specific set of transformations associated to this transition.
-         */
-        open val key: TransitionKey? = null
-
-        /**
-         * The progress of the transition. This is usually in the `[0; 1]` range, but it can also be
-         * less than `0` or greater than `1` when using transitions with a spring AnimationSpec or
-         * when flinging quickly during a swipe gesture.
-         */
-        abstract val progress: Float
-
-        /** The current velocity of [progress], in progress units. */
-        abstract val progressVelocity: Float
-
-        /** Whether the transition was triggered by user input rather than being programmatic. */
-        abstract val isInitiatedByUserInput: Boolean
-
-        /** Whether user input is currently driving the transition. */
-        abstract val isUserInputOngoing: Boolean
-
-        /**
-         * The current [TransformationSpecImpl] and [OverscrollSpecImpl] associated to this
-         * transition.
-         *
-         * Important: These will be set exactly once, when this transition is
-         * [started][MutableSceneTransitionLayoutStateImpl.startTransition].
-         */
-        internal var transformationSpec: TransformationSpecImpl = TransformationSpec.Empty
-        private var fromOverscrollSpec: OverscrollSpecImpl? = null
-        private var toOverscrollSpec: OverscrollSpecImpl? = null
-
-        /** The current [OverscrollSpecImpl], if this transition is currently overscrolling. */
-        internal val currentOverscrollSpec: OverscrollSpecImpl?
-            get() {
-                if (this !is HasOverscrollProperties) return null
-                val progress = progress
-                val bouncingScene = bouncingScene
-                return when {
-                    progress < 0f || bouncingScene == fromScene -> fromOverscrollSpec
-                    progress > 1f || bouncingScene == toScene -> toOverscrollSpec
-                    else -> null
-                }
-            }
-
-        /**
-         * An animatable that animates from 1f to 0f. This will be used to nicely animate the sudden
-         * jump of values when this transitions interrupts another one.
-         */
-        private var interruptionDecay: Animatable<Float, AnimationVector1D>? = null
-
-        init {
-            check(fromScene != toScene)
-            check(
-                replacedTransition == null ||
-                    (replacedTransition.fromScene == fromScene &&
-                        replacedTransition.toScene == toScene)
-            )
-        }
-
-        /**
-         * Force this transition to finish and animate to [currentScene], so that this transition
-         * progress will settle to either 0% (if [currentScene] == [fromScene]) or 100% (if
-         * [currentScene] == [toScene]) in a finite amount of time.
-         *
-         * @return the [Job] that animates the progress to [currentScene]. It can be used to wait
-         *   until the animation is complete or cancel it to snap to [currentScene]. Calling
-         *   [finish] multiple times will return the same [Job].
-         */
-        abstract fun finish(): Job
-
-        /**
-         * Whether we are transitioning. If [from] or [to] is empty, we will also check that they
-         * match the scenes we are animating from and/or to.
-         */
-        fun isTransitioning(from: SceneKey? = null, to: SceneKey? = null): Boolean {
-            return (from == null || fromScene == from) && (to == null || toScene == to)
-        }
-
-        /** Whether we are transitioning from [scene] to [other], or from [other] to [scene]. */
-        fun isTransitioningBetween(scene: SceneKey, other: SceneKey): Boolean {
-            return isTransitioning(from = scene, to = other) ||
-                isTransitioning(from = other, to = scene)
-        }
-
-        internal fun updateOverscrollSpecs(
-            fromSpec: OverscrollSpecImpl?,
-            toSpec: OverscrollSpecImpl?,
-        ) {
-            fromOverscrollSpec = fromSpec
-            toOverscrollSpec = toSpec
-        }
-
-        internal open fun interruptionProgress(
-            layoutImpl: SceneTransitionLayoutImpl,
-        ): Float {
-            if (!layoutImpl.state.enableInterruptions) {
-                return 0f
-            }
-
-            if (replacedTransition != null) {
-                return replacedTransition.interruptionProgress(layoutImpl)
-            }
-
-            fun create(): Animatable<Float, AnimationVector1D> {
-                val animatable = Animatable(1f, visibilityThreshold = ProgressVisibilityThreshold)
-                layoutImpl.coroutineScope.launch {
-                    val swipeSpec = layoutImpl.state.transitions.defaultSwipeSpec
-                    val progressSpec =
-                        spring(
-                            stiffness = swipeSpec.stiffness,
-                            dampingRatio = swipeSpec.dampingRatio,
-                            visibilityThreshold = ProgressVisibilityThreshold,
-                        )
-                    animatable.animateTo(0f, progressSpec)
-                }
-
-                return animatable
-            }
-
-            val animatable = interruptionDecay ?: create().also { interruptionDecay = it }
-            return animatable.value
-        }
-    }
-
-    interface HasOverscrollProperties {
-        /**
-         * The position of the [Transition.toScene].
-         *
-         * Used to understand the direction of the overscroll.
-         */
-        val isUpOrLeft: Boolean
-
-        /**
-         * The relative orientation between [Transition.fromScene] and [Transition.toScene].
-         *
-         * Used to understand the orientation of the overscroll.
-         */
-        val orientation: Orientation
-
-        /**
-         * Scope which can be used in the Overscroll DSL to define a transformation based on the
-         * distance between [Transition.fromScene] and [Transition.toScene].
-         */
-        val overscrollScope: OverscrollScope
-
-        /**
-         * The scene around which the transition is currently bouncing. When not `null`, this
-         * transition is currently oscillating around this scene and will soon settle to that scene.
-         */
-        val bouncingScene: SceneKey?
-
-        companion object {
-            const val DistanceUnspecified = 0f
-        }
-    }
-}
-
 /** A [MutableSceneTransitionLayoutState] that holds the value for the current scene. */
 internal class MutableSceneTransitionLayoutStateImpl(
     initialScene: SceneKey,
@@ -430,13 +240,17 @@
         // Compute the [TransformationSpec] when the transition starts.
         val fromScene = transition.fromScene
         val toScene = transition.toScene
-        val orientation = (transition as? TransitionState.HasOverscrollProperties)?.orientation
+        val orientation = (transition as? ContentState.HasOverscrollProperties)?.orientation
 
         // Update the transition specs.
         transition.transformationSpec =
             transitions
                 .transitionSpec(fromScene, toScene, key = transition.key)
                 .transformationSpec()
+        transition.previewTransformationSpec =
+            transitions
+                .transitionSpec(fromScene, toScene, key = transition.key)
+                .previewTransformationSpec()
         if (orientation != null) {
             transition.updateOverscrollSpecs(
                 fromSpec = transitions.overscrollSpec(fromScene, orientation),
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 e30dd356..3ded1de 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
@@ -110,7 +110,7 @@
     }
 
     private fun defaultTransition(from: SceneKey, to: SceneKey) =
-        TransitionSpecImpl(key = null, from, to, TransformationSpec.EmptyProvider)
+        TransitionSpecImpl(key = null, from, to, null, null, TransformationSpec.EmptyProvider)
 
     internal fun overscrollSpec(scene: SceneKey, orientation: Orientation): OverscrollSpecImpl? =
         overscrollCache
@@ -157,16 +157,16 @@
     val key: TransitionKey?
 
     /**
-     * The scene we are transitioning from. If `null`, this spec can be used to animate from any
-     * scene.
+     * The content we are transitioning from. If `null`, this spec can be used to animate from any
+     * content.
      */
-    val from: SceneKey?
+    val from: ContentKey?
 
     /**
-     * The scene we are transitioning to. If `null`, this spec can be used to animate from any
-     * scene.
+     * The content we are transitioning to. If `null`, this spec can be used to animate from any
+     * content.
      */
-    val to: SceneKey?
+    val to: ContentKey?
 
     /**
      * Return a reversed version of this [TransitionSpec] for a transition going from [to] to
@@ -177,10 +177,18 @@
     /**
      * The [TransformationSpec] associated to this [TransitionSpec].
      *
-     * Note that this is called once every a transition associated to this [TransitionSpec] is
+     * Note that this is called once whenever a transition associated to this [TransitionSpec] is
      * started.
      */
     fun transformationSpec(): TransformationSpec
+
+    /**
+     * The preview [TransformationSpec] associated to this [TransitionSpec].
+     *
+     * Note that this is called once whenever a transition associated to this [TransitionSpec] is
+     * started.
+     */
+    fun previewTransformationSpec(): TransformationSpec?
 }
 
 interface TransformationSpec {
@@ -223,15 +231,19 @@
 
 internal class TransitionSpecImpl(
     override val key: TransitionKey?,
-    override val from: SceneKey?,
-    override val to: SceneKey?,
-    private val transformationSpec: () -> TransformationSpecImpl,
+    override val from: ContentKey?,
+    override val to: ContentKey?,
+    private val previewTransformationSpec: (() -> TransformationSpecImpl)? = null,
+    private val reversePreviewTransformationSpec: (() -> TransformationSpecImpl)? = null,
+    private val transformationSpec: () -> TransformationSpecImpl
 ) : TransitionSpec {
     override fun reversed(): TransitionSpecImpl {
         return TransitionSpecImpl(
             key = key,
             from = to,
             to = from,
+            previewTransformationSpec = reversePreviewTransformationSpec,
+            reversePreviewTransformationSpec = previewTransformationSpec,
             transformationSpec = {
                 val reverse = transformationSpec.invoke()
                 TransformationSpecImpl(
@@ -245,6 +257,9 @@
     }
 
     override fun transformationSpec(): TransformationSpecImpl = this.transformationSpec.invoke()
+
+    override fun previewTransformationSpec(): TransformationSpecImpl? =
+        previewTransformationSpec?.invoke()
 }
 
 /** The definition of the overscroll behavior of the [scene]. */
@@ -287,18 +302,18 @@
     override val distance: UserActionDistance?,
     override val transformations: List<Transformation>,
 ) : TransformationSpec {
-    private val cache = mutableMapOf<ElementKey, MutableMap<SceneKey, ElementTransformations>>()
+    private val cache = mutableMapOf<ElementKey, MutableMap<ContentKey, ElementTransformations>>()
 
-    internal fun transformations(element: ElementKey, scene: SceneKey): ElementTransformations {
+    internal fun transformations(element: ElementKey, content: ContentKey): ElementTransformations {
         return cache
             .getOrPut(element) { mutableMapOf() }
-            .getOrPut(scene) { computeTransformations(element, scene) }
+            .getOrPut(content) { computeTransformations(element, content) }
     }
 
     /** Filter [transformations] to compute the [ElementTransformations] of [element]. */
     private fun computeTransformations(
         element: ElementKey,
-        scene: SceneKey,
+        content: ContentKey,
     ): ElementTransformations {
         var shared: SharedElementTransformation? = null
         var offset: PropertyTransformation<Offset>? = null
@@ -336,7 +351,7 @@
         }
 
         transformations.fastForEach { transformation ->
-            if (!transformation.matcher.matches(element, scene)) {
+            if (!transformation.matcher.matches(element, content)) {
                 return@fastForEach
             }
 
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 b8010f2..f062146 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
@@ -20,6 +20,7 @@
 import androidx.compose.runtime.Stable
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.input.nestedscroll.NestedScrollDispatcher
 import androidx.compose.ui.input.nestedscroll.nestedScrollModifierNode
 import androidx.compose.ui.input.pointer.PointerEvent
 import androidx.compose.ui.input.pointer.PointerEventPass
@@ -30,6 +31,7 @@
 import androidx.compose.ui.node.TraversableNode
 import androidx.compose.ui.node.findNearestAncestor
 import androidx.compose.ui.unit.IntSize
+import com.android.compose.animation.scene.content.Scene
 
 /**
  * Configures the swipeable behavior of a [SceneTransitionLayout] depending on the current state.
@@ -57,6 +59,7 @@
     draggableHandler: DraggableHandlerImpl,
     swipeDetector: SwipeDetector,
 ) : DelegatingNode(), PointerInputModifierNode {
+    private val dispatcher = NestedScrollDispatcher()
     private val multiPointerDraggableNode =
         delegate(
             MultiPointerDraggableNode(
@@ -65,6 +68,7 @@
                 startDragImmediately = ::startDragImmediately,
                 onDragStarted = draggableHandler::onDragStarted,
                 swipeDetector = swipeDetector,
+                dispatcher = dispatcher,
             )
         )
 
@@ -93,7 +97,7 @@
         )
 
     init {
-        delegate(nestedScrollModifierNode(nestedScrollHandlerImpl.connection, dispatcher = null))
+        delegate(nestedScrollModifierNode(nestedScrollHandlerImpl.connection, dispatcher))
         delegate(ScrollBehaviorOwnerNode(draggableHandler.nestedScrollKey, nestedScrollHandlerImpl))
     }
 
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 89ed8d6..38b8383 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
@@ -23,7 +23,7 @@
 import androidx.compose.ui.unit.Density
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.dp
-import com.android.compose.animation.scene.TransitionState.HasOverscrollProperties.Companion.DistanceUnspecified
+import com.android.compose.animation.scene.content.state.ContentState
 
 /** Define the [transitions][SceneTransitions] to be used with a [SceneTransitionLayout]. */
 fun transitions(builder: SceneTransitionsBuilder.() -> Unit): SceneTransitions {
@@ -47,38 +47,50 @@
     var interruptionHandler: InterruptionHandler
 
     /**
-     * Define the default animation to be played when transitioning [to] the specified scene, from
-     * any scene. For the animation specification to apply only when transitioning between two
-     * specific scenes, use [from] instead.
+     * Define the default animation to be played when transitioning [to] the specified content, from
+     * any content. For the animation specification to apply only when transitioning between two
+     * specific contents, use [from] instead.
      *
      * If [key] is not `null`, then this transition will only be used if the same key is specified
      * when triggering the transition.
      *
+     * Optionally, define a [preview] animation which will be played during the first stage of the
+     * transition, e.g. during the predictive back gesture. In case your transition should be
+     * reversible with the reverse animation having a preview as well, define a [reversePreview].
+     *
      * @see from
      */
     fun to(
-        to: SceneKey,
+        to: ContentKey,
         key: TransitionKey? = null,
+        preview: (TransitionBuilder.() -> Unit)? = null,
+        reversePreview: (TransitionBuilder.() -> Unit)? = null,
         builder: TransitionBuilder.() -> Unit = {},
     ): TransitionSpec
 
     /**
-     * Define the animation to be played when transitioning [from] the specified scene. For the
-     * animation specification to apply only when transitioning between two specific scenes, pass
-     * the destination scene via the [to] argument.
+     * Define the animation to be played when transitioning [from] the specified content. For the
+     * animation specification to apply only when transitioning between two specific contents, pass
+     * the destination content via the [to] argument.
      *
-     * When looking up which transition should be used when animating from scene A to scene B, we
-     * pick the single transition with the given [key] and matching one of these predicates (in
+     * When looking up which transition should be used when animating from content A to content B,
+     * we pick the single transition with the given [key] and matching one of these predicates (in
      * order of importance):
      * 1. from == A && to == B
      * 2. to == A && from == B, which is then treated in reverse.
      * 3. (from == A && to == null) || (from == null && to == B)
      * 4. (from == B && to == null) || (from == null && to == A), which is then treated in reverse.
+     *
+     * Optionally, define a [preview] animation which will be played during the first stage of the
+     * transition, e.g. during the predictive back gesture. In case your transition should be
+     * reversible with the reverse animation having a preview as well, define a [reversePreview].
      */
     fun from(
-        from: SceneKey,
-        to: SceneKey? = null,
+        from: ContentKey,
+        to: ContentKey? = null,
         key: TransitionKey? = null,
+        preview: (TransitionBuilder.() -> Unit)? = null,
+        reversePreview: (TransitionBuilder.() -> Unit)? = null,
         builder: TransitionBuilder.() -> Unit = {},
     ): TransitionSpec
 
@@ -92,8 +104,14 @@
     fun overscroll(
         scene: SceneKey,
         orientation: Orientation,
-        builder: OverscrollBuilder.() -> Unit = {},
+        builder: OverscrollBuilder.() -> Unit,
     ): OverscrollSpec
+
+    /**
+     * Prevents overscroll the [scene] in the given [orientation], allowing ancestors to eventually
+     * consume the remaining gesture.
+     */
+    fun overscrollDisabled(scene: SceneKey, orientation: Orientation): OverscrollSpec
 }
 
 interface BaseTransitionBuilder : PropertyTransformationBuilder {
@@ -215,98 +233,171 @@
 /**
  * An interface to decide where we should draw shared Elements or compose MovableElements.
  *
- * @see DefaultElementScenePicker
- * @see HighestZIndexScenePicker
- * @see LowestZIndexScenePicker
- * @see MovableElementScenePicker
+ * @see DefaultElementContentPicker
+ * @see HighestZIndexContentPicker
+ * @see LowestZIndexContentPicker
+ * @see MovableElementContentPicker
  */
-interface ElementScenePicker {
+interface ElementContentPicker {
     /**
-     * Return the scene in which [element] should be drawn (when using `Modifier.element(key)`) or
+     * Return the content in which [element] should be drawn (when using `Modifier.element(key)`) or
      * composed (when using `MovableElement(key)`) during the given [transition]. If this element
-     * should not be drawn or composed in neither [transition.fromScene] nor [transition.toScene],
-     * return `null`.
+     * should not be drawn or composed in neither [transition.fromContent] nor
+     * [transition.toContent], return `null`.
      *
-     * Important: For [MovableElements][SceneScope.MovableElement], this scene picker will *always*
-     * be used during transitions to decide whether we should compose that element in a given scene
-     * or not. Therefore, you should make sure that the returned [SceneKey] contains the movable
-     * element, otherwise that element will not be composed in any scene during the transition.
+     * Important: For [MovableElements][ContentScope.MovableElement], this content picker will
+     * *always* be used during transitions to decide whether we should compose that element in a
+     * given content or not. Therefore, you should make sure that the returned [ContentKey] contains
+     * the movable element, otherwise that element will not be composed in any scene during the
+     * transition.
      */
-    fun sceneDuringTransition(
+    fun contentDuringTransition(
         element: ElementKey,
-        transition: TransitionState.Transition,
-        fromSceneZIndex: Float,
-        toSceneZIndex: Float,
-    ): SceneKey?
+        transition: ContentState.Transition<*>,
+        fromContentZIndex: Float,
+        toContentZIndex: Float,
+    ): ContentKey
 
     /**
-     * Return [transition.fromScene] if it is in [scenes] and [transition.toScene] is not, or return
-     * [transition.toScene] if it is in [scenes] and [transition.fromScene] is not. If neither
-     * [transition.fromScene] and [transition.toScene] are in [scenes], return `null`. If both
-     * [transition.fromScene] and [transition.toScene] are in [scenes], throw an exception.
+     * Return [transition.fromContent] if it is in [contents] and [transition.toContent] is not, or
+     * return [transition.toContent] if it is in [contents] and [transition.fromContent] is not. If
+     * neither [transition.toContent] and [transition.fromContent] are in [contents] or if both
+     * [transition.fromContent] and [transition.toContent] are in [contents], throw an exception.
      *
-     * This function can be useful when computing the scene in which a movable element should be
+     * This function can be useful when computing the content in which a movable element should be
      * composed.
      */
-    fun pickSingleSceneIn(
-        scenes: Set<SceneKey>,
-        transition: TransitionState.Transition,
+    fun pickSingleContentIn(
+        contents: Set<ContentKey>,
+        transition: ContentState.Transition<*>,
         element: ElementKey,
-    ): SceneKey? {
-        val fromScene = transition.fromScene
-        val toScene = transition.toScene
-        val fromSceneInScenes = scenes.contains(fromScene)
-        val toSceneInScenes = scenes.contains(toScene)
+    ): ContentKey {
+        val fromContent = transition.fromContent
+        val toContent = transition.toContent
+        val fromContentInContents = contents.contains(fromContent)
+        val toContentInContents = contents.contains(toContent)
 
-        return when {
-            fromSceneInScenes && toSceneInScenes -> {
-                error(
-                    "Element $element can be in both $fromScene and $toScene. You should add a " +
-                        "special case for this transition before calling pickSingleSceneIn()."
-                )
-            }
-            fromSceneInScenes -> fromScene
-            toSceneInScenes -> toScene
-            else -> null
+        if (fromContentInContents && toContentInContents) {
+            error(
+                "Element $element can be in both $fromContent and $toContent. You should add a " +
+                    "special case for this transition before calling pickSingleSceneIn()."
+            )
         }
-    }
-}
 
-/** An [ElementScenePicker] that draws/composes elements in the scene with the highest z-order. */
-object HighestZIndexScenePicker : ElementScenePicker {
-    override fun sceneDuringTransition(
-        element: ElementKey,
-        transition: TransitionState.Transition,
-        fromSceneZIndex: Float,
-        toSceneZIndex: Float
-    ): SceneKey {
-        return if (fromSceneZIndex > toSceneZIndex) {
-            transition.fromScene
-        } else {
-            transition.toScene
+        if (!fromContentInContents && !toContentInContents) {
+            error(
+                "Element $element can be neither in $fromContent and $toContent. This either " +
+                    "means that you should add one of them in the scenes set passed to " +
+                    "pickSingleSceneIn(), or there is an internal error and this element was " +
+                    "composed when it shouldn't be."
+            )
         }
-    }
-}
 
-/** An [ElementScenePicker] that draws/composes elements in the scene with the lowest z-order. */
-object LowestZIndexScenePicker : ElementScenePicker {
-    override fun sceneDuringTransition(
-        element: ElementKey,
-        transition: TransitionState.Transition,
-        fromSceneZIndex: Float,
-        toSceneZIndex: Float
-    ): SceneKey {
-        return if (fromSceneZIndex < toSceneZIndex) {
-            transition.fromScene
+        return if (fromContentInContents) {
+            fromContent
         } else {
-            transition.toScene
+            toContent
         }
     }
 }
 
 /**
- * An [ElementScenePicker] that draws/composes elements in the scene we are transitioning to, iff
- * that scene is in [scenes].
+ * An element picker on which we can query the set of contents (scenes or overlays) that contain the
+ * element. This is needed by [MovableElement], that needs to know at composition time on which of
+ * the candidate contents an element should be composed.
+ */
+interface StaticElementContentPicker : ElementContentPicker {
+    /** The exhaustive lists of contents that contain this element. */
+    val contents: Set<ContentKey>
+}
+
+/**
+ * An [ElementContentPicker] that draws/composes elements in the content with the highest z-order.
+ */
+object HighestZIndexContentPicker : ElementContentPicker {
+    override fun contentDuringTransition(
+        element: ElementKey,
+        transition: ContentState.Transition<*>,
+        fromContentZIndex: Float,
+        toContentZIndex: Float
+    ): ContentKey {
+        return if (fromContentZIndex > toContentZIndex) {
+            transition.fromContent
+        } else {
+            transition.toContent
+        }
+    }
+
+    /**
+     * Return a [StaticElementContentPicker] that behaves like [HighestZIndexContentPicker] and can
+     * be used by [MovableElement].
+     */
+    operator fun invoke(contents: Set<ContentKey>): StaticElementContentPicker {
+        return object : StaticElementContentPicker {
+            override val contents: Set<ContentKey> = contents
+
+            override fun contentDuringTransition(
+                element: ElementKey,
+                transition: ContentState.Transition<*>,
+                fromContentZIndex: Float,
+                toContentZIndex: Float
+            ): ContentKey {
+                return HighestZIndexContentPicker.contentDuringTransition(
+                    element,
+                    transition,
+                    fromContentZIndex,
+                    toContentZIndex,
+                )
+            }
+        }
+    }
+}
+
+/**
+ * An [ElementContentPicker] that draws/composes elements in the content with the lowest z-order.
+ */
+object LowestZIndexContentPicker : ElementContentPicker {
+    override fun contentDuringTransition(
+        element: ElementKey,
+        transition: ContentState.Transition<*>,
+        fromContentZIndex: Float,
+        toContentZIndex: Float
+    ): ContentKey {
+        return if (fromContentZIndex < toContentZIndex) {
+            transition.fromContent
+        } else {
+            transition.toContent
+        }
+    }
+
+    /**
+     * Return a [StaticElementContentPicker] that behaves like [LowestZIndexContentPicker] and can
+     * be used by [MovableElement].
+     */
+    operator fun invoke(contents: Set<ContentKey>): StaticElementContentPicker {
+        return object : StaticElementContentPicker {
+            override val contents: Set<ContentKey> = contents
+
+            override fun contentDuringTransition(
+                element: ElementKey,
+                transition: ContentState.Transition<*>,
+                fromContentZIndex: Float,
+                toContentZIndex: Float
+            ): ContentKey {
+                return LowestZIndexContentPicker.contentDuringTransition(
+                    element,
+                    transition,
+                    fromContentZIndex,
+                    toContentZIndex,
+                )
+            }
+        }
+    }
+}
+
+/**
+ * An [ElementContentPicker] that draws/composes elements in the content we are transitioning to,
+ * iff that content is in [contents].
  *
  * This picker can be useful for movable elements whose content size depends on its content (because
  * it wraps it) in at least one scene. That way, the target size of the MovableElement will be
@@ -318,23 +409,30 @@
  * is not the same as when going from scene B to scene A, so it's not usable in situations where
  * z-ordering during the transition matters.
  */
-class MovableElementScenePicker(private val scenes: Set<SceneKey>) : ElementScenePicker {
-    override fun sceneDuringTransition(
+class MovableElementContentPicker(
+    override val contents: Set<ContentKey>,
+) : StaticElementContentPicker {
+    override fun contentDuringTransition(
         element: ElementKey,
-        transition: TransitionState.Transition,
-        fromSceneZIndex: Float,
-        toSceneZIndex: Float,
-    ): SceneKey? {
+        transition: ContentState.Transition<*>,
+        fromContentZIndex: Float,
+        toContentZIndex: Float,
+    ): ContentKey {
         return when {
-            scenes.contains(transition.toScene) -> transition.toScene
-            scenes.contains(transition.fromScene) -> transition.fromScene
-            else -> null
+            transition.toContent in contents -> transition.toContent
+            else -> {
+                check(transition.fromContent in contents) {
+                    "Neither ${transition.fromContent} nor ${transition.toContent} are in " +
+                        "contents. This transition should not have been used for this element."
+                }
+                transition.fromContent
+            }
         }
     }
 }
 
-/** The default [ElementScenePicker]. */
-val DefaultElementScenePicker = HighestZIndexScenePicker
+/** The default [ElementContentPicker]. */
+val DefaultElementContentPicker = HighestZIndexContentPicker
 
 @TransitionDsl
 interface PropertyTransformationBuilder {
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 1e67aa9..6515cb8 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
@@ -60,20 +60,24 @@
     val transitionOverscrollSpecs = mutableListOf<OverscrollSpecImpl>()
 
     override fun to(
-        to: SceneKey,
+        to: ContentKey,
         key: TransitionKey?,
+        preview: (TransitionBuilder.() -> Unit)?,
+        reversePreview: (TransitionBuilder.() -> Unit)?,
         builder: TransitionBuilder.() -> Unit
     ): TransitionSpec {
-        return transition(from = null, to = to, key = key, builder)
+        return transition(from = null, to = to, key = key, preview, reversePreview, builder)
     }
 
     override fun from(
-        from: SceneKey,
-        to: SceneKey?,
+        from: ContentKey,
+        to: ContentKey?,
         key: TransitionKey?,
+        preview: (TransitionBuilder.() -> Unit)?,
+        reversePreview: (TransitionBuilder.() -> Unit)?,
         builder: TransitionBuilder.() -> Unit
     ): TransitionSpec {
-        return transition(from = from, to = to, key = key, builder)
+        return transition(from = from, to = to, key = key, preview, reversePreview, builder)
     }
 
     override fun overscroll(
@@ -82,6 +86,22 @@
         builder: OverscrollBuilder.() -> Unit
     ): OverscrollSpec {
         val impl = OverscrollBuilderImpl().apply(builder)
+        check(impl.transformations.isNotEmpty()) {
+            "This method does not allow empty transformations. " +
+                "Use overscrollDisabled($scene, $orientation) instead."
+        }
+        return overscrollSpec(scene, orientation, impl)
+    }
+
+    override fun overscrollDisabled(scene: SceneKey, orientation: Orientation): OverscrollSpec {
+        return overscrollSpec(scene, orientation, OverscrollBuilderImpl())
+    }
+
+    private fun overscrollSpec(
+        scene: SceneKey,
+        orientation: Orientation,
+        impl: OverscrollBuilderImpl,
+    ): OverscrollSpec {
         val spec =
             OverscrollSpecImpl(
                 scene = scene,
@@ -93,19 +113,21 @@
                         distance = impl.distance,
                         transformations = impl.transformations,
                     ),
-                progressConverter = impl.progressConverter
+                progressConverter = impl.progressConverter,
             )
         transitionOverscrollSpecs.add(spec)
         return spec
     }
 
     private fun transition(
-        from: SceneKey?,
-        to: SceneKey?,
+        from: ContentKey?,
+        to: ContentKey?,
         key: TransitionKey?,
+        preview: (TransitionBuilder.() -> Unit)?,
+        reversePreview: (TransitionBuilder.() -> Unit)?,
         builder: TransitionBuilder.() -> Unit,
     ): TransitionSpec {
-        fun transformationSpec(): TransformationSpecImpl {
+        fun transformationSpec(builder: TransitionBuilder.() -> Unit): TransformationSpecImpl {
             val impl = TransitionBuilderImpl().apply(builder)
             return TransformationSpecImpl(
                 progressSpec = impl.spec,
@@ -115,7 +137,18 @@
             )
         }
 
-        val spec = TransitionSpecImpl(key, from, to, ::transformationSpec)
+        val previewTransformationSpec = preview?.let { { transformationSpec(it) } }
+        val reversePreviewTransformationSpec = reversePreview?.let { { transformationSpec(it) } }
+        val transformationSpec = { transformationSpec(builder) }
+        val spec =
+            TransitionSpecImpl(
+                key,
+                from,
+                to,
+                previewTransformationSpec,
+                reversePreviewTransformationSpec,
+                transformationSpec
+            )
         transitionSpecs.add(spec)
         return spec
     }
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/UserActionDistanceScopeImpl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/UserActionDistanceScopeImpl.kt
index b7abb33..0f66804 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/UserActionDistanceScopeImpl.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/UserActionDistanceScopeImpl.kt
@@ -23,13 +23,13 @@
     private val layoutImpl: SceneTransitionLayoutImpl,
 ) : ElementStateScope {
     override fun ElementKey.targetSize(scene: SceneKey): IntSize? {
-        return layoutImpl.elements[this]?.sceneStates?.get(scene)?.targetSize.takeIf {
+        return layoutImpl.elements[this]?.stateByContent?.get(scene)?.targetSize.takeIf {
             it != Element.SizeUnspecified
         }
     }
 
     override fun ElementKey.targetOffset(scene: SceneKey): Offset? {
-        return layoutImpl.elements[this]?.sceneStates?.get(scene)?.targetOffset.takeIf {
+        return layoutImpl.elements[this]?.stateByContent?.get(scene)?.targetOffset.takeIf {
             it != Offset.Unspecified
         }
     }
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Scene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/Content.kt
similarity index 63%
rename from packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Scene.kt
rename to packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/Content.kt
index a49f1af..6f608cb 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Scene.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/Content.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.compose.animation.scene
+package com.android.compose.animation.scene.content
 
 import androidx.compose.foundation.gestures.Orientation
 import androidx.compose.foundation.layout.Box
@@ -29,24 +29,45 @@
 import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.unit.IntSize
 import androidx.compose.ui.zIndex
+import com.android.compose.animation.scene.AnimatedState
+import com.android.compose.animation.scene.ContentKey
+import com.android.compose.animation.scene.ContentScope
+import com.android.compose.animation.scene.Element
+import com.android.compose.animation.scene.ElementContentScope
+import com.android.compose.animation.scene.ElementKey
+import com.android.compose.animation.scene.ElementScope
+import com.android.compose.animation.scene.ElementStateScope
+import com.android.compose.animation.scene.MovableElement
+import com.android.compose.animation.scene.MovableElementContentScope
+import com.android.compose.animation.scene.MovableElementKey
+import com.android.compose.animation.scene.NestedScrollBehavior
+import com.android.compose.animation.scene.SceneTransitionLayoutImpl
+import com.android.compose.animation.scene.SceneTransitionLayoutState
+import com.android.compose.animation.scene.SharedValueType
+import com.android.compose.animation.scene.UserAction
+import com.android.compose.animation.scene.UserActionResult
+import com.android.compose.animation.scene.ValueKey
+import com.android.compose.animation.scene.animateSharedValueAsState
+import com.android.compose.animation.scene.element
 import com.android.compose.animation.scene.modifiers.noResizeDuringTransitions
+import com.android.compose.animation.scene.nestedScrollToScene
 
-/** A scene in a [SceneTransitionLayout]. */
+/** A content defined in a [SceneTransitionLayout], i.e. a scene or an overlay. */
 @Stable
-internal class Scene(
-    val key: SceneKey,
-    layoutImpl: SceneTransitionLayoutImpl,
-    content: @Composable SceneScope.() -> Unit,
+internal sealed class Content(
+    open val key: ContentKey,
+    val layoutImpl: SceneTransitionLayoutImpl,
+    content: @Composable ContentScope.() -> Unit,
     actions: Map<UserAction.Resolved, UserActionResult>,
     zIndex: Float,
 ) {
-    internal val scope = SceneScopeImpl(layoutImpl, this)
+    internal val scope = ContentScopeImpl(layoutImpl, content = this)
 
     var content by mutableStateOf(content)
-    private var _userActions by mutableStateOf(checkValid(actions))
     var zIndex by mutableFloatStateOf(zIndex)
     var targetSize by mutableStateOf(IntSize.Zero)
 
+    private var _userActions by mutableStateOf(checkValid(actions))
     var userActions
         get() = _userActions
         set(value) {
@@ -59,8 +80,8 @@
         userActions.forEach { (action, result) ->
             if (key == result.toScene) {
                 error(
-                    "Transition to the same scene is not supported. Scene $key, action $action," +
-                        " result $result"
+                    "Transition to the same content (scene/overlay) is not supported. Content " +
+                        "$key, action $action, result $result"
                 )
             }
         }
@@ -73,7 +94,7 @@
             modifier
                 .zIndex(zIndex)
                 .approachLayout(
-                    isMeasurementApproachInProgress = { scope.layoutState.isTransitioning() }
+                    isMeasurementApproachInProgress = { layoutImpl.state.isTransitioning() }
                 ) { measurable, constraints ->
                     targetSize = lookaheadSize
                     val placeable = measurable.measure(constraints)
@@ -84,21 +105,19 @@
             scope.content()
         }
     }
-
-    override fun toString(): String {
-        return "Scene(key=$key)"
-    }
 }
 
-internal class SceneScopeImpl(
+internal class ContentScopeImpl(
     private val layoutImpl: SceneTransitionLayoutImpl,
-    private val scene: Scene,
-) : SceneScope, ElementStateScope by layoutImpl.elementStateScope {
-    override val sceneKey: SceneKey = scene.key
+    private val content: Content,
+) : ContentScope, ElementStateScope by layoutImpl.elementStateScope {
+    override val contentKey: ContentKey
+        get() = content.key
+
     override val layoutState: SceneTransitionLayoutState = layoutImpl.state
 
     override fun Modifier.element(key: ElementKey): Modifier {
-        return element(layoutImpl, scene, key)
+        return element(layoutImpl, content, key)
     }
 
     @Composable
@@ -107,28 +126,28 @@
         modifier: Modifier,
         content: @Composable (ElementScope<ElementContentScope>.() -> Unit)
     ) {
-        Element(layoutImpl, scene, key, modifier, content)
+        Element(layoutImpl, this@ContentScopeImpl.content, key, modifier, content)
     }
 
     @Composable
     override fun MovableElement(
-        key: ElementKey,
+        key: MovableElementKey,
         modifier: Modifier,
         content: @Composable (ElementScope<MovableElementContentScope>.() -> Unit)
     ) {
-        MovableElement(layoutImpl, scene, key, modifier, content)
+        MovableElement(layoutImpl, this@ContentScopeImpl.content, key, modifier, content)
     }
 
     @Composable
-    override fun <T> animateSceneValueAsState(
+    override fun <T> animateContentValueAsState(
         value: T,
         key: ValueKey,
         type: SharedValueType<T, *>,
-        canOverflow: Boolean
+        canOverflow: Boolean,
     ): AnimatedState<T> {
         return animateSharedValueAsState(
             layoutImpl = layoutImpl,
-            scene = scene.key,
+            content = content.key,
             element = null,
             key = key,
             value = value,
@@ -141,27 +160,29 @@
         leftBehavior: NestedScrollBehavior,
         rightBehavior: NestedScrollBehavior,
         isExternalOverscrollGesture: () -> Boolean,
-    ): Modifier =
-        nestedScrollToScene(
+    ): Modifier {
+        return nestedScrollToScene(
             layoutImpl = layoutImpl,
             orientation = Orientation.Horizontal,
             topOrLeftBehavior = leftBehavior,
             bottomOrRightBehavior = rightBehavior,
             isExternalOverscrollGesture = isExternalOverscrollGesture,
         )
+    }
 
     override fun Modifier.verticalNestedScrollToScene(
         topBehavior: NestedScrollBehavior,
         bottomBehavior: NestedScrollBehavior,
         isExternalOverscrollGesture: () -> Boolean,
-    ): Modifier =
-        nestedScrollToScene(
+    ): Modifier {
+        return nestedScrollToScene(
             layoutImpl = layoutImpl,
             orientation = Orientation.Vertical,
             topOrLeftBehavior = topBehavior,
             bottomOrRightBehavior = bottomBehavior,
             isExternalOverscrollGesture = isExternalOverscrollGesture,
         )
+    }
 
     override fun Modifier.noResizeDuringTransitions(): Modifier {
         return noResizeDuringTransitions(layoutState = layoutImpl.state)
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/Scene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/Scene.kt
new file mode 100644
index 0000000..4a7a94d
--- /dev/null
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/Scene.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.compose.animation.scene.content
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.Stable
+import com.android.compose.animation.scene.ContentScope
+import com.android.compose.animation.scene.SceneKey
+import com.android.compose.animation.scene.SceneTransitionLayoutImpl
+import com.android.compose.animation.scene.UserAction
+import com.android.compose.animation.scene.UserActionResult
+
+/** A scene defined in a [SceneTransitionLayout]. */
+@Stable
+internal class Scene(
+    override val key: SceneKey,
+    layoutImpl: SceneTransitionLayoutImpl,
+    content: @Composable ContentScope.() -> Unit,
+    actions: Map<UserAction.Resolved, UserActionResult>,
+    zIndex: Float,
+) : Content(key, layoutImpl, content, actions, zIndex) {
+    override fun toString(): String {
+        return "Scene(key=$key)"
+    }
+}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/state/ContentState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/state/ContentState.kt
new file mode 100644
index 0000000..add3934
--- /dev/null
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/state/ContentState.kt
@@ -0,0 +1,237 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.compose.animation.scene.content.state
+
+import androidx.compose.animation.core.Animatable
+import androidx.compose.animation.core.AnimationVector1D
+import androidx.compose.animation.core.spring
+import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.runtime.Stable
+import com.android.compose.animation.scene.ContentKey
+import com.android.compose.animation.scene.OverscrollScope
+import com.android.compose.animation.scene.OverscrollSpecImpl
+import com.android.compose.animation.scene.ProgressVisibilityThreshold
+import com.android.compose.animation.scene.SceneTransitionLayoutImpl
+import com.android.compose.animation.scene.TransformationSpec
+import com.android.compose.animation.scene.TransformationSpecImpl
+import com.android.compose.animation.scene.TransitionKey
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.launch
+
+/** The state associated to one or more contents. */
+@Stable
+sealed interface ContentState<out T : ContentKey> {
+    /** The [content] is idle, it does not animate. */
+    sealed class Idle<T : ContentKey>(val content: T) : ContentState<T>
+
+    /** The content is transitioning with another content. */
+    sealed class Transition<out T : ContentKey>(
+        val fromContent: T,
+        val toContent: T,
+        internal val replacedTransition: Transition<T>?,
+    ) : ContentState<T> {
+        /**
+         * The key of this transition. This should usually be null, but it can be specified to use a
+         * specific set of transformations associated to this transition.
+         */
+        open val key: TransitionKey? = null
+
+        /**
+         * The progress of the transition. This is usually in the `[0; 1]` range, but it can also be
+         * less than `0` or greater than `1` when using transitions with a spring AnimationSpec or
+         * when flinging quickly during a swipe gesture.
+         */
+        abstract val progress: Float
+
+        /** The current velocity of [progress], in progress units. */
+        abstract val progressVelocity: Float
+
+        /** Whether the transition was triggered by user input rather than being programmatic. */
+        abstract val isInitiatedByUserInput: Boolean
+
+        /** Whether user input is currently driving the transition. */
+        abstract val isUserInputOngoing: Boolean
+
+        /**
+         * The progress of the preview transition. This is usually in the `[0; 1]` range, but it can
+         * also be less than `0` or greater than `1` when using transitions with a spring
+         * AnimationSpec or when flinging quickly during a swipe gesture.
+         */
+        internal open val previewProgress: Float = 0f
+
+        /** The current velocity of [previewProgress], in progress units. */
+        internal open val previewProgressVelocity: Float = 0f
+
+        /** Whether the transition is currently in the preview stage */
+        internal open val isInPreviewStage: Boolean = false
+
+        /**
+         * The current [TransformationSpecImpl] and [OverscrollSpecImpl] associated to this
+         * transition.
+         *
+         * Important: These will be set exactly once, when this transition is
+         * [started][MutableSceneTransitionLayoutStateImpl.startTransition].
+         */
+        internal var transformationSpec: TransformationSpecImpl = TransformationSpec.Empty
+        internal var previewTransformationSpec: TransformationSpecImpl? = null
+        private var fromOverscrollSpec: OverscrollSpecImpl? = null
+        private var toOverscrollSpec: OverscrollSpecImpl? = null
+
+        /** The current [OverscrollSpecImpl], if this transition is currently overscrolling. */
+        internal val currentOverscrollSpec: OverscrollSpecImpl?
+            get() {
+                if (this !is HasOverscrollProperties) return null
+                val progress = progress
+                val bouncingContent = bouncingContent
+                return when {
+                    progress < 0f || bouncingContent == fromContent -> fromOverscrollSpec
+                    progress > 1f || bouncingContent == toContent -> toOverscrollSpec
+                    else -> null
+                }
+            }
+
+        /**
+         * An animatable that animates from 1f to 0f. This will be used to nicely animate the sudden
+         * jump of values when this transitions interrupts another one.
+         */
+        private var interruptionDecay: Animatable<Float, AnimationVector1D>? = null
+
+        init {
+            check(fromContent != toContent)
+            check(
+                replacedTransition == null ||
+                    (replacedTransition.fromContent == fromContent &&
+                        replacedTransition.toContent == toContent)
+            )
+        }
+
+        /**
+         * Force this transition to finish and animate to an [Idle] state.
+         *
+         * Important: Once this is called, the effective state of the transition should remain
+         * unchanged. For instance, in the case of a [TransitionState.Transition], its
+         * [currentScene][TransitionState.Transition.currentScene] should never change once [finish]
+         * is called.
+         *
+         * @return the [Job] that animates to the idle state. It can be used to wait until the
+         *   animation is complete or cancel it to snap the animation. Calling [finish] multiple
+         *   times will return the same [Job].
+         */
+        abstract fun finish(): Job
+
+        /**
+         * Whether we are transitioning. If [from] or [to] is empty, we will also check that they
+         * match the contents we are animating from and/or to.
+         */
+        fun isTransitioning(from: ContentKey? = null, to: ContentKey? = null): Boolean {
+            return (from == null || fromContent == from) && (to == null || toContent == to)
+        }
+
+        /** Whether we are transitioning from [content] to [other], or from [other] to [content]. */
+        fun isTransitioningBetween(content: ContentKey, other: ContentKey): Boolean {
+            return isTransitioning(from = content, to = other) ||
+                isTransitioning(from = other, to = content)
+        }
+
+        internal fun updateOverscrollSpecs(
+            fromSpec: OverscrollSpecImpl?,
+            toSpec: OverscrollSpecImpl?,
+        ) {
+            fromOverscrollSpec = fromSpec
+            toOverscrollSpec = toSpec
+        }
+
+        /** Returns if the [progress] value of this transition can go beyond range `[0; 1]` */
+        internal fun isWithinProgressRange(progress: Float): Boolean {
+            // If the properties are missing we assume that every [Transition] can overscroll
+            if (this !is HasOverscrollProperties) return true
+            // [OverscrollSpec] for the current scene, even if it hasn't started overscrolling yet.
+            val specForCurrentScene =
+                when {
+                    progress <= 0f -> fromOverscrollSpec
+                    progress >= 1f -> toOverscrollSpec
+                    else -> null
+                } ?: return true
+
+            return specForCurrentScene.transformationSpec.transformations.isNotEmpty()
+        }
+
+        internal open fun interruptionProgress(
+            layoutImpl: SceneTransitionLayoutImpl,
+        ): Float {
+            if (!layoutImpl.state.enableInterruptions) {
+                return 0f
+            }
+
+            if (replacedTransition != null) {
+                return replacedTransition.interruptionProgress(layoutImpl)
+            }
+
+            fun create(): Animatable<Float, AnimationVector1D> {
+                val animatable = Animatable(1f, visibilityThreshold = ProgressVisibilityThreshold)
+                layoutImpl.coroutineScope.launch {
+                    val swipeSpec = layoutImpl.state.transitions.defaultSwipeSpec
+                    val progressSpec =
+                        spring(
+                            stiffness = swipeSpec.stiffness,
+                            dampingRatio = swipeSpec.dampingRatio,
+                            visibilityThreshold = ProgressVisibilityThreshold,
+                        )
+                    animatable.animateTo(0f, progressSpec)
+                }
+
+                return animatable
+            }
+
+            val animatable = interruptionDecay ?: create().also { interruptionDecay = it }
+            return animatable.value
+        }
+    }
+
+    interface HasOverscrollProperties {
+        /**
+         * The position of the [Transition.toContent].
+         *
+         * Used to understand the direction of the overscroll.
+         */
+        val isUpOrLeft: Boolean
+
+        /**
+         * The relative orientation between [Transition.fromContent] and [Transition.toContent].
+         *
+         * Used to understand the orientation of the overscroll.
+         */
+        val orientation: Orientation
+
+        /**
+         * Scope which can be used in the Overscroll DSL to define a transformation based on the
+         * distance between [Transition.fromContent] and [Transition.toContent].
+         */
+        val overscrollScope: OverscrollScope
+
+        /**
+         * The content (scene or overlay) around which the transition is currently bouncing. When
+         * not `null`, this transition is currently oscillating around this content and will soon
+         * settle to that content.
+         */
+        val bouncingContent: ContentKey?
+
+        companion object {
+            const val DistanceUnspecified = 0f
+        }
+    }
+}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/state/TransitionState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/state/TransitionState.kt
new file mode 100644
index 0000000..77de22c
--- /dev/null
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/state/TransitionState.kt
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.compose.animation.scene.content.state
+
+import androidx.compose.runtime.Stable
+import com.android.compose.animation.scene.SceneKey
+
+/** The state associated to one or more scenes. */
+// TODO(b/353679003): Rename to SceneState.
+@Stable
+sealed interface TransitionState : ContentState<SceneKey> {
+    /**
+     * The current effective scene. If a new transition was triggered, it would start from this
+     * scene.
+     *
+     * For instance, when swiping from scene A to scene B, the [currentScene] is A when the swipe
+     * gesture starts, but then if the user flings their finger and commits the transition to scene
+     * B, then [currentScene] becomes scene B even if the transition is not finished yet and is
+     * still animating to settle to scene B.
+     */
+    val currentScene: SceneKey
+
+    /** The scene [currentScene] is idle. */
+    data class Idle(
+        override val currentScene: SceneKey,
+    ) : TransitionState, ContentState.Idle<SceneKey>(currentScene)
+
+    /** There is a transition animating between [fromScene] and [toScene]. */
+    abstract class Transition(
+        /** The scene this transition is starting from. Can't be the same as toScene */
+        val fromScene: SceneKey,
+
+        /** The scene this transition is going to. Can't be the same as fromScene */
+        val toScene: SceneKey,
+
+        /** The transition that `this` transition is replacing, if any. */
+        replacedTransition: Transition? = null,
+    ) : TransitionState, ContentState.Transition<SceneKey>(fromScene, toScene, replacedTransition)
+}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredSize.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredSize.kt
index 73ee451..538ce79 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredSize.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredSize.kt
@@ -17,12 +17,12 @@
 package com.android.compose.animation.scene.transformation
 
 import androidx.compose.ui.unit.IntSize
+import com.android.compose.animation.scene.ContentKey
 import com.android.compose.animation.scene.Element
 import com.android.compose.animation.scene.ElementKey
 import com.android.compose.animation.scene.ElementMatcher
-import com.android.compose.animation.scene.SceneKey
 import com.android.compose.animation.scene.SceneTransitionLayoutImpl
-import com.android.compose.animation.scene.TransitionState
+import com.android.compose.animation.scene.content.state.ContentState
 
 /** Anchor the size of an element to the size of another element. */
 internal class AnchoredSize(
@@ -33,21 +33,21 @@
 ) : PropertyTransformation<IntSize> {
     override fun transform(
         layoutImpl: SceneTransitionLayoutImpl,
-        scene: SceneKey,
+        content: ContentKey,
         element: Element,
-        sceneState: Element.SceneState,
-        transition: TransitionState.Transition,
+        stateInContent: Element.State,
+        transition: ContentState.Transition<*>,
         value: IntSize,
     ): IntSize {
-        fun anchorSizeIn(scene: SceneKey): IntSize {
+        fun anchorSizeIn(content: ContentKey): IntSize {
             val size =
-                layoutImpl.elements[anchor]?.sceneStates?.get(scene)?.targetSize?.takeIf {
+                layoutImpl.elements[anchor]?.stateByContent?.get(content)?.targetSize?.takeIf {
                     it != Element.SizeUnspecified
                 }
                     ?: throwMissingAnchorException(
                         transformation = "AnchoredSize",
                         anchor = anchor,
-                        scene = scene,
+                        content = content,
                     )
 
             return IntSize(
@@ -59,10 +59,10 @@
         // This simple implementation assumes that the size of [element] is the same as the size of
         // the [anchor] in [scene], so simply transform to the size of the anchor in the other
         // scene.
-        return if (scene == transition.fromScene) {
-            anchorSizeIn(transition.toScene)
+        return if (content == transition.fromContent) {
+            anchorSizeIn(transition.toContent)
         } else {
-            anchorSizeIn(transition.fromScene)
+            anchorSizeIn(transition.fromContent)
         }
     }
 }
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredTranslate.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredTranslate.kt
index 70dca4c..258f541 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredTranslate.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredTranslate.kt
@@ -18,12 +18,12 @@
 
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.geometry.isSpecified
+import com.android.compose.animation.scene.ContentKey
 import com.android.compose.animation.scene.Element
 import com.android.compose.animation.scene.ElementKey
 import com.android.compose.animation.scene.ElementMatcher
-import com.android.compose.animation.scene.SceneKey
 import com.android.compose.animation.scene.SceneTransitionLayoutImpl
-import com.android.compose.animation.scene.TransitionState
+import com.android.compose.animation.scene.content.state.ContentState
 
 /** Anchor the translation of an element to another element. */
 internal class AnchoredTranslate(
@@ -32,35 +32,35 @@
 ) : PropertyTransformation<Offset> {
     override fun transform(
         layoutImpl: SceneTransitionLayoutImpl,
-        scene: SceneKey,
+        content: ContentKey,
         element: Element,
-        sceneState: Element.SceneState,
-        transition: TransitionState.Transition,
+        stateInContent: Element.State,
+        transition: ContentState.Transition<*>,
         value: Offset,
     ): Offset {
-        fun throwException(scene: SceneKey?): Nothing {
+        fun throwException(content: ContentKey?): Nothing {
             throwMissingAnchorException(
                 transformation = "AnchoredTranslate",
                 anchor = anchor,
-                scene = scene,
+                content = content,
             )
         }
 
-        val anchor = layoutImpl.elements[anchor] ?: throwException(scene = null)
-        fun anchorOffsetIn(scene: SceneKey): Offset? {
-            return anchor.sceneStates[scene]?.targetOffset?.takeIf { it.isSpecified }
+        val anchor = layoutImpl.elements[anchor] ?: throwException(content = null)
+        fun anchorOffsetIn(content: ContentKey): Offset? {
+            return anchor.stateByContent[content]?.targetOffset?.takeIf { it.isSpecified }
         }
 
         // [element] will move the same amount as [anchor] does.
         // TODO(b/290184746): Also support anchors that are not shared but translated because of
         // other transformations, like an edge translation.
         val anchorFromOffset =
-            anchorOffsetIn(transition.fromScene) ?: throwException(transition.fromScene)
+            anchorOffsetIn(transition.fromContent) ?: throwException(transition.fromContent)
         val anchorToOffset =
-            anchorOffsetIn(transition.toScene) ?: throwException(transition.toScene)
+            anchorOffsetIn(transition.toContent) ?: throwException(transition.toContent)
         val offset = anchorToOffset - anchorFromOffset
 
-        return if (scene == transition.toScene) {
+        return if (content == transition.toContent) {
             Offset(
                 value.x - offset.x,
                 value.y - offset.y,
@@ -77,11 +77,11 @@
 internal fun throwMissingAnchorException(
     transformation: String,
     anchor: ElementKey,
-    scene: SceneKey?,
+    content: ContentKey?,
 ): Nothing {
     error(
         """
-        Anchor ${anchor.debugName} does not have a target state in scene ${scene?.debugName}.
+        Anchor ${anchor.debugName} does not have a target state in content ${content?.debugName}.
         This either means that it was not composed at all during the transition or that it was
         composed too late, for instance during layout/subcomposition. To avoid flickers in
         $transformation, you should make sure that the composition and layout of anchor is *not*
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/DrawScale.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/DrawScale.kt
index 98c2dd3..be8dac21 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/DrawScale.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/DrawScale.kt
@@ -17,12 +17,12 @@
 package com.android.compose.animation.scene.transformation
 
 import androidx.compose.ui.geometry.Offset
+import com.android.compose.animation.scene.ContentKey
 import com.android.compose.animation.scene.Element
 import com.android.compose.animation.scene.ElementMatcher
 import com.android.compose.animation.scene.Scale
-import com.android.compose.animation.scene.SceneKey
 import com.android.compose.animation.scene.SceneTransitionLayoutImpl
-import com.android.compose.animation.scene.TransitionState
+import com.android.compose.animation.scene.content.state.ContentState
 
 /**
  * Scales the draw size of an element. Note this will only scale the draw inside of an element,
@@ -37,10 +37,10 @@
 
     override fun transform(
         layoutImpl: SceneTransitionLayoutImpl,
-        scene: SceneKey,
+        content: ContentKey,
         element: Element,
-        sceneState: Element.SceneState,
-        transition: TransitionState.Transition,
+        stateInContent: Element.State,
+        transition: ContentState.Transition<*>,
         value: Scale,
     ): Scale {
         return Scale(scaleX, scaleY, pivot)
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/EdgeTranslate.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/EdgeTranslate.kt
index 7daefd0..d72e43a 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/EdgeTranslate.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/EdgeTranslate.kt
@@ -17,12 +17,12 @@
 package com.android.compose.animation.scene.transformation
 
 import androidx.compose.ui.geometry.Offset
+import com.android.compose.animation.scene.ContentKey
 import com.android.compose.animation.scene.Edge
 import com.android.compose.animation.scene.Element
 import com.android.compose.animation.scene.ElementMatcher
-import com.android.compose.animation.scene.SceneKey
 import com.android.compose.animation.scene.SceneTransitionLayoutImpl
-import com.android.compose.animation.scene.TransitionState
+import com.android.compose.animation.scene.content.state.ContentState
 
 /** Translate an element from an edge of the layout. */
 internal class EdgeTranslate(
@@ -32,14 +32,14 @@
 ) : PropertyTransformation<Offset> {
     override fun transform(
         layoutImpl: SceneTransitionLayoutImpl,
-        scene: SceneKey,
+        content: ContentKey,
         element: Element,
-        sceneState: Element.SceneState,
-        transition: TransitionState.Transition,
+        stateInContent: Element.State,
+        transition: ContentState.Transition<*>,
         value: Offset
     ): Offset {
-        val sceneSize = layoutImpl.scene(scene).targetSize
-        val elementSize = sceneState.targetSize
+        val sceneSize = layoutImpl.content(content).targetSize
+        val elementSize = stateInContent.targetSize
         if (elementSize == Element.SizeUnspecified) {
             return value
         }
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Fade.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Fade.kt
index ada814e..92ae30f8 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Fade.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Fade.kt
@@ -16,11 +16,11 @@
 
 package com.android.compose.animation.scene.transformation
 
+import com.android.compose.animation.scene.ContentKey
 import com.android.compose.animation.scene.Element
 import com.android.compose.animation.scene.ElementMatcher
-import com.android.compose.animation.scene.SceneKey
 import com.android.compose.animation.scene.SceneTransitionLayoutImpl
-import com.android.compose.animation.scene.TransitionState
+import com.android.compose.animation.scene.content.state.ContentState
 
 /** Fade an element in or out. */
 internal class Fade(
@@ -28,10 +28,10 @@
 ) : PropertyTransformation<Float> {
     override fun transform(
         layoutImpl: SceneTransitionLayoutImpl,
-        scene: SceneKey,
+        content: ContentKey,
         element: Element,
-        sceneState: Element.SceneState,
-        transition: TransitionState.Transition,
+        stateInContent: Element.State,
+        transition: ContentState.Transition<*>,
         value: Float
     ): Float {
         // Return the alpha value of [element] either when it starts fading in or when it finished
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/ScaleSize.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/ScaleSize.kt
index dca8f85..e8515dc 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/ScaleSize.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/ScaleSize.kt
@@ -17,11 +17,11 @@
 package com.android.compose.animation.scene.transformation
 
 import androidx.compose.ui.unit.IntSize
+import com.android.compose.animation.scene.ContentKey
 import com.android.compose.animation.scene.Element
 import com.android.compose.animation.scene.ElementMatcher
-import com.android.compose.animation.scene.SceneKey
 import com.android.compose.animation.scene.SceneTransitionLayoutImpl
-import com.android.compose.animation.scene.TransitionState
+import com.android.compose.animation.scene.content.state.ContentState
 import kotlin.math.roundToInt
 
 /**
@@ -35,10 +35,10 @@
 ) : PropertyTransformation<IntSize> {
     override fun transform(
         layoutImpl: SceneTransitionLayoutImpl,
-        scene: SceneKey,
+        content: ContentKey,
         element: Element,
-        sceneState: Element.SceneState,
-        transition: TransitionState.Transition,
+        stateInContent: Element.State,
+        transition: ContentState.Transition<*>,
         value: IntSize,
     ): IntSize {
         return IntSize(
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Transformation.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Transformation.kt
index 7be9ce1..77ec891 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Transformation.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Transformation.kt
@@ -19,11 +19,11 @@
 import androidx.compose.ui.util.fastCoerceAtLeast
 import androidx.compose.ui.util.fastCoerceAtMost
 import androidx.compose.ui.util.fastCoerceIn
+import com.android.compose.animation.scene.ContentKey
 import com.android.compose.animation.scene.Element
 import com.android.compose.animation.scene.ElementMatcher
-import com.android.compose.animation.scene.SceneKey
 import com.android.compose.animation.scene.SceneTransitionLayoutImpl
-import com.android.compose.animation.scene.TransitionState
+import com.android.compose.animation.scene.content.state.ContentState
 
 /** A transformation applied to one or more elements during a transition. */
 sealed interface Transformation {
@@ -61,10 +61,10 @@
     // to these internal classes.
     fun transform(
         layoutImpl: SceneTransitionLayoutImpl,
-        scene: SceneKey,
+        content: ContentKey,
         element: Element,
-        sceneState: Element.SceneState,
-        transition: TransitionState.Transition,
+        stateInContent: Element.State,
+        transition: ContentState.Transition<*>,
         value: T,
     ): T
 }
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Translate.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Translate.kt
index f066511..fab4ced 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Translate.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Translate.kt
@@ -19,12 +19,12 @@
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.dp
+import com.android.compose.animation.scene.ContentKey
 import com.android.compose.animation.scene.Element
 import com.android.compose.animation.scene.ElementMatcher
 import com.android.compose.animation.scene.OverscrollScope
-import com.android.compose.animation.scene.SceneKey
 import com.android.compose.animation.scene.SceneTransitionLayoutImpl
-import com.android.compose.animation.scene.TransitionState
+import com.android.compose.animation.scene.content.state.ContentState
 
 internal class Translate(
     override val matcher: ElementMatcher,
@@ -33,10 +33,10 @@
 ) : PropertyTransformation<Offset> {
     override fun transform(
         layoutImpl: SceneTransitionLayoutImpl,
-        scene: SceneKey,
+        content: ContentKey,
         element: Element,
-        sceneState: Element.SceneState,
-        transition: TransitionState.Transition,
+        stateInContent: Element.State,
+        transition: ContentState.Transition<*>,
         value: Offset,
     ): Offset {
         return with(layoutImpl.density) {
@@ -55,16 +55,16 @@
 ) : PropertyTransformation<Offset> {
     override fun transform(
         layoutImpl: SceneTransitionLayoutImpl,
-        scene: SceneKey,
+        content: ContentKey,
         element: Element,
-        sceneState: Element.SceneState,
-        transition: TransitionState.Transition,
+        stateInContent: Element.State,
+        transition: ContentState.Transition<*>,
         value: Offset,
     ): Offset {
         // As this object is created by OverscrollBuilderImpl and we retrieve the current
         // OverscrollSpec only when the transition implements HasOverscrollProperties, we can assume
         // that this method was invoked after performing this check.
-        val overscrollProperties = transition as TransitionState.HasOverscrollProperties
+        val overscrollProperties = transition as ContentState.HasOverscrollProperties
 
         return Offset(
             x = value.x + overscrollProperties.overscrollScope.x(),
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transition/link/LinkedTransition.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transition/link/LinkedTransition.kt
index ed98885..23bcf10 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transition/link/LinkedTransition.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transition/link/LinkedTransition.kt
@@ -18,7 +18,7 @@
 
 import com.android.compose.animation.scene.SceneKey
 import com.android.compose.animation.scene.TransitionKey
-import com.android.compose.animation.scene.TransitionState
+import com.android.compose.animation.scene.content.state.TransitionState
 import kotlinx.coroutines.Job
 
 /** A linked transition which is driven by a [originalTransition]. */
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transition/link/StateLink.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transition/link/StateLink.kt
index 2018d6e..c0c40dd 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transition/link/StateLink.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transition/link/StateLink.kt
@@ -20,7 +20,7 @@
 import com.android.compose.animation.scene.SceneKey
 import com.android.compose.animation.scene.SceneTransitionLayoutState
 import com.android.compose.animation.scene.TransitionKey
-import com.android.compose.animation.scene.TransitionState
+import com.android.compose.animation.scene.content.state.TransitionState
 
 /** A link between a source (implicit) and [target] `SceneTransitionLayoutState`. */
 class StateLink(target: SceneTransitionLayoutState, val transitionLinks: List<TransitionLink>) {
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnection.kt b/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnection.kt
index 8e35988..ae3169b 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnection.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnection.kt
@@ -57,7 +57,7 @@
             minHeight() < currentHeight && currentHeight < maxHeight()
         },
         canScrollOnFling = true,
-        onStart = { /* do nothing */},
+        onStart = { /* do nothing */ },
         onScroll = { offsetAvailable ->
             val currentHeight = height()
             val amountConsumed =
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityNestedScrollConnection.kt b/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityNestedScrollConnection.kt
index ac11d30..228f7ba 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityNestedScrollConnection.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityNestedScrollConnection.kt
@@ -38,7 +38,7 @@
     private val canStartPreScroll: (offsetAvailable: Offset, offsetBeforeStart: Offset) -> Boolean,
     private val canStartPostScroll: (offsetAvailable: Offset, offsetBeforeStart: Offset) -> Boolean,
     private val canStartPostFling: (velocityAvailable: Velocity) -> Boolean,
-    private val canContinueScroll: () -> Boolean,
+    private val canContinueScroll: (source: NestedScrollSource) -> Boolean,
     private val canScrollOnFling: Boolean,
     private val onStart: (offsetAvailable: Offset) -> Unit,
     private val onScroll: (offsetAvailable: Offset) -> Offset,
@@ -61,7 +61,7 @@
 
         if (
             isPriorityMode ||
-                (source == NestedScrollSource.Fling && !canScrollOnFling) ||
+                (source == NestedScrollSource.SideEffect && !canScrollOnFling) ||
                 !canStartPostScroll(available, offsetBeforeStart)
         ) {
             // The priority mode cannot start so we won't consume the available offset.
@@ -73,7 +73,7 @@
 
     override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
         if (!isPriorityMode) {
-            if (source != NestedScrollSource.Fling || canScrollOnFling) {
+            if (source == NestedScrollSource.UserInput || canScrollOnFling) {
                 if (canStartPreScroll(available, offsetScrolledBeforePriorityMode)) {
                     return onPriorityStart(available)
                 }
@@ -84,7 +84,7 @@
             return Offset.Zero
         }
 
-        if (!canContinueScroll()) {
+        if (!canContinueScroll(source)) {
             // Step 3a: We have lost priority and we no longer need to intercept scroll events.
             onPriorityStop(velocity = Velocity.Zero)
 
@@ -170,7 +170,7 @@
     canStartPreScroll: (offsetAvailable: Float, offsetBeforeStart: Float) -> Boolean,
     canStartPostScroll: (offsetAvailable: Float, offsetBeforeStart: Float) -> Boolean,
     canStartPostFling: (velocityAvailable: Float) -> Boolean,
-    canContinueScroll: () -> Boolean,
+    canContinueScroll: (source: NestedScrollSource) -> Boolean,
     canScrollOnFling: Boolean,
     onStart: (offsetAvailable: Float) -> Unit,
     onScroll: (offsetAvailable: Float) -> Float,
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/AnimatedSharedAsStateTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/AnimatedSharedAsStateTest.kt
index a7889e2..01895c9 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/AnimatedSharedAsStateTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/AnimatedSharedAsStateTest.kt
@@ -67,7 +67,7 @@
     }
 
     @Composable
-    private fun SceneScope.Foo(
+    private fun ContentScope.Foo(
         targetValues: Values,
         onCurrentValueChanged: (Values) -> Unit,
     ) {
@@ -87,12 +87,12 @@
     }
 
     @Composable
-    private fun SceneScope.MovableFoo(
+    private fun ContentScope.MovableFoo(
+        key: MovableElementKey,
         targetValues: Values,
         onCurrentValueChanged: (Values) -> Unit,
     ) {
-        val key = TestElements.Foo
-        MovableElement(key = key, Modifier) {
+        MovableElement(key, Modifier) {
             val int by animateElementIntAsState(targetValues.int, key = TestValues.Value1)
             val float by animateElementFloatAsState(targetValues.float, key = TestValues.Value2)
             val dp by animateElementDpAsState(targetValues.dp, key = TestValues.Value3)
@@ -105,14 +105,14 @@
     }
 
     @Composable
-    private fun SceneScope.SceneValues(
+    private fun ContentScope.SceneValues(
         targetValues: Values,
         onCurrentValueChanged: (Values) -> Unit,
     ) {
-        val int by animateSceneIntAsState(targetValues.int, key = TestValues.Value1)
-        val float by animateSceneFloatAsState(targetValues.float, key = TestValues.Value2)
-        val dp by animateSceneDpAsState(targetValues.dp, key = TestValues.Value3)
-        val color by animateSceneColorAsState(targetValues.color, key = TestValues.Value4)
+        val int by animateContentIntAsState(targetValues.int, key = TestValues.Value1)
+        val float by animateContentFloatAsState(targetValues.float, key = TestValues.Value2)
+        val dp by animateContentDpAsState(targetValues.dp, key = TestValues.Value3)
+        val color by animateContentColorAsState(targetValues.color, key = TestValues.Value4)
 
         LaunchedEffect(Unit) {
             snapshotFlow { Values(int, float, dp, color) }.collect(onCurrentValueChanged)
@@ -183,15 +183,22 @@
         var lastValueInFrom = fromValues
         var lastValueInTo = toValues
 
+        val key = MovableElementKey("Foo", contents = setOf(SceneA, SceneB))
+
         rule.testTransition(
             fromSceneContent = {
                 MovableFoo(
+                    key = key,
                     targetValues = fromValues,
-                    onCurrentValueChanged = { lastValueInFrom = it }
+                    onCurrentValueChanged = { lastValueInFrom = it },
                 )
             },
             toSceneContent = {
-                MovableFoo(targetValues = toValues, onCurrentValueChanged = { lastValueInTo = it })
+                MovableFoo(
+                    key = key,
+                    targetValues = toValues,
+                    onCurrentValueChanged = { lastValueInTo = it },
+                )
             },
             transition = {
                 // The transition lasts 64ms = 4 frames.
@@ -292,7 +299,7 @@
     fun readingAnimatedStateValueDuringCompositionThrows() {
         assertThrows(IllegalStateException::class.java) {
             rule.testTransition(
-                fromSceneContent = { animateSceneIntAsState(0, TestValues.Value1).value },
+                fromSceneContent = { animateContentIntAsState(0, TestValues.Value1).value },
                 toSceneContent = {},
                 transition = {},
             ) {}
@@ -302,21 +309,21 @@
     @Test
     fun readingAnimatedStateValueDuringCompositionIsStillPossible() {
         @Composable
-        fun SceneScope.SceneValuesDuringComposition(
+        fun ContentScope.SceneValuesDuringComposition(
             targetValues: Values,
             onCurrentValueChanged: (Values) -> Unit,
         ) {
             val int by
-                animateSceneIntAsState(targetValues.int, key = TestValues.Value1)
+                animateContentIntAsState(targetValues.int, key = TestValues.Value1)
                     .unsafeCompositionState(targetValues.int)
             val float by
-                animateSceneFloatAsState(targetValues.float, key = TestValues.Value2)
+                animateContentFloatAsState(targetValues.float, key = TestValues.Value2)
                     .unsafeCompositionState(targetValues.float)
             val dp by
-                animateSceneDpAsState(targetValues.dp, key = TestValues.Value3)
+                animateContentDpAsState(targetValues.dp, key = TestValues.Value3)
                     .unsafeCompositionState(targetValues.dp)
             val color by
-                animateSceneColorAsState(targetValues.color, key = TestValues.Value4)
+                animateContentColorAsState(targetValues.color, key = TestValues.Value4)
                     .unsafeCompositionState(targetValues.color)
 
             val values = Values(int, float, dp, color)
@@ -397,14 +404,14 @@
 
         val foo = ValueKey("foo")
         val bar = ValueKey("bar")
-        val lastValues = mutableMapOf<ValueKey, MutableMap<SceneKey, Float>>()
+        val lastValues = mutableMapOf<ValueKey, MutableMap<ContentKey, Float>>()
 
         @Composable
-        fun SceneScope.animateFloat(value: Float, key: ValueKey) {
-            val animatedValue = animateSceneFloatAsState(value, key)
+        fun ContentScope.animateFloat(value: Float, key: ValueKey) {
+            val animatedValue = animateContentFloatAsState(value, key)
             LaunchedEffect(animatedValue) {
                 snapshotFlow { animatedValue.value }
-                    .collect { lastValues.getOrPut(key) { mutableMapOf() }[sceneKey] = it }
+                    .collect { lastValues.getOrPut(key) { mutableMapOf() }[contentKey] = it }
             }
         }
 
@@ -453,18 +460,18 @@
             rule.runOnUiThread {
                 MutableSceneTransitionLayoutStateImpl(
                     SceneA,
-                    transitions { overscroll(SceneB, Orientation.Horizontal) }
+                    transitions { overscrollDisabled(SceneB, Orientation.Horizontal) }
                 )
             }
 
         val key = ValueKey("foo")
-        val lastValues = mutableMapOf<SceneKey, Float>()
+        val lastValues = mutableMapOf<ContentKey, Float>()
 
         @Composable
-        fun SceneScope.animateFloat(value: Float, key: ValueKey) {
-            val animatedValue = animateSceneFloatAsState(value, key)
+        fun ContentScope.animateFloat(value: Float, key: ValueKey) {
+            val animatedValue = animateContentFloatAsState(value, key)
             LaunchedEffect(animatedValue) {
-                snapshotFlow { animatedValue.value }.collect { lastValues[sceneKey] = it }
+                snapshotFlow { animatedValue.value }.collect { lastValues[contentKey] = it }
             }
         }
 
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 7a5a84e..dc5b2f7 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
@@ -35,7 +35,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.TransitionState.Transition
+import com.android.compose.animation.scene.content.state.TransitionState
+import com.android.compose.animation.scene.content.state.TransitionState.Transition
 import com.android.compose.animation.scene.subjects.assertThat
 import com.android.compose.test.MonotonicClockTestScope
 import com.android.compose.test.runMonotonicClockTest
@@ -195,10 +196,17 @@
             startedPosition: Offset = Offset.Zero,
             overSlop: Float,
             pointersDown: Int = 1,
+            expectedConsumedOverSlop: Float = overSlop,
         ): DragController {
             // overSlop should be 0f only if the drag gesture starts with startDragImmediately
             if (overSlop == 0f) error("Consider using onDragStartedImmediately()")
-            return onDragStarted(draggableHandler, startedPosition, overSlop, pointersDown)
+            return onDragStarted(
+                draggableHandler = draggableHandler,
+                startedPosition = startedPosition,
+                overSlop = overSlop,
+                pointersDown = pointersDown,
+                expectedConsumedOverSlop = expectedConsumedOverSlop,
+            )
         }
 
         fun onDragStartedImmediately(
@@ -212,7 +220,8 @@
             draggableHandler: DraggableHandler,
             startedPosition: Offset = Offset.Zero,
             overSlop: Float = 0f,
-            pointersDown: Int = 1
+            pointersDown: Int = 1,
+            expectedConsumedOverSlop: Float = overSlop,
         ): DragController {
             val dragController =
                 draggableHandler.onDragStarted(
@@ -222,17 +231,23 @@
                 )
 
             // MultiPointerDraggable will always call onDelta with the initial overSlop right after
-            dragController.onDragDelta(pixels = overSlop)
+            dragController.onDragDelta(pixels = overSlop, expectedConsumedOverSlop)
 
             return dragController
         }
 
-        fun DragController.onDragDelta(pixels: Float) {
-            onDrag(delta = pixels)
+        fun DragController.onDragDelta(pixels: Float, expectedConsumed: Float = pixels) {
+            val consumed = onDrag(delta = pixels)
+            assertThat(consumed).isEqualTo(expectedConsumed)
         }
 
-        fun DragController.onDragStopped(velocity: Float, canChangeScene: Boolean = true) {
-            onStop(velocity, canChangeScene)
+        fun DragController.onDragStopped(
+            velocity: Float,
+            canChangeScene: Boolean = true,
+            expectedConsumed: Boolean = true
+        ) {
+            val consumed = onStop(velocity, canChangeScene)
+            assertThat(consumed).isEqualTo(if (expectedConsumed) velocity else 0f)
         }
 
         fun NestedScrollConnection.scroll(
@@ -360,10 +375,18 @@
 
     @Test
     fun onDragStartedWithoutActionsInBothDirections_stayIdle() = runGestureTest {
-        onDragStarted(horizontalDraggableHandler, overSlop = up(fractionOfScreen = 0.3f))
+        onDragStarted(
+            horizontalDraggableHandler,
+            overSlop = up(fractionOfScreen = 0.3f),
+            expectedConsumedOverSlop = 0f,
+        )
         assertIdle(currentScene = SceneA)
 
-        onDragStarted(horizontalDraggableHandler, overSlop = down(fractionOfScreen = 0.3f))
+        onDragStarted(
+            horizontalDraggableHandler,
+            overSlop = down(fractionOfScreen = 0.3f),
+            expectedConsumedOverSlop = 0f,
+        )
         assertIdle(currentScene = SceneA)
     }
 
@@ -489,19 +512,19 @@
 
         // start accelaratedScroll and scroll over to B -> null
         val dragController2 = onDragStartedImmediately()
-        dragController2.onDragDelta(pixels = up(fractionOfScreen = 0.5f))
-        dragController2.onDragDelta(pixels = up(fractionOfScreen = 0.5f))
+        dragController2.onDragDelta(pixels = up(fractionOfScreen = 0.5f), expectedConsumed = 0f)
+        dragController2.onDragDelta(pixels = up(fractionOfScreen = 0.5f), expectedConsumed = 0f)
 
         // here onDragStopped is already triggered, but subsequent onDelta/onDragStopped calls may
         // still be called. Make sure that they don't crash or change the scene
-        dragController2.onDragDelta(pixels = up(fractionOfScreen = 0.5f))
+        dragController2.onDragDelta(pixels = up(fractionOfScreen = 0.5f), expectedConsumed = 0f)
         dragController2.onDragStopped(velocity = 0f)
 
         advanceUntilIdle()
         assertIdle(SceneB)
 
         // These events can still come in after the animation has settled
-        dragController2.onDragDelta(pixels = up(fractionOfScreen = 0.5f))
+        dragController2.onDragDelta(pixels = up(fractionOfScreen = 0.5f), expectedConsumed = 0f)
         dragController2.onDragStopped(velocity = 0f)
         assertIdle(SceneB)
     }
@@ -845,7 +868,7 @@
         assertThat(progress).isEqualTo(0.2f)
 
         // this should be ignored, we are scrolling now!
-        dragController.onDragStopped(-velocityThreshold)
+        dragController.onDragStopped(-velocityThreshold, expectedConsumed = false)
         assertTransition(currentScene = SceneA)
 
         nestedScroll.scroll(available = -offsetY10)
@@ -1032,12 +1055,20 @@
     @Test
     fun emptyOverscrollImmediatelyAbortsSettleAnimationWhenOverProgress() = runGestureTest {
         // Overscrolling on scene B does nothing.
-        layoutState.transitions = transitions { overscroll(SceneB, Orientation.Vertical) {} }
+        layoutState.transitions = transitions { overscrollDisabled(SceneB, Orientation.Vertical) }
 
         // Swipe up to scene B at progress = 200%.
         val middle = Offset(SCREEN_SIZE / 2f, SCREEN_SIZE / 2f)
-        val dragController = onDragStarted(startedPosition = middle, overSlop = up(2f))
-        val transition = assertTransition(fromScene = SceneA, toScene = SceneB, progress = 2f)
+        val dragController =
+            onDragStarted(
+                startedPosition = middle,
+                overSlop = up(2f),
+                // Overscroll is disabled, it will scroll up to 100%
+                expectedConsumedOverSlop = up(1f),
+            )
+
+        // The progress value is coerced in `[0..1]`
+        assertTransition(fromScene = SceneA, toScene = SceneB, progress = 1f)
 
         // Release the finger.
         dragController.onDragStopped(velocity = -velocityThreshold)
@@ -1046,9 +1077,6 @@
         // 100% and that the overscroll on scene B is doing nothing, we are already idle.
         runCurrent()
         assertIdle(SceneB)
-
-        // Progress is snapped to 100%.
-        assertThat(transition).hasProgress(1f)
     }
 
     @Test
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementScenePickerTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementContentPickerTest.kt
similarity index 93%
rename from packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementScenePickerTest.kt
rename to packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementContentPickerTest.kt
index 3b022e8..96e521b 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementScenePickerTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementContentPickerTest.kt
@@ -31,12 +31,12 @@
 import org.junit.runner.RunWith
 
 @RunWith(AndroidJUnit4::class)
-class ElementScenePickerTest {
+class ElementContentPickerTest {
     @get:Rule val rule = createComposeRule()
 
     @Test
     fun highestZIndexPicker() {
-        val key = ElementKey("TestElement", scenePicker = HighestZIndexScenePicker)
+        val key = ElementKey("TestElement", contentPicker = HighestZIndexContentPicker)
         rule.testTransition(
             fromSceneContent = { Box(Modifier.element(key).size(10.dp)) },
             toSceneContent = { Box(Modifier.element(key).size(10.dp)) },
@@ -62,7 +62,7 @@
 
     @Test
     fun lowestZIndexPicker() {
-        val key = ElementKey("TestElement", scenePicker = LowestZIndexScenePicker)
+        val key = ElementKey("TestElement", contentPicker = LowestZIndexContentPicker)
         rule.testTransition(
             fromSceneContent = { Box(Modifier.element(key).size(10.dp)) },
             toSceneContent = { Box(Modifier.element(key).size(10.dp)) },
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 c91151e..75f44ff 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
@@ -86,7 +86,7 @@
     @get:Rule val rule = createComposeRule()
 
     @Composable
-    private fun SceneScope.Element(
+    private fun ContentScope.Element(
         key: ElementKey,
         size: Dp,
         offset: Dp,
@@ -380,7 +380,7 @@
 
         assertThat(layoutImpl.elements.keys).containsExactly(key)
         val element = layoutImpl.elements.getValue(key)
-        assertThat(element.sceneStates.keys).containsExactly(SceneB)
+        assertThat(element.stateByContent.keys).containsExactly(SceneB)
 
         // Scene C, state 0: the same element is reused.
         rule.runOnUiThread { state.setTargetScene(SceneC, coroutineScope) }
@@ -389,13 +389,13 @@
 
         assertThat(layoutImpl.elements.keys).containsExactly(key)
         assertThat(layoutImpl.elements.getValue(key)).isSameInstanceAs(element)
-        assertThat(element.sceneStates.keys).containsExactly(SceneC)
+        assertThat(element.stateByContent.keys).containsExactly(SceneC)
 
         // Scene C, state 1: the element is removed from the map.
         sceneCState = 1
         rule.waitForIdle()
 
-        assertThat(element.sceneStates).isEmpty()
+        assertThat(element.stateByContent).isEmpty()
         assertThat(layoutImpl.elements).isEmpty()
     }
 
@@ -405,7 +405,7 @@
 
         assertThrows(IllegalStateException::class.java) {
             rule.setContent {
-                TestSceneScope {
+                TestContentScope {
                     Column {
                         Box(Modifier.element(key))
                         Box(Modifier.element(key))
@@ -421,7 +421,7 @@
 
         assertThrows(IllegalStateException::class.java) {
             rule.setContent {
-                TestSceneScope {
+                TestContentScope {
                     Column {
                         val childModifier = Modifier.element(key)
                         Box(childModifier)
@@ -439,7 +439,7 @@
         assertThrows(IllegalStateException::class.java) {
             var nElements by mutableStateOf(1)
             rule.setContent {
-                TestSceneScope {
+                TestContentScope {
                     Column {
                         val childModifier = Modifier.element(key)
                         repeat(nElements) { Box(childModifier) }
@@ -457,7 +457,7 @@
         assertThrows(IllegalStateException::class.java) {
             var key by mutableStateOf(TestElements.Foo)
             rule.setContent {
-                TestSceneScope {
+                TestContentScope {
                     Column {
                         Box(Modifier.element(key))
                         Box(Modifier.element(TestElements.Bar))
@@ -491,7 +491,7 @@
         // There is only Foo in the elements map.
         assertThat(layoutImpl.elements.keys).containsExactly(TestElements.Foo)
         val fooElement = layoutImpl.elements.getValue(TestElements.Foo)
-        assertThat(fooElement.sceneStates.keys).containsExactly(SceneA)
+        assertThat(fooElement.stateByContent.keys).containsExactly(SceneA)
 
         key = TestElements.Bar
 
@@ -499,8 +499,8 @@
         rule.waitForIdle()
         assertThat(layoutImpl.elements.keys).containsExactly(TestElements.Bar)
         val barElement = layoutImpl.elements.getValue(TestElements.Bar)
-        assertThat(barElement.sceneStates.keys).containsExactly(SceneA)
-        assertThat(fooElement.sceneStates).isEmpty()
+        assertThat(barElement.stateByContent.keys).containsExactly(SceneA)
+        assertThat(fooElement.stateByContent).isEmpty()
     }
 
     @Test
@@ -553,7 +553,7 @@
         // There is only Foo in the elements map.
         assertThat(layoutImpl.elements.keys).containsExactly(TestElements.Foo)
         val element = layoutImpl.elements.getValue(TestElements.Foo)
-        val sceneValues = element.sceneStates
+        val sceneValues = element.stateByContent
         assertThat(sceneValues.keys).containsExactly(SceneA)
 
         // Get the ElementModifier node that should be reused later on when coming back to this
@@ -576,7 +576,7 @@
 
         assertThat(layoutImpl.elements.keys).containsExactly(TestElements.Foo)
         val newElement = layoutImpl.elements.getValue(TestElements.Foo)
-        val newSceneValues = newElement.sceneStates
+        val newSceneValues = newElement.stateByContent
         assertThat(newElement).isNotEqualTo(element)
         assertThat(newSceneValues).isNotEqualTo(sceneValues)
         assertThat(newSceneValues.keys).containsExactly(SceneA)
@@ -677,7 +677,7 @@
                 modifier = Modifier.size(layoutWidth, layoutHeight)
             ) {
                 scene(key = SceneA, userActions = mapOf(Swipe.Down to SceneB)) {
-                    animateSceneFloatAsState(
+                    animateContentFloatAsState(
                         value = animatedFloatRange.start,
                         key = TestValues.Value1,
                         false
@@ -686,7 +686,7 @@
                 }
                 scene(SceneB) {
                     val animatedFloat by
-                        animateSceneFloatAsState(
+                        animateContentFloatAsState(
                             value = animatedFloatRange.endInclusive,
                             key = TestValues.Value1,
                             canOverflow = false
@@ -1215,15 +1215,15 @@
             }
 
         val layoutSize = DpSize(200.dp, 100.dp)
-        val lastValues = mutableMapOf<SceneKey, Float>()
+        val lastValues = mutableMapOf<ContentKey, Float>()
 
         @Composable
-        fun SceneScope.Foo(size: Dp, value: Float, modifier: Modifier = Modifier) {
-            val sceneKey = this.sceneKey
+        fun ContentScope.Foo(size: Dp, value: Float, modifier: Modifier = Modifier) {
+            val contentKey = this.contentKey
             Element(TestElements.Foo, modifier.size(size)) {
                 val animatedValue = animateElementFloatAsState(value, TestValues.Value1)
                 LaunchedEffect(animatedValue) {
-                    snapshotFlow { animatedValue.value }.collect { lastValues[sceneKey] = it }
+                    snapshotFlow { animatedValue.value }.collect { lastValues[contentKey] = it }
                 }
             }
         }
@@ -1388,8 +1388,8 @@
 
         // The interruption values should be unspecified and deltas should be set to zero.
         val foo = layoutImpl.elements.getValue(TestElements.Foo)
-        assertThat(foo.sceneStates.keys).containsExactly(SceneC)
-        val stateInC = foo.sceneStates.getValue(SceneC)
+        assertThat(foo.stateByContent.keys).containsExactly(SceneC)
+        val stateInC = foo.stateByContent.getValue(SceneC)
         assertThat(stateInC.offsetBeforeInterruption).isEqualTo(Offset.Unspecified)
         assertThat(stateInC.sizeBeforeInterruption).isEqualTo(Element.SizeUnspecified)
         assertThat(stateInC.scaleBeforeInterruption).isEqualTo(Scale.Unspecified)
@@ -1423,7 +1423,7 @@
             }
 
         @Composable
-        fun SceneScope.Foo(modifier: Modifier = Modifier) {
+        fun ContentScope.Foo(modifier: Modifier = Modifier) {
             Box(modifier.element(TestElements.Foo).size(fooSize))
         }
 
@@ -1513,7 +1513,7 @@
             rule.runOnUiThread {
                 MutableSceneTransitionLayoutStateImpl(
                         SceneA,
-                        transitions { overscroll(SceneA, Orientation.Horizontal) {} }
+                        transitions { overscrollDisabled(SceneA, Orientation.Horizontal) }
                     )
                     .apply {
                         startTransition(
@@ -1542,8 +1542,8 @@
         assertThat(layoutImpl.elements).containsKey(TestElements.Foo)
         val foo = layoutImpl.elements.getValue(TestElements.Foo)
 
-        assertThat(foo.sceneStates).containsKey(SceneB)
-        val bState = foo.sceneStates.getValue(SceneB)
+        assertThat(foo.stateByContent).containsKey(SceneB)
+        val bState = foo.stateByContent.getValue(SceneB)
 
         assertThat(bState.targetSize).isNotEqualTo(Element.SizeUnspecified)
         assertThat(bState.targetOffset).isNotEqualTo(Offset.Unspecified)
@@ -1583,9 +1583,9 @@
         rule.waitForIdle()
 
         val foo = checkNotNull(layoutImpl.elements[TestElements.Foo])
-        assertThat(foo.sceneStates[SceneA]).isNull()
+        assertThat(foo.stateByContent[SceneA]).isNull()
 
-        val fooInB = foo.sceneStates[SceneB]
+        val fooInB = foo.stateByContent[SceneB]
         assertThat(fooInB).isNotNull()
         assertThat(fooInB!!.lastAlpha).isEqualTo(0.5f)
 
@@ -1599,7 +1599,7 @@
             state.startTransition(transition(from = SceneB, to = SceneC, progress = { 0.3f }))
         }
         rule.waitForIdle()
-        val fooInC = foo.sceneStates[SceneC]
+        val fooInC = foo.stateByContent[SceneC]
         assertThat(fooInC).isNotNull()
         assertThat(fooInC!!.lastAlpha).isEqualTo(1f)
         assertThat(fooInB.lastAlpha).isEqualTo(Element.AlphaUnspecified)
@@ -1645,7 +1645,7 @@
         rule.waitForIdle()
 
         // Alpha of Foo should be 0f at interruption progress 100%.
-        val fooInB = layoutImpl.elements.getValue(TestElements.Foo).sceneStates.getValue(SceneB)
+        val fooInB = layoutImpl.elements.getValue(TestElements.Foo).stateByContent.getValue(SceneB)
         assertThat(fooInB.lastAlpha).isEqualTo(0f)
 
         // Alpha of Foo should be 0.6f at interruption progress 0%.
@@ -1666,14 +1666,14 @@
                 MutableSceneTransitionLayoutStateImpl(
                     SceneA,
                     transitions {
-                        overscroll(SceneA, Orientation.Horizontal)
-                        overscroll(SceneB, Orientation.Horizontal)
+                        overscrollDisabled(SceneA, Orientation.Horizontal)
+                        overscrollDisabled(SceneB, Orientation.Horizontal)
                     }
                 )
             }
 
         @Composable
-        fun SceneScope.Foo() {
+        fun ContentScope.Foo() {
             Box(Modifier.element(TestElements.Foo).size(10.dp))
         }
 
@@ -1714,8 +1714,8 @@
                 MutableSceneTransitionLayoutStateImpl(
                     SceneA,
                     transitions {
-                        overscroll(SceneA, Orientation.Horizontal)
-                        overscroll(SceneB, Orientation.Horizontal)
+                        overscrollDisabled(SceneA, Orientation.Horizontal)
+                        overscrollDisabled(SceneB, Orientation.Horizontal)
                     }
                 )
             }
@@ -1723,9 +1723,11 @@
         val fooInA = "fooInA"
         val fooInB = "fooInB"
 
+        val key = MovableElementKey("Foo", contents = setOf(SceneA, SceneB))
+
         @Composable
-        fun SceneScope.MovableFoo(text: String, modifier: Modifier = Modifier) {
-            MovableElement(TestElements.Foo, modifier) { content { Text(text) } }
+        fun ContentScope.MovableFoo(text: String, modifier: Modifier = Modifier) {
+            MovableElement(key, modifier) { content { Text(text) } }
         }
 
         rule.setContent {
@@ -1773,7 +1775,7 @@
             }
 
         @Composable
-        fun SceneScope.SceneWithFoo(offset: DpOffset, modifier: Modifier = Modifier) {
+        fun ContentScope.SceneWithFoo(offset: DpOffset, modifier: Modifier = Modifier) {
             Box(modifier.fillMaxSize()) {
                 Box(Modifier.offset(offset.x, offset.y).element(TestElements.Foo).size(100.dp))
             }
@@ -1856,7 +1858,7 @@
         val state = rule.runOnIdle { MutableSceneTransitionLayoutStateImpl(SceneA) }
 
         @Composable
-        fun SceneScope.NestedFooBar() {
+        fun ContentScope.NestedFooBar() {
             Box(Modifier.element(TestElements.Foo)) {
                 Box(Modifier.element(TestElements.Bar).size(10.dp))
             }
@@ -1881,13 +1883,13 @@
         val foo = layoutImpl.elements.getValue(TestElements.Foo)
         val bar = layoutImpl.elements.getValue(TestElements.Bar)
 
-        assertThat(foo.sceneStates).containsKey(SceneA)
-        assertThat(bar.sceneStates).containsKey(SceneA)
-        assertThat(foo.sceneStates).doesNotContainKey(SceneB)
-        assertThat(bar.sceneStates).doesNotContainKey(SceneB)
+        assertThat(foo.stateByContent).containsKey(SceneA)
+        assertThat(bar.stateByContent).containsKey(SceneA)
+        assertThat(foo.stateByContent).doesNotContainKey(SceneB)
+        assertThat(bar.stateByContent).doesNotContainKey(SceneB)
 
-        val fooInA = foo.sceneStates.getValue(SceneA)
-        val barInA = bar.sceneStates.getValue(SceneA)
+        val fooInA = foo.stateByContent.getValue(SceneA)
+        val barInA = bar.stateByContent.getValue(SceneA)
         assertThat(fooInA.lastOffset).isNotEqualTo(Offset.Unspecified)
         assertThat(fooInA.lastAlpha).isNotEqualTo(Element.AlphaUnspecified)
         assertThat(fooInA.lastScale).isNotEqualTo(Scale.Unspecified)
@@ -1903,11 +1905,11 @@
         rule.onNode(isElement(TestElements.Foo, SceneB)).assertIsDisplayed()
         rule.onNode(isElement(TestElements.Bar, SceneB)).assertIsDisplayed()
 
-        assertThat(foo.sceneStates).containsKey(SceneB)
-        assertThat(bar.sceneStates).containsKey(SceneB)
+        assertThat(foo.stateByContent).containsKey(SceneB)
+        assertThat(bar.stateByContent).containsKey(SceneB)
 
-        val fooInB = foo.sceneStates.getValue(SceneB)
-        val barInB = bar.sceneStates.getValue(SceneB)
+        val fooInB = foo.stateByContent.getValue(SceneB)
+        val barInB = bar.stateByContent.getValue(SceneB)
         assertThat(fooInA.lastOffset).isEqualTo(Offset.Unspecified)
         assertThat(fooInA.lastAlpha).isEqualTo(Element.AlphaUnspecified)
         assertThat(fooInA.lastScale).isEqualTo(Scale.Unspecified)
@@ -1938,8 +1940,8 @@
             }
 
         @Composable
-        fun SceneScope.Foo() {
-            Box(Modifier.testTag("fooParentIn${sceneKey.debugName}")) {
+        fun ContentScope.Foo() {
+            Box(Modifier.testTag("fooParentIn${contentKey.debugName}")) {
                 Box(Modifier.element(TestElements.Foo).size(20.dp))
             }
         }
@@ -1973,7 +1975,7 @@
         val state = rule.runOnIdle { MutableSceneTransitionLayoutStateImpl(SceneA) }
 
         @Composable
-        fun SceneScope.Foo(offset: Dp) {
+        fun ContentScope.Foo(offset: Dp) {
             Box(Modifier.fillMaxSize()) {
                 Box(Modifier.offset(offset, offset).element(TestElements.Foo).size(20.dp))
             }
@@ -2036,12 +2038,12 @@
             rule.runOnIdle {
                 MutableSceneTransitionLayoutStateImpl(
                     SceneA,
-                    transitions { overscroll(SceneA, Orientation.Horizontal) }
+                    transitions { overscrollDisabled(SceneA, Orientation.Horizontal) }
                 )
             }
 
         @Composable
-        fun SceneScope.Foo() {
+        fun ContentScope.Foo() {
             Box(Modifier.element(TestElements.Foo).size(10.dp))
         }
 
@@ -2062,7 +2064,11 @@
         rule.waitForIdle()
 
         assertThat(
-                layoutImpl.elements.getValue(TestElements.Foo).sceneStates.getValue(SceneB).lastSize
+                layoutImpl.elements
+                    .getValue(TestElements.Foo)
+                    .stateByContent
+                    .getValue(SceneB)
+                    .lastSize
             )
             .isEqualTo(Element.SizeUnspecified)
     }
@@ -2078,8 +2084,8 @@
                             // In A => B, Foo is not shared and first fades out from A then fades in
                             // B.
                             sharedElement(TestElements.Foo, enabled = false)
-                            fractionRange(end = 0.5f) { fade(TestElements.Foo.inScene(SceneA)) }
-                            fractionRange(start = 0.5f) { fade(TestElements.Foo.inScene(SceneB)) }
+                            fractionRange(end = 0.5f) { fade(TestElements.Foo.inContent(SceneA)) }
+                            fractionRange(start = 0.5f) { fade(TestElements.Foo.inContent(SceneB)) }
                         }
 
                         from(SceneB, to = SceneA) {
@@ -2091,7 +2097,7 @@
             }
 
         @Composable
-        fun SceneScope.Foo(modifier: Modifier = Modifier) {
+        fun ContentScope.Foo(modifier: Modifier = Modifier) {
             Box(modifier.element(TestElements.Foo).size(10.dp))
         }
 
@@ -2149,7 +2155,7 @@
         val state = rule.runOnIdle { MutableSceneTransitionLayoutStateImpl(SceneA) }
 
         @Composable
-        fun SceneScope.Foo(modifier: Modifier = Modifier) {
+        fun ContentScope.Foo(modifier: Modifier = Modifier) {
             Box(modifier.element(TestElements.Foo).size(10.dp))
         }
 
@@ -2181,4 +2187,165 @@
         rule.onNode(isElement(TestElements.Foo, SceneA)).assertIsNotDisplayed()
         rule.onNode(isElement(TestElements.Foo, SceneB)).assertPositionInRootIsEqualTo(40.dp, 60.dp)
     }
+
+    @Test
+    fun previewInterpolation_previewStage() {
+        val exiting1 = ElementKey("exiting1")
+        val exiting2 = ElementKey("exiting2")
+        val exiting3 = ElementKey("exiting3")
+        val entering1 = ElementKey("entering1")
+        val entering2 = ElementKey("entering2")
+        val entering3 = ElementKey("entering3")
+
+        val layoutImpl =
+            testPreviewTransformation(
+                from = SceneB,
+                to = SceneA,
+                exitingElements = listOf(exiting1, exiting2, exiting3),
+                enteringElements = listOf(entering1, entering2, entering3),
+                preview = {
+                    scaleDraw(exiting1, scaleX = 0.8f, scaleY = 0.8f)
+                    translate(exiting2, x = 20.dp)
+                    scaleDraw(entering1, scaleX = 0f, scaleY = 0f)
+                    translate(entering2, y = 30.dp)
+                },
+                transition = {
+                    translate(exiting2, x = 30.dp)
+                    scaleSize(exiting3, width = 0.8f, height = 0.8f)
+                    scaleDraw(entering1, scaleX = 0.5f, scaleY = 0.5f)
+                    scaleSize(entering3, width = 0.2f, height = 0.2f)
+                },
+                previewProgress = 0.5f,
+                progress = 0f,
+                isInPreviewStage = true
+            )
+
+        // verify that preview transition for exiting elements is halfway played from
+        // current-scene-value -> preview-target-value
+        val exiting1InB = layoutImpl.elements.getValue(exiting1).stateByContent.getValue(SceneB)
+        // e.g. exiting1 is half scaled...
+        assertThat(exiting1InB.lastScale).isEqualTo(Scale(0.9f, 0.9f, Offset.Unspecified))
+        // ...and exiting2 is halfway translated from 0.dp to 20.dp...
+        rule.onNode(isElement(exiting2)).assertPositionInRootIsEqualTo(10.dp, 0.dp)
+        // ...whereas exiting3 remains in its original size because it is only affected by the
+        // second phase of the transition
+        rule.onNode(isElement(exiting3)).assertSizeIsEqualTo(100.dp, 100.dp)
+
+        // verify that preview transition for entering elements is halfway played from
+        // preview-target-value -> transition-target-value (or target-scene-value if no
+        // transition-target-value defined).
+        val entering1InA = layoutImpl.elements.getValue(entering1).stateByContent.getValue(SceneA)
+        // e.g. entering1 is half scaled between 0f and 0.5f -> 0.25f...
+        assertThat(entering1InA.lastScale).isEqualTo(Scale(0.25f, 0.25f, Offset.Unspecified))
+        // ...and entering2 is half way translated between 30.dp and 0.dp
+        rule.onNode(isElement(entering2)).assertPositionInRootIsEqualTo(0.dp, 15.dp)
+        // ...and entering3 is still at its start size of 0.2f * 100.dp, because it is unaffected
+        // by the preview phase
+        rule.onNode(isElement(entering3)).assertSizeIsEqualTo(20.dp, 20.dp)
+    }
+
+    @Test
+    fun previewInterpolation_transitionStage() {
+        val exiting1 = ElementKey("exiting1")
+        val exiting2 = ElementKey("exiting2")
+        val exiting3 = ElementKey("exiting3")
+        val entering1 = ElementKey("entering1")
+        val entering2 = ElementKey("entering2")
+        val entering3 = ElementKey("entering3")
+
+        val layoutImpl =
+            testPreviewTransformation(
+                from = SceneB,
+                to = SceneA,
+                exitingElements = listOf(exiting1, exiting2, exiting3),
+                enteringElements = listOf(entering1, entering2, entering3),
+                preview = {
+                    scaleDraw(exiting1, scaleX = 0.8f, scaleY = 0.8f)
+                    translate(exiting2, x = 20.dp)
+                    scaleDraw(entering1, scaleX = 0f, scaleY = 0f)
+                    translate(entering2, y = 30.dp)
+                },
+                transition = {
+                    translate(exiting2, x = 30.dp)
+                    scaleSize(exiting3, width = 0.8f, height = 0.8f)
+                    scaleDraw(entering1, scaleX = 0.5f, scaleY = 0.5f)
+                    scaleSize(entering3, width = 0.2f, height = 0.2f)
+                },
+                previewProgress = 0.5f,
+                progress = 0.5f,
+                isInPreviewStage = false
+            )
+
+        // verify that exiting elements remain in the preview-end state if no further transition is
+        // defined for them in the second stage
+        val exiting1InB = layoutImpl.elements.getValue(exiting1).stateByContent.getValue(SceneB)
+        // i.e. exiting1 remains half scaled
+        assertThat(exiting1InB.lastScale).isEqualTo(Scale(0.9f, 0.9f, Offset.Unspecified))
+        // in case there is an additional transition defined for the second stage, verify that the
+        // animation is seamlessly taken over from the preview-end-state, e.g. the translation of
+        // exiting2 is at 10.dp after the preview phase. After half of the second phase, it
+        // should be half-way between 10.dp and the target-value of 30.dp -> 20.dp
+        rule.onNode(isElement(exiting2)).assertPositionInRootIsEqualTo(20.dp, 0.dp)
+        // if the element is only modified by the second phase transition, verify it's in the middle
+        // of start-scene-state and target-scene-state, i.e. exiting3 is halfway between 100.dp and
+        // 80.dp
+        rule.onNode(isElement(exiting3)).assertSizeIsEqualTo(90.dp, 90.dp)
+
+        // verify that entering elements animate seamlessly to their target state
+        val entering1InA = layoutImpl.elements.getValue(entering1).stateByContent.getValue(SceneA)
+        // e.g. entering1, which was scaled from 0f to 0.25f during the preview phase, should now be
+        // half way scaled between 0.25f and its target-state of 1f -> 0.625f
+        assertThat(entering1InA.lastScale).isEqualTo(Scale(0.625f, 0.625f, Offset.Unspecified))
+        // entering2, which was translated from y=30.dp to y=15.dp should now be half way
+        // between 15.dp and its target state of 0.dp...
+        rule.onNode(isElement(entering2)).assertPositionInRootIsEqualTo(0.dp, 7.5.dp)
+        // entering3, which isn't affected by the preview transformation should be half scaled
+        // between start size (20.dp) and target size (100.dp) -> 60.dp
+        rule.onNode(isElement(entering3)).assertSizeIsEqualTo(60.dp, 60.dp)
+    }
+
+    private fun testPreviewTransformation(
+        from: SceneKey,
+        to: SceneKey,
+        exitingElements: List<ElementKey> = listOf(),
+        enteringElements: List<ElementKey> = listOf(),
+        preview: (TransitionBuilder.() -> Unit)? = null,
+        transition: TransitionBuilder.() -> Unit,
+        progress: Float = 0f,
+        previewProgress: Float = 0.5f,
+        isInPreviewStage: Boolean = true
+    ): SceneTransitionLayoutImpl {
+        val state =
+            rule.runOnIdle {
+                MutableSceneTransitionLayoutStateImpl(
+                    from,
+                    transitions { from(from, to = to, preview = preview, builder = transition) }
+                )
+            }
+
+        @Composable
+        fun ContentScope.Foo(elementKey: ElementKey) {
+            Box(Modifier.element(elementKey).size(100.dp))
+        }
+
+        lateinit var layoutImpl: SceneTransitionLayoutImpl
+        rule.setContent {
+            SceneTransitionLayoutForTesting(state, onLayoutImpl = { layoutImpl = it }) {
+                scene(from) { Box { exitingElements.forEach { Foo(it) } } }
+                scene(to) { Box { enteringElements.forEach { Foo(it) } } }
+            }
+        }
+
+        val bToA =
+            transition(
+                from = from,
+                to = to,
+                progress = { progress },
+                previewProgress = { previewProgress },
+                isInPreviewStage = { isInPreviewStage }
+            )
+        rule.runOnUiThread { state.startTransition(bToA) }
+        rule.waitForIdle()
+        return layoutImpl
+    }
 }
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/InterruptionHandlerTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/InterruptionHandlerTest.kt
index 3552d3d..ca72181 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/InterruptionHandlerTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/InterruptionHandlerTest.kt
@@ -22,6 +22,7 @@
 import com.android.compose.animation.scene.TestScenes.SceneA
 import com.android.compose.animation.scene.TestScenes.SceneB
 import com.android.compose.animation.scene.TestScenes.SceneC
+import com.android.compose.animation.scene.content.state.TransitionState
 import com.android.compose.animation.scene.subjects.assertThat
 import com.android.compose.test.runMonotonicClockTest
 import com.google.common.truth.Correspondence
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MovableElementScenePickerTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MovableElementContentPickerTest.kt
similarity index 60%
rename from packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MovableElementScenePickerTest.kt
rename to packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MovableElementContentPickerTest.kt
index 6745fbe..e1d0945 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MovableElementScenePickerTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MovableElementContentPickerTest.kt
@@ -18,20 +18,22 @@
 
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import com.google.common.truth.Truth.assertThat
+import org.junit.Assert.assertThrows
 import org.junit.Test
 import org.junit.runner.RunWith
 
 @RunWith(AndroidJUnit4::class)
-class MovableElementScenePickerTest {
+class MovableElementContentPickerTest {
     @Test
     fun toSceneInScenes() {
-        val picker = MovableElementScenePicker(scenes = setOf(TestScenes.SceneA, TestScenes.SceneB))
+        val picker =
+            MovableElementContentPicker(contents = setOf(TestScenes.SceneA, TestScenes.SceneB))
         assertThat(
-                picker.sceneDuringTransition(
+                picker.contentDuringTransition(
                     TestElements.Foo,
                     transition(from = TestScenes.SceneA, to = TestScenes.SceneB),
-                    fromSceneZIndex = 0f,
-                    toSceneZIndex = 1f,
+                    fromContentZIndex = 0f,
+                    toContentZIndex = 1f,
                 )
             )
             .isEqualTo(TestScenes.SceneB)
@@ -39,13 +41,13 @@
 
     @Test
     fun fromSceneInScenes() {
-        val picker = MovableElementScenePicker(scenes = setOf(TestScenes.SceneA))
+        val picker = MovableElementContentPicker(contents = setOf(TestScenes.SceneA))
         assertThat(
-                picker.sceneDuringTransition(
+                picker.contentDuringTransition(
                     TestElements.Foo,
                     transition(from = TestScenes.SceneA, to = TestScenes.SceneB),
-                    fromSceneZIndex = 0f,
-                    toSceneZIndex = 1f,
+                    fromContentZIndex = 0f,
+                    toContentZIndex = 1f,
                 )
             )
             .isEqualTo(TestScenes.SceneA)
@@ -53,15 +55,14 @@
 
     @Test
     fun noneInScenes() {
-        val picker = MovableElementScenePicker(scenes = emptySet())
-        assertThat(
-                picker.sceneDuringTransition(
-                    TestElements.Foo,
-                    transition(from = TestScenes.SceneA, to = TestScenes.SceneB),
-                    fromSceneZIndex = 0f,
-                    toSceneZIndex = 1f,
-                )
+        val picker = MovableElementContentPicker(contents = emptySet())
+        assertThrows(IllegalStateException::class.java) {
+            picker.contentDuringTransition(
+                TestElements.Foo,
+                transition(from = TestScenes.SceneA, to = TestScenes.SceneB),
+                fromContentZIndex = 0f,
+                toContentZIndex = 1f,
             )
-            .isEqualTo(null)
+        }
     }
 }
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MovableElementTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MovableElementTest.kt
index 9523896..520e759 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MovableElementTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MovableElementTest.kt
@@ -43,6 +43,10 @@
 import androidx.compose.ui.test.performClick
 import androidx.compose.ui.unit.dp
 import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.compose.animation.scene.TestScenes.SceneA
+import com.android.compose.animation.scene.TestScenes.SceneB
+import com.android.compose.animation.scene.content.state.ContentState
+import com.android.compose.animation.scene.content.state.TransitionState
 import com.android.compose.animation.scene.subjects.assertThat
 import com.android.compose.test.assertSizeIsEqualTo
 import com.google.common.truth.Truth.assertThat
@@ -62,7 +66,7 @@
     }
 
     @Composable
-    private fun SceneScope.MovableCounter(key: ElementKey, modifier: Modifier) {
+    private fun ContentScope.MovableCounter(key: MovableElementKey, modifier: Modifier) {
         MovableElement(key, modifier) { content { Counter() } }
     }
 
@@ -74,8 +78,8 @@
             },
             toSceneContent = { Box(Modifier.element(TestElements.Foo).size(100.dp)) { Counter() } },
             transition = { spec = tween(durationMillis = 16 * 4, easing = LinearEasing) },
-            fromScene = TestScenes.SceneA,
-            toScene = TestScenes.SceneB,
+            fromScene = SceneA,
+            toScene = SceneB,
         ) {
             before {
                 // Click 3 times on the counter.
@@ -103,7 +107,7 @@
                 rule
                     .onNode(
                         hasText("count: 3") and
-                            hasParent(isElement(TestElements.Foo, scene = TestScenes.SceneA))
+                            hasParent(isElement(TestElements.Foo, scene = SceneA))
                     )
                     .assertExists()
                     .assertIsNotDisplayed()
@@ -111,7 +115,7 @@
                 rule
                     .onNode(
                         hasText("count: 0") and
-                            hasParent(isElement(TestElements.Foo, scene = TestScenes.SceneB))
+                            hasParent(isElement(TestElements.Foo, scene = SceneB))
                     )
                     .assertIsDisplayed()
                     .assertSizeIsEqualTo(75.dp, 75.dp)
@@ -148,27 +152,30 @@
     @Test
     fun movableElementIsMovedAndComposedOnlyOnce() {
         val key =
-            ElementKey(
+            MovableElementKey(
                 "Foo",
-                scenePicker =
-                    object : ElementScenePicker {
-                        override fun sceneDuringTransition(
+                contentPicker =
+                    object : StaticElementContentPicker {
+                        override val contents: Set<ContentKey> = setOf(SceneA, SceneB)
+
+                        override fun contentDuringTransition(
                             element: ElementKey,
-                            transition: TransitionState.Transition,
-                            fromSceneZIndex: Float,
-                            toSceneZIndex: Float
-                        ): SceneKey {
-                            assertThat(transition).hasFromScene(TestScenes.SceneA)
-                            assertThat(transition).hasToScene(TestScenes.SceneB)
-                            assertThat(fromSceneZIndex).isEqualTo(0)
-                            assertThat(toSceneZIndex).isEqualTo(1)
+                            transition: ContentState.Transition<*>,
+                            fromContentZIndex: Float,
+                            toContentZIndex: Float
+                        ): ContentKey {
+                            transition as TransitionState.Transition
+                            assertThat(transition).hasFromScene(SceneA)
+                            assertThat(transition).hasToScene(SceneB)
+                            assertThat(fromContentZIndex).isEqualTo(0)
+                            assertThat(toContentZIndex).isEqualTo(1)
 
                             // Compose Foo in Scene A if progress < 0.65f, otherwise compose it
                             // in Scene B.
                             return if (transition.progress < 0.65f) {
-                                TestScenes.SceneA
+                                SceneA
                             } else {
-                                TestScenes.SceneB
+                                SceneB
                             }
                         }
                     }
@@ -178,8 +185,8 @@
             fromSceneContent = { MovableCounter(key, Modifier.size(50.dp)) },
             toSceneContent = { MovableCounter(key, Modifier.size(100.dp)) },
             transition = { spec = tween(durationMillis = 16 * 4, easing = LinearEasing) },
-            fromScene = TestScenes.SceneA,
-            toScene = TestScenes.SceneB,
+            fromScene = SceneA,
+            toScene = SceneB,
         ) {
             before {
                 // Click 3 times on the counter.
@@ -207,7 +214,7 @@
                 rule
                     .onNode(
                         hasText("count: 3") and
-                            hasParent(isElement(TestElements.Foo, scene = TestScenes.SceneA))
+                            hasParent(isElement(TestElements.Foo, scene = SceneA))
                     )
                     .assertIsDisplayed()
                     .assertSizeIsEqualTo(75.dp, 75.dp)
@@ -228,7 +235,7 @@
                 rule
                     .onNode(
                         hasText("count: 3") and
-                            hasParent(isElement(TestElements.Foo, scene = TestScenes.SceneB))
+                            hasParent(isElement(TestElements.Foo, scene = SceneB))
                     )
                     .assertIsDisplayed()
 
@@ -263,17 +270,19 @@
 
     @Test
     fun movableElementContentIsRecomposedIfContentParametersChange() {
+        val key = MovableElementKey("Foo", contents = setOf(SceneA, SceneB))
+
         @Composable
-        fun SceneScope.MovableFoo(text: String, modifier: Modifier = Modifier) {
-            MovableElement(TestElements.Foo, modifier) { content { Text(text) } }
+        fun ContentScope.MovableFoo(text: String, modifier: Modifier = Modifier) {
+            MovableElement(key, modifier) { content { Text(text) } }
         }
 
         rule.testTransition(
             fromSceneContent = { MovableFoo(text = "fromScene") },
             toSceneContent = { MovableFoo(text = "toScene") },
             transition = { spec = tween(durationMillis = 16 * 4, easing = LinearEasing) },
-            fromScene = TestScenes.SceneA,
-            toScene = TestScenes.SceneB,
+            fromScene = SceneA,
+            toScene = SceneB,
         ) {
             // Before the transition, only fromScene is composed.
             before {
@@ -298,7 +307,7 @@
     @Test
     fun elementScopeExtendsBoxScope() {
         rule.setContent {
-            TestSceneScope {
+            TestContentScope {
                 Element(TestElements.Foo, Modifier.size(200.dp)) {
                     content {
                         Box(Modifier.testTag("bottomEnd").align(Alignment.BottomEnd))
@@ -314,9 +323,10 @@
 
     @Test
     fun movableElementScopeExtendsBoxScope() {
+        val key = MovableElementKey("Foo", contents = setOf(SceneA))
         rule.setContent {
-            TestSceneScope {
-                MovableElement(TestElements.Foo, Modifier.size(200.dp)) {
+            TestContentScope {
+                MovableElement(key, Modifier.size(200.dp)) {
                     content {
                         Box(Modifier.testTag("bottomEnd").align(Alignment.BottomEnd))
                         Box(Modifier.testTag("matchParentSize").matchParentSize())
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 ecafb17..2d37a0d 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
@@ -28,6 +28,10 @@
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.geometry.Size
+import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
+import androidx.compose.ui.input.nestedscroll.NestedScrollDispatcher
+import androidx.compose.ui.input.nestedscroll.NestedScrollSource
+import androidx.compose.ui.input.nestedscroll.nestedScroll
 import androidx.compose.ui.input.pointer.AwaitPointerEventScope
 import androidx.compose.ui.input.pointer.PointerEventPass
 import androidx.compose.ui.input.pointer.PointerInputChange
@@ -37,6 +41,7 @@
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.onRoot
 import androidx.compose.ui.test.performTouchInput
+import androidx.compose.ui.unit.Velocity
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.coroutineScope
@@ -49,6 +54,26 @@
 class MultiPointerDraggableTest {
     @get:Rule val rule = createComposeRule()
 
+    private val emptyConnection = object : NestedScrollConnection {}
+    private val defaultDispatcher = NestedScrollDispatcher()
+
+    private fun Modifier.nestedScrollDispatcher() = nestedScroll(emptyConnection, defaultDispatcher)
+
+    private class SimpleDragController(
+        val onDrag: (delta: Float) -> Unit,
+        val onStop: (velocity: Float) -> Unit,
+    ) : DragController {
+        override fun onDrag(delta: Float): Float {
+            onDrag.invoke(delta)
+            return delta
+        }
+
+        override fun onStop(velocity: Float, canChangeScene: Boolean): Float {
+            onStop.invoke(velocity)
+            return velocity
+        }
+    }
+
     @Test
     fun cancellingPointerCallsOnDragStopped() {
         val size = 200f
@@ -64,22 +89,19 @@
             touchSlop = LocalViewConfiguration.current.touchSlop
             Box(
                 Modifier.size(with(LocalDensity.current) { Size(size, size).toDpSize() })
+                    .nestedScrollDispatcher()
                     .multiPointerDraggable(
                         orientation = Orientation.Vertical,
                         enabled = { enabled },
                         startDragImmediately = { false },
                         onDragStarted = { _, _, _ ->
                             started = true
-                            object : DragController {
-                                override fun onDrag(delta: Float) {
-                                    dragged = true
-                                }
-
-                                override fun onStop(velocity: Float, canChangeScene: Boolean) {
-                                    stopped = true
-                                }
-                            }
+                            SimpleDragController(
+                                onDrag = { dragged = true },
+                                onStop = { stopped = true },
+                            )
                         },
+                        dispatcher = defaultDispatcher,
                     )
             )
         }
@@ -135,6 +157,7 @@
             touchSlop = LocalViewConfiguration.current.touchSlop
             Box(
                 Modifier.size(with(LocalDensity.current) { Size(size, size).toDpSize() })
+                    .nestedScrollDispatcher()
                     .multiPointerDraggable(
                         orientation = Orientation.Vertical,
                         enabled = { true },
@@ -142,16 +165,12 @@
                         startDragImmediately = { true },
                         onDragStarted = { _, _, _ ->
                             started = true
-                            object : DragController {
-                                override fun onDrag(delta: Float) {
-                                    dragged = true
-                                }
-
-                                override fun onStop(velocity: Float, canChangeScene: Boolean) {
-                                    stopped = true
-                                }
-                            }
+                            SimpleDragController(
+                                onDrag = { dragged = true },
+                                onStop = { stopped = true },
+                            )
                         },
+                        dispatcher = defaultDispatcher,
                     )
                     .pointerInput(Unit) {
                         coroutineScope {
@@ -212,22 +231,19 @@
             touchSlop = LocalViewConfiguration.current.touchSlop
             Box(
                 Modifier.size(with(LocalDensity.current) { Size(size, size).toDpSize() })
+                    .nestedScrollDispatcher()
                     .multiPointerDraggable(
                         orientation = Orientation.Vertical,
                         enabled = { true },
                         startDragImmediately = { false },
                         onDragStarted = { _, _, _ ->
                             started = true
-                            object : DragController {
-                                override fun onDrag(delta: Float) {
-                                    dragged = true
-                                }
-
-                                override fun onStop(velocity: Float, canChangeScene: Boolean) {
-                                    stopped = true
-                                }
-                            }
+                            SimpleDragController(
+                                onDrag = { dragged = true },
+                                onStop = { stopped = true },
+                            )
                         },
+                        dispatcher = defaultDispatcher,
                     )
             ) {
                 if (hasScrollable) {
@@ -335,22 +351,19 @@
             touchSlop = LocalViewConfiguration.current.touchSlop
             Box(
                 Modifier.size(with(LocalDensity.current) { Size(size, size).toDpSize() })
+                    .nestedScrollDispatcher()
                     .multiPointerDraggable(
                         orientation = Orientation.Vertical,
                         enabled = { true },
                         startDragImmediately = { false },
                         onDragStarted = { _, _, _ ->
                             started = true
-                            object : DragController {
-                                override fun onDrag(delta: Float) {
-                                    dragged = true
-                                }
-
-                                override fun onStop(velocity: Float, canChangeScene: Boolean) {
-                                    stopped = true
-                                }
-                            }
+                            SimpleDragController(
+                                onDrag = { dragged = true },
+                                onStop = { stopped = true },
+                            )
                         },
+                        dispatcher = defaultDispatcher,
                     )
             ) {
                 Box(
@@ -441,22 +454,19 @@
             touchSlop = LocalViewConfiguration.current.touchSlop
             Box(
                 Modifier.size(with(LocalDensity.current) { Size(size, size).toDpSize() })
+                    .nestedScrollDispatcher()
                     .multiPointerDraggable(
                         orientation = Orientation.Vertical,
                         enabled = { true },
                         startDragImmediately = { false },
                         onDragStarted = { _, _, _ ->
                             verticalStarted = true
-                            object : DragController {
-                                override fun onDrag(delta: Float) {
-                                    verticalDragged = true
-                                }
-
-                                override fun onStop(velocity: Float, canChangeScene: Boolean) {
-                                    verticalStopped = true
-                                }
-                            }
+                            SimpleDragController(
+                                onDrag = { verticalDragged = true },
+                                onStop = { verticalStopped = true },
+                            )
                         },
+                        dispatcher = defaultDispatcher,
                     )
                     .multiPointerDraggable(
                         orientation = Orientation.Horizontal,
@@ -464,16 +474,12 @@
                         startDragImmediately = { false },
                         onDragStarted = { _, _, _ ->
                             horizontalStarted = true
-                            object : DragController {
-                                override fun onDrag(delta: Float) {
-                                    horizontalDragged = true
-                                }
-
-                                override fun onStop(velocity: Float, canChangeScene: Boolean) {
-                                    horizontalStopped = true
-                                }
-                            }
+                            SimpleDragController(
+                                onDrag = { horizontalDragged = true },
+                                onStop = { horizontalStopped = true },
+                            )
                         },
+                        dispatcher = defaultDispatcher,
                     )
             )
         }
@@ -554,6 +560,7 @@
             touchSlop = LocalViewConfiguration.current.touchSlop
             Box(
                 Modifier.size(with(LocalDensity.current) { Size(size, size).toDpSize() })
+                    .nestedScrollDispatcher()
                     .multiPointerDraggable(
                         orientation = Orientation.Vertical,
                         enabled = { true },
@@ -567,12 +574,12 @@
                             },
                         onDragStarted = { _, _, _ ->
                             started = true
-                            object : DragController {
-                                override fun onDrag(delta: Float) {}
-
-                                override fun onStop(velocity: Float, canChangeScene: Boolean) {}
-                            }
+                            SimpleDragController(
+                                onDrag = { /* do nothing */ },
+                                onStop = { /* do nothing */ },
+                            )
                         },
+                        dispatcher = defaultDispatcher,
                     )
             ) {}
         }
@@ -603,4 +610,113 @@
 
         assertThat(started).isTrue()
     }
+
+    @Test
+    fun multiPointerNestedScrollDispatcher() {
+        val size = 200f
+        val middle = Offset(size / 2f, size / 2f)
+        var touchSlop = 0f
+
+        var consumedOnPreScroll = 0f
+
+        var availableOnPreScroll = Float.MIN_VALUE
+        var availableOnPostScroll = Float.MIN_VALUE
+        var availableOnPreFling = Float.MIN_VALUE
+        var availableOnPostFling = Float.MIN_VALUE
+
+        var consumedOnDrag = 0f
+        var consumedOnDragStop = 0f
+
+        val connection =
+            object : NestedScrollConnection {
+                override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
+                    availableOnPreScroll = available.y
+                    return Offset(0f, consumedOnPreScroll)
+                }
+
+                override fun onPostScroll(
+                    consumed: Offset,
+                    available: Offset,
+                    source: NestedScrollSource
+                ): Offset {
+                    availableOnPostScroll = available.y
+                    return Offset.Zero
+                }
+
+                override suspend fun onPreFling(available: Velocity): Velocity {
+                    availableOnPreFling = available.y
+                    return Velocity.Zero
+                }
+
+                override suspend fun onPostFling(
+                    consumed: Velocity,
+                    available: Velocity
+                ): Velocity {
+                    availableOnPostFling = available.y
+                    return Velocity.Zero
+                }
+            }
+
+        rule.setContent {
+            touchSlop = LocalViewConfiguration.current.touchSlop
+            Box(
+                Modifier.size(with(LocalDensity.current) { Size(size, size).toDpSize() })
+                    .nestedScroll(connection)
+                    .nestedScrollDispatcher()
+                    .multiPointerDraggable(
+                        orientation = Orientation.Vertical,
+                        enabled = { true },
+                        startDragImmediately = { false },
+                        onDragStarted = { _, _, _ ->
+                            SimpleDragController(
+                                onDrag = { consumedOnDrag = it },
+                                onStop = { consumedOnDragStop = it },
+                            )
+                        },
+                        dispatcher = defaultDispatcher,
+                    )
+            )
+        }
+
+        fun startDrag() {
+            rule.onRoot().performTouchInput {
+                down(middle)
+                moveBy(Offset(0f, touchSlop))
+            }
+        }
+
+        fun continueDrag() {
+            rule.onRoot().performTouchInput { moveBy(Offset(0f, touchSlop)) }
+        }
+
+        fun stopDrag() {
+            rule.onRoot().performTouchInput { up() }
+        }
+
+        startDrag()
+
+        continueDrag()
+        assertThat(availableOnPreScroll).isEqualTo(touchSlop)
+        assertThat(consumedOnDrag).isEqualTo(touchSlop)
+        assertThat(availableOnPostScroll).isEqualTo(0f)
+
+        // Parent node consumes half of the gesture
+        consumedOnPreScroll = touchSlop / 2f
+        continueDrag()
+        assertThat(availableOnPreScroll).isEqualTo(touchSlop)
+        assertThat(consumedOnDrag).isEqualTo(touchSlop / 2f)
+        assertThat(availableOnPostScroll).isEqualTo(0f)
+
+        // Parent node consumes the gesture
+        consumedOnPreScroll = touchSlop
+        continueDrag()
+        assertThat(availableOnPreScroll).isEqualTo(touchSlop)
+        assertThat(consumedOnDrag).isEqualTo(0f)
+        assertThat(availableOnPostScroll).isEqualTo(0f)
+
+        // Parent node can intercept the velocity on stop
+        stopDrag()
+        assertThat(availableOnPreFling).isEqualTo(consumedOnDragStop)
+        assertThat(availableOnPostFling).isEqualTo(0f)
+    }
 }
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/NestedScrollToSceneTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/NestedScrollToSceneTest.kt
index 311a580..9ebc426 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/NestedScrollToSceneTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/NestedScrollToSceneTest.kt
@@ -48,7 +48,7 @@
     private val layoutHeight = 400.dp
 
     private fun setup2ScenesAndScrollTouchSlop(
-        modifierSceneA: @Composable SceneScope.() -> Modifier = { Modifier },
+        modifierSceneA: @Composable ContentScope.() -> Modifier = { Modifier },
     ): MutableSceneTransitionLayoutState {
         val state =
             rule.runOnUiThread {
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/PredictiveBackHandlerTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/PredictiveBackHandlerTest.kt
index 6522eb3..0eaecb0 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/PredictiveBackHandlerTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/PredictiveBackHandlerTest.kt
@@ -80,6 +80,56 @@
         assertThat(transition).hasFromScene(SceneA)
         assertThat(transition).hasToScene(SceneB)
         assertThat(transition).hasProgress(0.4f)
+        assertThat(transition).isNotInPreviewStage()
+
+        // Cancel it.
+        rule.runOnUiThread { dispatcher.dispatchOnBackCancelled() }
+        rule.waitForIdle()
+        assertThat(layoutState.transitionState).hasCurrentScene(SceneA)
+        assertThat(layoutState.transitionState).isIdle()
+
+        // Start again and commit it.
+        rule.runOnUiThread {
+            dispatcher.dispatchOnBackStarted(backEvent())
+            dispatcher.dispatchOnBackProgressed(backEvent(progress = 0.4f))
+            dispatcher.onBackPressed()
+        }
+        rule.waitForIdle()
+        assertThat(layoutState.transitionState).hasCurrentScene(SceneB)
+        assertThat(layoutState.transitionState).isIdle()
+    }
+
+    @Test
+    fun testPredictiveBackWithPreview() {
+        val layoutState =
+            rule.runOnUiThread {
+                MutableSceneTransitionLayoutState(
+                    SceneA,
+                    transitions = transitions { from(SceneA, to = SceneB, preview = {}) }
+                )
+            }
+        rule.setContent {
+            SceneTransitionLayout(layoutState) {
+                scene(SceneA, mapOf(Back to SceneB)) { Box(Modifier.fillMaxSize()) }
+                scene(SceneB) { Box(Modifier.fillMaxSize()) }
+            }
+        }
+
+        assertThat(layoutState.transitionState).hasCurrentScene(SceneA)
+
+        // Start back.
+        val dispatcher = rule.activity.onBackPressedDispatcher
+        rule.runOnUiThread {
+            dispatcher.dispatchOnBackStarted(backEvent())
+            dispatcher.dispatchOnBackProgressed(backEvent(progress = 0.4f))
+        }
+
+        val transition = assertThat(layoutState.transitionState).isTransition()
+        assertThat(transition).hasFromScene(SceneA)
+        assertThat(transition).hasToScene(SceneB)
+        assertThat(transition).hasPreviewProgress(0.4f)
+        assertThat(transition).hasProgress(0f)
+        assertThat(transition).isInPreviewStage()
 
         // Cancel it.
         rule.runOnUiThread { dispatcher.dispatchOnBackCancelled() }
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 52cceec..6b417ee 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
@@ -25,6 +25,7 @@
 import com.android.compose.animation.scene.TestScenes.SceneB
 import com.android.compose.animation.scene.TestScenes.SceneC
 import com.android.compose.animation.scene.TestScenes.SceneD
+import com.android.compose.animation.scene.content.state.TransitionState
 import com.android.compose.animation.scene.subjects.assertThat
 import com.android.compose.animation.scene.transition.link.StateLink
 import com.android.compose.test.runMonotonicClockTest
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 1ec1079..84bcc28f 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
@@ -129,7 +129,7 @@
     }
 
     @Composable
-    private fun SceneScope.SharedFoo(size: Dp, childOffset: Dp, modifier: Modifier = Modifier) {
+    private fun ContentScope.SharedFoo(size: Dp, childOffset: Dp, modifier: Modifier = Modifier) {
         Element(TestElements.Foo, modifier.size(size).background(Color.Red)) {
             // Offset the single child of Foo by some animated shared offset.
             val offset by animateElementDpAsState(childOffset, TestValues.Value1)
@@ -322,7 +322,7 @@
             rule.runOnUiThread {
                 MutableSceneTransitionLayoutStateImpl(
                     SceneA,
-                    transitions { overscroll(SceneB, Orientation.Horizontal) }
+                    transitions { overscrollDisabled(SceneB, Orientation.Horizontal) }
                 )
             }
 
@@ -479,14 +479,14 @@
     fun sceneKeyInScope() {
         val state = rule.runOnUiThread { MutableSceneTransitionLayoutState(SceneA) }
 
-        var keyInA: SceneKey? = null
-        var keyInB: SceneKey? = null
-        var keyInC: SceneKey? = null
+        var keyInA: ContentKey? = null
+        var keyInB: ContentKey? = null
+        var keyInC: ContentKey? = null
         rule.setContent {
             SceneTransitionLayout(state) {
-                scene(SceneA) { keyInA = sceneKey }
-                scene(SceneB) { keyInB = sceneKey }
-                scene(SceneC) { keyInC = sceneKey }
+                scene(SceneA) { keyInA = contentKey }
+                scene(SceneB) { keyInB = contentKey }
+                scene(SceneC) { keyInC = contentKey }
             }
         }
 
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt
index 0766e00..04a9380 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt
@@ -29,6 +29,9 @@
 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.nestedScroll
 import androidx.compose.ui.platform.LocalLayoutDirection
 import androidx.compose.ui.platform.LocalViewConfiguration
 import androidx.compose.ui.platform.testTag
@@ -786,4 +789,61 @@
             .onNode(isElement(SceneB.rootElementKey))
             .assertPositionInRootIsEqualTo(-layoutSize, 0.dp)
     }
+
+    @Test
+    fun whenOverscrollIsDisabled_dragGestureShouldNotBeConsumed() {
+        val swipeDistance = 100.dp
+
+        var availableOnPostScroll = Float.MIN_VALUE
+        val connection =
+            object : NestedScrollConnection {
+                override fun onPostScroll(
+                    consumed: Offset,
+                    available: Offset,
+                    source: NestedScrollSource
+                ): Offset {
+                    availableOnPostScroll = available.y
+                    return super.onPostScroll(consumed, available, source)
+                }
+            }
+        val state =
+            rule.runOnUiThread {
+                MutableSceneTransitionLayoutState(
+                    SceneA,
+                    transitions {
+                        from(SceneA, to = SceneB) { distance = FixedDistance(swipeDistance) }
+                        overscrollDisabled(SceneB, Orientation.Vertical)
+                    }
+                )
+            }
+        val layoutSize = 200.dp
+        var touchSlop = 0f
+        rule.setContent {
+            touchSlop = LocalViewConfiguration.current.touchSlop
+            SceneTransitionLayout(state, Modifier.size(layoutSize).nestedScroll(connection)) {
+                scene(SceneA, userActions = mapOf(Swipe.Down to SceneB)) {
+                    Box(Modifier.fillMaxSize())
+                }
+                scene(SceneB) { Box(Modifier.element(TestElements.Foo).fillMaxSize()) }
+            }
+        }
+
+        // Swipe down by the swipe distance so that we are on scene B.
+        rule.onRoot().performTouchInput {
+            val middle = (layoutSize / 2).toPx()
+            down(Offset(middle, middle))
+            moveBy(Offset(0f, touchSlop + (swipeDistance).toPx()), delayMillis = 1_000)
+        }
+        val transition = state.currentTransition
+        assertThat(transition).isNotNull()
+        assertThat(transition!!.progress).isEqualTo(1f)
+        assertThat(availableOnPostScroll).isEqualTo(0f)
+
+        // Overscrolling on Scene B
+        val ovescrollPx = 100f
+        rule.onRoot().performTouchInput { moveBy(Offset(0f, ovescrollPx), delayMillis = 1_000) }
+        // Overscroll is disabled on Scene B
+        assertThat(transition.progress).isEqualTo(1f)
+        assertThat(availableOnPostScroll).isEqualTo(ovescrollPx)
+    }
 }
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/Transition.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/Transition.kt
index 65f4f9e..91bd7e1 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/Transition.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/Transition.kt
@@ -17,6 +17,8 @@
 package com.android.compose.animation.scene
 
 import androidx.compose.foundation.gestures.Orientation
+import com.android.compose.animation.scene.content.state.ContentState
+import com.android.compose.animation.scene.content.state.TransitionState
 import kotlinx.coroutines.Job
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.sync.Mutex
@@ -30,18 +32,21 @@
     current: () -> SceneKey = { from },
     progress: () -> Float = { 0f },
     progressVelocity: () -> Float = { 0f },
+    previewProgress: () -> Float = { 0f },
+    previewProgressVelocity: () -> Float = { 0f },
+    isInPreviewStage: () -> Boolean = { false },
     interruptionProgress: () -> Float = { 0f },
     isInitiatedByUserInput: Boolean = false,
     isUserInputOngoing: Boolean = false,
     isUpOrLeft: Boolean = false,
-    bouncingScene: SceneKey? = null,
+    bouncingContent: ContentKey? = null,
     orientation: Orientation = Orientation.Horizontal,
     onFinish: ((TransitionState.Transition) -> Job)? = null,
     replacedTransition: TransitionState.Transition? = null,
 ): TransitionState.Transition {
     return object :
         TransitionState.Transition(from, to, replacedTransition),
-        TransitionState.HasOverscrollProperties {
+        ContentState.HasOverscrollProperties {
         override val currentScene: SceneKey
             get() = current()
 
@@ -51,10 +56,19 @@
         override val progressVelocity: Float
             get() = progressVelocity()
 
+        override val previewProgress: Float
+            get() = previewProgress()
+
+        override val previewProgressVelocity: Float
+            get() = previewProgressVelocity()
+
+        override val isInPreviewStage: Boolean
+            get() = isInPreviewStage()
+
         override val isInitiatedByUserInput: Boolean = isInitiatedByUserInput
         override val isUserInputOngoing: Boolean = isUserInputOngoing
         override val isUpOrLeft: Boolean = isUpOrLeft
-        override val bouncingScene: SceneKey? = bouncingScene
+        override val bouncingContent: ContentKey? = bouncingContent
         override val orientation: Orientation = orientation
         override val overscrollScope: OverscrollScope =
             object : OverscrollScope {
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/TransitionDslTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/TransitionDslTest.kt
index 825fe13..68240b5 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/TransitionDslTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/TransitionDslTest.kt
@@ -27,6 +27,7 @@
 import com.android.compose.animation.scene.transformation.TransformationRange
 import com.google.common.truth.Correspondence
 import com.google.common.truth.Truth.assertThat
+import org.junit.Assert.assertThrows
 import org.junit.Test
 import org.junit.runner.RunWith
 
@@ -58,7 +59,7 @@
 
         assertThat(transitions.transitionSpecs)
             .comparingElementsUsing(
-                Correspondence.transforming<TransitionSpecImpl, Pair<SceneKey?, SceneKey?>>(
+                Correspondence.transforming<TransitionSpecImpl, Pair<ContentKey?, ContentKey?>>(
                     { it?.from to it?.to },
                     "has (from, to) equal to"
                 )
@@ -168,7 +169,14 @@
     @Test
     fun defaultReversed() {
         val transitions = transitions {
-            from(TestScenes.SceneA, to = TestScenes.SceneB) {
+            from(
+                TestScenes.SceneA,
+                to = TestScenes.SceneB,
+                preview = { fractionRange(start = 0.1f, end = 0.8f) { fade(TestElements.Foo) } },
+                reversePreview = {
+                    fractionRange(start = 0.5f, end = 0.6f) { fade(TestElements.Foo) }
+                }
+            ) {
                 spec = tween(500)
                 fractionRange(start = 0.1f, end = 0.8f) { fade(TestElements.Foo) }
                 timestampRange(startMillis = 100, endMillis = 300) { fade(TestElements.Foo) }
@@ -177,11 +185,10 @@
 
         // Fetch the transition from B to A, which will automatically reverse the transition from A
         // to B we defined.
-        val transformations =
-            transitions
-                .transitionSpec(from = TestScenes.SceneB, to = TestScenes.SceneA, key = null)
-                .transformationSpec()
-                .transformations
+        val transitionSpec =
+            transitions.transitionSpec(from = TestScenes.SceneB, to = TestScenes.SceneA, key = null)
+
+        val transformations = transitionSpec.transformationSpec().transformations
 
         assertThat(transformations)
             .comparingElementsUsing(TRANSFORMATION_RANGE)
@@ -189,6 +196,14 @@
                 TransformationRange(start = 1f - 0.8f, end = 1f - 0.1f),
                 TransformationRange(start = 1f - 300 / 500f, end = 1f - 100 / 500f),
             )
+
+        val previewTransformations = transitionSpec.previewTransformationSpec()?.transformations
+
+        assertThat(previewTransformations)
+            .comparingElementsUsing(TRANSFORMATION_RANGE)
+            .containsExactly(
+                TransformationRange(start = 0.5f, end = 0.6f),
+            )
     }
 
     @Test
@@ -238,6 +253,22 @@
         assertThat(transformation).isInstanceOf(OverscrollTranslate::class.java)
     }
 
+    @Test
+    fun overscrollSpec_for_overscrollDisabled() {
+        val transitions = transitions {
+            overscrollDisabled(TestScenes.SceneA, Orientation.Vertical)
+        }
+        val overscrollSpec = transitions.overscrollSpecs.single()
+        assertThat(overscrollSpec.transformationSpec.transformations).isEmpty()
+    }
+
+    @Test
+    fun overscrollSpec_throwIfTransformationsIsEmpty() {
+        assertThrows(IllegalStateException::class.java) {
+            transitions { overscroll(TestScenes.SceneA, Orientation.Vertical) {} }
+        }
+    }
+
     companion object {
         private val TRANSFORMATION_RANGE =
             Correspondence.transforming<Transformation, TransformationRange?>(
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/subjects/TransitionStateSubject.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/subjects/TransitionStateSubject.kt
index 3489892..a12ab78 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/subjects/TransitionStateSubject.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/subjects/TransitionStateSubject.kt
@@ -18,7 +18,8 @@
 
 import com.android.compose.animation.scene.OverscrollSpec
 import com.android.compose.animation.scene.SceneKey
-import com.android.compose.animation.scene.TransitionState
+import com.android.compose.animation.scene.content.state.ContentState
+import com.android.compose.animation.scene.content.state.TransitionState
 import com.google.common.truth.Fact.simpleFact
 import com.google.common.truth.FailureMetadata
 import com.google.common.truth.Subject
@@ -95,6 +96,25 @@
             .of(progressVelocity)
     }
 
+    fun hasPreviewProgress(progress: Float, tolerance: Float = 0f) {
+        check("previewProgress").that(actual.previewProgress).isWithin(tolerance).of(progress)
+    }
+
+    fun hasPreviewProgressVelocity(progressVelocity: Float, tolerance: Float = 0f) {
+        check("previewProgressVelocity")
+            .that(actual.previewProgressVelocity)
+            .isWithin(tolerance)
+            .of(progressVelocity)
+    }
+
+    fun isInPreviewStage() {
+        check("isInPreviewStage").that(actual.isInPreviewStage).isTrue()
+    }
+
+    fun isNotInPreviewStage() {
+        check("isInPreviewStage").that(actual.isInPreviewStage).isFalse()
+    }
+
     fun isInitiatedByUserInput() {
         check("isInitiatedByUserInput").that(actual.isInitiatedByUserInput).isTrue()
     }
@@ -113,12 +133,12 @@
     }
 
     fun hasBouncingScene(scene: SceneKey) {
-        if (actual !is TransitionState.HasOverscrollProperties) {
-            failWithActual(simpleFact("expected to be TransitionState.HasOverscrollProperties"))
+        if (actual !is ContentState.HasOverscrollProperties) {
+            failWithActual(simpleFact("expected to be ContentState.HasOverscrollProperties"))
         }
 
-        check("bouncingScene")
-            .that((actual as TransitionState.HasOverscrollProperties).bouncingScene)
+        check("bouncingContent")
+            .that((actual as ContentState.HasOverscrollProperties).bouncingContent)
             .isEqualTo(scene)
     }
 
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/transformation/AnchoredSizeTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/transformation/AnchoredSizeTest.kt
index 6233608..c9f71da 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/transformation/AnchoredSizeTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/transformation/AnchoredSizeTest.kt
@@ -25,7 +25,7 @@
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.unit.dp
 import androidx.test.ext.junit.runners.AndroidJUnit4
-import com.android.compose.animation.scene.SceneScope
+import com.android.compose.animation.scene.ContentScope
 import com.android.compose.animation.scene.TestElements
 import com.android.compose.animation.scene.TransitionBuilder
 import com.android.compose.animation.scene.TransitionRecordingSpec
@@ -108,8 +108,8 @@
     }
 
     private fun assertBarSizeMatchesGolden(
-        fromSceneContent: @Composable SceneScope.() -> Unit,
-        toSceneContent: @Composable SceneScope.() -> Unit,
+        fromSceneContent: @Composable ContentScope.() -> Unit,
+        toSceneContent: @Composable ContentScope.() -> Unit,
         transition: TransitionBuilder.() -> Unit,
     ) {
         val recordingSpec =
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/transformation/SharedElementTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/transformation/SharedElementTest.kt
index 8001f41..00acb13 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/transformation/SharedElementTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/transformation/SharedElementTest.kt
@@ -31,7 +31,7 @@
 import com.android.compose.animation.scene.Edge
 import com.android.compose.animation.scene.TestElements
 import com.android.compose.animation.scene.TestScenes
-import com.android.compose.animation.scene.inScene
+import com.android.compose.animation.scene.inContent
 import com.android.compose.animation.scene.testTransition
 import com.android.compose.test.assertSizeIsEqualTo
 import org.junit.Rule
@@ -125,10 +125,10 @@
                 sharedElement(TestElements.Foo, enabled = false)
 
                 // In SceneA, Foo leaves to the left edge.
-                translate(TestElements.Foo.inScene(TestScenes.SceneA), Edge.Left)
+                translate(TestElements.Foo.inContent(TestScenes.SceneA), Edge.Left)
 
                 // In SceneB, Foo comes from the bottom edge.
-                translate(TestElements.Foo.inScene(TestScenes.SceneB), Edge.Bottom)
+                translate(TestElements.Foo.inContent(TestScenes.SceneB), Edge.Bottom)
             },
         ) {
             before { onElement(TestElements.Foo).assertPositionInRootIsEqualTo(10.dp, 50.dp) }
diff --git a/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestSceneScope.kt b/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestContentScope.kt
similarity index 85%
rename from packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestSceneScope.kt
rename to packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestContentScope.kt
index fbd557f..00adefb 100644
--- a/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestSceneScope.kt
+++ b/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestContentScope.kt
@@ -20,11 +20,13 @@
 import androidx.compose.runtime.remember
 import androidx.compose.ui.Modifier
 
-/** `SceneScope` for tests, which allows a single scene to be drawn in a [SceneTransitionLayout]. */
+/**
+ * [ContentScope] for tests, which allows a single scene to be drawn in a [SceneTransitionLayout].
+ */
 @Composable
-fun TestSceneScope(
+fun TestContentScope(
     modifier: Modifier = Modifier,
-    content: @Composable SceneScope.() -> Unit,
+    content: @Composable ContentScope.() -> Unit,
 ) {
     val currentScene = remember { SceneKey("current") }
     val state = remember { MutableSceneTransitionLayoutState(currentScene) }
diff --git a/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestTransition.kt b/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestTransition.kt
index a37d78e..7f26b98 100644
--- a/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestTransition.kt
+++ b/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestTransition.kt
@@ -18,9 +18,7 @@
 
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.getValue
 import androidx.compose.runtime.rememberCoroutineScope
-import androidx.compose.runtime.setValue
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.semantics.SemanticsNode
 import androidx.compose.ui.test.SemanticsNodeInteraction
@@ -87,8 +85,8 @@
  * @sample com.android.compose.animation.scene.transformation.TranslateTest
  */
 fun ComposeContentTestRule.testTransition(
-    fromSceneContent: @Composable SceneScope.() -> Unit,
-    toSceneContent: @Composable SceneScope.() -> Unit,
+    fromSceneContent: @Composable ContentScope.() -> Unit,
+    toSceneContent: @Composable ContentScope.() -> Unit,
     transition: TransitionBuilder.() -> Unit,
     layoutModifier: Modifier = Modifier,
     fromScene: SceneKey = TestScenes.SceneA,
@@ -134,8 +132,8 @@
 
 /** Records the transition between two scenes of [transitionLayout][SceneTransitionLayout]. */
 fun MotionTestRule<ComposeToolkit>.recordTransition(
-    fromSceneContent: @Composable SceneScope.() -> Unit,
-    toSceneContent: @Composable SceneScope.() -> Unit,
+    fromSceneContent: @Composable ContentScope.() -> Unit,
+    toSceneContent: @Composable ContentScope.() -> Unit,
     transition: TransitionBuilder.() -> Unit,
     recordingSpec: TransitionRecordingSpec,
     layoutModifier: Modifier = Modifier,
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/notifications/data/repository/NotificationSettingsRepository.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/notifications/data/repository/NotificationSettingsRepository.kt
index 9e857deb..362e23d 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/notifications/data/repository/NotificationSettingsRepository.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/notifications/data/repository/NotificationSettingsRepository.kt
@@ -17,10 +17,13 @@
 package com.android.systemui.shared.notifications.data.repository
 
 import android.provider.Settings
+import android.provider.Settings.Secure.ZEN_DURATION_PROMPT
 import com.android.systemui.shared.settings.data.repository.SecureSettingsRepository
+import com.android.systemui.shared.settings.data.repository.SystemSettingsRepository
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.flowOn
@@ -30,9 +33,10 @@
 
 /** Provides access to state related to notification settings. */
 class NotificationSettingsRepository(
-    private val scope: CoroutineScope,
+    private val backgroundScope: CoroutineScope,
     private val backgroundDispatcher: CoroutineDispatcher,
     private val secureSettingsRepository: SecureSettingsRepository,
+    systemSettingsRepository: SystemSettingsRepository,
 ) {
     val isNotificationHistoryEnabled: Flow<Boolean> =
         secureSettingsRepository
@@ -48,9 +52,7 @@
             )
             .map { it == 1 }
             .flowOn(backgroundDispatcher)
-            .stateIn(
-                scope = scope,
-            )
+            .stateIn(scope = backgroundScope)
 
     suspend fun setShowNotificationsOnLockscreenEnabled(enabled: Boolean) {
         withContext(backgroundDispatcher) {
@@ -60,4 +62,27 @@
             )
         }
     }
+
+    val isCooldownEnabled: StateFlow<Boolean> =
+        systemSettingsRepository
+            .intSetting(name = Settings.System.NOTIFICATION_COOLDOWN_ENABLED)
+            .map { it == 1 }
+            .flowOn(backgroundDispatcher)
+            .stateIn(
+                scope = backgroundScope,
+                started = SharingStarted.Eagerly,
+                initialValue = false,
+            )
+
+    /** The default duration for DND mode when enabled. See [Settings.Secure.ZEN_DURATION]. */
+    val zenDuration: StateFlow<Int> =
+        secureSettingsRepository
+            .intSetting(name = Settings.Secure.ZEN_DURATION)
+            .distinctUntilChanged()
+            .flowOn(backgroundDispatcher)
+            .stateIn(
+                backgroundScope,
+                started = SharingStarted.Eagerly,
+                initialValue = ZEN_DURATION_PROMPT,
+            )
 }
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/notifications/domain/interactor/NotificationSettingsInteractor.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/notifications/domain/interactor/NotificationSettingsInteractor.kt
index b4105bd..a274f1d 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/notifications/domain/interactor/NotificationSettingsInteractor.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/notifications/domain/interactor/NotificationSettingsInteractor.kt
@@ -38,4 +38,5 @@
         val current = repository.isShowNotificationsOnLockScreenEnabled().value
         repository.setShowNotificationsOnLockscreenEnabled(!current)
     }
-}
+
+    val isCooldownEnabled = repository.isCooldownEnabled}
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/settings/data/repository/SystemSettingsRepository.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/settings/data/repository/SystemSettingsRepository.kt
new file mode 100644
index 0000000..afe82fb
--- /dev/null
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/settings/data/repository/SystemSettingsRepository.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.shared.settings.data.repository
+
+import android.content.ContentResolver
+import android.database.ContentObserver
+import android.provider.Settings
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.callbackFlow
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.withContext
+
+/**
+ * Defines interface for classes that can provide access to data from [Settings.System]. This
+ * repository doesn't guarantee to provide value across different users. For that see:
+ * [UserAwareSecureSettingsRepository] which does that for secure settings.
+ */
+interface SystemSettingsRepository {
+
+    /** Returns a [Flow] tracking the value of a setting as an [Int]. */
+    fun intSetting(
+        name: String,
+        defaultValue: Int = 0,
+    ): Flow<Int>
+
+    /** Updates the value of the setting with the given name. */
+    suspend fun setInt(
+        name: String,
+        value: Int,
+    )
+
+    suspend fun getInt(
+        name: String,
+        defaultValue: Int = 0,
+    ): Int
+
+    suspend fun getString(name: String): String?
+}
+
+class SystemSettingsRepositoryImpl(
+    private val contentResolver: ContentResolver,
+    private val backgroundDispatcher: CoroutineDispatcher,
+) : SystemSettingsRepository {
+
+    override fun intSetting(
+        name: String,
+        defaultValue: Int,
+    ): Flow<Int> {
+        return callbackFlow {
+                val observer =
+                    object : ContentObserver(null) {
+                        override fun onChange(selfChange: Boolean) {
+                            trySend(Unit)
+                        }
+                    }
+
+                contentResolver.registerContentObserver(
+                    Settings.System.getUriFor(name),
+                    /* notifyForDescendants= */ false,
+                    observer,
+                )
+                send(Unit)
+
+                awaitClose { contentResolver.unregisterContentObserver(observer) }
+            }
+            .map { Settings.System.getInt(contentResolver, name, defaultValue) }
+            // The above work is done on the background thread (which is important for accessing
+            // settings through the content resolver).
+            .flowOn(backgroundDispatcher)
+    }
+
+    override suspend fun setInt(name: String, value: Int) {
+        withContext(backgroundDispatcher) {
+            Settings.System.putInt(
+                contentResolver,
+                name,
+                value,
+            )
+        }
+    }
+
+    override suspend fun getInt(name: String, defaultValue: Int): Int {
+        return withContext(backgroundDispatcher) {
+            Settings.System.getInt(
+                contentResolver,
+                name,
+                defaultValue,
+            )
+        }
+    }
+
+    override suspend fun getString(name: String): String? {
+        return withContext(backgroundDispatcher) {
+            Settings.System.getString(
+                contentResolver,
+                name,
+            )
+        }
+    }
+}
diff --git a/packages/SystemUI/customization/tests/utils/src/com/android/systemui/shared/settings/data/repository/FakeSystemSettingsRepository.kt b/packages/SystemUI/customization/tests/utils/src/com/android/systemui/shared/settings/data/repository/FakeSystemSettingsRepository.kt
new file mode 100644
index 0000000..7da2b40
--- /dev/null
+++ b/packages/SystemUI/customization/tests/utils/src/com/android/systemui/shared/settings/data/repository/FakeSystemSettingsRepository.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.shared.settings.data.repository
+
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.map
+
+class FakeSystemSettingsRepository : SystemSettingsRepository {
+
+    private val settings = MutableStateFlow<Map<String, String>>(mutableMapOf())
+
+    override fun intSetting(name: String, defaultValue: Int): Flow<Int> {
+        return settings.map { it.getOrDefault(name, defaultValue.toString()) }.map { it.toInt() }
+    }
+
+    override suspend fun setInt(name: String, value: Int) {
+        settings.value = settings.value.toMutableMap().apply { this[name] = value.toString() }
+    }
+
+    override suspend fun getInt(name: String, defaultValue: Int): Int {
+        return settings.value[name]?.toInt() ?: defaultValue
+    }
+
+    override suspend fun getString(name: String): String? {
+        return settings.value[name]
+    }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
index 6c3f3c1..f7f69d3 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
@@ -105,6 +105,7 @@
 import org.mockito.Mockito.clearInvocations
 import org.mockito.Mockito.mock
 import org.mockito.Mockito.never
+import org.mockito.Mockito.reset
 import org.mockito.Mockito.spy
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
@@ -183,7 +184,7 @@
         whenever(view.context).thenReturn(mContext)
         whenever(view.resources).thenReturn(testableResources.resources)
 
-        val lp = FrameLayout.LayoutParams(/* width=  */ 0, /* height= */ 0)
+        val lp = FrameLayout.LayoutParams(/* width= */ 0, /* height= */ 0)
         lp.gravity = 0
         whenever(view.layoutParams).thenReturn(lp)
 
@@ -542,6 +543,7 @@
         // THEN the next security method of None will dismiss keyguard.
         verify(viewMediatorCallback, never()).keyguardDone(anyInt())
     }
+
     @Test
     fun showNextSecurityScreenOrFinish_SimPin_Swipe_userNotSetup() {
         // GIVEN the current security method is SimPin
@@ -635,14 +637,6 @@
         verify(configurationController).addCallback(configurationListenerArgumentCaptor.capture())
         clearInvocations(viewFlipperController)
         configurationListenerArgumentCaptor.value.onDensityOrFontScaleChanged()
-        verify(viewFlipperController).clearViews()
-        verify(viewFlipperController)
-            .asynchronouslyInflateView(
-                eq(SecurityMode.PIN),
-                any(),
-                onViewInflatedCallbackArgumentCaptor.capture()
-            )
-        onViewInflatedCallbackArgumentCaptor.value.onViewInflated(inputViewController)
         verify(view).onDensityOrFontScaleChanged()
     }
 
@@ -771,7 +765,9 @@
         underTest.reinflateViewFlipper(onViewInflatedCallback)
         verify(viewFlipperController).clearViews()
         verify(viewFlipperController)
-            .asynchronouslyInflateView(any(), any(), eq(onViewInflatedCallback))
+            .asynchronouslyInflateView(any(), any(), onViewInflatedCallbackArgumentCaptor.capture())
+        onViewInflatedCallbackArgumentCaptor.value.onViewInflated(inputViewController)
+        verify(view).updateSecurityViewFlipper()
     }
 
     @Test
@@ -935,8 +931,10 @@
         underTest.onViewAttached()
         verify(userSwitcherController)
             .addUserSwitchCallback(capture(userSwitchCallbackArgumentCaptor))
+        reset(primaryBouncerInteractor)
         userSwitchCallbackArgumentCaptor.value.onUserSwitched()
-        verify(viewFlipperController).asynchronouslyInflateView(any(), any(), any())
+
+        verify(primaryBouncerInteractor).setLastShownPrimarySecurityScreen(any())
     }
 
     @Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/BouncerFullscreenSwipeTouchHandlerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/BouncerFullscreenSwipeTouchHandlerTest.java
new file mode 100644
index 0000000..4850085
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/BouncerFullscreenSwipeTouchHandlerTest.java
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.ambient.touch;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyFloat;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.animation.ValueAnimator;
+import android.content.pm.UserInfo;
+import android.graphics.Rect;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
+import android.view.GestureDetector.OnGestureListener;
+import android.view.MotionEvent;
+import android.view.VelocityTracker;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.logging.UiEventLogger;
+import com.android.internal.widget.LockPatternUtils;
+import com.android.systemui.Flags;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.ambient.touch.scrim.ScrimController;
+import com.android.systemui.ambient.touch.scrim.ScrimManager;
+import com.android.systemui.communal.ui.viewmodel.CommunalViewModel;
+import com.android.systemui.kosmos.KosmosJavaAdapter;
+import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.settings.FakeUserTracker;
+import com.android.systemui.shared.system.InputChannelCompat;
+import com.android.systemui.statusbar.NotificationShadeWindowController;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
+import com.android.wm.shell.animation.FlingAnimationUtils;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Collections;
+import java.util.Optional;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+@EnableFlags(Flags.FLAG_HUBMODE_FULLSCREEN_VERTICAL_SWIPE)
+@DisableFlags(Flags.FLAG_COMMUNAL_BOUNCER_DO_NOT_MODIFY_PLUGIN_OPEN)
+public class BouncerFullscreenSwipeTouchHandlerTest extends SysuiTestCase {
+    private KosmosJavaAdapter mKosmos;
+
+    @Mock
+    CentralSurfaces mCentralSurfaces;
+
+    @Mock
+    ScrimManager mScrimManager;
+
+    @Mock
+    ScrimController mScrimController;
+
+    @Mock
+    NotificationShadeWindowController mNotificationShadeWindowController;
+
+    @Mock
+    FlingAnimationUtils mFlingAnimationUtils;
+
+    @Mock
+    FlingAnimationUtils mFlingAnimationUtilsClosing;
+
+    @Mock
+    TouchHandler.TouchSession mTouchSession;
+
+    BouncerSwipeTouchHandler mTouchHandler;
+
+    @Mock
+    BouncerSwipeTouchHandler.ValueAnimatorCreator mValueAnimatorCreator;
+
+    @Mock
+    ValueAnimator mValueAnimator;
+
+    @Mock
+    BouncerSwipeTouchHandler.VelocityTrackerFactory mVelocityTrackerFactory;
+
+    @Mock
+    VelocityTracker mVelocityTracker;
+
+    @Mock
+    UiEventLogger mUiEventLogger;
+
+    @Mock
+    LockPatternUtils mLockPatternUtils;
+
+    @Mock
+    ActivityStarter mActivityStarter;
+
+    @Mock
+    CommunalViewModel mCommunalViewModel;
+
+    FakeUserTracker mUserTracker;
+
+    private static final float TOUCH_REGION = .3f;
+    private static final float MIN_BOUNCER_HEIGHT = .05f;
+
+    private static final Rect SCREEN_BOUNDS = new Rect(0, 0, 1024, 100);
+    private static final UserInfo CURRENT_USER_INFO = new UserInfo(
+            10,
+            /* name= */ "user10",
+            /* flags= */ 0
+    );
+
+    @Before
+    public void setup() {
+        mKosmos = new KosmosJavaAdapter(this);
+        MockitoAnnotations.initMocks(this);
+        mUserTracker = new FakeUserTracker();
+        mTouchHandler = new BouncerSwipeTouchHandler(
+                mKosmos.getTestScope(),
+                mScrimManager,
+                Optional.of(mCentralSurfaces),
+                mNotificationShadeWindowController,
+                mValueAnimatorCreator,
+                mVelocityTrackerFactory,
+                mLockPatternUtils,
+                mUserTracker,
+                mCommunalViewModel,
+                mFlingAnimationUtils,
+                mFlingAnimationUtilsClosing,
+                TOUCH_REGION,
+                MIN_BOUNCER_HEIGHT,
+                mUiEventLogger,
+                mActivityStarter);
+
+        when(mScrimManager.getCurrentController()).thenReturn(mScrimController);
+        when(mValueAnimatorCreator.create(anyFloat(), anyFloat())).thenReturn(mValueAnimator);
+        when(mVelocityTrackerFactory.obtain()).thenReturn(mVelocityTracker);
+        when(mFlingAnimationUtils.getMinVelocityPxPerSecond()).thenReturn(Float.MAX_VALUE);
+        when(mTouchSession.getBounds()).thenReturn(SCREEN_BOUNDS);
+        when(mLockPatternUtils.isSecure(CURRENT_USER_INFO.id)).thenReturn(true);
+
+        mUserTracker.set(Collections.singletonList(CURRENT_USER_INFO), 0);
+    }
+
+    /**
+     * Ensures expansion does not happen for full vertical swipes when touch is not available.
+     */
+    @Test
+    public void testFullSwipe_notInitiatedWhenNotAvailable() {
+        mTouchHandler.onGlanceableTouchAvailable(false);
+        mTouchHandler.onSessionStart(mTouchSession);
+        ArgumentCaptor<OnGestureListener> gestureListenerCaptor =
+                ArgumentCaptor.forClass(OnGestureListener.class);
+        verify(mTouchSession).registerGestureListener(gestureListenerCaptor.capture());
+
+        // A touch within range at the bottom of the screen should trigger listening
+        assertThat(gestureListenerCaptor.getValue()
+                .onScroll(Mockito.mock(MotionEvent.class),
+                        Mockito.mock(MotionEvent.class),
+                        1,
+                        2)).isFalse();
+    }
+
+    /**
+     * Ensures expansion only happens for full vertical swipes when touch is available.
+     */
+    @Test
+    public void testFullSwipe_initiatedWhenAvailable() {
+        mTouchHandler.onGlanceableTouchAvailable(true);
+        mTouchHandler.onSessionStart(mTouchSession);
+        ArgumentCaptor<OnGestureListener> gestureListenerCaptor =
+                ArgumentCaptor.forClass(OnGestureListener.class);
+        verify(mTouchSession).registerGestureListener(gestureListenerCaptor.capture());
+
+        // A touch within range at the bottom of the screen should trigger listening
+        assertThat(gestureListenerCaptor.getValue()
+                .onScroll(Mockito.mock(MotionEvent.class),
+                        Mockito.mock(MotionEvent.class),
+                        1,
+                        2)).isTrue();
+    }
+
+    @Test
+    public void testFullSwipe_motionUpResetsTouchState() {
+        mTouchHandler.onGlanceableTouchAvailable(true);
+        mTouchHandler.onSessionStart(mTouchSession);
+        ArgumentCaptor<OnGestureListener> gestureListenerCaptor =
+                ArgumentCaptor.forClass(OnGestureListener.class);
+        ArgumentCaptor<InputChannelCompat.InputEventListener> inputListenerCaptor =
+                ArgumentCaptor.forClass(InputChannelCompat.InputEventListener.class);
+        verify(mTouchSession).registerGestureListener(gestureListenerCaptor.capture());
+        verify(mTouchSession).registerInputListener(inputListenerCaptor.capture());
+
+        // A touch within range at the bottom of the screen should trigger listening
+        assertThat(gestureListenerCaptor.getValue()
+                .onScroll(Mockito.mock(MotionEvent.class),
+                        Mockito.mock(MotionEvent.class),
+                        1,
+                        2)).isTrue();
+
+        MotionEvent upEvent = Mockito.mock(MotionEvent.class);
+        when(upEvent.getAction()).thenReturn(MotionEvent.ACTION_UP);
+        inputListenerCaptor.getValue().onInputEvent(upEvent);
+        verify(mCommunalViewModel).onResetTouchState();
+    }
+
+    @Test
+    public void testFullSwipe_motionCancelResetsTouchState() {
+        mTouchHandler.onGlanceableTouchAvailable(true);
+        mTouchHandler.onSessionStart(mTouchSession);
+        ArgumentCaptor<OnGestureListener> gestureListenerCaptor =
+                ArgumentCaptor.forClass(OnGestureListener.class);
+        ArgumentCaptor<InputChannelCompat.InputEventListener> inputListenerCaptor =
+                ArgumentCaptor.forClass(InputChannelCompat.InputEventListener.class);
+        verify(mTouchSession).registerGestureListener(gestureListenerCaptor.capture());
+        verify(mTouchSession).registerInputListener(inputListenerCaptor.capture());
+
+        // A touch within range at the bottom of the screen should trigger listening
+        assertThat(gestureListenerCaptor.getValue()
+                .onScroll(Mockito.mock(MotionEvent.class),
+                        Mockito.mock(MotionEvent.class),
+                        1,
+                        2)).isTrue();
+
+        MotionEvent upEvent = Mockito.mock(MotionEvent.class);
+        when(upEvent.getAction()).thenReturn(MotionEvent.ACTION_CANCEL);
+        inputListenerCaptor.getValue().onInputEvent(upEvent);
+        verify(mCommunalViewModel).onResetTouchState();
+    }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/BouncerSwipeTouchHandlerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/BouncerSwipeTouchHandlerTest.java
index 7ebc224..0e98b84 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/BouncerSwipeTouchHandlerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/BouncerSwipeTouchHandlerTest.java
@@ -50,6 +50,8 @@
 import com.android.systemui.ambient.touch.scrim.ScrimController;
 import com.android.systemui.ambient.touch.scrim.ScrimManager;
 import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants;
+import com.android.systemui.communal.ui.viewmodel.CommunalViewModel;
+import com.android.systemui.kosmos.KosmosJavaAdapter;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.settings.FakeUserTracker;
 import com.android.systemui.shade.ShadeExpansionChangeEvent;
@@ -72,7 +74,9 @@
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
+@DisableFlags(Flags.FLAG_HUBMODE_FULLSCREEN_VERTICAL_SWIPE)
 public class BouncerSwipeTouchHandlerTest extends SysuiTestCase {
+    private KosmosJavaAdapter mKosmos;
     @Mock
     CentralSurfaces mCentralSurfaces;
 
@@ -120,6 +124,9 @@
     @Mock
     Region mRegion;
 
+    @Mock
+    CommunalViewModel mCommunalViewModel;
+
     @Captor
     ArgumentCaptor<Rect> mRectCaptor;
 
@@ -139,9 +146,11 @@
 
     @Before
     public void setup() {
+        mKosmos = new KosmosJavaAdapter(this);
         MockitoAnnotations.initMocks(this);
         mUserTracker = new FakeUserTracker();
         mTouchHandler = new BouncerSwipeTouchHandler(
+                mKosmos.getTestScope(),
                 mScrimManager,
                 Optional.of(mCentralSurfaces),
                 mNotificationShadeWindowController,
@@ -149,6 +158,7 @@
                 mVelocityTrackerFactory,
                 mLockPatternUtils,
                 mUserTracker,
+                mCommunalViewModel,
                 mFlingAnimationUtils,
                 mFlingAnimationUtilsClosing,
                 TOUCH_REGION,
@@ -201,7 +211,6 @@
                         2)).isTrue();
     }
 
-
     /**
      * Ensures expansion only happens when touch down happens in valid part of the screen.
      */
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/ShadeTouchHandlerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/ShadeTouchHandlerTest.kt
index 7fd9ce2..204d4b0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/ShadeTouchHandlerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/ShadeTouchHandlerTest.kt
@@ -26,8 +26,10 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.ambient.touch.TouchHandler.TouchSession
 import com.android.systemui.communal.domain.interactor.communalSettingsInteractor
+import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
 import com.android.systemui.flags.Flags.COMMUNAL_SERVICE_ENABLED
 import com.android.systemui.flags.fakeFeatureFlagsClassic
+import com.android.systemui.kosmos.testScope
 import com.android.systemui.shade.ShadeViewController
 import com.android.systemui.shared.system.InputChannelCompat
 import com.android.systemui.statusbar.phone.CentralSurfaces
@@ -50,11 +52,11 @@
 @RunWith(AndroidJUnit4::class)
 class ShadeTouchHandlerTest : SysuiTestCase() {
     private var kosmos = testKosmos()
-
     private var mCentralSurfaces = mock<CentralSurfaces>()
     private var mShadeViewController = mock<ShadeViewController>()
     private var mDreamManager = mock<DreamManager>()
     private var mTouchSession = mock<TouchSession>()
+    private var communalViewModel = mock<CommunalViewModel>()
 
     private lateinit var mTouchHandler: ShadeTouchHandler
 
@@ -65,9 +67,11 @@
     fun setup() {
         mTouchHandler =
             ShadeTouchHandler(
+                kosmos.testScope,
                 Optional.of(mCentralSurfaces),
                 mShadeViewController,
                 mDreamManager,
+                communalViewModel,
                 kosmos.communalSettingsInteractor,
                 TOUCH_HEIGHT
             )
@@ -75,6 +79,7 @@
 
     // Verifies that a swipe down in the gesture region is captured by the shade touch handler.
     @Test
+    @DisableFlags(Flags.FLAG_HUBMODE_FULLSCREEN_VERTICAL_SWIPE)
     fun testSwipeDown_captured() {
         val captured = swipe(Direction.DOWN)
         Truth.assertThat(captured).isTrue()
@@ -82,6 +87,7 @@
 
     // Verifies that a swipe in the upward direction is not captured.
     @Test
+    @DisableFlags(Flags.FLAG_HUBMODE_FULLSCREEN_VERTICAL_SWIPE)
     fun testSwipeUp_notCaptured() {
         val captured = swipe(Direction.UP)
 
@@ -91,6 +97,7 @@
 
     // Verifies that a swipe down forwards captured touches to central surfaces for handling.
     @Test
+    @DisableFlags(Flags.FLAG_HUBMODE_FULLSCREEN_VERTICAL_SWIPE)
     @EnableFlags(Flags.FLAG_COMMUNAL_HUB)
     fun testSwipeDown_communalEnabled_sentToCentralSurfaces() {
         kosmos.fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, true)
@@ -103,7 +110,7 @@
 
     // Verifies that a swipe down forwards captured touches to the shade view for handling.
     @Test
-    @DisableFlags(Flags.FLAG_COMMUNAL_HUB)
+    @DisableFlags(Flags.FLAG_COMMUNAL_HUB, Flags.FLAG_HUBMODE_FULLSCREEN_VERTICAL_SWIPE)
     fun testSwipeDown_communalDisabled_sentToShadeView() {
         swipe(Direction.DOWN)
 
@@ -114,6 +121,7 @@
     // Verifies that a swipe down while dreaming forwards captured touches to the shade view for
     // handling.
     @Test
+    @DisableFlags(Flags.FLAG_HUBMODE_FULLSCREEN_VERTICAL_SWIPE)
     fun testSwipeDown_dreaming_sentToShadeView() {
         whenever(mDreamManager.isDreaming).thenReturn(true)
         swipe(Direction.DOWN)
@@ -124,6 +132,7 @@
 
     // Verifies that a swipe up is not forwarded to central surfaces.
     @Test
+    @DisableFlags(Flags.FLAG_HUBMODE_FULLSCREEN_VERTICAL_SWIPE)
     @EnableFlags(Flags.FLAG_COMMUNAL_HUB)
     fun testSwipeUp_communalEnabled_touchesNotSent() {
         kosmos.fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, true)
@@ -137,7 +146,7 @@
 
     // Verifies that a swipe up is not forwarded to the shade view.
     @Test
-    @DisableFlags(Flags.FLAG_COMMUNAL_HUB)
+    @DisableFlags(Flags.FLAG_COMMUNAL_HUB, Flags.FLAG_HUBMODE_FULLSCREEN_VERTICAL_SWIPE)
     fun testSwipeUp_communalDisabled_touchesNotSent() {
         swipe(Direction.UP)
 
@@ -147,6 +156,7 @@
     }
 
     @Test
+    @DisableFlags(Flags.FLAG_HUBMODE_FULLSCREEN_VERTICAL_SWIPE)
     fun testCancelMotionEvent_popsTouchSession() {
         swipe(Direction.DOWN)
         val event = MotionEvent.obtain(0, 0, MotionEvent.ACTION_CANCEL, 0f, 0f, 0)
@@ -154,6 +164,60 @@
         verify(mTouchSession).pop()
     }
 
+    @Test
+    @EnableFlags(Flags.FLAG_HUBMODE_FULLSCREEN_VERTICAL_SWIPE)
+    fun testFullVerticalSwipe_initiatedWhenAvailable() {
+        // Indicate touches are available
+        mTouchHandler.onGlanceableTouchAvailable(true)
+
+        // Verify swipe is handled
+        val captured = swipe(Direction.DOWN)
+        Truth.assertThat(captured).isTrue()
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_HUBMODE_FULLSCREEN_VERTICAL_SWIPE)
+    fun testFullVerticalSwipe_notInitiatedWhenNotAvailable() {
+        // Indicate touches aren't available
+        mTouchHandler.onGlanceableTouchAvailable(false)
+
+        // Verify swipe is not handled
+        val captured = swipe(Direction.DOWN)
+        Truth.assertThat(captured).isFalse()
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_HUBMODE_FULLSCREEN_VERTICAL_SWIPE)
+    fun testFullVerticalSwipe_resetsTouchStateOnUp() {
+        // Indicate touches are available
+        mTouchHandler.onGlanceableTouchAvailable(true)
+
+        // Verify swipe is handled
+        swipe(Direction.DOWN)
+
+        val upEvent: MotionEvent = mock()
+        whenever(upEvent.action).thenReturn(MotionEvent.ACTION_UP)
+        mInputListenerCaptor.lastValue.onInputEvent(upEvent)
+
+        verify(communalViewModel).onResetTouchState()
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_HUBMODE_FULLSCREEN_VERTICAL_SWIPE)
+    fun testFullVerticalSwipe_resetsTouchStateOnCancel() {
+        // Indicate touches are available
+        mTouchHandler.onGlanceableTouchAvailable(true)
+
+        // Verify swipe is handled
+        swipe(Direction.DOWN)
+
+        val upEvent: MotionEvent = mock()
+        whenever(upEvent.action).thenReturn(MotionEvent.ACTION_CANCEL)
+        mInputListenerCaptor.lastValue.onInputEvent(upEvent)
+
+        verify(communalViewModel).onResetTouchState()
+    }
+
     /**
      * Simulates a swipe in the given direction and returns true if the touch was intercepted by the
      * touch handler's gesture listener.
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
index 242e822..e2a6a55 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
@@ -35,6 +35,8 @@
 import android.view.accessibility.AccessibilityManager
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
+import com.android.app.viewcapture.ViewCapture
+import com.android.app.viewcapture.ViewCaptureAwareWindowManager
 import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.systemui.Flags
 import com.android.systemui.SysuiTestCase
@@ -70,6 +72,7 @@
 import com.android.systemui.testKosmos
 import com.android.systemui.user.domain.interactor.SelectedUserInteractor
 import com.google.common.truth.Truth.assertThat
+import dagger.Lazy
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.runCurrent
@@ -108,6 +111,7 @@
 
     @Mock private lateinit var inflater: LayoutInflater
     @Mock private lateinit var windowManager: WindowManager
+    @Mock private lateinit var lazyViewCapture: kotlin.Lazy<ViewCapture>
     @Mock private lateinit var accessibilityManager: AccessibilityManager
     @Mock private lateinit var statusBarStateController: StatusBarStateController
     @Mock private lateinit var statusBarKeyguardViewManager: StatusBarKeyguardViewManager
@@ -192,7 +196,8 @@
             UdfpsControllerOverlay(
                 context,
                 inflater,
-                windowManager,
+                ViewCaptureAwareWindowManager(windowManager, lazyViewCapture,
+                        isViewCaptureEnabled = false),
                 accessibilityManager,
                 statusBarStateController,
                 statusBarKeyguardViewManager,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index 54e0725..d86890b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -65,12 +65,12 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewRootImpl;
-import android.view.WindowManager;
 import android.view.accessibility.AccessibilityManager;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
+import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
 import com.android.internal.logging.InstanceIdSequence;
 import com.android.internal.util.LatencyTracker;
 import com.android.keyguard.KeyguardUpdateMonitor;
@@ -87,6 +87,7 @@
 import com.android.systemui.biometrics.ui.viewmodel.DeviceEntryUdfpsTouchOverlayViewModel;
 import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor;
 import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor;
+import com.android.systemui.camera.CameraGestureHelper;
 import com.android.systemui.classifier.FalsingCollector;
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor;
 import com.android.systemui.dump.DumpManager;
@@ -118,6 +119,8 @@
 
 import dagger.Lazy;
 
+import javax.inject.Provider;
+
 import kotlinx.coroutines.CoroutineScope;
 
 import org.junit.Before;
@@ -152,7 +155,7 @@
     @Mock
     private FingerprintManager mFingerprintManager;
     @Mock
-    private WindowManager mWindowManager;
+    private ViewCaptureAwareWindowManager mWindowManager;
     @Mock
     private StatusBarStateController mStatusBarStateController;
     @Mock
@@ -261,6 +264,8 @@
     private Lazy<DeviceEntryUdfpsTouchOverlayViewModel> mDeviceEntryUdfpsTouchOverlayViewModel;
     @Mock
     private Lazy<DefaultUdfpsTouchOverlayViewModel> mDefaultUdfpsTouchOverlayViewModel;
+    @Mock
+    private Provider<CameraGestureHelper> mCameraGestureHelper;
 
     @Before
     public void setUp() {
@@ -269,7 +274,8 @@
                 mPowerRepository,
                 mock(FalsingCollector.class),
                 mock(ScreenOffAnimationController.class),
-                mStatusBarStateController
+                mStatusBarStateController,
+                mCameraGestureHelper
         );
         mPowerRepository.updateWakefulness(
                 WakefulnessState.AWAKE,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractorTest.kt
index 4fd44cc..cfe0bec 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractorTest.kt
@@ -35,6 +35,7 @@
 import com.android.systemui.bouncer.data.repository.fakeKeyguardBouncerRepository
 import com.android.systemui.bouncer.shared.model.BouncerMessageModel
 import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.deviceentry.domain.interactor.deviceEntryBiometricsAllowedInteractor
 import com.android.systemui.deviceentry.domain.interactor.deviceEntryFingerprintAuthInteractor
 import com.android.systemui.flags.SystemPropertiesHelper
 import com.android.systemui.keyguard.data.repository.fakeBiometricSettingsRepository
@@ -108,7 +109,9 @@
                 facePropertyRepository = kosmos.fakeFacePropertyRepository,
                 deviceEntryFingerprintAuthInteractor = kosmos.deviceEntryFingerprintAuthInteractor,
                 faceAuthRepository = kosmos.fakeDeviceEntryFaceAuthRepository,
-                securityModel = securityModel
+                securityModel = securityModel,
+                deviceEntryBiometricsAllowedInteractor =
+                    kosmos.deviceEntryBiometricsAllowedInteractor,
             )
         biometricSettingsRepository.setIsFingerprintAuthCurrentlyAllowed(
             fingerprintAuthCurrentlyAllowed
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt
index c28cf34..a09189e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt
@@ -372,6 +372,7 @@
         nonAuxiliarySubtypes: Int,
     ): InputMethodModel {
         return InputMethodModel(
+            userId = UUID.randomUUID().mostSignificantBits.toInt(),
             imeId = UUID.randomUUID().toString(),
             subtypes =
                 List(auxiliarySubtypes + nonAuxiliarySubtypes) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/camera/CameraGestureHelperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/camera/CameraGestureHelperTest.kt
similarity index 67%
rename from packages/SystemUI/tests/src/com/android/systemui/camera/CameraGestureHelperTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/camera/CameraGestureHelperTest.kt
index bea0db6..a0928ad 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/camera/CameraGestureHelperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/camera/CameraGestureHelperTest.kt
@@ -18,6 +18,7 @@
 
 import android.app.ActivityManager
 import android.app.IActivityTaskManager
+import android.app.admin.DevicePolicyManager
 import android.content.ComponentName
 import android.content.ContentResolver
 import android.content.Intent
@@ -29,82 +30,73 @@
 import com.android.systemui.ActivityIntentHelper
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.statusbar.NotificationLockscreenUserManager
 import com.android.systemui.statusbar.StatusBarState
-import com.android.systemui.statusbar.phone.CentralSurfaces
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
 import com.android.systemui.statusbar.policy.KeyguardStateController
 import com.android.systemui.user.domain.interactor.SelectedUserInteractor
 import com.android.systemui.util.mockito.KotlinArgumentCaptor
+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.assertThat
 import com.google.common.util.concurrent.MoreExecutors
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.isNull
 import org.mockito.Mock
-import org.mockito.Mockito.any
 import org.mockito.Mockito.anyInt
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
-import org.mockito.Mockito.`when` as whenever
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class CameraGestureHelperTest : SysuiTestCase() {
 
-    @Mock
-    lateinit var centralSurfaces: CentralSurfaces
-    @Mock
-    lateinit var statusBarKeyguardViewManager: StatusBarKeyguardViewManager
-    @Mock
-    lateinit var keyguardStateController: KeyguardStateController
-    @Mock
-    lateinit var packageManager: PackageManager
-    @Mock
-    lateinit var activityManager: ActivityManager
-    @Mock
-    lateinit var activityStarter: ActivityStarter
-    @Mock
-    lateinit var activityIntentHelper: ActivityIntentHelper
-    @Mock
-    lateinit var activityTaskManager: IActivityTaskManager
-    @Mock
-    lateinit var cameraIntents: CameraIntentsWrapper
-    @Mock
-    lateinit var contentResolver: ContentResolver
-    @Mock
-    lateinit var mSelectedUserInteractor: SelectedUserInteractor
+    @Mock lateinit var statusBarKeyguardViewManager: StatusBarKeyguardViewManager
+    @Mock lateinit var keyguardStateController: KeyguardStateController
+    @Mock lateinit var packageManager: PackageManager
+    @Mock lateinit var activityManager: ActivityManager
+    @Mock lateinit var activityStarter: ActivityStarter
+    @Mock lateinit var activityIntentHelper: ActivityIntentHelper
+    @Mock lateinit var activityTaskManager: IActivityTaskManager
+    @Mock lateinit var cameraIntents: CameraIntentsWrapper
+    @Mock lateinit var contentResolver: ContentResolver
+    @Mock lateinit var mSelectedUserInteractor: SelectedUserInteractor
+    @Mock lateinit var devicePolicyManager: DevicePolicyManager
+    @Mock lateinit var lockscreenUserManager: NotificationLockscreenUserManager
 
     private lateinit var underTest: CameraGestureHelper
 
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
-        whenever(cameraIntents.getSecureCameraIntent(anyInt())).thenReturn(
-            Intent(CameraIntents.DEFAULT_SECURE_CAMERA_INTENT_ACTION)
-        )
-        whenever(cameraIntents.getInsecureCameraIntent(anyInt())).thenReturn(
-            Intent(CameraIntents.DEFAULT_INSECURE_CAMERA_INTENT_ACTION)
-        )
+        whenever(cameraIntents.getSecureCameraIntent(any()))
+            .thenReturn(Intent(CameraIntents.DEFAULT_SECURE_CAMERA_INTENT_ACTION))
+        whenever(cameraIntents.getInsecureCameraIntent(any()))
+            .thenReturn(Intent(CameraIntents.DEFAULT_INSECURE_CAMERA_INTENT_ACTION))
 
         prepare()
 
-        underTest = CameraGestureHelper(
-            context = mock(),
-            centralSurfaces = centralSurfaces,
-            keyguardStateController = keyguardStateController,
-            statusBarKeyguardViewManager = statusBarKeyguardViewManager,
-            packageManager = packageManager,
-            activityManager = activityManager,
-            activityStarter = activityStarter,
-            activityIntentHelper = activityIntentHelper,
-            activityTaskManager = activityTaskManager,
-            cameraIntents = cameraIntents,
-            contentResolver = contentResolver,
-            uiExecutor = MoreExecutors.directExecutor(),
-            selectedUserInteractor = mSelectedUserInteractor,
-        )
+        underTest =
+            CameraGestureHelper(
+                context = mock(),
+                keyguardStateController = keyguardStateController,
+                statusBarKeyguardViewManager = statusBarKeyguardViewManager,
+                packageManager = packageManager,
+                activityManager = activityManager,
+                activityStarter = activityStarter,
+                activityIntentHelper = activityIntentHelper,
+                activityTaskManager = activityTaskManager,
+                cameraIntents = cameraIntents,
+                contentResolver = contentResolver,
+                uiExecutor = MoreExecutors.directExecutor(),
+                selectedUserInteractor = mSelectedUserInteractor,
+                devicePolicyManager = devicePolicyManager,
+                lockscreenUserManager = lockscreenUserManager,
+            )
     }
 
     /**
@@ -116,13 +108,13 @@
      * @param isCameraAllowedByAdmin Whether the device administrator allows use of the camera app
      * @param installedCameraAppCount The number of installed camera apps on the device
      * @param isUsingSecureScreenLockOption Whether the user-controlled setting for Screen Lock is
-     * set with a "secure" option that requires the user to provide some secret/credentials to be
-     * able to unlock the device, for example "Face Unlock", "PIN", or "Password". Examples of
-     * non-secure options are "None" and "Swipe"
+     *   set with a "secure" option that requires the user to provide some secret/credentials to be
+     *   able to unlock the device, for example "Face Unlock", "PIN", or "Password". Examples of
+     *   non-secure options are "None" and "Swipe"
      * @param isCameraActivityRunningOnTop Whether the camera activity is running at the top of the
-     * most recent/current task of activities
+     *   most recent/current task of activities
      * @param isTaskListEmpty Whether there are no active activity tasks at all. Note that this is
-     * treated as `false` if [isCameraActivityRunningOnTop] is set to `true`
+     *   treated as `false` if [isCameraActivityRunningOnTop] is set to `true`
      */
     private fun prepare(
         isCameraAllowedByAdmin: Boolean = true,
@@ -131,7 +123,13 @@
         isCameraActivityRunningOnTop: Boolean = false,
         isTaskListEmpty: Boolean = false,
     ) {
-        whenever(centralSurfaces.isCameraAllowedByAdmin).thenReturn(isCameraAllowedByAdmin)
+        whenever(lockscreenUserManager.getCurrentUserId()).thenReturn(1)
+        if (isCameraAllowedByAdmin) {
+            whenever(devicePolicyManager.getCameraDisabled(isNull(), any())).thenReturn(false)
+            whenever(keyguardStateController.isMethodSecure).thenReturn(false)
+        } else {
+            whenever(devicePolicyManager.getCameraDisabled(isNull(), any())).thenReturn(true)
+        }
 
         whenever(activityIntentHelper.wouldLaunchResolverActivity(any(), anyInt()))
             .thenReturn(installedCameraAppCount > 1)
@@ -141,30 +139,26 @@
             .thenReturn(!isUsingSecureScreenLockOption)
 
         if (installedCameraAppCount >= 1) {
-            val resolveInfo = ResolveInfo().apply {
-                this.activityInfo = ActivityInfo().apply {
-                    packageName = CAMERA_APP_PACKAGE_NAME
+            val resolveInfo =
+                ResolveInfo().apply {
+                    this.activityInfo =
+                        ActivityInfo().apply { packageName = CAMERA_APP_PACKAGE_NAME }
                 }
-            }
-            whenever(packageManager.resolveActivityAsUser(any(), anyInt(), anyInt())).thenReturn(
-                resolveInfo
-            )
+            whenever(packageManager.resolveActivityAsUser(any(), anyInt(), anyInt()))
+                .thenReturn(resolveInfo)
         } else {
-            whenever(packageManager.resolveActivityAsUser(any(), anyInt(), anyInt())).thenReturn(
-                null
-            )
+            whenever(packageManager.resolveActivityAsUser(any(), anyInt(), anyInt()))
+                .thenReturn(null)
         }
 
         when {
             isCameraActivityRunningOnTop -> {
-                val runningTaskInfo = ActivityManager.RunningTaskInfo().apply {
-                    topActivity = ComponentName(CAMERA_APP_PACKAGE_NAME, "cameraActivity")
-                }
-                whenever(activityManager.getRunningTasks(anyInt())).thenReturn(
-                    listOf(
-                        runningTaskInfo
-                    )
-                )
+                val runningTaskInfo =
+                    ActivityManager.RunningTaskInfo().apply {
+                        topActivity = ComponentName(CAMERA_APP_PACKAGE_NAME, "cameraActivity")
+                    }
+                whenever(activityManager.getRunningTasks(anyInt()))
+                    .thenReturn(listOf(runningTaskInfo))
             }
             isTaskListEmpty -> {
                 whenever(activityManager.getRunningTasks(anyInt())).thenReturn(emptyList())
@@ -289,28 +283,28 @@
     ) {
         val intentCaptor = KotlinArgumentCaptor(Intent::class.java)
         if (isSecure && !moreThanOneCameraAppInstalled) {
-            verify(activityTaskManager).startActivityAsUser(
-                any(),
-                any(),
-                any(),
-                intentCaptor.capture(),
-                any(),
-                any(),
-                any(),
-                anyInt(),
-                anyInt(),
-                any(),
-                any(),
-                anyInt()
-            )
+            verify(activityTaskManager)
+                .startActivityAsUser(
+                    isNull(),
+                    isNull(),
+                    isNull(),
+                    intentCaptor.capture(),
+                    isNull(),
+                    isNull(),
+                    isNull(),
+                    anyInt(),
+                    anyInt(),
+                    isNull(),
+                    any(),
+                    anyInt()
+                )
         } else {
             verify(activityStarter).startActivity(intentCaptor.capture(), eq(false))
         }
         val intent = intentCaptor.value
 
         assertThat(CameraIntents.isSecureCameraIntent(intent)).isEqualTo(isSecure)
-        assertThat(intent.getIntExtra(CameraIntents.EXTRA_LAUNCH_SOURCE, -1))
-            .isEqualTo(source)
+        assertThat(intent.getIntExtra(CameraIntents.EXTRA_LAUNCH_SOURCE, -1)).isEqualTo(source)
     }
 
     companion object {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalMetricsStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalMetricsStartableTest.kt
new file mode 100644
index 0000000..370adee
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalMetricsStartableTest.kt
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal
+
+import android.app.StatsManager
+import android.app.StatsManager.StatsPullAtomCallback
+import android.content.pm.UserInfo
+import android.platform.test.annotations.EnableFlags
+import android.util.StatsEvent
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.Flags
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.communal.data.repository.fakeCommunalWidgetRepository
+import com.android.systemui.communal.domain.interactor.communalInteractor
+import com.android.systemui.communal.domain.interactor.communalSettingsInteractor
+import com.android.systemui.communal.shared.log.CommunalMetricsLogger
+import com.android.systemui.concurrency.fakeExecutor
+import com.android.systemui.flags.Flags.COMMUNAL_SERVICE_ENABLED
+import com.android.systemui.flags.fakeFeatureFlagsClassic
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.settings.fakeUserTracker
+import com.android.systemui.shared.system.SysUiStatsLog
+import com.android.systemui.testKosmos
+import com.android.systemui.user.data.repository.fakeUserRepository
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.kotlin.any
+import org.mockito.kotlin.anyOrNull
+import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.never
+import org.mockito.kotlin.verify
+
+@SmallTest
+@EnableFlags(Flags.FLAG_COMMUNAL_HUB)
+@RunWith(AndroidJUnit4::class)
+class CommunalMetricsStartableTest : SysuiTestCase() {
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
+
+    private val metricsLogger = mock<CommunalMetricsLogger>()
+    private val statsManager = mock<StatsManager>()
+
+    private val callbackCaptor = argumentCaptor<StatsPullAtomCallback>()
+
+    private val userTracker = kosmos.fakeUserTracker
+    private val userRepository = kosmos.fakeUserRepository
+    private val widgetsRepository = kosmos.fakeCommunalWidgetRepository
+
+    private lateinit var underTest: CommunalMetricsStartable
+
+    @Before
+    fun setUp() {
+        kosmos.fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, true)
+
+        // Set up an existing user, which is required for widgets to show
+        val userInfos = listOf(UserInfo(0, "main", UserInfo.FLAG_MAIN))
+        userRepository.setUserInfos(userInfos)
+        userTracker.set(
+            userInfos = userInfos,
+            selectedUserIndex = 0,
+        )
+
+        underTest =
+            CommunalMetricsStartable(
+                kosmos.fakeExecutor,
+                kosmos.communalSettingsInteractor,
+                kosmos.communalInteractor,
+                statsManager,
+                metricsLogger,
+            )
+    }
+
+    @Test
+    fun start_communalFlagDisabled_doNotSetPullAtomCallback() {
+        kosmos.fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, false)
+
+        underTest.start()
+
+        verify(statsManager, never()).setPullAtomCallback(anyInt(), anyOrNull(), any(), any())
+    }
+
+    @Test
+    fun onPullAtom_atomTagDoesNotMatch_pullSkip() {
+        underTest.start()
+
+        verify(statsManager)
+            .setPullAtomCallback(anyInt(), anyOrNull(), any(), callbackCaptor.capture())
+        val callback = callbackCaptor.firstValue
+
+        // Atom tag doesn't match COMMUNAL_HUB_SNAPSHOT
+        val result =
+            callback.onPullAtom(SysUiStatsLog.COMMUNAL_HUB_WIDGET_EVENT_REPORTED, mutableListOf())
+
+        assertThat(result).isEqualTo(StatsManager.PULL_SKIP)
+    }
+
+    @Test
+    fun onPullAtom_atomTagMatches_pullSuccess() =
+        testScope.runTest {
+            underTest.start()
+
+            verify(statsManager)
+                .setPullAtomCallback(anyInt(), anyOrNull(), any(), callbackCaptor.capture())
+            val callback = callbackCaptor.firstValue
+
+            // Populate some widgets
+            widgetsRepository.addWidget(appWidgetId = 1, componentName = "pkg_1/cls_1")
+            widgetsRepository.addWidget(appWidgetId = 2, componentName = "pkg_2/cls_2")
+
+            val statsEvents = mutableListOf<StatsEvent>()
+            val result = callback.onPullAtom(SysUiStatsLog.COMMUNAL_HUB_SNAPSHOT, statsEvents)
+
+            verify(metricsLogger)
+                .logWidgetsSnapshot(statsEvents, listOf("pkg_1/cls_1", "pkg_2/cls_2"))
+
+            assertThat(result).isEqualTo(StatsManager.PULL_SUCCESS)
+        }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt
index 4ad020f..9ccf99b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt
@@ -16,17 +16,20 @@
 
 package com.android.systemui.communal
 
+import android.platform.test.annotations.DisableFlags
 import android.platform.test.annotations.EnableFlags
 import android.provider.Settings
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.Flags.FLAG_COMMUNAL_HUB
+import com.android.systemui.Flags.FLAG_COMMUNAL_SCENE_KTF_REFACTOR
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.communal.domain.interactor.communalInteractor
 import com.android.systemui.communal.domain.interactor.communalSceneInteractor
 import com.android.systemui.communal.domain.interactor.communalSettingsInteractor
 import com.android.systemui.communal.domain.interactor.setCommunalAvailable
 import com.android.systemui.communal.shared.model.CommunalScenes
+import com.android.systemui.communal.shared.model.EditModeState
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.dock.dockManager
 import com.android.systemui.dock.fakeDockManager
@@ -103,6 +106,28 @@
     }
 
     @Test
+    @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
+    fun keyguardGoesAway_whenLaunchingEditMode_doNotForceBlankScene() =
+        with(kosmos) {
+            testScope.runTest {
+                val scene by collectLastValue(communalSceneInteractor.currentScene)
+
+                communalSceneInteractor.changeScene(CommunalScenes.Communal)
+                assertThat(scene).isEqualTo(CommunalScenes.Communal)
+
+                communalSceneInteractor.setEditModeState(EditModeState.STARTING)
+                fakeKeyguardTransitionRepository.sendTransitionSteps(
+                    from = KeyguardState.PRIMARY_BOUNCER,
+                    to = KeyguardState.GONE,
+                    testScope = this
+                )
+
+                assertThat(scene).isEqualTo(CommunalScenes.Communal)
+            }
+        }
+
+    @Test
+    @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
     fun keyguardGoesAway_whenLaunchingWidget_doNotForceBlankScene() =
         with(kosmos) {
             testScope.runTest {
@@ -123,6 +148,7 @@
         }
 
     @Test
+    @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
     fun keyguardGoesAway_whenNotLaunchingWidget_forceBlankScene() =
         with(kosmos) {
             testScope.runTest {
@@ -143,6 +169,7 @@
         }
 
     @Test
+    @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
     fun keyguardGoesAway_whenInEditMode_doesNotChangeScene() =
         with(kosmos) {
             testScope.runTest {
@@ -180,6 +207,7 @@
         }
 
     @Test
+    @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
     fun occluded_forceBlankScene() =
         with(kosmos) {
             testScope.runTest {
@@ -199,6 +227,7 @@
         }
 
     @Test
+    @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
     fun occluded_doesNotForceBlankSceneIfLaunchingActivityOverLockscreen() =
         with(kosmos) {
             testScope.runTest {
@@ -218,6 +247,7 @@
         }
 
     @Test
+    @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
     fun deviceDocked_doesNotForceCommunalIfTransitioningFromCommunal() =
         with(kosmos) {
             testScope.runTest {
@@ -235,6 +265,7 @@
         }
 
     @Test
+    @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
     fun deviceAsleep_forceBlankSceneAfterTimeout() =
         with(kosmos) {
             testScope.runTest {
@@ -256,6 +287,7 @@
         }
 
     @Test
+    @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
     fun deviceAsleep_wakesUpBeforeTimeout_noChangeInScene() =
         with(kosmos) {
             testScope.runTest {
@@ -483,6 +515,7 @@
         }
 
     @Test
+    @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
     fun transitionFromDozingToGlanceableHub_forcesCommunal() =
         with(kosmos) {
             testScope.runTest {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSmartspaceRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSmartspaceRepositoryImplTest.kt
index c1816ed..d251585 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSmartspaceRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSmartspaceRepositoryImplTest.kt
@@ -22,6 +22,7 @@
 import android.platform.test.annotations.EnableFlags
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
+import com.android.systemui.Flags.FLAG_COMMUNAL_TIMER_FLICKER_FIX
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.communal.smartspace.CommunalSmartspaceController
 import com.android.systemui.concurrency.fakeExecutor
@@ -97,6 +98,7 @@
         }
 
     @EnableFlags(FLAG_REMOTE_VIEWS)
+    @DisableFlags(FLAG_COMMUNAL_TIMER_FLICKER_FIX)
     @Test
     fun communalTimers_onlyShowTimersWithRemoteViews() =
         testScope.runTest {
@@ -138,6 +140,48 @@
             assertThat(communalTimers?.first()?.smartspaceTargetId).isEqualTo("timer-1-started")
         }
 
+    @EnableFlags(FLAG_REMOTE_VIEWS, FLAG_COMMUNAL_TIMER_FLICKER_FIX)
+    @Test
+    fun communalTimers_onlyShowTimersWithRemoteViews_timerFlickerFix() =
+        testScope.runTest {
+            underTest.startListening()
+
+            val communalTimers by collectLastValue(underTest.timers)
+            runCurrent()
+            fakeExecutor.runAllReady()
+
+            with(captureSmartspaceTargetListener()) {
+                onSmartspaceTargetsUpdated(
+                    listOf(
+                        // Invalid. Not a timer
+                        mock<SmartspaceTarget> {
+                            on { smartspaceTargetId }.doReturn("weather")
+                            on { featureType }.doReturn(SmartspaceTarget.FEATURE_WEATHER)
+                        },
+                        // Invalid. RemoteViews absent
+                        mock<SmartspaceTarget> {
+                            on { smartspaceTargetId }.doReturn("timer-0-started")
+                            on { featureType }.doReturn(SmartspaceTarget.FEATURE_TIMER)
+                            on { remoteViews }.doReturn(null)
+                            on { creationTimeMillis }.doReturn(1000)
+                        },
+                        // Valid
+                        mock<SmartspaceTarget> {
+                            on { smartspaceTargetId }.doReturn("timer-1-started")
+                            on { featureType }.doReturn(SmartspaceTarget.FEATURE_TIMER)
+                            on { remoteViews }.doReturn(mock())
+                            on { creationTimeMillis }.doReturn(2000)
+                        },
+                    )
+                )
+            }
+            runCurrent()
+
+            // Verify that only the valid target is listed
+            assertThat(communalTimers).hasSize(1)
+            assertThat(communalTimers?.first()?.smartspaceTargetId).isEqualTo("timer-1")
+        }
+
     @EnableFlags(FLAG_REMOTE_VIEWS)
     @Test
     fun communalTimers_cacheCreationTime() =
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt
index c707ebf..ca81838 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt
@@ -696,7 +696,7 @@
                     CommunalWidgetContentModel.Pending(
                         appWidgetId = 2,
                         priority = 2,
-                        packageName = "pk_2",
+                        componentName = ComponentName("pk_2", "cls_2"),
                         icon = fakeIcon,
                         user = mainUser,
                     ),
@@ -731,7 +731,7 @@
                     CommunalWidgetContentModel.Pending(
                         appWidgetId = 1,
                         priority = 1,
-                        packageName = "pk_1",
+                        componentName = ComponentName("pk_1", "cls_1"),
                         icon = fakeIcon,
                         user = mainUser,
                     ),
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
index 9539c04..0242c2d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
@@ -19,10 +19,8 @@
 
 import android.app.admin.DevicePolicyManager
 import android.app.admin.devicePolicyManager
-import android.appwidget.AppWidgetProviderInfo
 import android.content.Intent
 import android.content.pm.UserInfo
-import android.graphics.Bitmap
 import android.os.UserHandle
 import android.os.UserManager
 import android.os.userManager
@@ -52,7 +50,6 @@
 import com.android.systemui.communal.domain.model.CommunalTransitionProgressModel
 import com.android.systemui.communal.shared.model.CommunalContentSize
 import com.android.systemui.communal.shared.model.CommunalScenes
-import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
 import com.android.systemui.communal.shared.model.EditModeState
 import com.android.systemui.communal.widgets.EditWidgetsActivityStarter
 import com.android.systemui.coroutines.collectLastValue
@@ -251,18 +248,16 @@
             runCurrent()
 
             // Widgets available.
-            val widget1 = createWidgetForUser(1, USER_INFO_WORK.id)
-            val widget2 = createWidgetForUser(2, MAIN_USER_INFO.id)
-            val widget3 = createWidgetForUser(3, MAIN_USER_INFO.id)
-            val widgets = listOf(widget1, widget2, widget3)
-            widgetRepository.setCommunalWidgets(widgets)
+            widgetRepository.addWidget(appWidgetId = 1, userId = USER_INFO_WORK.id)
+            widgetRepository.addWidget(appWidgetId = 2, userId = MAIN_USER_INFO.id)
+            widgetRepository.addWidget(appWidgetId = 3, userId = MAIN_USER_INFO.id)
 
             val widgetContent by collectLastValue(underTest.widgetContent)
 
-            assertThat(widgetContent!!).isNotEmpty()
-            widgetContent!!.forEachIndexed { index, model ->
-                assertThat(model.appWidgetId).isEqualTo(widgets[index].appWidgetId)
-            }
+            assertThat(checkNotNull(widgetContent)).isNotEmpty()
+            assertThat(widgetContent!![0].appWidgetId).isEqualTo(1)
+            assertThat(widgetContent!![1].appWidgetId).isEqualTo(2)
+            assertThat(widgetContent!![2].appWidgetId).isEqualTo(3)
         }
 
     @Test
@@ -839,11 +834,9 @@
 
             val widgetContent by collectLastValue(underTest.widgetContent)
             // Given three widgets, and one of them is associated with pre-existing work profile.
-            val widget1 = createWidgetForUser(1, USER_INFO_WORK.id)
-            val widget2 = createWidgetForUser(2, MAIN_USER_INFO.id)
-            val widget3 = createWidgetForUser(3, MAIN_USER_INFO.id)
-            val widgets = listOf(widget1, widget2, widget3)
-            widgetRepository.setCommunalWidgets(widgets)
+            widgetRepository.addWidget(appWidgetId = 1, userId = USER_INFO_WORK.id)
+            widgetRepository.addWidget(appWidgetId = 2, userId = MAIN_USER_INFO.id)
+            widgetRepository.addWidget(appWidgetId = 3, userId = MAIN_USER_INFO.id)
 
             // One widget is filtered out and the remaining two link to main user id.
             assertThat(checkNotNull(widgetContent).size).isEqualTo(2)
@@ -882,11 +875,9 @@
             whenever(userManager.isManagedProfile(eq(USER_INFO_WORK.id))).thenReturn(true)
 
             val widgetContent by collectLastValue(underTest.widgetContent)
-            val widget1 = createWidgetForUser(1, USER_INFO_WORK.id)
-            val widget2 = createWidgetForUser(2, MAIN_USER_INFO.id)
-            val widget3 = createWidgetForUser(3, MAIN_USER_INFO.id)
-            val widgets = listOf(widget1, widget2, widget3)
-            widgetRepository.setCommunalWidgets(widgets)
+            widgetRepository.addWidget(appWidgetId = 1, userId = USER_INFO_WORK.id)
+            widgetRepository.addWidget(appWidgetId = 2, userId = MAIN_USER_INFO.id)
+            widgetRepository.addWidget(appWidgetId = 3, userId = MAIN_USER_INFO.id)
 
             // The work profile widget is in quiet mode, while other widgets are not.
             assertThat(widgetContent).hasSize(3)
@@ -927,11 +918,9 @@
 
             val widgetContent by collectLastValue(underTest.widgetContent)
             // One available work widget, one pending work widget, and one regular available widget.
-            val widget1 = createWidgetForUser(1, USER_INFO_WORK.id)
-            val widget2 = createPendingWidgetForUser(2, userId = USER_INFO_WORK.id)
-            val widget3 = createWidgetForUser(3, MAIN_USER_INFO.id)
-            val widgets = listOf(widget1, widget2, widget3)
-            widgetRepository.setCommunalWidgets(widgets)
+            widgetRepository.addWidget(appWidgetId = 1, userId = USER_INFO_WORK.id)
+            widgetRepository.addPendingWidget(appWidgetId = 2, userId = USER_INFO_WORK.id)
+            widgetRepository.addWidget(appWidgetId = 3, userId = MAIN_USER_INFO.id)
 
             setKeyguardFeaturesDisabled(
                 USER_INFO_WORK,
@@ -962,11 +951,9 @@
 
             val widgetContent by collectLastValue(underTest.widgetContent)
             // Given three widgets, and one of them is associated with work profile.
-            val widget1 = createWidgetForUser(1, USER_INFO_WORK.id)
-            val widget2 = createPendingWidgetForUser(2, userId = USER_INFO_WORK.id)
-            val widget3 = createWidgetForUser(3, MAIN_USER_INFO.id)
-            val widgets = listOf(widget1, widget2, widget3)
-            widgetRepository.setCommunalWidgets(widgets)
+            widgetRepository.addWidget(appWidgetId = 1, userId = USER_INFO_WORK.id)
+            widgetRepository.addPendingWidget(appWidgetId = 2, userId = USER_INFO_WORK.id)
+            widgetRepository.addWidget(appWidgetId = 3, userId = MAIN_USER_INFO.id)
 
             setKeyguardFeaturesDisabled(
                 USER_INFO_WORK,
@@ -1088,47 +1075,6 @@
         )
     }
 
-    private fun createWidgetForUser(
-        appWidgetId: Int,
-        userId: Int
-    ): CommunalWidgetContentModel.Available =
-        mock<CommunalWidgetContentModel.Available> {
-            whenever(this.appWidgetId).thenReturn(appWidgetId)
-            val providerInfo =
-                mock<AppWidgetProviderInfo>().apply {
-                    widgetCategory = AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD
-                }
-            whenever(providerInfo.profile).thenReturn(UserHandle(userId))
-            whenever(this.providerInfo).thenReturn(providerInfo)
-        }
-
-    private fun createPendingWidgetForUser(
-        appWidgetId: Int,
-        priority: Int = 0,
-        packageName: String = "",
-        icon: Bitmap? = null,
-        userId: Int = 0,
-    ): CommunalWidgetContentModel.Pending {
-        return CommunalWidgetContentModel.Pending(
-            appWidgetId = appWidgetId,
-            priority = priority,
-            packageName = packageName,
-            icon = icon,
-            user = UserHandle(userId),
-        )
-    }
-
-    private fun createWidgetWithCategory(
-        appWidgetId: Int,
-        category: Int
-    ): CommunalWidgetContentModel =
-        mock<CommunalWidgetContentModel.Available> {
-            whenever(this.appWidgetId).thenReturn(appWidgetId)
-            val providerInfo = mock<AppWidgetProviderInfo>().apply { widgetCategory = category }
-            whenever(providerInfo.profile).thenReturn(UserHandle(MAIN_USER_INFO.id))
-            whenever(this.providerInfo).thenReturn(providerInfo)
-        }
-
     private companion object {
         val MAIN_USER_INFO = UserInfo(0, "primary", UserInfo.FLAG_MAIN)
         val USER_INFO_WORK =
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalSceneTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalSceneTransitionInteractorTest.kt
index f7f70c1..ad73853 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalSceneTransitionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalSceneTransitionInteractorTest.kt
@@ -34,8 +34,11 @@
 import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
 import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository
 import com.android.systemui.keyguard.data.repository.realKeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.DozeStateModel
+import com.android.systemui.keyguard.shared.model.DozeTransitionModel
 import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
 import com.android.systemui.keyguard.shared.model.KeyguardState.GLANCEABLE_HUB
+import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
 import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
 import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED
 import com.android.systemui.keyguard.shared.model.TransitionInfo
@@ -46,6 +49,8 @@
 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.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -53,6 +58,7 @@
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.launch
+import kotlinx.coroutines.test.advanceTimeBy
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
@@ -211,8 +217,15 @@
     @Test
     fun transition_from_hub_end_in_dream() =
         testScope.runTest {
+            // Device is dreaming and not dozing.
+            kosmos.powerInteractor.setAwakeForTest()
+            kosmos.fakeKeyguardRepository.setDozeTransitionModel(
+                DozeTransitionModel(from = DozeStateModel.DOZE, to = DozeStateModel.FINISH)
+            )
+            kosmos.fakeKeyguardRepository.setKeyguardOccluded(true)
             kosmos.fakeKeyguardRepository.setDreaming(true)
-            runCurrent()
+            kosmos.fakeKeyguardRepository.setDreamingWithOverlay(true)
+            advanceTimeBy(100L)
 
             sceneTransitions.value = hubToBlank
 
@@ -254,6 +267,100 @@
                 )
         }
 
+    /** Transition from hub to occluded. */
+    @Test
+    fun transition_from_hub_end_in_occluded() =
+        testScope.runTest {
+            kosmos.fakeKeyguardRepository.setKeyguardOccluded(true)
+            runCurrent()
+
+            sceneTransitions.value = hubToBlank
+
+            val currentStep by collectLastValue(keyguardTransitionRepository.transitions)
+
+            assertThat(currentStep)
+                .isEqualTo(
+                    TransitionStep(
+                        from = GLANCEABLE_HUB,
+                        to = OCCLUDED,
+                        transitionState = STARTED,
+                        value = 0f,
+                        ownerName = ownerName,
+                    )
+                )
+
+            progress.emit(0.4f)
+            assertThat(currentStep)
+                .isEqualTo(
+                    TransitionStep(
+                        from = GLANCEABLE_HUB,
+                        to = OCCLUDED,
+                        transitionState = RUNNING,
+                        value = 0.4f,
+                        ownerName = ownerName,
+                    )
+                )
+
+            sceneTransitions.value = Idle(CommunalScenes.Blank)
+            assertThat(currentStep)
+                .isEqualTo(
+                    TransitionStep(
+                        from = GLANCEABLE_HUB,
+                        to = OCCLUDED,
+                        transitionState = FINISHED,
+                        value = 1f,
+                        ownerName = ownerName,
+                    )
+                )
+        }
+
+    /** Transition from hub to gone. */
+    @Test
+    fun transition_from_hub_end_in_gone() =
+        testScope.runTest {
+            kosmos.fakeKeyguardRepository.setKeyguardGoingAway(true)
+            runCurrent()
+
+            sceneTransitions.value = hubToBlank
+
+            val currentStep by collectLastValue(keyguardTransitionRepository.transitions)
+
+            assertThat(currentStep)
+                .isEqualTo(
+                    TransitionStep(
+                        from = GLANCEABLE_HUB,
+                        to = GONE,
+                        transitionState = STARTED,
+                        value = 0f,
+                        ownerName = ownerName,
+                    )
+                )
+
+            progress.emit(0.4f)
+            assertThat(currentStep)
+                .isEqualTo(
+                    TransitionStep(
+                        from = GLANCEABLE_HUB,
+                        to = GONE,
+                        transitionState = RUNNING,
+                        value = 0.4f,
+                        ownerName = ownerName,
+                    )
+                )
+
+            sceneTransitions.value = Idle(CommunalScenes.Blank)
+            assertThat(currentStep)
+                .isEqualTo(
+                    TransitionStep(
+                        from = GLANCEABLE_HUB,
+                        to = GONE,
+                        transitionState = FINISHED,
+                        value = 1f,
+                        ownerName = ownerName,
+                    )
+                )
+        }
+
     /** Transition from blank to hub, then settle back in blank. */
     @Test
     fun transition_from_blank_end_in_blank() =
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/log/CommunalLoggerStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/log/CommunalLoggerStartableTest.kt
index def8698..28ad269 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/log/CommunalLoggerStartableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/log/CommunalLoggerStartableTest.kt
@@ -22,10 +22,13 @@
 import com.android.compose.animation.scene.SceneKey
 import com.android.internal.logging.UiEventLogger
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.communal.domain.interactor.CommunalInteractor
-import com.android.systemui.communal.domain.interactor.communalInteractor
+import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
+import com.android.systemui.communal.domain.interactor.communalSceneInteractor
 import com.android.systemui.communal.shared.log.CommunalUiEvent
 import com.android.systemui.communal.shared.model.CommunalScenes
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
+import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.testKosmos
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -53,18 +56,21 @@
     private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
 
-    private lateinit var communalInteractor: CommunalInteractor
+    private lateinit var communalSceneInteractor: CommunalSceneInteractor
+    private lateinit var keyguardRepository: FakeKeyguardRepository
     private lateinit var underTest: CommunalLoggerStartable
 
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
-        communalInteractor = kosmos.communalInteractor
+        communalSceneInteractor = kosmos.communalSceneInteractor
+        keyguardRepository = kosmos.fakeKeyguardRepository
 
         underTest =
             CommunalLoggerStartable(
                 testScope.backgroundScope,
-                communalInteractor,
+                communalSceneInteractor,
+                kosmos.keyguardInteractor,
                 uiEventLogger,
             )
         underTest.start()
@@ -73,10 +79,13 @@
     @Test
     fun transitionStateLogging_enterCommunalHub() =
         testScope.runTest {
+            // Not dreaming
+            keyguardRepository.setDreamingWithOverlay(false)
+
             // Transition state is default (non-communal)
             val transitionState =
                 MutableStateFlow<ObservableTransitionState>(idle(CommunalScenes.Default))
-            communalInteractor.setTransitionState(transitionState)
+            communalSceneInteractor.setTransitionState(transitionState)
             runCurrent()
 
             // Verify nothing is logged from the default state
@@ -99,12 +108,15 @@
         }
 
     @Test
-    fun transitionStateLogging_enterCommunalHub_canceled() =
+    fun transitionStateLogging_cancelEnteringCommunalHub() =
         testScope.runTest {
+            // Not dreaming
+            keyguardRepository.setDreamingWithOverlay(false)
+
             // Transition state is default (non-communal)
             val transitionState =
                 MutableStateFlow<ObservableTransitionState>(idle(CommunalScenes.Default))
-            communalInteractor.setTransitionState(transitionState)
+            communalSceneInteractor.setTransitionState(transitionState)
             runCurrent()
 
             // Verify nothing is logged from the default state
@@ -132,10 +144,13 @@
     @Test
     fun transitionStateLogging_exitCommunalHub() =
         testScope.runTest {
+            // Not dreaming
+            keyguardRepository.setDreamingWithOverlay(false)
+
             // Transition state is communal
             val transitionState =
                 MutableStateFlow<ObservableTransitionState>(idle(CommunalScenes.Communal))
-            communalInteractor.setTransitionState(transitionState)
+            communalSceneInteractor.setTransitionState(transitionState)
             runCurrent()
 
             // Verify SHOWN is logged when it's the default state
@@ -158,12 +173,15 @@
         }
 
     @Test
-    fun transitionStateLogging_exitCommunalHub_canceled() =
+    fun transitionStateLogging_cancelExitingCommunalHub() =
         testScope.runTest {
+            // Not dreaming
+            keyguardRepository.setDreamingWithOverlay(false)
+
             // Transition state is communal
             val transitionState =
                 MutableStateFlow<ObservableTransitionState>(idle(CommunalScenes.Communal))
-            communalInteractor.setTransitionState(transitionState)
+            communalSceneInteractor.setTransitionState(transitionState)
             runCurrent()
 
             // Clear the initial SHOWN event from the logger
@@ -188,6 +206,136 @@
             verify(uiEventLogger, never()).log(CommunalUiEvent.COMMUNAL_HUB_GONE)
         }
 
+    @Test
+    fun transitionStateLogging_dreaming_enterCommunalHub() =
+        testScope.runTest {
+            // Dreaming
+            keyguardRepository.setDreamingWithOverlay(true)
+
+            // Transition state is default (non-communal)
+            val transitionState =
+                MutableStateFlow<ObservableTransitionState>(idle(CommunalScenes.Default))
+            communalSceneInteractor.setTransitionState(transitionState)
+            runCurrent()
+
+            // Verify nothing is logged from the default state
+            verify(uiEventLogger, never()).log(any())
+
+            // Start transition to communal
+            transitionState.value = transition(to = CommunalScenes.Communal)
+            runCurrent()
+
+            // Verify UiEvent logged
+            verify(uiEventLogger).log(CommunalUiEvent.DREAM_TO_COMMUNAL_HUB_SWIPE_START)
+
+            // Finish transition to communal
+            transitionState.value = idle(CommunalScenes.Communal)
+            runCurrent()
+
+            // Verify UiEvent logged
+            verify(uiEventLogger).log(CommunalUiEvent.DREAM_TO_COMMUNAL_HUB_SWIPE_FINISH)
+            verify(uiEventLogger).log(CommunalUiEvent.COMMUNAL_HUB_SHOWN)
+        }
+
+    @Test
+    fun transitionStateLogging_dreaming_cancelEnteringCommunalHub() =
+        testScope.runTest {
+            // Dreaming
+            keyguardRepository.setDreamingWithOverlay(true)
+
+            // Transition state is default (non-communal)
+            val transitionState =
+                MutableStateFlow<ObservableTransitionState>(idle(CommunalScenes.Default))
+            communalSceneInteractor.setTransitionState(transitionState)
+            runCurrent()
+
+            // Verify nothing is logged from the default state
+            verify(uiEventLogger, never()).log(any())
+
+            // Start transition to communal
+            transitionState.value = transition(to = CommunalScenes.Communal)
+            runCurrent()
+
+            // Verify UiEvent logged
+            verify(uiEventLogger).log(CommunalUiEvent.DREAM_TO_COMMUNAL_HUB_SWIPE_START)
+
+            // Cancel the transition
+            transitionState.value = idle(CommunalScenes.Default)
+            runCurrent()
+
+            // Verify UiEvent logged
+            verify(uiEventLogger).log(CommunalUiEvent.DREAM_TO_COMMUNAL_HUB_SWIPE_CANCEL)
+
+            // Verify neither SHOWN nor GONE is logged
+            verify(uiEventLogger, never()).log(CommunalUiEvent.COMMUNAL_HUB_SHOWN)
+            verify(uiEventLogger, never()).log(CommunalUiEvent.COMMUNAL_HUB_GONE)
+        }
+
+    @Test
+    fun transitionStateLogging_dreaming_exitCommunalHub() =
+        testScope.runTest {
+            // Dreaming
+            keyguardRepository.setDreamingWithOverlay(true)
+
+            // Transition state is communal
+            val transitionState =
+                MutableStateFlow<ObservableTransitionState>(idle(CommunalScenes.Communal))
+            communalSceneInteractor.setTransitionState(transitionState)
+            runCurrent()
+
+            // Verify SHOWN is logged when it's the default state
+            verify(uiEventLogger).log(CommunalUiEvent.COMMUNAL_HUB_SHOWN)
+
+            // Start transition from communal
+            transitionState.value = transition(from = CommunalScenes.Communal)
+            runCurrent()
+
+            // Verify UiEvent logged
+            verify(uiEventLogger).log(CommunalUiEvent.COMMUNAL_HUB_TO_DREAM_SWIPE_START)
+
+            // Finish transition to communal
+            transitionState.value = idle(CommunalScenes.Default)
+            runCurrent()
+
+            // Verify UiEvent logged
+            verify(uiEventLogger).log(CommunalUiEvent.COMMUNAL_HUB_TO_DREAM_SWIPE_FINISH)
+            verify(uiEventLogger).log(CommunalUiEvent.COMMUNAL_HUB_GONE)
+        }
+
+    @Test
+    fun transitionStateLogging_dreaming_cancelExitingCommunalHub() =
+        testScope.runTest {
+            // Dreaming
+            keyguardRepository.setDreamingWithOverlay(true)
+
+            // Transition state is communal
+            val transitionState =
+                MutableStateFlow<ObservableTransitionState>(idle(CommunalScenes.Communal))
+            communalSceneInteractor.setTransitionState(transitionState)
+            runCurrent()
+
+            // Clear the initial SHOWN event from the logger
+            clearInvocations(uiEventLogger)
+
+            // Start transition from communal
+            transitionState.value = transition(from = CommunalScenes.Communal)
+            runCurrent()
+
+            // Verify UiEvent logged
+            verify(uiEventLogger).log(CommunalUiEvent.COMMUNAL_HUB_TO_DREAM_SWIPE_START)
+
+            // Cancel the transition
+            transitionState.value = idle(CommunalScenes.Communal)
+            runCurrent()
+
+            // Verify UiEvent logged
+            verify(uiEventLogger).log(CommunalUiEvent.COMMUNAL_HUB_TO_DREAM_SWIPE_CANCEL)
+
+            // Verify neither SHOWN nor GONE is logged
+            verify(uiEventLogger, never()).log(CommunalUiEvent.COMMUNAL_HUB_SHOWN)
+            verify(uiEventLogger, never()).log(CommunalUiEvent.COMMUNAL_HUB_GONE)
+        }
+
     private fun transition(
         from: SceneKey = CommunalScenes.Default,
         to: SceneKey = CommunalScenes.Default,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/log/CommunalMetricsLoggerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/log/CommunalMetricsLoggerTest.kt
new file mode 100644
index 0000000..82918a5
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/log/CommunalMetricsLoggerTest.kt
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.log
+
+import android.util.StatsEvent
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.communal.shared.log.CommunalMetricsLogger
+import com.android.systemui.shared.system.SysUiStatsLog
+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.mock
+import org.mockito.kotlin.never
+import org.mockito.kotlin.verify
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class CommunalMetricsLoggerTest : SysuiTestCase() {
+    private val statsLogProxy = mock<CommunalMetricsLogger.StatsLogProxy>()
+
+    private val loggablePrefixes = listOf("com.blue.", "com.red.")
+    private lateinit var underTest: CommunalMetricsLogger
+
+    @Before
+    fun setUp() {
+        underTest = CommunalMetricsLogger(loggablePrefixes, statsLogProxy)
+    }
+
+    @Test
+    fun logAddWidget_componentNotLoggable_doNotLog() {
+        underTest.logAddWidget(
+            componentName = "com.green.package/my_test_widget",
+            rank = 1,
+        )
+        verify(statsLogProxy, never())
+            .writeCommunalHubWidgetEventReported(anyInt(), any(), anyInt())
+    }
+
+    @Test
+    fun logAddWidget_componentLoggable_logAddEvent() {
+        underTest.logAddWidget(
+            componentName = "com.blue.package/my_test_widget",
+            rank = 1,
+        )
+        verify(statsLogProxy)
+            .writeCommunalHubWidgetEventReported(
+                SysUiStatsLog.COMMUNAL_HUB_WIDGET_EVENT_REPORTED__ACTION__ADD,
+                "com.blue.package/my_test_widget",
+                1,
+            )
+    }
+
+    @Test
+    fun logRemoveWidget_componentNotLoggable_doNotLog() {
+        underTest.logRemoveWidget(
+            componentName = "com.yellow.package/my_test_widget",
+            rank = 2,
+        )
+        verify(statsLogProxy, never())
+            .writeCommunalHubWidgetEventReported(anyInt(), any(), anyInt())
+    }
+
+    @Test
+    fun logRemoveWidget_componentLoggable_logRemoveEvent() {
+        underTest.logRemoveWidget(
+            componentName = "com.red.package/my_test_widget",
+            rank = 2,
+        )
+        verify(statsLogProxy)
+            .writeCommunalHubWidgetEventReported(
+                SysUiStatsLog.COMMUNAL_HUB_WIDGET_EVENT_REPORTED__ACTION__REMOVE,
+                "com.red.package/my_test_widget",
+                2,
+            )
+    }
+
+    @Test
+    fun logTapWidget_componentNotLoggable_doNotLog() {
+        underTest.logTapWidget(
+            componentName = "com.yellow.package/my_test_widget",
+            rank = 2,
+        )
+        verify(statsLogProxy, never())
+            .writeCommunalHubWidgetEventReported(anyInt(), any(), anyInt())
+    }
+
+    @Test
+    fun logTapWidget_componentLoggable_logRemoveEvent() {
+        underTest.logTapWidget(
+            componentName = "com.red.package/my_test_widget",
+            rank = 2,
+        )
+        verify(statsLogProxy)
+            .writeCommunalHubWidgetEventReported(
+                SysUiStatsLog.COMMUNAL_HUB_WIDGET_EVENT_REPORTED__ACTION__TAP,
+                "com.red.package/my_test_widget",
+                2,
+            )
+    }
+
+    @Test
+    fun logWidgetsSnapshot_logOnlyLoggableComponents() {
+        val statsEvents = mutableListOf<StatsEvent>()
+        underTest.logWidgetsSnapshot(
+            statsEvents,
+            listOf(
+                "com.blue.package/my_test_widget_1",
+                "com.green.package/my_test_widget_2",
+                "com.red.package/my_test_widget_3",
+                "com.yellow.package/my_test_widget_4",
+            ),
+        )
+        verify(statsLogProxy)
+            .buildCommunalHubSnapshotStatsEvent(
+                componentNames =
+                    arrayOf(
+                        "com.blue.package/my_test_widget_1",
+                        "com.red.package/my_test_widget_3",
+                    ),
+                widgetCount = 4,
+            )
+        assertThat(statsEvents).hasSize(1)
+    }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/smartspace/SmartspaceInteractionHandlerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/smartspace/SmartspaceInteractionHandlerTest.kt
index d51d356..15c57d8 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/smartspace/SmartspaceInteractionHandlerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/smartspace/SmartspaceInteractionHandlerTest.kt
@@ -31,6 +31,7 @@
 import com.android.systemui.communal.widgets.SmartspaceAppWidgetHostView
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.kosmos.testScope
+import com.android.systemui.log.logcatLogBuffer
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.testKosmos
 import kotlinx.coroutines.test.runTest
@@ -55,7 +56,7 @@
     private val testIntent =
         PendingIntent.getActivity(
             context,
-            /* requestCode = */ 0,
+            /* requestCode= */ 0,
             Intent("action"),
             PendingIntent.FLAG_IMMUTABLE
         )
@@ -66,7 +67,12 @@
     @Before
     fun setUp() {
         with(kosmos) {
-            underTest = SmartspaceInteractionHandler(activityStarter, communalSceneInteractor)
+            underTest =
+                SmartspaceInteractionHandler(
+                    activityStarter = activityStarter,
+                    communalSceneInteractor = communalSceneInteractor,
+                    logBuffer = logcatLogBuffer(),
+                )
         }
     }
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt
index f8906ad..57ce9de 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt
@@ -18,13 +18,16 @@
 
 import android.appwidget.AppWidgetProviderInfo
 import android.content.ActivityNotFoundException
+import android.content.ComponentName
 import android.content.Intent
 import android.content.pm.ActivityInfo
 import android.content.pm.PackageManager
 import android.content.pm.ResolveInfo
 import android.content.pm.UserInfo
-import android.os.UserHandle
 import android.provider.Settings
+import android.view.accessibility.AccessibilityEvent
+import android.view.accessibility.AccessibilityManager
+import android.view.accessibility.accessibilityManager
 import android.widget.RemoteViews
 import androidx.activity.result.ActivityResultLauncher
 import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -43,12 +46,11 @@
 import com.android.systemui.communal.domain.interactor.CommunalInteractor
 import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
 import com.android.systemui.communal.domain.interactor.communalInteractor
-import com.android.systemui.communal.domain.interactor.communalPrefsInteractor
 import com.android.systemui.communal.domain.interactor.communalSceneInteractor
 import com.android.systemui.communal.domain.interactor.communalSettingsInteractor
 import com.android.systemui.communal.domain.model.CommunalContentModel
+import com.android.systemui.communal.shared.log.CommunalMetricsLogger
 import com.android.systemui.communal.shared.log.CommunalUiEvent
-import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
 import com.android.systemui.communal.shared.model.EditModeState
 import com.android.systemui.communal.ui.viewmodel.CommunalEditModeViewModel
 import com.android.systemui.coroutines.collectLastValue
@@ -62,8 +64,6 @@
 import com.android.systemui.settings.fakeUserTracker
 import com.android.systemui.testKosmos
 import com.android.systemui.user.data.repository.fakeUserRepository
-import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.whenever
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.test.advanceTimeBy
 import kotlinx.coroutines.test.runTest
@@ -78,17 +78,21 @@
 import org.mockito.Mockito.never
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.any
+import org.mockito.kotlin.argumentCaptor
 import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
 import org.mockito.kotlin.spy
+import org.mockito.kotlin.whenever
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class CommunalEditModeViewModelTest : SysuiTestCase() {
     @Mock private lateinit var mediaHost: MediaHost
     @Mock private lateinit var uiEventLogger: UiEventLogger
-    @Mock private lateinit var providerInfo: AppWidgetProviderInfo
     @Mock private lateinit var packageManager: PackageManager
     @Mock private lateinit var activityResultLauncher: ActivityResultLauncher<Intent>
+    @Mock private lateinit var metricsLogger: CommunalMetricsLogger
 
     private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
@@ -99,6 +103,7 @@
     private lateinit var mediaRepository: FakeCommunalMediaRepository
     private lateinit var communalSceneInteractor: CommunalSceneInteractor
     private lateinit var communalInteractor: CommunalInteractor
+    private lateinit var accessibilityManager: AccessibilityManager
 
     private val testableResources = context.orCreateTestableResources
 
@@ -120,7 +125,7 @@
             selectedUserIndex = 0,
         )
         kosmos.fakeFeatureFlagsClassic.set(Flags.COMMUNAL_SERVICE_ENABLED, true)
-        whenever(providerInfo.profile).thenReturn(UserHandle(MAIN_USER_INFO.id))
+        accessibilityManager = kosmos.accessibilityManager
 
         underTest =
             CommunalEditModeViewModel(
@@ -132,7 +137,10 @@
                 uiEventLogger,
                 logcatLogBuffer("CommunalEditModeViewModelTest"),
                 kosmos.testDispatcher,
-                kosmos.communalPrefsInteractor,
+                metricsLogger,
+                context,
+                accessibilityManager,
+                packageManager,
             )
     }
 
@@ -142,20 +150,8 @@
             tutorialRepository.setTutorialSettingState(Settings.Secure.HUB_MODE_TUTORIAL_COMPLETED)
 
             // Widgets available.
-            val widgets =
-                listOf(
-                    CommunalWidgetContentModel.Available(
-                        appWidgetId = 0,
-                        priority = 30,
-                        providerInfo = providerInfo,
-                    ),
-                    CommunalWidgetContentModel.Available(
-                        appWidgetId = 1,
-                        priority = 20,
-                        providerInfo = providerInfo,
-                    ),
-                )
-            widgetRepository.setCommunalWidgets(widgets)
+            widgetRepository.addWidget(appWidgetId = 0, priority = 30)
+            widgetRepository.addWidget(appWidgetId = 1, priority = 20)
 
             // Smartspace available.
             smartspaceRepository.setTimers(
@@ -216,20 +212,8 @@
             tutorialRepository.setTutorialSettingState(Settings.Secure.HUB_MODE_TUTORIAL_COMPLETED)
 
             // Widgets available.
-            val widgets =
-                listOf(
-                    CommunalWidgetContentModel.Available(
-                        appWidgetId = 0,
-                        priority = 30,
-                        providerInfo = providerInfo,
-                    ),
-                    CommunalWidgetContentModel.Available(
-                        appWidgetId = 1,
-                        priority = 20,
-                        providerInfo = providerInfo,
-                    ),
-                )
-            widgetRepository.setCommunalWidgets(widgets)
+            widgetRepository.addWidget(appWidgetId = 0, priority = 30)
+            widgetRepository.addWidget(appWidgetId = 1, priority = 20)
 
             val communalContent by collectLastValue(underTest.communalContent)
 
@@ -240,14 +224,18 @@
             assertThat(communalContent?.get(1))
                 .isInstanceOf(CommunalContentModel.WidgetContent::class.java)
 
-            underTest.onDeleteWidget(widgets.get(0).appWidgetId)
+            underTest.onDeleteWidget(
+                id = 0,
+                componentName = ComponentName("test_package", "test_class"),
+                priority = 30,
+            )
 
             // Only one widget and CTA tile remain.
             assertThat(communalContent?.size).isEqualTo(1)
             val item = communalContent?.get(0)
             val appWidgetId =
                 if (item is CommunalContentModel.WidgetContent) item.appWidgetId else null
-            assertThat(appWidgetId).isEqualTo(widgets.get(1).appWidgetId)
+            assertThat(appWidgetId).isEqualTo(1)
         }
 
     @Test
@@ -377,6 +365,37 @@
         verify(communalInteractor).setScrollPosition(eq(index), eq(offset))
     }
 
+    @Test
+    fun onNewWidgetAdded_accessibilityDisabled_doNothing() {
+        whenever(accessibilityManager.isEnabled).thenReturn(false)
+
+        val provider =
+            mock<AppWidgetProviderInfo> {
+                on { loadLabel(packageManager) }.thenReturn("Test Clock")
+            }
+        underTest.onNewWidgetAdded(provider)
+
+        verify(accessibilityManager, never()).sendAccessibilityEvent(any())
+    }
+
+    @Test
+    fun onNewWidgetAdded_accessibilityEnabled_sendAccessibilityAnnouncement() {
+        whenever(accessibilityManager.isEnabled).thenReturn(true)
+
+        val provider =
+            mock<AppWidgetProviderInfo> {
+                on { loadLabel(packageManager) }.thenReturn("Test Clock")
+            }
+        underTest.onNewWidgetAdded(provider)
+
+        val captor = argumentCaptor<AccessibilityEvent>()
+        verify(accessibilityManager).sendAccessibilityEvent(captor.capture())
+
+        val event = captor.firstValue
+        assertThat(event.eventType).isEqualTo(AccessibilityEvent.TYPE_ANNOUNCEMENT)
+        assertThat(event.contentDescription).isEqualTo("Test Clock widget added to lock screen")
+    }
+
     private companion object {
         val MAIN_USER_INFO = UserInfo(0, "primary", UserInfo.FLAG_MAIN)
         const val WIDGET_PICKER_PACKAGE_NAME = "widget_picker_package_name"
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt
index c480aa8..7a41bc6 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt
@@ -16,9 +16,8 @@
 
 package com.android.systemui.communal.view.viewmodel
 
-import android.appwidget.AppWidgetProviderInfo
+import android.content.ComponentName
 import android.content.pm.UserInfo
-import android.os.UserHandle
 import android.platform.test.flag.junit.FlagsParameterization
 import android.provider.Settings
 import android.widget.RemoteViews
@@ -43,8 +42,8 @@
 import com.android.systemui.communal.domain.interactor.communalSettingsInteractor
 import com.android.systemui.communal.domain.interactor.communalTutorialInteractor
 import com.android.systemui.communal.domain.model.CommunalContentModel
+import com.android.systemui.communal.shared.log.CommunalMetricsLogger
 import com.android.systemui.communal.shared.model.CommunalScenes
-import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
 import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
 import com.android.systemui.communal.ui.viewmodel.CommunalViewModel.Companion.POPUP_AUTO_HIDE_TIMEOUT_MS
 import com.android.systemui.communal.ui.viewmodel.PopupType
@@ -110,7 +109,7 @@
 @RunWith(ParameterizedAndroidJunit4::class)
 class CommunalViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
     @Mock private lateinit var mediaHost: MediaHost
-    @Mock private lateinit var providerInfo: AppWidgetProviderInfo
+    @Mock private lateinit var metricsLogger: CommunalMetricsLogger
 
     private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
@@ -153,7 +152,6 @@
             userInfos = listOf(MAIN_USER_INFO),
             selectedUserIndex = 0,
         )
-        whenever(providerInfo.profile).thenReturn(UserHandle(MAIN_USER_INFO.id))
         whenever(mediaHost.visible).thenReturn(true)
 
         kosmos.powerInteractor.setAwakeForTest()
@@ -175,7 +173,8 @@
                 kosmos.communalTutorialInteractor,
                 kosmos.shadeInteractor,
                 mediaHost,
-                logcatLogBuffer("CommunalViewModelTest")
+                logcatLogBuffer("CommunalViewModelTest"),
+                metricsLogger,
             )
     }
 
@@ -212,20 +211,8 @@
             tutorialRepository.setTutorialSettingState(Settings.Secure.HUB_MODE_TUTORIAL_COMPLETED)
 
             // Widgets available.
-            val widgets =
-                listOf(
-                    CommunalWidgetContentModel.Available(
-                        appWidgetId = 0,
-                        priority = 30,
-                        providerInfo = providerInfo,
-                    ),
-                    CommunalWidgetContentModel.Available(
-                        appWidgetId = 1,
-                        priority = 20,
-                        providerInfo = providerInfo,
-                    ),
-                )
-            widgetRepository.setCommunalWidgets(widgets)
+            widgetRepository.addWidget(appWidgetId = 0, priority = 30)
+            widgetRepository.addWidget(appWidgetId = 1, priority = 20)
 
             // Smartspace available.
             smartspaceRepository.setTimers(
@@ -314,15 +301,7 @@
         testScope.runTest {
             tutorialRepository.setTutorialSettingState(Settings.Secure.HUB_MODE_TUTORIAL_COMPLETED)
 
-            widgetRepository.setCommunalWidgets(
-                listOf(
-                    CommunalWidgetContentModel.Available(
-                        appWidgetId = 1,
-                        priority = 1,
-                        providerInfo = providerInfo,
-                    )
-                ),
-            )
+            widgetRepository.addWidget(appWidgetId = 1, priority = 1)
             mediaRepository.mediaInactive()
             smartspaceRepository.setTimers(emptyList())
 
@@ -676,20 +655,8 @@
             )
 
             // Widgets available
-            val widgets =
-                listOf(
-                    CommunalWidgetContentModel.Available(
-                        appWidgetId = 0,
-                        priority = 30,
-                        providerInfo = providerInfo,
-                    ),
-                    CommunalWidgetContentModel.Available(
-                        appWidgetId = 1,
-                        priority = 20,
-                        providerInfo = providerInfo,
-                    ),
-                )
-            widgetRepository.setCommunalWidgets(widgets)
+            widgetRepository.addWidget(appWidgetId = 0, priority = 30)
+            widgetRepository.addWidget(appWidgetId = 1, priority = 20)
 
             // Then hub shows widgets and the CTA tile
             assertThat(communalContent).hasSize(3)
@@ -743,20 +710,8 @@
             )
 
             // And widgets available
-            val widgets =
-                listOf(
-                    CommunalWidgetContentModel.Available(
-                        appWidgetId = 0,
-                        priority = 30,
-                        providerInfo = providerInfo,
-                    ),
-                    CommunalWidgetContentModel.Available(
-                        appWidgetId = 1,
-                        priority = 20,
-                        providerInfo = providerInfo,
-                    ),
-                )
-            widgetRepository.setCommunalWidgets(widgets)
+            widgetRepository.addWidget(appWidgetId = 0, priority = 30)
+            widgetRepository.addWidget(appWidgetId = 1, priority = 20)
 
             // Then emits widgets and the CTA tile
             assertThat(communalContent).hasSize(3)
@@ -795,6 +750,23 @@
         verify(communalInteractor).setScrollPosition(eq(index), eq(offset))
     }
 
+    @Test
+    fun onTapWidget_logEvent() {
+        underTest.onTapWidget(ComponentName("test_pkg", "test_cls"), priority = 10)
+        verify(metricsLogger).logTapWidget("test_pkg/test_cls", rank = 10)
+    }
+
+    @Test
+    fun glanceableTouchAvailable_availableWhenNestedScrollingWithoutConsumption() =
+        testScope.runTest {
+            val touchAvailable by collectLastValue(underTest.glanceableTouchAvailable)
+            assertThat(touchAvailable).isTrue()
+            underTest.onHubTouchConsumed()
+            assertThat(touchAvailable).isFalse()
+            underTest.onNestedScrolling()
+            assertThat(touchAvailable).isTrue()
+        }
+
     private suspend fun setIsMainUser(isMainUser: Boolean) {
         val user = if (isMainUser) MAIN_USER_INFO else SECONDARY_USER_INFO
         with(userRepository) {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartableTest.kt
index 3d2eabf..c9f3f14 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartableTest.kt
@@ -16,10 +16,7 @@
 
 package com.android.systemui.communal.widgets
 
-import android.appwidget.AppWidgetProviderInfo
 import android.content.pm.UserInfo
-import android.graphics.Bitmap
-import android.os.UserHandle
 import android.provider.Settings
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
@@ -27,7 +24,6 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.communal.data.repository.fakeCommunalWidgetRepository
 import com.android.systemui.communal.domain.interactor.communalInteractor
-import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.flags.Flags
 import com.android.systemui.flags.fakeFeatureFlagsClassic
@@ -38,7 +34,6 @@
 import com.android.systemui.settings.fakeUserTracker
 import com.android.systemui.testKosmos
 import com.android.systemui.user.data.repository.fakeUserRepository
-import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
 import com.android.systemui.util.settings.fakeSettings
 import com.google.common.truth.Truth.assertThat
@@ -172,26 +167,23 @@
         with(kosmos) {
             testScope.runTest {
                 // Set up communal widgets
-                val widget1 =
-                    mock<CommunalWidgetContentModel.Available> {
-                        whenever(this.appWidgetId).thenReturn(1)
-                    }
-                val widget2 =
-                    mock<CommunalWidgetContentModel.Available> {
-                        whenever(this.appWidgetId).thenReturn(2)
-                    }
-                val widget3 =
-                    mock<CommunalWidgetContentModel.Available> {
-                        whenever(this.appWidgetId).thenReturn(3)
-                    }
-                fakeCommunalWidgetRepository.setCommunalWidgets(listOf(widget1, widget2, widget3))
+                fakeCommunalWidgetRepository.addWidget(appWidgetId = 1)
+                fakeCommunalWidgetRepository.addWidget(appWidgetId = 2)
+                fakeCommunalWidgetRepository.addWidget(appWidgetId = 3)
 
                 underTest.start()
 
                 // Assert communal widgets has 3
                 val communalWidgets by
                     collectLastValue(fakeCommunalWidgetRepository.communalWidgets)
-                assertThat(communalWidgets).containsExactly(widget1, widget2, widget3)
+                assertThat(communalWidgets).hasSize(3)
+
+                val widget1 = communalWidgets!![0]
+                val widget2 = communalWidgets!![1]
+                val widget3 = communalWidgets!![2]
+                assertThat(widget1.appWidgetId).isEqualTo(1)
+                assertThat(widget2.appWidgetId).isEqualTo(2)
+                assertThat(widget3.appWidgetId).isEqualTo(3)
 
                 // Report app widget 1 to remove and assert widget removed
                 appWidgetIdToRemove.emit(1)
@@ -216,18 +208,26 @@
                     selectedUserIndex = 0,
                 )
                 // One work widget, one pending work widget, and one personal widget.
-                val widget1 = createWidgetForUser(1, USER_INFO_WORK.id)
-                val widget2 = createPendingWidgetForUser(2, USER_INFO_WORK.id)
-                val widget3 = createWidgetForUser(3, MAIN_USER_INFO.id)
-                val widgets = listOf(widget1, widget2, widget3)
-                fakeCommunalWidgetRepository.setCommunalWidgets(widgets)
+                fakeCommunalWidgetRepository.addWidget(appWidgetId = 1, userId = USER_INFO_WORK.id)
+                fakeCommunalWidgetRepository.addPendingWidget(
+                    appWidgetId = 2,
+                    userId = USER_INFO_WORK.id
+                )
+                fakeCommunalWidgetRepository.addWidget(appWidgetId = 3, userId = MAIN_USER_INFO.id)
 
                 underTest.start()
                 runCurrent()
 
                 val communalWidgets by
                     collectLastValue(fakeCommunalWidgetRepository.communalWidgets)
-                assertThat(communalWidgets).containsExactly(widget1, widget2, widget3)
+                assertThat(communalWidgets).hasSize(3)
+
+                val widget1 = communalWidgets!![0]
+                val widget2 = communalWidgets!![1]
+                val widget3 = communalWidgets!![2]
+                assertThat(widget1.appWidgetId).isEqualTo(1)
+                assertThat(widget2.appWidgetId).isEqualTo(2)
+                assertThat(widget3.appWidgetId).isEqualTo(3)
 
                 // Unlock the device and remove work profile.
                 fakeKeyguardRepository.setKeyguardShowing(false)
@@ -259,32 +259,6 @@
             )
         }
 
-    private fun createWidgetForUser(
-        appWidgetId: Int,
-        userId: Int
-    ): CommunalWidgetContentModel.Available =
-        mock<CommunalWidgetContentModel.Available> {
-            whenever(this.appWidgetId).thenReturn(appWidgetId)
-            val providerInfo = mock<AppWidgetProviderInfo>()
-            whenever(providerInfo.profile).thenReturn(UserHandle(userId))
-            whenever(this.providerInfo).thenReturn(providerInfo)
-        }
-
-    private fun createPendingWidgetForUser(
-        appWidgetId: Int,
-        userId: Int,
-        priority: Int = 0,
-        packageName: String = "",
-        icon: Bitmap? = null,
-    ): CommunalWidgetContentModel.Pending =
-        CommunalWidgetContentModel.Pending(
-            appWidgetId = appWidgetId,
-            priority = priority,
-            packageName = packageName,
-            icon = icon,
-            user = UserHandle(userId),
-        )
-
     private companion object {
         val MAIN_USER_INFO = UserInfo(0, "primary", UserInfo.FLAG_MAIN)
         val USER_INFO_WORK = UserInfo(10, "work", UserInfo.FLAG_PROFILE)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/EditWidgetsActivityStarterTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/EditWidgetsActivityStarterTest.kt
new file mode 100644
index 0000000..5b629b9
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/EditWidgetsActivityStarterTest.kt
@@ -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.systemui.communal.widgets
+
+import android.content.ComponentName
+import android.content.Intent
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.communal.widgets.EditWidgetsActivity.Companion.EXTRA_PRESELECTED_KEY
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.times
+import org.mockito.kotlin.verify
+
+@ExperimentalCoroutinesApi
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class EditWidgetsActivityStarterTest : SysuiTestCase() {
+    private val activityStarter = mock<ActivityStarter>()
+    private val kosmos = testKosmos()
+
+    private lateinit var component: ComponentName
+    private lateinit var underTest: EditWidgetsActivityStarter
+
+    @Before
+    fun setUp() {
+        component = ComponentName(context, EditWidgetsActivity::class.java)
+        underTest =
+            EditWidgetsActivityStarterImpl(
+                context.applicationContext,
+                activityStarter,
+            )
+    }
+
+    @Test
+    fun activityLaunch_intentIsWellFormed() {
+        with(kosmos) {
+            testScope.runTest {
+                underTest.startActivity(TEST_PRESELECTED_KEY, shouldOpenWidgetPickerOnStart = true)
+
+                val captor = argumentCaptor<Intent>()
+                verify(activityStarter)
+                    .startActivityDismissingKeyguard(captor.capture(), eq(true), eq(true), any())
+                assertThat(captor.lastValue.component).isEqualTo(component)
+                assertThat(captor.lastValue.flags and Intent.FLAG_ACTIVITY_NEW_TASK).isNotEqualTo(0)
+                assertThat(captor.lastValue.flags and Intent.FLAG_ACTIVITY_CLEAR_TASK)
+                    .isNotEqualTo(0)
+                assertThat(captor.lastValue.extras?.getString(EXTRA_PRESELECTED_KEY))
+                    .isEqualTo(TEST_PRESELECTED_KEY)
+                assertThat(
+                        captor.lastValue.extras?.getBoolean(
+                            EditWidgetsActivity.EXTRA_OPEN_WIDGET_PICKER_ON_START
+                        )
+                    )
+                    .isEqualTo(true)
+
+                underTest.startActivity(TEST_PRESELECTED_KEY, shouldOpenWidgetPickerOnStart = false)
+
+                verify(activityStarter, times(2))
+                    .startActivityDismissingKeyguard(captor.capture(), eq(true), eq(true), any())
+                assertThat(captor.lastValue.component).isEqualTo(component)
+                assertThat(captor.lastValue.flags and Intent.FLAG_ACTIVITY_NEW_TASK).isNotEqualTo(0)
+                assertThat(captor.lastValue.flags and Intent.FLAG_ACTIVITY_CLEAR_TASK)
+                    .isNotEqualTo(0)
+                assertThat(captor.lastValue.extras?.getString(EXTRA_PRESELECTED_KEY))
+                    .isEqualTo(TEST_PRESELECTED_KEY)
+                assertThat(
+                        captor.lastValue.extras?.getBoolean(
+                            EditWidgetsActivity.EXTRA_OPEN_WIDGET_PICKER_ON_START
+                        )
+                    )
+                    .isEqualTo(false)
+            }
+        }
+    }
+
+    companion object {
+        const val TEST_PRESELECTED_KEY = "test-key"
+    }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/WidgetInteractionHandlerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/WidgetInteractionHandlerTest.kt
index ea8b5ab..023de52 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/WidgetInteractionHandlerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/WidgetInteractionHandlerTest.kt
@@ -29,6 +29,7 @@
 import com.android.systemui.communal.domain.interactor.communalSceneInteractor
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.kosmos.testScope
+import com.android.systemui.log.logcatLogBuffer
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.testKosmos
 import kotlinx.coroutines.test.runTest
@@ -53,7 +54,7 @@
     private val testIntent =
         PendingIntent.getActivity(
             context,
-            /* requestCode = */ 0,
+            /* requestCode= */ 0,
             Intent("action"),
             PendingIntent.FLAG_IMMUTABLE
         )
@@ -64,7 +65,12 @@
     @Before
     fun setUp() {
         with(kosmos) {
-            underTest = WidgetInteractionHandler(activityStarter, communalSceneInteractor)
+            underTest =
+                WidgetInteractionHandler(
+                    activityStarter = activityStarter,
+                    communalSceneInteractor = communalSceneInteractor,
+                    logBuffer = logcatLogBuffer(),
+                )
         }
     }
 
@@ -76,7 +82,7 @@
                 assertFalse(launching!!)
 
                 val parent = FrameLayout(context)
-                val view = CommunalAppWidgetHostView(context)
+                val view = CommunalAppWidgetHostView(context, underTest)
                 parent.addView(view)
                 val (fillInIntent, activityOptions) = testResponse.getLaunchOptions(view)
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
index 2bf50b3..91259a6 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
@@ -58,7 +58,6 @@
 import com.android.systemui.flags.FakeFeatureFlags
 import com.android.systemui.keyguard.data.repository.BiometricType
 import com.android.systemui.keyguard.data.repository.fakeBiometricSettingsRepository
-import com.android.systemui.keyguard.data.repository.fakeCommandQueue
 import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository
 import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
 import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
@@ -78,7 +77,6 @@
 import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest
 import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest
 import com.android.systemui.power.domain.interactor.powerInteractor
-import com.android.systemui.statusbar.commandQueue
 import com.android.systemui.statusbar.phone.KeyguardBypassController
 import com.android.systemui.testKosmos
 import com.android.systemui.user.data.model.SelectionStatus
@@ -116,7 +114,7 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() {
-    private val kosmos = testKosmos().apply { this.commandQueue = this.fakeCommandQueue }
+    private val kosmos = testKosmos()
     private lateinit var underTest: DeviceEntryFaceAuthRepositoryImpl
 
     @Mock private lateinit var faceManager: FaceManager
@@ -162,7 +160,6 @@
     private val displayStateInteractor = kosmos.displayStateInteractor
     private val bouncerRepository = kosmos.fakeKeyguardBouncerRepository
     private val displayRepository = kosmos.displayRepository
-    private val fakeCommandQueue = kosmos.fakeCommandQueue
     private val keyguardTransitionInteractor = kosmos.keyguardTransitionInteractor
     private lateinit var featureFlags: FakeFeatureFlags
 
@@ -572,9 +569,7 @@
                 bouncerRepository.setAlternateVisible(false)
                 // Keyguard is occluded when secure camera is active.
                 keyguardRepository.setKeyguardOccluded(true)
-                fakeCommandQueue.doForEachCallback {
-                    it.onCameraLaunchGestureDetected(CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP)
-                }
+                keyguardInteractor.onCameraLaunchDetected(CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP)
             }
         }
 
@@ -589,9 +584,7 @@
             assertThat(canFaceAuthRun()).isTrue()
 
             // launch secure camera
-            fakeCommandQueue.doForEachCallback {
-                it.onCameraLaunchGestureDetected(CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP)
-            }
+            keyguardInteractor.onCameraLaunchDetected(CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP)
             keyguardRepository.setKeyguardOccluded(true)
             runCurrent()
             assertThat(canFaceAuthRun()).isFalse()
@@ -870,9 +863,7 @@
                 bouncerRepository.setAlternateVisible(false)
                 // Keyguard is occluded when secure camera is active.
                 keyguardRepository.setKeyguardOccluded(true)
-                fakeCommandQueue.doForEachCallback {
-                    it.onCameraLaunchGestureDetected(CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP)
-                }
+                keyguardInteractor.onCameraLaunchDetected(CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP)
             }
         }
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFingerprintAuthInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFingerprintAuthInteractorTest.kt
index 51f9957..605d125 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFingerprintAuthInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFingerprintAuthInteractorTest.kt
@@ -21,8 +21,6 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.biometrics.data.repository.fingerprintPropertyRepository
 import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.keyguard.data.repository.biometricSettingsRepository
-import com.android.systemui.keyguard.data.repository.deviceEntryFingerprintAuthRepository
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
@@ -36,41 +34,29 @@
     private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
     private val underTest = kosmos.deviceEntryFingerprintAuthInteractor
-    private val fingerprintAuthRepository = kosmos.deviceEntryFingerprintAuthRepository
     private val fingerprintPropertyRepository = kosmos.fingerprintPropertyRepository
-    private val biometricSettingsRepository = kosmos.biometricSettingsRepository
 
     @Test
-    fun isFingerprintAuthCurrentlyAllowed_allowedOnlyWhenItIsNotLockedOutAndAllowedBySettings() =
+    fun isSensorUnderDisplay() =
         testScope.runTest {
-            val currentlyAllowed by collectLastValue(underTest.isFingerprintAuthCurrentlyAllowed)
-            biometricSettingsRepository.setIsFingerprintAuthCurrentlyAllowed(true)
-            fingerprintAuthRepository.setLockedOut(true)
-
-            assertThat(currentlyAllowed).isFalse()
-
-            fingerprintAuthRepository.setLockedOut(false)
-            assertThat(currentlyAllowed).isTrue()
-
-            biometricSettingsRepository.setIsFingerprintAuthCurrentlyAllowed(false)
-            assertThat(currentlyAllowed).isFalse()
+            val isUdfps by collectLastValue(underTest.isSensorUnderDisplay)
+            fingerprintPropertyRepository.supportsUdfps()
+            assertThat(isUdfps).isTrue()
         }
 
     @Test
-    fun isFingerprintCurrentlyAllowedInBouncer_trueForNonUdfpsSensorTypes() =
+    fun isSensorUnderDisplay_rear() =
         testScope.runTest {
-            biometricSettingsRepository.setIsFingerprintAuthCurrentlyAllowed(true)
-
-            val isFingerprintCurrentlyAllowedInBouncer by
-                collectLastValue(underTest.isFingerprintCurrentlyAllowedOnBouncer)
-
-            fingerprintPropertyRepository.supportsUdfps()
-            assertThat(isFingerprintCurrentlyAllowedInBouncer).isFalse()
-
+            val isUdfps by collectLastValue(underTest.isSensorUnderDisplay)
             fingerprintPropertyRepository.supportsRearFps()
-            assertThat(isFingerprintCurrentlyAllowedInBouncer).isTrue()
+            assertThat(isUdfps).isFalse()
+        }
 
+    @Test
+    fun isSensorUnderDisplay_side() =
+        testScope.runTest {
+            val isUdfps by collectLastValue(underTest.isSensorUnderDisplay)
             fingerprintPropertyRepository.supportsSideFps()
-            assertThat(isFingerprintCurrentlyAllowedInBouncer).isTrue()
+            assertThat(isUdfps).isFalse()
         }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt
index 60c9bb0..5c09777 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt
@@ -54,6 +54,7 @@
 import com.android.systemui.communal.domain.interactor.CommunalInteractor
 import com.android.systemui.communal.domain.interactor.communalInteractor
 import com.android.systemui.communal.domain.interactor.setCommunalAvailable
+import com.android.systemui.communal.shared.log.CommunalUiEvent
 import com.android.systemui.communal.shared.model.CommunalScenes
 import com.android.systemui.complication.ComplicationHostViewController
 import com.android.systemui.complication.ComplicationLayoutEngine
@@ -61,12 +62,13 @@
 import com.android.systemui.dreams.complication.HideComplicationTouchHandler
 import com.android.systemui.dreams.dagger.DreamOverlayComponent
 import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
+import com.android.systemui.keyguard.gesture.domain.gestureInteractor
 import com.android.systemui.kosmos.testScope
+import com.android.systemui.navigationbar.gestural.domain.GestureInteractor
 import com.android.systemui.testKosmos
 import com.android.systemui.touch.TouchInsetManager
 import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.eq
 import com.android.systemui.util.mockito.whenever
 import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
@@ -83,9 +85,11 @@
 import org.mockito.Mockito
 import org.mockito.Mockito.clearInvocations
 import org.mockito.Mockito.isNull
-import org.mockito.Mockito.spy
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.spy
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
@@ -165,6 +169,7 @@
     private lateinit var bouncerRepository: FakeKeyguardBouncerRepository
     private lateinit var communalRepository: FakeCommunalSceneRepository
     private var viewCaptureSpy = spy(ViewCaptureFactory.getInstance(context))
+    private lateinit var gestureInteractor: GestureInteractor
 
     @Captor var mViewCaptor: ArgumentCaptor<View>? = null
     private lateinit var mService: DreamOverlayService
@@ -176,6 +181,7 @@
         lifecycleRegistry = FakeLifecycleRegistry(mLifecycleOwner)
         bouncerRepository = kosmos.fakeKeyguardBouncerRepository
         communalRepository = kosmos.fakeCommunalSceneRepository
+        gestureInteractor = spy(kosmos.gestureInteractor)
 
         whenever(mDreamOverlayComponent.getDreamOverlayContainerViewController())
             .thenReturn(mDreamOverlayContainerViewController)
@@ -230,6 +236,7 @@
                 HOME_CONTROL_PANEL_DREAM_COMPONENT,
                 mDreamOverlayCallbackController,
                 kosmos.keyguardInteractor,
+                gestureInteractor,
                 WINDOW_NAME
             )
     }
@@ -660,6 +667,7 @@
             verify(mDreamOverlayCallback).onRedirectWake(true)
             client.onWakeRequested()
             verify(mCommunalInteractor).changeScene(eq(CommunalScenes.Communal), isNull())
+            verify(mUiEventLogger).log(CommunalUiEvent.DREAM_TO_COMMUNAL_HUB_DREAM_AWAKE_START)
         }
 
     @Test
@@ -953,6 +961,47 @@
         assertThat(lifecycleRegistry.currentState).isEqualTo(Lifecycle.State.RESUMED)
     }
 
+    @Test
+    fun testDreamActivityGesturesBlockedOnStart() {
+        val client = client
+
+        // Inform the overlay service of dream starting.
+        client.startDream(
+            mWindowParams,
+            mDreamOverlayCallback,
+            DREAM_COMPONENT,
+            false /*shouldShowComplication*/
+        )
+        mMainExecutor.runAllReady()
+        val captor = argumentCaptor<ComponentName>()
+        verify(gestureInteractor)
+            .addGestureBlockedActivity(captor.capture(), eq(GestureInteractor.Scope.Global))
+        assertThat(captor.firstValue.packageName)
+            .isEqualTo(ComponentName.unflattenFromString(DREAM_COMPONENT)?.packageName)
+    }
+
+    @Test
+    fun testDreamActivityGesturesUnblockedOnEnd() {
+        val client = client
+
+        // Inform the overlay service of dream starting.
+        client.startDream(
+            mWindowParams,
+            mDreamOverlayCallback,
+            DREAM_COMPONENT,
+            false /*shouldShowComplication*/
+        )
+        mMainExecutor.runAllReady()
+
+        client.endDream()
+        mMainExecutor.runAllReady()
+        val captor = argumentCaptor<ComponentName>()
+        verify(gestureInteractor)
+            .removeGestureBlockedActivity(captor.capture(), eq(GestureInteractor.Scope.Global))
+        assertThat(captor.firstValue.packageName)
+            .isEqualTo(ComponentName.unflattenFromString(DREAM_COMPONENT)?.packageName)
+    }
+
     internal class FakeLifecycleRegistry(provider: LifecycleOwner) : LifecycleRegistry(provider) {
         val mLifecycles: MutableList<State> = ArrayList()
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/education/data/repository/ContextualEducationRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/education/data/repository/ContextualEducationRepositoryTest.kt
index 3a4b14b..331db52 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/education/data/repository/ContextualEducationRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/education/data/repository/ContextualEducationRepositoryTest.kt
@@ -22,10 +22,10 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.SysuiTestableContext
 import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.contextualeducation.GestureType.BACK
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testDispatcher
 import com.android.systemui.kosmos.testScope
-import com.android.systemui.shared.education.GestureType.BACK_GESTURE
 import com.google.common.truth.Truth.assertThat
 import java.io.File
 import java.time.Clock
@@ -70,8 +70,8 @@
     fun changeRetrievedValueForNewUser() =
         testScope.runTest {
             // Update data for old user.
-            underTest.incrementSignalCount(BACK_GESTURE)
-            val model by collectLastValue(underTest.readGestureEduModelFlow(BACK_GESTURE))
+            underTest.incrementSignalCount(BACK)
+            val model by collectLastValue(underTest.readGestureEduModelFlow(BACK))
             assertThat(model?.signalCount).isEqualTo(1)
 
             // User is changed.
@@ -83,17 +83,17 @@
     @Test
     fun incrementSignalCount() =
         testScope.runTest {
-            underTest.incrementSignalCount(BACK_GESTURE)
-            val model by collectLastValue(underTest.readGestureEduModelFlow(BACK_GESTURE))
+            underTest.incrementSignalCount(BACK)
+            val model by collectLastValue(underTest.readGestureEduModelFlow(BACK))
             assertThat(model?.signalCount).isEqualTo(1)
         }
 
     @Test
     fun dataAddedOnUpdateShortcutTriggerTime() =
         testScope.runTest {
-            val model by collectLastValue(underTest.readGestureEduModelFlow(BACK_GESTURE))
+            val model by collectLastValue(underTest.readGestureEduModelFlow(BACK))
             assertThat(model?.lastShortcutTriggeredTime).isNull()
-            underTest.updateShortcutTriggerTime(BACK_GESTURE)
+            underTest.updateShortcutTriggerTime(BACK)
             assertThat(model?.lastShortcutTriggeredTime).isEqualTo(clock.instant())
         }
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorTest.kt
new file mode 100644
index 0000000..ae3302c
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorTest.kt
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.education.domain.interactor
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.contextualeducation.GestureType
+import com.android.systemui.contextualeducation.GestureType.BACK
+import com.android.systemui.education.data.repository.contextualEducationRepository
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class KeyboardTouchpadEduInteractorTest : SysuiTestCase() {
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
+    private val repository = kosmos.contextualEducationRepository
+    private val underTest: KeyboardTouchpadEduInteractor = kosmos.keyboardTouchpadEduInteractor
+
+    @Before
+    fun setup() {
+        underTest.start()
+    }
+
+    @Test
+    fun newEducationInfoOnMaxSignalCountReached() =
+        testScope.runTest {
+            tryTriggeringEducation(BACK)
+            val model by collectLastValue(underTest.educationTriggered)
+            assertThat(model?.gestureType).isEqualTo(BACK)
+        }
+
+    @Test
+    fun noEducationInfoBeforeMaxSignalCountReached() =
+        testScope.runTest {
+            repository.incrementSignalCount(BACK)
+            val model by collectLastValue(underTest.educationTriggered)
+            assertThat(model).isNull()
+        }
+
+    @Test
+    fun noEducationInfoWhenShortcutTriggeredPreviously() =
+        testScope.runTest {
+            val model by collectLastValue(underTest.educationTriggered)
+            repository.updateShortcutTriggerTime(BACK)
+            tryTriggeringEducation(BACK)
+            assertThat(model).isNull()
+        }
+
+    private suspend fun tryTriggeringEducation(gestureType: GestureType) {
+        // Increment max number of signal to try triggering education
+        for (i in 1..KeyboardTouchpadEduInteractor.MAX_SIGNAL_COUNT) {
+            repository.incrementSignalCount(gestureType)
+        }
+    }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadStatsInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadStatsInteractorTest.kt
index ee51e37..cd0c58f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadStatsInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadStatsInteractorTest.kt
@@ -20,10 +20,10 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.contextualeducation.GestureType.BACK
 import com.android.systemui.education.data.repository.contextualEducationRepository
 import com.android.systemui.education.data.repository.fakeEduClock
 import com.android.systemui.kosmos.testScope
-import com.android.systemui.shared.education.GestureType.BACK_GESTURE
 import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.test.runTest
@@ -41,11 +41,9 @@
     fun dataUpdatedOnIncrementSignalCount() =
         testScope.runTest {
             val model by
-                collectLastValue(
-                    kosmos.contextualEducationRepository.readGestureEduModelFlow(BACK_GESTURE)
-                )
+                collectLastValue(kosmos.contextualEducationRepository.readGestureEduModelFlow(BACK))
             val originalValue = model!!.signalCount
-            underTest.incrementSignalCount(BACK_GESTURE)
+            underTest.incrementSignalCount(BACK)
             assertThat(model?.signalCount).isEqualTo(originalValue + 1)
         }
 
@@ -53,11 +51,9 @@
     fun dataAddedOnUpdateShortcutTriggerTime() =
         testScope.runTest {
             val model by
-                collectLastValue(
-                    kosmos.contextualEducationRepository.readGestureEduModelFlow(BACK_GESTURE)
-                )
+                collectLastValue(kosmos.contextualEducationRepository.readGestureEduModelFlow(BACK))
             assertThat(model?.lastShortcutTriggeredTime).isNull()
-            underTest.updateShortcutTriggerTime(BACK_GESTURE)
+            underTest.updateShortcutTriggerTime(BACK)
             assertThat(model?.lastShortcutTriggeredTime).isEqualTo(kosmos.fakeEduClock.instant())
         }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/gesture/data/GestureRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/gesture/data/GestureRepositoryTest.kt
new file mode 100644
index 0000000..91d37cf
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/gesture/data/GestureRepositoryTest.kt
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.gesture.data
+
+import android.content.ComponentName
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.navigationbar.gestural.data.respository.GestureRepositoryImpl
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.mock
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class GestureRepositoryTest : SysuiTestCase() {
+    private val testDispatcher = StandardTestDispatcher()
+    private val testScope = TestScope(testDispatcher)
+    private val underTest by lazy { GestureRepositoryImpl(testDispatcher) }
+
+    @Test
+    fun addRemoveComponentToBlock_updatesBlockedComponentSet() =
+        testScope.runTest {
+            val component = mock<ComponentName>()
+
+            underTest.addGestureBlockedActivity(component)
+            val addedBlockedComponents by collectLastValue(underTest.gestureBlockedActivities)
+            assertThat(addedBlockedComponents).contains(component)
+
+            underTest.removeGestureBlockedActivity(component)
+            val removedBlockedComponents by collectLastValue(underTest.gestureBlockedActivities)
+            assertThat(removedBlockedComponents).isEmpty()
+        }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/gesture/domain/GestureInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/gesture/domain/GestureInteractorTest.kt
new file mode 100644
index 0000000..bc142e6
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/gesture/domain/GestureInteractorTest.kt
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.gesture.domain
+
+import android.content.ComponentName
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.navigationbar.gestural.data.respository.GestureRepository
+import com.android.systemui.navigationbar.gestural.domain.GestureInteractor
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.resetMain
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import kotlinx.coroutines.test.setMain
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.junit.MockitoJUnit
+import org.mockito.junit.MockitoRule
+import org.mockito.kotlin.any
+import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.never
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class GestureInteractorTest : SysuiTestCase() {
+    @Rule @JvmField val mockitoRule: MockitoRule = MockitoJUnit.rule()
+
+    val dispatcher = StandardTestDispatcher()
+    val testScope = TestScope(dispatcher)
+
+    @Mock private lateinit var gestureRepository: GestureRepository
+
+    private val underTest by lazy {
+        GestureInteractor(gestureRepository, testScope.backgroundScope)
+    }
+
+    @Before
+    fun setup() {
+        Dispatchers.setMain(dispatcher)
+        whenever(gestureRepository.gestureBlockedActivities).thenReturn(MutableStateFlow(setOf()))
+    }
+
+    @After
+    fun tearDown() {
+        Dispatchers.resetMain()
+    }
+
+    @Test
+    fun addBlockedActivity_testCombination() =
+        testScope.runTest {
+            val globalComponent = mock<ComponentName>()
+            whenever(gestureRepository.gestureBlockedActivities)
+                .thenReturn(MutableStateFlow(setOf(globalComponent)))
+            val localComponent = mock<ComponentName>()
+            underTest.addGestureBlockedActivity(localComponent, GestureInteractor.Scope.Local)
+            val lastSeen by collectLastValue(underTest.gestureBlockedActivities)
+            testScope.runCurrent()
+            verify(gestureRepository, never()).addGestureBlockedActivity(any())
+            assertThat(lastSeen).hasSize(2)
+            assertThat(lastSeen).containsExactly(globalComponent, localComponent)
+        }
+
+    @Test
+    fun addBlockedActivityLocally_onlyAffectsLocalInteractor() =
+        testScope.runTest {
+            val component = mock<ComponentName>()
+            underTest.addGestureBlockedActivity(component, GestureInteractor.Scope.Local)
+            val lastSeen by collectLastValue(underTest.gestureBlockedActivities)
+            testScope.runCurrent()
+            verify(gestureRepository, never()).addGestureBlockedActivity(any())
+            assertThat(lastSeen).contains(component)
+        }
+
+    @Test
+    fun removeBlockedActivityLocally_onlyAffectsLocalInteractor() =
+        testScope.runTest {
+            val component = mock<ComponentName>()
+            underTest.addGestureBlockedActivity(component, GestureInteractor.Scope.Local)
+            val lastSeen by collectLastValue(underTest.gestureBlockedActivities)
+            testScope.runCurrent()
+            underTest.removeGestureBlockedActivity(component, GestureInteractor.Scope.Local)
+            testScope.runCurrent()
+            verify(gestureRepository, never()).removeGestureBlockedActivity(any())
+            assertThat(lastSeen).isEmpty()
+        }
+
+    @Test
+    fun addBlockedActivity_invokesRepository() =
+        testScope.runTest {
+            val component = mock<ComponentName>()
+            underTest.addGestureBlockedActivity(component, GestureInteractor.Scope.Global)
+            runCurrent()
+            val captor = argumentCaptor<ComponentName>()
+            verify(gestureRepository).addGestureBlockedActivity(captor.capture())
+            assertThat(captor.firstValue).isEqualTo(component)
+        }
+
+    @Test
+    fun removeBlockedActivity_invokesRepository() =
+        testScope.runTest {
+            val component = mock<ComponentName>()
+            underTest.removeGestureBlockedActivity(component, GestureInteractor.Scope.Global)
+            runCurrent()
+            val captor = argumentCaptor<ComponentName>()
+            verify(gestureRepository).removeGestureBlockedActivity(captor.capture())
+            assertThat(captor.firstValue).isEqualTo(component)
+        }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/qs/QSLongPressEffectTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/qs/QSLongPressEffectTest.kt
index 693fcda..273e3cb 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/qs/QSLongPressEffectTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/qs/QSLongPressEffectTest.kt
@@ -22,9 +22,10 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.classifier.falsingManager
+import com.android.systemui.animation.ActivityTransitionAnimator
 import com.android.systemui.haptics.vibratorHelper
 import com.android.systemui.kosmos.testScope
+import com.android.systemui.log.core.FakeLogBuffer
 import com.android.systemui.qs.qsTileFactory
 import com.android.systemui.statusbar.policy.keyguardStateController
 import com.android.systemui.testKosmos
@@ -52,6 +53,7 @@
     private val vibratorHelper = kosmos.vibratorHelper
     private val qsTile = kosmos.qsTileFactory.createTile("Test Tile")
     @Mock private lateinit var callback: QSLongPressEffect.Callback
+    @Mock private lateinit var controller: ActivityTransitionAnimator.Controller
 
     private val effectDuration = 400
     private val lowTickDuration = 12
@@ -71,7 +73,7 @@
             QSLongPressEffect(
                 vibratorHelper,
                 kosmos.keyguardStateController,
-                kosmos.falsingManager,
+                FakeLogBuffer.Factory.create(),
             )
         longPressEffect.callback = callback
         longPressEffect.qsTile = qsTile
@@ -218,8 +220,9 @@
             // GIVEN that the animation completes
             longPressEffect.handleAnimationComplete()
 
-            // THEN the effect ends in the idle state.
+            // THEN the effect ends in the idle state and the reversed callback is used.
             assertThat(longPressEffect.state).isEqualTo(QSLongPressEffect.State.IDLE)
+            verify(callback, times(1)).onEffectFinishedReversing()
         }
 
     @Test
@@ -301,13 +304,12 @@
     }
 
     @Test
-    fun getStateForClick_withFalseTapWhenLocked_returnsIdle() {
+    fun getStateForClick_whenKeyguardsIsShowing_returnsIdle() {
         // GIVEN an active tile
         qsTile.state?.state = Tile.STATE_ACTIVE
 
-        // GIVEN that the device is locked and a false tap is detected
-        whenever(kosmos.keyguardStateController.isUnlocked).thenReturn(false)
-        kosmos.falsingManager.setFalseTap(true)
+        // GIVEN that the keyguard is showing
+        whenever(kosmos.keyguardStateController.isShowing).thenReturn(true)
 
         // WHEN determining the state of a click action
         val clickState = longPressEffect.getStateForClick()
@@ -321,9 +323,8 @@
         // GIVEN an active tile
         qsTile.state?.state = Tile.STATE_ACTIVE
 
-        // GIVEN that the device is locked and a false tap is not detected
-        whenever(kosmos.keyguardStateController.isUnlocked).thenReturn(false)
-        kosmos.falsingManager.setFalseTap(false)
+        // GIVEN that the keyguard is not showing
+        whenever(kosmos.keyguardStateController.isShowing).thenReturn(false)
 
         // WHEN determining the state of a click action
         val clickState = longPressEffect.getStateForClick()
@@ -337,9 +338,8 @@
         // GIVEN that the tile is null
         longPressEffect.qsTile = null
 
-        // GIVEN that the device is locked and a false tap is not detected
-        whenever(kosmos.keyguardStateController.isUnlocked).thenReturn(false)
-        kosmos.falsingManager.setFalseTap(false)
+        // GIVEN that the keyguard is not showing
+        whenever(kosmos.keyguardStateController.isShowing).thenReturn(false)
 
         // WHEN determining the state of a click action
         val clickState = longPressEffect.getStateForClick()
@@ -348,6 +348,23 @@
         assertThat(clickState).isEqualTo(QSLongPressEffect.State.IDLE)
     }
 
+    @Test
+    fun onLongClickTransitionCancelled_whileInLongClickState_reversesEffect() =
+        testWhileInState(QSLongPressEffect.State.LONG_CLICKED) {
+            // GIVEN a transition controller delegate
+            val delegate = longPressEffect.createTransitionControllerDelegate(controller)
+
+            // WHEN the activity launch animation is cancelled
+            val newOccludedState = false
+            delegate.onTransitionAnimationCancelled(newOccludedState)
+
+            // THEN the effect reverses and ends in RUNNING_BACKWARDS_FROM_CANCEL
+            assertThat(longPressEffect.state)
+                .isEqualTo(QSLongPressEffect.State.RUNNING_BACKWARDS_FROM_CANCEL)
+            verify(callback, times(1)).onReverseAnimator(false)
+            verify(controller).onTransitionAnimationCancelled(newOccludedState)
+        }
+
     private fun testWithScope(initialize: Boolean = true, test: suspend TestScope.() -> Unit) =
         with(kosmos) {
             testScope.runTest {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/inputmethod/data/repository/InputMethodRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/inputmethod/data/repository/InputMethodRepositoryTest.kt
index 857cdce..274880b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/inputmethod/data/repository/InputMethodRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/inputmethod/data/repository/InputMethodRepositoryTest.kt
@@ -56,9 +56,6 @@
     fun setUp() {
         MockitoAnnotations.initMocks(this)
 
-        whenever(inputMethodManager.getEnabledInputMethodSubtypeList(eq(null), anyBoolean()))
-            .thenReturn(listOf())
-
         underTest =
             InputMethodRepositoryImpl(
                 backgroundDispatcher = kosmos.testDispatcher,
@@ -71,10 +68,16 @@
         testScope.runTest {
             whenever(inputMethodManager.getEnabledInputMethodListAsUser(eq(USER_HANDLE)))
                 .thenReturn(listOf())
-            whenever(inputMethodManager.getEnabledInputMethodSubtypeList(any(), anyBoolean()))
+            whenever(
+                    inputMethodManager.getEnabledInputMethodSubtypeListAsUser(
+                        any(),
+                        anyBoolean(),
+                        eq(USER_HANDLE)
+                    )
+                )
                 .thenReturn(listOf())
 
-            assertThat(underTest.enabledInputMethods(USER_ID, fetchSubtypes = true).count())
+            assertThat(underTest.enabledInputMethods(USER_HANDLE, fetchSubtypes = true).count())
                 .isEqualTo(0)
         }
 
@@ -83,11 +86,20 @@
         testScope.runTest {
             val subtypeId = 123
             val isAuxiliary = true
+            val selectedImiId = "imiId"
+            val selectedImi = mock<InputMethodInfo>()
+            whenever(selectedImi.id).thenReturn(selectedImiId)
+            whenever(inputMethodManager.getCurrentInputMethodInfoAsUser(eq(USER_HANDLE)))
+                .thenReturn(selectedImi)
             whenever(inputMethodManager.getEnabledInputMethodListAsUser(eq(USER_HANDLE)))
-                .thenReturn(listOf(mock<InputMethodInfo>()))
-            whenever(inputMethodManager.getEnabledInputMethodSubtypeList(any(), anyBoolean()))
-                .thenReturn(listOf())
-            whenever(inputMethodManager.getEnabledInputMethodSubtypeList(eq(null), anyBoolean()))
+                .thenReturn(listOf(selectedImi))
+            whenever(
+                    inputMethodManager.getEnabledInputMethodSubtypeListAsUser(
+                        eq(selectedImiId),
+                        anyBoolean(),
+                        eq(USER_HANDLE)
+                    )
+                )
                 .thenReturn(
                     listOf(
                         InputMethodSubtype.InputMethodSubtypeBuilder()
@@ -97,7 +109,7 @@
                     )
                 )
 
-            val result = underTest.selectedInputMethodSubtypes()
+            val result = underTest.selectedInputMethodSubtypes(USER_HANDLE)
             assertThat(result).hasSize(1)
             assertThat(result.first().subtypeId).isEqualTo(subtypeId)
             assertThat(result.first().isAuxiliary).isEqualTo(isAuxiliary)
@@ -108,7 +120,7 @@
         testScope.runTest {
             val displayId = 7
 
-            underTest.showInputMethodPicker(displayId, /* showAuxiliarySubtypes = */ true)
+            underTest.showInputMethodPicker(displayId, /* showAuxiliarySubtypes= */ true)
 
             verify(inputMethodManager)
                 .showInputMethodPickerFromSystem(
@@ -118,7 +130,6 @@
         }
 
     companion object {
-        private const val USER_ID = 100
-        private val USER_HANDLE = UserHandle.of(USER_ID)
+        private val USER_HANDLE = UserHandle.of(100)
     }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/inputmethod/domain/interactor/InputMethodInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/inputmethod/domain/interactor/InputMethodInteractorTest.kt
index d23ff2a..8e6de2f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/inputmethod/domain/interactor/InputMethodInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/inputmethod/domain/interactor/InputMethodInteractorTest.kt
@@ -143,6 +143,7 @@
         nonAuxiliarySubtypes: Int,
     ): InputMethodModel {
         return InputMethodModel(
+            userId = UUID.randomUUID().mostSignificantBits.toInt(),
             imeId = UUID.randomUUID().toString(),
             subtypes =
                 List(auxiliarySubtypes + nonAuxiliarySubtypes) {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractorTest.kt
index 5115f5a..3b8ffcd 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractorTest.kt
@@ -173,6 +173,7 @@
         }
 
     @Test
+    @DisableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
     fun transitionToGone_whenOpeningGlanceableHubEditMode() =
         testScope.runTest {
             kosmos.fakeKeyguardBouncerRepository.setAlternateVisible(true)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorTest.kt
index b885800..783e3b5 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorTest.kt
@@ -33,16 +33,21 @@
 package com.android.systemui.keyguard.domain.interactor
 
 import android.os.PowerManager
+import android.platform.test.annotations.DisableFlags
 import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.FlagsParameterization
 import android.service.dream.dreamManager
-import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.compose.animation.scene.ObservableTransitionState
-import com.android.systemui.Flags
+import com.android.systemui.Flags.FLAG_COMMUNAL_HUB
+import com.android.systemui.Flags.FLAG_COMMUNAL_SCENE_KTF_REFACTOR
+import com.android.systemui.Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.communal.data.repository.FakeCommunalSceneRepository
 import com.android.systemui.communal.data.repository.fakeCommunalSceneRepository
 import com.android.systemui.communal.domain.interactor.setCommunalAvailable
 import com.android.systemui.communal.shared.model.CommunalScenes
+import com.android.systemui.communal.shared.model.CommunalTransitionKeys
 import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
 import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
 import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
@@ -51,6 +56,7 @@
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.TransitionState
 import com.android.systemui.keyguard.util.KeyguardTransitionRepositorySpySubject.Companion.assertThat
+import com.android.systemui.kosmos.applicationCoroutineScope
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.power.domain.interactor.PowerInteractor
 import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest
@@ -69,15 +75,22 @@
 import org.mockito.Mockito.anyBoolean
 import org.mockito.Mockito.reset
 import org.mockito.Mockito.spy
+import org.mockito.kotlin.clearInvocations
+import org.mockito.kotlin.verify
 import org.mockito.kotlin.whenever
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RunWith(AndroidJUnit4::class)
-class FromDozingTransitionInteractorTest : SysuiTestCase() {
+@RunWith(ParameterizedAndroidJunit4::class)
+@EnableFlags(FLAG_COMMUNAL_HUB)
+class FromDozingTransitionInteractorTest(flags: FlagsParameterization?) : SysuiTestCase() {
     private val kosmos =
         testKosmos().apply {
             this.fakeKeyguardTransitionRepository = spy(FakeKeyguardTransitionRepository())
+            this.fakeCommunalSceneRepository =
+                spy(FakeCommunalSceneRepository(applicationScope = applicationCoroutineScope))
         }
 
     private val testScope = kosmos.testScope
@@ -86,6 +99,18 @@
     private lateinit var powerInteractor: PowerInteractor
     private lateinit var transitionRepository: FakeKeyguardTransitionRepository
 
+    companion object {
+        @JvmStatic
+        @Parameters(name = "{0}")
+        fun getParams(): List<FlagsParameterization> {
+            return FlagsParameterization.allCombinationsOf(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
+        }
+    }
+
+    init {
+        mSetFlagsRule.setFlagsParameterization(flags!!)
+    }
+
     @Before
     fun setup() {
         powerInteractor = kosmos.powerInteractor
@@ -108,7 +133,7 @@
     }
 
     @Test
-    @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
+    @EnableFlags(FLAG_KEYGUARD_WM_STATE_REFACTOR)
     fun testTransitionToLockscreen_onWakeup() =
         testScope.runTest {
             powerInteractor.setAwakeForTest()
@@ -123,7 +148,8 @@
         }
 
     @Test
-    @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR, Flags.FLAG_COMMUNAL_HUB)
+    @EnableFlags(FLAG_KEYGUARD_WM_STATE_REFACTOR)
+    @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
     fun testTransitionToLockscreen_onPowerButtonPress_canDream_glanceableHubAvailable() =
         testScope.runTest {
             whenever(kosmos.dreamManager.canStartDreaming(anyBoolean())).thenReturn(true)
@@ -143,7 +169,25 @@
         }
 
     @Test
-    @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
+    @EnableFlags(FLAG_KEYGUARD_WM_STATE_REFACTOR, FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
+    fun testTransitionToLockscreen_onPowerButtonPress_canDream_ktfRefactor() =
+        testScope.runTest {
+            whenever(kosmos.dreamManager.canStartDreaming(anyBoolean())).thenReturn(true)
+            kosmos.setCommunalAvailable(true)
+            runCurrent()
+            clearInvocations(kosmos.fakeCommunalSceneRepository)
+
+            powerInteractor.setAwakeForTest(reason = PowerManager.WAKE_REASON_POWER_BUTTON)
+            runCurrent()
+
+            // If dreaming is possible and communal is available, then we should transition to
+            // GLANCEABLE_HUB when waking up due to power button press.
+            verify(kosmos.fakeCommunalSceneRepository)
+                .changeScene(CommunalScenes.Communal, CommunalTransitionKeys.Immediately)
+        }
+
+    @Test
+    @EnableFlags(FLAG_KEYGUARD_WM_STATE_REFACTOR)
     fun testTransitionToLockscreen_onPowerButtonPress_canNotDream_glanceableHubAvailable() =
         testScope.runTest {
             whenever(kosmos.dreamManager.canStartDreaming(anyBoolean())).thenReturn(false)
@@ -163,7 +207,7 @@
         }
 
     @Test
-    @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
+    @EnableFlags(FLAG_KEYGUARD_WM_STATE_REFACTOR)
     fun testTransitionToLockscreen_onPowerButtonPress_canNDream_glanceableHubNotAvailable() =
         testScope.runTest {
             whenever(kosmos.dreamManager.canStartDreaming(anyBoolean())).thenReturn(true)
@@ -183,7 +227,8 @@
         }
 
     @Test
-    @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
+    @EnableFlags(FLAG_KEYGUARD_WM_STATE_REFACTOR)
+    @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
     fun testTransitionToGlanceableHub_onWakeup_ifIdleOnCommunal_noOccludingActivity() =
         testScope.runTest {
             kosmos.fakeCommunalSceneRepository.setTransitionState(
@@ -203,7 +248,7 @@
         }
 
     @Test
-    @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
+    @EnableFlags(FLAG_KEYGUARD_WM_STATE_REFACTOR)
     fun testTransitionToOccluded_onWakeup_whenOccludingActivityOnTop() =
         testScope.runTest {
             kosmos.keyguardOcclusionRepository.setShowWhenLockedActivityInfo(true)
@@ -219,7 +264,7 @@
         }
 
     @Test
-    @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
+    @EnableFlags(FLAG_KEYGUARD_WM_STATE_REFACTOR)
     fun testTransitionToOccluded_onWakeup_whenOccludingActivityOnTop_evenIfIdleOnCommunal() =
         testScope.runTest {
             kosmos.fakeCommunalSceneRepository.setTransitionState(
@@ -240,7 +285,7 @@
         }
 
     @Test
-    @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
+    @EnableFlags(FLAG_KEYGUARD_WM_STATE_REFACTOR)
     @Suppress("ktlint:standard:max-line-length")
     fun testTransitionToOccluded_onWakeUp_ifPowerButtonGestureDetected_fromAod_nonDismissableKeyguard() =
         testScope.runTest {
@@ -254,7 +299,7 @@
         }
 
     @Test
-    @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
+    @EnableFlags(FLAG_KEYGUARD_WM_STATE_REFACTOR)
     fun testTransitionToGone_onWakeUp_ifPowerButtonGestureDetected_fromAod_dismissableKeyguard() =
         testScope.runTest {
             kosmos.fakeKeyguardRepository.setKeyguardDismissible(true)
@@ -268,7 +313,7 @@
         }
 
     @Test
-    @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
+    @EnableFlags(FLAG_KEYGUARD_WM_STATE_REFACTOR)
     fun testTransitionToGone_onWakeUp_ifPowerButtonGestureDetected_fromGone() =
         testScope.runTest {
             powerInteractor.setAwakeForTest()
@@ -306,7 +351,7 @@
         }
 
     @Test
-    @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
+    @EnableFlags(FLAG_KEYGUARD_WM_STATE_REFACTOR)
     @Suppress("ktlint:standard:max-line-length")
     fun testTransitionToOccluded_onWakeUp_ifPowerButtonGestureDetectedAfterFinishedInAod_fromGone() =
         testScope.runTest {
@@ -342,7 +387,7 @@
         }
 
     @Test
-    @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
+    @EnableFlags(FLAG_KEYGUARD_WM_STATE_REFACTOR)
     fun testTransitionToOccluded_onWakeUp_ifPowerButtonGestureDetected_fromLockscreen() =
         testScope.runTest {
             powerInteractor.setAwakeForTest()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractorTest.kt
similarity index 96%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractorTest.kt
index 65aa825..7424320 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractorTest.kt
@@ -32,11 +32,13 @@
 
 package com.android.systemui.keyguard.domain.interactor
 
+import android.platform.test.annotations.DisableFlags
 import android.platform.test.annotations.EnableFlags
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.compose.animation.scene.ObservableTransitionState
 import com.android.systemui.Flags
+import com.android.systemui.Flags.FLAG_COMMUNAL_SCENE_KTF_REFACTOR
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.communal.data.repository.fakeCommunalSceneRepository
 import com.android.systemui.communal.shared.model.CommunalScenes
@@ -49,13 +51,13 @@
 import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest
 import com.android.systemui.power.domain.interactor.powerInteractor
 import com.android.systemui.testKosmos
-import kotlin.test.Test
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.runBlocking
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
+import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.Mockito.reset
 import org.mockito.Mockito.spy
@@ -108,6 +110,7 @@
 
     @Test
     @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
+    @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
     fun testShowWhenLockedActivity_noLongerOnTop_transitionsToGlanceableHub_ifIdleOnCommunal() =
         testScope.runTest {
             kosmos.fakeCommunalSceneRepository.setTransitionState(
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 7906a82..fc827a14 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
@@ -32,7 +32,7 @@
 import com.android.systemui.keyguard.data.repository.fakeCommandQueue
 import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
 import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
-import com.android.systemui.keyguard.shared.model.CameraLaunchSourceModel
+import com.android.systemui.keyguard.shared.model.CameraLaunchType
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.StatusBarState
 import com.android.systemui.keyguard.shared.model.TransitionState
@@ -47,7 +47,6 @@
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.flowOf
-import kotlinx.coroutines.flow.onCompletion
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
@@ -87,31 +86,17 @@
             val cameraLaunchSource = collectLastValue(flow)
             runCurrent()
 
-            commandQueue.doForEachCallback {
-                it.onCameraLaunchGestureDetected(StatusBarManager.CAMERA_LAUNCH_SOURCE_WIGGLE)
-            }
-            assertThat(cameraLaunchSource()).isEqualTo(CameraLaunchSourceModel.WIGGLE)
+            underTest.onCameraLaunchDetected(StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP)
+            assertThat(cameraLaunchSource()!!.type).isEqualTo(CameraLaunchType.POWER_DOUBLE_TAP)
 
-            commandQueue.doForEachCallback {
-                it.onCameraLaunchGestureDetected(
-                    StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP
-                )
-            }
-            assertThat(cameraLaunchSource()).isEqualTo(CameraLaunchSourceModel.POWER_DOUBLE_TAP)
+            underTest.onCameraLaunchDetected(StatusBarManager.CAMERA_LAUNCH_SOURCE_WIGGLE)
+            assertThat(cameraLaunchSource()!!.type).isEqualTo(CameraLaunchType.WIGGLE)
 
-            commandQueue.doForEachCallback {
-                it.onCameraLaunchGestureDetected(StatusBarManager.CAMERA_LAUNCH_SOURCE_LIFT_TRIGGER)
-            }
-            assertThat(cameraLaunchSource()).isEqualTo(CameraLaunchSourceModel.LIFT_TRIGGER)
+            underTest.onCameraLaunchDetected(StatusBarManager.CAMERA_LAUNCH_SOURCE_LIFT_TRIGGER)
+            assertThat(cameraLaunchSource()!!.type).isEqualTo(CameraLaunchType.LIFT_TRIGGER)
 
-            commandQueue.doForEachCallback {
-                it.onCameraLaunchGestureDetected(
-                    StatusBarManager.CAMERA_LAUNCH_SOURCE_QUICK_AFFORDANCE
-                )
-            }
-            assertThat(cameraLaunchSource()).isEqualTo(CameraLaunchSourceModel.QUICK_AFFORDANCE)
-
-            flow.onCompletion { assertThat(commandQueue.callbackCount()).isEqualTo(0) }
+            underTest.onCameraLaunchDetected(StatusBarManager.CAMERA_LAUNCH_SOURCE_QUICK_AFFORDANCE)
+            assertThat(cameraLaunchSource()!!.type).isEqualTo(CameraLaunchType.QUICK_AFFORDANCE)
         }
 
     @Test
@@ -121,11 +106,7 @@
             val secureCameraActive = collectLastValue(underTest.isSecureCameraActive)
             runCurrent()
 
-            commandQueue.doForEachCallback {
-                it.onCameraLaunchGestureDetected(
-                    StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP
-                )
-            }
+            underTest.onCameraLaunchDetected(StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP)
 
             assertThat(secureCameraActive()).isTrue()
 
@@ -146,11 +127,7 @@
             val secureCameraActive = collectLastValue(underTest.isSecureCameraActive)
             runCurrent()
 
-            commandQueue.doForEachCallback {
-                it.onCameraLaunchGestureDetected(
-                    StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP
-                )
-            }
+            underTest.onCameraLaunchDetected(StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP)
             assertThat(secureCameraActive()).isTrue()
 
             // Keyguard is showing and not occluded
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
index 2d77f4f..75c0d3b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
@@ -22,6 +22,7 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.internal.widget.LockPatternUtils
+import com.android.keyguard.logging.KeyguardQuickAffordancesLogger
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.animation.DialogTransitionAnimator
 import com.android.systemui.common.shared.model.ContentDescription
@@ -86,7 +87,8 @@
     @Mock private lateinit var launchAnimator: DialogTransitionAnimator
     @Mock private lateinit var devicePolicyManager: DevicePolicyManager
     @Mock private lateinit var shadeInteractor: ShadeInteractor
-    @Mock private lateinit var logger: KeyguardQuickAffordancesMetricsLogger
+    @Mock private lateinit var logger: KeyguardQuickAffordancesLogger
+    @Mock private lateinit var metricsLogger: KeyguardQuickAffordancesMetricsLogger
 
     private val kosmos = testKosmos()
 
@@ -194,6 +196,7 @@
                 repository = { quickAffordanceRepository },
                 launchAnimator = launchAnimator,
                 logger = logger,
+                metricsLogger = metricsLogger,
                 devicePolicyManager = devicePolicyManager,
                 dockManager = dockManager,
                 biometricSettingsRepository = biometricSettingsRepository,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
index a8eccc5..03647b9 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
@@ -136,30 +136,6 @@
         }
 
     @Test
-    fun finishedKeyguardTransitionStepTests() =
-        testScope.runTest {
-            val finishedSteps by collectValues(underTest.finishedKeyguardTransitionStep)
-            val steps = mutableListOf<TransitionStep>()
-
-            steps.add(TransitionStep(LOCKSCREEN, AOD, 0f, STARTED))
-            steps.add(TransitionStep(LOCKSCREEN, AOD, 0.9f, RUNNING))
-            steps.add(TransitionStep(LOCKSCREEN, AOD, 1f, FINISHED))
-            steps.add(TransitionStep(AOD, LOCKSCREEN, 0f, STARTED))
-            steps.add(TransitionStep(AOD, LOCKSCREEN, 0.5f, RUNNING))
-            steps.add(TransitionStep(AOD, LOCKSCREEN, 1f, FINISHED))
-            steps.add(TransitionStep(AOD, GONE, 1f, STARTED))
-
-            steps.forEach {
-                repository.sendTransitionStep(it)
-                runCurrent()
-            }
-
-            // Ignore the default state.
-            assertThat(finishedSteps.subList(1, finishedSteps.size))
-                .isEqualTo(listOf(steps[2], steps[5]))
-        }
-
-    @Test
     fun startedKeyguardTransitionStepTests() =
         testScope.runTest {
             val startedSteps by collectValues(underTest.startedKeyguardTransitionStep)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
index a3959d2..9762fd8 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
@@ -16,7 +16,8 @@
 
 package com.android.systemui.keyguard.domain.interactor
 
-import android.app.StatusBarManager
+import android.app.StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP
+import android.platform.test.annotations.DisableFlags
 import android.platform.test.annotations.EnableFlags
 import android.platform.test.flag.junit.FlagsParameterization
 import androidx.test.filters.SmallTest
@@ -25,9 +26,13 @@
 import com.android.keyguard.KeyguardSecurityModel.SecurityMode.PIN
 import com.android.systemui.Flags
 import com.android.systemui.Flags.FLAG_COMMUNAL_HUB
+import com.android.systemui.Flags.FLAG_COMMUNAL_SCENE_KTF_REFACTOR
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.bouncer.data.repository.fakeKeyguardBouncerRepository
+import com.android.systemui.communal.domain.interactor.CommunalSceneTransitionInteractor
 import com.android.systemui.communal.domain.interactor.communalInteractor
+import com.android.systemui.communal.domain.interactor.communalSceneInteractor
+import com.android.systemui.communal.domain.interactor.communalSceneTransitionInteractor
 import com.android.systemui.communal.domain.interactor.setCommunalAvailable
 import com.android.systemui.communal.shared.model.CommunalScenes
 import com.android.systemui.flags.BrokenWithSceneContainer
@@ -37,7 +42,6 @@
 import com.android.systemui.flags.andSceneContainer
 import com.android.systemui.flags.fakeFeatureFlagsClassic
 import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
-import com.android.systemui.keyguard.data.repository.fakeCommandQueue
 import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
 import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
 import com.android.systemui.keyguard.shared.model.BiometricUnlockMode
@@ -54,7 +58,6 @@
 import com.android.systemui.power.domain.interactor.powerInteractor
 import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import com.android.systemui.shade.shadeTestUtil
-import com.android.systemui.statusbar.commandQueue
 import com.android.systemui.testKosmos
 import com.android.systemui.util.mockito.whenever
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -88,13 +91,12 @@
     private val kosmos =
         testKosmos().apply {
             fakeKeyguardTransitionRepository = spy(FakeKeyguardTransitionRepository())
-            this.commandQueue = fakeCommandQueue
         }
     private val testScope = kosmos.testScope
 
     private val keyguardRepository by lazy { kosmos.fakeKeyguardRepository }
+    private val keyguardInteractor by lazy { kosmos.keyguardInteractor }
     private val bouncerRepository by lazy { kosmos.fakeKeyguardBouncerRepository }
-    private var commandQueue = kosmos.fakeCommandQueue
     private val shadeTestUtil by lazy { kosmos.shadeTestUtil }
     private val transitionRepository by lazy { kosmos.fakeKeyguardTransitionRepository }
     private lateinit var featureFlags: FakeFeatureFlags
@@ -122,15 +124,22 @@
     private val fromGlanceableHubTransitionInteractor by lazy {
         kosmos.fromGlanceableHubTransitionInteractor
     }
+    private val communalSceneTransitionInteractor by lazy {
+        kosmos.communalSceneTransitionInteractor
+    }
 
     private val powerInteractor by lazy { kosmos.powerInteractor }
     private val communalInteractor by lazy { kosmos.communalInteractor }
+    private val communalSceneInteractor by lazy { kosmos.communalSceneInteractor }
 
     companion object {
         @JvmStatic
         @Parameters(name = "{0}")
         fun getParams(): List<FlagsParameterization> {
-            return FlagsParameterization.allCombinationsOf().andSceneContainer()
+            return FlagsParameterization.allCombinationsOf(
+                    FLAG_COMMUNAL_SCENE_KTF_REFACTOR,
+                )
+                .andSceneContainer()
         }
     }
 
@@ -163,6 +172,7 @@
         fromOccludedTransitionInteractor.start()
         fromAlternateBouncerTransitionInteractor.start()
         fromGlanceableHubTransitionInteractor.start()
+        communalSceneTransitionInteractor.start()
     }
 
     @Test
@@ -349,6 +359,7 @@
         }
 
     @Test
+    @DisableSceneContainer
     fun dreamingLockscreenHostedToLockscreen() =
         testScope.runTest {
             // GIVEN a device dreaming with the lockscreen hosted dream and not dozing
@@ -436,6 +447,7 @@
         }
 
     @Test
+    @DisableSceneContainer
     fun dreamingLockscreenHostedToDozing() =
         testScope.runTest {
             // GIVEN a device is dreaming with lockscreen hosted dream
@@ -467,6 +479,7 @@
         }
 
     @Test
+    @DisableSceneContainer
     fun dreamingLockscreenHostedToOccluded() =
         testScope.runTest {
             // GIVEN device is dreaming with lockscreen hosted dream and not occluded
@@ -636,6 +649,7 @@
 
     @Test
     @DisableSceneContainer
+    @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
     fun dozingToGlanceableHub() =
         testScope.runTest {
             // GIVEN a prior transition has run to DOZING
@@ -770,6 +784,7 @@
 
     @Test
     @BrokenWithSceneContainer(339465026)
+    @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
     fun goneToGlanceableHub() =
         testScope.runTest {
             // GIVEN a prior transition has run to GONE
@@ -799,6 +814,29 @@
         }
 
     @Test
+    @BrokenWithSceneContainer(339465026)
+    @EnableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
+    fun goneToGlanceableHub_communalKtfRefactor() =
+        testScope.runTest {
+            // GIVEN a prior transition has run to GONE
+            runTransitionAndSetWakefulness(KeyguardState.LOCKSCREEN, KeyguardState.GONE)
+
+            // WHEN the glanceable hub is shown
+            communalSceneInteractor.changeScene(CommunalScenes.Communal)
+            runCurrent()
+
+            assertThat(transitionRepository)
+                .startedTransition(
+                    to = KeyguardState.GLANCEABLE_HUB,
+                    from = KeyguardState.GONE,
+                    ownerName = CommunalSceneTransitionInteractor::class.simpleName,
+                    animatorAssertion = { it.isNull() }
+                )
+
+            coroutineContext.cancelChildren()
+        }
+
+    @Test
     @DisableSceneContainer
     fun alternateBouncerToPrimaryBouncer() =
         testScope.runTest {
@@ -939,8 +977,14 @@
         }
 
     @Test
+    @DisableSceneContainer
     fun alternateBouncerToGlanceableHub() =
         testScope.runTest {
+            // GIVEN the device is idle on the glanceable hub
+            communalSceneInteractor.changeScene(CommunalScenes.Communal)
+            runCurrent()
+            clearInvocations(transitionRepository)
+
             // GIVEN a prior transition has run to ALTERNATE_BOUNCER
             bouncerRepository.setAlternateVisible(true)
             runTransitionAndSetWakefulness(
@@ -951,19 +995,11 @@
             // GIVEN the primary bouncer isn't showing and device not sleeping
             bouncerRepository.setPrimaryShow(false)
 
-            // GIVEN the device is idle on the glanceable hub
-            val idleTransitionState =
-                MutableStateFlow<ObservableTransitionState>(
-                    ObservableTransitionState.Idle(CommunalScenes.Communal)
-                )
-            communalInteractor.setTransitionState(idleTransitionState)
-            runCurrent()
-
             // WHEN the alternateBouncer stops showing
             bouncerRepository.setAlternateVisible(false)
             advanceTimeBy(200L)
 
-            // THEN a transition to LOCKSCREEN should occur
+            // THEN a transition to GLANCEABLE_HUB should occur
             assertThat(transitionRepository)
                 .startedTransition(
                     ownerName = FromAlternateBouncerTransitionInteractor::class.simpleName,
@@ -1063,17 +1099,16 @@
     @DisableSceneContainer
     fun primaryBouncerToGlanceableHub() =
         testScope.runTest {
+            // GIVEN the device is idle on the glanceable hub
+            communalSceneInteractor.changeScene(CommunalScenes.Communal)
+            runCurrent()
+
             // GIVEN a prior transition has run to PRIMARY_BOUNCER
             bouncerRepository.setPrimaryShow(true)
-            runTransitionAndSetWakefulness(KeyguardState.LOCKSCREEN, KeyguardState.PRIMARY_BOUNCER)
-
-            // GIVEN the device is idle on the glanceable hub
-            val idleTransitionState =
-                MutableStateFlow<ObservableTransitionState>(
-                    ObservableTransitionState.Idle(CommunalScenes.Communal)
-                )
-            communalInteractor.setTransitionState(idleTransitionState)
-            runCurrent()
+            runTransitionAndSetWakefulness(
+                KeyguardState.GLANCEABLE_HUB,
+                KeyguardState.PRIMARY_BOUNCER
+            )
 
             // WHEN the primaryBouncer stops showing
             bouncerRepository.setPrimaryShow(false)
@@ -1095,27 +1130,26 @@
     @DisableSceneContainer
     fun primaryBouncerToGlanceableHubWhileDreaming() =
         testScope.runTest {
+            // GIVEN the device is idle on the glanceable hub
+            communalSceneInteractor.changeScene(CommunalScenes.Communal)
+            runCurrent()
+
             // GIVEN a prior transition has run to PRIMARY_BOUNCER
             bouncerRepository.setPrimaryShow(true)
-            runTransitionAndSetWakefulness(KeyguardState.LOCKSCREEN, KeyguardState.PRIMARY_BOUNCER)
+            runTransitionAndSetWakefulness(
+                KeyguardState.GLANCEABLE_HUB,
+                KeyguardState.PRIMARY_BOUNCER
+            )
 
             // GIVEN that we are dreaming and occluded
             keyguardRepository.setDreaming(true)
             keyguardRepository.setKeyguardOccluded(true)
 
-            // GIVEN the device is idle on the glanceable hub
-            val idleTransitionState =
-                MutableStateFlow<ObservableTransitionState>(
-                    ObservableTransitionState.Idle(CommunalScenes.Communal)
-                )
-            communalInteractor.setTransitionState(idleTransitionState)
-            runCurrent()
-
             // WHEN the primaryBouncer stops showing
             bouncerRepository.setPrimaryShow(false)
             runCurrent()
 
-            // THEN a transition to LOCKSCREEN should occur
+            // THEN a transition to GLANCEABLE_HUB should occur
             assertThat(transitionRepository)
                 .startedTransition(
                     ownerName = FromPrimaryBouncerTransitionInteractor::class.simpleName,
@@ -1219,6 +1253,7 @@
 
     @Test
     @BrokenWithSceneContainer(339465026)
+    @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
     fun occludedToGlanceableHub() =
         testScope.runTest {
             // GIVEN a device on lockscreen
@@ -1256,6 +1291,7 @@
 
     @Test
     @BrokenWithSceneContainer(339465026)
+    @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
     fun occludedToGlanceableHubWhenInitiallyOnHub() =
         testScope.runTest {
             // GIVEN a device on lockscreen and communal is available
@@ -1293,6 +1329,37 @@
         }
 
     @Test
+    @BrokenWithSceneContainer(339465026)
+    @EnableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
+    fun occludedToGlanceableHub_communalKtfRefactor() =
+        testScope.runTest {
+            // GIVEN a device on lockscreen and communal is available
+            keyguardRepository.setKeyguardShowing(true)
+            kosmos.setCommunalAvailable(true)
+            runCurrent()
+
+            // GIVEN a prior transition has run to OCCLUDED from GLANCEABLE_HUB
+            runTransitionAndSetWakefulness(KeyguardState.GLANCEABLE_HUB, KeyguardState.OCCLUDED)
+            keyguardRepository.setKeyguardOccluded(true)
+            runCurrent()
+
+            // WHEN occlusion ends
+            keyguardRepository.setKeyguardOccluded(false)
+            runCurrent()
+
+            // THEN a transition to GLANCEABLE_HUB should occur
+            assertThat(transitionRepository)
+                .startedTransition(
+                    ownerName = CommunalSceneTransitionInteractor::class.simpleName,
+                    from = KeyguardState.OCCLUDED,
+                    to = KeyguardState.GLANCEABLE_HUB,
+                    animatorAssertion = { it.isNull() },
+                )
+
+            coroutineContext.cancelChildren()
+        }
+
+    @Test
     fun occludedToAlternateBouncer() =
         testScope.runTest {
             // GIVEN a prior transition has run to OCCLUDED
@@ -1511,6 +1578,7 @@
 
     @Test
     @DisableSceneContainer
+    @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
     fun dreamingToGlanceableHub() =
         testScope.runTest {
             // GIVEN a prior transition has run to DREAMING
@@ -1550,6 +1618,47 @@
         }
 
     @Test
+    @DisableSceneContainer
+    @EnableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
+    fun dreamingToGlanceableHub_communalKtfRefactor() =
+        testScope.runTest {
+            // GIVEN a prior transition has run to DREAMING
+            keyguardRepository.setDreaming(true)
+            runTransitionAndSetWakefulness(KeyguardState.LOCKSCREEN, KeyguardState.DREAMING)
+            runCurrent()
+
+            // WHEN a transition to the glanceable hub starts
+            val currentScene = CommunalScenes.Blank
+            val targetScene = CommunalScenes.Communal
+
+            val progress = MutableStateFlow(0f)
+            val transitionState =
+                MutableStateFlow<ObservableTransitionState>(
+                    ObservableTransitionState.Transition(
+                        fromScene = currentScene,
+                        toScene = targetScene,
+                        currentScene = flowOf(targetScene),
+                        progress = progress,
+                        isInitiatedByUserInput = false,
+                        isUserInputOngoing = flowOf(false),
+                    )
+                )
+            communalSceneInteractor.setTransitionState(transitionState)
+            progress.value = .1f
+            runCurrent()
+
+            assertThat(transitionRepository)
+                .startedTransition(
+                    ownerName = CommunalSceneTransitionInteractor::class.simpleName,
+                    from = KeyguardState.DREAMING,
+                    to = KeyguardState.GLANCEABLE_HUB,
+                    animatorAssertion = { it.isNull() }, // transition should be manually animated
+                )
+
+            coroutineContext.cancelChildren()
+        }
+
+    @Test
     @BrokenWithSceneContainer(339465026)
     fun lockscreenToOccluded() =
         testScope.runTest {
@@ -1612,11 +1721,7 @@
             reset(transitionRepository)
 
             // ...AND WHEN the camera gesture is detected quickly afterwards
-            commandQueue.doForEachCallback {
-                it.onCameraLaunchGestureDetected(
-                    StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP
-                )
-            }
+            keyguardInteractor.onCameraLaunchDetected(CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP)
             runCurrent()
 
             // THEN a transition from DOZING => OCCLUDED should occur
@@ -1679,6 +1784,7 @@
 
     @Test
     @DisableSceneContainer
+    @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
     fun lockscreenToGlanceableHub() =
         testScope.runTest {
             // GIVEN a prior transition has run to LOCKSCREEN
@@ -1737,6 +1843,48 @@
 
     @Test
     @DisableSceneContainer
+    @EnableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
+    fun lockscreenToGlanceableHub_communalKtfRefactor() =
+        testScope.runTest {
+            // GIVEN a prior transition has run to LOCKSCREEN
+            runTransitionAndSetWakefulness(KeyguardState.AOD, KeyguardState.LOCKSCREEN)
+            runCurrent()
+
+            // WHEN a glanceable hub transition starts
+            val currentScene = CommunalScenes.Blank
+            val targetScene = CommunalScenes.Communal
+
+            val progress = MutableStateFlow(0f)
+            val transitionState =
+                MutableStateFlow<ObservableTransitionState>(
+                    ObservableTransitionState.Transition(
+                        fromScene = currentScene,
+                        toScene = targetScene,
+                        currentScene = flowOf(targetScene),
+                        progress = progress,
+                        isInitiatedByUserInput = false,
+                        isUserInputOngoing = flowOf(false),
+                    )
+                )
+            communalSceneInteractor.setTransitionState(transitionState)
+            progress.value = .1f
+            runCurrent()
+
+            // THEN a transition from LOCKSCREEN => GLANCEABLE_HUB should occur
+            assertThat(transitionRepository)
+                .startedTransition(
+                    ownerName = CommunalSceneTransitionInteractor::class.simpleName,
+                    from = KeyguardState.LOCKSCREEN,
+                    to = KeyguardState.GLANCEABLE_HUB,
+                    animatorAssertion = { it.isNull() }, // transition should be manually animated
+                )
+
+            coroutineContext.cancelChildren()
+        }
+
+    @Test
+    @DisableSceneContainer
+    @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
     fun glanceableHubToLockscreen() =
         testScope.runTest {
             // GIVEN a prior transition has run to GLANCEABLE_HUB
@@ -1792,6 +1940,48 @@
 
     @Test
     @DisableSceneContainer
+    @EnableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
+    fun glanceableHubToLockscreen_communalKtfRefactor() =
+        testScope.runTest {
+            // GIVEN a prior transition has run to GLANCEABLE_HUB
+            communalSceneInteractor.changeScene(CommunalScenes.Communal)
+            runCurrent()
+            clearInvocations(transitionRepository)
+
+            // WHEN a transition away from glanceable hub starts
+            val currentScene = CommunalScenes.Communal
+            val targetScene = CommunalScenes.Blank
+
+            val progress = MutableStateFlow(0f)
+            val transitionState =
+                MutableStateFlow<ObservableTransitionState>(
+                    ObservableTransitionState.Transition(
+                        fromScene = currentScene,
+                        toScene = targetScene,
+                        currentScene = flowOf(targetScene),
+                        progress = progress,
+                        isInitiatedByUserInput = false,
+                        isUserInputOngoing = flowOf(false),
+                    )
+                )
+            communalSceneInteractor.setTransitionState(transitionState)
+            progress.value = .1f
+            runCurrent()
+
+            assertThat(transitionRepository)
+                .startedTransition(
+                    ownerName = CommunalSceneTransitionInteractor::class.simpleName,
+                    from = KeyguardState.GLANCEABLE_HUB,
+                    to = KeyguardState.LOCKSCREEN,
+                    animatorAssertion = { it.isNull() }, // transition should be manually animated
+                )
+
+            coroutineContext.cancelChildren()
+        }
+
+    @Test
+    @DisableSceneContainer
+    @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
     fun glanceableHubToDozing() =
         testScope.runTest {
             // GIVEN a prior transition has run to GLANCEABLE_HUB
@@ -1814,6 +2004,31 @@
 
     @Test
     @DisableSceneContainer
+    @EnableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
+    fun glanceableHubToDozing_communalKtfRefactor() =
+        testScope.runTest {
+            // GIVEN a prior transition has run to GLANCEABLE_HUB
+            communalSceneInteractor.changeScene(CommunalScenes.Communal)
+            runCurrent()
+            clearInvocations(transitionRepository)
+
+            // WHEN the device begins to sleep
+            powerInteractor.setAsleepForTest()
+            runCurrent()
+
+            assertThat(transitionRepository)
+                .startedTransition(
+                    ownerName = CommunalSceneTransitionInteractor::class.simpleName,
+                    from = KeyguardState.GLANCEABLE_HUB,
+                    to = KeyguardState.DOZING,
+                    animatorAssertion = { it.isNull() },
+                )
+
+            coroutineContext.cancelChildren()
+        }
+
+    @Test
+    @DisableSceneContainer
     fun glanceableHubToPrimaryBouncer() =
         testScope.runTest {
             // GIVEN a prior transition has run to ALTERNATE_BOUNCER
@@ -1858,6 +2073,7 @@
 
     @Test
     @BrokenWithSceneContainer(339465026)
+    @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
     fun glanceableHubToOccluded() =
         testScope.runTest {
             // GIVEN a prior transition has run to GLANCEABLE_HUB
@@ -1888,7 +2104,33 @@
         }
 
     @Test
+    @BrokenWithSceneContainer(339465026)
+    @EnableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
+    fun glanceableHubToOccluded_communalKtfRefactor() =
+        testScope.runTest {
+            // GIVEN a prior transition has run to GLANCEABLE_HUB
+            communalSceneInteractor.changeScene(CommunalScenes.Communal)
+            runCurrent()
+            clearInvocations(transitionRepository)
+
+            // WHEN the keyguard is occluded
+            keyguardRepository.setKeyguardOccluded(true)
+            runCurrent()
+
+            assertThat(transitionRepository)
+                .startedTransition(
+                    ownerName = CommunalSceneTransitionInteractor::class.simpleName,
+                    from = KeyguardState.GLANCEABLE_HUB,
+                    to = KeyguardState.OCCLUDED,
+                    animatorAssertion = { it.isNull() },
+                )
+
+            coroutineContext.cancelChildren()
+        }
+
+    @Test
     @DisableSceneContainer
+    @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
     fun glanceableHubToGone() =
         testScope.runTest {
             // GIVEN a prior transition has run to GLANCEABLE_HUB
@@ -1911,6 +2153,32 @@
 
     @Test
     @DisableSceneContainer
+    @EnableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
+    fun glanceableHubToGone_communalKtfRefactor() =
+        testScope.runTest {
+            // GIVEN a prior transition has run to GLANCEABLE_HUB
+            communalSceneInteractor.changeScene(CommunalScenes.Communal)
+            runCurrent()
+            clearInvocations(transitionRepository)
+
+            // WHEN keyguard goes away
+            keyguardRepository.setKeyguardGoingAway(true)
+            runCurrent()
+
+            assertThat(transitionRepository)
+                .startedTransition(
+                    ownerName = CommunalSceneTransitionInteractor::class.simpleName,
+                    from = KeyguardState.GLANCEABLE_HUB,
+                    to = KeyguardState.GONE,
+                    animatorAssertion = { it.isNull() },
+                )
+
+            coroutineContext.cancelChildren()
+        }
+
+    @Test
+    @DisableSceneContainer
+    @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
     fun glanceableHubToDreaming() =
         testScope.runTest {
             // GIVEN that we are dreaming and not dozing
@@ -1939,7 +2207,7 @@
                         isUserInputOngoing = flowOf(false),
                     )
                 )
-            communalInteractor.setTransitionState(transitionState)
+            communalSceneInteractor.setTransitionState(transitionState)
             runCurrent()
 
             assertThat(transitionRepository)
@@ -1953,6 +2221,54 @@
             coroutineContext.cancelChildren()
         }
 
+    @Test
+    @DisableSceneContainer
+    @EnableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
+    fun glanceableHubToDreaming_communalKtfRefactor() =
+        testScope.runTest {
+            // GIVEN that we are dreaming and not dozing
+            powerInteractor.setAwakeForTest()
+            keyguardRepository.setDreaming(true)
+            keyguardRepository.setDreamingWithOverlay(true)
+            keyguardRepository.setDozeTransitionModel(
+                DozeTransitionModel(from = DozeStateModel.DOZE, to = DozeStateModel.FINISH)
+            )
+            advanceTimeBy(100L)
+
+            // GIVEN a prior transition has run to GLANCEABLE_HUB
+            communalSceneInteractor.changeScene(CommunalScenes.Communal)
+            runCurrent()
+            clearInvocations(transitionRepository)
+
+            // WHEN a transition away from glanceable hub starts
+            val currentScene = CommunalScenes.Communal
+            val targetScene = CommunalScenes.Blank
+
+            val transitionState =
+                MutableStateFlow<ObservableTransitionState>(
+                    ObservableTransitionState.Transition(
+                        fromScene = currentScene,
+                        toScene = targetScene,
+                        currentScene = flowOf(targetScene),
+                        progress = flowOf(0f, 0.1f),
+                        isInitiatedByUserInput = false,
+                        isUserInputOngoing = flowOf(false),
+                    )
+                )
+            communalSceneInteractor.setTransitionState(transitionState)
+            runCurrent()
+
+            assertThat(transitionRepository)
+                .startedTransition(
+                    ownerName = CommunalSceneTransitionInteractor::class.simpleName,
+                    from = KeyguardState.GLANCEABLE_HUB,
+                    to = KeyguardState.DREAMING,
+                    animatorAssertion = { it.isNull() }, // transition should be manually animated
+                )
+
+            coroutineContext.cancelChildren()
+        }
+
     private suspend fun TestScope.runTransitionAndSetWakefulness(
         from: KeyguardState,
         to: KeyguardState
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToLockscreenTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToLockscreenTransitionViewModelTest.kt
new file mode 100644
index 0000000..f74b74a
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToLockscreenTransitionViewModelTest.kt
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.ui.viewmodel
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.data.repository.fingerprintPropertyRepository
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.coroutines.collectValues
+import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.shared.model.TransitionState.RUNNING
+import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@ExperimentalCoroutinesApi
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class AlternateBouncerToLockscreenTransitionViewModelTest : SysuiTestCase() {
+    val kosmos = testKosmos()
+    val testScope = kosmos.testScope
+
+    val keyguardTransitionRepository = kosmos.fakeKeyguardTransitionRepository
+    val fingerprintPropertyRepository = kosmos.fingerprintPropertyRepository
+
+    val underTest = kosmos.alternateBouncerToLockscreenTransitionViewModel
+
+    @Test
+    fun lockscreenAlpha_zeroInitialAlpha() =
+        testScope.runTest {
+            // ViewState starts at 0 alpha.
+            val viewState = ViewStateAccessor(alpha = { 0f })
+            val alpha by collectValues(underTest.lockscreenAlpha(viewState))
+
+            keyguardTransitionRepository.sendTransitionSteps(
+                from = KeyguardState.ALTERNATE_BOUNCER,
+                to = KeyguardState.LOCKSCREEN,
+                testScope
+            )
+
+            assertThat(alpha[0]).isEqualTo(0f)
+            // alpha duration is 250ms of the 300ms total, so 0.5f of the total is 0.6
+            assertThat(alpha[1]).isEqualTo(0.6f)
+            assertThat(alpha[2]).isEqualTo(1f)
+        }
+
+    @Test
+    fun deviceEntryParentViewAlpha() =
+        testScope.runTest {
+            val deviceEntryParentViewAlpha by collectLastValue(underTest.deviceEntryParentViewAlpha)
+
+            // immediately 1f
+            keyguardTransitionRepository.sendTransitionStep(step(0f, TransitionState.STARTED))
+            assertThat(deviceEntryParentViewAlpha).isEqualTo(1f)
+
+            keyguardTransitionRepository.sendTransitionStep(step(0.4f))
+            assertThat(deviceEntryParentViewAlpha).isEqualTo(1f)
+
+            keyguardTransitionRepository.sendTransitionStep(step(.85f))
+            assertThat(deviceEntryParentViewAlpha).isEqualTo(1f)
+
+            keyguardTransitionRepository.sendTransitionStep(step(1f))
+            assertThat(deviceEntryParentViewAlpha).isEqualTo(1f)
+        }
+
+    @Test
+    fun deviceEntryBackgroundViewAlpha_udfpsEnrolled_show() =
+        testScope.runTest {
+            fingerprintPropertyRepository.supportsUdfps()
+            val bgViewAlpha by collectLastValue(underTest.deviceEntryBackgroundViewAlpha)
+            runCurrent()
+
+            // immediately 1f
+            keyguardTransitionRepository.sendTransitionStep(step(0f, TransitionState.STARTED))
+            assertThat(bgViewAlpha).isEqualTo(1f)
+
+            keyguardTransitionRepository.sendTransitionStep(step(0.1f))
+            assertThat(bgViewAlpha).isEqualTo(1f)
+
+            keyguardTransitionRepository.sendTransitionStep(step(.3f))
+            assertThat(bgViewAlpha).isEqualTo(1f)
+
+            keyguardTransitionRepository.sendTransitionStep(step(.5f))
+            assertThat(bgViewAlpha).isEqualTo(1f)
+
+            keyguardTransitionRepository.sendTransitionStep(step(1f, TransitionState.FINISHED))
+            assertThat(bgViewAlpha).isEqualTo(1f)
+        }
+
+    private fun step(
+        value: Float,
+        state: TransitionState = TransitionState.RUNNING
+    ): TransitionStep {
+        return TransitionStep(
+            from = KeyguardState.ALTERNATE_BOUNCER,
+            to = KeyguardState.LOCKSCREEN,
+            value = value,
+            transitionState = state,
+            ownerName = "AlternateBouncerToLockscreenTransitionViewModelTest"
+        )
+    }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelTest.kt
index cde703b..5e9badc 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelTest.kt
@@ -156,6 +156,43 @@
             assertThat(deviceEntryBackgroundViewAlpha).isEqualTo(1f)
         }
 
+    @Test
+    fun deviceEntryBackgroundView_onCancel() =
+        testScope.runTest {
+            fingerprintPropertyRepository.supportsUdfps()
+            val deviceEntryBackgroundViewAlpha by
+                collectLastValue(underTest.deviceEntryBackgroundViewAlpha)
+            runCurrent()
+
+            // GIVEN transition START
+            repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+            assertThat(deviceEntryBackgroundViewAlpha).isEqualTo(0f)
+
+            // WHEN transition is cancelled
+            repository.sendTransitionStep(step(.1f, TransitionState.CANCELED))
+
+            // THEN alpha is immediately set to 1f (expected lockscreen alpha state)
+            assertThat(deviceEntryBackgroundViewAlpha).isEqualTo(1f)
+        }
+
+    @Test
+    fun deviceEntryParentViewAlpha_onCancel() =
+        testScope.runTest {
+            fingerprintPropertyRepository.supportsUdfps()
+            val deviceEntryBackgroundViewAlpha by
+                collectLastValue(underTest.deviceEntryParentViewAlpha)
+            runCurrent()
+
+            // GIVEN transition START
+            repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+
+            // WHEN transition is cancelled
+            repository.sendTransitionStep(step(.1f, TransitionState.CANCELED))
+
+            // THEN alpha is immediately set to 1f (expected lockscreen alpha state)
+            assertThat(deviceEntryBackgroundViewAlpha).isEqualTo(1f)
+        }
+
     private fun step(
         value: Float,
         state: TransitionState = TransitionState.RUNNING
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToGlanceableHubTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToGlanceableHubTransitionViewModelTest.kt
index aba21c9..cd0a11c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToGlanceableHubTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToGlanceableHubTransitionViewModelTest.kt
@@ -16,6 +16,8 @@
 
 package com.android.systemui.keyguard.ui.viewmodel
 
+import android.content.res.Configuration
+import android.util.LayoutDirection
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
@@ -33,6 +35,8 @@
 import kotlinx.coroutines.test.runTest
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
@@ -73,6 +77,9 @@
                 R.dimen.dreaming_to_hub_transition_dream_overlay_translation_x,
                 -100
             )
+            val configuration: Configuration = mock()
+            whenever(configuration.layoutDirection).thenReturn(LayoutDirection.LTR)
+            configurationRepository.onConfigurationChange(configuration)
 
             val values by collectValues(underTest.dreamOverlayTranslationX)
             assertThat(values).isEmpty()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToDreamingTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToDreamingTransitionViewModelTest.kt
index 11890c7..69361ef 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToDreamingTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToDreamingTransitionViewModelTest.kt
@@ -16,6 +16,8 @@
 
 package com.android.systemui.keyguard.ui.viewmodel
 
+import android.content.res.Configuration
+import android.util.LayoutDirection
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
@@ -33,6 +35,8 @@
 import kotlinx.coroutines.test.runTest
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
@@ -69,6 +73,9 @@
     @Test
     fun dreamOverlayTranslationX() =
         testScope.runTest {
+            val config: Configuration = mock()
+            whenever(config.layoutDirection).thenReturn(LayoutDirection.LTR)
+            configurationRepository.onConfigurationChange(config)
             configurationRepository.setDimensionPixelSize(
                 R.dimen.hub_to_dreaming_transition_dream_overlay_translation_x,
                 100
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToLockscreenTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToLockscreenTransitionViewModelTest.kt
index 1aa1ec4..d2be649 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToLockscreenTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToLockscreenTransitionViewModelTest.kt
@@ -16,6 +16,8 @@
 
 package com.android.systemui.keyguard.ui.viewmodel
 
+import android.content.res.Configuration
+import android.util.LayoutDirection
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
@@ -34,6 +36,8 @@
 import kotlinx.coroutines.test.runTest
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
@@ -77,6 +81,10 @@
     @Test
     fun lockscreenTranslationX() =
         testScope.runTest {
+            val config: Configuration = mock()
+            whenever(config.layoutDirection).thenReturn(LayoutDirection.LTR)
+            configurationRepository.onConfigurationChange(config)
+
             configurationRepository.setDimensionPixelSize(
                 R.dimen.hub_to_lockscreen_transition_lockscreen_translation_x,
                 100
@@ -102,6 +110,10 @@
     @Test
     fun lockscreenTranslationX_resetsAfterCancellation() =
         testScope.runTest {
+            val config: Configuration = mock()
+            whenever(config.layoutDirection).thenReturn(LayoutDirection.LTR)
+            configurationRepository.onConfigurationChange(config)
+
             configurationRepository.setDimensionPixelSize(
                 R.dimen.hub_to_lockscreen_transition_lockscreen_translation_x,
                 100
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
index 194f362..6dbe94b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
@@ -19,11 +19,13 @@
 
 package com.android.systemui.keyguard.ui.viewmodel
 
+import android.platform.test.annotations.EnableFlags
 import android.platform.test.flag.junit.FlagsParameterization
 import android.view.View
 import androidx.test.filters.SmallTest
 import com.android.compose.animation.scene.ObservableTransitionState
-import com.android.systemui.Flags as AConfigFlags
+import com.android.systemui.Flags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR
+import com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT
 import com.android.systemui.Flags.FLAG_NEW_AOD_TRANSITION
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.communal.data.repository.communalSceneRepository
@@ -68,6 +70,11 @@
 
 @SmallTest
 @RunWith(ParameterizedAndroidJunit4::class)
+@EnableFlags(
+    FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT,
+    FLAG_NEW_AOD_TRANSITION,
+    FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR
+)
 class KeyguardRootViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
     private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
@@ -102,13 +109,6 @@
 
     @Before
     fun setUp() {
-        mSetFlagsRule.enableFlags(FLAG_NEW_AOD_TRANSITION)
-        if (!SceneContainerFlag.isEnabled) {
-            mSetFlagsRule.enableFlags(AConfigFlags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR)
-            mSetFlagsRule.disableFlags(
-                AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT,
-            )
-        }
         kosmos.sceneContainerRepository.setTransitionState(transitionState)
     }
 
@@ -212,6 +212,11 @@
         testScope.runTest {
             val isVisible by collectLastValue(underTest.isNotifIconContainerVisible)
             runCurrent()
+            keyguardTransitionRepository.sendTransitionSteps(
+                from = KeyguardState.LOCKSCREEN,
+                to = KeyguardState.DOZING,
+                testScope,
+            )
             notificationsKeyguardInteractor.setPulseExpanding(false)
             deviceEntryRepository.setBypassEnabled(false)
             whenever(dozeParameters.alwaysOn).thenReturn(false)
@@ -227,6 +232,11 @@
         testScope.runTest {
             val isVisible by collectLastValue(underTest.isNotifIconContainerVisible)
             runCurrent()
+            keyguardTransitionRepository.sendTransitionSteps(
+                from = KeyguardState.LOCKSCREEN,
+                to = KeyguardState.DOZING,
+                testScope,
+            )
             notificationsKeyguardInteractor.setPulseExpanding(false)
             deviceEntryRepository.setBypassEnabled(false)
             whenever(dozeParameters.alwaysOn).thenReturn(true)
@@ -243,6 +253,11 @@
         testScope.runTest {
             val isVisible by collectLastValue(underTest.isNotifIconContainerVisible)
             runCurrent()
+            keyguardTransitionRepository.sendTransitionSteps(
+                from = KeyguardState.LOCKSCREEN,
+                to = KeyguardState.DOZING,
+                testScope,
+            )
             notificationsKeyguardInteractor.setPulseExpanding(false)
             deviceEntryRepository.setBypassEnabled(false)
             whenever(dozeParameters.alwaysOn).thenReturn(true)
@@ -255,6 +270,27 @@
         }
 
     @Test
+    fun iconContainer_isNotVisible_bypassDisabled_onLockscreen() =
+        testScope.runTest {
+            val isVisible by collectLastValue(underTest.isNotifIconContainerVisible)
+            runCurrent()
+            keyguardTransitionRepository.sendTransitionSteps(
+                from = KeyguardState.AOD,
+                to = KeyguardState.LOCKSCREEN,
+                testScope,
+            )
+            notificationsKeyguardInteractor.setPulseExpanding(false)
+            deviceEntryRepository.setBypassEnabled(false)
+            whenever(dozeParameters.alwaysOn).thenReturn(true)
+            whenever(dozeParameters.displayNeedsBlanking).thenReturn(false)
+            notificationsKeyguardInteractor.setNotificationsFullyHidden(true)
+            runCurrent()
+
+            assertThat(isVisible?.value).isFalse()
+            assertThat(isVisible?.isAnimating).isTrue()
+        }
+
+    @Test
     fun isIconContainerVisible_stopAnimation() =
         testScope.runTest {
             val isVisible by collectLastValue(underTest.isNotifIconContainerVisible)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
index 3db9ef1..bca83f0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
@@ -257,7 +257,6 @@
 
     private fun createLockscreenSceneViewModel(): LockscreenSceneViewModel {
         return LockscreenSceneViewModel(
-            applicationScope = testScope.backgroundScope,
             deviceEntryInteractor = kosmos.deviceEntryInteractor,
             communalInteractor = kosmos.communalInteractor,
             touchHandling =
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGlanceableHubTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGlanceableHubTransitionViewModelTest.kt
index 68a7b7e..a60a486 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGlanceableHubTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGlanceableHubTransitionViewModelTest.kt
@@ -16,6 +16,8 @@
 
 package com.android.systemui.keyguard.ui.viewmodel
 
+import android.content.res.Configuration
+import android.util.LayoutDirection
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
@@ -34,6 +36,9 @@
 import kotlinx.coroutines.test.runTest
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.mockito.Mockito.mock
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
@@ -82,6 +87,9 @@
                 R.dimen.lockscreen_to_hub_transition_lockscreen_translation_x,
                 -100
             )
+            val configuration = mock<Configuration>()
+            whenever(configuration.layoutDirection).thenReturn(LayoutDirection.LTR)
+            configurationRepository.onConfigurationChange(configuration)
             val values by collectValues(underTest.keyguardTranslationX)
             assertThat(values).isEmpty()
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt
index 6ce7e88..e6ea64f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt
@@ -201,6 +201,24 @@
             assertThat(actual).isEqualTo(0f)
         }
 
+    @Test
+    fun deviceEntryParentViewAlpha_shadeNotExpanded_onCancel() =
+        testScope.runTest {
+            val actual by collectLastValue(underTest.deviceEntryParentViewAlpha)
+            shadeExpanded(false)
+            runCurrent()
+
+            // START transition
+            repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+            assertThat(actual).isEqualTo(1f)
+
+            // WHEN transition is canceled
+            repository.sendTransitionStep(step(1f, TransitionState.CANCELED))
+
+            // THEN alpha is immediately set to 0f
+            assertThat(actual).isEqualTo(0f)
+        }
+
     private fun step(
         value: Float,
         state: TransitionState = TransitionState.RUNNING,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/power/domain/interactor/PowerInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/power/domain/interactor/PowerInteractorTest.kt
similarity index 76%
rename from packages/SystemUI/tests/src/com/android/systemui/power/domain/interactor/PowerInteractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/power/domain/interactor/PowerInteractorTest.kt
index 12c9eb9..53dec69 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/power/domain/interactor/PowerInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/power/domain/interactor/PowerInteractorTest.kt
@@ -21,22 +21,23 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.camera.cameraGestureHelper
 import com.android.systemui.classifier.FalsingCollector
+import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
-import com.android.systemui.power.shared.model.WakeSleepReason
-import com.android.systemui.power.shared.model.WakefulnessState
+import com.android.systemui.kosmos.testScope
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.power.data.repository.FakePowerRepository
+import com.android.systemui.power.shared.model.WakeSleepReason
+import com.android.systemui.power.shared.model.WakefulnessState
 import com.android.systemui.statusbar.phone.ScreenOffAnimationController
+import com.android.systemui.testKosmos
+import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.whenever
 import com.google.common.truth.Truth.assertThat
 import junit.framework.Assert.assertFalse
 import junit.framework.Assert.assertTrue
-import kotlin.test.assertEquals
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.flow.launchIn
-import kotlinx.coroutines.flow.onEach
-import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.test.runTest
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -47,6 +48,9 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class PowerInteractorTest : SysuiTestCase() {
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
+    private val cameraGestureHelper = kosmos.cameraGestureHelper
 
     private lateinit var underTest: PowerInteractor
     private lateinit var repository: FakePowerRepository
@@ -66,33 +70,30 @@
                 falsingCollector,
                 screenOffAnimationController,
                 statusBarStateController,
+                { cameraGestureHelper },
             )
+
+        whenever(cameraGestureHelper.canCameraGestureBeLaunched(any())).thenReturn(true)
     }
 
     @Test
     fun isInteractive_screenTurnsOff() =
-        runBlocking(IMMEDIATE) {
+        testScope.runTest {
             repository.setInteractive(true)
-            var value: Boolean? = null
-            val job = underTest.isInteractive.onEach { value = it }.launchIn(this)
+            val isInteractive by collectLastValue(underTest.isInteractive)
 
             repository.setInteractive(false)
-
-            assertThat(value).isFalse()
-            job.cancel()
+            assertThat(isInteractive).isFalse()
         }
 
     @Test
     fun isInteractive_becomesInteractive() =
-        runBlocking(IMMEDIATE) {
+        testScope.runTest {
             repository.setInteractive(false)
-            var value: Boolean? = null
-            val job = underTest.isInteractive.onEach { value = it }.launchIn(this)
+            val isInteractive by collectLastValue(underTest.isInteractive)
 
             repository.setInteractive(true)
-
-            assertThat(value).isTrue()
-            job.cancel()
+            assertThat(isInteractive).isTrue()
         }
 
     @Test
@@ -203,6 +204,23 @@
     }
 
     @Test
+    fun onCameraLaunchGestureDetected_isNotTrueWhenCannotLaunch() {
+        whenever(cameraGestureHelper.canCameraGestureBeLaunched(any())).thenReturn(false)
+        underTest.onStartedWakingUp(
+            PowerManager.WAKE_REASON_POWER_BUTTON,
+            /*powerButtonLaunchGestureTriggeredDuringSleep= */ false
+        )
+        underTest.onFinishedWakingUp()
+        underTest.onCameraLaunchGestureDetected()
+
+        assertThat(repository.wakefulness.value.internalWakefulnessState)
+            .isEqualTo(WakefulnessState.AWAKE)
+        assertThat(repository.wakefulness.value.lastWakeReason)
+            .isEqualTo(WakeSleepReason.POWER_BUTTON)
+        assertFalse(repository.wakefulness.value.powerButtonLaunchGestureTriggered)
+    }
+
+    @Test
     fun onCameraLaunchGestureDetected_maintainsAllOtherState() {
         underTest.onStartedWakingUp(
             PowerManager.WAKE_REASON_POWER_BUTTON,
@@ -211,8 +229,10 @@
         underTest.onFinishedWakingUp()
         underTest.onCameraLaunchGestureDetected()
 
-        assertEquals(WakefulnessState.AWAKE, repository.wakefulness.value.internalWakefulnessState)
-        assertEquals(WakeSleepReason.POWER_BUTTON, repository.wakefulness.value.lastWakeReason)
+        assertThat(repository.wakefulness.value.internalWakefulnessState)
+            .isEqualTo(WakefulnessState.AWAKE)
+        assertThat(repository.wakefulness.value.lastWakeReason)
+            .isEqualTo(WakeSleepReason.POWER_BUTTON)
         assertTrue(repository.wakefulness.value.powerButtonLaunchGestureTriggered)
     }
 
@@ -221,21 +241,23 @@
         underTest.onCameraLaunchGestureDetected()
         // Ensure that the 'false' here does not clear the direct launch detection call earlier.
         // This state should only be reset onStartedGoingToSleep.
-        underTest.onFinishedGoingToSleep(/*powerButtonLaunchGestureTriggeredDuringSleep= */ false)
+        underTest.onFinishedGoingToSleep(/* powerButtonLaunchGestureTriggeredDuringSleep= */ false)
         underTest.onStartedWakingUp(
             PowerManager.WAKE_REASON_POWER_BUTTON,
             /*powerButtonLaunchGestureTriggeredDuringSleep= */ false
         )
         underTest.onFinishedWakingUp()
 
-        assertEquals(WakefulnessState.AWAKE, repository.wakefulness.value.internalWakefulnessState)
-        assertEquals(WakeSleepReason.POWER_BUTTON, repository.wakefulness.value.lastWakeReason)
+        assertThat(repository.wakefulness.value.internalWakefulnessState)
+            .isEqualTo(WakefulnessState.AWAKE)
+        assertThat(repository.wakefulness.value.lastWakeReason)
+            .isEqualTo(WakeSleepReason.POWER_BUTTON)
         assertTrue(repository.wakefulness.value.powerButtonLaunchGestureTriggered)
     }
 
     @Test
     fun cameraLaunchDetectedOnGoingToSleep_stillTrue_ifGestureNotDetectedOnWakingUp() {
-        underTest.onFinishedGoingToSleep(/*powerButtonLaunchGestureTriggeredDuringSleep= */ true)
+        underTest.onFinishedGoingToSleep(/* powerButtonLaunchGestureTriggeredDuringSleep= */ true)
         // Ensure that the 'false' here does not clear the direct launch detection call earlier.
         // This state should only be reset onStartedGoingToSleep.
         underTest.onStartedWakingUp(
@@ -244,12 +266,10 @@
         )
         underTest.onFinishedWakingUp()
 
-        assertEquals(WakefulnessState.AWAKE, repository.wakefulness.value.internalWakefulnessState)
-        assertEquals(WakeSleepReason.POWER_BUTTON, repository.wakefulness.value.lastWakeReason)
+        assertThat(repository.wakefulness.value.internalWakefulnessState)
+            .isEqualTo(WakefulnessState.AWAKE)
+        assertThat(repository.wakefulness.value.lastWakeReason)
+            .isEqualTo(WakeSleepReason.POWER_BUTTON)
         assertTrue(repository.wakefulness.value.powerButtonLaunchGestureTriggered)
     }
-
-    companion object {
-        private val IMMEDIATE = Dispatchers.Main.immediate
-    }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/GridConsistencyInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/GridConsistencyInteractorTest.kt
index 56b3679..42db96e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/GridConsistencyInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/GridConsistencyInteractorTest.kt
@@ -23,8 +23,8 @@
 import com.android.systemui.qs.panels.data.repository.DefaultLargeTilesRepository
 import com.android.systemui.qs.panels.data.repository.defaultLargeTilesRepository
 import com.android.systemui.qs.panels.data.repository.gridLayoutTypeRepository
+import com.android.systemui.qs.panels.shared.model.GridLayoutType
 import com.android.systemui.qs.panels.shared.model.InfiniteGridLayoutType
-import com.android.systemui.qs.panels.shared.model.PartitionedGridLayoutType
 import com.android.systemui.qs.pipeline.data.repository.tileSpecRepository
 import com.android.systemui.qs.pipeline.domain.interactor.currentTilesInteractor
 import com.android.systemui.qs.pipeline.shared.TileSpec
@@ -42,6 +42,8 @@
 @RunWith(AndroidJUnit4::class)
 class GridConsistencyInteractorTest : SysuiTestCase() {
 
+    data object NoopGridLayoutType : GridLayoutType
+
     private val kosmos =
         testKosmos().apply {
             defaultLargeTilesRepository =
@@ -54,6 +56,11 @@
                             TileSpec.create("largeD"),
                         )
                 }
+            gridConsistencyInteractorsMap =
+                mapOf(
+                    Pair(NoopGridLayoutType, noopGridConsistencyInteractor),
+                    Pair(InfiniteGridLayoutType, infiniteGridConsistencyInteractor)
+                )
         }
 
     private val underTest = with(kosmos) { gridConsistencyInteractor }
@@ -71,7 +78,7 @@
         with(kosmos) {
             testScope.runTest {
                 // Using the no-op grid consistency interactor
-                gridLayoutTypeRepository.setLayout(PartitionedGridLayoutType)
+                gridLayoutTypeRepository.setLayout(NoopGridLayoutType)
 
                 // Setting an invalid layout with holes
                 // [ Large A ] [ sa ]
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/DragAndDropStateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/DragAndDropStateTest.kt
index 73a0039..45262ca 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/DragAndDropStateTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/DragAndDropStateTest.kt
@@ -40,21 +40,19 @@
         TestEditTiles.forEach { assertThat(underTest.isMoving(it.tileSpec)).isFalse() }
 
         // Start the drag movement
-        val movingTileSpec = TestEditTiles[0].tileSpec
-        underTest.onStarted(movingTileSpec)
+        underTest.onStarted(TestEditTiles[0])
 
         // Assert that the correct tile is marked as moving
         TestEditTiles.forEach {
-            assertThat(underTest.isMoving(it.tileSpec)).isEqualTo(movingTileSpec == it.tileSpec)
+            assertThat(underTest.isMoving(it.tileSpec))
+                .isEqualTo(TestEditTiles[0].tileSpec == it.tileSpec)
         }
     }
 
     @Test
     fun onMoved_updatesList() {
-        val movingTileSpec = TestEditTiles[0].tileSpec
-
         // Start the drag movement
-        underTest.onStarted(movingTileSpec)
+        underTest.onStarted(TestEditTiles[0])
 
         // Move the tile to the end of the list
         underTest.onMoved(listState.tiles[5].tileSpec)
@@ -67,10 +65,8 @@
 
     @Test
     fun onDrop_resetsMovingTile() {
-        val movingTileSpec = TestEditTiles[0].tileSpec
-
         // Start the drag movement
-        underTest.onStarted(movingTileSpec)
+        underTest.onStarted(TestEditTiles[0])
 
         // Move the tile to the end of the list
         underTest.onMoved(listState.tiles[5].tileSpec)
@@ -84,16 +80,15 @@
 
     @Test
     fun onMoveOutOfBounds_removeMovingTileFromCurrentList() {
-        val movingTileSpec = TestEditTiles[0].tileSpec
-
         // Start the drag movement
-        underTest.onStarted(movingTileSpec)
+        underTest.onStarted(TestEditTiles[0])
 
         // Move the tile outside of the list
         underTest.movedOutOfBounds()
 
         // Asserts the moving tile is not current
-        assertThat(listState.tiles.first { it.tileSpec == movingTileSpec }.isCurrent).isFalse()
+        assertThat(listState.tiles.firstOrNull { it.tileSpec == TestEditTiles[0].tileSpec })
+            .isNull()
     }
 
     companion object {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/EditTileListStateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/EditTileListStateTest.kt
index 517b601..e76d389 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/EditTileListStateTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/EditTileListStateTest.kt
@@ -33,22 +33,25 @@
     val underTest = EditTileListState(TestEditTiles)
 
     @Test
-    fun movingNonExistentTile_listUnchanged() {
-        underTest.move(TileSpec.create("other_tile"), TestEditTiles[0].tileSpec)
+    fun movingNonExistentTile_tileAdded() {
+        val newTile = createEditTile("other_tile", false)
+        underTest.move(newTile, TestEditTiles[0].tileSpec)
 
-        assertThat(underTest.tiles).containsExactly(*TestEditTiles.toTypedArray())
+        assertThat(underTest.tiles[0]).isEqualTo(newTile)
+        assertThat(underTest.tiles.subList(1, underTest.tiles.size))
+            .containsExactly(*TestEditTiles.toTypedArray())
     }
 
     @Test
     fun movingTileToNonExistentTarget_listUnchanged() {
-        underTest.move(TestEditTiles[0].tileSpec, TileSpec.create("other_tile"))
+        underTest.move(TestEditTiles[0], TileSpec.create("other_tile"))
 
         assertThat(underTest.tiles).containsExactly(*TestEditTiles.toTypedArray())
     }
 
     @Test
     fun movingTileToItself_listUnchanged() {
-        underTest.move(TestEditTiles[0].tileSpec, TestEditTiles[0].tileSpec)
+        underTest.move(TestEditTiles[0], TestEditTiles[0].tileSpec)
 
         assertThat(underTest.tiles).containsExactly(*TestEditTiles.toTypedArray())
     }
@@ -56,7 +59,7 @@
     @Test
     fun movingTileToSameSection_listUpdates() {
         // Move tile at index 0 to index 1. Tile 0 should remain current.
-        underTest.move(TestEditTiles[0].tileSpec, TestEditTiles[1].tileSpec)
+        underTest.move(TestEditTiles[0], TestEditTiles[1].tileSpec)
 
         // Assert the tiles 0 and 1 have changed places.
         assertThat(underTest.tiles[0]).isEqualTo(TestEditTiles[1])
@@ -67,22 +70,12 @@
             .containsExactly(*TestEditTiles.subList(2, 5).toTypedArray())
     }
 
-    @Test
-    fun movingTileToDifferentSection_listAndTileUpdates() {
-        // Move tile at index 0 to index 3. Tile 0 should no longer be current.
-        underTest.move(TestEditTiles[0].tileSpec, TestEditTiles[3].tileSpec)
+    fun removingTile_listUpdates() {
+        // Remove tile at index 0
+        underTest.remove(TestEditTiles[0].tileSpec)
 
-        // Assert tile 0 is now at index 3 and is no longer current.
-        assertThat(underTest.tiles[3]).isEqualTo(TestEditTiles[0].copy(isCurrent = false))
-
-        // Assert previous tiles have shifted places
-        assertThat(underTest.tiles[0]).isEqualTo(TestEditTiles[1])
-        assertThat(underTest.tiles[1]).isEqualTo(TestEditTiles[2])
-        assertThat(underTest.tiles[2]).isEqualTo(TestEditTiles[3])
-
-        // Assert the rest of the list is unchanged
-        assertThat(underTest.tiles.subList(4, 5))
-            .containsExactly(*TestEditTiles.subList(4, 5).toTypedArray())
+        // Assert the tile was removed
+        assertThat(underTest.tiles).containsExactly(*TestEditTiles.subList(1, 6).toTypedArray())
     }
 
     companion object {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/PartitionedGridLayoutTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/PartitionedGridLayoutTest.kt
deleted file mode 100644
index 55b7454..0000000
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/PartitionedGridLayoutTest.kt
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.qs.panels.ui.compose
-
-import androidx.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.qs.panels.data.repository.DefaultLargeTilesRepository
-import com.android.systemui.qs.panels.data.repository.defaultLargeTilesRepository
-import com.android.systemui.qs.panels.ui.viewmodel.MockTileViewModel
-import com.android.systemui.qs.panels.ui.viewmodel.iconTilesViewModel
-import com.android.systemui.qs.panels.ui.viewmodel.partitionedGridViewModel
-import com.android.systemui.qs.pipeline.shared.TileSpec
-import com.android.systemui.testKosmos
-import com.google.common.truth.Truth
-import kotlinx.coroutines.test.runTest
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@SmallTest
-@RunWith(AndroidJUnit4::class)
-class PartitionedGridLayoutTest : SysuiTestCase() {
-    private val kosmos =
-        testKosmos().apply {
-            defaultLargeTilesRepository =
-                object : DefaultLargeTilesRepository {
-                    override val defaultLargeTiles: Set<TileSpec> = setOf(TileSpec.create("large"))
-                }
-        }
-
-    private val underTest = with(kosmos) { PartitionedGridLayout(partitionedGridViewModel) }
-
-    @Test
-    fun correctPagination_underOnePage_partitioned_sameRelativeOrder() =
-        with(kosmos) {
-            testScope.runTest {
-                val rows = 3
-                val columns = 4
-
-                val tiles =
-                    listOf(
-                        largeTile(),
-                        smallTile(),
-                        smallTile(),
-                        largeTile(),
-                        largeTile(),
-                        smallTile()
-                    )
-                val (smallTiles, largeTiles) =
-                    tiles.partition { iconTilesViewModel.isIconTile(it.spec) }
-
-                // [L L] [L L]
-                // [L L]
-                // [S] [S] [S]
-
-                val pages = underTest.splitIntoPages(tiles, rows = rows, columns = columns)
-
-                Truth.assertThat(pages).hasSize(1)
-                Truth.assertThat(pages[0]).isEqualTo(largeTiles + smallTiles)
-            }
-        }
-
-    @Test
-    fun correctPagination_twoPages_partitioned_sameRelativeOrder() =
-        with(kosmos) {
-            testScope.runTest {
-                val rows = 3
-                val columns = 4
-
-                val tiles =
-                    listOf(
-                        largeTile(),
-                        smallTile(),
-                        smallTile(),
-                        largeTile(),
-                        smallTile(),
-                        smallTile(),
-                        largeTile(),
-                        smallTile(),
-                        smallTile(),
-                    )
-                // --- Page 1 ---
-                // [L L] [L L]
-                // [L L]
-                // [S] [S] [S] [S]
-                // --- Page 2 ---
-                // [S] [S]
-
-                val (smallTiles, largeTiles) =
-                    tiles.partition { iconTilesViewModel.isIconTile(it.spec) }
-
-                val pages = underTest.splitIntoPages(tiles, rows = rows, columns = columns)
-
-                val expectedPage0 = largeTiles + smallTiles.take(4)
-                val expectedPage1 = smallTiles.drop(4)
-
-                Truth.assertThat(pages).hasSize(2)
-                Truth.assertThat(pages[0]).isEqualTo(expectedPage0)
-                Truth.assertThat(pages[1]).isEqualTo(expectedPage1)
-            }
-        }
-
-    companion object {
-        fun largeTile() = MockTileViewModel(TileSpec.create("large"))
-
-        fun smallTile() = MockTileViewModel(TileSpec.create("small"))
-    }
-}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt
index 6ad4b31..3146318 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt
@@ -675,6 +675,24 @@
             assertThat(tiles!!.size).isEqualTo(3)
         }
 
+    @Test
+    fun changeInPackagesTiles_doesntTriggerUserChange_logged() =
+        testScope.runTest(USER_INFO_0) {
+            val specs =
+                listOf(
+                    TileSpec.create("a"),
+                )
+            tileSpecRepository.setTiles(USER_INFO_0.id, specs)
+            runCurrent()
+            // Settled on the same list of tiles.
+            assertThat(underTest.currentTilesSpecs).isEqualTo(specs)
+
+            installedTilesPackageRepository.setInstalledPackagesForUser(USER_INFO_0.id, emptySet())
+            runCurrent()
+
+            verify(logger, never()).logTileUserChanged(TileSpec.create("a"), 0)
+        }
+
     private fun QSTile.State.fillIn(state: Int, label: CharSequence, secondaryLabel: CharSequence) {
         this.state = state
         this.label = label
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractorTest.kt
index 09dca25..69b8ee1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractorTest.kt
@@ -22,11 +22,14 @@
 import android.platform.test.annotations.EnableFlags
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.settingslib.notification.data.repository.FakeZenModeRepository
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectValues
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
 import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
 import com.android.systemui.qs.tiles.impl.modes.domain.model.ModesTileModel
+import com.android.systemui.statusbar.policy.data.repository.fakeZenModeRepository
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.flowOf
@@ -40,51 +43,72 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class ModesTileDataInteractorTest : SysuiTestCase() {
-    private val zenModeRepository = FakeZenModeRepository()
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
+    private val dispatcher = kosmos.testDispatcher
+    private val zenModeRepository = kosmos.fakeZenModeRepository
 
-    private val underTest = ModesTileDataInteractor(zenModeRepository)
+    private val underTest = ModesTileDataInteractor(zenModeRepository, dispatcher)
 
     @EnableFlags(Flags.FLAG_MODES_UI)
     @Test
-    fun availableWhenFlagIsOn() = runTest {
-        val availability = underTest.availability(TEST_USER).toCollection(mutableListOf())
+    fun availableWhenFlagIsOn() =
+        testScope.runTest {
+            val availability = underTest.availability(TEST_USER).toCollection(mutableListOf())
 
-        assertThat(availability).containsExactly(true)
-    }
+            assertThat(availability).containsExactly(true)
+        }
 
     @DisableFlags(Flags.FLAG_MODES_UI)
     @Test
-    fun unavailableWhenFlagIsOff() = runTest {
-        val availability = underTest.availability(TEST_USER).toCollection(mutableListOf())
+    fun unavailableWhenFlagIsOff() =
+        testScope.runTest {
+            val availability = underTest.availability(TEST_USER).toCollection(mutableListOf())
 
-        assertThat(availability).containsExactly(false)
-    }
+            assertThat(availability).containsExactly(false)
+        }
 
     @EnableFlags(Flags.FLAG_MODES_UI)
     @Test
-    fun isActivatedWhenModesChange() = runTest {
-        val dataList: List<ModesTileModel> by
-            collectValues(underTest.tileData(TEST_USER, flowOf(DataUpdateTrigger.InitialRequest)))
-        runCurrent()
-        assertThat(dataList.map { it.isActivated }).containsExactly(false).inOrder()
+    fun isActivatedWhenModesChange() =
+        testScope.runTest {
+            val dataList: List<ModesTileModel> by
+                collectValues(
+                    underTest.tileData(TEST_USER, flowOf(DataUpdateTrigger.InitialRequest))
+                )
+            runCurrent()
+            assertThat(dataList.map { it.isActivated }).containsExactly(false).inOrder()
 
-        // Add active mode
-        zenModeRepository.addMode(id = "One", active = true)
-        runCurrent()
-        assertThat(dataList.map { it.isActivated }).containsExactly(false, true).inOrder()
+            // Add active mode
+            zenModeRepository.addMode(id = "One", active = true)
+            runCurrent()
+            assertThat(dataList.map { it.isActivated }).containsExactly(false, true).inOrder()
+            assertThat(dataList.map { it.activeModes }.last()).containsExactly("Mode One")
 
-        // Add another mode: state hasn't changed, so this shouldn't cause another emission
-        zenModeRepository.addMode(id = "Two", active = true)
-        runCurrent()
-        assertThat(dataList.map { it.isActivated }).containsExactly(false, true).inOrder()
+            // Add an inactive mode: state hasn't changed, so this shouldn't cause another emission
+            zenModeRepository.addMode(id = "Two", active = false)
+            runCurrent()
+            assertThat(dataList.map { it.isActivated }).containsExactly(false, true).inOrder()
+            assertThat(dataList.map { it.activeModes }.last()).containsExactly("Mode One")
 
-        // Remove a mode and disable the other
-        zenModeRepository.removeMode("One")
-        runCurrent()
-        zenModeRepository.deactivateMode("Two")
-        runCurrent()
-        assertThat(dataList.map { it.isActivated }).containsExactly(false, true, false).inOrder()
-    }
+            // Add another active mode
+            zenModeRepository.addMode(id = "Three", active = true)
+            runCurrent()
+            assertThat(dataList.map { it.isActivated }).containsExactly(false, true, true).inOrder()
+            assertThat(dataList.map { it.activeModes }.last())
+                .containsExactly("Mode One", "Mode Three")
+                .inOrder()
+
+            // Remove a mode and deactivate the other
+            zenModeRepository.removeMode("One")
+            runCurrent()
+            zenModeRepository.deactivateMode("Three")
+            runCurrent()
+            assertThat(dataList.map { it.isActivated })
+                .containsExactly(false, true, true, true, false)
+                .inOrder()
+            assertThat(dataList.map { it.activeModes }.last()).isEmpty()
+        }
 
     private companion object {
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractorTest.kt
index d5c9102..4b75649 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractorTest.kt
@@ -21,64 +21,76 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.animation.DialogTransitionAnimator
-import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler
+import com.android.systemui.animation.Expandable
 import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandlerSubject
+import com.android.systemui.qs.tiles.base.actions.qsTileIntentUserInputHandler
 import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx
 import com.android.systemui.qs.tiles.impl.modes.domain.model.ModesTileModel
-import com.android.systemui.statusbar.phone.SystemUIDialog
-import com.android.systemui.statusbar.policy.ui.dialog.ModesDialogDelegate
-import com.google.common.truth.Truth
-import kotlin.coroutines.EmptyCoroutineContext
+import com.android.systemui.statusbar.policy.ui.dialog.mockModesDialogDelegate
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.test.runTest
-import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.mockito.Mock
-import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
 import org.mockito.kotlin.verify
-import org.mockito.kotlin.whenever
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 @EnableFlags(android.app.Flags.FLAG_MODES_UI)
 class ModesTileUserActionInteractorTest : SysuiTestCase() {
-    private val inputHandler = FakeQSTileIntentUserInputHandler()
+    private val kosmos = testKosmos()
+    private val inputHandler = kosmos.qsTileIntentUserInputHandler
+    private val mockDialogDelegate = kosmos.mockModesDialogDelegate
 
-    @Mock private lateinit var dialogTransitionAnimator: DialogTransitionAnimator
-    @Mock private lateinit var dialogDelegate: ModesDialogDelegate
-    @Mock private lateinit var mockDialog: SystemUIDialog
+    private val underTest =
+        ModesTileUserActionInteractor(
+            inputHandler,
+            mockDialogDelegate,
+        )
 
-    private lateinit var underTest: ModesTileUserActionInteractor
-
-    @Before
-    fun setup() {
-        MockitoAnnotations.initMocks(this)
-
-        whenever(dialogDelegate.createDialog()).thenReturn(mockDialog)
-
-        underTest =
-            ModesTileUserActionInteractor(
-                EmptyCoroutineContext,
-                inputHandler,
-                dialogTransitionAnimator,
-                dialogDelegate,
+    @Test
+    fun handleClick_active() = runTest {
+        val expandable = mock<Expandable>()
+        underTest.handleInput(
+            QSTileInputTestKtx.click(
+                data = ModesTileModel(true, listOf("DND")),
+                expandable = expandable
             )
+        )
+
+        verify(mockDialogDelegate).showDialog(eq(expandable))
     }
 
     @Test
-    fun handleClick() = runTest {
-        underTest.handleInput(QSTileInputTestKtx.click(ModesTileModel(false)))
+    fun handleClick_inactive() = runTest {
+        val expandable = mock<Expandable>()
+        underTest.handleInput(
+            QSTileInputTestKtx.click(
+                data = ModesTileModel(false, emptyList()),
+                expandable = expandable
+            )
+        )
 
-        verify(mockDialog).show()
+        verify(mockDialogDelegate).showDialog(eq(expandable))
     }
 
     @Test
-    fun handleLongClick() = runTest {
-        underTest.handleInput(QSTileInputTestKtx.longClick(ModesTileModel(false)))
+    fun handleLongClick_active() = runTest {
+        underTest.handleInput(QSTileInputTestKtx.longClick(ModesTileModel(true, listOf("DND"))))
 
         QSTileIntentUserInputHandlerSubject.assertThat(inputHandler).handledOneIntentInput {
-            Truth.assertThat(it.intent.action).isEqualTo(Settings.ACTION_ZEN_MODE_SETTINGS)
+            assertThat(it.intent.action).isEqualTo(Settings.ACTION_ZEN_MODE_SETTINGS)
+        }
+    }
+
+    @Test
+    fun handleLongClick_inactive() = runTest {
+        underTest.handleInput(QSTileInputTestKtx.longClick(ModesTileModel(false, emptyList())))
+
+        QSTileIntentUserInputHandlerSubject.assertThat(inputHandler).handledOneIntentInput {
+            assertThat(it.intent.action).isEqualTo(Settings.ACTION_ZEN_MODE_SETTINGS)
         }
     }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapperTest.kt
index 3baf2f4..dd9711e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapperTest.kt
@@ -54,21 +54,35 @@
 
     @Test
     fun inactiveState() {
-        val model = ModesTileModel(isActivated = false)
+        val model = ModesTileModel(isActivated = false, activeModes = emptyList())
 
         val state = underTest.map(config, model)
 
         assertThat(state.activationState).isEqualTo(QSTileState.ActivationState.INACTIVE)
         assertThat(state.iconRes).isEqualTo(R.drawable.qs_dnd_icon_off)
+        assertThat(state.secondaryLabel).isEqualTo("No active modes")
     }
 
     @Test
-    fun activeState() {
-        val model = ModesTileModel(isActivated = true)
+    fun activeState_oneMode() {
+        val model = ModesTileModel(isActivated = true, activeModes = listOf("DND"))
 
         val state = underTest.map(config, model)
 
         assertThat(state.activationState).isEqualTo(QSTileState.ActivationState.ACTIVE)
         assertThat(state.iconRes).isEqualTo(R.drawable.qs_dnd_icon_on)
+        assertThat(state.secondaryLabel).isEqualTo("DND is active")
+    }
+
+    @Test
+    fun activeState_multipleModes() {
+        val model =
+            ModesTileModel(isActivated = true, activeModes = listOf("Mode 1", "Mode 2", "Mode 3"))
+
+        val state = underTest.map(config, model)
+
+        assertThat(state.activationState).isEqualTo(QSTileState.ActivationState.ACTIVE)
+        assertThat(state.iconRes).isEqualTo(R.drawable.qs_dnd_icon_on)
+        assertThat(state.secondaryLabel).isEqualTo("3 modes are active")
     }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/adapter/QSSceneAdapterImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/adapter/QSSceneAdapterImplTest.kt
index 09580c5..d472d98 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/adapter/QSSceneAdapterImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/adapter/QSSceneAdapterImplTest.kt
@@ -24,9 +24,11 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.domain.interactor.displayStateInteractor
 import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
 import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
 import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.display.data.repository.displayStateRepository
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testCase
@@ -117,6 +119,7 @@
         }
 
     private val shadeInteractor = kosmos.shadeInteractor
+    private val displayStateInteractor = kosmos.displayStateInteractor
     private val dumpManager = mock<DumpManager>()
 
     private val underTest =
@@ -124,6 +127,7 @@
             qsSceneComponentFactory,
             qsImplProvider,
             shadeInteractor,
+            displayStateInteractor,
             dumpManager,
             testDispatcher,
             testScope.backgroundScope,
@@ -583,6 +587,25 @@
         }
 
     @Test
+    fun setIsNotificationPanelFullWidth() =
+        testScope.runTest {
+            val qsImpl by collectLastValue(underTest.qsImpl)
+
+            underTest.inflate(context)
+            runCurrent()
+
+            kosmos.displayStateRepository.setIsLargeScreen(true)
+            runCurrent()
+
+            verify(qsImpl!!).setIsNotificationPanelFullWidth(false)
+
+            underTest.inflate(context)
+            runCurrent()
+
+            verify(qsImpl!!).setIsNotificationPanelFullWidth(false)
+        }
+
+    @Test
     fun setBrightnessMirrorController() =
         testScope.runTest {
             val qsImpl by collectLastValue(underTest.qsImpl)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt
index 9249621..3ca802e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt
@@ -93,7 +93,6 @@
         sceneContainerStartable.start()
         underTest =
             QuickSettingsSceneViewModel(
-                applicationScope = testScope.backgroundScope,
                 brightnessMirrorViewModel = kosmos.brightnessMirrorViewModel,
                 shadeHeaderViewModel = kosmos.shadeHeaderViewModel,
                 qsSceneAdapter = qsFlexiglassAdapter,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
index 39b3662..228d61a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
@@ -143,7 +143,6 @@
 
     private val lockscreenSceneViewModel by lazy {
         LockscreenSceneViewModel(
-            applicationScope = testScope.backgroundScope,
             deviceEntryInteractor = deviceEntryInteractor,
             communalInteractor = communalInteractor,
             touchHandling =
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
index 540a85a..f26c39d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
@@ -78,6 +78,7 @@
 import com.android.systemui.statusbar.phone.centralSurfaces
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepository
 import com.android.systemui.statusbar.policy.data.repository.fakeDeviceProvisioningRepository
+import com.android.systemui.statusbar.sysuiStatusBarStateController
 import com.android.systemui.testKosmos
 import com.android.systemui.util.mockito.mock
 import com.google.common.truth.Truth.assertThat
@@ -274,9 +275,10 @@
         }
 
     @Test
-    fun switchFromBouncerToQuickSettingsWhenDeviceUnlocked() =
+    fun switchFromBouncerToQuickSettingsWhenDeviceUnlocked_whenLeaveOpenShade() =
         testScope.runTest {
             val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
+            kosmos.sysuiStatusBarStateController.leaveOpen = true // leave shade open
 
             val transitionState =
                 prepareState(
@@ -306,6 +308,39 @@
         }
 
     @Test
+    fun switchFromBouncerToGoneWhenDeviceUnlocked_whenDoNotLeaveOpenShade() =
+        testScope.runTest {
+            val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
+            kosmos.sysuiStatusBarStateController.leaveOpen = false // don't leave shade open
+
+            val transitionState =
+                prepareState(
+                    authenticationMethod = AuthenticationMethodModel.Pin,
+                    isDeviceUnlocked = false,
+                    initialSceneKey = Scenes.Lockscreen,
+                )
+            assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
+            underTest.start()
+            runCurrent()
+
+            sceneInteractor.changeScene(Scenes.QuickSettings, "switching to qs for test")
+            transitionState.value = ObservableTransitionState.Idle(Scenes.QuickSettings)
+            runCurrent()
+            assertThat(currentSceneKey).isEqualTo(Scenes.QuickSettings)
+
+            sceneInteractor.changeScene(Scenes.Bouncer, "switching to bouncer for test")
+            transitionState.value = ObservableTransitionState.Idle(Scenes.Bouncer)
+            runCurrent()
+            assertThat(currentSceneKey).isEqualTo(Scenes.Bouncer)
+
+            kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus(
+                SuccessFingerprintAuthenticationStatus(0, true)
+            )
+
+            assertThat(currentSceneKey).isEqualTo(Scenes.Gone)
+        }
+
+    @Test
     fun switchFromLockscreenToGoneWhenDeviceUnlocksWithBypassOn() =
         testScope.runTest {
             val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/GoneSceneViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/GoneSceneViewModelTest.kt
index 0ab6a82..a4992e2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/GoneSceneViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/GoneSceneViewModelTest.kt
@@ -53,7 +53,6 @@
     fun setUp() {
         underTest =
             GoneSceneViewModel(
-                applicationScope = testScope.backgroundScope,
                 shadeInteractor = kosmos.shadeInteractor,
             )
     }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
index 636d5a7..40fb769 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
@@ -46,6 +46,7 @@
 
 import androidx.test.filters.SmallTest;
 
+import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
 import com.android.internal.colorextraction.ColorExtractor;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.biometrics.AuthController;
@@ -89,7 +90,7 @@
 @RunWithLooper(setAsMainLooper = true)
 @SmallTest
 public class NotificationShadeWindowControllerImplTest extends SysuiTestCase {
-    @Mock private WindowManager mWindowManager;
+    @Mock private ViewCaptureAwareWindowManager mWindowManager;
     @Mock private DozeParameters mDozeParameters;
     @Spy private final NotificationShadeWindowView mNotificationShadeWindowView = spy(
             new NotificationShadeWindowView(mContext, null));
@@ -160,6 +161,7 @@
                 mShadeWindowLogger,
                 () -> mSelectedUserInteractor,
                 mUserTracker,
+                mKosmos.getNotificationShadeWindowModel(),
                 mKosmos::getCommunalInteractor) {
                     @Override
                     protected boolean isDebuggable() {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/startable/ShadeStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/startable/ShadeStartableTest.kt
index cecc70c..d37e0fb 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/startable/ShadeStartableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/startable/ShadeStartableTest.kt
@@ -27,6 +27,7 @@
 import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
 import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
 import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.display.data.repository.displayStateRepository
 import com.android.systemui.flags.EnableSceneContainer
 import com.android.systemui.flags.parameterizeSceneContainerFlag
 import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository
@@ -41,6 +42,8 @@
 import com.android.systemui.shade.domain.interactor.shadeInteractor
 import com.android.systemui.shade.shared.flag.DualShade
 import com.android.systemui.shade.shared.model.ShadeMode
+import com.android.systemui.statusbar.notification.stack.notificationStackScrollLayoutController
+import com.android.systemui.statusbar.phone.scrimController
 import com.android.systemui.testKosmos
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.mock
@@ -54,6 +57,7 @@
 import kotlinx.coroutines.test.runTest
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.mockito.kotlin.verify
 import platform.test.runner.parameterized.ParameterizedAndroidJunit4
 import platform.test.runner.parameterized.Parameters
 
@@ -167,6 +171,18 @@
             }
         }
 
+    @Test
+    @EnableSceneContainer
+    fun hydrateFullWidth() =
+        testScope.runTest {
+            underTest.start()
+
+            kosmos.displayStateRepository.setIsLargeScreen(true)
+            runCurrent()
+            verify(kosmos.notificationStackScrollLayoutController).setIsFullWidth(false)
+            assertThat(kosmos.scrimController.clipQsScrim).isFalse()
+        }
+
     private fun TestScope.changeScene(
         toScene: SceneKey,
         transitionState: MutableStateFlow<ObservableTransitionState>,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/NotificationShadeWindowModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/NotificationShadeWindowModelTest.kt
new file mode 100644
index 0000000..add33da
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/NotificationShadeWindowModelTest.kt
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.shade.ui.viewmodel
+
+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.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class NotificationShadeWindowModelTest : SysuiTestCase() {
+
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
+    private val keyguardTransitionRepository by lazy { kosmos.fakeKeyguardTransitionRepository }
+    private val underTest: NotificationShadeWindowModel by lazy {
+        kosmos.notificationShadeWindowModel
+    }
+
+    @Test
+    fun transitionToOccluded() =
+        testScope.runTest {
+            val isKeyguardOccluded by collectLastValue(underTest.isKeyguardOccluded)
+            assertThat(isKeyguardOccluded).isFalse()
+
+            keyguardTransitionRepository.sendTransitionSteps(
+                from = KeyguardState.LOCKSCREEN,
+                to = KeyguardState.OCCLUDED,
+                testScope,
+            )
+            assertThat(isKeyguardOccluded).isTrue()
+
+            keyguardTransitionRepository.sendTransitionSteps(
+                from = KeyguardState.OCCLUDED,
+                to = KeyguardState.GONE,
+                testScope,
+            )
+            assertThat(isKeyguardOccluded).isFalse()
+        }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt
index da22c6d..343b6bd 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt
@@ -25,6 +25,7 @@
 import com.android.compose.animation.scene.Swipe
 import com.android.compose.animation.scene.SwipeDirection
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.activatable.activateIn
 import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository
 import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
 import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
@@ -59,6 +60,7 @@
 import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
+import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
 
@@ -78,6 +80,11 @@
 
     private val underTest: ShadeSceneViewModel by lazy { kosmos.shadeSceneViewModel }
 
+    @Before
+    fun setUp() {
+        underTest.activateIn(testScope)
+    }
+
     @Test
     fun upTransitionSceneKey_deviceLocked_lockScreen() =
         testScope.runTest {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationStackAppearanceIntegrationTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationStackAppearanceIntegrationTest.kt
index 23b28e3..1df2553 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationStackAppearanceIntegrationTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationStackAppearanceIntegrationTest.kt
@@ -27,6 +27,8 @@
 import com.android.systemui.flags.EnableSceneContainer
 import com.android.systemui.flags.Flags
 import com.android.systemui.flags.fakeFeatureFlagsClassic
+import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
+import com.android.systemui.keyguard.shared.model.StatusBarState
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.scene.domain.interactor.sceneInteractor
 import com.android.systemui.scene.shared.model.Scenes
@@ -64,6 +66,7 @@
     private val placeholderViewModel by lazy { kosmos.notificationsPlaceholderViewModel }
     private val scrollViewModel by lazy { kosmos.notificationScrollViewModel }
     private val sceneInteractor by lazy { kosmos.sceneInteractor }
+    private val fakeKeyguardRepository by lazy { kosmos.fakeKeyguardRepository }
     private val fakeSceneDataSource by lazy { kosmos.fakeSceneDataSource }
 
     @Test
@@ -73,9 +76,11 @@
             val leftOffset = MutableStateFlow(0)
             val shape by collectLastValue(scrollViewModel.shadeScrimShape(radius, leftOffset))
 
+            // When: receive scrim bounds
             placeholderViewModel.onScrimBoundsChanged(
                 ShadeScrimBounds(left = 0f, top = 200f, right = 100f, bottom = 550f)
             )
+            // Then: shape is updated
             assertThat(shape)
                 .isEqualTo(
                     ShadeScrimShape(
@@ -86,11 +91,13 @@
                     )
                 )
 
+            // When: receive new scrim bounds
             leftOffset.value = 200
             radius.value = 24
             placeholderViewModel.onScrimBoundsChanged(
                 ShadeScrimBounds(left = 210f, top = 200f, right = 300f, bottom = 550f)
             )
+            // Then: shape is updated
             assertThat(shape)
                 .isEqualTo(
                     ShadeScrimShape(
@@ -100,6 +107,16 @@
                         bottomRadius = 0
                     )
                 )
+
+            // When: QuickSettings shows up full screen
+            fakeKeyguardRepository.setStatusBarState(StatusBarState.SHADE)
+            val transitionState =
+                MutableStateFlow<ObservableTransitionState>(
+                    ObservableTransitionState.Idle(Scenes.QuickSettings)
+                )
+            sceneInteractor.setTransitionState(transitionState)
+            // Then: shape is null
+            assertThat(shape).isNull()
         }
 
     @Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/LockScreenMinimalismCoordinatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/LockScreenMinimalismCoordinatorTest.kt
index 8810ade..7b87aeb 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/LockScreenMinimalismCoordinatorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/LockScreenMinimalismCoordinatorTest.kt
@@ -20,7 +20,6 @@
 import android.app.Notification
 import android.app.NotificationManager.IMPORTANCE_DEFAULT
 import android.app.NotificationManager.IMPORTANCE_LOW
-import android.os.UserHandle
 import android.platform.test.annotations.EnableFlags
 import android.provider.Settings
 import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -475,20 +474,6 @@
 
         val collectionListener: NotifCollectionListener =
             argumentCaptor { verify(notifPipeline).addCollectionListener(capture()) }.lastValue
-
-        var showOnlyUnseenNotifsOnKeyguardSetting: Boolean
-            get() =
-                fakeSettings.getIntForUser(
-                    Settings.Secure.LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS,
-                    UserHandle.USER_CURRENT,
-                ) == 1
-            set(value) {
-                fakeSettings.putIntForUser(
-                    Settings.Secure.LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS,
-                    if (value) 1 else 2,
-                    UserHandle.USER_CURRENT,
-                )
-            }
     }
 
     companion object {
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 6ddc074..3fd9c21 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
@@ -18,23 +18,26 @@
 package com.android.systemui.statusbar.notification.collection.coordinator
 
 import android.app.Notification
-import android.os.UserHandle
 import android.platform.test.flag.junit.FlagsParameterization
 import android.provider.Settings
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.dump.DumpManager
+import com.android.systemui.dump.dumpManager
 import com.android.systemui.flags.andSceneContainer
-import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
 import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.data.repository.keyguardRepository
+import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository
 import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.TransitionStep
-import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
 import com.android.systemui.log.logcatLogBuffer
-import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.plugins.statusbar.statusBarStateController
 import com.android.systemui.scene.data.repository.Idle
 import com.android.systemui.scene.data.repository.setTransition
+import com.android.systemui.scene.domain.interactor.sceneInteractor
 import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder
 import com.android.systemui.statusbar.notification.collection.NotifPipeline
@@ -42,12 +45,15 @@
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable
 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
-import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository
 import com.android.systemui.statusbar.notification.domain.interactor.SeenNotificationsInteractor
+import com.android.systemui.statusbar.notification.domain.interactor.lockScreenShowOnlyUnseenNotificationsSetting
+import com.android.systemui.statusbar.notification.domain.interactor.seenNotificationsInteractor
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
-import com.android.systemui.statusbar.policy.HeadsUpManager
 import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener
+import com.android.systemui.statusbar.policy.headsUpManager
+import com.android.systemui.testKosmos
 import com.android.systemui.util.settings.FakeSettings
+import com.android.systemui.util.settings.fakeSettings
 import com.google.common.truth.Truth.assertThat
 import kotlin.time.Duration.Companion.seconds
 import kotlinx.coroutines.CoroutineScope
@@ -72,13 +78,23 @@
 @RunWith(ParameterizedAndroidJunit4::class)
 class OriginalUnseenKeyguardCoordinatorTest(flags: FlagsParameterization) : SysuiTestCase() {
 
-    private val kosmos = Kosmos()
+    private val kosmos =
+        testKosmos().apply {
+            testDispatcher = UnconfinedTestDispatcher()
+            statusBarStateController = mock()
+            fakeSettings.putInt(Settings.Secure.LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS, 1)
+        }
 
-    private val headsUpManager: HeadsUpManager = mock()
-    private val keyguardRepository = FakeKeyguardRepository()
-    private val keyguardTransitionRepository = kosmos.fakeKeyguardTransitionRepository
+    private val keyguardRepository
+        get() = kosmos.fakeKeyguardRepository
+
+    private val keyguardTransitionRepository
+        get() = kosmos.fakeKeyguardTransitionRepository
+
+    private val statusBarStateController
+        get() = kosmos.statusBarStateController
+
     private val notifPipeline: NotifPipeline = mock()
-    private val statusBarStateController: StatusBarStateController = mock()
 
     init {
         mSetFlagsRule.setFlagsParameterization(flags)
@@ -252,7 +268,7 @@
             collectionListener.onEntryAdded(fakeEntry)
 
             // GIVEN: The setting for filtering unseen notifications is disabled
-            showOnlyUnseenNotifsOnKeyguardSetting = false
+            kosmos.lockScreenShowOnlyUnseenNotificationsSetting = false
 
             // GIVEN: The pipeline has registered the unseen filter for invalidation
             val invalidationListener: Pluggable.PluggableListener<NotifFilter> = mock()
@@ -266,7 +282,7 @@
             assertThat(unseenFilter.shouldFilterOut(fakeEntry, 0L)).isFalse()
 
             // WHEN: The secure setting is changed
-            showOnlyUnseenNotifsOnKeyguardSetting = true
+            kosmos.lockScreenShowOnlyUnseenNotificationsSetting = true
 
             // THEN: The pipeline is invalidated
             verify(invalidationListener).onPluggableInvalidated(same(unseenFilter), any())
@@ -607,34 +623,25 @@
     private fun runKeyguardCoordinatorTest(
         testBlock: suspend KeyguardCoordinatorTestScope.() -> Unit
     ) {
-        val testDispatcher = UnconfinedTestDispatcher()
-        val testScope = TestScope(testDispatcher)
-        val fakeSettings =
-            FakeSettings().apply {
-                putInt(Settings.Secure.LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS, 1)
-            }
-        val seenNotificationsInteractor =
-            SeenNotificationsInteractor(ActiveNotificationListRepository())
         val keyguardCoordinator =
             OriginalUnseenKeyguardCoordinator(
-                testDispatcher,
-                mock<DumpManager>(),
-                headsUpManager,
-                keyguardRepository,
-                kosmos.keyguardTransitionInteractor,
-                KeyguardCoordinatorLogger(logcatLogBuffer()),
-                testScope.backgroundScope,
-                fakeSettings,
-                seenNotificationsInteractor,
-                statusBarStateController,
+                dumpManager = kosmos.dumpManager,
+                headsUpManager = kosmos.headsUpManager,
+                keyguardRepository = kosmos.keyguardRepository,
+                keyguardTransitionInteractor = kosmos.keyguardTransitionInteractor,
+                logger = KeyguardCoordinatorLogger(logcatLogBuffer()),
+                scope = kosmos.testScope.backgroundScope,
+                seenNotificationsInteractor = kosmos.seenNotificationsInteractor,
+                statusBarStateController = kosmos.statusBarStateController,
+                sceneInteractor = kosmos.sceneInteractor,
             )
         keyguardCoordinator.attach(notifPipeline)
-        testScope.runTest {
+        kosmos.testScope.runTest {
             KeyguardCoordinatorTestScope(
                     keyguardCoordinator,
-                    testScope,
-                    seenNotificationsInteractor,
-                    fakeSettings,
+                    kosmos.testScope,
+                    kosmos.seenNotificationsInteractor,
+                    kosmos.fakeSettings,
                 )
                 .testBlock()
         }
@@ -656,21 +663,8 @@
             argumentCaptor { verify(notifPipeline).addCollectionListener(capture()) }.lastValue
 
         val onHeadsUpChangedListener: OnHeadsUpChangedListener
-            get() = argumentCaptor { verify(headsUpManager).addListener(capture()) }.lastValue
-
-        var showOnlyUnseenNotifsOnKeyguardSetting: Boolean
             get() =
-                fakeSettings.getIntForUser(
-                    Settings.Secure.LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS,
-                    UserHandle.USER_CURRENT,
-                ) == 1
-            set(value) {
-                fakeSettings.putIntForUser(
-                    Settings.Secure.LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS,
-                    if (value) 1 else 2,
-                    UserHandle.USER_CURRENT,
-                )
-            }
+                argumentCaptor { verify(kosmos.headsUpManager).addListener(capture()) }.lastValue
     }
 
     companion object {
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 bec8cfe..9f40f60 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
@@ -24,8 +24,11 @@
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.statusbar.notification.collection.render.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
 import com.android.systemui.statusbar.notification.data.repository.setActiveNotifs
+import com.android.systemui.statusbar.notification.shared.CallType
 import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -56,6 +59,117 @@
         }
 
     @Test
+    fun ongoingCallNotification_noCallNotifs_null() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.ongoingCallNotification)
+
+            val normalNotifs =
+                listOf(
+                    activeNotificationModel(
+                        key = "notif1",
+                        callType = CallType.None,
+                    ),
+                    activeNotificationModel(
+                        key = "notif2",
+                        callType = CallType.None,
+                    )
+                )
+
+            activeNotificationListRepository.activeNotifications.value =
+                ActiveNotificationsStore.Builder()
+                    .apply { normalNotifs.forEach(::addIndividualNotif) }
+                    .build()
+
+            assertThat(latest).isNull()
+        }
+
+    @Test
+    fun ongoingCallNotification_incomingCallNotif_null() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.ongoingCallNotification)
+
+            activeNotificationListRepository.activeNotifications.value =
+                ActiveNotificationsStore.Builder()
+                    .apply {
+                        addIndividualNotif(
+                            activeNotificationModel(
+                                key = "incomingNotif",
+                                callType = CallType.Incoming,
+                            )
+                        )
+                    }
+                    .build()
+
+            assertThat(latest).isNull()
+        }
+
+    @Test
+    fun ongoingCallNotification_screeningCallNotif_null() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.ongoingCallNotification)
+
+            activeNotificationListRepository.activeNotifications.value =
+                ActiveNotificationsStore.Builder()
+                    .apply {
+                        addIndividualNotif(
+                            activeNotificationModel(
+                                key = "screeningNotif",
+                                callType = CallType.Screening,
+                            )
+                        )
+                    }
+                    .build()
+
+            assertThat(latest).isNull()
+        }
+
+    @Test
+    fun ongoingCallNotification_ongoingCallNotif_hasNotif() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.ongoingCallNotification)
+
+            val ongoingNotif =
+                activeNotificationModel(
+                    key = "ongoingNotif",
+                    callType = CallType.Ongoing,
+                )
+
+            activeNotificationListRepository.activeNotifications.value =
+                ActiveNotificationsStore.Builder()
+                    .apply { addIndividualNotif(ongoingNotif) }
+                    .build()
+
+            assertThat(latest).isEqualTo(ongoingNotif)
+        }
+
+    @Test
+    fun ongoingCallNotification_multipleCallNotifs_usesEarlierNotif() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.ongoingCallNotification)
+
+            val earlierOngoingNotif =
+                activeNotificationModel(
+                    key = "earlierOngoingNotif",
+                    callType = CallType.Ongoing,
+                    whenTime = 45L,
+                )
+            val laterOngoingNotif =
+                activeNotificationModel(
+                    key = "laterOngoingNotif",
+                    callType = CallType.Ongoing,
+                    whenTime = 55L,
+                )
+
+            activeNotificationListRepository.activeNotifications.value =
+                ActiveNotificationsStore.Builder()
+                    .apply { addIndividualNotif(earlierOngoingNotif) }
+                    .apply { addIndividualNotif(laterOngoingNotif) }
+                    .build()
+
+            assertThat(latest).isEqualTo(earlierOngoingNotif)
+        }
+
+    @Test
     fun areAnyNotificationsPresent_isTrue() =
         testScope.runTest {
             val areAnyNotificationsPresent by collectLastValue(underTest.areAnyNotificationsPresent)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/SeenNotificationsInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/SeenNotificationsInteractorTest.kt
new file mode 100644
index 0000000..2159b86
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/SeenNotificationsInteractorTest.kt
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.domain.interactor
+
+import android.platform.test.annotations.EnableFlags
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
+import com.android.systemui.statusbar.notification.shared.NotificationMinimalismPrototype
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class SeenNotificationsInteractorTest : SysuiTestCase() {
+
+    private val kosmos = testKosmos()
+    private val underTest
+        get() = kosmos.seenNotificationsInteractor
+
+    @Test
+    fun testNoFilteredOutSeenNotifications() = runTest {
+        val hasFilteredOutSeenNotifications by
+            collectLastValue(underTest.hasFilteredOutSeenNotifications)
+
+        underTest.setHasFilteredOutSeenNotifications(false)
+
+        assertThat(hasFilteredOutSeenNotifications).isFalse()
+    }
+
+    @Test
+    fun testHasFilteredOutSeenNotifications() = runTest {
+        val hasFilteredOutSeenNotifications by
+            collectLastValue(underTest.hasFilteredOutSeenNotifications)
+
+        underTest.setHasFilteredOutSeenNotifications(true)
+
+        assertThat(hasFilteredOutSeenNotifications).isTrue()
+    }
+
+    @Test
+    @EnableFlags(NotificationMinimalismPrototype.FLAG_NAME)
+    fun topOngoingAndUnseenNotification() = runTest {
+        val entry1 = NotificationEntryBuilder().setTag("entry1").build()
+        val entry2 = NotificationEntryBuilder().setTag("entry2").build()
+
+        underTest.setTopOngoingNotification(null)
+        underTest.setTopUnseenNotification(null)
+
+        assertThat(underTest.isTopOngoingNotification(entry1)).isFalse()
+        assertThat(underTest.isTopOngoingNotification(entry2)).isFalse()
+        assertThat(underTest.isTopUnseenNotification(entry1)).isFalse()
+        assertThat(underTest.isTopUnseenNotification(entry2)).isFalse()
+
+        underTest.setTopOngoingNotification(entry1)
+        underTest.setTopUnseenNotification(entry2)
+
+        assertThat(underTest.isTopOngoingNotification(entry1)).isTrue()
+        assertThat(underTest.isTopOngoingNotification(entry2)).isFalse()
+        assertThat(underTest.isTopUnseenNotification(entry1)).isFalse()
+        assertThat(underTest.isTopUnseenNotification(entry2)).isTrue()
+    }
+
+    fun testShowOnlyUnseenNotifsOnKeyguardSetting() = runTest {
+        val settingEnabled by
+            collectLastValue(underTest.isLockScreenShowOnlyUnseenNotificationsEnabled())
+
+        kosmos.lockScreenShowOnlyUnseenNotificationsSetting = false
+        testScheduler.runCurrent()
+        assertThat(settingEnabled).isFalse()
+
+        kosmos.lockScreenShowOnlyUnseenNotificationsSetting = true
+        testScheduler.runCurrent()
+        assertThat(settingEnabled).isTrue()
+    }
+}
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 6f09931..9fea7a2 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
@@ -19,12 +19,10 @@
 
 package com.android.systemui.statusbar.notification.stack.ui.viewmodel
 
-import android.platform.test.annotations.DisableFlags
 import android.platform.test.annotations.EnableFlags
 import android.platform.test.flag.junit.FlagsParameterization
 import androidx.test.filters.SmallTest
 import com.android.compose.animation.scene.ObservableTransitionState
-import com.android.systemui.Flags.FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX
 import com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.bouncer.data.repository.keyguardBouncerRepository
@@ -38,8 +36,8 @@
 import com.android.systemui.flags.DisableSceneContainer
 import com.android.systemui.flags.EnableSceneContainer
 import com.android.systemui.flags.Flags
-import com.android.systemui.flags.andSceneContainer
 import com.android.systemui.flags.fakeFeatureFlagsClassic
+import com.android.systemui.flags.parameterizeSceneContainerFlag
 import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
 import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
 import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
@@ -60,7 +58,6 @@
 import com.android.systemui.res.R
 import com.android.systemui.shade.mockLargeScreenHeaderHelper
 import com.android.systemui.shade.shadeTestUtil
-import com.android.systemui.statusbar.notification.NotificationUtils.interpolate
 import com.android.systemui.statusbar.notification.stack.domain.interactor.sharedNotificationContainerInteractor
 import com.android.systemui.testKosmos
 import com.android.systemui.util.mockito.any
@@ -90,10 +87,7 @@
         @JvmStatic
         @Parameters(name = "{0}")
         fun getParams(): List<FlagsParameterization> {
-            return FlagsParameterization.allCombinationsOf(
-                    FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX,
-                )
-                .andSceneContainer()
+            return parameterizeSceneContainerFlag()
         }
     }
 
@@ -178,25 +172,6 @@
         }
 
     @Test
-    @DisableFlags(FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX)
-    fun validatePaddingTopInSplitShade_refactorFlagOff_usesLargeHeaderResource() =
-        testScope.runTest {
-            whenever(largeScreenHeaderHelper.getLargeScreenHeaderHeight()).thenReturn(5)
-            overrideResource(R.bool.config_use_split_notification_shade, true)
-            overrideResource(R.bool.config_use_large_screen_shade_header, true)
-            overrideResource(R.dimen.large_screen_shade_header_height, 10)
-            overrideResource(R.dimen.keyguard_split_shade_top_margin, 50)
-
-            val paddingTop by collectLastValue(underTest.paddingTopDimen)
-
-            configurationRepository.onAnyConfigurationChange()
-
-            // Should directly use the header height (flagged off value)
-            assertThat(paddingTop).isEqualTo(10)
-        }
-
-    @Test
-    @EnableFlags(FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX)
     fun validatePaddingTopInSplitShade_refactorFlagOn_usesLargeHeaderHelper() =
         testScope.runTest {
             whenever(largeScreenHeaderHelper.getLargeScreenHeaderHeight()).thenReturn(5)
@@ -268,49 +243,8 @@
         }
 
     @Test
-    @DisableFlags(FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX)
     @DisableSceneContainer
-    fun validateMarginTopWithLargeScreenHeader_refactorFlagOff_usesResource() =
-        testScope.runTest {
-            val headerResourceHeight = 50
-            val headerHelperHeight = 100
-            whenever(largeScreenHeaderHelper.getLargeScreenHeaderHeight())
-                .thenReturn(headerHelperHeight)
-            overrideResource(R.bool.config_use_large_screen_shade_header, true)
-            overrideResource(R.dimen.large_screen_shade_header_height, headerResourceHeight)
-            overrideResource(R.dimen.notification_panel_margin_top, 0)
-
-            val dimens by collectLastValue(underTest.configurationBasedDimensions)
-
-            configurationRepository.onAnyConfigurationChange()
-
-            assertThat(dimens!!.marginTop).isEqualTo(headerResourceHeight)
-        }
-
-    @Test
-    @DisableFlags(FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX)
-    @EnableSceneContainer
-    fun validateMarginTopWithLargeScreenHeader_refactorFlagOff_sceneContainerFlagOn_stillZero() =
-        testScope.runTest {
-            val headerResourceHeight = 50
-            val headerHelperHeight = 100
-            whenever(largeScreenHeaderHelper.getLargeScreenHeaderHeight())
-                .thenReturn(headerHelperHeight)
-            overrideResource(R.bool.config_use_large_screen_shade_header, true)
-            overrideResource(R.dimen.large_screen_shade_header_height, headerResourceHeight)
-            overrideResource(R.dimen.notification_panel_margin_top, 0)
-
-            val dimens by collectLastValue(underTest.configurationBasedDimensions)
-
-            configurationRepository.onAnyConfigurationChange()
-
-            assertThat(dimens!!.marginTop).isEqualTo(0)
-        }
-
-    @Test
-    @DisableSceneContainer
-    @EnableFlags(FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX)
-    fun validateMarginTopWithLargeScreenHeader_refactorFlagOn_usesHelper() =
+    fun validateMarginTopWithLargeScreenHeader_usesHelper() =
         testScope.runTest {
             val headerResourceHeight = 50
             val headerHelperHeight = 100
@@ -329,7 +263,6 @@
 
     @Test
     @EnableSceneContainer
-    @EnableFlags(FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX)
     fun validateMarginTopWithLargeScreenHeader_sceneContainerFlagOn_stillZero() =
         testScope.runTest {
             val headerResourceHeight = 50
@@ -590,44 +523,45 @@
         }
 
     @Test
-    @DisableFlags(FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX)
     @DisableSceneContainer
-    fun boundsOnLockscreenInSplitShade_refactorFlagOff_usesLargeHeaderResource() =
+    fun boundsDoNotChangeWhileLockscreenToAodTransitionIsActive() =
         testScope.runTest {
             val bounds by collectLastValue(underTest.bounds)
 
-            // When in split shade
-            whenever(largeScreenHeaderHelper.getLargeScreenHeaderHeight()).thenReturn(5)
-            overrideResource(R.bool.config_use_split_notification_shade, true)
-            overrideResource(R.bool.config_use_large_screen_shade_header, true)
-            overrideResource(R.dimen.large_screen_shade_header_height, 10)
-            overrideResource(R.dimen.keyguard_split_shade_top_margin, 50)
-
-            configurationRepository.onAnyConfigurationChange()
-            runCurrent()
-
             // Start on lockscreen
             showLockscreen()
 
             keyguardInteractor.setNotificationContainerBounds(
-                NotificationContainerBounds(top = 1f, bottom = 52f)
+                NotificationContainerBounds(top = 1f, bottom = 1f)
+            )
+            assertThat(bounds).isEqualTo(NotificationContainerBounds(top = 1f, bottom = 1f))
+
+            // Begin transition to AOD
+            keyguardTransitionRepository.sendTransitionStep(
+                TransitionStep(LOCKSCREEN, AOD, 0f, TransitionState.STARTED)
             )
             runCurrent()
+            keyguardTransitionRepository.sendTransitionStep(
+                TransitionStep(LOCKSCREEN, AOD, 0.5f, TransitionState.RUNNING)
+            )
 
-            // Top should be equal to bounds (1) - padding adjustment (10)
-            assertThat(bounds)
-                .isEqualTo(
-                    NotificationContainerBounds(
-                        top = -9f,
-                        bottom = 2f,
-                    )
-                )
+            // Attempt to update bounds
+            keyguardInteractor.setNotificationContainerBounds(
+                NotificationContainerBounds(top = 5f, bottom = 5f)
+            )
+            // Bounds should not have moved
+            assertThat(bounds).isEqualTo(NotificationContainerBounds(top = 1f, bottom = 1f))
+
+            // Transition is over, now move
+            keyguardTransitionRepository.sendTransitionStep(
+                TransitionStep(LOCKSCREEN, AOD, 1f, TransitionState.FINISHED)
+            )
+            assertThat(bounds).isEqualTo(NotificationContainerBounds(top = 5f, bottom = 5f))
         }
 
     @Test
-    @EnableFlags(FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX)
     @DisableSceneContainer
-    fun boundsOnLockscreenInSplitShade_refactorFlagOn_usesLargeHeaderHelper() =
+    fun boundsOnLockscreenInSplitShade_usesLargeHeaderHelper() =
         testScope.runTest {
             val bounds by collectLastValue(underTest.bounds)
 
@@ -820,54 +754,6 @@
 
     @Test
     @DisableSceneContainer
-    fun updateBounds_fromKeyguardRoot() =
-        testScope.runTest {
-            val startProgress = 0f
-            val startStep = TransitionStep(LOCKSCREEN, AOD, startProgress, TransitionState.STARTED)
-            val boundsChangingProgress = 0.2f
-            val boundsChangingStep =
-                TransitionStep(LOCKSCREEN, AOD, boundsChangingProgress, TransitionState.RUNNING)
-            val boundsInterpolatingProgress = 0.6f
-            val boundsInterpolatingStep =
-                TransitionStep(
-                    LOCKSCREEN,
-                    AOD,
-                    boundsInterpolatingProgress,
-                    TransitionState.RUNNING
-                )
-            val finishProgress = 1.0f
-            val finishStep =
-                TransitionStep(LOCKSCREEN, AOD, finishProgress, TransitionState.FINISHED)
-
-            val bounds by collectLastValue(underTest.bounds)
-            val top = 123f
-            val bottom = 456f
-
-            kosmos.fakeKeyguardTransitionRepository.sendTransitionStep(startStep)
-            runCurrent()
-            kosmos.fakeKeyguardTransitionRepository.sendTransitionStep(boundsChangingStep)
-            runCurrent()
-            keyguardRootViewModel.onNotificationContainerBoundsChanged(top, bottom)
-
-            kosmos.fakeKeyguardTransitionRepository.sendTransitionStep(boundsInterpolatingStep)
-            runCurrent()
-            val adjustedProgress =
-                (boundsInterpolatingProgress - boundsChangingProgress) /
-                    (1 - boundsChangingProgress)
-            val interpolatedTop = interpolate(0f, top, adjustedProgress)
-            val interpolatedBottom = interpolate(0f, bottom, adjustedProgress)
-            assertThat(bounds)
-                .isEqualTo(
-                    NotificationContainerBounds(top = interpolatedTop, bottom = interpolatedBottom)
-                )
-
-            kosmos.fakeKeyguardTransitionRepository.sendTransitionStep(finishStep)
-            runCurrent()
-            assertThat(bounds).isEqualTo(NotificationContainerBounds(top = top, bottom = bottom))
-        }
-
-    @Test
-    @DisableSceneContainer
     fun updateBounds_fromGone_withoutTransitions() =
         testScope.runTest {
             // Start step is already at 1.0
@@ -878,9 +764,9 @@
             val top = 123f
             val bottom = 456f
 
-            kosmos.fakeKeyguardTransitionRepository.sendTransitionStep(runningStep)
+            keyguardTransitionRepository.sendTransitionStep(runningStep)
             runCurrent()
-            kosmos.fakeKeyguardTransitionRepository.sendTransitionStep(finishStep)
+            keyguardTransitionRepository.sendTransitionStep(finishStep)
             runCurrent()
             keyguardRootViewModel.onNotificationContainerBoundsChanged(top, bottom)
             runCurrent()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/AvalancheControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/AvalancheControllerTest.kt
index 8f9da3b..9a862fc 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/AvalancheControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/AvalancheControllerTest.kt
@@ -60,6 +60,7 @@
     // For creating TestableHeadsUpManager
     @Mock private val mAccessibilityMgr: AccessibilityManagerWrapper? = null
     private val mUiEventLoggerFake = UiEventLoggerFake()
+    @Mock private lateinit var mHeadsUpManagerLogger: HeadsUpManagerLogger
 
     @Mock private lateinit var mBgHandler: Handler
 
@@ -82,7 +83,8 @@
 
         // Initialize AvalancheController and TestableHeadsUpManager during setUp instead of
         // declaration, where mocks are null
-        mAvalancheController = AvalancheController(dumpManager, mUiEventLoggerFake, mBgHandler)
+        mAvalancheController = AvalancheController(dumpManager, mUiEventLoggerFake,
+                mHeadsUpManagerLogger, mBgHandler)
 
         testableHeadsUpManager =
             TestableHeadsUpManager(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java
index df07b44..9005ae3 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java
@@ -81,6 +81,7 @@
     static final int TEST_A11Y_AUTO_DISMISS_TIME = 1_000;
 
     private UiEventLoggerFake mUiEventLoggerFake = new UiEventLoggerFake();
+
     private final HeadsUpManagerLogger mLogger = spy(new HeadsUpManagerLogger(logcatLogBuffer()));
     @Mock private Handler mBgHandler;
     @Mock private DumpManager dumpManager;
@@ -149,7 +150,8 @@
     @Override
     public void SysuiSetup() throws Exception {
         super.SysuiSetup();
-        mAvalancheController = new AvalancheController(dumpManager, mUiEventLoggerFake, mBgHandler);
+        mAvalancheController = new AvalancheController(dumpManager, mUiEventLoggerFake, mLogger,
+                mBgHandler);
     }
 
     @Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/HeadsUpManagerPhoneTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/HeadsUpManagerPhoneTest.kt
index b91bde4..7a6838a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/HeadsUpManagerPhoneTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/HeadsUpManagerPhoneTest.kt
@@ -179,7 +179,8 @@
         mContext
             .getOrCreateTestableResources()
             .addOverride(R.integer.ambient_notification_extension_time, 500)
-        mAvalancheController = AvalancheController(dumpManager, mUiEventLogger, mBgHandler)
+        mAvalancheController = AvalancheController(dumpManager, mUiEventLogger,
+                mHeadsUpManagerLogger, mBgHandler)
     }
 
     @Test
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 88431f0..32f66c1 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,15 +18,21 @@
 
 import android.app.NotificationManager.Policy
 import android.provider.Settings
+import android.provider.Settings.Secure.ZEN_DURATION
+import android.provider.Settings.Secure.ZEN_DURATION_FOREVER
+import android.provider.Settings.Secure.ZEN_DURATION_PROMPT
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.settingslib.notification.data.repository.updateNotificationPolicy
+import com.android.settingslib.notification.modes.TestModeBuilder
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.kosmos.testScope
+import com.android.systemui.shared.settings.data.repository.secureSettingsRepository
 import com.android.systemui.statusbar.policy.data.repository.fakeZenModeRepository
 import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
+import java.time.Duration
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
@@ -39,7 +45,8 @@
 class ZenModeInteractorTest : SysuiTestCase() {
     private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
-    private val repository = kosmos.fakeZenModeRepository
+    private val zenModeRepository = kosmos.fakeZenModeRepository
+    private val settingsRepository = kosmos.secureSettingsRepository
 
     private val underTest = kosmos.zenModeInteractor
 
@@ -48,7 +55,7 @@
         testScope.runTest {
             val enabled by collectLastValue(underTest.isZenModeEnabled)
 
-            repository.updateZenMode(Settings.Global.ZEN_MODE_OFF)
+            zenModeRepository.updateZenMode(Settings.Global.ZEN_MODE_OFF)
             runCurrent()
 
             assertThat(enabled).isFalse()
@@ -59,7 +66,7 @@
         testScope.runTest {
             val enabled by collectLastValue(underTest.isZenModeEnabled)
 
-            repository.updateZenMode(Settings.Global.ZEN_MODE_ALARMS)
+            zenModeRepository.updateZenMode(Settings.Global.ZEN_MODE_ALARMS)
             runCurrent()
 
             assertThat(enabled).isTrue()
@@ -70,7 +77,7 @@
         testScope.runTest {
             val enabled by collectLastValue(underTest.isZenModeEnabled)
 
-            repository.updateZenMode(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS)
+            zenModeRepository.updateZenMode(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS)
             runCurrent()
 
             assertThat(enabled).isTrue()
@@ -81,7 +88,7 @@
         testScope.runTest {
             val enabled by collectLastValue(underTest.isZenModeEnabled)
 
-            repository.updateZenMode(Settings.Global.ZEN_MODE_NO_INTERRUPTIONS)
+            zenModeRepository.updateZenMode(Settings.Global.ZEN_MODE_NO_INTERRUPTIONS)
             runCurrent()
 
             assertThat(enabled).isTrue()
@@ -92,7 +99,8 @@
         testScope.runTest {
             val enabled by collectLastValue(underTest.isZenModeEnabled)
 
-            repository.updateZenMode(4) // this should fail if we ever add another zen mode type
+            // this should fail if we ever add another zen mode type
+            zenModeRepository.updateZenMode(4)
             runCurrent()
 
             assertThat(enabled).isFalse()
@@ -103,8 +111,8 @@
         testScope.runTest {
             val hidden by collectLastValue(underTest.areNotificationsHiddenInShade)
 
-            repository.updateNotificationPolicy(null)
-            repository.updateZenMode(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS)
+            zenModeRepository.updateNotificationPolicy(null)
+            zenModeRepository.updateZenMode(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS)
             runCurrent()
 
             assertThat(hidden).isFalse()
@@ -115,10 +123,10 @@
         testScope.runTest {
             val hidden by collectLastValue(underTest.areNotificationsHiddenInShade)
 
-            repository.updateNotificationPolicy(
+            zenModeRepository.updateNotificationPolicy(
                 suppressedVisualEffects = Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST
             )
-            repository.updateZenMode(Settings.Global.ZEN_MODE_OFF)
+            zenModeRepository.updateZenMode(Settings.Global.ZEN_MODE_OFF)
             runCurrent()
 
             assertThat(hidden).isFalse()
@@ -129,10 +137,10 @@
         testScope.runTest {
             val hidden by collectLastValue(underTest.areNotificationsHiddenInShade)
 
-            repository.updateNotificationPolicy(
+            zenModeRepository.updateNotificationPolicy(
                 suppressedVisualEffects = Policy.SUPPRESSED_EFFECT_STATUS_BAR
             )
-            repository.updateZenMode(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS)
+            zenModeRepository.updateZenMode(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS)
             runCurrent()
 
             assertThat(hidden).isFalse()
@@ -143,12 +151,70 @@
         testScope.runTest {
             val hidden by collectLastValue(underTest.areNotificationsHiddenInShade)
 
-            repository.updateNotificationPolicy(
+            zenModeRepository.updateNotificationPolicy(
                 suppressedVisualEffects = Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST
             )
-            repository.updateZenMode(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS)
+            zenModeRepository.updateZenMode(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS)
             runCurrent()
 
             assertThat(hidden).isTrue()
         }
+
+    @Test
+    fun shouldAskForZenDuration_falseForNonManualDnd() =
+        testScope.runTest {
+            settingsRepository.setInt(ZEN_DURATION, ZEN_DURATION_PROMPT)
+            runCurrent()
+
+            assertThat(underTest.shouldAskForZenDuration(TestModeBuilder.EXAMPLE)).isFalse()
+        }
+
+    @Test
+    fun shouldAskForZenDuration_changesWithSetting() =
+        testScope.runTest {
+            val manualDnd = TestModeBuilder.MANUAL_DND
+
+            settingsRepository.setInt(ZEN_DURATION, ZEN_DURATION_FOREVER)
+            runCurrent()
+
+            assertThat(underTest.shouldAskForZenDuration(manualDnd)).isFalse()
+
+            settingsRepository.setInt(ZEN_DURATION, ZEN_DURATION_PROMPT)
+            runCurrent()
+
+            assertThat(underTest.shouldAskForZenDuration(manualDnd)).isTrue()
+        }
+
+    @Test
+    fun activateMode_nonManualDnd() =
+        testScope.runTest {
+            val mode = TestModeBuilder().setActive(false).build()
+            zenModeRepository.addModes(listOf(mode))
+            settingsRepository.setInt(ZEN_DURATION, 60)
+            runCurrent()
+
+            underTest.activateMode(mode)
+            assertThat(zenModeRepository.getMode(mode.id)?.isActive).isTrue()
+            assertThat(zenModeRepository.getModeActiveDuration(mode.id)).isNull()
+        }
+
+    @Test
+    fun activateMode_usesCorrectDuration() =
+        testScope.runTest {
+            val manualDnd = TestModeBuilder.MANUAL_DND
+            zenModeRepository.addModes(listOf(manualDnd))
+            settingsRepository.setInt(ZEN_DURATION, ZEN_DURATION_FOREVER)
+            runCurrent()
+
+            underTest.activateMode(manualDnd)
+            assertThat(zenModeRepository.getModeActiveDuration(manualDnd.id)).isNull()
+
+            zenModeRepository.deactivateMode(manualDnd.id)
+            settingsRepository.setInt(ZEN_DURATION, 60)
+            runCurrent()
+
+            underTest.activateMode(manualDnd)
+            assertThat(zenModeRepository.getModeActiveDuration(manualDnd.id))
+                .isEqualTo(Duration.ofMinutes(60))
+        }
 }
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 fdfc7f1..62161bf 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
@@ -18,6 +18,8 @@
 
 package com.android.systemui.statusbar.policy.ui.dialog.viewmodel
 
+import android.content.Intent
+import android.provider.Settings
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.settingslib.notification.modes.TestModeBuilder
@@ -27,32 +29,46 @@
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.statusbar.policy.data.repository.fakeZenModeRepository
 import com.android.systemui.statusbar.policy.domain.interactor.zenModeInteractor
+import com.android.systemui.statusbar.policy.ui.dialog.mockModesDialogDelegate
 import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.Job
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.mockito.Mockito.clearInvocations
+import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.verify
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class ModesDialogViewModelTest : SysuiTestCase() {
     private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
-    val repository = kosmos.fakeZenModeRepository
-    val interactor = kosmos.zenModeInteractor
+    private val repository = kosmos.fakeZenModeRepository
+    private val interactor = kosmos.zenModeInteractor
+    private val mockDialogDelegate = kosmos.mockModesDialogDelegate
 
-    val underTest = ModesDialogViewModel(context, interactor, kosmos.testDispatcher)
+    private val underTest =
+        ModesDialogViewModel(context, interactor, kosmos.testDispatcher, mockDialogDelegate)
 
     @Test
-    fun tiles_filtersOutDisabledModes() =
+    fun tiles_filtersOutUserDisabledModes() =
         testScope.runTest {
             val tiles by collectLastValue(underTest.tiles)
 
             repository.addModes(
                 listOf(
-                    TestModeBuilder().setName("Disabled").setEnabled(false).build(),
+                    TestModeBuilder()
+                        .setName("Disabled by user")
+                        .setEnabled(false, /* byUser= */ true)
+                        .build(),
+                    TestModeBuilder()
+                        .setName("Disabled by other")
+                        .setEnabled(false, /* byUser= */ false)
+                        .build(),
                     TestModeBuilder.MANUAL_DND,
                     TestModeBuilder()
                         .setName("Enabled")
@@ -61,19 +77,25 @@
                         .build(),
                     TestModeBuilder()
                         .setName("Disabled with manual")
-                        .setEnabled(false)
+                        .setEnabled(false, /* byUser= */ true)
                         .setManualInvocationAllowed(true)
                         .build(),
-                ))
+                )
+            )
             runCurrent()
 
-            assertThat(tiles?.size).isEqualTo(2)
+            assertThat(tiles?.size).isEqualTo(3)
             with(tiles?.elementAt(0)!!) {
+                assertThat(this.text).isEqualTo("Disabled by other")
+                assertThat(this.subtext).isEqualTo("Set up")
+                assertThat(this.enabled).isEqualTo(false)
+            }
+            with(tiles?.elementAt(1)!!) {
                 assertThat(this.text).isEqualTo("Manual DND")
                 assertThat(this.subtext).isEqualTo("On")
                 assertThat(this.enabled).isEqualTo(true)
             }
-            with(tiles?.elementAt(1)!!) {
+            with(tiles?.elementAt(2)!!) {
                 assertThat(this.text).isEqualTo("Enabled")
                 assertThat(this.subtext).isEqualTo("Off")
                 assertThat(this.enabled).isEqualTo(false)
@@ -108,7 +130,8 @@
                         .setActive(false)
                         .setManualInvocationAllowed(false)
                         .build(),
-                ))
+                )
+            )
             runCurrent()
 
             assertThat(tiles?.size).isEqualTo(3)
@@ -130,6 +153,117 @@
         }
 
     @Test
+    fun tiles_stableWhileCollecting() =
+        testScope.runTest {
+            val job = Job()
+            val tiles by collectLastValue(underTest.tiles, context = job)
+
+            repository.addModes(
+                listOf(
+                    TestModeBuilder()
+                        .setName("Active without manual")
+                        .setActive(true)
+                        .setManualInvocationAllowed(false)
+                        .build(),
+                    TestModeBuilder()
+                        .setName("Active with manual")
+                        .setActive(true)
+                        .setManualInvocationAllowed(true)
+                        .build(),
+                    TestModeBuilder()
+                        .setName("Inactive with manual")
+                        .setActive(false)
+                        .setManualInvocationAllowed(true)
+                        .build(),
+                    TestModeBuilder()
+                        .setName("Inactive without manual")
+                        .setActive(false)
+                        .setManualInvocationAllowed(false)
+                        .build(),
+                )
+            )
+            runCurrent()
+
+            assertThat(tiles?.size).isEqualTo(3)
+
+            // Check that tile is initially present
+            with(tiles?.elementAt(0)!!) {
+                assertThat(this.text).isEqualTo("Active without manual")
+                assertThat(this.subtext).isEqualTo("On")
+                assertThat(this.enabled).isEqualTo(true)
+
+                // Click tile to toggle it
+                this.onClick()
+                runCurrent()
+            }
+            // Check that tile is still present at the same location, but turned off
+            assertThat(tiles?.size).isEqualTo(3)
+            with(tiles?.elementAt(0)!!) {
+                assertThat(this.text).isEqualTo("Active without manual")
+                assertThat(this.subtext).isEqualTo("Manage in settings")
+                assertThat(this.enabled).isEqualTo(false)
+            }
+
+            // Stop collecting, then start again
+            job.cancel()
+            val tiles2 by collectLastValue(underTest.tiles)
+            runCurrent()
+
+            // Check that tile is now gone
+            assertThat(tiles2?.size).isEqualTo(2)
+            assertThat(tiles2?.elementAt(0)!!.text).isEqualTo("Active with manual")
+            assertThat(tiles2?.elementAt(1)!!.text).isEqualTo("Inactive with manual")
+        }
+
+    @Test
+    fun tiles_filtersOutRemovedModes() =
+        testScope.runTest {
+            val job = Job()
+            val tiles by collectLastValue(underTest.tiles, context = job)
+
+            repository.addModes(
+                listOf(
+                    TestModeBuilder()
+                        .setId("A")
+                        .setName("Active without manual")
+                        .setActive(true)
+                        .setManualInvocationAllowed(false)
+                        .build(),
+                    TestModeBuilder()
+                        .setId("B")
+                        .setName("Active with manual")
+                        .setActive(true)
+                        .setManualInvocationAllowed(true)
+                        .build(),
+                    TestModeBuilder()
+                        .setId("C")
+                        .setName("Inactive with manual")
+                        .setActive(false)
+                        .setManualInvocationAllowed(true)
+                        .build(),
+                )
+            )
+            runCurrent()
+
+            assertThat(tiles?.size).isEqualTo(3)
+
+            repository.removeMode("A")
+            runCurrent()
+
+            assertThat(tiles?.size).isEqualTo(2)
+
+            repository.removeMode("B")
+            runCurrent()
+
+            assertThat(tiles?.size).isEqualTo(1)
+
+            repository.removeMode("C")
+            runCurrent()
+
+            assertThat(tiles?.size).isEqualTo(0)
+        }
+
+    @Test
     fun onClick_togglesTileState() =
         testScope.runTest {
             val tiles by collectLastValue(underTest.tiles)
@@ -161,4 +295,141 @@
 
             assertThat(tiles?.first()?.enabled).isFalse()
         }
+
+    @Test
+    fun onClick_noManualActivation() =
+        testScope.runTest {
+            val job = Job()
+            val tiles by collectLastValue(underTest.tiles, context = job)
+
+            repository.addModes(
+                listOf(
+                    TestModeBuilder()
+                        .setName("Active without manual")
+                        .setActive(true)
+                        .setManualInvocationAllowed(false)
+                        .build(),
+                )
+            )
+            runCurrent()
+
+            assertThat(tiles?.size).isEqualTo(1)
+
+            // Click tile to toggle it off
+            tiles?.elementAt(0)!!.onClick()
+            runCurrent()
+
+            assertThat(tiles?.size).isEqualTo(1)
+            with(tiles?.elementAt(0)!!) {
+                assertThat(this.text).isEqualTo("Active without manual")
+                assertThat(this.subtext).isEqualTo("Manage in settings")
+                assertThat(this.enabled).isEqualTo(false)
+
+                // Press the tile again
+                this.onClick()
+                runCurrent()
+            }
+
+            // Check that nothing happened
+            with(tiles?.elementAt(0)!!) {
+                assertThat(this.text).isEqualTo("Active without manual")
+                assertThat(this.subtext).isEqualTo("Manage in settings")
+                assertThat(this.enabled).isEqualTo(false)
+            }
+        }
+
+    @Test
+    fun onClick_setUp() =
+        testScope.runTest {
+            val tiles by collectLastValue(underTest.tiles)
+
+            repository.addModes(
+                listOf(
+                    TestModeBuilder()
+                        .setId("ID")
+                        .setName("Disabled by other")
+                        .setEnabled(false, /* byUser= */ false)
+                        .build(),
+                )
+            )
+            runCurrent()
+
+            assertThat(tiles?.size).isEqualTo(1)
+            with(tiles?.elementAt(0)!!) {
+                assertThat(this.text).isEqualTo("Disabled by other")
+                assertThat(this.subtext).isEqualTo("Set up")
+                assertThat(this.enabled).isEqualTo(false)
+
+                // Click the tile
+                this.onClick()
+                runCurrent()
+            }
+
+            // Check that it launched the correct intent
+            val intentCaptor = argumentCaptor<Intent>()
+            verify(mockDialogDelegate).launchFromDialog(intentCaptor.capture())
+            val intent = intentCaptor.lastValue
+            assertThat(intent.action).isEqualTo(Settings.ACTION_AUTOMATIC_ZEN_RULE_SETTINGS)
+            assertThat(intent.extras?.getString(Settings.EXTRA_AUTOMATIC_ZEN_RULE_ID))
+                .isEqualTo("ID")
+
+            // Check that nothing happened to the tile
+            with(tiles?.elementAt(0)!!) {
+                assertThat(this.text).isEqualTo("Disabled by other")
+                assertThat(this.subtext).isEqualTo("Set up")
+                assertThat(this.enabled).isEqualTo(false)
+            }
+        }
+
+    @Test
+    fun onLongClick_launchesIntent() =
+        testScope.runTest {
+            val tiles by collectLastValue(underTest.tiles)
+            val intentCaptor = argumentCaptor<Intent>()
+
+            val modeId = "id"
+            repository.addModes(
+                listOf(
+                    TestModeBuilder()
+                        .setId(modeId)
+                        .setId("A")
+                        .setActive(true)
+                        .setManualInvocationAllowed(true)
+                        .build(),
+                    TestModeBuilder()
+                        .setId(modeId)
+                        .setId("B")
+                        .setActive(false)
+                        .setManualInvocationAllowed(true)
+                        .build(),
+                )
+            )
+            runCurrent()
+
+            assertThat(tiles?.size).isEqualTo(2)
+
+            // Trigger onLongClick for A
+            tiles?.first()?.onLongClick?.let { it() }
+            runCurrent()
+
+            // Check that it launched the correct intent
+            verify(mockDialogDelegate).launchFromDialog(intentCaptor.capture())
+            var intent = intentCaptor.lastValue
+            assertThat(intent.action).isEqualTo(Settings.ACTION_AUTOMATIC_ZEN_RULE_SETTINGS)
+            assertThat(intent.extras?.getString(Settings.EXTRA_AUTOMATIC_ZEN_RULE_ID))
+                .isEqualTo("A")
+
+            clearInvocations(mockDialogDelegate)
+
+            // Trigger onLongClick for B
+            tiles?.last()?.onLongClick?.let { it() }
+            runCurrent()
+
+            // Check that it launched the correct intent
+            verify(mockDialogDelegate).launchFromDialog(intentCaptor.capture())
+            intent = intentCaptor.lastValue
+            assertThat(intent.action).isEqualTo(Settings.ACTION_AUTOMATIC_ZEN_RULE_SETTINGS)
+            assertThat(intent.extras?.getString(Settings.EXTRA_AUTOMATIC_ZEN_RULE_ID))
+                .isEqualTo("B")
+        }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/interactor/AudioSharingInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/interactor/AudioSharingInteractorTest.kt
index 142631e..a1fcfcd 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/interactor/AudioSharingInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/interactor/AudioSharingInteractorTest.kt
@@ -16,8 +16,10 @@
 
 package com.android.systemui.volume.domain.interactor
 
+import android.media.AudioManager.STREAM_MUSIC
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
+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
@@ -40,17 +42,57 @@
 
     @Before
     fun setUp() {
-        with(kosmos) { underTest = audioSharingInteractor }
+        with(kosmos) {
+            with(audioSharingRepository) { setVolumeMap(mapOf(TEST_GROUP_ID to TEST_VOLUME)) }
+            underTest = audioSharingInteractor
+        }
     }
 
     @Test
-    fun volumeChanges_returnVolume() {
+    fun handlePrimaryGroupChange_nullVolume() {
         with(kosmos) {
             testScope.runTest {
-                with(audioSharingRepository) {
-                    setSecondaryGroupId(TEST_GROUP_ID)
-                    setVolumeMap(mapOf(TEST_GROUP_ID to TEST_VOLUME))
-                }
+                with(audioSharingRepository) { setPrimaryGroupId(TEST_GROUP_ID_INVALID) }
+                val preMusicStream by
+                    collectLastValue(
+                        audioVolumeInteractor.getAudioStream(AudioStream(STREAM_MUSIC))
+                    )
+                val preVolume = preMusicStream?.volume
+                runCurrent()
+                underTest.handlePrimaryGroupChange()
+                val musicStream by
+                    collectLastValue(
+                        audioVolumeInteractor.getAudioStream(AudioStream(STREAM_MUSIC))
+                    )
+                runCurrent()
+
+                Truth.assertThat(musicStream?.volume).isEqualTo(preVolume)
+            }
+        }
+    }
+
+    @Test
+    fun handlePrimaryGroupChange_setStreamVolume() {
+        with(kosmos) {
+            testScope.runTest {
+                with(audioSharingRepository) { setPrimaryGroupId(TEST_GROUP_ID) }
+                underTest.handlePrimaryGroupChange()
+                val musicStream by
+                    collectLastValue(
+                        audioVolumeInteractor.getAudioStream(AudioStream(STREAM_MUSIC))
+                    )
+                runCurrent()
+
+                Truth.assertThat(musicStream?.volume).isEqualTo(TEST_MUSIC_VOLUME)
+            }
+        }
+    }
+
+    @Test
+    fun secondaryGroupVolumeChanges_returnVolume() {
+        with(kosmos) {
+            testScope.runTest {
+                with(audioSharingRepository) { setSecondaryGroupId(TEST_GROUP_ID) }
                 val volume by collectLastValue(underTest.volume)
                 runCurrent()
 
@@ -60,13 +102,10 @@
     }
 
     @Test
-    fun volumeChanges_returnNull() {
+    fun secondaryGroupVolumeChanges_returnNull() {
         with(kosmos) {
             testScope.runTest {
-                with(audioSharingRepository) {
-                    setSecondaryGroupId(TEST_GROUP_ID_INVALID)
-                    setVolumeMap(mapOf(TEST_GROUP_ID to TEST_VOLUME))
-                }
+                with(audioSharingRepository) { setSecondaryGroupId(TEST_GROUP_ID_INVALID) }
                 val volume by collectLastValue(underTest.volume)
                 runCurrent()
 
@@ -76,7 +115,7 @@
     }
 
     @Test
-    fun volumeChanges_returnDefaultVolume() {
+    fun secondaryGroupVolumeChanges_returnDefaultVolume() {
         with(kosmos) {
             testScope.runTest {
                 with(audioSharingRepository) {
@@ -94,7 +133,8 @@
     private companion object {
         const val TEST_GROUP_ID = 1
         const val TEST_GROUP_ID_INVALID = -1
-        const val TEST_VOLUME = 10
+        const val TEST_MUSIC_VOLUME = 10
+        const val TEST_VOLUME = 255
         const val TEST_VOLUME_DEFAULT = 20
     }
 }
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java
index bf58eee..d3218ad 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java
@@ -14,6 +14,7 @@
 
 package com.android.systemui.plugins.qs;
 
+import android.graphics.Rect;
 import android.view.View;
 
 import androidx.annotation.FloatRange;
@@ -35,7 +36,7 @@
 
     String ACTION = "com.android.systemui.action.PLUGIN_QS";
 
-    int VERSION = 15;
+    int VERSION = 16;
 
     String TAG = "QS";
 
@@ -89,8 +90,45 @@
      */
     int getHeightDiff();
 
+    /**
+     * Returns the header view that contains QQS. This might return null (or throw) if there's no
+     * actual header view.
+     */
     View getHeader();
 
+    /**
+     * Returns the top of the header view that contains QQS wrt to the container view
+     */
+    int getHeaderTop();
+
+    /**
+     * Returns the bottom of the header view that contains QQS wrt to the container view
+     */
+    int getHeaderBottom();
+
+    /**
+     * Returns the left bound of the header view that contains QQS wrt to the container view
+     */
+    int getHeaderLeft();
+
+    /**
+     * Fills outBounds with the bounds of the header view (container of QQS) on the screen
+     */
+    void getHeaderBoundsOnScreen(Rect outBounds);
+
+    /**
+     * Returns the height of the header view that contains QQS. It defaults to bottom - top.
+     */
+    default int getHeaderHeight() {
+        return getHeaderBottom() - getHeaderTop();
+    }
+
+    /**
+     * Returns whether the header view that contains QQS is shown on screen (similar semantics to
+     * View.isShown).
+     */
+    boolean isHeaderShown();
+
     default void setHasNotifications(boolean hasNotifications) {
     }
 
diff --git a/packages/SystemUI/res-keyguard/values-es-rUS/strings.xml b/packages/SystemUI/res-keyguard/values-es-rUS/strings.xml
index bfb2ed0..5ae41fe 100644
--- a/packages/SystemUI/res-keyguard/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-es-rUS/strings.xml
@@ -83,7 +83,7 @@
     <string name="kg_primary_auth_locked_out_pin" msgid="5492230176361601475">"Demasiados intentos con PIN incorrecto"</string>
     <string name="kg_primary_auth_locked_out_pattern" msgid="8266214607346180952">"Demasiados intentos con patrón incorrecto"</string>
     <string name="kg_primary_auth_locked_out_password" msgid="6170245108400198659">"Demasiados intentos con contraseña incorrecta"</string>
-    <string name="kg_too_many_failed_attempts_countdown" msgid="2038195171919795529">"{count,plural, =1{Vuelve a intentarlo en # segundo}many{Vuelve a intentarlo en # segundos}other{Vuelve a intentarlo en # segundos}}"</string>
+    <string name="kg_too_many_failed_attempts_countdown" msgid="2038195171919795529">"{count,plural, =1{Vuelve a intentarlo en # segundo.}many{Vuelve a intentarlo en # de segundos.}other{Vuelve a intentarlo en # segundos.}}"</string>
     <string name="kg_sim_pin_instructions" msgid="1942424305184242951">"Ingresa el PIN de la tarjeta SIM."</string>
     <string name="kg_sim_pin_instructions_multi" msgid="3639863309953109649">"Ingresa el PIN de la tarjeta SIM de \"<xliff:g id="CARRIER">%1$s</xliff:g>\"."</string>
     <string name="kg_sim_lock_esim_instructions" msgid="5577169988158738030">"<xliff:g id="PREVIOUS_MSG">%1$s</xliff:g> Inhabilita la tarjeta eSIM para usar el dispositivo sin servicio móvil."</string>
@@ -100,7 +100,7 @@
     <string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"El código PIN de la tarjeta SIM es incorrecto. Debes comunicarte con tu proveedor para desbloquear el dispositivo."</string>
     <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{El código PIN de la tarjeta SIM es incorrecto. Tienes # intento restante antes de que debas comunicarte con tu operador para desbloquear el dispositivo.}many{El código PIN de la tarjeta SIM es incorrecto. Tienes # intentos restantes. }other{El código PIN de la tarjeta SIM es incorrecto. Tienes # intentos restantes. }}"</string>
     <string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"La tarjeta SIM no se puede usar. Comunícate con tu proveedor."</string>
-    <string name="kg_password_wrong_puk_code" msgid="6820515467645087827">"{count,plural, =1{El código PUK de la tarjeta SIM es incorrecto. Tienes # intento restante antes de que la tarjeta SIM quede inutilizable permanentemente.}many{El código PUK de la tarjeta SIM es incorrecto. Tienes # intentos restantes antes de que la tarjeta SIM quede inutilizable permanentemente.}other{El código PUK de la tarjeta SIM es incorrecto. Tienes # intentos restantes antes de que la tarjeta SIM quede inutilizable permanentemente.}}"</string>
+    <string name="kg_password_wrong_puk_code" msgid="6820515467645087827">"{count,plural, =1{El código PUK de la tarjeta SIM es incorrecto. Tienes # intento restante antes de que la tarjeta SIM quede inutilizable permanentemente.}many{El código PUK de la tarjeta SIM es incorrecto. Tienes # de intentos restantes antes de que la tarjeta SIM quede inutilizable permanentemente.}other{El código PUK de la tarjeta SIM es incorrecto. Tienes # intentos restantes antes de que la tarjeta SIM quede inutilizable permanentemente.}}"</string>
     <string name="kg_password_pin_failed" msgid="5136259126330604009">"Se produjo un error al desbloquear la tarjeta SIM con el PIN."</string>
     <string name="kg_password_puk_failed" msgid="6778867411556937118">"Se produjo un error al desbloquear la tarjeta SIM con el PUK."</string>
     <string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Cambiar método de entrada"</string>
@@ -118,8 +118,8 @@
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"El dispositivo se bloqueó de forma manual"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"No se reconoció"</string>
     <string name="kg_face_sensor_privacy_enabled" msgid="939511161763558512">"Activa acceso a cámara en Config. y usa Desb. facial"</string>
-    <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{Ingresa el PIN de la tarjeta SIM. Tienes # intento restante antes de que debas comunicarte con tu operador para desbloquear el dispositivo.}many{Ingresa el PIN de la tarjeta SIM. Tienes # intentos restantes.}other{Ingresa el PIN de la tarjeta SIM. Tienes # intentos restantes.}}"</string>
-    <string name="kg_password_default_puk_message" msgid="1025139786449741950">"{count,plural, =1{Se inhabilitó la tarjeta SIM. Para continuar, ingresa el código PUK. Tienes # intento restante antes de que la SIM quede inutilizable permanentemente. Comunícate con tu operador para conocer más detalles.}many{Se inhabilitó la tarjeta SIM. Para continuar, ingresa el código PUK. Tienes # intentos restantes antes de que la SIM quede inutilizable permanentemente. Comunícate con tu operador para conocer más detalles.}other{Se inhabilitó la tarjeta SIM. Para continuar, ingresa el código PUK. Tienes # intentos restantes antes de que la SIM quede inutilizable permanentemente. Comunícate con tu operador para conocer más detalles.}}"</string>
+    <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{Ingresa el PIN de la tarjeta SIM. Tienes # intento restante antes de que debas comunicarte con tu operador para desbloquear el dispositivo.}many{Ingresa el PIN de la tarjeta SIM. Tienes # de intentos restantes.}other{Ingresa el PIN de la tarjeta SIM. Tienes # intentos restantes.}}"</string>
+    <string name="kg_password_default_puk_message" msgid="1025139786449741950">"{count,plural, =1{Se inhabilitó la tarjeta SIM. Para continuar, ingresa el código PUK. Tienes # intento restante antes de que la SIM quede inutilizable permanentemente. Comunícate con tu operador para conocer más detalles.}many{Se inhabilitó la tarjeta SIM. Para continuar, ingresa el código PUK. Tienes # de intentos restantes antes de que la SIM quede inutilizable permanentemente. Comunícate con tu operador para conocer más detalles.}other{Se inhabilitó la tarjeta SIM. Para continuar, ingresa el código PUK. Tienes # intentos restantes antes de que la SIM quede inutilizable permanentemente. Comunícate con tu operador para conocer más detalles.}}"</string>
     <string name="clock_title_default" msgid="6342735240617459864">"Predeterminado"</string>
     <string name="clock_title_bubble" msgid="2204559396790593213">"Burbuja"</string>
     <string name="clock_title_analog" msgid="8409262532900918273">"Analógico"</string>
diff --git a/packages/SystemUI/res-keyguard/values-es/strings.xml b/packages/SystemUI/res-keyguard/values-es/strings.xml
index 3a09852..2d18c1c 100644
--- a/packages/SystemUI/res-keyguard/values-es/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-es/strings.xml
@@ -67,7 +67,7 @@
     <string name="kg_bio_too_many_attempts_pattern" msgid="736884689355181602">"Demasiados intentos, se necesita el patrón"</string>
     <string name="kg_unlock_with_pin_or_fp" msgid="5635161174698729890">"Desbloquea con PIN o huella digital"</string>
     <string name="kg_unlock_with_password_or_fp" msgid="2251295907826814237">"Desbloquea con contraseña o huella digital"</string>
-    <string name="kg_unlock_with_pattern_or_fp" msgid="2391870539909135046">"Desbloquea con patrón o huella digital"</string>
+    <string name="kg_unlock_with_pattern_or_fp" msgid="2391870539909135046">"Desbloquea con tu patrón o huella digital"</string>
     <string name="kg_prompt_after_dpm_lock" msgid="6002804765868345917">"Por política del trabajo, se ha bloqueado el dispositivo para mayor seguridad"</string>
     <string name="kg_prompt_after_user_lockdown_pin" msgid="5374732179740050373">"Se necesita el PIN después del bloqueo"</string>
     <string name="kg_prompt_after_user_lockdown_password" msgid="9097968458291129795">"Se necesita la contraseña después del bloqueo"</string>
diff --git a/packages/SystemUI/res-keyguard/values-eu/strings.xml b/packages/SystemUI/res-keyguard/values-eu/strings.xml
index 41c3e06..83a607b 100644
--- a/packages/SystemUI/res-keyguard/values-eu/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-eu/strings.xml
@@ -42,7 +42,7 @@
     <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIMa PUKaren bidez desblokeatu behar da."</string>
     <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"SIMa desblokeatzen…"</string>
     <string name="keyguard_accessibility_pin_area" msgid="7403009340414014734">"PIN kodearen eremua"</string>
-    <string name="keyguard_accessibility_password" msgid="3524161948484801450">"Gailuaren pasahitza"</string>
+    <string name="keyguard_accessibility_password" msgid="3524161948484801450">"Gailuko pasahitza"</string>
     <string name="keyguard_accessibility_sim_pin_area" msgid="6272116591533888062">"SIM txartelaren PIN kodearen eremua"</string>
     <string name="keyguard_accessibility_sim_puk_area" msgid="5537294043180237374">"SIM txartelaren PUK kodearen eremua"</string>
     <string name="keyboardview_keycode_delete" msgid="8489719929424895174">"Ezabatu"</string>
diff --git a/packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml b/packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml
index 6eea6c9..82a553c 100644
--- a/packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml
@@ -27,7 +27,7 @@
     <string name="keyguard_enter_your_password" msgid="7225626204122735501">"Entrez votre mot de passe"</string>
     <string name="keyguard_enter_password" msgid="6483623792371009758">"Entrez le mot de passe"</string>
     <string name="keyguard_sim_error_message_short" msgid="633630844240494070">"Cette carte n\'est pas valide."</string>
-    <string name="keyguard_charged" msgid="5478247181205188995">"Chargé"</string>
+    <string name="keyguard_charged" msgid="5478247181205188995">"Chargée"</string>
     <string name="keyguard_plugged_in_wireless" msgid="2537874724955057383">"<xliff:g id="PERCENTAGE">%s</xliff:g> • En recharge sans fil"</string>
     <string name="keyguard_plugged_in_dock" msgid="2122073051904360987">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Recharge en cours…"</string>
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"En recharge : <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
@@ -83,7 +83,7 @@
     <string name="kg_primary_auth_locked_out_pin" msgid="5492230176361601475">"Trop de tentatives avec un NIP incorrect"</string>
     <string name="kg_primary_auth_locked_out_pattern" msgid="8266214607346180952">"Trop de tentatives avec un schéma incorrect"</string>
     <string name="kg_primary_auth_locked_out_password" msgid="6170245108400198659">"Trop de tentatives avec un mot de passe incorrect"</string>
-    <string name="kg_too_many_failed_attempts_countdown" msgid="2038195171919795529">"{count,plural, =1{Réessayez dans # seconde.}one{Réessayez dans # seconde.}many{Réessayez dans # secondes.}other{Réessayez dans # secondes.}}"</string>
+    <string name="kg_too_many_failed_attempts_countdown" msgid="2038195171919795529">"{count,plural, =1{Réessayez dans # seconde.}one{Réessayez dans # seconde.}many{Réessayez dans # de secondes.}other{Réessayez dans # secondes.}}"</string>
     <string name="kg_sim_pin_instructions" msgid="1942424305184242951">"Entrez le NIP de la carte SIM."</string>
     <string name="kg_sim_pin_instructions_multi" msgid="3639863309953109649">"Entrez le NIP de la carte SIM pour « <xliff:g id="CARRIER">%1$s</xliff:g> »."</string>
     <string name="kg_sim_lock_esim_instructions" msgid="5577169988158738030">"<xliff:g id="PREVIOUS_MSG">%1$s</xliff:g> Désactivez la carte eSIM pour utiliser l\'appareil sans service cellulaire."</string>
@@ -98,7 +98,7 @@
     <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"Vous avez entré un mot de passe incorrect à <xliff:g id="NUMBER_0">%1$d</xliff:g> reprises.\n\nVeuillez réessayer dans <xliff:g id="NUMBER_1">%2$d</xliff:g> secondes."</string>
     <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"Vous avez dessiné un schéma de déverrouillage incorrect à <xliff:g id="NUMBER_0">%1$d</xliff:g> reprises.\n\nVeuillez réessayer dans <xliff:g id="NUMBER_1">%2$d</xliff:g> secondes."</string>
     <string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"NIP de carte SIM incorrect. Vous devez maintenant communiquer avec votre fournisseur de services pour déverrouiller votre appareil."</string>
-    <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{NIP de la carte SIM incorrect. Il vous reste # tentative. Après cela, vous devrez communiquer avec votre fournisseur de services pour déverrouiller votre appareil.}one{NIP de la carte SIM incorrect. Il vous reste # tentative. }many{NIP de la carte SIM incorrect. Il vous reste # de tentatives. }other{NIP de la carte SIM incorrect. Il vous reste # tentatives. }}"</string>
+    <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{NIP de la carte SIM incorrect. Il vous reste # tentative. Après cela, vous devrez communiquer avec votre opérateur pour déverrouiller votre appareil.}one{NIP de la carte SIM incorrect. Il vous reste # tentative. }many{NIP de la carte SIM incorrect. Il vous reste # de tentatives. }other{NIP de la carte SIM incorrect. Il vous reste # tentatives. }}"</string>
     <string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"La carte SIM est inutilisable. Communiquez avec votre fournisseur de services."</string>
     <string name="kg_password_wrong_puk_code" msgid="6820515467645087827">"{count,plural, =1{Code PUK de la carte SIM incorrect. Il vous reste # tentative avant que votre carte SIM devienne définitivement inutilisable.}one{Code PUK de la carte SIM incorrect. Il vous reste # tentative avant que votre carte SIM devienne définitivement inutilisable.}many{Code PUK de la carte SIM incorrect. Il vous reste # de tentatives avant que votre carte SIM devienne définitivement inutilisable.}other{Code PUK de la carte SIM incorrect. Il vous reste # tentatives avant que votre carte SIM devienne définitivement inutilisable.}}"</string>
     <string name="kg_password_pin_failed" msgid="5136259126330604009">"Le déverrouillage par NIP de la carte SIM a échoué."</string>
diff --git a/packages/SystemUI/res-keyguard/values-it/strings.xml b/packages/SystemUI/res-keyguard/values-it/strings.xml
index 722d43d..609a517 100644
--- a/packages/SystemUI/res-keyguard/values-it/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-it/strings.xml
@@ -83,7 +83,7 @@
     <string name="kg_primary_auth_locked_out_pin" msgid="5492230176361601475">"Troppi tentativi con il PIN errato"</string>
     <string name="kg_primary_auth_locked_out_pattern" msgid="8266214607346180952">"Troppi tentativi con la sequenza errata"</string>
     <string name="kg_primary_auth_locked_out_password" msgid="6170245108400198659">"Troppi tentativi con la password errata"</string>
-    <string name="kg_too_many_failed_attempts_countdown" msgid="2038195171919795529">"{count,plural, =1{Riprova fra # secondo.}many{Riprova fra # secondi.}other{Riprova fra # secondi.}}"</string>
+    <string name="kg_too_many_failed_attempts_countdown" msgid="2038195171919795529">"{count,plural, =1{Riprova fra # secondo.}many{Riprova fra # di secondi.}other{Riprova fra # secondi.}}"</string>
     <string name="kg_sim_pin_instructions" msgid="1942424305184242951">"Inserisci il PIN della SIM."</string>
     <string name="kg_sim_pin_instructions_multi" msgid="3639863309953109649">"Inserisci il PIN della SIM \"<xliff:g id="CARRIER">%1$s</xliff:g>\"."</string>
     <string name="kg_sim_lock_esim_instructions" msgid="5577169988158738030">"<xliff:g id="PREVIOUS_MSG">%1$s</xliff:g> Disattiva la eSIM per usare il dispositivo senza servizio dati mobile."</string>
@@ -98,9 +98,9 @@
     <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"Hai digitato la tua password <xliff:g id="NUMBER_0">%1$d</xliff:g> volte in modo errato. \n\nRiprova tra <xliff:g id="NUMBER_1">%2$d</xliff:g> secondi."</string>
     <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"<xliff:g id="NUMBER_0">%1$d</xliff:g> tentativi errati di inserimento della sequenza di sblocco. \n\nRiprova tra <xliff:g id="NUMBER_1">%2$d</xliff:g> secondi."</string>
     <string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"Codice PIN della SIM errato. Devi contattare l\'operatore per sbloccare il dispositivo."</string>
-    <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{Codice PIN della SIM errato. Hai ancora # tentativo a disposizione, dopodiché dovrai contattare l\'operatore per sbloccare il dispositivo.}many{Codice PIN della SIM errato. Hai ancora # tentativi a disposizione. }other{Codice PIN della SIM errato. Hai ancora # tentativi a disposizione. }}"</string>
+    <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{Codice PIN della SIM errato. Hai ancora # tentativo a disposizione, dopodiché dovrai contattare l\'operatore per sbloccare il dispositivo.}many{Codice PIN della SIM errato. Hai ancora # di tentativi a disposizione. }other{Codice PIN della SIM errato. Hai ancora # tentativi a disposizione. }}"</string>
     <string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"SIM inutilizzabile. Contatta il tuo operatore."</string>
-    <string name="kg_password_wrong_puk_code" msgid="6820515467645087827">"{count,plural, =1{Codice PUK della SIM errato. Hai ancora # tentativo a disposizione prima che la SIM diventi definitivamente inutilizzabile.}many{Codice PUK della SIM errato. Hai ancora # tentativi a disposizione prima che la SIM diventi definitivamente inutilizzabile.}other{Codice PUK della SIM errato. Hai ancora # tentativi a disposizione prima che la SIM diventi definitivamente inutilizzabile.}}"</string>
+    <string name="kg_password_wrong_puk_code" msgid="6820515467645087827">"{count,plural, =1{Codice PUK della SIM errato. Hai ancora # tentativo a disposizione prima che la SIM diventi definitivamente inutilizzabile.}many{Codice PUK della SIM errato. Hai ancora # di tentativi a disposizione prima che la SIM diventi definitivamente inutilizzabile.}other{Codice PUK della SIM errato. Hai ancora # tentativi a disposizione prima che la SIM diventi definitivamente inutilizzabile.}}"</string>
     <string name="kg_password_pin_failed" msgid="5136259126330604009">"Operazione con PIN della SIM non riuscita."</string>
     <string name="kg_password_puk_failed" msgid="6778867411556937118">"Operazione con PUK della SIM non riuscita."</string>
     <string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Cambia metodo di immissione"</string>
@@ -118,7 +118,7 @@
     <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Il dispositivo è stato bloccato manualmente"</string>
     <string name="kg_face_not_recognized" msgid="7903950626744419160">"Non riconosciuto"</string>
     <string name="kg_face_sensor_privacy_enabled" msgid="939511161763558512">"Sblocco con volto richiede l\'accesso alla fotocamera"</string>
-    <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{Inserisci il codice PIN della SIM. Hai ancora # tentativo a disposizione, dopodiché dovrai contattare l\'operatore per sbloccare il dispositivo.}many{Inserisci il PIN della SIM. Hai a disposizione ancora # tentativi.}other{Inserisci il PIN della SIM. Hai a disposizione ancora # tentativi.}}"</string>
+    <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{Inserisci il codice PIN della SIM. Hai ancora # tentativo a disposizione, dopodiché dovrai contattare l\'operatore per sbloccare il dispositivo.}many{Inserisci il PIN della SIM. Hai a disposizione ancora # di tentativi.}other{Inserisci il PIN della SIM. Hai a disposizione ancora # tentativi.}}"</string>
     <string name="kg_password_default_puk_message" msgid="1025139786449741950">"{count,plural, =1{La scheda SIM è ora disattivata. Inserisci il codice PUK per continuare. Hai ancora # tentativo a disposizione prima che la SIM diventi definitivamente inutilizzabile. Per informazioni dettagliate, contatta l\'operatore.}many{La scheda SIM è ora disattivata. Inserisci il codice PUK per continuare. Hai ancora # tentativi a disposizione prima che la SIM diventi definitivamente inutilizzabile. Per informazioni dettagliate, contatta l\'operatore.}other{La scheda SIM è ora disattivata. Inserisci il codice PUK per continuare. Hai ancora # tentativi a disposizione prima che la SIM diventi definitivamente inutilizzabile. Per informazioni dettagliate, contatta l\'operatore.}}"</string>
     <string name="clock_title_default" msgid="6342735240617459864">"Predefinito"</string>
     <string name="clock_title_bubble" msgid="2204559396790593213">"Bolla"</string>
diff --git a/packages/SystemUI/res-product/values-es/strings.xml b/packages/SystemUI/res-product/values-es/strings.xml
index d39c6ed..eb7d849 100644
--- a/packages/SystemUI/res-product/values-es/strings.xml
+++ b/packages/SystemUI/res-product/values-es/strings.xml
@@ -58,7 +58,7 @@
     <string name="high_temp_dialog_message" product="default" msgid="4272882413847595625">"El teléfono intentará enfriarse automáticamente. Puedes seguir usándolo, pero es posible que funcione más lento.\n\nUna vez que el teléfono se haya enfriado, funcionará con normalidad."</string>
     <string name="high_temp_dialog_message" product="device" msgid="263861943935989046">"El dispositivo intentará enfriarse automáticamente. Puedes seguir usándolo, pero es posible que funcione más lento.\n\nUna vez que el dispositivo se haya enfriado, funcionará con normalidad."</string>
     <string name="high_temp_dialog_message" product="tablet" msgid="5613713326841935537">"La tablet intentará enfriarse automáticamente. Puedes seguir usándola, pero es posible que funcione más lenta.\n\nUna vez que la tablet se haya enfriado, funcionará con normalidad."</string>
-    <string name="security_settings_sfps_enroll_find_sensor_message" product="tablet" msgid="3726972508570143945">"El sensor de huellas digitales está en el botón de encendido. Es el botón plano situado junto al botón de volumen con relieve cerca de una de la esquinas de la tablet."</string>
+    <string name="security_settings_sfps_enroll_find_sensor_message" product="tablet" msgid="3726972508570143945">"El sensor de huellas digitales está en el botón de encendido. Es el botón plano situado junto al botón de volumen con relieve en el lateral de la tablet."</string>
     <string name="security_settings_sfps_enroll_find_sensor_message" product="device" msgid="2929467060295094725">"El sensor de huellas digitales está en el botón de encendido. Es el botón plano situado junto al botón de volumen con relieve en el lateral del dispositivo."</string>
     <string name="security_settings_sfps_enroll_find_sensor_message" product="default" msgid="8582726566542997639">"El sensor de huellas digitales está en el botón de encendido. Es el botón plano situado junto al botón de volumen con relieve en el lateral del teléfono."</string>
     <string name="global_action_lock_message" product="default" msgid="7092460751050168771">"Desbloquea el teléfono para ver más opciones"</string>
diff --git a/packages/SystemUI/res-product/values-fr-rCA-feminine/strings.xml b/packages/SystemUI/res-product/values-fr-rCA-feminine/strings.xml
new file mode 100644
index 0000000..96393c5
--- /dev/null
+++ b/packages/SystemUI/res-product/values-fr-rCA-feminine/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="kg_failed_attempts_now_erasing_user" product="default" msgid="3051962486994265014">"Vous avez tenté de déverrouiller ce téléphone à <xliff:g id="NUMBER">%d</xliff:g> reprises. Cette utilisatrice sera supprimée, ce qui entraîne la suppression de toutes ses données."</string>
+</resources>
diff --git a/packages/SystemUI/res-product/values-fr-rCA-masculine/strings.xml b/packages/SystemUI/res-product/values-fr-rCA-masculine/strings.xml
new file mode 100644
index 0000000..5dd186f
--- /dev/null
+++ b/packages/SystemUI/res-product/values-fr-rCA-masculine/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="kg_failed_attempts_now_erasing_user" product="default" msgid="3051962486994265014">"Vous avez tenté de déverrouiller ce téléphone à <xliff:g id="NUMBER">%d</xliff:g> reprises. Cet utilisateur sera supprimé, ce qui entraîne la suppression de toutes ses données."</string>
+</resources>
diff --git a/packages/SystemUI/res-product/values-fr-rCA-neuter/strings.xml b/packages/SystemUI/res-product/values-fr-rCA-neuter/strings.xml
new file mode 100644
index 0000000..7c9a362
--- /dev/null
+++ b/packages/SystemUI/res-product/values-fr-rCA-neuter/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="kg_failed_attempts_now_erasing_user" product="default" msgid="3051962486994265014">"Vous avez tenté de déverrouiller ce téléphone à <xliff:g id="NUMBER">%d</xliff:g> reprises. Cet·te utilisateur·trice sera supprimé·e, ce qui entraîne la suppression de toutes ses données."</string>
+</resources>
diff --git a/packages/SystemUI/res-product/values-fr-rCA/strings.xml b/packages/SystemUI/res-product/values-fr-rCA/strings.xml
index 15d3606..9ff7ff8 100644
--- a/packages/SystemUI/res-product/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res-product/values-fr-rCA/strings.xml
@@ -32,12 +32,12 @@
     <string name="kg_failed_attempts_now_wiping" product="default" msgid="6381835450014881813">"Vous avez tenté de déverrouiller ce téléphone à <xliff:g id="NUMBER">%d</xliff:g> reprises. Ce téléphone sera réinitialisé, ce qui entraîne la suppression de toutes les données qu\'il contient."</string>
     <string name="kg_failed_attempts_almost_at_erase_user" product="tablet" msgid="7325071812832605911">"Vous avez tenté de déverrouiller cette tablette à <xliff:g id="NUMBER_0">%1$d</xliff:g> reprises. Après <xliff:g id="NUMBER_1">%2$d</xliff:g> tentative(s) infructueuse(s) supplémentaire(s), cet utilisateur sera supprimé, ce qui entraînera la suppression de toutes ses données."</string>
     <string name="kg_failed_attempts_almost_at_erase_user" product="default" msgid="8110939900089863103">"Vous avez tenté de déverrouiller ce téléphone à <xliff:g id="NUMBER_0">%1$d</xliff:g> reprises. Après <xliff:g id="NUMBER_1">%2$d</xliff:g> tentative(s) infructueuse(s) supplémentaire(s), cet utilisateur sera supprimé, ce qui entraînera la suppression de toutes ses données."</string>
-    <string name="kg_failed_attempts_now_erasing_user" product="tablet" msgid="8509811676952707883">"Vous avez tenté de déverrouiller cette tablette à <xliff:g id="NUMBER">%d</xliff:g> reprises. Cet utilisateur sera supprimé, ce qui entraîne la suppression de toutes ses données."</string>
+    <string name="kg_failed_attempts_now_erasing_user" product="tablet" msgid="8509811676952707883">"Vous avez tenté de déverrouiller cette tablette à <xliff:g id="NUMBER">%d</xliff:g> reprises. Cet utilisateur sera supprimé, ce qui entraînera la suppression de toutes ses données."</string>
     <string name="kg_failed_attempts_now_erasing_user" product="default" msgid="3051962486994265014">"Vous avez tenté de déverrouiller ce téléphone à <xliff:g id="NUMBER">%d</xliff:g> reprises. Cet utilisateur sera supprimé, ce qui entraîne la suppression de toutes ses données."</string>
-    <string name="kg_failed_attempts_almost_at_erase_profile" product="tablet" msgid="1049523640263353830">"Vous avez tenté de déverrouiller cette tablette à <xliff:g id="NUMBER_0">%1$d</xliff:g> reprises. Après <xliff:g id="NUMBER_1">%2$d</xliff:g> tentative(s) infructueuse(s) supplémentaire(s), le profil professionnel sera supprimé, ce qui entraînera la suppression de toutes ses données."</string>
-    <string name="kg_failed_attempts_almost_at_erase_profile" product="default" msgid="3280816298678433681">"Vous avez tenté de déverrouiller ce téléphone à <xliff:g id="NUMBER_0">%1$d</xliff:g> reprises. Après <xliff:g id="NUMBER_1">%2$d</xliff:g> tentative(s) infructueuse(s) supplémentaire(s), le profil professionnel sera supprimé, ce qui entraînera la suppression de toutes ses données."</string>
-    <string name="kg_failed_attempts_now_erasing_profile" product="tablet" msgid="4417100487251371559">"Vous avez tenté de déverrouiller cette tablette à <xliff:g id="NUMBER">%d</xliff:g> reprises. Le profil professionnel sera supprimé, ce qui entraîne la suppression de toutes ses données."</string>
-    <string name="kg_failed_attempts_now_erasing_profile" product="default" msgid="4682221342671290678">"Vous avez tenté de déverrouiller ce téléphone à <xliff:g id="NUMBER">%d</xliff:g> reprises. Le profil professionnel sera supprimé, ce qui entraîne la suppression de toutes ses données."</string>
+    <string name="kg_failed_attempts_almost_at_erase_profile" product="tablet" msgid="1049523640263353830">"Vous avez tenté de déverrouiller cette tablette à <xliff:g id="NUMBER_0">%1$d</xliff:g> reprises. Après <xliff:g id="NUMBER_1">%2$d</xliff:g> tentatives infructueuses supplémentaires, le profil professionnel sera supprimé, ce qui entraînera la suppression de toutes ses données."</string>
+    <string name="kg_failed_attempts_almost_at_erase_profile" product="default" msgid="3280816298678433681">"Vous avez tenté de déverrouiller ce téléphone à <xliff:g id="NUMBER_0">%1$d</xliff:g> reprises. Après <xliff:g id="NUMBER_1">%2$d</xliff:g> tentatives infructueuses supplémentaires, le profil professionnel sera supprimé, ce qui entraînera la suppression de toutes ses données."</string>
+    <string name="kg_failed_attempts_now_erasing_profile" product="tablet" msgid="4417100487251371559">"Vous avez incorrectement tenté de déverrouiller cette tablette à <xliff:g id="NUMBER">%d</xliff:g> reprises. Le profil professionnel sera retiré, ce qui entraîne la suppression de toutes ses données."</string>
+    <string name="kg_failed_attempts_now_erasing_profile" product="default" msgid="4682221342671290678">"Vous avez incorrectement tenté de déverrouiller ce téléphone à <xliff:g id="NUMBER">%d</xliff:g> reprises. Le profil professionnel sera retiré, ce qui entraînera la suppression de toutes ses données."</string>
     <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="1860049973474855672">"Vous avez dessiné un schéma de déverrouillage incorrect à <xliff:g id="NUMBER_0">%1$d</xliff:g> reprises. Si vous échouez encore <xliff:g id="NUMBER_1">%2$d</xliff:g> fois, vous devrez déverrouiller votre tablette à l\'aide d\'un compte de courriel.\n\nVeuillez réessayer dans <xliff:g id="NUMBER_2">%3$d</xliff:g> secondes."</string>
     <string name="kg_failed_attempts_almost_at_login" product="default" msgid="44112553371516141">"Vous avez dessiné un schéma de déverrouillage incorrect à <xliff:g id="NUMBER_0">%1$d</xliff:g> reprises. Si vous échouez encore <xliff:g id="NUMBER_1">%2$d</xliff:g> fois, vous devrez déverrouiller votre téléphone à l\'aide d\'un compte de courriel.\n\nVeuillez réessayer dans <xliff:g id="NUMBER_2">%3$d</xliff:g> secondes."</string>
     <string name="thermal_shutdown_title" product="default" msgid="8039593017174903505">"Le téléphone s\'est éteint; surchauffage."</string>
diff --git a/packages/SystemUI/res/color/disconnected_network_primary_color.xml b/packages/SystemUI/res/color/disconnected_network_primary_color.xml
new file mode 100644
index 0000000..536bf78
--- /dev/null
+++ b/packages/SystemUI/res/color/disconnected_network_primary_color.xml
@@ -0,0 +1,20 @@
+<!--
+  ~ Copyright (C) 2024 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
+    <item android:color="?androidprv:attr/materialColorPrimaryContainer" />
+</selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/brightness_bar.xml b/packages/SystemUI/res/drawable/brightness_bar.xml
new file mode 100644
index 0000000..2afe164
--- /dev/null
+++ b/packages/SystemUI/res/drawable/brightness_bar.xml
@@ -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.
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+        android:width="200dp"
+        android:height="32dp"
+        android:viewportWidth="304"
+        android:viewportHeight="48">
+<path
+    android:pathData="M2,22L302,22A2,2 0,0 1,304 24L304,24A2,2 0,0 1,302 26L2,26A2,2 0,0 1,0 24L0,24A2,2 0,0 1,2 22z"
+    android:fillColor="@color/brightness_slider_track"/>
+<path
+    android:pathData="M24,0L205.71,0A24,24 0,0 1,229.71 24L229.71,24A24,24 0,0 1,205.71 48L24,48A24,24 0,0 1,0 24L0,24A24,24 0,0 1,24 0z"
+    android:fillColor="?attr/shadeActive"/>
+<path
+    android:pathData="M0,24C0,10.75 10.75,0 24,0H63.85V48H24C10.75,48 0,37.25 0,24Z"
+    android:fillColor="?androidprv:attr/colorAccentPrimaryVariant"/>
+<path
+    android:pathData="M208.98,21.26V17.37H205.09L202.34,14.62L199.6,17.37H195.71V21.26L192.96,24L195.71,26.75V30.63H199.6L202.34,33.38L205.09,30.63H208.98V26.75L211.72,24L208.98,21.26ZM207.32,26.06V28.98H204.4L202.34,31.03L200.29,28.98H197.37V26.06L195.31,24L197.37,21.94V19.02H200.29L202.34,16.97L204.4,19.02H207.32V21.94L209.37,24L207.32,26.06ZM206.49,24C206.49,26.29 204.63,28.15 202.34,28.15V19.85C204.63,19.85 206.49,21.71 206.49,24Z"
+    android:fillColor="?attr/onShadeActive"
+    android:fillType="evenOdd"/>
+</vector>
+
diff --git a/packages/SystemUI/res/drawable/ic_arrow_back_2.xml b/packages/SystemUI/res/drawable/ic_arrow_back_2.xml
new file mode 100644
index 0000000..8522d38
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_arrow_back_2.xml
@@ -0,0 +1,24 @@
+<!--
+  ~ Copyright (C) 2024 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportHeight="960"
+    android:viewportWidth="960">
+    <path
+        android:fillColor="#000"
+        android:pathData="M640,760L200,480L640,200L640,760ZM560,480L560,480L560,480ZM560,614L560,346L350,480L560,614Z" />
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_check_box_outline_blank.xml b/packages/SystemUI/res/drawable/ic_check_box_outline_blank.xml
new file mode 100644
index 0000000..f413d900
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_check_box_outline_blank.xml
@@ -0,0 +1,24 @@
+<!--
+  ~ Copyright (C) 2024 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="960"
+    android:viewportHeight="960">
+    <path
+        android:fillColor="#000"
+        android:pathData="M200,840Q167,840 143.5,816.5Q120,793 120,760L120,200Q120,167 143.5,143.5Q167,120 200,120L760,120Q793,120 816.5,143.5Q840,167 840,200L840,760Q840,793 816.5,816.5Q793,840 760,840L200,840ZM200,760L760,760Q760,760 760,760Q760,760 760,760L760,200Q760,200 760,200Q760,200 760,200L200,200Q200,200 200,200Q200,200 200,200L200,760Q200,760 200,760Q200,760 200,760Z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_radio_button_unchecked.xml b/packages/SystemUI/res/drawable/ic_radio_button_unchecked.xml
new file mode 100644
index 0000000..5bf914b
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_radio_button_unchecked.xml
@@ -0,0 +1,24 @@
+<!--
+  ~ Copyright (C) 2024 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="960"
+    android:viewportHeight="960">
+    <path
+        android:fillColor="#000"
+        android:pathData="M480,880Q397,880 324,848.5Q251,817 197,763Q143,709 111.5,636Q80,563 80,480Q80,397 111.5,324Q143,251 197,197Q251,143 324,111.5Q397,80 480,80Q563,80 636,111.5Q709,143 763,197Q817,251 848.5,324Q880,397 880,480Q880,563 848.5,636Q817,709 763,763Q709,817 636,848.5Q563,880 480,880ZM480,800Q614,800 707,707Q800,614 800,480Q800,346 707,253Q614,160 480,160Q346,160 253,253Q160,346 160,480Q160,614 253,707Q346,800 480,800ZM480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Z"/>
+</vector>
diff --git a/packages/SystemUI/res/layout/accessibility_deprecate_extra_dim_dialog.xml b/packages/SystemUI/res/layout/accessibility_deprecate_extra_dim_dialog.xml
new file mode 100644
index 0000000..e839f4c
--- /dev/null
+++ b/packages/SystemUI/res/layout/accessibility_deprecate_extra_dim_dialog.xml
@@ -0,0 +1,41 @@
+<!--
+  ~ Copyright (C) 2024 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/illustration_frame"
+    android:orientation="vertical"
+    android:paddingHorizontal="16dp"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:layout_gravity="center">
+
+    <ImageView
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:adjustViewBounds="true"
+        android:layout_marginVertical="24dp"
+        android:scaleType="fitCenter"
+        android:importantForAccessibility="no"
+        android:theme="@style/Theme.SystemUI.QuickSettings"
+        android:src="@drawable/brightness_bar"/>
+    <TextView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center"
+        android:textSize="16sp"
+        android:text="@string/accessibility_deprecate_extra_dim_dialog_description"
+        android:textAppearance="@style/TextAppearance.Dialog.Body"/>
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/app_clips_backlinks_drop_down_entry.xml b/packages/SystemUI/res/layout/app_clips_backlinks_drop_down_entry.xml
new file mode 100644
index 0000000..7eab340
--- /dev/null
+++ b/packages/SystemUI/res/layout/app_clips_backlinks_drop_down_entry.xml
@@ -0,0 +1,24 @@
+<?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.
+  -->
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="48dp"
+    android:drawablePadding="4dp"
+    android:ellipsize="end"
+    android:gravity="center_vertical"
+    android:paddingHorizontal="8dp"
+    android:textColor="?android:textColorSecondary" />
diff --git a/packages/SystemUI/res/layout/biometric_prompt_one_pane_layout.xml b/packages/SystemUI/res/layout/biometric_prompt_one_pane_layout.xml
index 4670f34..3b3ed39 100644
--- a/packages/SystemUI/res/layout/biometric_prompt_one_pane_layout.xml
+++ b/packages/SystemUI/res/layout/biometric_prompt_one_pane_layout.xml
@@ -10,7 +10,7 @@
         android:id="@+id/background"
         android:layout_width="0dp"
         android:layout_height="0dp"
-        android:contentDescription="@string/biometric_dialog_empty_space_description"
+        android:contentDescription="@string/cancel"
         app:layout_constraintBottom_toBottomOf="parent"
         app:layout_constraintEnd_toEndOf="parent"
         app:layout_constraintStart_toStartOf="parent"
diff --git a/packages/SystemUI/res/layout/biometric_prompt_two_pane_layout.xml b/packages/SystemUI/res/layout/biometric_prompt_two_pane_layout.xml
index c599f9e..2a00495 100644
--- a/packages/SystemUI/res/layout/biometric_prompt_two_pane_layout.xml
+++ b/packages/SystemUI/res/layout/biometric_prompt_two_pane_layout.xml
@@ -11,7 +11,7 @@
         android:id="@+id/background"
         android:layout_width="0dp"
         android:layout_height="0dp"
-        android:contentDescription="@string/biometric_dialog_empty_space_description"
+        android:contentDescription="@string/cancel"
         app:layout_constraintBottom_toBottomOf="parent"
         app:layout_constraintEnd_toEndOf="parent"
         app:layout_constraintStart_toStartOf="parent"
@@ -87,20 +87,21 @@
                 android:id="@+id/logo_description"
                 style="@style/TextAppearance.AuthCredential.LogoDescription"
                 android:layout_width="0dp"
-                android:layout_height="wrap_content"
+                android:layout_height="@dimen/biometric_prompt_logo_size"
+                android:gravity="start|center_vertical"
                 android:textAlignment="viewStart"
-                android:paddingStart="16dp"
-                app:layout_constraintBottom_toBottomOf="@+id/logo"
+                android:layout_marginStart="16dp"
+                app:layout_goneMarginStart="0dp"
+                app:layout_constraintBottom_toTopOf="@+id/title"
                 app:layout_constraintEnd_toEndOf="parent"
                 app:layout_constraintStart_toEndOf="@+id/logo"
-                app:layout_constraintTop_toTopOf="@+id/logo" />
+                app:layout_constraintTop_toTopOf="parent" />
 
             <TextView
                 android:id="@+id/title"
                 style="@style/TextAppearance.AuthCredential.Title"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
-                android:layout_marginTop="12dp"
                 android:gravity="@integer/biometric_dialog_text_gravity"
                 android:paddingHorizontal="0dp"
                 android:textAlignment="viewStart"
@@ -108,7 +109,7 @@
                 app:layout_constraintEnd_toEndOf="parent"
                 app:layout_constraintHorizontal_bias="0.0"
                 app:layout_constraintStart_toStartOf="parent"
-                app:layout_constraintTop_toBottomOf="@+id/logo"
+                app:layout_constraintTop_toBottomOf="@+id/logoBarrier"
                 app:layout_constraintVertical_bias="0.0"
                 app:layout_constraintVertical_chainStyle="packed" />
 
@@ -159,6 +160,15 @@
                 app:layout_constraintVertical_bias="0.0" />
 
             <androidx.constraintlayout.widget.Barrier
+                android:id="@+id/logoBarrier"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                app:barrierMargin="12dp"
+                app:barrierAllowsGoneWidgets="false"
+                app:barrierDirection="bottom"
+                app:constraint_referenced_ids="logo_description, logo" />
+
+            <androidx.constraintlayout.widget.Barrier
                 android:id="@+id/contentBarrier"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
diff --git a/packages/SystemUI/res/layout/custom_trace_settings_dialog.xml b/packages/SystemUI/res/layout/custom_trace_settings_dialog.xml
new file mode 100644
index 0000000..6180bf5
--- /dev/null
+++ b/packages/SystemUI/res/layout/custom_trace_settings_dialog.xml
@@ -0,0 +1,167 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2024 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:layout_gravity="center"
+    android:orientation="vertical" >
+
+    <TextView
+        android:id="@+id/categories"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center"
+        android:layout_marginTop="@dimen/qqs_layout_margin_top"
+        android:textAppearance="@style/TextAppearance.Dialog.Body.Message" />
+
+    <TextView
+        android:id="@+id/cpu_buffer_size"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center"
+        android:layout_marginTop="@dimen/qqs_layout_margin_top"
+        android:textAppearance="@style/TextAppearance.Dialog.Body.Message" />
+
+    <!-- Attach to Bugreport Switch -->
+    <LinearLayout
+        android:id="@+id/attach_to_bugreport_switch_container"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="@dimen/qqs_layout_margin_top"
+        android:orientation="horizontal">
+
+        <TextView
+            android:id="@+id/attach_to_bugreport_switch_label"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:minHeight="@dimen/screenrecord_option_icon_size"
+            android:layout_weight="1"
+            android:layout_gravity="fill_vertical"
+            android:gravity="start"
+            android:textAppearance="@style/TextAppearance.Dialog.Body.Message" />
+
+        <Switch
+            android:id="@+id/attach_to_bugreport_switch"
+            android:layout_width="wrap_content"
+            android:minHeight="@dimen/screenrecord_option_icon_size"
+            android:layout_height="wrap_content"
+            android:gravity="end"
+            android:layout_gravity="fill_vertical"
+            android:layout_weight="0" />
+    </LinearLayout>
+
+    <!-- Winscope Switch -->
+    <LinearLayout
+        android:id="@+id/winscope_switch_container"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="@dimen/qqs_layout_margin_top"
+        android:orientation="horizontal">
+
+        <TextView
+            android:id="@+id/winscope_switch_label"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:minHeight="@dimen/screenrecord_option_icon_size"
+            android:layout_weight="1"
+            android:layout_gravity="fill_vertical"
+            android:gravity="start"
+            android:textAppearance="@style/TextAppearance.Dialog.Body.Message"/>
+
+        <Switch
+            android:id="@+id/winscope_switch"
+            android:layout_width="wrap_content"
+            android:minHeight="@dimen/screenrecord_option_icon_size"
+            android:layout_height="wrap_content"
+            android:gravity="end"
+            android:layout_gravity="fill_vertical"
+            android:layout_weight="0" />
+    </LinearLayout>
+
+    <!-- Trace Debuggable Apps Switch -->
+    <LinearLayout
+        android:id="@+id/trace_debuggable_apps_switch_container"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="@dimen/qqs_layout_margin_top"
+        android:orientation="horizontal">
+
+        <TextView
+            android:id="@+id/debuggable_apps_switch_label"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:minHeight="@dimen/screenrecord_option_icon_size"
+            android:layout_weight="1"
+            android:layout_gravity="fill_vertical"
+            android:gravity="start"
+            android:textAppearance="@style/TextAppearance.Dialog.Body.Message" />
+
+        <Switch
+            android:id="@+id/trace_debuggable_apps_switch"
+            android:layout_width="wrap_content"
+            android:minHeight="@dimen/screenrecord_option_icon_size"
+            android:layout_height="wrap_content"
+            android:gravity="end"
+            android:layout_gravity="fill_vertical"
+            android:layout_weight="0" />
+    </LinearLayout>
+
+    <!-- Long Traces Switch -->
+    <LinearLayout
+        android:id="@+id/long_traces_switch_container"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="@dimen/qqs_layout_margin_top"
+        android:orientation="horizontal">
+
+        <TextView
+            android:id="@+id/long_traces_switch_label"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:minHeight="@dimen/screenrecord_option_icon_size"
+            android:layout_weight="1"
+            android:layout_gravity="fill_vertical"
+            android:gravity="start"
+            android:textAppearance="@style/TextAppearance.Dialog.Body.Message" />
+
+        <Switch
+            android:id="@+id/long_traces_switch"
+            android:layout_width="wrap_content"
+            android:minHeight="@dimen/screenrecord_option_icon_size"
+            android:layout_height="wrap_content"
+            android:gravity="end"
+            android:layout_gravity="fill_vertical"
+            android:layout_weight="0" />
+    </LinearLayout>
+
+    <TextView
+        android:id="@+id/long_trace_size"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center"
+        android:layout_marginTop="@dimen/qqs_layout_margin_top"
+        android:textAppearance="@style/TextAppearance.Dialog.Body.Message" />
+
+    <TextView
+        android:id="@+id/long_trace_duration"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center"
+        android:layout_marginTop="@dimen/qqs_layout_margin_top"
+        android:textAppearance="@style/TextAppearance.Dialog.Body.Message" />
+</LinearLayout>
+
diff --git a/packages/SystemUI/res/layout/hearing_tool_item.xml b/packages/SystemUI/res/layout/hearing_tool_item.xml
index 84462d0..ff2fbe07 100644
--- a/packages/SystemUI/res/layout/hearing_tool_item.xml
+++ b/packages/SystemUI/res/layout/hearing_tool_item.xml
@@ -20,7 +20,7 @@
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:orientation="vertical"
-    android:gravity="center"
+    android:gravity="top|center_horizontal"
     android:focusable="true"
     android:clickable="true"
     android:layout_weight="1">
@@ -46,8 +46,8 @@
         android:layout_height="wrap_content"
         android:layout_marginTop="@dimen/hearing_devices_layout_margin"
         android:ellipsize="end"
-        android:maxLines="1"
         android:textSize="12sp"
+        android:maxLines="3"
         android:textAppearance="@style/TextAppearance.Dialog.Body.Message"
         android:focusable="false" />
 </LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/internet_connectivity_dialog.xml b/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
index 22d156d..0029180 100644
--- a/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
+++ b/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
@@ -170,8 +170,7 @@
                         android:layout_height="28dp"
                         android:layout_marginStart="7dp"
                         android:layout_marginEnd="16dp"
-                        android:layout_gravity="center_vertical"
-                        android:background="?android:attr/textColorSecondary"/>
+                        android:layout_gravity="center_vertical"/>
 
                     <FrameLayout
                         android:layout_width="@dimen/settingslib_switch_track_width"
diff --git a/packages/SystemUI/res/layout/record_issue_dialog.xml b/packages/SystemUI/res/layout/record_issue_dialog.xml
index 30d7b0a..e30ae6e 100644
--- a/packages/SystemUI/res/layout/record_issue_dialog.xml
+++ b/packages/SystemUI/res/layout/record_issue_dialog.xml
@@ -35,7 +35,7 @@
         android:layout_height="wrap_content"
         android:text="@string/qs_record_issue_dropdown_prompt"
         android:lines="1"
-        android:drawableRight="@drawable/arrow_pointing_down"
+        android:drawableEnd="@drawable/arrow_pointing_down"
         android:layout_marginTop="@dimen/qqs_layout_margin_top"
         android:focusable="false"
         android:clickable="true" />
diff --git a/packages/SystemUI/res/raw/trackpad_back_edu.json b/packages/SystemUI/res/raw/trackpad_back_edu.json
new file mode 100644
index 0000000..908d26f
--- /dev/null
+++ b/packages/SystemUI/res/raw/trackpad_back_edu.json
@@ -0,0 +1 @@
+{"v":"5.12.1","fr":60,"ip":0,"op":900,"w":554,"h":564,"nm":"Trackpad-JSON_BackGesture-EDU","ddd":0,"assets":[{"id":"comp_0","nm":"Back_LeftDismiss","fr":60,"pfr":1,"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"release Scale","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"s":true,"x":{"a":0,"k":79},"y":{"a":0,"k":197}},"a":{"a":0,"k":[0,0,0]},"s":{"a":1,"k":[{"i":{"x":[0.1,0.1,0.1],"y":[1,1,1]},"o":{"x":[0.08,0.08,0.08],"y":[0.47,0.47,0]},"t":250,"s":[100,100,100]},{"i":{"x":[0.999,0.999,0.999],"y":[1,1,1]},"o":{"x":[0.3,0.3,0.3],"y":[0,0,0]},"t":254,"s":[105,105,100]},{"t":266,"s":[50,50,100]}]}},"ao":0,"ip":0,"op":451,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".onTertiaryFixed","cl":"onTertiaryFixed","parent":3,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":151,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":154,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":255,"s":[100]},{"t":258,"s":[0]}]},"r":{"a":0,"k":0},"p":{"k":[{"s":[-0.692,0,0],"t":149,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[3.308,0,0],"t":150,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[4.009,0,0],"t":151,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[8.291,0,0],"t":152,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[13.138,0,0],"t":153,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[15.452,0,0],"t":154,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[16.757,0,0],"t":155,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[17.542,0,0],"t":156,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[18.002,0,0],"t":157,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[18.238,0,0],"t":158,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[18.308,0,0],"t":159,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[21.331,0,0],"t":160,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[23.006,0,0],"t":161,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[23.308,0,0],"t":162,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[23.382,0,0],"t":163,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[23.657,0,0],"t":164,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[24.165,0,0],"t":165,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[24.794,0,0],"t":166,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[25.403,0,0],"t":167,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[25.942,0,0],"t":168,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[26.411,0,0],"t":169,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[26.822,0,0],"t":170,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[27.186,0,0],"t":171,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[27.511,0,0],"t":172,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[27.803,0,0],"t":173,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[28.069,0,0],"t":174,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[28.311,0,0],"t":175,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[28.534,0,0],"t":176,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[28.739,0,0],"t":177,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[28.928,0,0],"t":178,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[29.103,0,0],"t":179,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[29.267,0,0],"t":180,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[29.419,0,0],"t":181,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[29.56,0,0],"t":182,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[29.693,0,0],"t":183,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[29.816,0,0],"t":184,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[29.932,0,0],"t":185,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[30.041,0,0],"t":186,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[30.142,0,0],"t":187,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[30.238,0,0],"t":188,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[30.327,0,0],"t":189,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[30.411,0,0],"t":190,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[30.489,0,0],"t":191,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[30.563,0,0],"t":192,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[30.632,0,0],"t":193,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[30.696,0,0],"t":194,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[30.756,0,0],"t":195,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[30.812,0,0],"t":196,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[30.864,0,0],"t":197,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[30.913,0,0],"t":198,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[30.958,0,0],"t":199,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[31,0,0],"t":200,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[31.039,0,0],"t":201,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[31.074,0,0],"t":202,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[31.107,0,0],"t":203,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[31.137,0,0],"t":204,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[31.164,0,0],"t":205,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[31.188,0,0],"t":206,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[31.21,0,0],"t":207,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[31.23,0,0],"t":208,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[31.247,0,0],"t":209,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[31.274,0,0],"t":211,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[31.305,0,0],"t":215,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.36,"y":0},"t":150,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[5.459,5.2],[-3.459,0],[5.459,-5.2]],"c":false}]},{"i":{"x":0.02,"y":1},"o":{"x":0.167,"y":0.167},"t":152,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[4.779,4.88],[-3.459,0],[4.779,-4.88]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":159,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[3.459,7.2],[-3.459,0],[3.459,-7.2]],"c":false}]},{"i":{"x":0,"y":1},"o":{"x":0.12,"y":0},"t":162,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[3.459,7.2],[-3.459,0],[3.459,-7.2]],"c":false}]},{"t":217,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[3.459,9.2],[-3.459,0],[3.459,-9.2]],"c":false}]}]},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[0.121568627656,0.211764708161,0.101960785687,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":4},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Vector 1","bm":0,"hd":false}],"ip":0,"op":451,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".tertiaryFixedDim","cl":"tertiaryFixedDim","parent":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":257,"s":[100]},{"t":260,"s":[0]}]},"r":{"a":0,"k":0},"p":{"s":true,"x":{"a":1,"k":[{"i":{"x":[0.22],"y":[1]},"o":{"x":[0.06],"y":[0.15]},"t":160,"s":[-14]},{"t":189,"s":[0]}]},"y":{"a":0,"k":0}},"a":{"a":0,"k":[32,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"ef":[{"ty":5,"nm":"IndieCorners","np":21,"mn":"Pseudo/0.20784385308943532","ix":1,"en":1,"ef":[{"ty":7,"nm":"Align","mn":"Pseudo/0.20784385308943532-0001","ix":1,"v":{"a":0,"k":8}},{"ty":6,"nm":"Size","mn":"Pseudo/0.20784385308943532-0002","ix":2,"v":0},{"ty":0,"nm":"w","mn":"Pseudo/0.20784385308943532-0003","ix":3,"v":{"a":1,"k":[{"t":149,"s":[0],"h":1},{"i":{"x":[0.02],"y":[1]},"o":{"x":[0.365],"y":[0]},"t":150,"s":[8]},{"i":{"x":[0.1],"y":[1]},"o":{"x":[0.336],"y":[0]},"t":159,"s":[38]},{"i":{"x":[0.002],"y":[1]},"o":{"x":[0.119],"y":[0]},"t":162,"s":[48]},{"t":217,"s":[64]}]}},{"ty":0,"nm":"h","mn":"Pseudo/0.20784385308943532-0004","ix":4,"v":{"a":0,"k":48}},{"ty":6,"nm":"","mn":"Pseudo/0.20784385308943532-0005","ix":5,"v":0},{"ty":6,"nm":"Rounding","mn":"Pseudo/0.20784385308943532-0006","ix":6,"v":0},{"ty":7,"nm":"Same for all corners","mn":"Pseudo/0.20784385308943532-0007","ix":7,"v":{"a":0,"k":1}},{"ty":0,"nm":"All corners","mn":"Pseudo/0.20784385308943532-0008","ix":8,"v":{"a":1,"k":[{"i":{"x":[0.02],"y":[1]},"o":{"x":[0.365],"y":[0]},"t":150,"s":[80]},{"i":{"x":[0.1],"y":[1]},"o":{"x":[0.336],"y":[0]},"t":159,"s":[24]},{"t":162,"s":[80]}]}},{"ty":0,"nm":"tl","mn":"Pseudo/0.20784385308943532-0009","ix":9,"v":{"a":0,"k":12}},{"ty":0,"nm":"tr","mn":"Pseudo/0.20784385308943532-0010","ix":10,"v":{"a":0,"k":12}},{"ty":0,"nm":"br","mn":"Pseudo/0.20784385308943532-0011","ix":11,"v":{"a":0,"k":12}},{"ty":0,"nm":"bl","mn":"Pseudo/0.20784385308943532-0012","ix":12,"v":{"a":0,"k":12}},{"ty":6,"nm":"","mn":"Pseudo/0.20784385308943532-0013","ix":13,"v":0},{"ty":6,"nm":"Alignment","mn":"Pseudo/0.20784385308943532-0014","ix":14,"v":0},{"ty":0,"nm":"X Anchor %","mn":"Pseudo/0.20784385308943532-0015","ix":15,"v":{"a":0,"k":0}},{"ty":0,"nm":"Y Anchor %","mn":"Pseudo/0.20784385308943532-0016","ix":16,"v":{"a":0,"k":0}},{"ty":0,"nm":"X Position","mn":"Pseudo/0.20784385308943532-0017","ix":17,"v":{"a":0,"k":0}},{"ty":0,"nm":"Y Position ","mn":"Pseudo/0.20784385308943532-0018","ix":18,"v":{"a":0,"k":0}},{"ty":6,"nm":"","mn":"Pseudo/0.20784385308943532-0019","ix":19,"v":0}]}],"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"k":[{"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[0,-24],[0,-24],[0,-24],[0,-24],[0,24],[0,24],[0,24],[0,24]],"c":true}],"t":149,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-2.208,0],[0,0],[0,-2.208],[0,0],[2.208,0],[0,0],[0,2.208]],"o":[[0,-2.208],[0,0],[2.208,0],[0,0],[0,2.208],[0,0],[-2.208,0],[0,0]],"v":[[0,-20],[4,-24],[4,-24],[8,-20],[8,20],[4,24],[4,24],[0,20]],"c":true}],"t":150,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-2.594,0],[0,0],[0,-2.594],[0,0],[2.594,0],[0,0],[0,2.594]],"o":[[0,-2.594],[0,0],[2.594,0],[0,0],[0,2.594],[0,0],[-2.594,0],[0,0]],"v":[[0,-19.3],[4.7,-24],[4.7,-24],[9.401,-19.3],[9.401,19.3],[4.7,24],[4.7,24],[0,19.3]],"c":true}],"t":151,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-4.958,0],[0,0],[0,-4.958],[0,0],[4.958,0],[0,0],[0,4.958]],"o":[[0,-4.958],[0,0],[4.958,0],[0,0],[0,4.958],[0,0],[-4.958,0],[0,0]],"v":[[0,-15.017],[8.983,-24],[8.983,-24],[17.967,-15.017],[17.967,15.017],[8.983,24],[8.983,24],[0,15.017]],"c":true}],"t":152,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-7.632,0],[0,0],[0,-7.632],[0,0],[7.632,0],[0,0],[0,7.632]],"o":[[0,-7.632],[0,0],[7.632,0],[0,0],[0,7.632],[0,0],[-7.632,0],[0,0]],"v":[[0,-10.171],[13.829,-24],[13.829,-24],[27.659,-10.171],[27.659,10.171],[13.829,24],[13.829,24],[0,10.171]],"c":true}],"t":153,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-8.91,0],[0,0],[0,-8.91],[0,0],[8.91,0],[0,0],[0,8.91]],"o":[[0,-8.91],[0,0],[8.91,0],[0,0],[0,8.91],[0,0],[-8.91,0],[0,0]],"v":[[0,-7.856],[16.144,-24],[16.144,-24],[32.287,-7.856],[32.287,7.856],[16.144,24],[16.144,24],[0,7.856]],"c":true}],"t":154,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-9.63,0],[0,0],[0,-9.63],[0,0],[9.63,0],[0,0],[0,9.63]],"o":[[0,-9.63],[0,0],[9.63,0],[0,0],[0,9.63],[0,0],[-9.63,0],[0,0]],"v":[[0,-6.551],[17.449,-24],[17.449,-24],[34.898,-6.551],[34.898,6.551],[17.449,24],[17.449,24],[0,6.551]],"c":true}],"t":155,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-10.063,0],[0,0],[0,-10.063],[0,0],[10.063,0],[0,0],[0,10.063]],"o":[[0,-10.063],[0,0],[10.063,0],[0,0],[0,10.063],[0,0],[-10.063,0],[0,0]],"v":[[0,-5.766],[18.234,-24],[18.234,-24],[36.467,-5.766],[36.467,5.766],[18.234,24],[18.234,24],[0,5.766]],"c":true}],"t":156,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-10.317,0],[0,0],[0,-10.317],[0,0],[10.317,0],[0,0],[0,10.317]],"o":[[0,-10.317],[0,0],[10.317,0],[0,0],[0,10.317],[0,0],[-10.317,0],[0,0]],"v":[[0,-5.306],[18.694,-24],[18.694,-24],[37.388,-5.306],[37.388,5.306],[18.694,24],[18.694,24],[0,5.306]],"c":true}],"t":157,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-10.448,0],[0,0],[0,-10.448],[0,0],[10.448,0],[0,0],[0,10.448]],"o":[[0,-10.448],[0,0],[10.448,0],[0,0],[0,10.448],[0,0],[-10.448,0],[0,0]],"v":[[0,-5.07],[18.93,-24],[18.93,-24],[37.861,-5.07],[37.861,5.07],[18.93,24],[18.93,24],[0,5.07]],"c":true}],"t":158,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-10.486,0],[0,0],[0,-10.486],[0,0],[10.486,0],[0,0],[0,10.486]],"o":[[0,-10.486],[0,0],[10.486,0],[0,0],[0,10.486],[0,0],[-10.486,0],[0,0]],"v":[[0,-5],[19,-24],[19,-24],[38,-5],[38,5],[19,24],[19,24],[0,5]],"c":true}],"t":159,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-12.154,0],[0,0],[0,-12.154],[0,0],[12.154,0],[0,0],[0,12.154]],"o":[[0,-12.154],[0,0],[12.154,0],[0,0],[0,12.154],[0,0],[-12.154,0],[0,0]],"v":[[0,-1.977],[22.023,-24],[22.023,-24],[44.045,-1.977],[44.045,1.977],[22.023,24],[22.023,24],[0,1.977]],"c":true}],"t":160,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.079,0],[0,0],[0,-13.079],[0,0],[13.079,0],[0,0],[0,13.079]],"o":[[0,-13.079],[0,0],[13.079,0],[0,0],[0,13.079],[0,0],[-13.079,0],[0,0]],"v":[[0,-0.302],[23.698,-24],[23.698,-24],[47.396,-0.302],[47.396,0.302],[23.698,24],[23.698,24],[0,0.302]],"c":true}],"t":161,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[24,-24],[48,0],[48,0],[24,24],[24,24],[0,0]],"c":true}],"t":162,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[24.149,-24],[48.149,0],[48.149,0],[24.149,24],[24,24],[0,0]],"c":true}],"t":163,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[24.698,-24],[48.698,0],[48.698,0],[24.698,24],[24,24],[0,0]],"c":true}],"t":164,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[25.714,-24],[49.714,0],[49.714,0],[25.714,24],[24,24],[0,0]],"c":true}],"t":165,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[26.973,-24],[50.973,0],[50.973,0],[26.973,24],[24,24],[0,0]],"c":true}],"t":166,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[28.19,-24],[52.19,0],[52.19,0],[28.19,24],[24,24],[0,0]],"c":true}],"t":167,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[29.268,-24],[53.268,0],[53.268,0],[29.268,24],[24,24],[0,0]],"c":true}],"t":168,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[30.206,-24],[54.206,0],[54.206,0],[30.206,24],[24,24],[0,0]],"c":true}],"t":169,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[31.028,-24],[55.028,0],[55.028,0],[31.028,24],[24,24],[0,0]],"c":true}],"t":170,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[31.755,-24],[55.755,0],[55.755,0],[31.755,24],[24,24],[0,0]],"c":true}],"t":171,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[32.405,-24],[56.405,0],[56.405,0],[32.405,24],[24,24],[0,0]],"c":true}],"t":172,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[32.99,-24],[56.99,0],[56.99,0],[32.99,24],[24,24],[0,0]],"c":true}],"t":173,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[33.522,-24],[57.522,0],[57.522,0],[33.522,24],[24,24],[0,0]],"c":true}],"t":174,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[34.006,-24],[58.006,0],[58.006,0],[34.006,24],[24,24],[0,0]],"c":true}],"t":175,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[34.451,-24],[58.451,0],[58.451,0],[34.451,24],[24,24],[0,0]],"c":true}],"t":176,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[34.861,-24],[58.861,0],[58.861,0],[34.861,24],[24,24],[0,0]],"c":true}],"t":177,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[35.24,-24],[59.24,0],[59.24,0],[35.24,24],[24,24],[0,0]],"c":true}],"t":178,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[35.591,-24],[59.591,0],[59.591,0],[35.591,24],[24,24],[0,0]],"c":true}],"t":179,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[35.917,-24],[59.917,0],[59.917,0],[35.917,24],[24,24],[0,0]],"c":true}],"t":180,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[36.221,-24],[60.221,0],[60.221,0],[36.221,24],[24,24],[0,0]],"c":true}],"t":181,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[36.504,-24],[60.504,0],[60.504,0],[36.504,24],[24,24],[0,0]],"c":true}],"t":182,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[36.769,-24],[60.769,0],[60.769,0],[36.769,24],[24,24],[0,0]],"c":true}],"t":183,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[37.017,-24],[61.017,0],[61.017,0],[37.017,24],[24,24],[0,0]],"c":true}],"t":184,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[37.248,-24],[61.248,0],[61.248,0],[37.248,24],[24,24],[0,0]],"c":true}],"t":185,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[37.465,-24],[61.465,0],[61.465,0],[37.465,24],[24,24],[0,0]],"c":true}],"t":186,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[37.669,-24],[61.669,0],[61.669,0],[37.669,24],[24,24],[0,0]],"c":true}],"t":187,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[37.859,-24],[61.859,0],[61.859,0],[37.859,24],[24,24],[0,0]],"c":true}],"t":188,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[38.038,-24],[62.038,0],[62.038,0],[38.038,24],[24,24],[0,0]],"c":true}],"t":189,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[38.205,-24],[62.205,0],[62.205,0],[38.205,24],[24,24],[0,0]],"c":true}],"t":190,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[38.362,-24],[62.362,0],[62.362,0],[38.362,24],[24,24],[0,0]],"c":true}],"t":191,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[38.509,-24],[62.509,0],[62.509,0],[38.509,24],[24,24],[0,0]],"c":true}],"t":192,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[38.647,-24],[62.647,0],[62.647,0],[38.647,24],[24,24],[0,0]],"c":true}],"t":193,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[38.776,-24],[62.776,0],[62.776,0],[38.776,24],[24,24],[0,0]],"c":true}],"t":194,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[38.896,-24],[62.896,0],[62.896,0],[38.896,24],[24,24],[0,0]],"c":true}],"t":195,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[39.008,-24],[63.008,0],[63.008,0],[39.008,24],[24,24],[0,0]],"c":true}],"t":196,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[39.113,-24],[63.113,0],[63.113,0],[39.113,24],[24,24],[0,0]],"c":true}],"t":197,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[39.21,-24],[63.21,0],[63.21,0],[39.21,24],[24,24],[0,0]],"c":true}],"t":198,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[39.3,-24],[63.3,0],[63.3,0],[39.3,24],[24,24],[0,0]],"c":true}],"t":199,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[39.384,-24],[63.384,0],[63.384,0],[39.384,24],[24,24],[0,0]],"c":true}],"t":200,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[39.461,-24],[63.461,0],[63.461,0],[39.461,24],[24,24],[0,0]],"c":true}],"t":201,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[39.532,-24],[63.532,0],[63.532,0],[39.532,24],[24,24],[0,0]],"c":true}],"t":202,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[39.597,-24],[63.597,0],[63.597,0],[39.597,24],[24,24],[0,0]],"c":true}],"t":203,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[39.657,-24],[63.657,0],[63.657,0],[39.657,24],[24,24],[0,0]],"c":true}],"t":204,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[39.711,-24],[63.711,0],[63.711,0],[39.711,24],[24,24],[0,0]],"c":true}],"t":205,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[39.76,-24],[63.76,0],[63.76,0],[39.76,24],[24,24],[0,0]],"c":true}],"t":206,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[39.804,-24],[63.804,0],[63.804,0],[39.804,24],[24,24],[0,0]],"c":true}],"t":207,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[39.843,-24],[63.843,0],[63.843,0],[39.843,24],[24,24],[0,0]],"c":true}],"t":208,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[39.877,-24],[63.877,0],[63.877,0],[39.877,24],[24,24],[0,0]],"c":true}],"t":209,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[39.907,-24],[63.907,0],[63.907,0],[39.907,24],[24,24],[0,0]],"c":true}],"t":210,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[39.932,-24],[63.932,0],[63.932,0],[39.932,24],[24,24],[0,0]],"c":true}],"t":211,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[39.971,-24],[63.971,0],[63.971,0],[39.971,24],[24,24],[0,0]],"c":true}],"t":213,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}]},"nm":"Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.698039233685,0.811764717102,0.654901981354,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"k":[{"s":[0,0],"t":25,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,0],"t":450,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"IndieCorners Shape","bm":0,"hd":false}],"ip":0,"op":451,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".tertiaryFixedDim","cl":"tertiaryFixedDim","parent":6,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":37,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":47,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":249,"s":[100]},{"t":255,"s":[0]}]},"r":{"a":0,"k":0},"p":{"k":[{"s":[0,0,0],"t":123,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,0,0],"t":124,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0.001,0,0],"t":125,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0.005,0,0],"t":126,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0.013,0,0],"t":127,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0.029,0,0],"t":128,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0.054,0,0],"t":129,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0.089,0,0],"t":130,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0.134,0,0],"t":131,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0.193,0,0],"t":132,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0.267,0,0],"t":133,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0.358,0,0],"t":134,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0.466,0,0],"t":135,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0.593,0,0],"t":136,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0.739,0,0],"t":137,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0.903,0,0],"t":138,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[1.054,0,0],"t":139,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[1.22,0,0],"t":140,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[1.403,0,0],"t":141,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[1.602,0,0],"t":142,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[1.821,0,0],"t":143,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[2.059,0,0],"t":144,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[2.319,0,0],"t":145,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[2.601,0,0],"t":146,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[2.909,0,0],"t":147,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[3.242,0,0],"t":148,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[3.604,0,0],"t":149,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[3.998,0,0],"t":150,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[4.427,0,0],"t":151,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[4.897,0,0],"t":152,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[5.407,0,0],"t":153,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[5.965,0,0],"t":154,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[6.576,0,0],"t":155,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[7.246,0,0],"t":156,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[7.983,0,0],"t":157,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[8.8,0,0],"t":158,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[9.701,0,0],"t":159,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[10.699,0,0],"t":160,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[11.808,0,0],"t":161,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[13.041,0,0],"t":162,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[14.414,0,0],"t":163,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[15.945,0,0],"t":164,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[17.621,0,0],"t":165,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[19.429,0,0],"t":166,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[21.324,0,0],"t":167,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[23.241,0,0],"t":168,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[25.111,0,0],"t":169,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[26.859,0,0],"t":170,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[28.457,0,0],"t":171,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[29.897,0,0],"t":172,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[31.185,0,0],"t":173,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[32.333,0,0],"t":174,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[33.36,0,0],"t":175,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[34.272,0,0],"t":176,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[35.088,0,0],"t":177,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[35.82,0,0],"t":178,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.479,0,0],"t":179,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[37.076,0,0],"t":180,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[37.613,0,0],"t":181,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[38.099,0,0],"t":182,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[38.538,0,0],"t":183,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[38.937,0,0],"t":184,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[39.299,0,0],"t":185,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[39.629,0,0],"t":186,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[39.927,0,0],"t":187,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[40.198,0,0],"t":188,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[40.442,0,0],"t":189,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[40.663,0,0],"t":190,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[40.862,0,0],"t":191,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[41.041,0,0],"t":192,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[41.2,0,0],"t":193,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[41.342,0,0],"t":194,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[41.467,0,0],"t":195,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[41.577,0,0],"t":196,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[41.672,0,0],"t":197,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[41.754,0,0],"t":198,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[41.822,0,0],"t":199,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[41.879,0,0],"t":200,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[41.925,0,0],"t":201,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[41.961,0,0],"t":202,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"ef":[{"ty":5,"nm":"Super Slider","np":3,"mn":"ADBE Slider Control","ix":1,"en":1,"ef":[{"ty":0,"nm":"Slider","mn":"ADBE Slider Control-0001","ix":1,"v":{"a":1,"k":[{"i":{"x":[0.64],"y":[0.48]},"o":{"x":[0.36],"y":[0]},"t":121,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":138,"s":[17.5]},{"t":205,"s":[100]}]}}]}],"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":62,"s":[36,36]},{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":72,"s":[28,28]},{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":247,"s":[28,28]},{"t":257,"s":[36,36]}]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.698039233685,0.811764717102,0.654901981354,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.56,"y":1},"o":{"x":0.44,"y":0},"t":62,"s":[41,0],"to":[0,0],"ti":[0,0]},{"i":{"x":0.56,"y":0.56},"o":{"x":0.44,"y":0.44},"t":72,"s":[33,0],"to":[0,0],"ti":[0,0]},{"i":{"x":0.56,"y":1},"o":{"x":0.44,"y":0},"t":247,"s":[33,0],"to":[0,0],"ti":[0,0]},{"t":257,"s":[41,0]}]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"right circle","bm":0,"hd":false},{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":62,"s":[36,36]},{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":72,"s":[28,28]},{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":247,"s":[28,28]},{"t":257,"s":[36,36]}]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.698039233685,0.811764717102,0.654901981354,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.56,"y":1},"o":{"x":0.44,"y":0},"t":62,"s":[-41,0],"to":[0,0],"ti":[0,0]},{"i":{"x":0.56,"y":0.56},"o":{"x":0.44,"y":0.44},"t":72,"s":[-33,0],"to":[0,0],"ti":[0,0]},{"i":{"x":0.56,"y":1},"o":{"x":0.44,"y":0},"t":247,"s":[-33,0],"to":[0,0],"ti":[0,0]},{"t":257,"s":[-41,0]}]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"left circle","bm":0,"hd":false},{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":62,"s":[36,36]},{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":72,"s":[28,28]},{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":247,"s":[28,28]},{"t":257,"s":[36,36]}]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.698039233685,0.811764717102,0.654901981354,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"size","bm":0,"hd":false}],"ip":37,"op":345,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":".onTertiaryFixedVariant","cl":"onTertiaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[277,459,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[200,128]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":18},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Frame 1321317559","bm":0,"hd":false}],"ip":0,"op":451,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":7,"ty":3,"nm":"pb:scale","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"k":[{"s":[277.263,197.5,0],"t":148,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.43,197.5,0],"t":150,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.681,197.5,0],"t":152,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.85,197.5,0],"t":153,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[278.058,197.5,0],"t":154,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[278.313,197.5,0],"t":155,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[278.63,197.5,0],"t":156,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[279.023,197.5,0],"t":157,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[279.517,197.5,0],"t":158,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[280.151,197.5,0],"t":159,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[280.992,197.5,0],"t":160,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[282.175,197.5,0],"t":161,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[283.778,197.5,0],"t":162,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[285.586,197.5,0],"t":163,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[287.564,197.5,0],"t":164,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[289.63,197.5,0],"t":165,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[291.671,197.5,0],"t":166,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[293.578,197.5,0],"t":167,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[295.298,197.5,0],"t":168,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[296.823,197.5,0],"t":169,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[298.167,197.5,0],"t":170,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[299.353,197.5,0],"t":171,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[300.405,197.5,0],"t":172,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[301.343,197.5,0],"t":173,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[302.187,197.5,0],"t":174,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[302.949,197.5,0],"t":175,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[303.641,197.5,0],"t":176,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[304.271,197.5,0],"t":177,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[304.846,197.5,0],"t":178,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[305.373,197.5,0],"t":179,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[305.858,197.5,0],"t":180,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[306.306,197.5,0],"t":181,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[306.72,197.5,0],"t":182,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[307.103,197.5,0],"t":183,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[307.459,197.5,0],"t":184,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[307.789,197.5,0],"t":185,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[308.096,197.5,0],"t":186,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[308.382,197.5,0],"t":187,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[308.648,197.5,0],"t":188,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[308.895,197.5,0],"t":189,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[309.126,197.5,0],"t":190,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[309.34,197.5,0],"t":191,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[309.54,197.5,0],"t":192,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[309.726,197.5,0],"t":193,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[309.901,197.5,0],"t":194,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[310.063,197.5,0],"t":195,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[310.352,197.5,0],"t":197,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[310.599,197.5,0],"t":199,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[310.903,197.5,0],"t":202,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[311.196,197.5,0],"t":206,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[311.191,197.5,0],"t":381,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[310.194,197.5,0],"t":382,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[308.275,197.5,0],"t":383,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[304.841,197.5,0],"t":384,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[297.7,197.5,0],"t":385,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[289.568,197.5,0],"t":386,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[285.993,197.5,0],"t":387,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[283.914,197.5,0],"t":388,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[282.504,197.5,0],"t":389,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[281.464,197.5,0],"t":390,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[280.661,197.5,0],"t":391,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[280.021,197.5,0],"t":392,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[279.499,197.5,0],"t":393,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[279.068,197.5,0],"t":394,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[278.705,197.5,0],"t":395,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[278.401,197.5,0],"t":396,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[278.143,197.5,0],"t":397,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.925,197.5,0],"t":398,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.741,197.5,0],"t":399,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.585,197.5,0],"t":400,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.345,197.5,0],"t":402,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.074,197.5,0],"t":406,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}]},"a":{"a":0,"k":[0,0,0]},"s":{"k":[{"s":[99.914,99.914,100],"t":146,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.848,99.848,100],"t":148,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.751,99.751,100],"t":150,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.685,99.685,100],"t":151,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.605,99.605,100],"t":152,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.507,99.507,100],"t":153,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.387,99.387,100],"t":154,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.239,99.239,100],"t":155,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.056,99.056,100],"t":156,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.829,98.829,100],"t":157,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.542,98.542,100],"t":158,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.174,98.174,100],"t":159,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.686,97.686,100],"t":160,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97,97,100],"t":161,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.071,96.071,100],"t":162,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[95.025,95.025,100],"t":163,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[93.878,93.878,100],"t":164,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[92.678,92.678,100],"t":165,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[91.495,91.495,100],"t":166,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[90.39,90.39,100],"t":167,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[89.393,89.393,100],"t":168,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[88.508,88.508,100],"t":169,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[87.729,87.729,100],"t":170,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[87.041,87.041,100],"t":171,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[86.43,86.43,100],"t":172,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[85.886,85.886,100],"t":173,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[85.397,85.397,100],"t":174,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[84.956,84.956,100],"t":175,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[84.555,84.555,100],"t":176,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[84.191,84.191,100],"t":177,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[83.857,83.857,100],"t":178,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[83.552,83.552,100],"t":179,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[83.271,83.271,100],"t":180,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[83.011,83.011,100],"t":181,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[82.771,82.771,100],"t":182,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[82.549,82.549,100],"t":183,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[82.342,82.342,100],"t":184,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[82.151,82.151,100],"t":185,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[81.973,81.973,100],"t":186,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[81.807,81.807,100],"t":187,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[81.653,81.653,100],"t":188,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[81.51,81.51,100],"t":189,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[81.376,81.376,100],"t":190,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[81.251,81.251,100],"t":191,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[81.135,81.135,100],"t":192,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[81.027,81.027,100],"t":193,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.926,80.926,100],"t":194,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.833,80.833,100],"t":195,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.746,80.746,100],"t":196,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.665,80.665,100],"t":197,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.591,80.591,100],"t":198,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.522,80.522,100],"t":199,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.458,80.458,100],"t":200,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.4,80.4,100],"t":201,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.346,80.346,100],"t":202,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.298,80.298,100],"t":203,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.253,80.253,100],"t":204,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.176,80.176,100],"t":206,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.115,80.115,100],"t":208,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.049,80.049,100],"t":211,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80,80,100],"t":380,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.179,80.179,100],"t":381,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.757,80.757,100],"t":382,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[81.87,81.87,100],"t":383,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[83.86,83.86,100],"t":384,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[88,88,100],"t":385,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[92.714,92.714,100],"t":386,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[94.789,94.789,100],"t":387,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[95.992,95.992,100],"t":388,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.809,96.809,100],"t":389,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.412,97.412,100],"t":390,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.878,97.878,100],"t":391,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.249,98.249,100],"t":392,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.553,98.553,100],"t":393,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.803,98.803,100],"t":394,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.012,99.012,100],"t":395,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.188,99.188,100],"t":396,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.337,99.337,100],"t":397,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.464,99.464,100],"t":398,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.57,99.57,100],"t":399,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.661,99.661,100],"t":400,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.737,99.737,100],"t":401,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.8,99.8,100],"t":402,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.896,99.896,100],"t":404,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.99,99.99,100],"t":408,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}}]}},"ao":0,"ef":[{"ty":5,"nm":"Void","np":19,"mn":"Pseudo/250958","ix":1,"en":1,"ef":[{"ty":0,"nm":"Width","mn":"Pseudo/250958-0001","ix":1,"v":{"a":0,"k":100}},{"ty":0,"nm":"Height","mn":"Pseudo/250958-0002","ix":2,"v":{"a":0,"k":100}},{"ty":0,"nm":"Offset X","mn":"Pseudo/250958-0003","ix":3,"v":{"a":0,"k":0}},{"ty":0,"nm":"Offset Y","mn":"Pseudo/250958-0004","ix":4,"v":{"a":0,"k":0}},{"ty":0,"nm":"Roundness","mn":"Pseudo/250958-0005","ix":5,"v":{"a":0,"k":0}},{"ty":6,"nm":"About","mn":"Pseudo/250958-0006","ix":6,"v":0},{"ty":6,"nm":"Plague of null layers.","mn":"Pseudo/250958-0007","ix":7,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0008","ix":8,"v":0},{"ty":6,"nm":"Following projects","mn":"Pseudo/250958-0009","ix":9,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0010","ix":10,"v":0},{"ty":6,"nm":"through time.","mn":"Pseudo/250958-0011","ix":11,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0012","ix":12,"v":0},{"ty":6,"nm":"Be free of the past.","mn":"Pseudo/250958-0013","ix":13,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0014","ix":14,"v":0},{"ty":6,"nm":"Copyright 2023 Battle Axe Inc","mn":"Pseudo/250958-0015","ix":15,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0016","ix":16,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0017","ix":17,"v":0}]}],"ip":0,"op":451,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":".onTertiaryFixed","cl":"onTertiaryFixed","parent":9,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":253,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":256,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":389,"s":[100]},{"t":392,"s":[0]}]},"r":{"a":0,"k":0},"p":{"a":0,"k":[0,0,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"ef":[{"ty":5,"nm":"Global Position","np":4,"mn":"Pseudo/88900","ix":1,"en":1,"ef":[{"ty":10,"nm":"Master Parent","mn":"Pseudo/88900-0001","ix":1,"v":{"a":0,"k":7}},{"ty":3,"nm":"Global Position","mn":"Pseudo/88900-0002","ix":2,"v":{"k":[{"s":[277.263,197.5],"t":148,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.43,197.5],"t":150,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.681,197.5],"t":152,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.85,197.5],"t":153,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[278.058,197.5],"t":154,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[278.313,197.5],"t":155,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[278.63,197.5],"t":156,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[279.023,197.5],"t":157,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[279.517,197.5],"t":158,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[280.151,197.5],"t":159,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[280.992,197.5],"t":160,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[282.175,197.5],"t":161,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[283.778,197.5],"t":162,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[285.586,197.5],"t":163,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[287.564,197.5],"t":164,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[289.63,197.5],"t":165,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[291.671,197.5],"t":166,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[293.578,197.5],"t":167,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[295.298,197.5],"t":168,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[296.823,197.5],"t":169,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[298.167,197.5],"t":170,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[299.353,197.5],"t":171,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[300.405,197.5],"t":172,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[301.343,197.5],"t":173,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[302.187,197.5],"t":174,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[302.949,197.5],"t":175,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[303.641,197.5],"t":176,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[304.271,197.5],"t":177,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[304.846,197.5],"t":178,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[305.373,197.5],"t":179,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[305.858,197.5],"t":180,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[306.306,197.5],"t":181,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[306.72,197.5],"t":182,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[307.103,197.5],"t":183,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[307.459,197.5],"t":184,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[307.789,197.5],"t":185,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[308.096,197.5],"t":186,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[308.382,197.5],"t":187,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[308.648,197.5],"t":188,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[308.895,197.5],"t":189,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[309.126,197.5],"t":190,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[309.34,197.5],"t":191,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[309.54,197.5],"t":192,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[309.726,197.5],"t":193,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[309.901,197.5],"t":194,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[310.063,197.5],"t":195,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[310.352,197.5],"t":197,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[310.599,197.5],"t":199,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[310.903,197.5],"t":202,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[311.196,197.5],"t":206,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[311.5,197.5],"t":250,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[310.936,197.788],"t":251,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[308.7,199.014],"t":252,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[304.071,202.033],"t":253,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[298.438,206.77],"t":254,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[293.978,211.581],"t":255,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[290.807,215.785],"t":256,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[288.487,219.444],"t":257,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[286.718,222.659],"t":258,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[285.317,225.519],"t":259,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[284.171,228.085],"t":260,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[283.211,230.396],"t":261,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[282.392,232.474],"t":262,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[281.682,234.334],"t":263,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[281.059,235.992],"t":264,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[280.506,237.461],"t":265,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[280.012,238.754],"t":266,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[279.568,239.881],"t":267,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[279.169,240.855],"t":268,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[278.809,241.684],"t":269,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[278.487,242.379],"t":270,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[278.199,242.951],"t":271,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.943,243.409],"t":272,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.72,243.76],"t":273,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.528,243.874],"t":274,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.369,243.701],"t":275,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.24,243.336],"t":276,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.142,242.847],"t":277,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.073,242.284],"t":278,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.033,241.684],"t":279,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.02,241.075],"t":280,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.02,240.497],"t":281,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.02,239.98],"t":282,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.02,239.538],"t":283,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.02,239.181],"t":284,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.02,238.917],"t":285,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.02,239.065],"t":293,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.02,239.265],"t":295,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.02,239.455],"t":297,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.02,239.685],"t":300,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.85,239.729],"t":381,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.285,239.199],"t":382,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[275.162,238.218],"t":383,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[273.05,236.594],"t":384,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[267.986,234.04],"t":385,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[265.592,226.983],"t":386,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[270.166,217.207],"t":387,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[272.184,212.309],"t":388,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[273.238,209.328],"t":389,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[273.904,207.237],"t":390,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[274.379,205.654],"t":391,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[274.741,204.399],"t":392,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[275.029,203.375],"t":393,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[275.267,202.521],"t":394,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[275.466,201.799],"t":395,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[275.638,201.182],"t":396,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[275.788,200.65],"t":397,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[275.921,200.189],"t":398,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.041,199.789],"t":399,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.149,199.439],"t":400,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.246,199.134],"t":401,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.337,198.867],"t":402,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.419,198.634],"t":403,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.495,198.431],"t":404,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.566,198.255],"t":405,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.632,198.103],"t":406,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.692,197.973],"t":407,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.748,197.862],"t":408,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.86,197.692],"t":410,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}]}}]}],"shapes":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0,0],"y":[1,1]},"o":{"x":[0.2,0.2],"y":[0,0]},"t":250,"s":[504,315]},{"i":{"x":[0,0],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0,0]},"t":280,"s":[30,30]},{"i":{"x":[0.8,0.8],"y":[0.15,0.15]},"o":{"x":[0.3,0.3],"y":[0,0]},"t":380,"s":[30,30]},{"i":{"x":[0.1,0.1],"y":[1,1]},"o":{"x":[0.05,0.05],"y":[0.7,0.7]},"t":386,"s":[219.6,144]},{"t":416,"s":[504,315]}]},"p":{"a":0,"k":[0,0]},"r":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0.2],"y":[0]},"t":250,"s":[28]},{"i":{"x":[0],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":280,"s":[30]},{"i":{"x":[0.8],"y":[0.15]},"o":{"x":[0.3],"y":[0]},"t":380,"s":[30]},{"i":{"x":[0.1],"y":[1]},"o":{"x":[0.05],"y":[0.7]},"t":386,"s":[29.2]},{"t":416,"s":[28]}]},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215687662,0.1254902035,0.027450982481,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false}],"ip":0,"op":451,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":"matte","parent":7,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":1,"k":[{"i":{"x":0,"y":1},"o":{"x":0.2,"y":0},"t":250,"s":[0,0,0],"to":[-28.906,14.531,0],"ti":[7.183,-8.833,0]},{"t":280,"s":[-43.1,53,0],"h":1},{"i":{"x":0.8,"y":0.15},"o":{"x":0.3,"y":0},"t":380,"s":[-43.1,53,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.1,"y":1},"o":{"x":0.05,"y":0.7},"t":386,"s":[-25.86,31.8,0],"to":[7.183,-8.833,0],"ti":[-7.167,9.833,0]},{"t":416,"s":[0,0,0]}]},"a":{"a":1,"k":[{"i":{"x":0.5,"y":1},"o":{"x":0.28,"y":0},"t":255,"s":[0,0,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.573,"y":1},"o":{"x":0.236,"y":0},"t":273,"s":[0,-6,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.5,"y":1},"o":{"x":0.28,"y":0},"t":287,"s":[0,1.5,0],"to":[0,0,0],"ti":[0,0,0]},{"t":307,"s":[0,0,0]}]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0,0],"y":[1,1]},"o":{"x":[0.2,0.2],"y":[0,0]},"t":250,"s":[504,315]},{"i":{"x":[0,0],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0,0]},"t":280,"s":[30,30]},{"i":{"x":[0.8,0.8],"y":[0.15,0.15]},"o":{"x":[0.3,0.3],"y":[0,0]},"t":380,"s":[30,30]},{"i":{"x":[0.1,0.1],"y":[1,1]},"o":{"x":[0.05,0.05],"y":[0.7,0.7]},"t":386,"s":[219.6,144]},{"t":416,"s":[504,315]}]},"p":{"a":0,"k":[0,0]},"r":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0.2],"y":[0]},"t":250,"s":[28]},{"i":{"x":[0],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":280,"s":[30]},{"i":{"x":[0.8],"y":[0.15]},"o":{"x":[0.3],"y":[0]},"t":380,"s":[30]},{"i":{"x":[0.1],"y":[1]},"o":{"x":[0.05],"y":[0.7]},"t":386,"s":[29.2]},{"t":416,"s":[28]}]},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false}],"ip":0,"op":451,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":10,"ty":0,"nm":"Back_LofiApp","parent":9,"tt":1,"tp":9,"refId":"comp_1","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[0,0,0]},"a":{"a":0,"k":[252,157.5,0]},"s":{"a":1,"k":[{"i":{"x":[0,0,0],"y":[1,1,1]},"o":{"x":[0.2,0.2,0.2],"y":[0,0,0]},"t":250,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":280,"s":[10,10,100]},{"i":{"x":[0.8,0.8,0.8],"y":[0.15,0.15,1]},"o":{"x":[0.3,0.3,0.3],"y":[0,0,0]},"t":380,"s":[10,10,100]},{"i":{"x":[0.1,0.1,0.1],"y":[1,1,1]},"o":{"x":[0.05,0.05,0.05],"y":[0.7,0.7,0]},"t":386,"s":[46,46,100]},{"t":416,"s":[100,100,100]}]}},"ao":0,"w":504,"h":315,"ip":0,"op":451,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":"behindApp","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":253,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":259,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":386,"s":[0]},{"t":397,"s":[100]}]},"r":{"a":0,"k":0},"p":{"a":0,"k":[277,197.5,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[503.5,314.5]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":28},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"frame","bm":0,"hd":false}],"ip":0,"op":451,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":12,"ty":0,"nm":"Back_LofiLauncher","refId":"comp_2","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[277,197.5,0]},"a":{"a":0,"k":[252,157.5,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"w":504,"h":315,"ip":0,"op":451,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":".tertiaryFixedDim","cl":"tertiaryFixedDim","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[277,197.5,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":28},"nm":"Rectangle Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[0.698039233685,0.811764717102,0.654901981354,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":14},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"op","nm":"Stroke align: Outside","a":{"k":[{"s":[7],"t":25,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[7],"t":450,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"lj":1,"ml":{"a":0,"k":4},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.698039233685,0.811764717102,0.654901981354,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"frame","bm":0,"hd":false}],"ip":0,"op":451,"st":0,"ct":1,"bm":0}]},{"id":"comp_1","nm":"Back_LofiApp","fr":60,"pfr":1,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".onTertiaryFixedVariant","cl":"onTertiaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[339.937,151.75,0]},"a":{"a":0,"k":[339.937,151.75,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[1.021,-1.766],[0,0],[-2.043,0],[0,0],[1.022,1.767]],"o":[[-1.021,-1.766],[0,0],[-1.022,1.767],[0,0],[2.043,0],[0,0]],"v":[[2.297,-7.675],[-2.297,-7.675],[-9.64,5.025],[-7.343,9],[7.343,9],[9.64,5.025]],"c":true}},"nm":"Path 1","hd":false},{"ty":"rd","nm":"Round Corners 1","r":{"a":0,"k":9},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Triangle","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[481.874,21]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Triangle","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[18,18]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":200},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Rectangle","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[457.874,21]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Rectangle","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[292,25]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":200},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Text field","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[334,279]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Text field","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[109,28]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":12},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Sent","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[425.5,208.5]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Sent","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[160,56]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":14},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Sent","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[400,158.5]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Sent","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[126,40]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":14},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Received","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[251,78.5]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Received","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".onTertiaryFixed","cl":"onTertiaryFixed","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[334,157.5,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[340,315]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":16},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215687662,0.1254902035,0.027450980619,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Message","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".onTertiaryFixedVariant","cl":"onTertiaryFixedVariant","parent":4,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[82,171.125,0]},"a":{"a":0,"k":[82,171.125,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[64,8]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":39.375},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Line 2","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[80,177.125]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Line 4","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[92,8]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":39.375},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Line 1","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[94,165.125]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Line 3","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[20,20]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":39.375},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Avatar","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[34,171.125]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"circle 2","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".onTertiaryFixed","cl":"onTertiaryFixed","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[82,140,0]},"a":{"a":0,"k":[82,140.938,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[132,22]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":39.375},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215687662,0.1254902035,0.027450980619,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Search","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[82,31.5]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"header","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[64,8]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":200},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215687662,0.1254902035,0.027450980619,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Line 2","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[80,257.375]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Line 6","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[92,8]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":200},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215687662,0.1254902035,0.027450980619,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Line 1","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[94,245.375]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Line 5","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[20,20]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":200},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215687662,0.1254902035,0.027450980619,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Avatar","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[34,251.375]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"circle 3","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[132,64]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":12},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215687662,0.1254902035,0.027450980619,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Message","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[82,171]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"block","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[64,8]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":200},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215687662,0.1254902035,0.027450980619,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Line 2","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[80,96.875]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Line 2","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[92,8]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":200},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215687662,0.1254902035,0.027450980619,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Line 1","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[94,84.875]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Line 1","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[20,20]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":200},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215687662,0.1254902035,0.027450980619,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Avatar","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[34,90.875]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"circle 1","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".onTertiaryFixedVariant","cl":"onTertiaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[252,157.5,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":28},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"app only","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0}]},{"id":"comp_2","nm":"Back_LofiLauncher","fr":60,"pfr":1,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".onTertiaryFixed","cl":"onTertiaryFixed","parent":4,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[0,117.5,0]},"a":{"a":0,"k":[252,275,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215686275,0.125490196078,0.027450980392,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[444,275]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"hotseat - 5","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215686275,0.125490196078,0.027450980392,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[396,275]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"hotseat - 4","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215686275,0.125490196078,0.027450980392,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[348,275]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"hotseat - 3","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215686275,0.125490196078,0.027450980392,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[300,275]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"hotseat - 2","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215686275,0.125490196078,0.027450980392,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[252,275]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"hotseat - 1","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[168,20]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":15},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215686275,0.125490196078,0.027450980392,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"qsb","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[132,275]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"qsb","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".onTertiaryFixed","cl":"onTertiaryFixed","parent":4,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[0,-29.497,0]},"a":{"a":0,"k":[252,128.003,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[20.144,20.144],[20.144,-20.144],[0,0],[-20.144,-20.144],[-20.144,20.144],[0,0]],"o":[[-20.144,-20.144],[0,0],[-20.144,20.144],[20.144,20.144],[0,0],[20.144,-20.144]],"v":[[44.892,-44.892],[-28.057,-44.892],[-44.892,-28.057],[-44.892,44.892],[28.057,44.892],[44.892,28.057]],"c":true}},"nm":"Path 1","hd":false},{"ty":"rd","nm":"Round Corners 1","r":{"a":0,"k":15},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215686275,0.125490196078,0.027450980392,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"widgets","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[108,152.004]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"widgets weather","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[4.782,-2.684],[0,0],[2.63,-0.033],[0,0],[2.807,-4.716],[0,0],[2.263,-1.343],[0,0],[0.066,-5.485],[0,0],[1.292,-2.295],[0,0],[-2.683,-4.784],[0,0],[-0.033,-2.63],[0,0],[-4.716,-2.807],[0,0],[-1.338,-2.263],[0,0],[-5.483,-0.066],[0,0],[-2.296,-1.292],[0,0],[-4.782,2.683],[0,0],[-2.63,0.033],[0,0],[-2.807,4.716],[0,0],[-2.263,1.338],[0,0],[-0.066,5.483],[0,0],[-1.292,2.295],[0,0],[2.683,4.784],[0,0],[0.033,2.631],[0,0],[4.716,2.801],[0,0],[1.338,2.262],[0,0],[5.483,0.068],[0,0],[2.296,1.287]],"o":[[-4.782,-2.684],[0,0],[-2.296,1.287],[0,0],[-5.483,0.068],[0,0],[-1.338,2.262],[0,0],[-4.716,2.801],[0,0],[-0.033,2.631],[0,0],[-2.683,4.784],[0,0],[1.292,2.295],[0,0],[0.066,5.483],[0,0],[2.263,1.338],[0,0],[2.807,4.716],[0,0],[2.63,0.033],[0,0],[4.782,2.683],[0,0],[2.296,-1.292],[0,0],[5.483,-0.066],[0,0],[1.338,-2.263],[0,0],[4.716,-2.807],[0,0],[0.033,-2.63],[0,0],[2.683,-4.784],[0,0],[-1.292,-2.295],[0,0],[-0.066,-5.485],[0,0],[-2.263,-1.343],[0,0],[-2.807,-4.716],[0,0],[-2.63,-0.033],[0,0]],"v":[[7.7,-57.989],[-7.7,-57.989],[-11.019,-56.128],[-18.523,-54.117],[-22.327,-54.07],[-35.668,-46.369],[-37.609,-43.1],[-43.099,-37.605],[-46.372,-35.663],[-54.072,-22.324],[-54.118,-18.522],[-56.132,-11.016],[-57.988,-7.7],[-57.988,7.703],[-56.132,11.019],[-54.118,18.524],[-54.072,22.328],[-46.372,35.669],[-43.099,37.611],[-37.609,43.101],[-35.668,46.373],[-22.327,54.074],[-18.523,54.12],[-11.019,56.133],[-7.7,57.99],[7.7,57.99],[11.019,56.133],[18.523,54.12],[22.327,54.074],[35.668,46.373],[37.609,43.101],[43.099,37.611],[46.372,35.669],[54.072,22.328],[54.118,18.524],[56.132,11.019],[57.988,7.703],[57.988,-7.7],[56.132,-11.016],[54.118,-18.522],[54.072,-22.324],[46.372,-35.663],[43.099,-37.605],[37.609,-43.1],[35.668,-46.369],[22.327,-54.07],[18.523,-54.117],[11.019,-56.128]],"c":true}},"nm":"Path 1","hd":false},{"ty":"rd","nm":"Round Corners 1","r":{"a":0,"k":15},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215686275,0.125490196078,0.027450980392,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"widgets","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[396,104.003]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"widgets clock","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".onTertiaryFixed","cl":"onTertiaryFixed","parent":4,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[0,-29.497,0]},"a":{"a":0,"k":[252,128.003,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215686275,0.125490196078,0.027450980392,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[444,200.004]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"app - 7","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215686275,0.125490196078,0.027450980392,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[348,200.004]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"app - 6","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215686275,0.125490196078,0.027450980392,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[252,128.004]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"app - 4","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215686275,0.125490196078,0.027450980392,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[252,56.002]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"app - 3","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215686275,0.125490196078,0.027450980392,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[156,56.004]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"app - 2","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215686275,0.125490196078,0.027450980392,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[60,56.004]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"app - 1","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":3,"nm":"Scale Up","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[252,157.5,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":1,"k":[{"i":{"x":[0.8,0.8,0.8],"y":[0.15,0.15,1]},"o":{"x":[0.3,0.3,0.3],"y":[0,0,0]},"t":250,"s":[85,85,100]},{"i":{"x":[0.1,0.1,0.1],"y":[1,1,1]},"o":{"x":[0.05,0.05,0.05],"y":[0.7,0.7,0]},"t":256,"s":[91,91,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":286,"s":[100,100,100]},{"i":{"x":[0.8,0.8,0.8],"y":[0.15,0.15,1]},"o":{"x":[0.3,0.3,0.3],"y":[0,0,0]},"t":380,"s":[100,100,100]},{"i":{"x":[0.1,0.1,0.1],"y":[1,1,1]},"o":{"x":[0.05,0.05,0.05],"y":[0.7,0.7,0]},"t":386,"s":[96,96,100]},{"t":416,"s":[90,90,100]}]}},"ao":0,"ef":[{"ty":5,"nm":"Void","np":19,"mn":"Pseudo/250958","ix":1,"en":1,"ef":[{"ty":0,"nm":"Width","mn":"Pseudo/250958-0001","ix":1,"v":{"a":0,"k":100}},{"ty":0,"nm":"Height","mn":"Pseudo/250958-0002","ix":2,"v":{"a":0,"k":100}},{"ty":0,"nm":"Offset X","mn":"Pseudo/250958-0003","ix":3,"v":{"a":0,"k":0}},{"ty":0,"nm":"Offset Y","mn":"Pseudo/250958-0004","ix":4,"v":{"a":0,"k":0}},{"ty":0,"nm":"Roundness","mn":"Pseudo/250958-0005","ix":5,"v":{"a":0,"k":0}},{"ty":6,"nm":"About","mn":"Pseudo/250958-0006","ix":6,"v":0},{"ty":6,"nm":"Plague of null layers.","mn":"Pseudo/250958-0007","ix":7,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0008","ix":8,"v":0},{"ty":6,"nm":"Following projects","mn":"Pseudo/250958-0009","ix":9,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0010","ix":10,"v":0},{"ty":6,"nm":"through time.","mn":"Pseudo/250958-0011","ix":11,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0012","ix":12,"v":0},{"ty":6,"nm":"Be free of the past.","mn":"Pseudo/250958-0013","ix":13,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0014","ix":14,"v":0},{"ty":6,"nm":"Copyright 2023 Battle Axe Inc","mn":"Pseudo/250958-0015","ix":15,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0016","ix":16,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0017","ix":17,"v":0}]}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".onTertiaryFixedVariant","cl":"onTertiaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[252,157.5,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":28},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"illustrations: action key","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0}]},{"id":"comp_3","nm":"Back_RightDismiss","fr":60,"pfr":1,"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"release Scale","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"s":true,"x":{"a":0,"k":476},"y":{"a":0,"k":197}},"a":{"a":0,"k":[0,0,0]},"s":{"a":1,"k":[{"i":{"x":[0.1,0.1,0.1],"y":[1,1,1]},"o":{"x":[0.08,0.08,0.08],"y":[0.47,0.47,0]},"t":250,"s":[100,100,100]},{"i":{"x":[0.999,0.999,0.999],"y":[1,1,1]},"o":{"x":[0.3,0.3,0.3],"y":[0,0,0]},"t":254,"s":[105,105,100]},{"t":266,"s":[50,50,100]}]}},"ao":0,"ef":[{"ty":5,"nm":"Void","np":19,"mn":"Pseudo/250958","ix":1,"en":1,"ef":[{"ty":0,"nm":"Width","mn":"Pseudo/250958-0001","ix":1,"v":{"a":0,"k":100}},{"ty":0,"nm":"Height","mn":"Pseudo/250958-0002","ix":2,"v":{"a":0,"k":100}},{"ty":0,"nm":"Offset X","mn":"Pseudo/250958-0003","ix":3,"v":{"a":0,"k":0}},{"ty":0,"nm":"Offset Y","mn":"Pseudo/250958-0004","ix":4,"v":{"a":0,"k":0}},{"ty":0,"nm":"Roundness","mn":"Pseudo/250958-0005","ix":5,"v":{"a":0,"k":0}},{"ty":6,"nm":"About","mn":"Pseudo/250958-0006","ix":6,"v":0},{"ty":6,"nm":"Plague of null layers.","mn":"Pseudo/250958-0007","ix":7,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0008","ix":8,"v":0},{"ty":6,"nm":"Following projects","mn":"Pseudo/250958-0009","ix":9,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0010","ix":10,"v":0},{"ty":6,"nm":"through time.","mn":"Pseudo/250958-0011","ix":11,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0012","ix":12,"v":0},{"ty":6,"nm":"Be free of the past.","mn":"Pseudo/250958-0013","ix":13,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0014","ix":14,"v":0},{"ty":6,"nm":"Copyright 2023 Battle Axe Inc","mn":"Pseudo/250958-0015","ix":15,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0016","ix":16,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0017","ix":17,"v":0}]}],"ip":0,"op":501,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".onTertiaryFixed","cl":"onTertiaryFixed","parent":3,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":151,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":154,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":255,"s":[100]},{"t":258,"s":[0]}]},"r":{"a":0,"k":0},"p":{"k":[{"s":[-0.692,0,0],"t":149,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-4.692,0,0],"t":150,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-5.392,0,0],"t":151,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-9.675,0,0],"t":152,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-14.521,0,0],"t":153,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-16.835,0,0],"t":154,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-18.141,0,0],"t":155,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-18.925,0,0],"t":156,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-19.386,0,0],"t":157,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-19.622,0,0],"t":158,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-19.692,0,0],"t":159,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-22.714,0,0],"t":160,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-24.39,0,0],"t":161,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-24.692,0,0],"t":162,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-24.766,0,0],"t":163,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-25.041,0,0],"t":164,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-25.549,0,0],"t":165,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-26.178,0,0],"t":166,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-26.787,0,0],"t":167,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-27.326,0,0],"t":168,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-27.795,0,0],"t":169,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-28.206,0,0],"t":170,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-28.57,0,0],"t":171,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-28.894,0,0],"t":172,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-29.187,0,0],"t":173,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-29.453,0,0],"t":174,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-29.695,0,0],"t":175,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-29.917,0,0],"t":176,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-30.122,0,0],"t":177,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-30.312,0,0],"t":178,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-30.487,0,0],"t":179,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-30.65,0,0],"t":180,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-30.802,0,0],"t":181,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-30.944,0,0],"t":182,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-31.076,0,0],"t":183,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-31.2,0,0],"t":184,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-31.316,0,0],"t":185,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-31.424,0,0],"t":186,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-31.526,0,0],"t":187,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-31.621,0,0],"t":188,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-31.711,0,0],"t":189,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-31.795,0,0],"t":190,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-31.873,0,0],"t":191,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-31.947,0,0],"t":192,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-32.015,0,0],"t":193,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-32.08,0,0],"t":194,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-32.14,0,0],"t":195,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-32.196,0,0],"t":196,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-32.248,0,0],"t":197,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-32.297,0,0],"t":198,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-32.342,0,0],"t":199,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-32.384,0,0],"t":200,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-32.422,0,0],"t":201,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-32.458,0,0],"t":202,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-32.49,0,0],"t":203,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-32.52,0,0],"t":204,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-32.547,0,0],"t":205,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-32.572,0,0],"t":206,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-32.594,0,0],"t":207,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-32.613,0,0],"t":208,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-32.645,0,0],"t":210,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-32.677,0,0],"t":213,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.36,"y":0},"t":150,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[5.459,5.2],[-3.459,0],[5.459,-5.2]],"c":false}]},{"i":{"x":0.02,"y":1},"o":{"x":0.167,"y":0.167},"t":152,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[4.779,4.88],[-3.459,0],[4.779,-4.88]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":159,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[3.459,7.2],[-3.459,0],[3.459,-7.2]],"c":false}]},{"i":{"x":0,"y":1},"o":{"x":0.12,"y":0},"t":162,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[3.459,7.2],[-3.459,0],[3.459,-7.2]],"c":false}]},{"t":217,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[3.459,9.2],[-3.459,0],[3.459,-9.2]],"c":false}]}]},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[0.121568627656,0.211764708161,0.101960785687,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":4},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Vector 1","bm":0,"hd":false}],"ip":0,"op":501,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".tertiaryFixedDim","cl":"tertiaryFixedDim","parent":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":257,"s":[100]},{"t":260,"s":[0]}]},"r":{"a":0,"k":0},"p":{"s":true,"x":{"a":1,"k":[{"i":{"x":[0.22],"y":[1]},"o":{"x":[0.06],"y":[-0.15]},"t":160,"s":[13.981]},{"t":189,"s":[-0.019]}]},"y":{"a":0,"k":0}},"a":{"a":0,"k":[-31.019,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"ef":[{"ty":5,"nm":"IndieCorners","np":21,"mn":"Pseudo/0.20784385308943532","ix":1,"en":1,"ef":[{"ty":7,"nm":"Align","mn":"Pseudo/0.20784385308943532-0001","ix":1,"v":{"a":0,"k":4}},{"ty":6,"nm":"Size","mn":"Pseudo/0.20784385308943532-0002","ix":2,"v":0},{"ty":0,"nm":"w","mn":"Pseudo/0.20784385308943532-0003","ix":3,"v":{"a":1,"k":[{"t":149,"s":[0],"h":1},{"i":{"x":[0.02],"y":[1]},"o":{"x":[0.365],"y":[0]},"t":150,"s":[8]},{"i":{"x":[0.1],"y":[1]},"o":{"x":[0.336],"y":[0]},"t":159,"s":[38]},{"i":{"x":[0.002],"y":[1]},"o":{"x":[0.119],"y":[0]},"t":162,"s":[48]},{"t":217,"s":[64]}]}},{"ty":0,"nm":"h","mn":"Pseudo/0.20784385308943532-0004","ix":4,"v":{"a":0,"k":48}},{"ty":6,"nm":"","mn":"Pseudo/0.20784385308943532-0005","ix":5,"v":0},{"ty":6,"nm":"Rounding","mn":"Pseudo/0.20784385308943532-0006","ix":6,"v":0},{"ty":7,"nm":"Same for all corners","mn":"Pseudo/0.20784385308943532-0007","ix":7,"v":{"a":0,"k":1}},{"ty":0,"nm":"All corners","mn":"Pseudo/0.20784385308943532-0008","ix":8,"v":{"a":1,"k":[{"i":{"x":[0.02],"y":[1]},"o":{"x":[0.365],"y":[0]},"t":150,"s":[80]},{"i":{"x":[0.1],"y":[1]},"o":{"x":[0.336],"y":[0]},"t":159,"s":[24]},{"t":162,"s":[80]}]}},{"ty":0,"nm":"tl","mn":"Pseudo/0.20784385308943532-0009","ix":9,"v":{"a":0,"k":12}},{"ty":0,"nm":"tr","mn":"Pseudo/0.20784385308943532-0010","ix":10,"v":{"a":0,"k":12}},{"ty":0,"nm":"br","mn":"Pseudo/0.20784385308943532-0011","ix":11,"v":{"a":0,"k":12}},{"ty":0,"nm":"bl","mn":"Pseudo/0.20784385308943532-0012","ix":12,"v":{"a":0,"k":12}},{"ty":6,"nm":"","mn":"Pseudo/0.20784385308943532-0013","ix":13,"v":0},{"ty":6,"nm":"Alignment","mn":"Pseudo/0.20784385308943532-0014","ix":14,"v":0},{"ty":0,"nm":"X Anchor %","mn":"Pseudo/0.20784385308943532-0015","ix":15,"v":{"a":0,"k":0}},{"ty":0,"nm":"Y Anchor %","mn":"Pseudo/0.20784385308943532-0016","ix":16,"v":{"a":0,"k":0}},{"ty":0,"nm":"X Position","mn":"Pseudo/0.20784385308943532-0017","ix":17,"v":{"a":0,"k":0}},{"ty":0,"nm":"Y Position ","mn":"Pseudo/0.20784385308943532-0018","ix":18,"v":{"a":0,"k":0}},{"ty":6,"nm":"","mn":"Pseudo/0.20784385308943532-0019","ix":19,"v":0}]}],"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"k":[{"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[0,-24],[0,-24],[0,-24],[0,-24],[0,24],[0,24],[0,24],[0,24]],"c":true}],"t":149,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-2.208,0],[0,0],[0,-2.208],[0,0],[2.208,0],[0,0],[0,2.208]],"o":[[0,-2.208],[0,0],[2.208,0],[0,0],[0,2.208],[0,0],[-2.208,0],[0,0]],"v":[[-8,-20],[-4,-24],[-4,-24],[0,-20],[0,20],[-4,24],[-4,24],[-8,20]],"c":true}],"t":150,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-2.594,0],[0,0],[0,-2.594],[0,0],[2.594,0],[0,0],[0,2.594]],"o":[[0,-2.594],[0,0],[2.594,0],[0,0],[0,2.594],[0,0],[-2.594,0],[0,0]],"v":[[-9.401,-19.3],[-4.7,-24],[-4.7,-24],[0,-19.3],[0,19.3],[-4.7,24],[-4.7,24],[-9.401,19.3]],"c":true}],"t":151,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-4.958,0],[0,0],[0,-4.958],[0,0],[4.958,0],[0,0],[0,4.958]],"o":[[0,-4.958],[0,0],[4.958,0],[0,0],[0,4.958],[0,0],[-4.958,0],[0,0]],"v":[[-17.967,-15.017],[-8.983,-24],[-8.983,-24],[0,-15.017],[0,15.017],[-8.983,24],[-8.983,24],[-17.967,15.017]],"c":true}],"t":152,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-7.632,0],[0,0],[0,-7.632],[0,0],[7.632,0],[0,0],[0,7.632]],"o":[[0,-7.632],[0,0],[7.632,0],[0,0],[0,7.632],[0,0],[-7.632,0],[0,0]],"v":[[-27.659,-10.171],[-13.829,-24],[-13.829,-24],[0,-10.171],[0,10.171],[-13.829,24],[-13.829,24],[-27.659,10.171]],"c":true}],"t":153,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-8.91,0],[0,0],[0,-8.91],[0,0],[8.91,0],[0,0],[0,8.91]],"o":[[0,-8.91],[0,0],[8.91,0],[0,0],[0,8.91],[0,0],[-8.91,0],[0,0]],"v":[[-32.287,-7.856],[-16.144,-24],[-16.144,-24],[0,-7.856],[0,7.856],[-16.144,24],[-16.144,24],[-32.287,7.856]],"c":true}],"t":154,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-9.63,0],[0,0],[0,-9.63],[0,0],[9.63,0],[0,0],[0,9.63]],"o":[[0,-9.63],[0,0],[9.63,0],[0,0],[0,9.63],[0,0],[-9.63,0],[0,0]],"v":[[-34.898,-6.551],[-17.449,-24],[-17.449,-24],[0,-6.551],[0,6.551],[-17.449,24],[-17.449,24],[-34.898,6.551]],"c":true}],"t":155,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-10.063,0],[0,0],[0,-10.063],[0,0],[10.063,0],[0,0],[0,10.063]],"o":[[0,-10.063],[0,0],[10.063,0],[0,0],[0,10.063],[0,0],[-10.063,0],[0,0]],"v":[[-36.467,-5.766],[-18.234,-24],[-18.234,-24],[0,-5.766],[0,5.766],[-18.234,24],[-18.234,24],[-36.467,5.766]],"c":true}],"t":156,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-10.317,0],[0,0],[0,-10.317],[0,0],[10.317,0],[0,0],[0,10.317]],"o":[[0,-10.317],[0,0],[10.317,0],[0,0],[0,10.317],[0,0],[-10.317,0],[0,0]],"v":[[-37.388,-5.306],[-18.694,-24],[-18.694,-24],[0,-5.306],[0,5.306],[-18.694,24],[-18.694,24],[-37.388,5.306]],"c":true}],"t":157,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-10.448,0],[0,0],[0,-10.448],[0,0],[10.448,0],[0,0],[0,10.448]],"o":[[0,-10.448],[0,0],[10.448,0],[0,0],[0,10.448],[0,0],[-10.448,0],[0,0]],"v":[[-37.861,-5.07],[-18.93,-24],[-18.93,-24],[0,-5.07],[0,5.07],[-18.93,24],[-18.93,24],[-37.861,5.07]],"c":true}],"t":158,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-10.486,0],[0,0],[0,-10.486],[0,0],[10.486,0],[0,0],[0,10.486]],"o":[[0,-10.486],[0,0],[10.486,0],[0,0],[0,10.486],[0,0],[-10.486,0],[0,0]],"v":[[-38,-5],[-19,-24],[-19,-24],[0,-5],[0,5],[-19,24],[-19,24],[-38,5]],"c":true}],"t":159,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-12.154,0],[0,0],[0,-12.154],[0,0],[12.154,0],[0,0],[0,12.154]],"o":[[0,-12.154],[0,0],[12.154,0],[0,0],[0,12.154],[0,0],[-12.154,0],[0,0]],"v":[[-44.045,-1.977],[-22.023,-24],[-22.023,-24],[0,-1.977],[0,1.977],[-22.023,24],[-22.023,24],[-44.045,1.977]],"c":true}],"t":160,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.079,0],[0,0],[0,-13.079],[0,0],[13.079,0],[0,0],[0,13.079]],"o":[[0,-13.079],[0,0],[13.079,0],[0,0],[0,13.079],[0,0],[-13.079,0],[0,0]],"v":[[-47.396,-0.302],[-23.698,-24],[-23.698,-24],[0,-0.302],[0,0.302],[-23.698,24],[-23.698,24],[-47.396,0.302]],"c":true}],"t":161,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-48,0],[-24,-24],[-24,-24],[0,0],[0,0],[-24,24],[-24,24],[-48,0]],"c":true}],"t":162,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-48.149,0],[-24.149,-24],[-24,-24],[0,0],[0,0],[-24,24],[-24.149,24],[-48.149,0]],"c":true}],"t":163,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-48.698,0],[-24.698,-24],[-24,-24],[0,0],[0,0],[-24,24],[-24.698,24],[-48.698,0]],"c":true}],"t":164,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-49.714,0],[-25.714,-24],[-24,-24],[0,0],[0,0],[-24,24],[-25.714,24],[-49.714,0]],"c":true}],"t":165,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-50.973,0],[-26.973,-24],[-24,-24],[0,0],[0,0],[-24,24],[-26.973,24],[-50.973,0]],"c":true}],"t":166,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-52.19,0],[-28.19,-24],[-24,-24],[0,0],[0,0],[-24,24],[-28.19,24],[-52.19,0]],"c":true}],"t":167,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-53.268,0],[-29.268,-24],[-24,-24],[0,0],[0,0],[-24,24],[-29.268,24],[-53.268,0]],"c":true}],"t":168,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-54.206,0],[-30.206,-24],[-24,-24],[0,0],[0,0],[-24,24],[-30.206,24],[-54.206,0]],"c":true}],"t":169,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-55.028,0],[-31.028,-24],[-24,-24],[0,0],[0,0],[-24,24],[-31.028,24],[-55.028,0]],"c":true}],"t":170,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-55.755,0],[-31.755,-24],[-24,-24],[0,0],[0,0],[-24,24],[-31.755,24],[-55.755,0]],"c":true}],"t":171,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-56.405,0],[-32.405,-24],[-24,-24],[0,0],[0,0],[-24,24],[-32.405,24],[-56.405,0]],"c":true}],"t":172,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-56.99,0],[-32.99,-24],[-24,-24],[0,0],[0,0],[-24,24],[-32.99,24],[-56.99,0]],"c":true}],"t":173,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-57.522,0],[-33.522,-24],[-24,-24],[0,0],[0,0],[-24,24],[-33.522,24],[-57.522,0]],"c":true}],"t":174,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-58.006,0],[-34.006,-24],[-24,-24],[0,0],[0,0],[-24,24],[-34.006,24],[-58.006,0]],"c":true}],"t":175,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-58.451,0],[-34.451,-24],[-24,-24],[0,0],[0,0],[-24,24],[-34.451,24],[-58.451,0]],"c":true}],"t":176,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-58.861,0],[-34.861,-24],[-24,-24],[0,0],[0,0],[-24,24],[-34.861,24],[-58.861,0]],"c":true}],"t":177,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-59.24,0],[-35.24,-24],[-24,-24],[0,0],[0,0],[-24,24],[-35.24,24],[-59.24,0]],"c":true}],"t":178,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-59.591,0],[-35.591,-24],[-24,-24],[0,0],[0,0],[-24,24],[-35.591,24],[-59.591,0]],"c":true}],"t":179,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-59.917,0],[-35.917,-24],[-24,-24],[0,0],[0,0],[-24,24],[-35.917,24],[-59.917,0]],"c":true}],"t":180,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-60.221,0],[-36.221,-24],[-24,-24],[0,0],[0,0],[-24,24],[-36.221,24],[-60.221,0]],"c":true}],"t":181,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-60.504,0],[-36.504,-24],[-24,-24],[0,0],[0,0],[-24,24],[-36.504,24],[-60.504,0]],"c":true}],"t":182,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-60.769,0],[-36.769,-24],[-24,-24],[0,0],[0,0],[-24,24],[-36.769,24],[-60.769,0]],"c":true}],"t":183,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-61.017,0],[-37.017,-24],[-24,-24],[0,0],[0,0],[-24,24],[-37.017,24],[-61.017,0]],"c":true}],"t":184,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-61.248,0],[-37.248,-24],[-24,-24],[0,0],[0,0],[-24,24],[-37.248,24],[-61.248,0]],"c":true}],"t":185,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-61.465,0],[-37.465,-24],[-24,-24],[0,0],[0,0],[-24,24],[-37.465,24],[-61.465,0]],"c":true}],"t":186,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-61.669,0],[-37.669,-24],[-24,-24],[0,0],[0,0],[-24,24],[-37.669,24],[-61.669,0]],"c":true}],"t":187,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-61.859,0],[-37.859,-24],[-24,-24],[0,0],[0,0],[-24,24],[-37.859,24],[-61.859,0]],"c":true}],"t":188,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-62.038,0],[-38.038,-24],[-24,-24],[0,0],[0,0],[-24,24],[-38.038,24],[-62.038,0]],"c":true}],"t":189,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-62.205,0],[-38.205,-24],[-24,-24],[0,0],[0,0],[-24,24],[-38.205,24],[-62.205,0]],"c":true}],"t":190,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-62.362,0],[-38.362,-24],[-24,-24],[0,0],[0,0],[-24,24],[-38.362,24],[-62.362,0]],"c":true}],"t":191,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-62.509,0],[-38.509,-24],[-24,-24],[0,0],[0,0],[-24,24],[-38.509,24],[-62.509,0]],"c":true}],"t":192,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-62.647,0],[-38.647,-24],[-24,-24],[0,0],[0,0],[-24,24],[-38.647,24],[-62.647,0]],"c":true}],"t":193,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-62.776,0],[-38.776,-24],[-24,-24],[0,0],[0,0],[-24,24],[-38.776,24],[-62.776,0]],"c":true}],"t":194,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-62.896,0],[-38.896,-24],[-24,-24],[0,0],[0,0],[-24,24],[-38.896,24],[-62.896,0]],"c":true}],"t":195,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-63.008,0],[-39.008,-24],[-24,-24],[0,0],[0,0],[-24,24],[-39.008,24],[-63.008,0]],"c":true}],"t":196,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-63.113,0],[-39.113,-24],[-24,-24],[0,0],[0,0],[-24,24],[-39.113,24],[-63.113,0]],"c":true}],"t":197,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-63.21,0],[-39.21,-24],[-24,-24],[0,0],[0,0],[-24,24],[-39.21,24],[-63.21,0]],"c":true}],"t":198,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-63.3,0],[-39.3,-24],[-24,-24],[0,0],[0,0],[-24,24],[-39.3,24],[-63.3,0]],"c":true}],"t":199,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-63.384,0],[-39.384,-24],[-24,-24],[0,0],[0,0],[-24,24],[-39.384,24],[-63.384,0]],"c":true}],"t":200,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-63.461,0],[-39.461,-24],[-24,-24],[0,0],[0,0],[-24,24],[-39.461,24],[-63.461,0]],"c":true}],"t":201,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-63.532,0],[-39.532,-24],[-24,-24],[0,0],[0,0],[-24,24],[-39.532,24],[-63.532,0]],"c":true}],"t":202,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-63.597,0],[-39.597,-24],[-24,-24],[0,0],[0,0],[-24,24],[-39.597,24],[-63.597,0]],"c":true}],"t":203,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-63.657,0],[-39.657,-24],[-24,-24],[0,0],[0,0],[-24,24],[-39.657,24],[-63.657,0]],"c":true}],"t":204,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-63.711,0],[-39.711,-24],[-24,-24],[0,0],[0,0],[-24,24],[-39.711,24],[-63.711,0]],"c":true}],"t":205,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-63.76,0],[-39.76,-24],[-24,-24],[0,0],[0,0],[-24,24],[-39.76,24],[-63.76,0]],"c":true}],"t":206,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-63.804,0],[-39.804,-24],[-24,-24],[0,0],[0,0],[-24,24],[-39.804,24],[-63.804,0]],"c":true}],"t":207,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-63.843,0],[-39.843,-24],[-24,-24],[0,0],[0,0],[-24,24],[-39.843,24],[-63.843,0]],"c":true}],"t":208,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-63.877,0],[-39.877,-24],[-24,-24],[0,0],[0,0],[-24,24],[-39.877,24],[-63.877,0]],"c":true}],"t":209,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-63.907,0],[-39.907,-24],[-24,-24],[0,0],[0,0],[-24,24],[-39.907,24],[-63.907,0]],"c":true}],"t":210,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-63.932,0],[-39.932,-24],[-24,-24],[0,0],[0,0],[-24,24],[-39.932,24],[-63.932,0]],"c":true}],"t":211,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-63.971,0],[-39.971,-24],[-24,-24],[0,0],[0,0],[-24,24],[-39.971,24],[-63.971,0]],"c":true}],"t":213,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}]},"nm":"Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.698039233685,0.811764717102,0.654901981354,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"k":[{"s":[0,0],"t":25,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,0],"t":498,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"IndieCorners Shape","bm":0,"hd":false}],"ip":0,"op":501,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".tertiaryFixedDim","cl":"tertiaryFixedDim","parent":6,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":37,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":47,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":249,"s":[100]},{"t":255,"s":[0]}]},"r":{"a":0,"k":0},"p":{"k":[{"s":[0,0,0],"t":123,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,0,0],"t":124,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-0.001,0,0],"t":125,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-0.005,0,0],"t":126,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-0.013,0,0],"t":127,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-0.029,0,0],"t":128,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-0.054,0,0],"t":129,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-0.089,0,0],"t":130,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-0.134,0,0],"t":131,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-0.193,0,0],"t":132,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-0.267,0,0],"t":133,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-0.358,0,0],"t":134,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-0.466,0,0],"t":135,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-0.593,0,0],"t":136,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-0.739,0,0],"t":137,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-0.903,0,0],"t":138,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-1.054,0,0],"t":139,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-1.22,0,0],"t":140,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-1.403,0,0],"t":141,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-1.602,0,0],"t":142,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-1.821,0,0],"t":143,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-2.059,0,0],"t":144,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-2.319,0,0],"t":145,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-2.601,0,0],"t":146,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-2.909,0,0],"t":147,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-3.242,0,0],"t":148,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-3.604,0,0],"t":149,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-3.998,0,0],"t":150,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-4.427,0,0],"t":151,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-4.897,0,0],"t":152,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-5.407,0,0],"t":153,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-5.965,0,0],"t":154,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-6.576,0,0],"t":155,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-7.246,0,0],"t":156,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-7.983,0,0],"t":157,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-8.8,0,0],"t":158,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-9.701,0,0],"t":159,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-10.699,0,0],"t":160,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-11.808,0,0],"t":161,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-13.041,0,0],"t":162,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-14.414,0,0],"t":163,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-15.945,0,0],"t":164,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-17.621,0,0],"t":165,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-19.429,0,0],"t":166,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-21.324,0,0],"t":167,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-23.241,0,0],"t":168,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-25.111,0,0],"t":169,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-26.859,0,0],"t":170,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-28.457,0,0],"t":171,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-29.897,0,0],"t":172,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-31.185,0,0],"t":173,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-32.333,0,0],"t":174,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-33.36,0,0],"t":175,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-34.272,0,0],"t":176,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-35.088,0,0],"t":177,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-35.82,0,0],"t":178,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-36.479,0,0],"t":179,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-37.076,0,0],"t":180,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-37.613,0,0],"t":181,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-38.099,0,0],"t":182,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-38.538,0,0],"t":183,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-38.937,0,0],"t":184,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-39.299,0,0],"t":185,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-39.629,0,0],"t":186,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-39.927,0,0],"t":187,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-40.198,0,0],"t":188,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-40.442,0,0],"t":189,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-40.663,0,0],"t":190,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-40.862,0,0],"t":191,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-41.041,0,0],"t":192,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-41.2,0,0],"t":193,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-41.342,0,0],"t":194,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-41.467,0,0],"t":195,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-41.577,0,0],"t":196,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-41.672,0,0],"t":197,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-41.754,0,0],"t":198,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-41.822,0,0],"t":199,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-41.879,0,0],"t":200,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-41.925,0,0],"t":201,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-41.961,0,0],"t":202,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"ef":[{"ty":5,"nm":"Super Slider","np":3,"mn":"ADBE Slider Control","ix":1,"en":1,"ef":[{"ty":0,"nm":"Slider","mn":"ADBE Slider Control-0001","ix":1,"v":{"a":1,"k":[{"i":{"x":[0.64],"y":[0.48]},"o":{"x":[0.36],"y":[0]},"t":121,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":138,"s":[17.5]},{"t":205,"s":[100]}]}}]}],"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":62,"s":[36,36]},{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":72,"s":[28,28]},{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":247,"s":[28,28]},{"t":257,"s":[36,36]}]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.698039233685,0.811764717102,0.654901981354,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.56,"y":1},"o":{"x":0.44,"y":0},"t":62,"s":[41,0],"to":[0,0],"ti":[0,0]},{"i":{"x":0.56,"y":0.56},"o":{"x":0.44,"y":0.44},"t":72,"s":[33,0],"to":[0,0],"ti":[0,0]},{"i":{"x":0.56,"y":1},"o":{"x":0.44,"y":0},"t":247,"s":[33,0],"to":[0,0],"ti":[0,0]},{"t":257,"s":[41,0]}]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"right circle","bm":0,"hd":false},{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":62,"s":[36,36]},{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":72,"s":[28,28]},{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":247,"s":[28,28]},{"t":257,"s":[36,36]}]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.698039233685,0.811764717102,0.654901981354,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.56,"y":1},"o":{"x":0.44,"y":0},"t":62,"s":[-41,0],"to":[0,0],"ti":[0,0]},{"i":{"x":0.56,"y":0.56},"o":{"x":0.44,"y":0.44},"t":72,"s":[-33,0],"to":[0,0],"ti":[0,0]},{"i":{"x":0.56,"y":1},"o":{"x":0.44,"y":0},"t":247,"s":[-33,0],"to":[0,0],"ti":[0,0]},{"t":257,"s":[-41,0]}]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"left circle","bm":0,"hd":false},{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":62,"s":[36,36]},{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":72,"s":[28,28]},{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":247,"s":[28,28]},{"t":257,"s":[36,36]}]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.698039233685,0.811764717102,0.654901981354,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"size","bm":0,"hd":false}],"ip":37,"op":345,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":".onTertiaryFixedVariant","cl":"onTertiaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[277,459,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[200,128]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":18},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Frame 1321317559","bm":0,"hd":false}],"ip":0,"op":501,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":7,"ty":3,"nm":"pb:scale","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"k":[{"s":[276.737,197.5,0],"t":148,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.57,197.5,0],"t":150,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.319,197.5,0],"t":152,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.15,197.5,0],"t":153,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[275.942,197.5,0],"t":154,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[275.687,197.5,0],"t":155,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[275.37,197.5,0],"t":156,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[274.978,197.5,0],"t":157,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[274.484,197.5,0],"t":158,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[273.85,197.5,0],"t":159,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[273.008,197.5,0],"t":160,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[271.825,197.5,0],"t":161,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[270.222,197.5,0],"t":162,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[268.416,197.5,0],"t":163,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[266.436,197.5,0],"t":164,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[264.37,197.5,0],"t":165,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[262.33,197.5,0],"t":166,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[260.423,197.5,0],"t":167,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[258.703,197.5,0],"t":168,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[257.178,197.5,0],"t":169,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[255.833,197.5,0],"t":170,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[254.646,197.5,0],"t":171,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[253.594,197.5,0],"t":172,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252.657,197.5,0],"t":173,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[251.814,197.5,0],"t":174,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[251.052,197.5,0],"t":175,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[250.36,197.5,0],"t":176,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[249.73,197.5,0],"t":177,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[249.154,197.5,0],"t":178,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[248.627,197.5,0],"t":179,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[248.142,197.5,0],"t":180,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[247.694,197.5,0],"t":181,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[247.28,197.5,0],"t":182,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[246.897,197.5,0],"t":183,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[246.541,197.5,0],"t":184,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[246.211,197.5,0],"t":185,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[245.904,197.5,0],"t":186,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[245.619,197.5,0],"t":187,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[245.353,197.5,0],"t":188,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[245.107,197.5,0],"t":189,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[244.876,197.5,0],"t":190,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[244.661,197.5,0],"t":191,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[244.461,197.5,0],"t":192,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[244.274,197.5,0],"t":193,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[244.099,197.5,0],"t":194,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[243.937,197.5,0],"t":195,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[243.787,197.5,0],"t":196,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[243.648,197.5,0],"t":197,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[243.52,197.5,0],"t":198,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[243.291,197.5,0],"t":200,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[243.098,197.5,0],"t":202,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[242.866,197.5,0],"t":205,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[242.655,197.5,0],"t":209,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[242.5,197.5,0],"t":380,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[242.809,197.5,0],"t":381,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[243.805,197.5,0],"t":382,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[245.726,197.5,0],"t":383,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[249.159,197.5,0],"t":384,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[256.3,197.5,0],"t":385,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[264.431,197.5,0],"t":386,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[268.009,197.5,0],"t":387,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[270.087,197.5,0],"t":388,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[271.496,197.5,0],"t":389,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[272.536,197.5,0],"t":390,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[273.339,197.5,0],"t":391,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[273.98,197.5,0],"t":392,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[274.502,197.5,0],"t":393,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[274.933,197.5,0],"t":394,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[275.295,197.5,0],"t":395,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[275.599,197.5,0],"t":396,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[275.857,197.5,0],"t":397,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.075,197.5,0],"t":398,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.259,197.5,0],"t":399,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.415,197.5,0],"t":400,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.655,197.5,0],"t":402,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.926,197.5,0],"t":406,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}]},"a":{"a":0,"k":[0,0,0]},"s":{"k":[{"s":[99.914,99.914,100],"t":146,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.848,99.848,100],"t":148,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.751,99.751,100],"t":150,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.685,99.685,100],"t":151,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.605,99.605,100],"t":152,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.507,99.507,100],"t":153,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.387,99.387,100],"t":154,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.239,99.239,100],"t":155,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.056,99.056,100],"t":156,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.829,98.829,100],"t":157,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.542,98.542,100],"t":158,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.174,98.174,100],"t":159,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.686,97.686,100],"t":160,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97,97,100],"t":161,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.071,96.071,100],"t":162,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[95.025,95.025,100],"t":163,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[93.878,93.878,100],"t":164,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[92.678,92.678,100],"t":165,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[91.495,91.495,100],"t":166,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[90.39,90.39,100],"t":167,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[89.393,89.393,100],"t":168,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[88.508,88.508,100],"t":169,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[87.729,87.729,100],"t":170,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[87.041,87.041,100],"t":171,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[86.43,86.43,100],"t":172,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[85.886,85.886,100],"t":173,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[85.397,85.397,100],"t":174,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[84.956,84.956,100],"t":175,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[84.555,84.555,100],"t":176,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[84.191,84.191,100],"t":177,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[83.857,83.857,100],"t":178,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[83.552,83.552,100],"t":179,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[83.271,83.271,100],"t":180,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[83.011,83.011,100],"t":181,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[82.771,82.771,100],"t":182,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[82.549,82.549,100],"t":183,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[82.342,82.342,100],"t":184,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[82.151,82.151,100],"t":185,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[81.973,81.973,100],"t":186,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[81.807,81.807,100],"t":187,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[81.653,81.653,100],"t":188,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[81.51,81.51,100],"t":189,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[81.376,81.376,100],"t":190,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[81.251,81.251,100],"t":191,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[81.135,81.135,100],"t":192,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[81.027,81.027,100],"t":193,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.926,80.926,100],"t":194,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.833,80.833,100],"t":195,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.746,80.746,100],"t":196,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.665,80.665,100],"t":197,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.591,80.591,100],"t":198,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.522,80.522,100],"t":199,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.458,80.458,100],"t":200,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.4,80.4,100],"t":201,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.346,80.346,100],"t":202,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.298,80.298,100],"t":203,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.253,80.253,100],"t":204,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.176,80.176,100],"t":206,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.115,80.115,100],"t":208,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.049,80.049,100],"t":211,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80,80,100],"t":380,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.179,80.179,100],"t":381,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.757,80.757,100],"t":382,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[81.87,81.87,100],"t":383,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[83.86,83.86,100],"t":384,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[88,88,100],"t":385,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[92.714,92.714,100],"t":386,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[94.789,94.789,100],"t":387,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[95.992,95.992,100],"t":388,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.809,96.809,100],"t":389,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.412,97.412,100],"t":390,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.878,97.878,100],"t":391,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.249,98.249,100],"t":392,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.553,98.553,100],"t":393,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.803,98.803,100],"t":394,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.012,99.012,100],"t":395,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.188,99.188,100],"t":396,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.337,99.337,100],"t":397,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.464,99.464,100],"t":398,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.57,99.57,100],"t":399,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.661,99.661,100],"t":400,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.737,99.737,100],"t":401,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.8,99.8,100],"t":402,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.896,99.896,100],"t":404,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.99,99.99,100],"t":408,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}}]}},"ao":0,"ef":[{"ty":5,"nm":"Void","np":19,"mn":"Pseudo/250958","ix":1,"en":1,"ef":[{"ty":0,"nm":"Width","mn":"Pseudo/250958-0001","ix":1,"v":{"a":0,"k":100}},{"ty":0,"nm":"Height","mn":"Pseudo/250958-0002","ix":2,"v":{"a":0,"k":100}},{"ty":0,"nm":"Offset X","mn":"Pseudo/250958-0003","ix":3,"v":{"a":0,"k":0}},{"ty":0,"nm":"Offset Y","mn":"Pseudo/250958-0004","ix":4,"v":{"a":0,"k":0}},{"ty":0,"nm":"Roundness","mn":"Pseudo/250958-0005","ix":5,"v":{"a":0,"k":0}},{"ty":6,"nm":"About","mn":"Pseudo/250958-0006","ix":6,"v":0},{"ty":6,"nm":"Plague of null layers.","mn":"Pseudo/250958-0007","ix":7,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0008","ix":8,"v":0},{"ty":6,"nm":"Following projects","mn":"Pseudo/250958-0009","ix":9,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0010","ix":10,"v":0},{"ty":6,"nm":"through time.","mn":"Pseudo/250958-0011","ix":11,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0012","ix":12,"v":0},{"ty":6,"nm":"Be free of the past.","mn":"Pseudo/250958-0013","ix":13,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0014","ix":14,"v":0},{"ty":6,"nm":"Copyright 2023 Battle Axe Inc","mn":"Pseudo/250958-0015","ix":15,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0016","ix":16,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0017","ix":17,"v":0}]}],"ip":0,"op":501,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":".onTertiaryFixed","cl":"onTertiaryFixed","parent":9,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":253,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":256,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":389,"s":[100]},{"t":392,"s":[0]}]},"r":{"a":0,"k":0},"p":{"a":0,"k":[0,0,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"ef":[{"ty":5,"nm":"Global Position","np":4,"mn":"Pseudo/88900","ix":1,"en":1,"ef":[{"ty":10,"nm":"Master Parent","mn":"Pseudo/88900-0001","ix":1,"v":{"a":0,"k":7}},{"ty":3,"nm":"Global Position","mn":"Pseudo/88900-0002","ix":2,"v":{"k":[{"s":[276.737,197.5],"t":148,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.57,197.5],"t":150,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.319,197.5],"t":152,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.15,197.5],"t":153,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[275.942,197.5],"t":154,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[275.687,197.5],"t":155,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[275.37,197.5],"t":156,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[274.978,197.5],"t":157,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[274.484,197.5],"t":158,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[273.85,197.5],"t":159,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[273.008,197.5],"t":160,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[271.825,197.5],"t":161,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[270.222,197.5],"t":162,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[268.416,197.5],"t":163,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[266.436,197.5],"t":164,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[264.37,197.5],"t":165,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[262.33,197.5],"t":166,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[260.423,197.5],"t":167,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[258.703,197.5],"t":168,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[257.178,197.5],"t":169,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[255.833,197.5],"t":170,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[254.646,197.5],"t":171,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[253.594,197.5],"t":172,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252.657,197.5],"t":173,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[251.814,197.5],"t":174,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[251.052,197.5],"t":175,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[250.36,197.5],"t":176,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[249.73,197.5],"t":177,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[249.154,197.5],"t":178,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[248.627,197.5],"t":179,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[248.142,197.5],"t":180,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[247.694,197.5],"t":181,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[247.28,197.5],"t":182,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[246.897,197.5],"t":183,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[246.541,197.5],"t":184,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[246.211,197.5],"t":185,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[245.904,197.5],"t":186,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[245.619,197.5],"t":187,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[245.353,197.5],"t":188,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[245.107,197.5],"t":189,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[244.876,197.5],"t":190,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[244.661,197.5],"t":191,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[244.461,197.5],"t":192,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[244.274,197.5],"t":193,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[244.099,197.5],"t":194,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[243.937,197.5],"t":195,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[243.787,197.5],"t":196,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[243.648,197.5],"t":197,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[243.52,197.5],"t":198,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[243.291,197.5],"t":200,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[243.098,197.5],"t":202,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[242.866,197.5],"t":205,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[242.655,197.5],"t":209,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[242.5,197.5],"t":250,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[243.159,197.525],"t":251,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[245.792,197.842],"t":252,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[251.233,199.672],"t":253,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[257.542,204.005],"t":254,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[262.216,208.989],"t":255,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[265.399,213.457],"t":256,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[267.667,217.355],"t":257,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[269.364,220.773],"t":258,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[270.685,223.812],"t":259,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[271.743,226.538],"t":260,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[272.606,228.991],"t":261,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[273.321,231.197],"t":262,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[273.92,233.179],"t":263,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[274.427,234.953],"t":264,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[274.858,236.532],"t":265,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[275.225,237.926],"t":266,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[275.539,239.152],"t":267,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[275.808,240.219],"t":268,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.038,241.138],"t":269,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.233,241.918],"t":270,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.398,242.568],"t":271,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.537,243.099],"t":272,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.653,243.517],"t":273,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.824,243.574],"t":275,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.884,243.254],"t":276,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.928,242.802],"t":277,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.959,242.269],"t":278,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.976,241.685],"t":279,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.98,241.075],"t":280,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.98,240.497],"t":281,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.98,239.98],"t":282,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.98,239.538],"t":283,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.98,239.181],"t":284,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.98,238.917],"t":285,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.98,239.065],"t":293,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.98,239.265],"t":295,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.98,239.455],"t":297,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.98,239.685],"t":300,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.15,239.729],"t":381,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.715,239.199],"t":382,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[278.839,238.218],"t":383,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[280.95,236.594],"t":384,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[286.015,234.04],"t":385,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[288.407,226.983],"t":386,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[283.954,217.108],"t":387,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[282.005,212.156],"t":388,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[280.975,209.156],"t":389,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[280.314,207.064],"t":390,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[279.834,205.487],"t":391,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[279.461,204.24],"t":392,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[279.159,203.226],"t":393,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[278.905,202.385],"t":394,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[278.691,201.676],"t":395,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[278.503,201.072],"t":396,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[278.339,200.553],"t":397,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[278.193,200.105],"t":398,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[278.061,199.716],"t":399,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.941,199.376],"t":400,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.831,199.079],"t":401,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.729,198.82],"t":402,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.636,198.594],"t":403,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.551,198.398],"t":404,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.472,198.228],"t":405,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.399,198.082],"t":406,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.333,197.956],"t":407,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.209,197.759],"t":409,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.063,197.577],"t":412,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}]}}]}],"shapes":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0,0],"y":[1,1]},"o":{"x":[0.2,0.2],"y":[0,0]},"t":250,"s":[504,315]},{"t":280,"s":[30,30],"h":1},{"i":{"x":[0.8,0.8],"y":[0.15,0.15]},"o":{"x":[0.3,0.3],"y":[0,0]},"t":380,"s":[30,30]},{"i":{"x":[0.1,0.1],"y":[1,1]},"o":{"x":[0.05,0.05],"y":[0.7,0.7]},"t":386,"s":[219.6,144]},{"t":416,"s":[504,315]}]},"p":{"a":0,"k":[0,0]},"r":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0.2],"y":[0]},"t":250,"s":[28]},{"t":280,"s":[30],"h":1},{"i":{"x":[0.8],"y":[0.15]},"o":{"x":[0.3],"y":[0]},"t":380,"s":[30]},{"i":{"x":[0.1],"y":[1]},"o":{"x":[0.05],"y":[0.7]},"t":386,"s":[29.2]},{"t":416,"s":[28]}]},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215687662,0.1254902035,0.027450982481,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false}],"ip":0,"op":501,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":"matte","parent":7,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":1,"k":[{"i":{"x":0,"y":1},"o":{"x":0.2,"y":0},"t":250,"s":[0,0,0],"to":[29.688,0.625,0],"ti":[0,0,0]},{"t":280,"s":[43.1,53,0],"h":1},{"i":{"x":0.8,"y":0.15},"o":{"x":0.3,"y":0},"t":380,"s":[43.1,53,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.1,"y":1},"o":{"x":0.05,"y":0.7},"t":386,"s":[25.86,31.8,0],"to":[0,0,0],"ti":[0,0,0]},{"t":416,"s":[0,0,0]}]},"a":{"a":1,"k":[{"i":{"x":0.5,"y":1},"o":{"x":0.28,"y":0},"t":255,"s":[0,0,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.573,"y":1},"o":{"x":0.236,"y":0},"t":273,"s":[0,-6,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.5,"y":1},"o":{"x":0.28,"y":0},"t":287,"s":[0,1.5,0],"to":[0,0,0],"ti":[0,0,0]},{"t":307,"s":[0,0,0]}]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0,0],"y":[1,1]},"o":{"x":[0.2,0.2],"y":[0,0]},"t":250,"s":[504,315]},{"t":280,"s":[30,30],"h":1},{"i":{"x":[0.8,0.8],"y":[0.15,0.15]},"o":{"x":[0.3,0.3],"y":[0,0]},"t":380,"s":[30,30]},{"i":{"x":[0.1,0.1],"y":[1,1]},"o":{"x":[0.05,0.05],"y":[0.7,0.7]},"t":386,"s":[219.6,144]},{"t":416,"s":[504,315]}]},"p":{"a":0,"k":[0,0]},"r":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0.2],"y":[0]},"t":250,"s":[28]},{"t":280,"s":[30],"h":1},{"i":{"x":[0.8],"y":[0.15]},"o":{"x":[0.3],"y":[0]},"t":380,"s":[30]},{"i":{"x":[0.1],"y":[1]},"o":{"x":[0.05],"y":[0.7]},"t":386,"s":[29.2]},{"t":416,"s":[28]}]},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false}],"ip":0,"op":501,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":10,"ty":0,"nm":"Back_LofiApp","parent":9,"tt":1,"tp":9,"refId":"comp_1","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[0,0,0]},"a":{"a":0,"k":[252,157.5,0]},"s":{"a":1,"k":[{"i":{"x":[0,0,0],"y":[1,1,1]},"o":{"x":[0.2,0.2,0.2],"y":[0,0,0]},"t":250,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":280,"s":[10,10,100]},{"i":{"x":[0.8,0.8,0.8],"y":[0.15,0.15,1]},"o":{"x":[0.3,0.3,0.3],"y":[0,0,0]},"t":380,"s":[10,10,100]},{"i":{"x":[0.1,0.1,0.1],"y":[1,1,1]},"o":{"x":[0.05,0.05,0.05],"y":[0.7,0.7,0]},"t":386,"s":[46,46,100]},{"t":416,"s":[100,100,100]}]}},"ao":0,"w":504,"h":315,"ip":0,"op":501,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":"behindApp","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":253,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":259,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":386,"s":[0]},{"t":397,"s":[100]}]},"r":{"a":0,"k":0},"p":{"a":0,"k":[277,197.5,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[503.5,314.5]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":28},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"frame","bm":0,"hd":false}],"ip":0,"op":501,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":12,"ty":0,"nm":"Back_LofiLauncher","refId":"comp_2","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[277,197.5,0]},"a":{"a":0,"k":[252,157.5,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"w":504,"h":315,"ip":0,"op":501,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":".tertiaryFixedDim","cl":"tertiaryFixedDim","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[277,197.5,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":28},"nm":"Rectangle Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[0.698039233685,0.811764717102,0.654901981354,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":14},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"op","nm":"Stroke align: Outside","a":{"k":[{"s":[7],"t":25,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[7],"t":498,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"lj":1,"ml":{"a":0,"k":4},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.698039233685,0.811764717102,0.654901981354,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"frame","bm":0,"hd":false}],"ip":0,"op":501,"st":0,"ct":1,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":0,"nm":"Back_LeftDismiss","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[277,282,0]},"a":{"a":0,"k":[277,282,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"w":554,"h":564,"ip":0,"op":426,"st":-25,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":0,"nm":"Back_RightDismiss","refId":"comp_3","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[277,282,0]},"a":{"a":0,"k":[277,282,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"w":554,"h":564,"ip":426,"op":902,"st":401,"ct":1,"bm":0}],"markers":[],"props":{}}
\ No newline at end of file
diff --git a/packages/SystemUI/res/raw/trackpad_back_success.json b/packages/SystemUI/res/raw/trackpad_back_success.json
new file mode 100644
index 0000000..56b6ff1
--- /dev/null
+++ b/packages/SystemUI/res/raw/trackpad_back_success.json
@@ -0,0 +1 @@
+{"v":"5.12.1","fr":60,"ip":0,"op":50,"w":554,"h":564,"nm":"Trackpad-JSON_Success","ddd":0,"assets":[{"id":"comp_0","nm":"TrackpadBack_Success_Checkmark","fr":60,"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"Check Rotate","parent":2,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":1,"k":[{"i":{"x":[0.12],"y":[1]},"o":{"x":[0.44],"y":[0]},"t":2,"s":[-16]},{"t":20,"s":[6]}]},"p":{"a":0,"k":[0,0,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[95.049,95.049,100]}},"ao":0,"ip":0,"op":228,"st":-72,"bm":0},{"ddd":0,"ind":2,"ty":3,"nm":"Bounce","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":1,"k":[{"i":{"x":[0.12],"y":[1]},"o":{"x":[0.44],"y":[0]},"t":12,"s":[0]},{"t":36,"s":[-6]}]},"p":{"a":0,"k":[81,127,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":1,"k":[{"i":{"x":[0.263,0.263,0.833],"y":[1.126,1.126,1]},"o":{"x":[0.05,0.05,0.05],"y":[0.958,0.958,0]},"t":1,"s":[80,80,100]},{"i":{"x":[0.1,0.1,0.1],"y":[1,1,1]},"o":{"x":[0.45,0.45,0.167],"y":[0.325,0.325,0]},"t":20,"s":[105,105,100]},{"t":36,"s":[100,100,100]}]}},"ao":0,"ip":0,"op":300,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".tertiaryFixedDim","cl":"tertiaryFixedDim","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":-0.289},"p":{"a":0,"k":[14.364,-33.591,0]},"a":{"a":0,"k":[-0.125,0,0]},"s":{"a":0,"k":[104.744,104.744,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[-1.401,-0.007],[-10.033,11.235]],"o":[[5.954,7.288],[1.401,0.007],[0,0]],"v":[[-28.591,4.149],[-10.73,26.013],[31.482,-21.255]],"c":false}},"nm":"Path 1","hd":false},{"ty":"tm","s":{"a":0,"k":0},"e":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.4],"y":[0]},"t":3,"s":[0]},{"i":{"x":[0.22],"y":[1]},"o":{"x":[0.001],"y":[0.149]},"t":10,"s":[29]},{"t":27,"s":[100]}]},"o":{"a":0,"k":0},"m":1,"nm":"Trim Paths 1","hd":false},{"ty":"st","c":{"a":0,"k":[0.698039233685,0.811764717102,0.654901981354,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":11},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Shape 1","bm":0,"hd":false}],"ip":5,"op":44,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".onTertiaryFixedVariant","cl":"onTertiaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[95,95,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":1,"k":[{"i":{"x":[0.275,0.275,0.21],"y":[1.102,1.102,1]},"o":{"x":[0.037,0.037,0.05],"y":[0.476,0.476,0]},"t":0,"s":[0,0,100]},{"i":{"x":[0.1,0.1,0.1],"y":[1,1,1]},"o":{"x":[0.252,0.252,0.47],"y":[0.159,0.159,0]},"t":16,"s":[120,120,100]},{"t":28,"s":[100,100,100]}]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.1,0.1],"y":[1,1]},"o":{"x":[0.32,0.32],"y":[0.11,0.11]},"t":16,"s":[148,148]},{"t":28,"s":[136,136]}]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":88},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"k":[{"s":[0.208,0.302,0.184,1],"t":0,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.208,0.302,0.184,1],"t":43,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Checkbox - Widget","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0}]},{"id":"comp_1","nm":"Back_LofiApp","fr":60,"pfr":1,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".onTertiaryFixedVariant","cl":"onTertiaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[339.937,151.75,0]},"a":{"a":0,"k":[339.937,151.75,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[1.021,-1.766],[0,0],[-2.043,0],[0,0],[1.022,1.767]],"o":[[-1.021,-1.766],[0,0],[-1.022,1.767],[0,0],[2.043,0],[0,0]],"v":[[2.297,-7.675],[-2.297,-7.675],[-9.64,5.025],[-7.343,9],[7.343,9],[9.64,5.025]],"c":true}},"nm":"Path 1","hd":false},{"ty":"rd","nm":"Round Corners 1","r":{"a":0,"k":9},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Triangle","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[481.874,21]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Triangle","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[18,18]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":200},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Rectangle","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[457.874,21]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Rectangle","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[292,25]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":200},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Text field","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[334,279]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Text field","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[109,28]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":12},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Sent","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[425.5,208.5]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Sent","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[160,56]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":14},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Sent","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[400,158.5]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Sent","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[126,40]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":14},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Received","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[251,78.5]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Received","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".onTertiaryFixed","cl":"onTertiaryFixed","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[334,157.5,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[340,315]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":16},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215687662,0.1254902035,0.027450980619,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Message","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".onTertiaryFixedVariant","cl":"onTertiaryFixedVariant","parent":4,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[82,171.125,0]},"a":{"a":0,"k":[82,171.125,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[64,8]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":39.375},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Line 2","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[80,177.125]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Line 4","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[92,8]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":39.375},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Line 1","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[94,165.125]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Line 3","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[20,20]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":39.375},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Avatar","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[34,171.125]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"circle 2","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".onTertiaryFixed","cl":"onTertiaryFixed","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[82,140,0]},"a":{"a":0,"k":[82,140.938,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[132,22]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":39.375},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215687662,0.1254902035,0.027450980619,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Search","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[82,31.5]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"header","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[64,8]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":200},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215687662,0.1254902035,0.027450980619,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Line 2","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[80,257.375]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Line 6","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[92,8]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":200},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215687662,0.1254902035,0.027450980619,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Line 1","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[94,245.375]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Line 5","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[20,20]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":200},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215687662,0.1254902035,0.027450980619,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Avatar","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[34,251.375]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"circle 3","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[132,64]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":12},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215687662,0.1254902035,0.027450980619,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Message","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[82,171]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"block","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[64,8]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":200},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215687662,0.1254902035,0.027450980619,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Line 2","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[80,96.875]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Line 2","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[92,8]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":200},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215687662,0.1254902035,0.027450980619,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Line 1","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[94,84.875]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Line 1","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[20,20]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":200},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215687662,0.1254902035,0.027450980619,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Avatar","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[34,90.875]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"circle 1","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".onTertiaryFixedVariant","cl":"onTertiaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[252,157.5,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":18},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"app only","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0}]},{"id":"comp_2","nm":"Back_LofiLauncher","fr":60,"pfr":1,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".onTertiaryFixed","cl":"onTertiaryFixed","parent":4,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[0,117.5,0]},"a":{"a":0,"k":[252,275,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"ef":[{"ty":25,"nm":"Drop Shadow","np":8,"mn":"ADBE Drop Shadow","ix":1,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,0.039999999106]}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":10.2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0.394}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":1.181}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0}}]},{"ty":25,"nm":"Drop Shadow 2","np":8,"mn":"ADBE Drop Shadow","ix":2,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,0.029999999329]}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":7.65}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":2.362}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0}}]},{"ty":25,"nm":"Drop Shadow 3","np":8,"mn":"ADBE Drop Shadow","ix":3,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,0.039999999106]}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":10.2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0.394}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":1.181}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0}}]},{"ty":25,"nm":"Drop Shadow 4","np":8,"mn":"ADBE Drop Shadow","ix":4,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,0.029999999329]}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":7.65}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":2.362}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0}}]},{"ty":25,"nm":"Drop Shadow 5","np":8,"mn":"ADBE Drop Shadow","ix":5,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,0.039999999106]}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":10.2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0.394}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":1.181}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0}}]},{"ty":25,"nm":"Drop Shadow 6","np":8,"mn":"ADBE Drop Shadow","ix":6,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,0.029999999329]}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":7.65}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":2.362}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0}}]},{"ty":25,"nm":"Drop Shadow 7","np":8,"mn":"ADBE Drop Shadow","ix":7,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,0.039999999106]}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":10.2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0.394}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":1.181}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0}}]},{"ty":25,"nm":"Drop Shadow 8","np":8,"mn":"ADBE Drop Shadow","ix":8,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,0.029999999329]}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":7.65}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":2.362}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0}}]},{"ty":25,"nm":"Drop Shadow 9","np":8,"mn":"ADBE Drop Shadow","ix":9,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,0.039999999106]}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":10.2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0.394}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":1.181}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0}}]},{"ty":25,"nm":"Drop Shadow 10","np":8,"mn":"ADBE Drop Shadow","ix":10,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,0.029999999329]}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":7.65}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":2.362}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0}}]},{"ty":25,"nm":"Drop Shadow 11","np":8,"mn":"ADBE Drop Shadow","ix":11,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,0.039999999106]}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":10.2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0.394}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":1.181}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0}}]},{"ty":25,"nm":"Drop Shadow 12","np":8,"mn":"ADBE Drop Shadow","ix":12,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,0.029999999329]}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":7.65}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":2.362}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0}}]}],"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215686275,0.125490196078,0.027450980392,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[444,275]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"hotseat - 5","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215686275,0.125490196078,0.027450980392,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[396,275]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"hotseat - 4","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215686275,0.125490196078,0.027450980392,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[348,275]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"hotseat - 3","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215686275,0.125490196078,0.027450980392,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[300,275]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"hotseat - 2","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215686275,0.125490196078,0.027450980392,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[252,275]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"hotseat - 1","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[168,20]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":15},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215686275,0.125490196078,0.027450980392,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"qsb","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[132,275]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"qsb","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".onTertiaryFixed","cl":"onTertiaryFixed","parent":4,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[0,-29.497,0]},"a":{"a":0,"k":[252,128.003,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"ef":[{"ty":25,"nm":"Drop Shadow","np":8,"mn":"ADBE Drop Shadow","ix":1,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,0.039999999106]}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":10.2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0.394}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":1.181}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0}}]},{"ty":25,"nm":"Drop Shadow 2","np":8,"mn":"ADBE Drop Shadow","ix":2,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,0.029999999329]}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":7.65}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":2.362}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0}}]},{"ty":25,"nm":"Drop Shadow 3","np":8,"mn":"ADBE Drop Shadow","ix":3,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,0.039999999106]}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":10.2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0.394}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":1.181}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0}}]},{"ty":25,"nm":"Drop Shadow 4","np":8,"mn":"ADBE Drop Shadow","ix":4,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,0.029999999329]}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":7.65}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":2.362}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0}}]}],"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[20.144,20.144],[20.144,-20.144],[0,0],[-20.144,-20.144],[-20.144,20.144],[0,0]],"o":[[-20.144,-20.144],[0,0],[-20.144,20.144],[20.144,20.144],[0,0],[20.144,-20.144]],"v":[[44.892,-44.892],[-28.057,-44.892],[-44.892,-28.057],[-44.892,44.892],[28.057,44.892],[44.892,28.057]],"c":true}},"nm":"Path 1","hd":false},{"ty":"rd","nm":"Round Corners 1","r":{"a":0,"k":15},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215686275,0.125490196078,0.027450980392,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"widgets","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[108,152.004]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"widgets weather","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[4.782,-2.684],[0,0],[2.63,-0.033],[0,0],[2.807,-4.716],[0,0],[2.263,-1.343],[0,0],[0.066,-5.485],[0,0],[1.292,-2.295],[0,0],[-2.683,-4.784],[0,0],[-0.033,-2.63],[0,0],[-4.716,-2.807],[0,0],[-1.338,-2.263],[0,0],[-5.483,-0.066],[0,0],[-2.296,-1.292],[0,0],[-4.782,2.683],[0,0],[-2.63,0.033],[0,0],[-2.807,4.716],[0,0],[-2.263,1.338],[0,0],[-0.066,5.483],[0,0],[-1.292,2.295],[0,0],[2.683,4.784],[0,0],[0.033,2.631],[0,0],[4.716,2.801],[0,0],[1.338,2.262],[0,0],[5.483,0.068],[0,0],[2.296,1.287]],"o":[[-4.782,-2.684],[0,0],[-2.296,1.287],[0,0],[-5.483,0.068],[0,0],[-1.338,2.262],[0,0],[-4.716,2.801],[0,0],[-0.033,2.631],[0,0],[-2.683,4.784],[0,0],[1.292,2.295],[0,0],[0.066,5.483],[0,0],[2.263,1.338],[0,0],[2.807,4.716],[0,0],[2.63,0.033],[0,0],[4.782,2.683],[0,0],[2.296,-1.292],[0,0],[5.483,-0.066],[0,0],[1.338,-2.263],[0,0],[4.716,-2.807],[0,0],[0.033,-2.63],[0,0],[2.683,-4.784],[0,0],[-1.292,-2.295],[0,0],[-0.066,-5.485],[0,0],[-2.263,-1.343],[0,0],[-2.807,-4.716],[0,0],[-2.63,-0.033],[0,0]],"v":[[7.7,-57.989],[-7.7,-57.989],[-11.019,-56.128],[-18.523,-54.117],[-22.327,-54.07],[-35.668,-46.369],[-37.609,-43.1],[-43.099,-37.605],[-46.372,-35.663],[-54.072,-22.324],[-54.118,-18.522],[-56.132,-11.016],[-57.988,-7.7],[-57.988,7.703],[-56.132,11.019],[-54.118,18.524],[-54.072,22.328],[-46.372,35.669],[-43.099,37.611],[-37.609,43.101],[-35.668,46.373],[-22.327,54.074],[-18.523,54.12],[-11.019,56.133],[-7.7,57.99],[7.7,57.99],[11.019,56.133],[18.523,54.12],[22.327,54.074],[35.668,46.373],[37.609,43.101],[43.099,37.611],[46.372,35.669],[54.072,22.328],[54.118,18.524],[56.132,11.019],[57.988,7.703],[57.988,-7.7],[56.132,-11.016],[54.118,-18.522],[54.072,-22.324],[46.372,-35.663],[43.099,-37.605],[37.609,-43.1],[35.668,-46.369],[22.327,-54.07],[18.523,-54.117],[11.019,-56.128]],"c":true}},"nm":"Path 1","hd":false},{"ty":"rd","nm":"Round Corners 1","r":{"a":0,"k":15},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215686275,0.125490196078,0.027450980392,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"widgets","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[396,104.003]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"widgets clock","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".onTertiaryFixed","cl":"onTertiaryFixed","parent":4,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[0,-29.497,0]},"a":{"a":0,"k":[252,128.003,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"ef":[{"ty":25,"nm":"Drop Shadow","np":8,"mn":"ADBE Drop Shadow","ix":1,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,0.039999999106]}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":10.2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0.394}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":1.181}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0}}]},{"ty":25,"nm":"Drop Shadow 2","np":8,"mn":"ADBE Drop Shadow","ix":2,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,0.029999999329]}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":7.65}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":2.362}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0}}]},{"ty":25,"nm":"Drop Shadow 3","np":8,"mn":"ADBE Drop Shadow","ix":3,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,0.039999999106]}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":10.2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0.394}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":1.181}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0}}]},{"ty":25,"nm":"Drop Shadow 4","np":8,"mn":"ADBE Drop Shadow","ix":4,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,0.029999999329]}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":7.65}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":2.362}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0}}]},{"ty":25,"nm":"Drop Shadow 5","np":8,"mn":"ADBE Drop Shadow","ix":5,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,0.039999999106]}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":10.2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0.394}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":1.181}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0}}]},{"ty":25,"nm":"Drop Shadow 6","np":8,"mn":"ADBE Drop Shadow","ix":6,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,0.029999999329]}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":7.65}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":2.362}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0}}]},{"ty":25,"nm":"Drop Shadow 7","np":8,"mn":"ADBE Drop Shadow","ix":7,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,0.039999999106]}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":10.2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0.394}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":1.181}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0}}]},{"ty":25,"nm":"Drop Shadow 8","np":8,"mn":"ADBE Drop Shadow","ix":8,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,0.029999999329]}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":7.65}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":2.362}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0}}]},{"ty":25,"nm":"Drop Shadow 9","np":8,"mn":"ADBE Drop Shadow","ix":9,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,0.039999999106]}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":10.2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0.394}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":1.181}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0}}]},{"ty":25,"nm":"Drop Shadow 10","np":8,"mn":"ADBE Drop Shadow","ix":10,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,0.029999999329]}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":7.65}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":2.362}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0}}]},{"ty":25,"nm":"Drop Shadow 11","np":8,"mn":"ADBE Drop Shadow","ix":11,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,0.039999999106]}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":10.2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0.394}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":1.181}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0}}]},{"ty":25,"nm":"Drop Shadow 12","np":8,"mn":"ADBE Drop Shadow","ix":12,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,0.029999999329]}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":7.65}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":2.362}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0}}]},{"ty":25,"nm":"Drop Shadow 13","np":8,"mn":"ADBE Drop Shadow","ix":13,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,0.039999999106]}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":10.2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0.394}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":1.181}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0}}]},{"ty":25,"nm":"Drop Shadow 14","np":8,"mn":"ADBE Drop Shadow","ix":14,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,0.029999999329]}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":7.65}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":2.362}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0}}]}],"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215686275,0.125490196078,0.027450980392,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[444,200.004]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"app - 7","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215686275,0.125490196078,0.027450980392,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[348,200.004]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"app - 6","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215686275,0.125490196078,0.027450980392,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[252,128.004]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"app - 4","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215686275,0.125490196078,0.027450980392,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[252,56.002]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"app - 3","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215686275,0.125490196078,0.027450980392,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[156,56.004]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"app - 2","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215686275,0.125490196078,0.027450980392,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[60,56.004]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"app - 1","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":3,"nm":"Scale Up","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[252,157.5,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":1,"k":[{"i":{"x":[0.8,0.8,0.8],"y":[0.15,0.15,1]},"o":{"x":[0.3,0.3,0.3],"y":[0,0,0]},"t":250,"s":[85,85,100]},{"i":{"x":[0.1,0.1,0.1],"y":[1,1,1]},"o":{"x":[0.05,0.05,0.05],"y":[0.7,0.7,0]},"t":256,"s":[91,91,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":286,"s":[100,100,100]},{"i":{"x":[0.8,0.8,0.8],"y":[0.15,0.15,1]},"o":{"x":[0.3,0.3,0.3],"y":[0,0,0]},"t":380,"s":[100,100,100]},{"i":{"x":[0.1,0.1,0.1],"y":[1,1,1]},"o":{"x":[0.05,0.05,0.05],"y":[0.7,0.7,0]},"t":386,"s":[96,96,100]},{"t":416,"s":[90,90,100]}]}},"ao":0,"ef":[{"ty":5,"nm":"Void","np":19,"mn":"Pseudo/250958","ix":1,"en":1,"ef":[{"ty":0,"nm":"Width","mn":"Pseudo/250958-0001","ix":1,"v":{"a":0,"k":100}},{"ty":0,"nm":"Height","mn":"Pseudo/250958-0002","ix":2,"v":{"a":0,"k":100}},{"ty":0,"nm":"Offset X","mn":"Pseudo/250958-0003","ix":3,"v":{"a":0,"k":0}},{"ty":0,"nm":"Offset Y","mn":"Pseudo/250958-0004","ix":4,"v":{"a":0,"k":0}},{"ty":0,"nm":"Roundness","mn":"Pseudo/250958-0005","ix":5,"v":{"a":0,"k":0}},{"ty":6,"nm":"About","mn":"Pseudo/250958-0006","ix":6,"v":0},{"ty":6,"nm":"Plague of null layers.","mn":"Pseudo/250958-0007","ix":7,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0008","ix":8,"v":0},{"ty":6,"nm":"Following projects","mn":"Pseudo/250958-0009","ix":9,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0010","ix":10,"v":0},{"ty":6,"nm":"through time.","mn":"Pseudo/250958-0011","ix":11,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0012","ix":12,"v":0},{"ty":6,"nm":"Be free of the past.","mn":"Pseudo/250958-0013","ix":13,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0014","ix":14,"v":0},{"ty":6,"nm":"Copyright 2023 Battle Axe Inc","mn":"Pseudo/250958-0015","ix":15,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0016","ix":16,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0017","ix":17,"v":0}]}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".onTertiaryFixedVariant","cl":"onTertiaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[252,157.5,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":28},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"illustrations: action key","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".onTertiaryFixedVariant","cl":"onTertiaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[277,459,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[200,128]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":18},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Frame 1321317559","bm":0,"hd":false}],"ip":0,"op":50,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":0,"nm":"TrackpadBack_Success_Checkmark","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[277,198.5,0]},"a":{"a":0,"k":[95,95,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"w":190,"h":190,"ip":6,"op":50,"st":6,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".onTertiaryFixed","cl":"onTertiaryFixed","parent":4,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":1,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":7,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":389,"s":[100]},{"t":392,"s":[0]}]},"r":{"a":0,"k":0},"p":{"a":0,"k":[0,0,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"ef":[{"ty":5,"nm":"Global Position","np":4,"mn":"Pseudo/88900","ix":1,"en":1,"ef":[{"ty":10,"nm":"Master Parent","mn":"Pseudo/88900-0001","ix":1,"v":{"a":0,"k":3}},{"ty":3,"nm":"Global Position","mn":"Pseudo/88900-0002","ix":2,"v":{"k":[{"s":[0,0],"t":0,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,0],"t":49,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}]}}]}],"shapes":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":28},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215687662,0.1254902035,0.027450982481,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false}],"ip":0,"op":50,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"matte","td":1,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[277,197.5,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":28},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false}],"ip":0,"op":50,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":5,"ty":0,"nm":"Back_LofiApp","parent":4,"tt":1,"tp":4,"refId":"comp_1","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[0,0,0]},"a":{"a":0,"k":[252,157.5,0]},"s":{"a":1,"k":[{"i":{"x":[0,0,0],"y":[1,1,1]},"o":{"x":[0.2,0.2,0.2],"y":[0,0,0]},"t":250,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":280,"s":[10,10,100]},{"i":{"x":[0.8,0.8,0.8],"y":[0.15,0.15,1]},"o":{"x":[0.3,0.3,0.3],"y":[0,0,0]},"t":380,"s":[10,10,100]},{"i":{"x":[0.1,0.1,0.1],"y":[1,1,1]},"o":{"x":[0.05,0.05,0.05],"y":[0.7,0.7,0]},"t":386,"s":[46,46,100]},{"t":416,"s":[100,100,100]}]}},"ao":0,"w":504,"h":315,"ip":0,"op":50,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"behindApp","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":253,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":259,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":386,"s":[0]},{"t":397,"s":[100]}]},"r":{"a":0,"k":0},"p":{"a":0,"k":[277,197.5,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[503.5,314.5]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":28},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"frame","bm":0,"hd":false}],"ip":0,"op":50,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":7,"ty":0,"nm":"Back_LofiLauncher","refId":"comp_2","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[277,197.5,0]},"a":{"a":0,"k":[252,157.5,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"w":504,"h":315,"ip":0,"op":50,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":".tertiaryFixedDim","cl":"tertiaryFixedDim","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[277,197.5,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":28},"nm":"Rectangle Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[0.698039233685,0.811764717102,0.654901981354,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":14},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"op","nm":"Stroke align: Outside","a":{"k":[{"s":[7],"t":0,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[7],"t":49,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"lj":1,"ml":{"a":0,"k":4},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.698039233685,0.811764717102,0.654901981354,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"frame","bm":0,"hd":false}],"ip":0,"op":50,"st":0,"ct":1,"bm":0}],"markers":[],"props":{}}
\ No newline at end of file
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index adeafbc..8c685ff 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -126,45 +126,27 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"Tik om te bekyk"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"Kon nie skermopname stoor nie"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Kon nie skermopname begin nie"</string>
-    <!-- no translation found for screenrecord_stop_dialog_title (8716193661764511095) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message (6262768207331626817) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5995770227684523244) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
-    <skip />
+    <string name="screenrecord_stop_dialog_title" msgid="8716193661764511095">"Stop opname?"</string>
+    <string name="screenrecord_stop_dialog_message" msgid="6262768207331626817">"Jy neem tans jou hele skerm op"</string>
+    <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Jy neem tans <xliff:g id="APP_NAME">%1$s</xliff:g> op"</string>
+    <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Stop opname"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Deel tans skerm"</string>
-    <!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen_with_host_app (522823522115375414) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen (5090115386271179270) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_specific (5923772039347985172) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_generic (6681016774654578261) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
-    <skip />
+    <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Hou op om skerm te deel?"</string>
+    <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Jy deel tans jou hele skerm met <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
+    <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Jy deel tans jou hele skerm met ’n app"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Jy deel tans <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Jy deel tans ’n app"</string>
+    <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Hou op deel"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Skerm word tans uitgesaai"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Hou op uitsaai?"</string>
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen_with_device (1474703115926205251) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen (8419219169553867625) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app_with_device (2715934698604085519) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (8616103075630934513) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic_with_device (9213582497852420203) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic (4100272100480415076) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
-    <skip />
-    <!-- no translation found for close_dialog_button (4749497706540104133) -->
-    <skip />
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen_with_device" msgid="1474703115926205251">"Jy saai tans jou hele skerm uit na <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen" msgid="8419219169553867625">"Jy saai tans jou hele skerm uit na ’n toestel in die omtrek"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app_with_device" msgid="2715934698604085519">"Jy saai tans <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> uit na <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="8616103075630934513">"Jy saai tans <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> uit na ’n toestel in die omtrek"</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic_with_device" msgid="9213582497852420203">"Jy saai tans uit na <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic" msgid="4100272100480415076">"Jy saai tans uit na ’n toestel in die omtrek"</string>
+    <string name="cast_to_other_device_stop_dialog_button" msgid="6420183747435521834">"Hou op uitsaai"</string>
+    <string name="close_dialog_button" msgid="4749497706540104133">"Maak toe"</string>
     <string name="issuerecord_title" msgid="286627115110121849">"Kwessieopnemer"</string>
     <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Verwerk tans kwessieopname"</string>
     <string name="issuerecord_channel_description" msgid="6142326363431474632">"Deurlopende kennisgewing vir ’n kwessieversamelingsessie"</string>
@@ -175,8 +157,7 @@
     <string name="issuerecord_save_error" msgid="6913040083446722726">"Kon nie kwessieopname stoor nie"</string>
     <string name="issuerecord_start_error" msgid="3402782952722871190">"Kon nie kwessieopname begin nie"</string>
     <string name="immersive_cling_title" msgid="8372056499315585941">"Bekyk tans volskerm"</string>
-    <!-- no translation found for immersive_cling_description (2717426731830851921) -->
-    <skip />
+    <string name="immersive_cling_description" msgid="2717426731830851921">"Swiep van die bokant van jou skerm af ondertoe om uit te gaan"</string>
     <string name="immersive_cling_positive" msgid="3076681691468978568">"Het dit"</string>
     <string name="accessibility_back" msgid="6530104400086152611">"Terug"</string>
     <string name="accessibility_home" msgid="5430449841237966217">"Tuis"</string>
@@ -309,8 +290,7 @@
     <string name="start_dreams" msgid="9131802557946276718">"Sluimerskerm"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"Moenie Steur Nie"</string>
-    <!-- no translation found for quick_settings_modes_label (5407025818652750501) -->
-    <skip />
+    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Prioriteitmodusse"</string>
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Geen saamgebinde toestelle beskikbaar nie"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Tik om ’n toestel te koppel of ontkoppel"</string>
@@ -322,8 +302,7 @@
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Gestoor"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ontkoppel"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktiveer"</string>
-    <!-- no translation found for turn_on_bluetooth_auto_tomorrow (3345758139235739006) -->
-    <skip />
+    <string name="turn_on_bluetooth_auto_tomorrow" msgid="3345758139235739006">"Skakel dit môre outomaties aan"</string>
     <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Kenmerke soos Kitsdeel en Kry My Toestel gebruik Bluetooth"</string>
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth sal môreoggend aanskakel"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Deel oudio"</string>
@@ -409,6 +388,9 @@
     <string name="performance" msgid="6552785217174378320">"Werkverrigting"</string>
     <string name="user_interface" msgid="3712869377953950887">"Gebruikerkoppelvlak"</string>
     <string name="thermal" msgid="6758074791325414831">"Termies"</string>
+    <string name="custom" msgid="3337456985275158299">"Gepasmaak"</string>
+    <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Gepasmaakte Nasporinginstellings"</string>
+    <string name="restore_default" msgid="5259420807486239755">"Stel verstek terug"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Eenhandmodus"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Gehoortoestelle"</string>
     <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktief"</string>
@@ -447,6 +429,17 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Maak Instellings oop"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Ander toestel"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Wissel oorsig"</string>
+    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Prioriteitmodusse"</string>
+    <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Klaar"</string>
+    <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Instellings"</string>
+    <string name="zen_mode_on" msgid="9085304934016242591">"Aan"</string>
+    <string name="zen_mode_off" msgid="1736604456618147306">"Af"</string>
+    <!-- no translation found for zen_mode_set_up (7457957033034460064) -->
+    <skip />
+    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
+    <skip />
+    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
+    <skip />
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Jy sal nie deur geluide en vibrasies gepla word nie, behalwe deur wekkers, herinneringe, geleenthede en bellers wat jy spesifiseer. Jy sal steeds enigiets hoor wat jy kies om te speel, insluitend musiek, video\'s en speletjies."</string>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Jy sal nie deur geluide en vibrasies gepla word nie, behalwe deur wekkers. Jy sal steeds enigiets hoor wat jy kies om te speel, insluitend musiek, video\'s en speletjies."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Pasmaak"</string>
@@ -485,6 +478,8 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Laai tans stadig • Vol oor <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Laai tans • Vol oor <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Legstukke op sluitskerm"</string>
+    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
+    <skip />
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Swiep links om die gemeenskaplike tutoriaal te begin"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Pasmaak"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Maak toe"</string>
@@ -511,16 +506,12 @@
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"kies legstuk"</string>
     <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"verwyder legstuk"</string>
     <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"plaas gekose legstuk"</string>
-    <!-- no translation found for communal_widget_picker_title (1953369090475731663) -->
-    <skip />
-    <!-- no translation found for communal_widget_picker_description (490515450110487871) -->
-    <skip />
-    <!-- no translation found for communal_widgets_disclaimer_title (1150954395585308868) -->
-    <skip />
-    <!-- no translation found for communal_widgets_disclaimer_text (1423545475160506349) -->
-    <skip />
-    <!-- no translation found for communal_widgets_disclaimer_button (4423059765740780753) -->
-    <skip />
+    <string name="communal_widget_picker_title" msgid="1953369090475731663">"Sluitskermlegstukke"</string>
+    <string name="communal_widget_picker_description" msgid="490515450110487871">"Enigiemand kan legstukke op jou sluitskerm sien, selfs al is jou tablet gesluit."</string>
+    <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"ontkies legstuk"</string>
+    <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Sluitskermlegstukke"</string>
+    <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Om ’n app met ’n legstuk oop te maak, sal jy moet verifieer dat dit jy is. Hou ook in gedagte dat enigeen dit kan bekyk, selfs wanneer jou tablet gesluit is. Sommige legstukke is moontlik nie vir jou sluitskerm bedoel nie en dit kan onveilig wees om dit hier by te voeg."</string>
+    <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Het dit"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Wissel gebruiker"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"aftrekkieslys"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Alle programme en data in hierdie sessie sal uitgevee word."</string>
@@ -574,8 +565,8 @@
     <string name="media_projection_action_text" msgid="3634906766918186440">"Begin nou"</string>
     <string name="empty_shade_text" msgid="8935967157319717412">"Geen kennisgewings nie"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"Geen nuwe kennisgewings nie"</string>
-    <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"Aanpasbare kennisgewings is aan"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"Jou toestel verlaag nou die volume en verminder opspringers op die skerm vir tot twee minute wanneer jy baie kennisgewings in ’n kort tydperk ontvang."</string>
+    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Kennisgewingdemping is aan"</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Jou toestelvolume en -waarskuwings word outomaties vir tot 2 minute lank verminder wanneer jy te veel kennisgewings op een slag kry."</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Skakel af"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Ontsluit om ouer kennisgewings te sien"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Hierdie toestel word deur jou ouer bestuur"</string>
@@ -742,8 +733,7 @@
     <string name="notification_alert_title" msgid="3656229781017543655">"Verstek"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Outomaties"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"Geen klank of vibrasie nie"</string>
-    <!-- no translation found for notification_conversation_summary_low (3855696451919728790) -->
-    <skip />
+    <string name="notification_conversation_summary_low" msgid="3855696451919728790">"Geen klank of vibrasie nie, maar verskyn steeds in die gesprekafdeling"</string>
     <string name="notification_channel_summary_default" msgid="777294388712200605">"Kan lui of vibreer op grond van toestelinstellings"</string>
     <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Kan lui of vibreer op grond van toestelinstellings. Gesprekke van <xliff:g id="APP_NAME">%1$s</xliff:g> af verskyn by verstek in ’n borrel."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Laat die stelsel bepaal of hierdie kennisgewing \'n klank moet maak of vibreer"</string>
@@ -800,8 +790,7 @@
     <string name="keyboard_key_page_up" msgid="173914303254199845">"Page Up"</string>
     <string name="keyboard_key_page_down" msgid="9035902490071829731">"Page Down"</string>
     <string name="keyboard_key_forward_del" msgid="5325501825762733459">"Delete"</string>
-    <!-- no translation found for keyboard_key_esc (6230365950511411322) -->
-    <skip />
+    <string name="keyboard_key_esc" msgid="6230365950511411322">"Esc"</string>
     <string name="keyboard_key_move_home" msgid="3496502501803911971">"Home"</string>
     <string name="keyboard_key_move_end" msgid="99190401463834854">"End"</string>
     <string name="keyboard_key_insert" msgid="4621692715704410493">"Insert"</string>
@@ -1243,7 +1232,6 @@
     <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>
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobiele data"</string>
-    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Gekoppel"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Tydelik gekoppel"</string>
     <string name="mobile_data_poor_connection" msgid="819617772268371434">"Swak verbinding"</string>
@@ -1384,31 +1372,40 @@
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Verdeelde skerm"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Invoer"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Appkortpaaie"</string>
-    <!-- no translation found for shortcut_helper_category_current_app_shortcuts (4017840565974573628) -->
-    <skip />
+    <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Huidige app"</string>
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Toeganklikheid"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Kortpadsleutels"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Soekkortpaaie"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Vou ikoon in"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Vou ikoon uit"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"of"</string>
-    <!-- no translation found for touchpad_tutorial_back_gesture_button (2746834288077265946) -->
-    <skip />
-    <!-- no translation found for touchpad_tutorial_home_gesture_button (7640544867625955304) -->
-    <skip />
-    <!-- no translation found for touchpad_tutorial_action_key_button (3220074511852927267) -->
-    <skip />
-    <!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
-    <skip />
+    <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Teruggebaar"</string>
+    <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Tuisgebaar"</string>
+    <string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Handelingsleutel"</string>
+    <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Klaar"</string>
     <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Uitstekende werk!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Gaan terug"</string>
-    <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"Swiep enige plek op die raakpaneel links of regs met drie vingers om terug te gaan."</string>
+    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
+    <skip />
+    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
+    <skip />
     <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Raakpaneel wat drie vingers wat regs en links beweeg wys"</string>
     <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Toestelskerm wat animasie vir teruggebaar wys"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Sleutelbordlig"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Vlak %1$d van %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Huiskontroles"</string>
     <string name="home_controls_dream_description" msgid="4644150952104035789">"Kry vinnig toegang tot jou huiskontroles as ’n sluimerskerm"</string>
-    <!-- no translation found for volume_undo_action (5815519725211877114) -->
-    <skip />
+    <string name="volume_undo_action" msgid="5815519725211877114">"Ontdoen"</string>
+    <string name="back_edu_toast_content" msgid="4530314597378982956">"Swiep links of regs met drie vingers op die raakpaneel om terug te gaan"</string>
+    <string name="home_edu_toast_content" msgid="3381071147871955415">"Swiep op met drie vingers op die raakpaneel om terug na die tuisskerm te gaan"</string>
+    <string name="overview_edu_toast_content" msgid="5797030644017804518">"Swiep op en hou met drie vingers op die raakpaneel om onlangse apps te bekyk"</string>
+    <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Druk die handelingsleutel op jou sleutelbord om al jou apps te bekyk"</string>
+    <string name="back_edu_notification_title" msgid="5624780717751357278">"Gebruik jou raakpaneel om terug te gaan"</string>
+    <string name="back_edu_notification_content" msgid="2497557451540954068">"Swiep links of regs met drie vingers. Tik om meer gebare te leer."</string>
+    <string name="home_edu_notification_title" msgid="6097902076909654045">"Gebruik jou raakpaneel om na die tuisskerm toe te gaan"</string>
+    <string name="home_edu_notification_content" msgid="6631697734535766588">"Swiep op met drie vingers. Tik om meer gebare te leer."</string>
+    <string name="overview_edu_notification_title" msgid="1265824157319562406">"Gebruik jou raakpaneel om onlangse apps te bekyk"</string>
+    <string name="overview_edu_notification_content" msgid="3578204677648432500">"Swiep op en hou met drie vingers. Tik om meer gebare te leer."</string>
+    <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Gebruik jou sleutelbord om alle apps te bekyk"</string>
+    <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Druk enige tyd die handelingsleutel. Tik om meer gebare te leer."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-af/tiles_states_strings.xml b/packages/SystemUI/res/values-af/tiles_states_strings.xml
index 1af9fd9..1b4781d 100644
--- a/packages/SystemUI/res/values-af/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-af/tiles_states_strings.xml
@@ -56,9 +56,6 @@
     <item msgid="5376619709702103243">"Af"</item>
     <item msgid="4875147066469902392">"Aan"</item>
   </string-array>
-    <!-- no translation found for tile_states_modes:0 (7764936419245199023) -->
-    <!-- no translation found for tile_states_modes:1 (2004750556637773692) -->
-    <!-- no translation found for tile_states_modes:2 (8968530753931637871) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Onbeskikbaar"</item>
     <item msgid="5044688398303285224">"Af"</item>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index fb4c765..26e5c1e5 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -126,38 +126,25 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"ለመመልከት መታ ያድርጉ"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"የማያ ገጽ ቀረጻን ማስቀመጥ ላይ ስህተት"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"የማያ ገፅ ቀረጻን መጀመር ላይ ስህተት"</string>
-    <!-- no translation found for screenrecord_stop_dialog_title (8716193661764511095) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message (6262768207331626817) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5995770227684523244) -->
-    <skip />
+    <string name="screenrecord_stop_dialog_title" msgid="8716193661764511095">"መቅዳት ይቁም?"</string>
+    <string name="screenrecord_stop_dialog_message" msgid="6262768207331626817">"በአሁኑ ጊዜ ሙሉ ማያ ገፅዎን በመቅዳት ላይ ነዎት"</string>
+    <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"በአሁኑ ጊዜ <xliff:g id="APP_NAME">%1$s</xliff:g> በመቅዳት ላይ ነዎት"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"መቅረጽ አቁም"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"ማያ ገፅን በማጋራት ላይ"</string>
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"ማያ ገፅን ማጋራት ይቁም?"</string>
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen_with_host_app (522823522115375414) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen (5090115386271179270) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_specific (5923772039347985172) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_generic (6681016774654578261) -->
-    <skip />
+    <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"በአሁኑ ጊዜ ሙሉ ማያ ገፅዎን ከ<xliff:g id="HOST_APP_NAME">%1$s</xliff:g> ጋር በማጋራት ላይ ነዎት"</string>
+    <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"በአሁኑ ጊዜ መሉ ማያ ገፅዎን ከመተግበሪያ ጋር በማጋራት ላይ ነዎት"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"በአሁኑ ጊዜ <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> በማጋራት ላይ ነዎት"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"በአሁኑ ጊዜ መተግበሪያ በማጋራት ላይ ነዎት"</string>
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"ማጋራት አቁም"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"ማያ ገፅን cast በማድረግ ላይ"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"cast ማድረግ ይቁም?"</string>
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen_with_device (1474703115926205251) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen (8419219169553867625) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app_with_device (2715934698604085519) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (8616103075630934513) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic_with_device (9213582497852420203) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic (4100272100480415076) -->
-    <skip />
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen_with_device" msgid="1474703115926205251">"በአሁኑ ጊዜ ሙሉ ማያ ገፅዎን ወደ <xliff:g id="DEVICE_NAME">%1$s</xliff:g> cast በማድረግ ላይ ነዎት"</string>
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen" msgid="8419219169553867625">"በአሁኑ ጊዜ ሙሉ ማያ ገፅዎን ወደ በአቅራቢያ ያለ መሣሪያ cast በማድረግ ላይ ነዎት"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app_with_device" msgid="2715934698604085519">"በአሁኑ ጊዜ <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> ወደ <xliff:g id="DEVICE_NAME">%2$s</xliff:g> cast በማድረግ ላይ ነዎት"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="8616103075630934513">"በአሁኑ ጊዜ <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> ወደ በአቅራቢያ ያለ መሣሪያ cast በማድረግ ላይ ነዎት"</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic_with_device" msgid="9213582497852420203">"በአሁኑ ጊዜ ወደ <xliff:g id="DEVICE_NAME">%1$s</xliff:g> cast በማድረግ ላይ ነዎት"</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic" msgid="4100272100480415076">"በአሁኑ ጊዜ ወደ በአቅራቢያ ወዳለ መሣሪያ cast በማድረግ ላይ ነዎት"</string>
     <string name="cast_to_other_device_stop_dialog_button" msgid="6420183747435521834">"cast ማድረግ አቁም"</string>
     <string name="close_dialog_button" msgid="4749497706540104133">"ዝጋ"</string>
     <string name="issuerecord_title" msgid="286627115110121849">"ችግር መመዝገቢያ"</string>
@@ -303,8 +290,7 @@
     <string name="start_dreams" msgid="9131802557946276718">"የማያ ገፅ ማቆያ"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"ኤተርኔት"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"አትረብሽ"</string>
-    <!-- no translation found for quick_settings_modes_label (5407025818652750501) -->
-    <skip />
+    <string name="quick_settings_modes_label" msgid="5407025818652750501">"ቅድሚያ ሁነታዎች"</string>
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"ብሉቱዝ"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"ምንም የተጣመሩ መሣሪያዎች አይገኝም"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"መሣሪያን ለማገናኘት ወይም ግንኙነቱን ለማቋረጥ መታ ያድርጉ"</string>
@@ -402,6 +388,9 @@
     <string name="performance" msgid="6552785217174378320">"አፈጻጸም"</string>
     <string name="user_interface" msgid="3712869377953950887">"የተጠቃሚ በይነገፅ"</string>
     <string name="thermal" msgid="6758074791325414831">"ተርማል"</string>
+    <string name="custom" msgid="3337456985275158299">"ብጁ"</string>
+    <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"የብጁ መከታተያ ቅንብሮች"</string>
+    <string name="restore_default" msgid="5259420807486239755">"ወደ ነባሪ መልስ"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"የአንድ እጅ ሁነታ"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"የመስሚያ መሣሪያዎች"</string>
     <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"ገቢር"</string>
@@ -440,6 +429,17 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"ቅንብሮችን ክፈት"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"ሌላ መሣሪያ"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"አጠቃላይ እይታን ቀያይር"</string>
+    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"ቅድሚያ ሁነታዎች"</string>
+    <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_off" msgid="1736604456618147306">"ጠፍቷል"</string>
+    <!-- no translation found for zen_mode_set_up (7457957033034460064) -->
+    <skip />
+    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
+    <skip />
+    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
+    <skip />
     <string name="zen_priority_introduction" msgid="3159291973383796646">"እርስዎ ከወሰንዋቸው ማንቂያዎች፣ አስታዋሾች፣ ክስተቶች እና ደዋዮች በስተቀር፣ በድምጾች እና ንዝረቶች አይረበሹም። ሙዚቃ፣ ቪዲዮዎች እና ጨዋታዎች ጨምሮ ለመጫወት የሚመርጡትን ማንኛውም ነገር አሁንም ይሰማሉ።"</string>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"ከማንቂያዎች በስተቀር፣ በድምጾች እና ንዝረቶች አይረበሹም። ሙዚቃ፣ ቪዲዮዎች እና ጨዋታዎች ጨምሮ ለመጫወት የሚመርጡትን ማንኛውም ነገር አሁንም ይሰማሉ።"</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"አብጅ"</string>
@@ -478,6 +478,8 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • በዝግታ ኃይልን በመሙላት ላይ • በ<xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> ውስጥ ይሞላል"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ኃይል በመሙላት ላይ • በ<xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> ውስጥ ይሞላል"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"ምግብሮች በማያ ገጽ ቁልፍ ላይ"</string>
+    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
+    <skip />
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"የጋራ አጋዥ ሥልጠናውን ለመጀመር ወደ ግራ ያንሸራትቱ።"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"አብጅ"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"አሰናብት"</string>
@@ -504,10 +506,9 @@
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"ምግብር ይምረጡ"</string>
     <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"ምግብር አስወግድ"</string>
     <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"በቦታ የተመረጠ ምግብር"</string>
-    <!-- no translation found for communal_widget_picker_title (1953369090475731663) -->
-    <skip />
-    <!-- no translation found for communal_widget_picker_description (490515450110487871) -->
-    <skip />
+    <string name="communal_widget_picker_title" msgid="1953369090475731663">"የማያ ገፅ ቁልፍ ምግብሮች"</string>
+    <string name="communal_widget_picker_description" msgid="490515450110487871">"የእርስዎ ጡባዊ ቁልፍ ተቆልፎ ቢሆን እንኳን ማንኛውም ሰው በማያ ገፅ ቁልፍዎ ላይ ምግብሮችን ማየት ይችላል።"</string>
+    <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"ምግብር አትምረጥ"</string>
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"የማያ ገፅ ቁልፍ ምግብሮች"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"ምግብር በመጠቀም መተግበሪያ ለመክፈት እርስዎ መሆንዎን ማረጋገጥ አለብዎት። እንዲሁም የእርስዎ ጡባዊ በተቆለፈበት ጊዜ እንኳን ማንኛውም ሰው እነሱን ማየት እንደሚችል ከግምት ውስጥ ያስገቡ። አንዳንድ ምግብሮች ለማያ ገፅ ቁልፍዎ የታሰቡ ላይሆኑ ይችላሉ እና እዚህ ለማከል አስተማማኝ ላይሆኑ ይችላሉ።"</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"ገባኝ"</string>
@@ -564,8 +565,8 @@
     <string name="media_projection_action_text" msgid="3634906766918186440">"አሁን ጀምር"</string>
     <string name="empty_shade_text" msgid="8935967157319717412">"ምንም ማሳወቂያ የለም"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"ምንም አዲስ ማሳወቂያዎች የሉም"</string>
-    <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"ተለማማጅ ማሳወቂያዎች በርተዋል"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"በአጭር የጊዜ ቆይታ ውስጥ ብዙ ማሳወቂያዎች ሲደርሱዎት መሣሪያዎ አሁን ድምፁን ይቀንሳል እና በማያ ገፁ ላይ ብቅ ባዮችን እስከ ሁለት ደቂቃዎች ይቀንሳል።"</string>
+    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"የማሳወቂያ ረጋ ማለት በርቷል"</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"በአንድ ጊዜ ብዙ ማሳወቂያዎችን ሲያገኙ የመሣሪያዎ ድምፅ እና ማንቂያዎች እስከ 2 ደቂቃዎች ድረስ በራስ-ሰር ይቀንሳሉ።"</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"አጥፋ"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"የቆዩ ማሳወቂያዎችን ለማየት ይክፈቱ"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"ይህ መሣሪያ በእርስዎ ወላጅ የሚተዳደር ነው።"</string>
@@ -732,8 +733,7 @@
     <string name="notification_alert_title" msgid="3656229781017543655">"ነባሪ"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"ራስ-ሰር"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"ምንም ድምፅ ወይም ንዝረት የለም"</string>
-    <!-- no translation found for notification_conversation_summary_low (3855696451919728790) -->
-    <skip />
+    <string name="notification_conversation_summary_low" msgid="3855696451919728790">"ምንም ድምፅ ወይም ንዝረት የለም ነገር ግን አሁንም የውይይት ክፍል ውስጥ ይታያል"</string>
     <string name="notification_channel_summary_default" msgid="777294388712200605">"በመሣሪያ ቅንብሮች መሰረት ሊጮህ ወይም ሊነዝር ይችላል"</string>
     <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"በስልክ ቅንብሮች መሰረት ሊጮህ ወይም ሊነዝር ይችላል። የ<xliff:g id="APP_NAME">%1$s</xliff:g> ውይይቶች በነባሪነት አረፋ ይሆናሉ።"</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"ይህ ማሳወቂያ ድምፅ ወይም ንዝረት መደረግ ካለበት ስርዓቱ እንዲወሰን ያድርጉት"</string>
@@ -790,8 +790,7 @@
     <string name="keyboard_key_page_up" msgid="173914303254199845">"ገፅ ወደ ላይ"</string>
     <string name="keyboard_key_page_down" msgid="9035902490071829731">"ገፅ ወደ ታች"</string>
     <string name="keyboard_key_forward_del" msgid="5325501825762733459">"ሰርዝ"</string>
-    <!-- no translation found for keyboard_key_esc (6230365950511411322) -->
-    <skip />
+    <string name="keyboard_key_esc" msgid="6230365950511411322">"Esc"</string>
     <string name="keyboard_key_move_home" msgid="3496502501803911971">"መነሻ"</string>
     <string name="keyboard_key_move_end" msgid="99190401463834854">"መጨረሻ"</string>
     <string name="keyboard_key_insert" msgid="4621692715704410493">"አስገባ"</string>
@@ -1233,7 +1232,6 @@
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"ማረጋገጥ ያስፈልጋል። ለማረጋገጥ የጣት አሻራ ዳሳሹን ይንኩ።"</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"በመካሄድ ላይ የስልክ ጥሪ"</string>
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"የተንቀሳቃሽ ስልክ ውሂብ"</string>
-    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"ተገናኝቷል"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"በጊዜያዊነት ተገናኝቷል"</string>
     <string name="mobile_data_poor_connection" msgid="819617772268371434">"ደካማ ግንኙነት"</string>
@@ -1374,8 +1372,7 @@
     <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>
-    <!-- no translation found for shortcut_helper_category_current_app_shortcuts (4017840565974573628) -->
-    <skip />
+    <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_search_placeholder" msgid="5488547526269871819">"የፍለጋ አቋራጮች"</string>
@@ -1388,13 +1385,27 @@
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"ተከናውኗል"</string>
     <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"ጥሩ ሠርተዋል!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"ወደኋላ ተመለስ"</string>
-    <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"ወደኋላ ለመመለስ በመዳሰሻ ሰሌዳው ላይ በየትኛውም ቦታ ሦስት ጣቶችን በመጠቀም ወደግራ ወይም ወደቀኝ ያንሸራትቱ።"</string>
+    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
+    <skip />
+    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
+    <skip />
     <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"ሦስት ጣቶች ወደቀኝ እና ግራ ሲንቀሳቀሱ የሚያሳይ የመዳሰሻ ሰሌዳ"</string>
     <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"የኋላ ምልክት እነማ የሚያሳይ የመሣሪያ ማያ ገፅ"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"የቁልፍ ሰሌዳ የጀርባ ብርሃን"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"ደረጃ %1$d ከ %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"የቤት ውስጥ ቁጥጥሮች"</string>
     <string name="home_controls_dream_description" msgid="4644150952104035789">"የቤት መቆጣጠሪያዎችዎን እንደ የገጸ ማያ አሳራፊ በፍጥነት ይድረሱባቸው"</string>
-    <!-- no translation found for volume_undo_action (5815519725211877114) -->
-    <skip />
+    <string name="volume_undo_action" msgid="5815519725211877114">"ቀልብስ"</string>
+    <string name="back_edu_toast_content" msgid="4530314597378982956">"ለመመለስ የመዳሰሻ ሰሌዳው ላይ በሦስት ጣቶች ወደግራ ወይም ወደቀኝ ያንሸራትቱ"</string>
+    <string name="home_edu_toast_content" msgid="3381071147871955415">"ወደ መነሻ ለመሄድ የመዳሰሻ ሰሌዳው ላይ በሦስት ጣቶች ወደላይ ያንሸራትቱ"</string>
+    <string name="overview_edu_toast_content" msgid="5797030644017804518">"የቅርብ ጊዜ መተግበሪያዎችን ለማየት የመዳሰሻ ሰሌዳው ላይ በሦስት ጣቶች ወደላይ ያንሸራትቱ እና ይያዙ"</string>
+    <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"ሁሉንም መተግበሪያዎችዎን ለማየት በቁልፍ ሰሌዳዎ ላይ ያለውን የተግባር ቁልፍ ይጫኑ"</string>
+    <string name="back_edu_notification_title" msgid="5624780717751357278">"ለመመለስ የመዳሰሻ ሰሌዳዎን ይጠቀሙ"</string>
+    <string name="back_edu_notification_content" msgid="2497557451540954068">"ሦስት ጣቶችን በመጠቀም ወደ ግራ ወይም ወደ ቀኝ ያንሸራትቱ። ምልክቶችን የበለጠ ለማወቅ መታ ያድርጉ።"</string>
+    <string name="home_edu_notification_title" msgid="6097902076909654045">"ወደ መነሻ ለመመለስ የመዳሰሻ ሰሌዳዎን ይጠቀሙ"</string>
+    <string name="home_edu_notification_content" msgid="6631697734535766588">"ሦስት ጣቶችን በመጠቀም ወደላይ ያንሸራትቱ። ምልክቶችን የበለጠ ለማወቅ መታ ያድርጉ።"</string>
+    <string name="overview_edu_notification_title" msgid="1265824157319562406">"የቅርብ ጊዜ መተግበሪያዎችን ለማየት የመዳሰሻ ሰሌዳዎን ይጠቀሙ"</string>
+    <string name="overview_edu_notification_content" msgid="3578204677648432500">"ሦስት ጣቶችን በመጠቀም ወደላይ ያንሸራትቱ እና ይያዙ። ምልክቶችን የበለጠ ለማወቅ መታ ያድርጉ።"</string>
+    <string name="all_apps_edu_notification_title" msgid="372262997265569063">"ሁሉንም መተግበሪያዎች ለማየት የቁልፍ ሰሌዳዎን ይጠቀሙ"</string>
+    <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"በማንኛውም ጊዜ የተግባር ቁልፍን ይጫኑ። ምልክቶችን የበለጠ ለማወቅ መታ ያድርጉ።"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-am/tiles_states_strings.xml b/packages/SystemUI/res/values-am/tiles_states_strings.xml
index 8bad7ab..a3c590c 100644
--- a/packages/SystemUI/res/values-am/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-am/tiles_states_strings.xml
@@ -56,9 +56,6 @@
     <item msgid="5376619709702103243">"ጠፍቷል"</item>
     <item msgid="4875147066469902392">"በርቷል"</item>
   </string-array>
-    <!-- no translation found for tile_states_modes:0 (7764936419245199023) -->
-    <!-- no translation found for tile_states_modes:1 (2004750556637773692) -->
-    <!-- no translation found for tile_states_modes:2 (8968530753931637871) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"አይገኝም"</item>
     <item msgid="5044688398303285224">"ጠፍቷል"</item>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index b96793a3..3b65cd8 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -126,38 +126,25 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"انقر لعرض التسجيل"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"حدث خطأ أثناء حفظ تسجيل محتوى الشاشة."</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"حدث خطأ في بدء تسجيل الشاشة"</string>
-    <!-- no translation found for screenrecord_stop_dialog_title (8716193661764511095) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message (6262768207331626817) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5995770227684523244) -->
-    <skip />
+    <string name="screenrecord_stop_dialog_title" msgid="8716193661764511095">"هل تريد إيقاف التسجيل؟"</string>
+    <string name="screenrecord_stop_dialog_message" msgid="6262768207331626817">"يتم حاليًا تسجيل محتوى الشاشة بأكمله"</string>
+    <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"يتم حاليًا تسجيل محتوى \"<xliff:g id="APP_NAME">%1$s</xliff:g>\""</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"إيقاف التسجيل"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"جارِ مشاركة محتوى الشاشة"</string>
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"هل تريد إيقاف مشاركة الشاشة؟"</string>
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen_with_host_app (522823522115375414) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen (5090115386271179270) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_specific (5923772039347985172) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_generic (6681016774654578261) -->
-    <skip />
+    <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"تتم حاليًا مشاركة محتوى الشاشة بأكمله مع \"<xliff:g id="HOST_APP_NAME">%1$s</xliff:g>\""</string>
+    <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"تتم حاليًا مشاركة محتوى الشاشة بأكمله مع تطبيق"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"تتم حاليًا مشاركة محتوى \"<xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>\""</string>
+    <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"تتم حاليًا مشاركة محتوى تطبيق"</string>
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"إيقاف المشاركة"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"جارٍ بث محتوى الشاشة"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"هل تريد إيقاف البث؟"</string>
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen_with_device (1474703115926205251) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen (8419219169553867625) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app_with_device (2715934698604085519) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (8616103075630934513) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic_with_device (9213582497852420203) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic (4100272100480415076) -->
-    <skip />
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen_with_device" msgid="1474703115926205251">"يتم حاليًا بثّ محتوى الشاشة بأكمله على \"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>\""</string>
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen" msgid="8419219169553867625">"يتم حاليًا بثّ محتوى الشاشة بأكمله على جهاز مجاور"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app_with_device" msgid="2715934698604085519">"يتم حاليًا بثّ محتوى \"<xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>\" على \"<xliff:g id="DEVICE_NAME">%2$s</xliff:g>\""</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="8616103075630934513">"يتم حاليًا بثّ محتوى \"<xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>\" على جهاز مجاور"</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic_with_device" msgid="9213582497852420203">"يتم حاليًا بثّ المحتوى على \"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>\""</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic" msgid="4100272100480415076">"يتم حاليًا بثّ المحتوى على جهاز مجاور"</string>
     <string name="cast_to_other_device_stop_dialog_button" msgid="6420183747435521834">"إيقاف البث"</string>
     <string name="close_dialog_button" msgid="4749497706540104133">"إغلاق"</string>
     <string name="issuerecord_title" msgid="286627115110121849">"مسجّلة المشاكل"</string>
@@ -223,9 +210,9 @@
     <string name="biometric_dialog_last_pattern_attempt_before_wipe_user" msgid="8400180746043407270">"عند إدخال نقش غير صحيح في المحاولة التالية، سيتم حذف هذا المستخدم."</string>
     <string name="biometric_dialog_last_pin_attempt_before_wipe_user" msgid="4159878829962411168">"عند إدخال رقم تعريف شخصي غير صحيح في المحاولة التالية، سيتم حذف هذا المستخدم."</string>
     <string name="biometric_dialog_last_password_attempt_before_wipe_user" msgid="4695682515465063885">"عند إدخال كلمة مرور غير صحيحة في المحاولة التالية، سيتم حذف هذا المستخدم."</string>
-    <string name="biometric_dialog_last_pattern_attempt_before_wipe_profile" msgid="6045224069529284686">"عند إدخال نقش غير صحيح في المحاولة التالية، سيتم حذف ملفك الشخصي للعمل وبياناته."</string>
-    <string name="biometric_dialog_last_pin_attempt_before_wipe_profile" msgid="545567685899091757">"عند إدخال رقم تعريف شخصي غير صحيح في المحاولة التالية، سيتم حذف ملفك الشخصي للعمل وبياناته."</string>
-    <string name="biometric_dialog_last_password_attempt_before_wipe_profile" msgid="8538032972389729253">"عند إدخال كلمة مرور غير صحيحة في المحاولة التالية، سيتم حذف ملفك الشخصي للعمل وبياناته."</string>
+    <string name="biometric_dialog_last_pattern_attempt_before_wipe_profile" msgid="6045224069529284686">"عند إدخال نقش غير صحيح في المحاولة التالية، سيتم حذف ملف العمل الخاص بك وبياناته."</string>
+    <string name="biometric_dialog_last_pin_attempt_before_wipe_profile" msgid="545567685899091757">"عند إدخال رقم تعريف شخصي غير صحيح في المحاولة التالية، سيتم حذف ملف العمل الخاص بك وبياناته."</string>
+    <string name="biometric_dialog_last_password_attempt_before_wipe_profile" msgid="8538032972389729253">"عند إدخال كلمة مرور غير صحيحة في المحاولة التالية، سيتم حذف ملف العمل الخاص بك وبياناته."</string>
     <string name="biometric_re_enroll_dialog_confirm" msgid="3049858021857801836">"إعداد"</string>
     <string name="biometric_re_enroll_dialog_cancel" msgid="93760939407091417">"لاحقًا"</string>
     <string name="biometric_re_enroll_notification_content" msgid="8685925877186288180">"هذا الإجراء مطلوب لتحسين مستوى الأمان والأداء."</string>
@@ -235,11 +222,11 @@
     <string name="fingerprint_re_enroll_dialog_content" msgid="4866561176695984879">"لإعادة إعداد ميزة \"فتح الجهاز ببصمة الإصبع\"، سيتم حذف صور بصمة الإصبع ونماذجها.\n\nبعد حذفها، عليك إعادة إعداد ميزة \"فتح الجهاز ببصمة الإصبع\" لتتمكن من استخدام بصمة الإصبع في فتح قفل الهاتف أو إثبات هويتك."</string>
     <string name="fingerprint_re_enroll_dialog_content_singular" msgid="3083663339787381218">"لإعادة إعداد ميزة \"فتح الجهاز ببصمة الإصبع\"، سيتم حذف صور بصمة الإصبع ونموذجها.\n\nبعد حذفها، عليك إعادة إعداد ميزة \"فتح الجهاز ببصمة الإصبع\" لتتمكن من استخدام بصمة الإصبع في فتح قفل الهاتف أو إثبات هويتك."</string>
     <string name="fingerprint_reenroll_failure_dialog_content" msgid="4733768492747300666">"تعذّر إعداد ميزة \"فتح الجهاز ببصمة الإصبع\". انتقِل إلى \"الإعدادات\" لإعادة المحاولة."</string>
-    <string name="face_re_enroll_notification_title" msgid="1850838867718410520">"إعادة إعداد ميزة \"فتح الجهاز بالتعرّف على الوجه\""</string>
-    <string name="face_re_enroll_notification_name" msgid="7384545252206120659">"فتح الجهاز بالتعرّف على الوجه"</string>
-    <string name="face_re_enroll_dialog_title" msgid="6392173708176069994">"إعداد ميزة \"فتح الجهاز بالتعرّف على الوجه\""</string>
-    <string name="face_re_enroll_dialog_content" msgid="7353502359464038511">"لإعادة إعداد ميزة \"فتح الجهاز بالتعرّف على الوجه\"، سيتم حذف نموذج الوجه الحالي.\n\nعليك إعادة إعداد الميزة لتتمكن من فتح قفل الهاتف باستخدام وجهك."</string>
-    <string name="face_reenroll_failure_dialog_content" msgid="7073947334397236935">"تعذّر إعداد ميزة \"فتح الجهاز بالتعرّف على الوجه\". انتقِل إلى \"الإعدادات\" لإعادة المحاولة."</string>
+    <string name="face_re_enroll_notification_title" msgid="1850838867718410520">"إعادة إعداد ميزة \"فتح الجهاز ببصمة الوجه\""</string>
+    <string name="face_re_enroll_notification_name" msgid="7384545252206120659">"فتح الجهاز ببصمة الوجه"</string>
+    <string name="face_re_enroll_dialog_title" msgid="6392173708176069994">"إعداد ميزة \"فتح الجهاز ببصمة الوجه\""</string>
+    <string name="face_re_enroll_dialog_content" msgid="7353502359464038511">"لإعادة إعداد ميزة \"فتح الجهاز ببصمة الوجه\"، سيتم حذف نموذج الوجه الحالي.\n\nعليك إعادة إعداد الميزة لتتمكن من فتح قفل الهاتف باستخدام وجهك."</string>
+    <string name="face_reenroll_failure_dialog_content" msgid="7073947334397236935">"تعذّر إعداد ميزة \"فتح الجهاز ببصمة الوجه\". انتقِل إلى \"الإعدادات\" لإعادة المحاولة."</string>
     <string name="fingerprint_dialog_touch_sensor" msgid="2817887108047658975">"المس أداة استشعار بصمة الإصبع"</string>
     <string name="fingerprint_dialog_authenticated_confirmation" msgid="1603899612957562862">"للمتابعة، اضغط على رمز فتح القفل."</string>
     <string name="fingerprint_dialog_use_fingerprint_instead" msgid="5542430577183894219">"يتعذّر التعرّف على الوجه. يمكنك استخدام بصمة إصبعك."</string>
@@ -247,7 +234,7 @@
     <skip />
     <string name="keyguard_face_failed" msgid="2346762871330729634">"يتعذّر التعرّف على الوجه."</string>
     <string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"يمكنك استخدام بصمة إصبعك."</string>
-    <string name="keyguard_face_unlock_unavailable" msgid="1581949044193418736">"ميزة \"فتح الجهاز بالتعرف على الوجه\" غير متاحة."</string>
+    <string name="keyguard_face_unlock_unavailable" msgid="1581949044193418736">"ميزة \"فتح الجهاز ببصمة الوجه\" غير متاحة."</string>
     <string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"تم توصيل البلوتوث."</string>
     <string name="accessibility_bluetooth_device_icon" msgid="9163840051642587982">"رمز الجهاز الذي يتضمّن بلوتوث"</string>
     <string name="accessibility_bluetooth_device_settings_gear" msgid="3314916468105272540">"انقر هنا لضبط إعدادات الجهاز."</string>
@@ -303,8 +290,7 @@
     <string name="start_dreams" msgid="9131802557946276718">"شاشة الاستراحة"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"عدم الإزعاج"</string>
-    <!-- no translation found for quick_settings_modes_label (5407025818652750501) -->
-    <skip />
+    <string name="quick_settings_modes_label" msgid="5407025818652750501">"الأوضاع ذات الأولوية"</string>
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"بلوتوث"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"لا يتوفر أي أجهزة مقترنة"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"انقر لربط جهاز أو إلغاء ربطه"</string>
@@ -402,6 +388,9 @@
     <string name="performance" msgid="6552785217174378320">"الأداء"</string>
     <string name="user_interface" msgid="3712869377953950887">"واجهة المستخدم"</string>
     <string name="thermal" msgid="6758074791325414831">"الأداء الحراري"</string>
+    <string name="custom" msgid="3337456985275158299">"مخصّص"</string>
+    <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"إعدادات التتبع المخصصة"</string>
+    <string name="restore_default" msgid="5259420807486239755">"استعادة الإعدادات التلقائية"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"وضع \"التصفح بيد واحدة\""</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"سماعات الأذن الطبية"</string>
     <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"متّصلة حاليًا"</string>
@@ -440,6 +429,17 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"فتح الإعدادات"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"جهاز آخر"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"تبديل \"النظرة العامة\""</string>
+    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"الأوضاع ذات الأولوية"</string>
+    <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_off" msgid="1736604456618147306">"غير مفعَّل"</string>
+    <!-- no translation found for zen_mode_set_up (7457957033034460064) -->
+    <skip />
+    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
+    <skip />
+    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
+    <skip />
     <string name="zen_priority_introduction" msgid="3159291973383796646">"لن يتم إزعاجك بالأصوات والاهتزاز، باستثناء المُنبِّهات والتذكيرات والأحداث والمتصلين الذين تحددهم. وسيظل بإمكانك سماع أي عناصر أخرى تختار تشغيلها، بما في ذلك الموسيقى والفيديوهات والألعاب."</string>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"لن يتم إزعاجك بالأصوات والاهتزاز، باستثناء المُنبِّهات. وسيظل بإمكانك سماع أي عناصر أخرى تختار تشغيلها، بما في ذلك الموسيقى والفيديوهات والألعاب."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"تخصيص"</string>
@@ -450,14 +450,14 @@
     <string name="keyguard_unlock" msgid="8031975796351361601">"التمرير إلى الأعلى لفتح القفل"</string>
     <string name="keyguard_unlock_press" msgid="9140109453735019209">"اضغط على رمز فتح القفل لفتح قفل الشاشة."</string>
     <string name="keyguard_face_successful_unlock_swipe" msgid="6180997591385846073">"تم فتح قفل جهازك عند تقريبه من وجهك. مرِّر سريعًا للأعلى لفتح الجهاز."</string>
-    <string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"تم فتح القفل بالتعرّف على وجهك. لفتح الجهاز، اضغط على رمز فتح القفل."</string>
+    <string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"تم فتح القفل ببصمة وجهك. لفتح الجهاز، اضغط على رمز فتح القفل."</string>
     <string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"تم فتح قفل جهازك عند تقريبه من وجهك. اضغط لفتح الجهاز."</string>
     <string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"تم التعرّف على الوجه. اضغط لفتح الجهاز."</string>
     <string name="keyguard_face_successful_unlock_press_alt_3" msgid="7219030481255573962">"تم التعرّف على الوجه. اضغط على رمز فتح القفل لفتح الجهاز."</string>
     <string name="keyguard_face_successful_unlock" msgid="4203999851465708287">"تم فتح قفل جهازك عند تقريبه من وجهك."</string>
     <string name="keyguard_face_successful_unlock_alt1" msgid="5853906076353839628">"تم التعرّف على الوجه."</string>
     <string name="keyguard_retry" msgid="886802522584053523">"مرِّر سريعًا للأعلى لإعادة المحاولة."</string>
-    <string name="accesssibility_keyguard_retry" msgid="8880238862712870676">"مرِّر سريعًا للأعلى لاستخدام \"فتح الجهاز بالتعرف على الوجه\""</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_with_name" msgid="2091641464065004091">"هذا الجهاز يخص <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>."</string>
@@ -478,6 +478,8 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • جارٍ الشحن ببطء • ستمتلئ البطارية خلال <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • جارٍ الشحن • ستمتلئ البطارية خلال <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"التطبيقات المصغّرة على شاشة القفل"</string>
+    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
+    <skip />
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"مرِّر سريعًا لليمين لبدء الدليل التوجيهي العام."</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"تخصيص"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"إغلاق"</string>
@@ -504,10 +506,9 @@
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"اختيار التطبيق المصغّر"</string>
     <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"إزالة التطبيق المصغّر"</string>
     <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"إضافة التطبيق المصغّر المحدَّد"</string>
-    <!-- no translation found for communal_widget_picker_title (1953369090475731663) -->
-    <skip />
-    <!-- no translation found for communal_widget_picker_description (490515450110487871) -->
-    <skip />
+    <string name="communal_widget_picker_title" msgid="1953369090475731663">"التطبيقات المصغّرة المصمَّمة لشاشة القفل"</string>
+    <string name="communal_widget_picker_description" msgid="490515450110487871">"يمكن للجميع رؤية التطبيقات المصغّرة على شاشة القفل، حتى في حال قفل الجهاز اللوحي."</string>
+    <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"إلغاء اختيار التطبيق المصغّر"</string>
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"التطبيقات المصغّرة المصمَّمة لشاشة القفل"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"لفتح تطبيق باستخدام تطبيق مصغَّر، عليك إثبات هويتك. يُرجى ملاحظة أنّ أي شخص يمكنه الاطّلاع محتوى التطبيقات المصغَّرة، حتى وإن كان جهازك اللوحي مُقفلاً. بعض التطبيقات المصغّرة قد لا تكون مُصمَّمة لإضافتها إلى شاشة القفل، وقد يكون هذا الإجراء غير آمن."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"حسنًا"</string>
@@ -564,8 +565,8 @@
     <string name="media_projection_action_text" msgid="3634906766918186440">"البدء الآن"</string>
     <string name="empty_shade_text" msgid="8935967157319717412">"ما مِن إشعارات"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"ما مِن إشعارات جديدة"</string>
-    <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"\"الإشعارات التكيّفية\" مفعَّلة"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"عند تلقّي إشعارات متعددة في فترة زمنية قصيرة، يخفِّض جهازك الصوت ويقلّل من ظهور النوافذ المنبثقة على الشاشة لمدة تصل إلى دقيقتين."</string>
+    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"ميزة \"تخفيض الإشعارات الصوتية والاهتزاز\" مُفعَّلة"</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"يتم تلقائيًا خفض مستوى صوت جهازك والتنبيهات لمدة تصل إلى دقيقتين عند تلقّي إشعارات كثيرة في آنٍ واحد."</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"إيقاف"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"افتَح قفل الشاشة لعرض الإشعارات الأقدم."</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"يتولّى أحد الوالدين إدارة هذا الجهاز."</string>
@@ -579,7 +580,7 @@
     <string name="quick_settings_disclosure_management_vpns" msgid="929181757984262902">"‏ينتمي هذا الجهاز إلى مؤسستك، ويتّصل بالإنترنت من خلال خدمات الشبكة الافتراضية الخاصة (VPN)."</string>
     <string name="quick_settings_disclosure_named_management_vpns" msgid="3312645578322079185">"‏ينتمي هذا الجهاز إلى <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>، ويتّصل بالإنترنت من خلال خدمات الشبكة الافتراضية الخاصة (VPN)."</string>
     <string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"يمكن لمؤسستك مراقبة حركة بيانات الشبكة في ملف العمل"</string>
-    <string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8321469176706219860">"يمكن لـ <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> مراقبة حركة بيانات الشبكة في ملفك الشخصي للعمل"</string>
+    <string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8321469176706219860">"يمكن لـ <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> مراقبة حركة بيانات الشبكة في ملف العمل الخاص بك"</string>
     <string name="quick_settings_disclosure_managed_profile_network_activity" msgid="2636594621387832827">"تكون أنشطة شبكة ملف العمل مرئية لمشرف تكنولوجيا المعلومات."</string>
     <string name="quick_settings_disclosure_monitoring" msgid="8548019955631378680">"قد تكون الشبكة خاضعة للمراقبة"</string>
     <string name="quick_settings_disclosure_vpns" msgid="3586175303518266301">"‏هذا الجهاز متّصل بالإنترنت من خلال خدمات الشبكات الافتراضية الخاصة (VPN)."</string>
@@ -597,10 +598,10 @@
     <string name="monitoring_financed_description_named_management" msgid="6108439201399938668">"قد تتمكّن مؤسسة <xliff:g id="ORGANIZATION_NAME_0">%1$s</xliff:g> من الوصول إلى البيانات المرتبطة بهذا الجهاز وإدارة التطبيقات وتغيير إعدادات الجهاز.\n\nإذا كان لديك أسئلة، يُرجى التواصل مع <xliff:g id="ORGANIZATION_NAME_1">%2$s</xliff:g>."</string>
     <string name="monitoring_description_management" msgid="4308879039175729014">"هذا الجهاز يخص مؤسستك.\n\nيمكن لمشرف تكنولوجيا المعلومات في مؤسستك تتبّع وإدارة الإعدادات والتطبيقات والبيانات المرتبطة بجهازك ومعلومات الموقع الجغرافي للجهاز وعمليات الدخول إلى نظام المؤسسة.\n\nللحصول على المزيد من المعلومات، يمكنك التواصل مع مشرف تكنولوجيا المعلومات."</string>
     <string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"ثبّتت مؤسستك مرجعًا مصدّقًا على هذا الجهاز. قد تتم مراقبة حركة بيانات شبكتك الآمنة أو تعديلها."</string>
-    <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"ثبّتت مؤسستك مرجعًا مصدّقًا في ملفك الشخصي للعمل. قد تتم مراقبة حركة بيانات شبكتك الآمنة أو تعديلها."</string>
+    <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"ثبّتت مؤسستك مرجعًا مصدّقًا في ملف العمل الخاص بك. قد تتم مراقبة حركة بيانات شبكتك الآمنة أو تعديلها."</string>
     <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"تم تثبيت مرجع مصدّق على هذا الجهاز. قد تتم مراقبة حركة بيانات شبكتك الآمنة أو تعديلها."</string>
     <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"شغَّل المشرف ميزة تسجيل بيانات الشبكة، والتي يتم من خلالها مراقبة حركة البيانات على جهازك."</string>
-    <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"شغَّل المشرف ميزة تسجيل بيانات الشبكة، والتي يتم من خلالها مراقبة حركة البيانات في ملفك الشخصي للعمل ولكن لا تتم مراقبتها في ملفك الشخصي."</string>
+    <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"شغَّل المشرف ميزة تسجيل بيانات الشبكة، والتي يتم من خلالها مراقبة حركة البيانات في ملف العمل الخاص بك ولكن لا تتم مراقبتها في ملفك الشخصي."</string>
     <string name="monitoring_description_named_vpn" msgid="8220190039787149671">"‏هذا الجهاز متّصل بالإنترنت من خلال \"<xliff:g id="VPN_APP">%1$s</xliff:g>\". ويمكن لمقدِّم شبكة VPN الاطّلاع على أنشطة الشبكة، بما في ذلك الرسائل الإلكترونية وبيانات التصفّح."</string>
     <string name="monitoring_description_managed_device_named_vpn" msgid="7693648349547785255">"هذا الجهاز متّصل بالإنترنت من خلال \"<xliff:g id="VPN_APP">%1$s</xliff:g>\". تجدر الإشارة إلى أنّ أنشطة الشبكة، بما في ذلك الرسائل الإلكترونية وبيانات التصفُّح، مرئية لمشرف تكنولوجيا المعلومات في مؤسستك."</string>
     <string name="monitoring_description_two_named_vpns" msgid="6726394451199620634">"هذا الجهاز متّصل بالإنترنت من خلال <xliff:g id="VPN_APP_0">%1$s</xliff:g> و<xliff:g id="VPN_APP_1">%2$s</xliff:g>. يمكن لمشرف تكنولوجيا المعلومات رؤية أنشطة الشبكة، بما في ذلك الرسائل الإلكترونية وبيانات التصفُّح."</string>
@@ -692,7 +693,7 @@
     <string name="show_demo_mode" msgid="3677956462273059726">"عرض الوضع التجريبي"</string>
     <string name="status_bar_ethernet" msgid="5690979758988647484">"إيثرنت"</string>
     <string name="status_bar_alarm" msgid="87160847643623352">"المنبّه"</string>
-    <string name="wallet_title" msgid="5369767670735827105">"محفظة"</string>
+    <string name="wallet_title" msgid="5369767670735827105">"‏محفظة Google"</string>
     <string name="wallet_empty_state_label" msgid="7776761245237530394">"يمكنك إعداد طريقة دفع لإجراء عمليات شراء بسرعة وأمان أكبر باستخدام هاتفك."</string>
     <string name="wallet_app_button_label" msgid="7123784239111190992">"عرض الكل"</string>
     <string name="wallet_secondary_label_no_card" msgid="8488069304491125713">"انقر لفتح قفل الجهاز"</string>
@@ -732,8 +733,7 @@
     <string name="notification_alert_title" msgid="3656229781017543655">"تلقائية"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"تلقائي"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"بدون صوت أو اهتزاز"</string>
-    <!-- no translation found for notification_conversation_summary_low (3855696451919728790) -->
-    <skip />
+    <string name="notification_conversation_summary_low" msgid="3855696451919728790">"سيظهر الإشعار في قسم المحادثات ولكن بدون صوت أو اهتزاز"</string>
     <string name="notification_channel_summary_default" msgid="777294388712200605">"يمكن إصدار رنين أو اهتزاز بناءً على إعدادات الجهاز"</string>
     <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"يمكن إصدار رنين أو اهتزاز بناءً على إعدادات الجهاز. تظهر المحادثات من \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" كفقاعات تلقائيًا."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"السماح للنظام بتحديد ما إذا يجب اهتزاز الجهاز أو إصدار رنين عند تلقّي هذا الإشعار"</string>
@@ -790,8 +790,7 @@
     <string name="keyboard_key_page_up" msgid="173914303254199845">"Page Up"</string>
     <string name="keyboard_key_page_down" msgid="9035902490071829731">"Page Down"</string>
     <string name="keyboard_key_forward_del" msgid="5325501825762733459">"Delete"</string>
-    <!-- no translation found for keyboard_key_esc (6230365950511411322) -->
-    <skip />
+    <string name="keyboard_key_esc" msgid="6230365950511411322">"Esc"</string>
     <string name="keyboard_key_move_home" msgid="3496502501803911971">"الرئيسية"</string>
     <string name="keyboard_key_move_end" msgid="99190401463834854">"End"</string>
     <string name="keyboard_key_insert" msgid="4621692715704410493">"Insert"</string>
@@ -1233,7 +1232,6 @@
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"المصادقة مطلوبة. المس مستشعر بصمات الإصبع للمصادقة."</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"مكالمة هاتفية جارية"</string>
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"بيانات الجوّال"</string>
-    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"متصلة بالإنترنت"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"متصلة مؤقتًا"</string>
     <string name="mobile_data_poor_connection" msgid="819617772268371434">"الاتصال ضعيف"</string>
@@ -1387,13 +1385,27 @@
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"تم"</string>
     <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"أحسنت."</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"رجوع"</string>
-    <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"للرجوع، مرِّر سريعًا لليسار أو لليمين باستخدام ثلاثة أصابع في أي مكان على لوحة اللمس."</string>
+    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
+    <skip />
+    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
+    <skip />
     <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"لوحة لمس تعرض ثلاثة أصابع تتحرك يمينًا ويسارًا"</string>
     <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"شاشة جهاز تعرض صورة متحركة لإيماءة الرجوع"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"الإضاءة الخلفية للوحة المفاتيح"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"‏مستوى الإضاءة: %1$d من %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"إدارة المنزل آليًّا"</string>
     <string name="home_controls_dream_description" msgid="4644150952104035789">"يمكنك إدارة المنزل آليًّا بشكل سريع من شاشة الاستراحة"</string>
-    <!-- no translation found for volume_undo_action (5815519725211877114) -->
-    <skip />
+    <string name="volume_undo_action" msgid="5815519725211877114">"تراجع"</string>
+    <string name="back_edu_toast_content" msgid="4530314597378982956">"للرجوع، مرِّر سريعًا لليمين أو لليسار على لوحة اللمس باستخدام 3 أصابع"</string>
+    <string name="home_edu_toast_content" msgid="3381071147871955415">"للانتقال إلى الشاشة الرئيسية، مرِّر سريعًا للأعلى على لوحة اللمس باستخدام 3 أصابع"</string>
+    <string name="overview_edu_toast_content" msgid="5797030644017804518">"لعرض التطبيقات المستخدَمة مؤخرًا، مرِّر سريعًا للأعلى مع استمرار الضغط على لوحة اللمس باستخدام 3 أصابع"</string>
+    <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"لعرض جميع التطبيقات، اضغط على مفتاح الإجراء في لوحة المفاتيح"</string>
+    <string name="back_edu_notification_title" msgid="5624780717751357278">"استخدِم لوحة اللمس للرجوع"</string>
+    <string name="back_edu_notification_content" msgid="2497557451540954068">"مرِّر سريعًا لليمين أو لليسار باستخدام 3 أصابع. انقر للتعرّف على المزيد من الإيماءات."</string>
+    <string name="home_edu_notification_title" msgid="6097902076909654045">"استخدِم لوحة اللمس للانتقال إلى الشاشة الرئيسية"</string>
+    <string name="home_edu_notification_content" msgid="6631697734535766588">"مرِّر سريعًا للأعلى باستخدام 3 أصابع. انقر للتعرّف على المزيد من الإيماءات."</string>
+    <string name="overview_edu_notification_title" msgid="1265824157319562406">"استخدِم لوحة اللمس لعرض التطبيقات المستخدَمة مؤخرًا"</string>
+    <string name="overview_edu_notification_content" msgid="3578204677648432500">"مرِّر سريعًا للأعلى مع استمرار الضغط باستخدام 3 أصابع. انقر للتعرّف على المزيد من الإيماءات."</string>
+    <string name="all_apps_edu_notification_title" msgid="372262997265569063">"استخدِم لوحة المفاتيح لعرض جميع التطبيقات"</string>
+    <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"اضغط على مفتاح الإجراء في أي وقت. انقر للتعرّف على المزيد من الإيماءات."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ar/tiles_states_strings.xml b/packages/SystemUI/res/values-ar/tiles_states_strings.xml
index 62a6816..a89650a 100644
--- a/packages/SystemUI/res/values-ar/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ar/tiles_states_strings.xml
@@ -56,9 +56,6 @@
     <item msgid="5376619709702103243">"الميزة غير مفعّلة"</item>
     <item msgid="4875147066469902392">"الميزة مفعّلة"</item>
   </string-array>
-    <!-- no translation found for tile_states_modes:0 (7764936419245199023) -->
-    <!-- no translation found for tile_states_modes:1 (2004750556637773692) -->
-    <!-- no translation found for tile_states_modes:2 (8968530753931637871) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"الميزة غير متاحة"</item>
     <item msgid="5044688398303285224">"الميزة غير مفعّلة"</item>
diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml
index 99c4326..576e8ad 100644
--- a/packages/SystemUI/res/values-as/strings.xml
+++ b/packages/SystemUI/res/values-as/strings.xml
@@ -388,6 +388,9 @@
     <string name="performance" msgid="6552785217174378320">"পাৰদৰ্শিতা"</string>
     <string name="user_interface" msgid="3712869377953950887">"ব্যৱহাৰকাৰীৰ ইণ্টাৰফে’চ"</string>
     <string name="thermal" msgid="6758074791325414831">"থাৰ্মেল"</string>
+    <string name="custom" msgid="3337456985275158299">"কাষ্টম"</string>
+    <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"কাষ্টম ট্ৰে’চৰ ছেটিং"</string>
+    <string name="restore_default" msgid="5259420807486239755">"ডিফ’ল্ট পুনঃস্থাপন কৰক"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"এখন হাতেৰে ব্যৱহাৰ কৰা ম’ড"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"শুনাৰ ডিভাইচ"</string>
     <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"সক্ৰিয় হৈ আছে"</string>
@@ -426,6 +429,14 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"ছেটিং খোলক"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"অন্য ডিভাইচ"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"অৱলোকন ট’গল কৰক"</string>
+    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"অগ্ৰাধিকাৰপ্ৰাপ্ত ম’ডসমূহ"</string>
+    <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_off" msgid="1736604456618147306">"অফ আছে"</string>
+    <string name="zen_mode_set_up" msgid="7457957033034460064">"ছেট আপ কৰক"</string>
+    <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"ছেটিঙত পৰিচালনা কৰক"</string>
+    <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{কোনো সক্ৰিয় ম’ড নাই}=1{{mode} সক্ৰিয় আছে}one{# টা ম’ড সক্ৰিয় আছে}other{# টা ম’ড সক্ৰিয় আছে}}"</string>
     <string name="zen_priority_introduction" msgid="3159291973383796646">"আপুনি নিৰ্দিষ্ট কৰা এলাৰ্ম, ৰিমাইণ্ডাৰ, ইভেন্ট আৰু কল কৰোঁতাৰ বাহিৰে আন কোনো শব্দৰ পৰা আপুনি অসুবিধা নাপাব। কিন্তু, সংগীত, ভিডিঅ\' আৰু খেলসমূহকে ধৰি আপুনি প্লে কৰিব খোজা যিকোনো বস্তু তথাপি শুনিব পাৰিব।"</string>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"আপুনি নিৰ্দিষ্ট কৰা এলাৰ্মৰ বাহিৰে আন কোনো ধ্বনি আৰু কম্পনৰ পৰা আপুনি অসুবিধা নাপাব। কিন্তু, সংগীত, ভিডিঅ\' আৰু খেলসমূহকে ধৰি আপুনি প্লে কৰিব খোজা যিকোনো বস্তু তথাপি শুনিব পাৰিব।"</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"কাষ্টমাইজ কৰক"</string>
@@ -464,6 +475,8 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • লাহে লাহে চাৰ্জ হৈ আছে • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>ত সম্পূৰ্ণ হ’ব"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • চাৰ্জ হৈ আছে • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>ত সম্পূৰ্ণ হ’ব"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"লক স্ক্ৰীনত ৱিজেট"</string>
+    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
+    <skip />
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"সম্প্ৰদায় সম্পৰ্কীয় নিৰ্দেশনা আৰম্ভ কৰিবলৈ বাওঁফালে ছোৱাইপ কৰক"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"কাষ্টমাইজ কৰক"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"অগ্ৰাহ্য কৰক"</string>
@@ -492,6 +505,7 @@
     <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"বাছনি কৰা ৱিজেটটো ৰাখক"</string>
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"লক স্ক্ৰীনৰ ৱিজেট"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"আপোনাৰ টেবলেটটো লক কৰি ৰাখিলেও যিকোনো লোকে আপোনাৰ লক স্ক্ৰীনত ৱিজেট চাব পাৰে।"</string>
+    <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"ৱিজেট বাছনিৰ পৰা আঁতৰাওক"</string>
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"লক স্ক্ৰীন ৱিজেট"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"এটা ৱিজেট ব্যৱহাৰ কৰি কোনো এপ্ খুলিবলৈ, এয়া আপুনিয়েই বুলি সত্যাপন পৰীক্ষা কৰিব লাগিব। লগতে, মনত ৰাখিব যে যিকোনো লোকেই সেইবোৰ চাব পাৰে, আনকি আপোনাৰ টেবলেটটো লক হৈ থাকিলেও। কিছুমান ৱিজেট হয়তো আপোনাৰ লক স্ক্ৰীনৰ বাবে কৰা হোৱা নাই আৰু ইয়াত যোগ কৰাটো অসুৰক্ষিত হ’ব পাৰে।"</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"বুজি পালোঁ"</string>
@@ -548,8 +562,8 @@
     <string name="media_projection_action_text" msgid="3634906766918186440">"এতিয়াই আৰম্ভ কৰক"</string>
     <string name="empty_shade_text" msgid="8935967157319717412">"কোনো জাননী নাই"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"কোনো নতুন জাননী নাই"</string>
-    <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"অভিযোজিত জাননী অন কৰা আছে"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"আপোনাৰ ডিভাইচে এতিয়া ভলিউম কমায়, কম সময়ৰ ভিতৰত বহুতো জাননী পালে ২ মিনিটলৈকে স্ক্ৰীনত ওলোৱা পপ-আপ কমায়।"</string>
+    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"জাননী কুলডাউন কৰাটো অন আছে"</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"আপুনি একেলগে বহুতো জাননী পালে আপোনাৰ ডিভাইচটোৰ ভলিউম আৰু সতৰ্কবাৰ্তা স্বয়ংক্ৰিয়ভাৱে ২ মিনিটলৈকে কমোৱা হয়।"</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"অফ কৰক"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"পুৰণি জাননী চবলৈ আনলক কৰক"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"এই ডিভাইচটো আপোনাৰ অভিভাৱকে পৰিচালনা কৰে"</string>
@@ -716,8 +730,7 @@
     <string name="notification_alert_title" msgid="3656229781017543655">"ডিফ’ল্ট"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"স্বয়ংক্ৰিয়"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"কোনো ধ্বনি অথবা কম্পন নাই"</string>
-    <!-- no translation found for notification_conversation_summary_low (3855696451919728790) -->
-    <skip />
+    <string name="notification_conversation_summary_low" msgid="3855696451919728790">"কোনো ধ্বনি বা কম্পন নাই, কিন্তু বাৰ্তালাপৰ শাখাত প্ৰদৰ্শিত হয়"</string>
     <string name="notification_channel_summary_default" msgid="777294388712200605">"ডিভাইচৰ ছেটিঙৰ ওপৰত নিৰ্ভৰ কৰি ৰিং কৰিব অথবা কম্পন হ’ব পাৰে"</string>
     <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"ডিভাইচৰ ছেটিঙৰ ওপৰত নিৰ্ভৰ কৰি ৰিং কৰিব অথবা কম্পন হ’ব পাৰে। <xliff:g id="APP_NAME">%1$s</xliff:g>ৰ বাৰ্তালাপ ডিফ’ল্টভাৱে বাবল হিচাপে প্ৰদৰ্শিত হয়।"</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"এই জাননীটোৱে ধ্বনি নে কম্পন সৃষ্টি কৰিব সেয়া ছিষ্টেমটোক নিৰ্ধাৰণ কৰিবলৈ দিয়ক"</string>
@@ -1216,7 +1229,6 @@
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"বিশ্বাসযোগ্যতা প্ৰমাণীকৰণৰ আৱশ্যক। বিশ্বাসযোগ্যতা প্ৰমাণীকৰণ কৰিবলৈ ফিংগাৰপ্ৰিণ্ট ছেন্সৰটো স্পৰ্শ কৰক।"</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"চলি থকা ফ’ন কল"</string>
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"ম’বাইল ডেটা"</string>
-    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"সংযোজিত হৈ আছে"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"অস্থায়ীভাৱে সংযোগ কৰা হৈছে"</string>
     <string name="mobile_data_poor_connection" msgid="819617772268371434">"বেয়া সংযোগ"</string>
@@ -1370,7 +1382,10 @@
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"হ’ল"</string>
     <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"বঢ়িয়া!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"উভতি যাওক"</string>
-    <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"উভতি যাবলৈ, টাচ্চপেডৰ যিকোনো স্থানত তিনিটা আঙুলি ব্যৱহাৰ কৰি বাওঁ বা সোঁফালে ছোৱাইপ কৰক।"</string>
+    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
+    <skip />
+    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
+    <skip />
     <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"তিনিটা আঙুলি সোঁ আৰু বাওঁফালে লৰচৰ কৰা দেখুওৱা টাচ্চপেড"</string>
     <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"পিছফালৰ নিৰ্দেশৰ এনিমেশ্বন দেখুওৱা ডিভাইচ স্ক্ৰীন"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"কীব’ৰ্ডৰ বেকলাইট"</string>
@@ -1378,4 +1393,16 @@
     <string name="home_controls_dream_label" msgid="6567105701292324257">"ঘৰৰ সা-সৰঞ্জামৰ নিয়ন্ত্ৰণ"</string>
     <string name="home_controls_dream_description" msgid="4644150952104035789">"স্ক্ৰীনছেভাৰ হিচাপে ক্ষিপ্ৰতাৰে ঘৰৰ সা-সৰঞ্জামৰ নিয়ন্ত্ৰণ এক্সেছ কৰক"</string>
     <string name="volume_undo_action" msgid="5815519725211877114">"আনডু কৰক"</string>
+    <string name="back_edu_toast_content" msgid="4530314597378982956">"উভতি যাবলৈ টাচ্চপেডখনত তিনিটা আঙুলিৰে বাওঁ বা সোঁফালে ছোৱাইপ কৰক"</string>
+    <string name="home_edu_toast_content" msgid="3381071147871955415">"গৃহপৃষ্ঠালৈ যাওক, টাচ্চপেডত তিনিটা আঙুলিৰে ওপৰলৈ ছোৱাইপ কৰক"</string>
+    <string name="overview_edu_toast_content" msgid="5797030644017804518">"শেহতীয়া এপ্‌সমূহ চাবলৈ টাচ্চপেডখনত তিনিটা আঙুলিৰে ওপৰলৈ ছোৱাইপ কৰি ধৰি ৰাখক"</string>
+    <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"আপোনাৰ আটাইবোৰ এপ্‌ চাবলৈ আপোনাৰ কীব’ৰ্ডৰ কাৰ্য কীটোত টিপক"</string>
+    <string name="back_edu_notification_title" msgid="5624780717751357278">"উভতি যাবলৈ আপোনাৰ টাচ্চপেড ব্যৱহাৰ কৰক"</string>
+    <string name="back_edu_notification_content" msgid="2497557451540954068">"তিনিটা আঙুলি ব্যৱহাৰ কৰি বাওঁফাললৈ বা সোঁফাললৈ ছোৱাইপ কৰক। অধিক নিৰ্দেশ শিকিবলৈ টিপক।"</string>
+    <string name="home_edu_notification_title" msgid="6097902076909654045">"গৃহপৃষ্ঠালৈ যাবলৈ আপোনাৰ টাচ্চপেড ব্যৱহাৰ কৰক"</string>
+    <string name="home_edu_notification_content" msgid="6631697734535766588">"তিনিটা আঙুলি ব্যৱহাৰ কৰি ওপৰলৈ ছোৱাইপ কৰক। অধিক নিৰ্দেশ শিকিবলৈ টিপক।"</string>
+    <string name="overview_edu_notification_title" msgid="1265824157319562406">"শেহতীয়া এপ্‌সমূহ চাবলৈ আপোনাৰ টাচ্চপেড ব্যৱহাৰ কৰক"</string>
+    <string name="overview_edu_notification_content" msgid="3578204677648432500">"তিনিটা আঙুলি ব্যৱহাৰ কৰি ওপৰলৈ ছোৱাইপ কৰি ধৰি ৰাখক। অধিক নিৰ্দেশ শিকিবলৈ টিপক।"</string>
+    <string name="all_apps_edu_notification_title" msgid="372262997265569063">"আটাইবোৰ এপ্‌ চাবলৈ আপোনাৰ কীব’ৰ্ড ব্যৱহাৰ কৰক"</string>
+    <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"যিকোনো সময়তে কাৰ্য কীটোত টিপক। অধিক নিৰ্দেশ শিকিবলৈ টিপক।"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-as/tiles_states_strings.xml b/packages/SystemUI/res/values-as/tiles_states_strings.xml
index fdd1f103..e978fe2 100644
--- a/packages/SystemUI/res/values-as/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-as/tiles_states_strings.xml
@@ -56,11 +56,6 @@
     <item msgid="5376619709702103243">"অফ আছে"</item>
     <item msgid="4875147066469902392">"অন কৰা আছে"</item>
   </string-array>
-  <string-array name="tile_states_modes">
-    <item msgid="7764936419245199023">"উপলব্ধ নহয়"</item>
-    <item msgid="2004750556637773692">"অফ আছে"</item>
-    <item msgid="8968530753931637871">"অন আছে"</item>
-  </string-array>
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"উপলব্ধ নহয়"</item>
     <item msgid="5044688398303285224">"অফ আছে"</item>
diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml
index e2ca41b..d6abf07 100644
--- a/packages/SystemUI/res/values-az/strings.xml
+++ b/packages/SystemUI/res/values-az/strings.xml
@@ -126,38 +126,25 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"Baxmaq üçün toxunun"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"Ekran çəkimini yadda saxlayarkən xəta oldu"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Ekranın yazılması ilə bağlı xəta"</string>
-    <!-- no translation found for screenrecord_stop_dialog_title (8716193661764511095) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message (6262768207331626817) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5995770227684523244) -->
-    <skip />
+    <string name="screenrecord_stop_dialog_title" msgid="8716193661764511095">"Çəkiliş dayandırılsın?"</string>
+    <string name="screenrecord_stop_dialog_message" msgid="6262768207331626817">"Hazırda bütün ekranı çəkirsiniz"</string>
+    <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Hazırda <xliff:g id="APP_NAME">%1$s</xliff:g> tətbiqini çəkirsiniz"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Qeydəalmanı dayandırın"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Ekran paylaşılır"</string>
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Ekran paylaşımı dayandırılsın?"</string>
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen_with_host_app (522823522115375414) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen (5090115386271179270) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_specific (5923772039347985172) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_generic (6681016774654578261) -->
-    <skip />
+    <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Hazırda bütün ekranı <xliff:g id="HOST_APP_NAME">%1$s</xliff:g> ilə paylaşırsınız"</string>
+    <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Hazırda bütün ekranı tətbiq ilə paylaşırsınız"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Hazırda <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> paylaşırsınız"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Hazırda tətbiq paylaşırsınız"</string>
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Paylaşımı dayandırın"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Ekran yayımlanır"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Yayım dayandırılsın?"</string>
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen_with_device (1474703115926205251) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen (8419219169553867625) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app_with_device (2715934698604085519) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (8616103075630934513) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic_with_device (9213582497852420203) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic (4100272100480415076) -->
-    <skip />
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen_with_device" msgid="1474703115926205251">"Hazırda bütün ekranı <xliff:g id="DEVICE_NAME">%1$s</xliff:g> cihazında yayımlayırsınız"</string>
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen" msgid="8419219169553867625">"Hazırda bütün ekranı yaxınlıqdakı cihazda yayımlayırsınız"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app_with_device" msgid="2715934698604085519">"Hazırda <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> tətbiqini <xliff:g id="DEVICE_NAME">%2$s</xliff:g> cihazında yayımlayırsınız"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="8616103075630934513">"Hazırda <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> tətbiqini yaxınlıqdakı cihazda yayımlayırsınız"</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic_with_device" msgid="9213582497852420203">"Hazırda <xliff:g id="DEVICE_NAME">%1$s</xliff:g> cihazında yayımlayırsınız"</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic" msgid="4100272100480415076">"Hazırda yaxınlıqdakı cihazda yayımlayırsınız"</string>
     <string name="cast_to_other_device_stop_dialog_button" msgid="6420183747435521834">"Yayımı dayandırın"</string>
     <string name="close_dialog_button" msgid="4749497706540104133">"Bağlayın"</string>
     <string name="issuerecord_title" msgid="286627115110121849">"Problem qeydə alan"</string>
@@ -303,8 +290,7 @@
     <string name="start_dreams" msgid="9131802557946276718">"Ekran qoruyucu"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"Narahat etməyin"</string>
-    <!-- no translation found for quick_settings_modes_label (5407025818652750501) -->
-    <skip />
+    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Prioritet rejimləri"</string>
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Heç bir cütlənmiş cihaz əlçatan deyil"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Toxunaraq cihaza qoşulun, yaxud əlaqəni ayırın"</string>
@@ -402,6 +388,9 @@
     <string name="performance" msgid="6552785217174378320">"Performans"</string>
     <string name="user_interface" msgid="3712869377953950887">"İstifadəçi interfeysi"</string>
     <string name="thermal" msgid="6758074791325414831">"Termal"</string>
+    <string name="custom" msgid="3337456985275158299">"Fərdi"</string>
+    <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Fərdi fəaliyyət izi ayarları"</string>
+    <string name="restore_default" msgid="5259420807486239755">"Defoltu bərpa edin"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Birəlli rejim"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Eşitmə cihazları"</string>
     <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktiv"</string>
@@ -440,6 +429,17 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Ayarları açın"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Digər cihaz"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"İcmala Keçin"</string>
+    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Prioritet rejimləri"</string>
+    <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Hazırdır"</string>
+    <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Ayarlar"</string>
+    <string name="zen_mode_on" msgid="9085304934016242591">"Aktiv"</string>
+    <string name="zen_mode_off" msgid="1736604456618147306">"Deaktiv"</string>
+    <!-- no translation found for zen_mode_set_up (7457957033034460064) -->
+    <skip />
+    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
+    <skip />
+    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
+    <skip />
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Seçdiyiniz siqnal, xatırladıcı, tədbir və zənglər istisna olmaqla səslər və vibrasiyalar Sizi narahat etməyəcək. Musiqi, video və oyunlar da daxil olmaqla oxutmaq istədiyiniz hər şeyi eşidəcəksiniz."</string>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Siqnallar istisna olmaqla səslər və vibrasiyalar Sizi narahat etməyəcək. Musiqi, video və oyunlar da daxil olmaqla oxutmaq istədiyiniz hər şeyi eşidəcəksiniz."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Fərdiləşdirin"</string>
@@ -478,6 +478,8 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Asta şarj edilir • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> sonra dolacaq"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Şarj edilir • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> sonra dolacaq"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Kilid ekranındakı vidcetlər"</string>
+    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
+    <skip />
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"İcma təlimatını başlatmaq üçün sola sürüşdürün"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Fərdiləşdirin"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Bağlayın"</string>
@@ -504,10 +506,9 @@
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"vidcet seçin"</string>
     <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"vidceti silin"</string>
     <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"seçilmiş vidceti yerləşdirin"</string>
-    <!-- no translation found for communal_widget_picker_title (1953369090475731663) -->
-    <skip />
-    <!-- no translation found for communal_widget_picker_description (490515450110487871) -->
-    <skip />
+    <string name="communal_widget_picker_title" msgid="1953369090475731663">"Kilid ekranı vidcetləri"</string>
+    <string name="communal_widget_picker_description" msgid="490515450110487871">"Planşet kilidli olsa belə, hər kəs kilid ekranınızdakı vidcetlərə baxa bilər."</string>
+    <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"vidcet seçimini silin"</string>
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Kilid ekranı vidcetləri"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Vidcetdən istifadə edərək tətbiqi açmaq üçün kimliyi doğrulamalısınız. Planşet kilidli olsa da, hər kəs vidcetlərə baxa bilər. Bəzi vidcetlər kilid ekranı üçün nəzərdə tutulmayıb və bura əlavə etmək təhlükəli ola bilər."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Anladım"</string>
@@ -564,8 +565,8 @@
     <string name="media_projection_action_text" msgid="3634906766918186440">"İndi başlayın"</string>
     <string name="empty_shade_text" msgid="8935967157319717412">"Heç bir bildiriş yoxdur"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"Yeni bildiriş yoxdur"</string>
-    <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"Adaptiv bildirişlər aktivdir"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"Qısa vaxtda çoxlu bildiriş alanda cihaz səsi azaldır, ekranda popapları iki dəqiqəyə qədər qısaldır."</string>
+    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Bildiriş gözləmə müddəti yanılıdır"</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Eyni anda çox bildiriş aldıqda cihazın səs və xəbərdarlıqları avtomatik 2 dəqiqəyə qədər azalır."</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Deaktiv edin"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Köhnə bildirişləri görmək üçün kilidi açın"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Bu cihaz valideyniniz tərəfindən idarə olunur"</string>
@@ -732,8 +733,7 @@
     <string name="notification_alert_title" msgid="3656229781017543655">"Defolt"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Avtomatik"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"Səs və ya vibrasiya yoxdur"</string>
-    <!-- no translation found for notification_conversation_summary_low (3855696451919728790) -->
-    <skip />
+    <string name="notification_conversation_summary_low" msgid="3855696451919728790">"Səs və ya vibrasiya yoxdur, lakin hələ də söhbət bölməsində görünür"</string>
     <string name="notification_channel_summary_default" msgid="777294388712200605">"Cihaz ayarlarına əsasən zəng çala və ya vibrasiya edə bilər"</string>
     <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Cihaz ayarlarına əsasən zəng çala və ya vibrasiya edə bilər. <xliff:g id="APP_NAME">%1$s</xliff:g> tətbiqindən söhbətlərdə defolt olaraq qabarcıq çıxır."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Bu bildirişin səs çıxarması və ya vibrasiya etməsi sistem tərəfindən təyin edilsin"</string>
@@ -790,8 +790,7 @@
     <string name="keyboard_key_page_up" msgid="173914303254199845">"Yuxarı Səhifə"</string>
     <string name="keyboard_key_page_down" msgid="9035902490071829731">"Aşağı Səhifə"</string>
     <string name="keyboard_key_forward_del" msgid="5325501825762733459">"Silin"</string>
-    <!-- no translation found for keyboard_key_esc (6230365950511411322) -->
-    <skip />
+    <string name="keyboard_key_esc" msgid="6230365950511411322">"Esc"</string>
     <string name="keyboard_key_move_home" msgid="3496502501803911971">"Home"</string>
     <string name="keyboard_key_move_end" msgid="99190401463834854">"Son"</string>
     <string name="keyboard_key_insert" msgid="4621692715704410493">"Daxil edin"</string>
@@ -1233,7 +1232,6 @@
     <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>
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobil data"</string>
-    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</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>
     <string name="mobile_data_poor_connection" msgid="819617772268371434">"Zəif bağlantı"</string>
@@ -1374,8 +1372,7 @@
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Bölünmüş ekran"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Daxiletmə"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Tətbiq qısayolları"</string>
-    <!-- no translation found for shortcut_helper_category_current_app_shortcuts (4017840565974573628) -->
-    <skip />
+    <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Cari tətbiq"</string>
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Xüsusi imkanlar"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Klaviatura qısayolları"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Axtarış qısayolları"</string>
@@ -1388,13 +1385,27 @@
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Hazırdır"</string>
     <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Əla!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Geri qayıdın"</string>
-    <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"Geri qayıtmaq üçün taçpedin istənilən yerində üç barmaqla sola və ya sağa çəkin."</string>
+    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
+    <skip />
+    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
+    <skip />
     <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Üç barmağın sağa və sola hərəkət etdiyini göstərən taçped"</string>
     <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Geri jesti üçün animasiya göstərən cihaz ekranı"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Klaviatura işığı"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Səviyyə %1$d/%2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Ev nizamlayıcıları"</string>
     <string name="home_controls_dream_description" msgid="4644150952104035789">"Ekran qoruyucu kimi ev nizamlayıcılarına tez giriş"</string>
-    <!-- no translation found for volume_undo_action (5815519725211877114) -->
-    <skip />
+    <string name="volume_undo_action" msgid="5815519725211877114">"Geri qaytarın"</string>
+    <string name="back_edu_toast_content" msgid="4530314597378982956">"Geri qayıtmaq üçün taçpeddə üç barmağınızla sola və ya sağa çəkin"</string>
+    <string name="home_edu_toast_content" msgid="3381071147871955415">"Ana səhifəyə keçmək üçün taçpeddə üç barmağınızla yuxarı çəkin"</string>
+    <string name="overview_edu_toast_content" msgid="5797030644017804518">"Son tətbiqlərə baxmaq üçün taçpeddə üç barmağınızla yuxarı çəkib saxlayın"</string>
+    <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Bütün tətbiqlərə baxmaq üçün klaviaturada fəaliyyət açarını basın"</string>
+    <string name="back_edu_notification_title" msgid="5624780717751357278">"Geri qayıtmaq üçün taçped istifadə edin"</string>
+    <string name="back_edu_notification_content" msgid="2497557451540954068">"Üç barmaqla sola və ya sağa çəkin. Daha çox jest öyrənmək üçün toxunun."</string>
+    <string name="home_edu_notification_title" msgid="6097902076909654045">"Ana səhifəyə keçmək üçün taçped istifadə edin"</string>
+    <string name="home_edu_notification_content" msgid="6631697734535766588">"Üç barmaqla yuxarı çəkin. Daha çox jest öyrənmək üçün toxunun."</string>
+    <string name="overview_edu_notification_title" msgid="1265824157319562406">"Son tətbiqlərə baxmaq üçün taçped istifadə edin"</string>
+    <string name="overview_edu_notification_content" msgid="3578204677648432500">"Üç barmaq ilə yuxarı çəkib saxlayın. Daha çox jest öyrənmək üçün toxunun."</string>
+    <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Bütün tətbiqlərə baxmaq üçün klaviatura istifadə edin"</string>
+    <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"İstənilən vaxt fəaliyyət açarını basın. Daha çox jest öyrənmək üçün toxunun."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-az/tiles_states_strings.xml b/packages/SystemUI/res/values-az/tiles_states_strings.xml
index da6d217..c24f402 100644
--- a/packages/SystemUI/res/values-az/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-az/tiles_states_strings.xml
@@ -56,9 +56,6 @@
     <item msgid="5376619709702103243">"Deaktiv"</item>
     <item msgid="4875147066469902392">"Aktiv"</item>
   </string-array>
-    <!-- no translation found for tile_states_modes:0 (7764936419245199023) -->
-    <!-- no translation found for tile_states_modes:1 (2004750556637773692) -->
-    <!-- no translation found for tile_states_modes:2 (8968530753931637871) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Əlçatan deyil"</item>
     <item msgid="5044688398303285224">"Deaktiv"</item>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index 3a7cc66..307dcc0 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -141,7 +141,7 @@
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Želite da zaustavite prebacivanje?"</string>
     <string name="cast_to_other_device_stop_dialog_message_entire_screen_with_device" msgid="1474703115926205251">"Trenutno prebacujete ceo ekran na: <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="cast_to_other_device_stop_dialog_message_entire_screen" msgid="8419219169553867625">"Trenutno prebacujete ceo ekran na uređaj u blizini"</string>
-    <string name="cast_to_other_device_stop_dialog_message_specific_app_with_device" msgid="2715934698604085519">"<xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> se trenutno prebacuje na: <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app_with_device" msgid="2715934698604085519">"<xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> trenutno prebacujete na: <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
     <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="8616103075630934513">"<xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> se trenutno prebacuje na uređaj u blizini"</string>
     <string name="cast_to_other_device_stop_dialog_message_generic_with_device" msgid="9213582497852420203">"Trenutno prebacujete na: <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="cast_to_other_device_stop_dialog_message_generic" msgid="4100272100480415076">"Trenutno prebacujete na uređaj u blizini"</string>
@@ -290,8 +290,7 @@
     <string name="start_dreams" msgid="9131802557946276718">"Čuvar ekrana"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Eternet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"Ne uznemiravaj"</string>
-    <!-- no translation found for quick_settings_modes_label (5407025818652750501) -->
-    <skip />
+    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Prioritetni režimi"</string>
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Nije dostupan nijedan upareni uređaj"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Dodirnite da biste povezali uređaj ili prekinuli vezu"</string>
@@ -389,6 +388,9 @@
     <string name="performance" msgid="6552785217174378320">"Performanse"</string>
     <string name="user_interface" msgid="3712869377953950887">"Korisnički interfejs"</string>
     <string name="thermal" msgid="6758074791325414831">"Termalna kamera"</string>
+    <string name="custom" msgid="3337456985275158299">"Prilagođeno"</string>
+    <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Podešavanja prilagođenog praćenja"</string>
+    <string name="restore_default" msgid="5259420807486239755">"Vrati podrazumevano"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Režim jednom rukom"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Slušni aparati"</string>
     <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktivno"</string>
@@ -427,6 +429,17 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Otvori Podešavanja"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Drugi uređaj"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Uključi/isključi pregled"</string>
+    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Prioritetni režimi"</string>
+    <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Gotovo"</string>
+    <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Podešavanja"</string>
+    <string name="zen_mode_on" msgid="9085304934016242591">"Uključeno"</string>
+    <string name="zen_mode_off" msgid="1736604456618147306">"Isključeno"</string>
+    <!-- no translation found for zen_mode_set_up (7457957033034460064) -->
+    <skip />
+    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
+    <skip />
+    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
+    <skip />
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Neće vas uznemiravati zvukovi i vibracije osim za alarme, podsetnike, događaje i pozivaoce koje navedete. I dalje ćete čuti sve što odaberete da pustite, uključujući muziku, video snimke i igre."</string>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Neće vas uznemiravati zvukovi i vibracije osim za alarme. I dalje ćete čuti sve što odaberete da pustite, uključujući muziku, video snimke i igre."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Prilagodi"</string>
@@ -465,6 +478,8 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Sporo se puni • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> do kraja punjenja"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Puni se • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> do kraja punjenja"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Vidžeti na zaključanom ekranu"</string>
+    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
+    <skip />
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Prevucite ulevo da biste započeli zajednički vodič"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Prilagodite"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Odbaci"</string>
@@ -492,7 +507,8 @@
     <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"uklonite vidžet"</string>
     <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"postavite izabrani vidžet"</string>
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"Vidžeti za zaključani ekran"</string>
-    <string name="communal_widget_picker_description" msgid="490515450110487871">"Svi mogu da vide vedžete na zaključanom ekranu, čak i kada je tablet zaključan."</string>
+    <string name="communal_widget_picker_description" msgid="490515450110487871">"Svi mogu da vide vidžete na zaključanom ekranu, čak i kada je tablet zaključan."</string>
+    <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"poništi izbor vidžeta"</string>
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Vidžeti za zaključani ekran"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Da biste otvorili aplikaciju koja koristi vidžet, treba da potvrdite da ste to vi. Imajte u vidu da svako može da ga vidi, čak i kada je tablet zaključan. Neki vidžeti možda nisu namenjeni za zaključani ekran i možda nije bezbedno da ih tamo dodate."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Važi"</string>
@@ -549,8 +565,8 @@
     <string name="media_projection_action_text" msgid="3634906766918186440">"Započni"</string>
     <string name="empty_shade_text" msgid="8935967157319717412">"Nema obaveštenja"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"Nema novih obaveštenja"</string>
-    <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"Prilag. obaveštenja su uključena"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"Uređaj sada smanjuje zvuk i broj iskačućih prozora na ekranu do 2 minuta kad primite mnogo obaveštenja u kratkom roku."</string>
+    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Utišavanje obaveštenja je uključeno"</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Zvuk i broj upozorenja na uređaju se automatski smanjuju na 2 minuta kada dobijete previše obaveštenja."</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Isključi"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Otključajte za starija obaveštenja"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Ovim uređajem upravlja roditelj"</string>
@@ -717,8 +733,7 @@
     <string name="notification_alert_title" msgid="3656229781017543655">"Podrazumevano"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Automatska"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"Bez zvuka i vibriranja"</string>
-    <!-- no translation found for notification_conversation_summary_low (3855696451919728790) -->
-    <skip />
+    <string name="notification_conversation_summary_low" msgid="3855696451919728790">"Bez zvuka i vibriranja, ali se još uvek prikazuje u odeljku za konverzacije"</string>
     <string name="notification_channel_summary_default" msgid="777294388712200605">"Može da zvoni ili vibrira u zavisnosti od podešavanja uređaja"</string>
     <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Može da zvoni ili vibrira u zavisnosti od podešavanja uređaja. Konverzacije iz aplikacije <xliff:g id="APP_NAME">%1$s</xliff:g> podrazumevano se prikazuju u oblačićima."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Neka sistem utvrdi da li ovo obaveštenje treba da emituje zvuk ili da vibrira"</string>
@@ -1217,7 +1232,6 @@
     <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>
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobilni podaci"</string>
-    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Povezano"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Privremeno povezano"</string>
     <string name="mobile_data_poor_connection" msgid="819617772268371434">"Veza je loša"</string>
@@ -1371,13 +1385,27 @@
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Gotovo"</string>
     <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Odlično!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Nazad"</string>
-    <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"Da biste se vratili, prevucite ulevo ili udesno sa tri prsta bilo gde na tačpedu."</string>
+    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
+    <skip />
+    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
+    <skip />
     <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Tačped sa prikazom tri prsta koji se pomeraju udesno i ulevo"</string>
     <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Ekran uređaja sa prikazom animacije pokreta za nazad"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Pozadinsko osvetljenje tastature"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"%1$d. nivo od %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Kontrole za dom"</string>
     <string name="home_controls_dream_description" msgid="4644150952104035789">"Brz pristup kontrolama za dom kao čuvaru ekrana"</string>
-    <!-- no translation found for volume_undo_action (5815519725211877114) -->
-    <skip />
+    <string name="volume_undo_action" msgid="5815519725211877114">"Opozovi"</string>
+    <string name="back_edu_toast_content" msgid="4530314597378982956">"Da biste se vratili, prevucite ulevo ili udesno sa tri prsta na tačpedu"</string>
+    <string name="home_edu_toast_content" msgid="3381071147871955415">"Da biste otišli na početni ekran, prevucite nagore sa tri prsta na tačpedu"</string>
+    <string name="overview_edu_toast_content" msgid="5797030644017804518">"Da biste pregledali nedavne aplikacije, prevucite nagore i zadržite sa tri prsta na tačpedu"</string>
+    <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Da biste pogledali sve aplikacije, pritisnite taster radnji na tastaturi"</string>
+    <string name="back_edu_notification_title" msgid="5624780717751357278">"Koristite tačped da biste se vratili"</string>
+    <string name="back_edu_notification_content" msgid="2497557451540954068">"Prevucite ulevo ili udesno sa tri prsta. Dodirnite da biste videli više pokreta."</string>
+    <string name="home_edu_notification_title" msgid="6097902076909654045">"Koristite tačped da biste otišli na početni ekran"</string>
+    <string name="home_edu_notification_content" msgid="6631697734535766588">"Prevucite nagore sa tri prsta. Dodirnite da biste videli više pokreta."</string>
+    <string name="overview_edu_notification_title" msgid="1265824157319562406">"Koristite tačped da biste pregledali nedavne aplikacije"</string>
+    <string name="overview_edu_notification_content" msgid="3578204677648432500">"Prevucite nagore i zadržite sa tri prsta. Dodirnite da biste videli više pokreta."</string>
+    <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Koristite tastaturu da biste pregledali sve aplikacije"</string>
+    <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Pritisnite taster radnji u bilo kom trenutku. Dodirnite da biste videli više pokreta."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/tiles_states_strings.xml b/packages/SystemUI/res/values-b+sr+Latn/tiles_states_strings.xml
index 6833c27..df0b786 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/tiles_states_strings.xml
@@ -56,9 +56,6 @@
     <item msgid="5376619709702103243">"Isključeno"</item>
     <item msgid="4875147066469902392">"Uključeno"</item>
   </string-array>
-    <!-- no translation found for tile_states_modes:0 (7764936419245199023) -->
-    <!-- no translation found for tile_states_modes:1 (2004750556637773692) -->
-    <!-- no translation found for tile_states_modes:2 (8968530753931637871) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Nedostupno"</item>
     <item msgid="5044688398303285224">"Isključeno"</item>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index 7280866..0580d7e 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -290,8 +290,7 @@
     <string name="start_dreams" msgid="9131802557946276718">"Экранная застаўка"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"Не турбаваць"</string>
-    <!-- no translation found for quick_settings_modes_label (5407025818652750501) -->
-    <skip />
+    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Прыярытэтныя рэжымы"</string>
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Няма даступных спалучаных прылад"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Націсніце, каб падключыць або адключыць прыладу"</string>
@@ -389,6 +388,9 @@
     <string name="performance" msgid="6552785217174378320">"Прадукцыйнасць"</string>
     <string name="user_interface" msgid="3712869377953950887">"Карыстальніцкі інтэрфейс"</string>
     <string name="thermal" msgid="6758074791325414831">"Тэрмальныя паказчыкі"</string>
+    <string name="custom" msgid="3337456985275158299">"Карыстальніцкае значэнне"</string>
+    <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Карыстальніцкія налады трасіроўкі"</string>
+    <string name="restore_default" msgid="5259420807486239755">"Аднавіць стандартнае значэнне"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Рэжым кіравання адной рукой"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Слыхавыя апараты"</string>
     <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Актыўныя"</string>
@@ -427,6 +429,17 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Адкрыць налады"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Іншая прылада"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Уключыць/выключыць агляд"</string>
+    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Прыярытэтныя рэжымы"</string>
+    <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_off" msgid="1736604456618147306">"Выключана"</string>
+    <!-- no translation found for zen_mode_set_up (7457957033034460064) -->
+    <skip />
+    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
+    <skip />
+    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
+    <skip />
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Вас не будуць турбаваць гукі і вібрацыя, за выключэннем будзільнікаў, напамінаў, падзей і выбраных вамі абанентаў. Вы будзеце чуць усё, што ўключыце, у тым ліку музыку, відэа і гульні."</string>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Вас не будуць турбаваць гукі і вібрацыя, за выключэннем будзільнікаў. Вы будзеце чуць усё, што ўключыце, у тым ліку музыку, відэа і гульні."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Дапасаваць"</string>
@@ -465,6 +478,8 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Ідзе павольная зарадка • Поўны зарад праз <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Ідзе зарадка • Поўны зарад праз <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Віджэты на экране блакіроўкі"</string>
+    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
+    <skip />
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Правядзіце пальцам па экране ўлева, каб азнаёміцца з дапаможнікам"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Наладзіць"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Закрыць"</string>
@@ -493,6 +508,7 @@
     <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"размясціць выбраны віджэт"</string>
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"Віджэты на экране блакіроўкі"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"Віджэты на экране блакіроўкі будуць бачныя, нават калі планшэт заблакіраваны."</string>
+    <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"скасаваць выбар віджэта"</string>
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Віджэты на экране блакіроўкі"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Каб адкрыць праграму з дапамогай віджэта, вам неабходна будзе пацвердзіць сваю асобу. Таксама памятайце, што такія віджэты могуць пабачыць іншыя людзі, нават калі экран планшэта заблакіраваны. Некаторыя віджэты могуць не падыходзіць для выкарыстання на экране блакіроўкі, і дадаваць іх сюды можа быць небяспечна."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Зразумела"</string>
@@ -549,8 +565,8 @@
     <string name="media_projection_action_text" msgid="3634906766918186440">"Пачаць зараз"</string>
     <string name="empty_shade_text" msgid="8935967157319717412">"Апавяшчэнняў няма"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"Няма новых апавяшчэнняў"</string>
-    <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"Адаптыўныя апавяшчэнні ўключаны"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"Пры атрыманні шматлікіх апавяшчэнняў за кароткі час прылада памяншае гучнасць і колькасць усплывальных вокнаў на 2 хв."</string>
+    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Зніжэнне гучнасці апавяшчэнняў уключана"</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Калі адначасова прыходзіць шмат апавяшчэнняў, гук прылады і абвестак зніжаецца на час да 2 хвілін."</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Выключыць"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Разблакіруйце, каб убачыць усе апавяшчэнні"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Гэта прылада знаходзіцца пад кантролем бацькоў"</string>
@@ -717,8 +733,7 @@
     <string name="notification_alert_title" msgid="3656229781017543655">"Стандартна"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Аўтаматычна"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"Без гуку ці вібрацыі"</string>
-    <!-- no translation found for notification_conversation_summary_low (3855696451919728790) -->
-    <skip />
+    <string name="notification_conversation_summary_low" msgid="3855696451919728790">"Гукавы сігнал або вібрацыя выключаны, але апавяшчэнні ўсё роўна з’яўляюцца ў раздзеле размоў"</string>
     <string name="notification_channel_summary_default" msgid="777294388712200605">"У залежнасці ад налад прылады магчымы званок або вібрацыя"</string>
     <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"У залежнасці ад налад прылады магчымы званок або вібрацыя. Размовы ў праграме \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" стандартна паяўляюцца ў выглядзе ўсплывальных апавяшчэнняў."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Сістэма сама будзе вызначаць, ці трэба для гэтага апавяшчэння ўключаць гук або вібрацыю"</string>
@@ -1217,7 +1232,6 @@
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Патрабуецца аўтэнтыфікацыя. Дакраніцеся да сканера адбіткаў пальцаў."</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Бягучы тэлефонны выклік"</string>
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Мабільная перадача даных"</string>
-    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Падключана"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Падключана часова"</string>
     <string name="mobile_data_poor_connection" msgid="819617772268371434">"Нестабільнае падключэнне"</string>
@@ -1358,8 +1372,7 @@
     <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>
-    <!-- no translation found for shortcut_helper_category_current_app_shortcuts (4017840565974573628) -->
-    <skip />
+    <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_search_placeholder" msgid="5488547526269871819">"Пошук спалучэнняў клавіш"</string>
@@ -1372,13 +1385,27 @@
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Гатова"</string>
     <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Цудоўна!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Назад"</string>
-    <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"Каб вярнуцца на папярэдні экран, правядзіце трыма пальцамі ўлева або ўправа ў любым месцы сэнсарнай панэлі."</string>
+    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
+    <skip />
+    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
+    <skip />
     <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Паказваецца, як на сэнсарнай панэлі тры пальцы рухаюцца ўправа і ўлева"</string>
     <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"На экране прылады паказваецца анімацыя жэста \"Назад\""</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Падсветка клавіятуры"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Узровень %1$d з %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Кіраванне домам"</string>
     <string name="home_controls_dream_description" msgid="4644150952104035789">"Хуткі доступ да кіравання домам на застаўцы"</string>
-    <!-- no translation found for volume_undo_action (5815519725211877114) -->
-    <skip />
+    <string name="volume_undo_action" msgid="5815519725211877114">"Адрабіць"</string>
+    <string name="back_edu_toast_content" msgid="4530314597378982956">"Каб вярнуцца, правядзіце па сэнсарнай панэлі трыма пальцамі ўлева ці ўправа"</string>
+    <string name="home_edu_toast_content" msgid="3381071147871955415">"Каб вярнуцца на галоўны экран, правядзіце па сэнсарнай панэлі трыма пальцамі ўверх"</string>
+    <string name="overview_edu_toast_content" msgid="5797030644017804518">"Для прагляду нядаўніх праграм правядзіце па сэнсарнай панэлі трыма пальцамі ўверх і затрымайцеся"</string>
+    <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Каб праглядзець усе праграмы, націсніце на клавішу дзеяння на клавіятуры"</string>
+    <string name="back_edu_notification_title" msgid="5624780717751357278">"Выкарыстайце сэнсарную панэль для вяртання"</string>
+    <string name="back_edu_notification_content" msgid="2497557451540954068">"Правядзіце ўлева ці ўправа трыма пальцамі. Націсніце, каб азнаёміцца з іншымі жэстамі."</string>
+    <string name="home_edu_notification_title" msgid="6097902076909654045">"Выкарыстайце сэнсарную панэль для вяртання на галоўны экран"</string>
+    <string name="home_edu_notification_content" msgid="6631697734535766588">"Правядзіце трыма пальцамі ўверх. Націсніце, каб азнаёміцца з іншымі жэстамі."</string>
+    <string name="overview_edu_notification_title" msgid="1265824157319562406">"Выкарыстайце сэнсарную панэль, каб праглядзець нядаўнія праграмы"</string>
+    <string name="overview_edu_notification_content" msgid="3578204677648432500">"Правядзіце трыма пальцамі ўверх і затрымайце пальцы. Націсніце, каб азнаёміцца з іншымі жэстамі."</string>
+    <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Выкарыстайце клавіятуру для прагляду ўсіх праграм"</string>
+    <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Можна націснуць на клавішу дзеяння ў любы момант. Націсніце, каб азнаёміцца з іншымі жэстамі."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-be/tiles_states_strings.xml b/packages/SystemUI/res/values-be/tiles_states_strings.xml
index 4cc09a7..33e704c 100644
--- a/packages/SystemUI/res/values-be/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-be/tiles_states_strings.xml
@@ -56,9 +56,6 @@
     <item msgid="5376619709702103243">"Выключана"</item>
     <item msgid="4875147066469902392">"Уключана"</item>
   </string-array>
-    <!-- no translation found for tile_states_modes:0 (7764936419245199023) -->
-    <!-- no translation found for tile_states_modes:1 (2004750556637773692) -->
-    <!-- no translation found for tile_states_modes:2 (8968530753931637871) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Недаступна"</item>
     <item msgid="5044688398303285224">"Выключана"</item>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index 5ac0be0..dfa9598 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -126,38 +126,25 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"Докоснете за преглед"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"Грешка при запазването на записа на екрана"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"При стартирането на записа на екрана възникна грешка"</string>
-    <!-- no translation found for screenrecord_stop_dialog_title (8716193661764511095) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message (6262768207331626817) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5995770227684523244) -->
-    <skip />
+    <string name="screenrecord_stop_dialog_title" msgid="8716193661764511095">"Да се спре ли записването?"</string>
+    <string name="screenrecord_stop_dialog_message" msgid="6262768207331626817">"В момента записвате целия си екран"</string>
+    <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"В момента записвате <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Спиране на записа"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Екранът се споделя"</string>
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Да се спре ли споделянето на екрана?"</string>
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen_with_host_app (522823522115375414) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen (5090115386271179270) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_specific (5923772039347985172) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_generic (6681016774654578261) -->
-    <skip />
+    <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"В момента споделяте целия си екран с(ъс) <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
+    <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"В момента споделяте целия си екран с приложение"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"В момента споделяте <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"В момента споделяте приложение"</string>
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Спиране на споделянето"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Екранът се предава"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Да се спре ли предаването?"</string>
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen_with_device (1474703115926205251) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen (8419219169553867625) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app_with_device (2715934698604085519) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (8616103075630934513) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic_with_device (9213582497852420203) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic (4100272100480415076) -->
-    <skip />
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen_with_device" msgid="1474703115926205251">"В момента предавате целия си екран към <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen" msgid="8419219169553867625">"В момента предавате целия си екран към устройство в близост"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app_with_device" msgid="2715934698604085519">"В момента предавате <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> към <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="8616103075630934513">"В момента предавате <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> към устройство в близост"</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic_with_device" msgid="9213582497852420203">"В момента предавате към <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic" msgid="4100272100480415076">"В момента предавате към устройство в близост"</string>
     <string name="cast_to_other_device_stop_dialog_button" msgid="6420183747435521834">"Спиране на предаването"</string>
     <string name="close_dialog_button" msgid="4749497706540104133">"Затваряне"</string>
     <string name="issuerecord_title" msgid="286627115110121849">"Записване на проблем"</string>
@@ -303,8 +290,7 @@
     <string name="start_dreams" msgid="9131802557946276718">"Скрийнсейвър"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"Не безпокойте"</string>
-    <!-- no translation found for quick_settings_modes_label (5407025818652750501) -->
-    <skip />
+    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Приоритетни режими"</string>
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Няма налични сдвоени устройства"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Докоснете, за да свържете устройство или да прекъснете връзката му"</string>
@@ -402,6 +388,9 @@
     <string name="performance" msgid="6552785217174378320">"Ефективност"</string>
     <string name="user_interface" msgid="3712869377953950887">"Потребителски интерфейс"</string>
     <string name="thermal" msgid="6758074791325414831">"Термално"</string>
+    <string name="custom" msgid="3337456985275158299">"Персонализирано"</string>
+    <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Настройки за персонализираната следа"</string>
+    <string name="restore_default" msgid="5259420807486239755">"Възстановяване на стандартната настройка"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Режим за работа с една ръка"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Слухови апарати"</string>
     <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Активно"</string>
@@ -440,6 +429,17 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Към настройките"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Друго устройство"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Превключване на общия преглед"</string>
+    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Приоритетни режими"</string>
+    <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_off" msgid="1736604456618147306">"Изкл."</string>
+    <!-- no translation found for zen_mode_set_up (7457957033034460064) -->
+    <skip />
+    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
+    <skip />
+    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
+    <skip />
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Няма да бъдете обезпокоявани от звуци и вибрирания освен от будилници, напомняния, събития и обаждания от посочени от вас контакти. Пак ще чувате всичко, което изберете да се пусне, включително музика, видеоклипове и игри."</string>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Няма да бъдете обезпокоявани от звуци и вибрирания освен от будилници. Ще чувате обаче всичко, което изберете пуснете, включително музика, видеоклипове и игри."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Персонализиране"</string>
@@ -478,6 +478,8 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Зарежда се бавно • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> до пълно зареждане"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Зарежда се • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> до пълно зареждане"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Приспособления на заключения екран"</string>
+    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
+    <skip />
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Прекарайте пръст наляво, за да стартирате общия урок"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Персонализиране"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Отхвърляне"</string>
@@ -504,10 +506,9 @@
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"избиране на приспособление"</string>
     <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"премахване на приспособлението"</string>
     <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"поставяне на избраното приспособление"</string>
-    <!-- no translation found for communal_widget_picker_title (1953369090475731663) -->
-    <skip />
-    <!-- no translation found for communal_widget_picker_description (490515450110487871) -->
-    <skip />
+    <string name="communal_widget_picker_title" msgid="1953369090475731663">"Приспособления за заключения екран"</string>
+    <string name="communal_widget_picker_description" msgid="490515450110487871">"Всеки ще вижда приспособленията на закл. екран дори ако таблетът ви е заключен."</string>
+    <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"премахване на избора от приспособлението"</string>
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Приспособления за заключения екран"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"За да отворите дадено приложение посредством приспособление, ще трябва да потвърдите, че това сте вие. Също така имайте предвид, че всеки ще вижда приспособленията дори когато таблетът ви е заключен. Възможно е някои от тях да не са предназначени за заключения екран и добавянето им на него може да е опасно."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Разбрах"</string>
@@ -564,8 +565,8 @@
     <string name="media_projection_action_text" msgid="3634906766918186440">"Стартиране сега"</string>
     <string name="empty_shade_text" msgid="8935967157319717412">"Няма известия"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"Няма нови известия"</string>
-    <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"Адаптивните известия са вкл."</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"У-вото ви намалява силата на звука и броя на изскачащите прозорци за период до 2 мин, ако получавате много известия за кратко време."</string>
+    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Изчакването за известията е включено"</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Силата на звука и сигналите на у-вото се намаляват за до 2 минути, когато получавате твърде много известия наведнъж."</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Изключване"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Отключете за достъп до по-стари известия"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Това устройство се управлява от родителя ви"</string>
@@ -732,8 +733,7 @@
     <string name="notification_alert_title" msgid="3656229781017543655">"Стандартно"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Автоматично"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"Без звук или вибриране"</string>
-    <!-- no translation found for notification_conversation_summary_low (3855696451919728790) -->
-    <skip />
+    <string name="notification_conversation_summary_low" msgid="3855696451919728790">"Без звук или вибриране, но пак ще се показва в секцията с разговори"</string>
     <string name="notification_channel_summary_default" msgid="777294388712200605">"Може да звъни или да вибрира въз основа на настройките на устройството"</string>
     <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Може да звъни или да вибрира въз основа на настройките на устройството. Разговорите от <xliff:g id="APP_NAME">%1$s</xliff:g> се показват като балончета по подразбиране."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Нека системата да определя дали дадено известие да се придружава от звук, или вибриране"</string>
@@ -790,8 +790,7 @@
     <string name="keyboard_key_page_up" msgid="173914303254199845">"Страница нагоре"</string>
     <string name="keyboard_key_page_down" msgid="9035902490071829731">"Страница надолу"</string>
     <string name="keyboard_key_forward_del" msgid="5325501825762733459">"Изтриване"</string>
-    <!-- no translation found for keyboard_key_esc (6230365950511411322) -->
-    <skip />
+    <string name="keyboard_key_esc" msgid="6230365950511411322">"Esc"</string>
     <string name="keyboard_key_move_home" msgid="3496502501803911971">"Home"</string>
     <string name="keyboard_key_move_end" msgid="99190401463834854">"End"</string>
     <string name="keyboard_key_insert" msgid="4621692715704410493">"Insert"</string>
@@ -1233,7 +1232,6 @@
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Изисква се удостоверяване на самоличността. За целта докоснете сензора за отпечатъци."</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Текущо телефонно обаждане"</string>
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Мобилни данни"</string>
-    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Свързано"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Установена е временна връзка"</string>
     <string name="mobile_data_poor_connection" msgid="819617772268371434">"Слаба връзка"</string>
@@ -1374,8 +1372,7 @@
     <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>
-    <!-- no translation found for shortcut_helper_category_current_app_shortcuts (4017840565974573628) -->
-    <skip />
+    <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_search_placeholder" msgid="5488547526269871819">"Търсете клавишни комбинации"</string>
@@ -1388,13 +1385,27 @@
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Готово"</string>
     <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Отлично!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Назад"</string>
-    <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"За да се върнете назад, прекарайте три пръста наляво или надясно по сензорния панел."</string>
+    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
+    <skip />
+    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
+    <skip />
     <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Сензорен панел, върху който три пръста се движат надясно и наляво"</string>
     <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Екран на устройство, показващ анимация за жеста за връщане назад"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Подсветка на клавиатурата"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Ниво %1$d от %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Контроли за дома"</string>
     <string name="home_controls_dream_description" msgid="4644150952104035789">"Достъп до контролите за дома ви като скрийнсейвър"</string>
-    <!-- no translation found for volume_undo_action (5815519725211877114) -->
-    <skip />
+    <string name="volume_undo_action" msgid="5815519725211877114">"Отмяна"</string>
+    <string name="back_edu_toast_content" msgid="4530314597378982956">"За да се върнете назад, плъзнете три пръста наляво или надясно по сензорния панел"</string>
+    <string name="home_edu_toast_content" msgid="3381071147871955415">"За да преминете към началния екран, плъзнете нагоре по сензорния панел с три пръста"</string>
+    <string name="overview_edu_toast_content" msgid="5797030644017804518">"За да прегледате скорошните приложения, плъзнете нагоре по сензорния панел с три пръста и задръжте"</string>
+    <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"За да прегледате всичките си приложения, натиснете клавиша за действия на клавиатурата си"</string>
+    <string name="back_edu_notification_title" msgid="5624780717751357278">"Използвайте сензорния панел, за да се върнете назад"</string>
+    <string name="back_edu_notification_content" msgid="2497557451540954068">"Плъзнете три пръста наляво или надясно. Докоснете, за да научите повече жестове."</string>
+    <string name="home_edu_notification_title" msgid="6097902076909654045">"Използвайте сензорния панел, за да преминете към началния екран"</string>
+    <string name="home_edu_notification_content" msgid="6631697734535766588">"Плъзнете нагоре с три пръста. Докоснете, за да научите повече жестове."</string>
+    <string name="overview_edu_notification_title" msgid="1265824157319562406">"Използвайте сензорния панел, за да прегледате скорошните приложения"</string>
+    <string name="overview_edu_notification_content" msgid="3578204677648432500">"Плъзнете нагоре с три пръста и задръжте. Докоснете, за да научите повече жестове."</string>
+    <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Използвайте клавиатурата, за да прегледате всички приложения"</string>
+    <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Натиснете клавиша за действия по всяко време. Докоснете, за да научите повече жестове."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-bg/tiles_states_strings.xml b/packages/SystemUI/res/values-bg/tiles_states_strings.xml
index 92db279..e2fd653 100644
--- a/packages/SystemUI/res/values-bg/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-bg/tiles_states_strings.xml
@@ -56,9 +56,6 @@
     <item msgid="5376619709702103243">"Изкл."</item>
     <item msgid="4875147066469902392">"Вкл."</item>
   </string-array>
-    <!-- no translation found for tile_states_modes:0 (7764936419245199023) -->
-    <!-- no translation found for tile_states_modes:1 (2004750556637773692) -->
-    <!-- no translation found for tile_states_modes:2 (8968530753931637871) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Не е налице"</item>
     <item msgid="5044688398303285224">"Изкл."</item>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index fa38ac3..3f6c269 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -126,38 +126,25 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"দেখতে ট্যাপ করুন"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"স্ক্রিন রেকর্ডিং সেভ করার সময় কোনও সমস্যা হয়েছে"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"স্ক্রিন রেকর্ডিং শুরু করার সময় সমস্যা হয়েছে"</string>
-    <!-- no translation found for screenrecord_stop_dialog_title (8716193661764511095) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message (6262768207331626817) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5995770227684523244) -->
-    <skip />
+    <string name="screenrecord_stop_dialog_title" msgid="8716193661764511095">"রেকডিং বন্ধ করবেন?"</string>
+    <string name="screenrecord_stop_dialog_message" msgid="6262768207331626817">"আপনি বর্তমানে সম্পূর্ণ স্ক্রিন রেকর্ড করছেন"</string>
+    <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"আপনি বর্তমানে <xliff:g id="APP_NAME">%1$s</xliff:g> রেকর্ড করছেন"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"রেকর্ড করা বন্ধ করুন"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"স্ক্রিন শেয়ার করা হচ্ছে"</string>
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"স্ক্রিন শেয়ার করা বন্ধ করবেন?"</string>
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen_with_host_app (522823522115375414) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen (5090115386271179270) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_specific (5923772039347985172) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_generic (6681016774654578261) -->
-    <skip />
+    <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"আপনি বর্তমানে <xliff:g id="HOST_APP_NAME">%1$s</xliff:g> অ্যাপের সাথে আপনার সম্পূর্ণ স্ক্রিন শেয়ার করছেন"</string>
+    <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"আপনি বর্তমানে কোনও একটি অ্যাপের সাথে আপনার সম্পূর্ণ স্ক্রিন শেয়ার করছেন"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"আপনি বর্তমানে <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> অ্যাপের সাথে শেয়ার করছেন"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"আপনি বর্তমানে কোনও একটি অ্যাপের সাথে শেয়ার করছেন"</string>
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"শেয়ার করা বন্ধ করুন"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"স্ক্রিন কাস্ট করা হচ্ছে"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"কাস্ট করা বন্ধ করবেন?"</string>
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen_with_device (1474703115926205251) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen (8419219169553867625) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app_with_device (2715934698604085519) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (8616103075630934513) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic_with_device (9213582497852420203) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic (4100272100480415076) -->
-    <skip />
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen_with_device" msgid="1474703115926205251">"আপনি বর্তমানে <xliff:g id="DEVICE_NAME">%1$s</xliff:g> ডিভাইসে সম্পূর্ণ স্ক্রিন কাস্ট করছেন"</string>
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen" msgid="8419219169553867625">"আপনি বর্তমানে আশেপাশের ডিভাইসে সম্পূর্ণ স্ক্রিন কাস্ট করছেন"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app_with_device" msgid="2715934698604085519">"আপনি বর্তমানে <xliff:g id="DEVICE_NAME">%2$s</xliff:g> ডিভাইসে <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> কাস্ট করছেন"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="8616103075630934513">"আপনি বর্তমানে আশেপাশের ডিভাইসে <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> কাস্ট করছেন"</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic_with_device" msgid="9213582497852420203">"আপনি বর্তমানে <xliff:g id="DEVICE_NAME">%1$s</xliff:g> ডিভাইসে কাস্ট করছেন"</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic" msgid="4100272100480415076">"আপনি বর্তমানে আশেপাশের ডিভাইসে কাস্ট করছেন"</string>
     <string name="cast_to_other_device_stop_dialog_button" msgid="6420183747435521834">"কাস্টিং বন্ধ করুন"</string>
     <string name="close_dialog_button" msgid="4749497706540104133">"বন্ধ করুন"</string>
     <string name="issuerecord_title" msgid="286627115110121849">"Recorder-এ সমস্যা হয়েছে"</string>
@@ -303,8 +290,7 @@
     <string name="start_dreams" msgid="9131802557946276718">"স্ক্রিন সেভার"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"ইথারনেট"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"বিরক্ত করবে না"</string>
-    <!-- no translation found for quick_settings_modes_label (5407025818652750501) -->
-    <skip />
+    <string name="quick_settings_modes_label" msgid="5407025818652750501">"প্রায়োরিটি মোড"</string>
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"ব্লুটুথ"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"চেনা কোনও ডিভাইস নেই"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"কোনও ডিভাইস কানেক্ট বা ডিসকানেক্ট করতে ট্যাপ করুন"</string>
@@ -402,6 +388,9 @@
     <string name="performance" msgid="6552785217174378320">"পারফর্ম্যান্স"</string>
     <string name="user_interface" msgid="3712869377953950887">"ইউজার ইন্টারফেস"</string>
     <string name="thermal" msgid="6758074791325414831">"থার্মাল"</string>
+    <string name="custom" msgid="3337456985275158299">"কাস্টম"</string>
+    <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"কাস্টম ট্রেস সেটিংস"</string>
+    <string name="restore_default" msgid="5259420807486239755">"ডিফল্ট সেটিং ফিরিয়ে আনুন"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"এক হাতে ব্যবহার করার মোড"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"হিয়ারিং ডিভাইস"</string>
     <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"অ্যাক্টিভ"</string>
@@ -440,6 +429,17 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"সেটিংস খুলুন"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"অন্য ডিভাইস"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"\'এক নজরে\' বৈশিষ্ট্যটি চালু বা বন্ধ করুন"</string>
+    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"প্রায়োরিটি মোড"</string>
+    <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_off" msgid="1736604456618147306">"বন্ধ আছে"</string>
+    <!-- no translation found for zen_mode_set_up (7457957033034460064) -->
+    <skip />
+    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
+    <skip />
+    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
+    <skip />
     <string name="zen_priority_introduction" msgid="3159291973383796646">"অ্যালার্ম, রিমাইন্ডার, ইভেন্ট, এবং আপনার নির্দিষ্ট করে দেওয়া ব্যক্তিদের কল ছাড়া অন্য কোনও আওয়াজ বা ভাইব্রেশন হবে না। তবে সঙ্গীত, ভিডিও, এবং গেম সহ আপনি যা কিছু চালাবেন তার আওয়াজ শুনতে পাবেন।"</string>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"অ্যালার্ম ছাড়া অন্য কোনও আওয়াজ বা ভাইব্রেশন হবে না। তবে সঙ্গীত, ভিডিও, এবং গেম সহ আপনি যা কিছু চালাবেন তার আওয়াজ শুনতে পাবেন।"</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"কাস্টমাইজ করুন"</string>
@@ -478,6 +478,8 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ধীরে চার্জ হচ্ছে • পুরো চার্জ হতে <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> লাগবে"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • চার্জ হচ্ছে • পুরো চার্জ হতে আরও <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> সময় লাগবে"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"লক স্ক্রিনে উইজেট"</string>
+    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
+    <skip />
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"কমিউনিটি টিউটোরিয়াল চালু করতে বাঁদিকে সোয়াইপ করুন"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"কাস্টমাইজ করুন"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"বাতিল করুন"</string>
@@ -504,10 +506,9 @@
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"উইজেট বেছে নিন"</string>
     <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"উইজেট সরান"</string>
     <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"বেছে নেওয়া উইজেটটি রাখুন"</string>
-    <!-- no translation found for communal_widget_picker_title (1953369090475731663) -->
-    <skip />
-    <!-- no translation found for communal_widget_picker_description (490515450110487871) -->
-    <skip />
+    <string name="communal_widget_picker_title" msgid="1953369090475731663">"লক স্ক্রিন উইজেট"</string>
+    <string name="communal_widget_picker_description" msgid="490515450110487871">"আপনার ট্যাবলেট লক থাকলেও যেকোনও ব্যক্তি লক স্ক্রিনে উইজেট দেখতে পাবেন।"</string>
+    <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"উইজেট বাদ দিন"</string>
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"লক স্ক্রিন উইজেট"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"উইজেট ব্যবহার করে কোনও অ্যাপ খুলতে, আপনাকে নিজের পরিচয় যাচাই করতে হবে। এছাড়াও, মনে রাখবেন, এমনকি আপনার ট্যাবলেট লক থাকাকালীন যেকেউ তা দেখতে পারবেন। কিছু উইজেট আপনার লক স্ক্রিনের উদ্দেশ্যে তৈরি করা হয়নি এবং এখানে যোগ করা নিরাপদ নাও হতে পারে।"</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"বুঝেছি"</string>
@@ -564,8 +565,8 @@
     <string name="media_projection_action_text" msgid="3634906766918186440">"এখন শুরু করুন"</string>
     <string name="empty_shade_text" msgid="8935967157319717412">"কোনও বিজ্ঞপ্তি নেই"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"নতুন কোনও বিজ্ঞপ্তি নেই"</string>
-    <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"অ্যাডাপ্টিভ বিজ্ঞপ্তি চালু আছে"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"অল্প সময়ে অনেক বেশি বিজ্ঞপ্তি পেলে, আপনার ডিভাইস এখন ২ মিনিটের জন্য ভলিউম ও স্ক্রিনে আসা পপ-আপ কমায়।"</string>
+    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"নোটিফিকেশন কুলডাউন চালু আছে"</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"আপনি একসঙ্গে খুব বেশি বিজ্ঞপ্তি পেলে আপনার ডিভাইসের ভলিউম এবং সতর্কবার্তা সর্বাধিক ২ মিনিটের জন্য অটোমেটিক কমে যায়।"</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"বন্ধ করুন"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"পুরনো বিজ্ঞপ্তি দেখতে আনলক করুন"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"আপনার অভিভাবক এই ডিভাইস ম্যানেজ করেন"</string>
@@ -732,8 +733,7 @@
     <string name="notification_alert_title" msgid="3656229781017543655">"ডিফল্ট"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"অটোমেটিক"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"আওয়াজ করবে না বা ভাইব্রেট হবে না"</string>
-    <!-- no translation found for notification_conversation_summary_low (3855696451919728790) -->
-    <skip />
+    <string name="notification_conversation_summary_low" msgid="3855696451919728790">"কোনও সাউন্ড বা ভাইব্রেশন ছাড়াই কথোপকথন বিভাগে দেখা যাবে"</string>
     <string name="notification_channel_summary_default" msgid="777294388712200605">"ডিভাইসের সেটিংস অনুযায়ী রিং বা ভাইব্রেট হতে পারে"</string>
     <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"ডিভাইসের সেটিংস অনুযায়ী রিং বা ভাইব্রেট হতে পারে। <xliff:g id="APP_NAME">%1$s</xliff:g>-এর কথোপকথন সাধারণত বাবলের মতো দেখাবে।"</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"এই বিজ্ঞপ্তি এলে ডিভাইস আওয়াজ করবে না ভাইব্রেট করবে তা সিস্টেমকে সেট করতে দিন"</string>
@@ -790,8 +790,7 @@
     <string name="keyboard_key_page_up" msgid="173914303254199845">"পেজ আপ"</string>
     <string name="keyboard_key_page_down" msgid="9035902490071829731">"পেজ ডাউন"</string>
     <string name="keyboard_key_forward_del" msgid="5325501825762733459">"মুছুন"</string>
-    <!-- no translation found for keyboard_key_esc (6230365950511411322) -->
-    <skip />
+    <string name="keyboard_key_esc" msgid="6230365950511411322">"Esc"</string>
     <string name="keyboard_key_move_home" msgid="3496502501803911971">"হোম"</string>
     <string name="keyboard_key_move_end" msgid="99190401463834854">"শেষ"</string>
     <string name="keyboard_key_insert" msgid="4621692715704410493">"ঢোকান"</string>
@@ -1233,7 +1232,6 @@
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"যাচাইকরণ করতে হবে। যাচাইকরণ করতে আঙুলের ছাপের সেন্সরে টাচ করুন।"</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"ব্যবহারকারী এখন ফোনে কথা বলছেন"</string>
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"মোবাইল ডেটা"</string>
-    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"কানেক্ট করা আছে"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"সাময়িকভাবে কানেক্ট করা হয়েছে"</string>
     <string name="mobile_data_poor_connection" msgid="819617772268371434">"খারাপ কানেকশন"</string>
@@ -1387,13 +1385,27 @@
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"হয়ে গেছে"</string>
     <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"অসাধারণ!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"ফিরে যান"</string>
-    <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"ফিরে যেতে, টাচপ্যাডের যেকোনও জায়গায় তিনটি আঙুল দিয়ে ডান বা বাঁদিকে সোয়াইপ করুন।"</string>
+    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
+    <skip />
+    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
+    <skip />
     <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"টাচপ্যাডে, তিনটি আঙুল ডান ও বাঁদিকে সরানো দেখানো হচ্ছে"</string>
     <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"ডিভাইসের স্ক্রিনে ফিরে যাওয়ার জেসচারের অ্যানিমেশন দেখানো হচ্ছে"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"কীবোর্ড ব্যাকলাইট"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d-এর মধ্যে %1$d লেভেল"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"হোম কন্ট্রোল"</string>
     <string name="home_controls_dream_description" msgid="4644150952104035789">"স্ক্রিন সেভার হিসেবে ঝটপট \'হোম কন্ট্রোল\' অ্যাক্সেস করুন"</string>
-    <!-- no translation found for volume_undo_action (5815519725211877114) -->
-    <skip />
+    <string name="volume_undo_action" msgid="5815519725211877114">"আগের অবস্থায় ফিরুন"</string>
+    <string name="back_edu_toast_content" msgid="4530314597378982956">"ফিরে যেতে, টাচপ্যাডে তিনটি আঙুল দিয়ে ডান বা বাঁদিকে সোয়াইপ করুন"</string>
+    <string name="home_edu_toast_content" msgid="3381071147871955415">"হোমে যেতে, টাচপ্যাডে তিনটি আঙুল দিয়ে উপরের দিকে সোয়াইপ করুন"</string>
+    <string name="overview_edu_toast_content" msgid="5797030644017804518">"সম্প্রতি ব্যবহার করা অ্যাপ দেখতে, টাচপ্যাডে তিনটি আঙুল ব্যবহার করে উপরের দিকে সোয়াইপ করে ধরে রাখুন"</string>
+    <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"আপনার সব অ্যাপ দেখতে, কীবোর্ডে অ্যাকশন কী প্রেস করুন"</string>
+    <string name="back_edu_notification_title" msgid="5624780717751357278">"ফিরে যেতে টাচপ্যাড ব্যবহার করুন"</string>
+    <string name="back_edu_notification_content" msgid="2497557451540954068">"তিনটি আঙুলের ব্যবহার করে ডান বা বাঁদিকে সোয়াইপ করুন। আরও জেসচার সম্পর্কে জানতে ট্যাপ করুন।"</string>
+    <string name="home_edu_notification_title" msgid="6097902076909654045">"হোমে যেতে টাচপ্যাড ব্যবহার করুন"</string>
+    <string name="home_edu_notification_content" msgid="6631697734535766588">"তিনটি আঙুল ব্যবহার করে উপরের দিকে সোয়াইপ করুন। আরও জেসচার সম্পর্কে জানতে ট্যাপ করুন।"</string>
+    <string name="overview_edu_notification_title" msgid="1265824157319562406">"সম্প্রতি ব্যবহার করা অ্যাপ দেখতে টাচপ্যাড ব্যবহার করুন"</string>
+    <string name="overview_edu_notification_content" msgid="3578204677648432500">"তিনটি আঙুল ব্যবহার করে উপরের দিকে সোয়াইপ করে ধরে রাখুন। আরও জেসচার সম্পর্কে জানতে ট্যাপ করুন।"</string>
+    <string name="all_apps_edu_notification_title" msgid="372262997265569063">"সব অ্যাপ দেখতে আপনার কীবোর্ড ব্যবহার করুন"</string>
+    <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"যেকোনও সময় অ্যাকশন কী প্রেস করুন। আরও জেসচার সম্পর্কে জানতে ট্যাপ করুন।"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-bn/tiles_states_strings.xml b/packages/SystemUI/res/values-bn/tiles_states_strings.xml
index b6336ba..6e4dfbf 100644
--- a/packages/SystemUI/res/values-bn/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-bn/tiles_states_strings.xml
@@ -56,9 +56,6 @@
     <item msgid="5376619709702103243">"বন্ধ আছে"</item>
     <item msgid="4875147066469902392">"চালু আছে"</item>
   </string-array>
-    <!-- no translation found for tile_states_modes:0 (7764936419245199023) -->
-    <!-- no translation found for tile_states_modes:1 (2004750556637773692) -->
-    <!-- no translation found for tile_states_modes:2 (8968530753931637871) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"উপলভ্য নেই"</item>
     <item msgid="5044688398303285224">"বন্ধ আছে"</item>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index 5a85435..2bc13c0 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -126,25 +126,25 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"Dodirnite da vidite"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"Greška prilikom pohranjivanja snimka ekrana"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Greška pri pokretanju snimanja ekrana"</string>
-    <string name="screenrecord_stop_dialog_title" msgid="8716193661764511095">"Želite li zaustaviti snimanje?"</string>
-    <string name="screenrecord_stop_dialog_message" msgid="6262768207331626817">"Trenutačno snimate cijeli zaslon"</string>
-    <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Trenutačno snimate aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="screenrecord_stop_dialog_title" msgid="8716193661764511095">"Zaustaviti snimanje?"</string>
+    <string name="screenrecord_stop_dialog_message" msgid="6262768207331626817">"Trenutno snimate cijeli ekran"</string>
+    <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Trenutno snimate aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Zaustavi snimanje"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Dijeljenje ekrana"</string>
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Zaustaviti dijeljenje ekrana?"</string>
-    <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Trenutačno dijelite cijeli zaslon s aplikacijom <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
-    <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Trenutačno dijelite cijeli zaslon s aplikacijom"</string>
-    <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Trenutačno dijelite aplikaciju <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
-    <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Trenutačno dijelite aplikaciju"</string>
+    <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Trenutno dijelite cijeli ekran s aplikacijom <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
+    <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Trenutno dijelite cijeli ekran s aplikacijom"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Trenutno dijelite aplikaciju <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Trenutno dijelite aplikaciju"</string>
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Zaustavi dijeljenje"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Emitiranje ekrana"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Zaustaviti emitiranje?"</string>
-    <string name="cast_to_other_device_stop_dialog_message_entire_screen_with_device" msgid="1474703115926205251">"Trenutačno emitirate cijeli zaslon na uređaj <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
-    <string name="cast_to_other_device_stop_dialog_message_entire_screen" msgid="8419219169553867625">"Trenutačno emitirate cijeli zaslon na uređaj u blizini"</string>
-    <string name="cast_to_other_device_stop_dialog_message_specific_app_with_device" msgid="2715934698604085519">"Trenutačno emitirate aplikaciju <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> na uređaj <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
-    <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="8616103075630934513">"Trenutačno emitirate aplikaciju <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> na uređaj u blizini"</string>
-    <string name="cast_to_other_device_stop_dialog_message_generic_with_device" msgid="9213582497852420203">"Trenutačno emitirate na uređaj <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
-    <string name="cast_to_other_device_stop_dialog_message_generic" msgid="4100272100480415076">"Trenutačno emitirate na uređaj u blizini"</string>
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen_with_device" msgid="1474703115926205251">"Trenutno emitirate cijeli ekran na uređaju <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen" msgid="8419219169553867625">"Trenutno emitirate cijeli ekran na uređaju u blizini"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app_with_device" msgid="2715934698604085519">"Trenutno emitirate aplikaciju <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> na uređaju <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="8616103075630934513">"Trenutno emitirate aplikaciju <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> na uređaju u blizini"</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic_with_device" msgid="9213582497852420203">"Trenutno emitirate na uređaju <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic" msgid="4100272100480415076">"Trenutno emitirate na uređaju u blizini"</string>
     <string name="cast_to_other_device_stop_dialog_button" msgid="6420183747435521834">"Zaustavi emitiranje"</string>
     <string name="close_dialog_button" msgid="4749497706540104133">"Zatvori"</string>
     <string name="issuerecord_title" msgid="286627115110121849">"Snimač problema"</string>
@@ -290,8 +290,7 @@
     <string name="start_dreams" msgid="9131802557946276718">"Čuvar ekrana"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"Ne ometaj"</string>
-    <!-- no translation found for quick_settings_modes_label (5407025818652750501) -->
-    <skip />
+    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Prioritetni načini rada"</string>
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Nema dostupnih uparenih uređaja"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Dodirnite da povežete ili prekinete povezanost uređaja"</string>
@@ -389,6 +388,9 @@
     <string name="performance" msgid="6552785217174378320">"Performanse"</string>
     <string name="user_interface" msgid="3712869377953950887">"Korisnički interfejs"</string>
     <string name="thermal" msgid="6758074791325414831">"Termalno"</string>
+    <string name="custom" msgid="3337456985275158299">"Prilagođeno"</string>
+    <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Prilagodite postavke traga"</string>
+    <string name="restore_default" msgid="5259420807486239755">"Vrati zadano"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Način rada jednom rukom"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Slušni aparati"</string>
     <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktivno"</string>
@@ -427,6 +429,17 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Otvori Postavke"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Drugi uređaj"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Pregled uključivanja/isključivanja"</string>
+    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Prioritetni načini rada"</string>
+    <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Gotovo"</string>
+    <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Postavke"</string>
+    <string name="zen_mode_on" msgid="9085304934016242591">"Uključeno"</string>
+    <string name="zen_mode_off" msgid="1736604456618147306">"Isključeno"</string>
+    <!-- no translation found for zen_mode_set_up (7457957033034460064) -->
+    <skip />
+    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
+    <skip />
+    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
+    <skip />
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Neće vas ometati zvukovi i vibracije, osim alarma, podsjetnika, događaja i pozivalaca koje odredite. I dalje ćete čuti sve što ste odabrali za reprodukciju, uključujući muziku, videozapise i igre."</string>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Neće vas ometati zvukovi i vibracije, osim alarma. I dalje ćete čuti sve što izaberete za reprodukciju, uključujući muziku, videozapise i igre."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Prilagodi"</string>
@@ -465,6 +478,8 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Sporo punjenje • Potpuna napunjenost za <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Punjenje • Potpuna napunjenost za <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Vidžeti na zaključanom ekranu"</string>
+    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
+    <skip />
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Prevucite ulijevo da pokrenete zajednički vodič"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Prilagodite"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Odbaci"</string>
@@ -491,8 +506,9 @@
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"odabir vidžeta"</string>
     <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"uklanjanje vidžeta"</string>
     <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"postavljanje odabranog vidžeta"</string>
-    <string name="communal_widget_picker_title" msgid="1953369090475731663">"Widgeti zaključanog zaslona"</string>
-    <string name="communal_widget_picker_description" msgid="490515450110487871">"Svi vide widgete na vašem zaključanom zaslonu, čak i ako je tablet zaključan."</string>
+    <string name="communal_widget_picker_title" msgid="1953369090475731663">"Vidžeti na zaključanom ekranu"</string>
+    <string name="communal_widget_picker_description" msgid="490515450110487871">"Svi mogu pregledati vidžete na zaključanom ekranu, čak i ako je tablet zaključan."</string>
+    <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"poništavanje odabira vidžeta"</string>
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Vidžeti zaključanog ekrana"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Da otvorite aplikaciju pomoću vidžeta, morat ćete potvrditi identitet. Također imajte na umu da ih svako može pregledati, čak i ako je tablet zaključan. Neki vidžeti možda nisu namijenjeni za vaš zaključani ekran i njihovo dodavanje ovdje možda nije sigurno."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Razumijem"</string>
@@ -549,8 +565,8 @@
     <string name="media_projection_action_text" msgid="3634906766918186440">"Započni odmah"</string>
     <string name="empty_shade_text" msgid="8935967157319717412">"Nema obavještenja"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"Nema novih obavještenja"</string>
-    <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"Prilagodljiva obavještenja su uključena"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"Uređaj smanjuje jačinu zvuka i broj skočnih prozora na ekranu do dvije minute kada dobijate mnogo obavještenja unutar kratkog vremenskog raspona."</string>
+    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Stišavanje obavještenja je uključeno"</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Jačina zvuka uređaja i obavještenja se automatski stišavaju do 2 minute kada odjednom dobijete previše obavještenja."</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Isključi"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Otključajte da vidite starija obavještenja"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Ovim uređajem upravlja tvoj roditelj"</string>
@@ -717,8 +733,7 @@
     <string name="notification_alert_title" msgid="3656229781017543655">"Zadano"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Automatski"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"Bez zvuka ili vibracije"</string>
-    <!-- no translation found for notification_conversation_summary_low (3855696451919728790) -->
-    <skip />
+    <string name="notification_conversation_summary_low" msgid="3855696451919728790">"Nema zvuka ni vibracije ali se i dalje pojavljuje u odjeljku razgovora"</string>
     <string name="notification_channel_summary_default" msgid="777294388712200605">"Može zvoniti ili vibrirati na osnovu postavki uređaja"</string>
     <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Može zvoniti ili vibrirati na osnovu postavki uređaja. Razgovori iz aplikacije <xliff:g id="APP_NAME">%1$s</xliff:g> prikazuju se u oblačićima prema zadanim postavkama."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Neka sistem odluči treba li se ovo obavještenje oglasiti zvukom ili vibracijom"</string>
@@ -1217,7 +1232,6 @@
     <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>
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Prijenos podataka na mobilnoj mreži"</string>
-    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Povezano"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Privremeno povezano"</string>
     <string name="mobile_data_poor_connection" msgid="819617772268371434">"Slaba veza"</string>
@@ -1371,13 +1385,27 @@
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Gotovo"</string>
     <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Sjajno!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Nazad"</string>
-    <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"Da se vratite, prevucite ulijevo ili udesno s tri prsta bilo gdje na dodirnoj podlozi."</string>
+    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
+    <skip />
+    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
+    <skip />
     <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Dodirna podloga prikazuje tri prsta koji se pomjeraju desno-lijevo"</string>
     <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Ekran uređaja pokazuje animaciju pokreta unazad"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Pozadinsko osvjetljenje tastature"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"%1$d. nivo od %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Kontrole za dom"</string>
     <string name="home_controls_dream_description" msgid="4644150952104035789">"Brzo pristupajte kontrolama za dom putem čuvara ekrana"</string>
-    <!-- no translation found for volume_undo_action (5815519725211877114) -->
-    <skip />
+    <string name="volume_undo_action" msgid="5815519725211877114">"Poništi"</string>
+    <string name="back_edu_toast_content" msgid="4530314597378982956">"Da se vratite, prevucite ulijevo ili udesno s tri prsta na dodirnoj podlozi"</string>
+    <string name="home_edu_toast_content" msgid="3381071147871955415">"Da se vratite na početnu stranicu, prevucite nagore s tri prsta na dodirnoj podlozi"</string>
+    <string name="overview_edu_toast_content" msgid="5797030644017804518">"Da pregledate nedavne aplikacije, prevucite nagore i zadržite s tri prsta na dodirnoj podlozi"</string>
+    <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Da pregledate sve aplikacije, pritisnite tipku radnji na tastaturi"</string>
+    <string name="back_edu_notification_title" msgid="5624780717751357278">"Koristite dodirnu podlogu da se vratite"</string>
+    <string name="back_edu_notification_content" msgid="2497557451540954068">"Prevucite ulijevo ili udesno s tri prsta. Dodirnite da saznate za više pokreta."</string>
+    <string name="home_edu_notification_title" msgid="6097902076909654045">"Koristite dodirnu podlogu da se vratite na početnu stranicu"</string>
+    <string name="home_edu_notification_content" msgid="6631697734535766588">"Prevucite nagore s tri prsta. Dodirnite da saznate za više pokreta."</string>
+    <string name="overview_edu_notification_title" msgid="1265824157319562406">"Koristite dodirnu podlogu da pregledate nedavne aplikacije"</string>
+    <string name="overview_edu_notification_content" msgid="3578204677648432500">"Prevucite nagore i zadržite s tri prsta. Dodirnite da saznate za više pokreta."</string>
+    <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Koristite tastaturu da pregledate sve aplikacije"</string>
+    <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Pritisnite tipku radnji bilo kada. Dodirnite da saznate za više pokreta."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-bs/tiles_states_strings.xml b/packages/SystemUI/res/values-bs/tiles_states_strings.xml
index 6833c27..df0b786 100644
--- a/packages/SystemUI/res/values-bs/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-bs/tiles_states_strings.xml
@@ -56,9 +56,6 @@
     <item msgid="5376619709702103243">"Isključeno"</item>
     <item msgid="4875147066469902392">"Uključeno"</item>
   </string-array>
-    <!-- no translation found for tile_states_modes:0 (7764936419245199023) -->
-    <!-- no translation found for tile_states_modes:1 (2004750556637773692) -->
-    <!-- no translation found for tile_states_modes:2 (8968530753931637871) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Nedostupno"</item>
     <item msgid="5044688398303285224">"Isključeno"</item>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index 2b4e309..a9ab9fc 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -290,8 +290,7 @@
     <string name="start_dreams" msgid="9131802557946276718">"Estalvi de pantalla"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"No molestis"</string>
-    <!-- no translation found for quick_settings_modes_label (5407025818652750501) -->
-    <skip />
+    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Modes prioritaris"</string>
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"No hi ha dispositius vinculats  disponibles"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Toca per connectar o desconnectar un dispositiu"</string>
@@ -389,6 +388,9 @@
     <string name="performance" msgid="6552785217174378320">"Rendiment"</string>
     <string name="user_interface" msgid="3712869377953950887">"Interfície d\'usuari"</string>
     <string name="thermal" msgid="6758074791325414831">"Tèrmic"</string>
+    <string name="custom" msgid="3337456985275158299">"Personalitzat"</string>
+    <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Configuració de traça personalitzada"</string>
+    <string name="restore_default" msgid="5259420807486239755">"Restaura la configuració predeterminada"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Mode d\'una mà"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Audiòfons"</string>
     <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Actiu"</string>
@@ -427,6 +429,17 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Obre Configuració"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Un altre dispositiu"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Activa o desactiva Aplicacions recents"</string>
+    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Modes prioritaris"</string>
+    <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Fet"</string>
+    <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Configuració"</string>
+    <string name="zen_mode_on" msgid="9085304934016242591">"Activat"</string>
+    <string name="zen_mode_off" msgid="1736604456618147306">"Desactivat"</string>
+    <!-- no translation found for zen_mode_set_up (7457957033034460064) -->
+    <skip />
+    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
+    <skip />
+    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
+    <skip />
     <string name="zen_priority_introduction" msgid="3159291973383796646">"No t\'interromprà cap so ni cap vibració, tret dels de les alarmes, recordatoris, esdeveniments i trucades de les persones que especifiquis. Continuaràs sentint tot allò que decideixis reproduir, com ara música, vídeos i jocs."</string>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"No t\'interromprà cap so ni cap vibració, tret dels de les alarmes. Continuaràs sentint tot allò que decideixis reproduir, com ara música, vídeos i jocs."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Personalitza"</string>
@@ -465,6 +478,8 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Carregant lentament • Es completarà d\'aquí a <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • S\'està carregant • Es completarà d\'aquí a <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Widgets a la pantalla de bloqueig"</string>
+    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
+    <skip />
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Llisca cap a l\'esquerra per iniciar el tutorial de la comunitat"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Personalitza"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Ignora"</string>
@@ -493,6 +508,7 @@
     <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"col·loca el widget seleccionat"</string>
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"Widgets de la pantalla de bloqueig"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"Tothom pot veure widgets a la pantalla de bloqueig, fins i tot amb la tauleta bloquejada."</string>
+    <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"desselecciona el widget"</string>
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widgets de la pantalla de bloqueig"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Per obrir una aplicació utilitzant un widget, necessitaràs verificar la teva identitat. També has de tenir en compte que qualsevol persona pot veure els widgets, fins i tot quan la tauleta està bloquejada. És possible que alguns widgets no estiguin pensats per a la pantalla de bloqueig i que no sigui segur afegir-los-hi."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Entesos"</string>
@@ -549,8 +565,8 @@
     <string name="media_projection_action_text" msgid="3634906766918186440">"Comença ara"</string>
     <string name="empty_shade_text" msgid="8935967157319717412">"No hi ha cap notificació"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"No hi ha cap notificació nova"</string>
-    <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"Notif. adaptatives activades"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"El dispositiu abaixa el volum i redueix les notificacions emergents durant un màxim de 2 minuts quan reps moltes notificacions en poc temps."</string>
+    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"La moderació de notificacions està activada"</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"El volum i les alertes del dispositiu es redueixen automàticament durant 2 minuts quan reps massa notificacions alhora."</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Desactiva"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Desbloqueja per veure notif. anteriors"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Els teus pares gestionen aquest dispositiu"</string>
@@ -717,8 +733,7 @@
     <string name="notification_alert_title" msgid="3656229781017543655">"Predeterminat"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Automàtic"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"Sense so ni vibració"</string>
-    <!-- no translation found for notification_conversation_summary_low (3855696451919728790) -->
-    <skip />
+    <string name="notification_conversation_summary_low" msgid="3855696451919728790">"Sense so ni vibració, però encara apareix a la secció de converses"</string>
     <string name="notification_channel_summary_default" msgid="777294388712200605">"Pot sonar o vibrar en funció de la configuració del dispositiu"</string>
     <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Pot sonar o vibrar en funció de la configuració del dispositiu. Les converses de l\'aplicació <xliff:g id="APP_NAME">%1$s</xliff:g> es mostren com a bombolles de manera predeterminada."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Fes que el sistema determini si aquesta notificació ha d\'emetre un so o una vibració"</string>
@@ -1217,7 +1232,6 @@
     <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>
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Dades mòbils"</string>
-    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Connectat"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Connexió temporal"</string>
     <string name="mobile_data_poor_connection" msgid="819617772268371434">"Connexió feble"</string>
@@ -1358,8 +1372,7 @@
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Pantalla dividida"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Entrada"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Dreceres d\'aplicacions"</string>
-    <!-- no translation found for shortcut_helper_category_current_app_shortcuts (4017840565974573628) -->
-    <skip />
+    <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Aplicació actual"</string>
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accessibilitat"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Tecles de drecera"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Dreceres de cerca"</string>
@@ -1372,13 +1385,27 @@
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Fet"</string>
     <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Ben fet!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Torna"</string>
-    <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"Per tornar enrere, llisca cap a l\'esquerra o cap a la dreta amb tres dits en qualsevol lloc del ratolí tàctil."</string>
+    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
+    <skip />
+    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
+    <skip />
     <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Ratolí tàctil que mostra tres dits que es mouen cap a la dreta i cap a l\'esquerra"</string>
     <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Pantalla del dispositiu que mostra l\'animació del gest per anar enrere"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Retroil·luminació del teclat"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Nivell %1$d de %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Controls de la llar"</string>
     <string name="home_controls_dream_description" msgid="4644150952104035789">"Utilitza controls de la llar com a estalvi de pantalla"</string>
-    <!-- no translation found for volume_undo_action (5815519725211877114) -->
-    <skip />
+    <string name="volume_undo_action" msgid="5815519725211877114">"Desfés"</string>
+    <string name="back_edu_toast_content" msgid="4530314597378982956">"Per tornar enrere, llisca tres dits cap a l\'esquerra o cap a la dreta al ratolí tàctil"</string>
+    <string name="home_edu_toast_content" msgid="3381071147871955415">"Per anar a la pantalla d\'inici, llisca tres dits cap amunt al ratolí tàctil"</string>
+    <string name="overview_edu_toast_content" msgid="5797030644017804518">"Per veure les aplicacions recents, llisca cap amunt amb tres dits i mantén premut al ratolí tàctil"</string>
+    <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Per veure totes les aplicacions, prem la tecla d\'acció al teclat"</string>
+    <string name="back_edu_notification_title" msgid="5624780717751357278">"Utilitza el ratolí tàctil per tornar enrere"</string>
+    <string name="back_edu_notification_content" msgid="2497557451540954068">"Fes lliscar tres dits cap a l\'esquerra o cap a la dreta. Toca per aprendre més gestos."</string>
+    <string name="home_edu_notification_title" msgid="6097902076909654045">"Utilitza el ratolí tàctil per anar a la pantalla d\'inici"</string>
+    <string name="home_edu_notification_content" msgid="6631697734535766588">"Llisca tres dits cap amunt. Toca per aprendre més gestos."</string>
+    <string name="overview_edu_notification_title" msgid="1265824157319562406">"Utilitza el ratolí tàctil per veure les aplicacions recents"</string>
+    <string name="overview_edu_notification_content" msgid="3578204677648432500">"Llisca cap amunt amb tres dits i mantén premut. Toca per aprendre més gestos."</string>
+    <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Utilitza el teclat per veure totes les aplicacions"</string>
+    <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Prem la tecla d\'acció en qualsevol moment. Toca per aprendre més gestos."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ca/tiles_states_strings.xml b/packages/SystemUI/res/values-ca/tiles_states_strings.xml
index 3b908fd..67eb853 100644
--- a/packages/SystemUI/res/values-ca/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ca/tiles_states_strings.xml
@@ -56,9 +56,6 @@
     <item msgid="5376619709702103243">"Desactivat"</item>
     <item msgid="4875147066469902392">"Activat"</item>
   </string-array>
-    <!-- no translation found for tile_states_modes:0 (7764936419245199023) -->
-    <!-- no translation found for tile_states_modes:1 (2004750556637773692) -->
-    <!-- no translation found for tile_states_modes:2 (8968530753931637871) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"No disponible"</item>
     <item msgid="5044688398303285224">"Desactivat"</item>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index 992b35b..194057a 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -126,38 +126,25 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"Klepnutím nahrávku zobrazíte"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"Při ukládání záznamu obrazovky došlo k chybě"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Při spouštění nahrávání obrazovky došlo k chybě"</string>
-    <!-- no translation found for screenrecord_stop_dialog_title (8716193661764511095) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message (6262768207331626817) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5995770227684523244) -->
-    <skip />
+    <string name="screenrecord_stop_dialog_title" msgid="8716193661764511095">"Zastavit nahrávání?"</string>
+    <string name="screenrecord_stop_dialog_message" msgid="6262768207331626817">"Momentálně nahráváte celou obrazovku"</string>
+    <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Momentálně nahráváte aplikaci <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Ukončit nahrávání"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Sdílení obrazovky"</string>
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Ukončit sdílení obrazovky?"</string>
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen_with_host_app (522823522115375414) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen (5090115386271179270) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_specific (5923772039347985172) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_generic (6681016774654578261) -->
-    <skip />
+    <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Momentálně sdílíte celou obrazovku s aplikací <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
+    <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Momentálně sdílíte celou obrazovku s aplikací"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Momentálně sdílíte aplikaci <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Momentálně sdílíte aplikaci"</string>
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Ukončit sdílení"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Odesílání obsahu obrazovky"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Ukončit odesílání?"</string>
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen_with_device (1474703115926205251) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen (8419219169553867625) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app_with_device (2715934698604085519) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (8616103075630934513) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic_with_device (9213582497852420203) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic (4100272100480415076) -->
-    <skip />
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen_with_device" msgid="1474703115926205251">"Momentálně odesíláte celou obrazovku do zařízení <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen" msgid="8419219169553867625">"Momentálně odesíláte celou obrazovku do zařízení v okolí"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app_with_device" msgid="2715934698604085519">"Momentálně odesíláte aplikaci <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> do zařízení <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="8616103075630934513">"Momentálně odesíláte aplikaci <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> do zařízení v okolí"</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic_with_device" msgid="9213582497852420203">"Momentálně odesíláte do zařízení <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic" msgid="4100272100480415076">"Momentálně odesíláte do zařízení v okolí"</string>
     <string name="cast_to_other_device_stop_dialog_button" msgid="6420183747435521834">"Ukončit odesílání"</string>
     <string name="close_dialog_button" msgid="4749497706540104133">"Zavřít"</string>
     <string name="issuerecord_title" msgid="286627115110121849">"Rekordér problémů"</string>
@@ -303,8 +290,7 @@
     <string name="start_dreams" msgid="9131802557946276718">"Spořič obrazovky"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"Nerušit"</string>
-    <!-- no translation found for quick_settings_modes_label (5407025818652750501) -->
-    <skip />
+    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Režimy priority"</string>
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Nejsou dostupná žádná spárovaná zařízení"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Klepnutím zařízení připojíte nebo odpojíte"</string>
@@ -402,6 +388,9 @@
     <string name="performance" msgid="6552785217174378320">"Výkon"</string>
     <string name="user_interface" msgid="3712869377953950887">"Uživatelské rozhraní"</string>
     <string name="thermal" msgid="6758074791325414831">"Termovize"</string>
+    <string name="custom" msgid="3337456985275158299">"Vlastní"</string>
+    <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Vlastní nastavení trasování"</string>
+    <string name="restore_default" msgid="5259420807486239755">"Obnovit výchozí"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Režim jedné ruky"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Naslouchátka"</string>
     <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktivní"</string>
@@ -440,6 +429,17 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Otevřít nastavení"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Další zařízení"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Přepnout přehled"</string>
+    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Režimy priority"</string>
+    <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Hotovo"</string>
+    <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Nastavení"</string>
+    <string name="zen_mode_on" msgid="9085304934016242591">"Zapnuto"</string>
+    <string name="zen_mode_off" msgid="1736604456618147306">"Vypnuto"</string>
+    <!-- no translation found for zen_mode_set_up (7457957033034460064) -->
+    <skip />
+    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
+    <skip />
+    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
+    <skip />
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Nebudou vás rušit zvuky ani vibrace s výjimkou budíků, upozornění, událostí a volajících, které zadáte. Nadále uslyšíte veškerý obsah, který si sami pustíte (např. hudba, videa nebo hry)."</string>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Nebudou vás rušit zvuky ani vibrace s výjimkou budíků. Nadále uslyšíte veškerý obsah, který si sami pustíte (např. hudba, videa nebo hry)."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Přizpůsobit"</string>
@@ -478,6 +478,8 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Pomalé nabíjení • Plně nabito za <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Nabíjení • Plně nabito za <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Widgety na obrazovce uzamčení"</string>
+    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
+    <skip />
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Přejetím doleva spustíte komunitní výukový program"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Přizpůsobit"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Zavřít"</string>
@@ -504,10 +506,9 @@
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"vybrat widget"</string>
     <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"odstranit widget"</string>
     <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"umístit vybraný widget"</string>
-    <!-- no translation found for communal_widget_picker_title (1953369090475731663) -->
-    <skip />
-    <!-- no translation found for communal_widget_picker_description (490515450110487871) -->
-    <skip />
+    <string name="communal_widget_picker_title" msgid="1953369090475731663">"Widgety na obrazovce uzamčení"</string>
+    <string name="communal_widget_picker_description" msgid="490515450110487871">"Widgety na obrazovce uzamčení může zobrazit kdokoli, i když je tablet uzamčen."</string>
+    <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"zrušit výběr widgetu"</string>
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widgety na obrazovce uzamčení"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"K otevření aplikace pomocí widgetu budete muset ověřit svou totožnost. Také mějte na paměti, že widgety uvidí kdokoli, i když tablet bude uzamčen. Některé widgety nemusí být pro obrazovku uzamčení určeny a nemusí být bezpečné je na ni přidat."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Rozumím"</string>
@@ -564,8 +565,8 @@
     <string name="media_projection_action_text" msgid="3634906766918186440">"Spustit"</string>
     <string name="empty_shade_text" msgid="8935967157319717412">"Žádná oznámení"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"Žádná nová oznámení"</string>
-    <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"Jsou zapnutá adaptivní oznámení"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"Když během krátké chvíle obdržíte mnoho oznámení, zařízení teď až na dvě minuty sníží hlasitost a omezí na obrazovce vyskakovací okna."</string>
+    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Je zapnuté zeslabení oznámení"</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Když dostanete příliš mnoho oznámení najednou, až na dvě minuty se sníží hlasitost zařízení a oznámení se omezí."</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Vypnout"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Starší oznámení se zobrazí po odemknutí"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Toto zařízení spravuje rodič"</string>
@@ -732,8 +733,7 @@
     <string name="notification_alert_title" msgid="3656229781017543655">"Výchozí"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Automaticky"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"Žádný zvuk ani vibrace"</string>
-    <!-- no translation found for notification_conversation_summary_low (3855696451919728790) -->
-    <skip />
+    <string name="notification_conversation_summary_low" msgid="3855696451919728790">"Bez zvuku a vibrace, ale nadále se bude zobrazovat v sekci konverzací"</string>
     <string name="notification_channel_summary_default" msgid="777294388712200605">"Vyzvání nebo vibruje podle nastavení zařízení"</string>
     <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Vyzvání nebo vibruje podle nastavení zařízení. Konverzace z aplikace <xliff:g id="APP_NAME">%1$s</xliff:g> ve výchozím nastavení bublají."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Nechat systém rozhodnout, zda má toto oznámení vydat zvuk či zavibrovat"</string>
@@ -790,8 +790,7 @@
     <string name="keyboard_key_page_up" msgid="173914303254199845">"Page Up"</string>
     <string name="keyboard_key_page_down" msgid="9035902490071829731">"Page Down"</string>
     <string name="keyboard_key_forward_del" msgid="5325501825762733459">"Delete"</string>
-    <!-- no translation found for keyboard_key_esc (6230365950511411322) -->
-    <skip />
+    <string name="keyboard_key_esc" msgid="6230365950511411322">"Esc"</string>
     <string name="keyboard_key_move_home" msgid="3496502501803911971">"Home"</string>
     <string name="keyboard_key_move_end" msgid="99190401463834854">"End"</string>
     <string name="keyboard_key_insert" msgid="4621692715704410493">"Insert"</string>
@@ -1233,7 +1232,6 @@
     <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>
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobilní data"</string>
-    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</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>
     <string name="mobile_data_poor_connection" msgid="819617772268371434">"Nekvalitní připojení"</string>
@@ -1374,8 +1372,7 @@
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Rozdělená obrazovka"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Vstup"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Zkratky aplikací"</string>
-    <!-- no translation found for shortcut_helper_category_current_app_shortcuts (4017840565974573628) -->
-    <skip />
+    <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Aktuální aplikace"</string>
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Přístupnost"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Klávesové zkratky"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Vyhledat zkratky"</string>
@@ -1388,13 +1385,27 @@
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Hotovo"</string>
     <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Výborně!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Zpět"</string>
-    <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"Pokud se chcete vrátit zpět, stačí kdekoli na touchpadu přejet třemi prsty doleva nebo doprava."</string>
+    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
+    <skip />
+    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
+    <skip />
     <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Touchpad se třemi prsty, které se pohybují doprava a doleva"</string>
     <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Obrazovka zařízení s animaci gesta k vrácení zpět"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Podsvícení klávesnice"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Úroveň %1$d z %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Ovládání domácnosti"</string>
     <string name="home_controls_dream_description" msgid="4644150952104035789">"Rychlý přístup k funkcím, jako je spořič obrazovky"</string>
-    <!-- no translation found for volume_undo_action (5815519725211877114) -->
-    <skip />
+    <string name="volume_undo_action" msgid="5815519725211877114">"Vrátit zpět"</string>
+    <string name="back_edu_toast_content" msgid="4530314597378982956">"Pokud se chcete vrátit zpět, přejeďte po touchpadu třemi prsty doleva nebo doprava"</string>
+    <string name="home_edu_toast_content" msgid="3381071147871955415">"Pokud se chcete vrátit domů, přejeďte po touchpadu třemi prsty nahoru"</string>
+    <string name="overview_edu_toast_content" msgid="5797030644017804518">"Pokud chcete zobrazit poslední aplikace, přejeďte na touchpadu třemi prsty nahoru a podržte je"</string>
+    <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Pokud chcete zobrazit všechny aplikace, stiskněte na klávesnici akční klávesu"</string>
+    <string name="back_edu_notification_title" msgid="5624780717751357278">"Návrat zpět pomocí touchpadu"</string>
+    <string name="back_edu_notification_content" msgid="2497557451540954068">"Přejeďte třemi prsty doleva nebo doprava. Další gesta zjistíte klepnutím."</string>
+    <string name="home_edu_notification_title" msgid="6097902076909654045">"Návrat domů pomocí touchpadu"</string>
+    <string name="home_edu_notification_content" msgid="6631697734535766588">"Přejeďte třemi prsty nahoru. Další gesta zjistíte klepnutím."</string>
+    <string name="overview_edu_notification_title" msgid="1265824157319562406">"Zobrazení posledních aplikací pomocí touchpadu"</string>
+    <string name="overview_edu_notification_content" msgid="3578204677648432500">"Přejeďte třemi prsty nahoru a podržte je. Další gesta zjistíte klepnutím."</string>
+    <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Zobrazení všech aplikací pomocí klávesnice"</string>
+    <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Kdykoli stiskněte akční klávesu. Další gesta zjistíte klepnutím."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-cs/tiles_states_strings.xml b/packages/SystemUI/res/values-cs/tiles_states_strings.xml
index c4d7388..ae533a8 100644
--- a/packages/SystemUI/res/values-cs/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-cs/tiles_states_strings.xml
@@ -56,9 +56,6 @@
     <item msgid="5376619709702103243">"Vypnuto"</item>
     <item msgid="4875147066469902392">"Zapnuto"</item>
   </string-array>
-    <!-- no translation found for tile_states_modes:0 (7764936419245199023) -->
-    <!-- no translation found for tile_states_modes:1 (2004750556637773692) -->
-    <!-- no translation found for tile_states_modes:2 (8968530753931637871) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Nedostupné"</item>
     <item msgid="5044688398303285224">"Vypnuto"</item>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index 2750b42..ba1babf 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -126,38 +126,25 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"Tryk for at se"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"Skærmoptagelsen kunne ikke gemmes"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Skærmoptagelsen kunne ikke startes"</string>
-    <!-- no translation found for screenrecord_stop_dialog_title (8716193661764511095) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message (6262768207331626817) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5995770227684523244) -->
-    <skip />
+    <string name="screenrecord_stop_dialog_title" msgid="8716193661764511095">"Vil du stoppe optagelsen?"</string>
+    <string name="screenrecord_stop_dialog_message" msgid="6262768207331626817">"Du optager i øjeblikket hele skærmen"</string>
+    <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Du optager i øjeblikket <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Stop optagelse"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Skærmen deles"</string>
-    <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Stop skærmdelingen?"</string>
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen_with_host_app (522823522115375414) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen (5090115386271179270) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_specific (5923772039347985172) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_generic (6681016774654578261) -->
-    <skip />
+    <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Vil du stoppe skærmdelingen?"</string>
+    <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Du deler i øjeblikket hele skærmen med <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
+    <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Du deler i øjeblikket hele skærmen med en app"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Du deler i øjeblikket <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Du deler i øjeblikket en app"</string>
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Stop deling"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Skærmen castes"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Vil du stoppe din cast?"</string>
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen_with_device (1474703115926205251) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen (8419219169553867625) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app_with_device (2715934698604085519) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (8616103075630934513) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic_with_device (9213582497852420203) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic (4100272100480415076) -->
-    <skip />
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen_with_device" msgid="1474703115926205251">"Du caster i øjeblikket hele skærmen til <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen" msgid="8419219169553867625">"Du caster i øjeblikket hele skærmen til en enhed i nærheden"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app_with_device" msgid="2715934698604085519">"Du caster i øjeblikket <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> til <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="8616103075630934513">"Du caster i øjeblikket <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> til en enhed i nærheden"</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic_with_device" msgid="9213582497852420203">"Du caster i øjeblikket til <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic" msgid="4100272100480415076">"Du caster i øjeblikket til en enhed i nærheden"</string>
     <string name="cast_to_other_device_stop_dialog_button" msgid="6420183747435521834">"Stop cast"</string>
     <string name="close_dialog_button" msgid="4749497706540104133">"Luk"</string>
     <string name="issuerecord_title" msgid="286627115110121849">"Problemoptagelse"</string>
@@ -231,13 +218,13 @@
     <string name="biometric_re_enroll_notification_content" msgid="8685925877186288180">"Dette er påkrævet for at forbedre sikkerheden og ydeevnen"</string>
     <string name="fingerprint_re_enroll_notification_title" msgid="4539432429683916604">"Konfigurer fingeroplåsning igen"</string>
     <string name="fingerprint_re_enroll_notification_name" msgid="630798657797645704">"Fingeroplåsning"</string>
-    <string name="fingerprint_re_enroll_dialog_title" msgid="3526033128113925780">"Konfigurer fingeroplåsning"</string>
+    <string name="fingerprint_re_enroll_dialog_title" msgid="3526033128113925780">"Konfigurer finger­oplåsning"</string>
     <string name="fingerprint_re_enroll_dialog_content" msgid="4866561176695984879">"Hvis du vil konfigurere fingeroplåsning igen, bliver dine nuværende fingeraftryksbilleder og -modeller slettet.\n\nNår de er slettet, skal du konfigurere fingeroplåsning igen for at bruge dit fingeraftryk til at låse din telefon op eller verificere din identitet."</string>
     <string name="fingerprint_re_enroll_dialog_content_singular" msgid="3083663339787381218">"Hvis du vil konfigurere fingeroplåsning igen, bliver dine nuværende fingeraftryksbilleder og -modeller slettet.\n\nNår de er slettet, skal du konfigurere fingeroplåsning igen for at bruge dit fingeraftryk til at låse din telefon op eller verificere din identitet."</string>
     <string name="fingerprint_reenroll_failure_dialog_content" msgid="4733768492747300666">"Fingeroplåsning kunne ikke konfigureres. Gå til Indstillinger for at prøve igen."</string>
     <string name="face_re_enroll_notification_title" msgid="1850838867718410520">"Konfigurer ansigtsoplåsning igen"</string>
     <string name="face_re_enroll_notification_name" msgid="7384545252206120659">"Ansigtsoplåsning"</string>
-    <string name="face_re_enroll_dialog_title" msgid="6392173708176069994">"Konfigurer ansigtsoplåsning"</string>
+    <string name="face_re_enroll_dialog_title" msgid="6392173708176069994">"Konfigurer ansigts­oplåsning"</string>
     <string name="face_re_enroll_dialog_content" msgid="7353502359464038511">"Hvis du vil konfigurere ansigtsoplåsning igen, bliver din nuværende ansigtsmodel slettet.\n\nDu skal konfigurere funktionen igen for at bruge ansigtsgenkendelse til at låse din telefon op."</string>
     <string name="face_reenroll_failure_dialog_content" msgid="7073947334397236935">"Ansigtsoplåsning kunne ikke konfigureres. Gå til Indstillinger for at prøve igen."</string>
     <string name="fingerprint_dialog_touch_sensor" msgid="2817887108047658975">"Sæt fingeren på fingeraftrykssensoren"</string>
@@ -303,8 +290,7 @@
     <string name="start_dreams" msgid="9131802557946276718">"Pauseskærm"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"Forstyr ikke"</string>
-    <!-- no translation found for quick_settings_modes_label (5407025818652750501) -->
-    <skip />
+    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Tilstande med prioritet"</string>
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Der er ingen tilgængelige parrede enheder"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Tryk for at oprette eller afbryde forbindelse til en enhed"</string>
@@ -402,6 +388,9 @@
     <string name="performance" msgid="6552785217174378320">"Ydeevne"</string>
     <string name="user_interface" msgid="3712869377953950887">"Brugerflade"</string>
     <string name="thermal" msgid="6758074791325414831">"Termisk"</string>
+    <string name="custom" msgid="3337456985275158299">"Tilpas"</string>
+    <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Tilpas indstillinger for registrering"</string>
+    <string name="restore_default" msgid="5259420807486239755">"Gendan standard"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Enhåndstilstand"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Høreapparater"</string>
     <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktivt"</string>
@@ -440,6 +429,17 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Åbn Indstillinger"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Anden enhed"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Slå Oversigt til/fra"</string>
+    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Tilstande med prioritet"</string>
+    <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Udfør"</string>
+    <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Indstillinger"</string>
+    <string name="zen_mode_on" msgid="9085304934016242591">"Til"</string>
+    <string name="zen_mode_off" msgid="1736604456618147306">"Fra"</string>
+    <!-- no translation found for zen_mode_set_up (7457957033034460064) -->
+    <skip />
+    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
+    <skip />
+    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
+    <skip />
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Du bliver ikke forstyrret af lyde eller vibrationer, undtagen fra alarmer, påmindelser, begivenheder og opkald fra udvalgte personer, du selv angiver. Du kan stadig høre alt, du vælger at afspille, f.eks. musik, videoer og spil."</string>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Du bliver ikke forstyrret af lyde eller vibrationer, undtagen fra alarmer. Du kan stadig høre alt, du vælger at afspille, f.eks. musik, videoer og spil."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Tilpas"</string>
@@ -478,6 +478,8 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Oplader langsomt • Fuldt opladet om <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Oplader • Fuldt opladet om <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Widgets på låseskærmen"</string>
+    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
+    <skip />
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Stryg mod venstre for at starte den fælles vejledning"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Tilpas"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Luk"</string>
@@ -504,10 +506,9 @@
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"vælg widget"</string>
     <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"fjern widget"</string>
     <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"placer valgt widget"</string>
-    <!-- no translation found for communal_widget_picker_title (1953369090475731663) -->
-    <skip />
-    <!-- no translation found for communal_widget_picker_description (490515450110487871) -->
-    <skip />
+    <string name="communal_widget_picker_title" msgid="1953369090475731663">"Widgets på låseskærmen"</string>
+    <string name="communal_widget_picker_description" msgid="490515450110487871">"Alle kan se widgets på din låseskærm, også selvom din tablet er låst."</string>
+    <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"fjern markering af widget"</string>
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widgets på låseskærmen"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Hvis du vil åbne en app ved hjælp af en widget, skal du verificere din identitet. Husk også, at alle kan se dem, også når din tablet er låst. Nogle widgets er muligvis ikke beregnet til låseskærmen, og det kan være usikkert at tilføje dem her."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"OK"</string>
@@ -564,8 +565,8 @@
     <string name="media_projection_action_text" msgid="3634906766918186440">"Start nu"</string>
     <string name="empty_shade_text" msgid="8935967157319717412">"Ingen notifikationer"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"Ingen nye notifikationer"</string>
-    <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"Adaptive notifikationer er aktiveret"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"Din enhed skruer nu ned for lydstyrken og reducerer pop op-vinduer på skærmen i op til to minutter, når du modtager mange notifikationer over kort tid."</string>
+    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Dæmpning af notifikationer er aktiveret"</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Enheden skruer automatisk ned for lydstyrken og minimerer underretninger på skærmen i op til 2 minutter, når du får for mange notifikationer på én gang."</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Deaktiver"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Lås op for at se ældre notifikationer"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Denne enhed administreres af din forælder"</string>
@@ -732,8 +733,7 @@
     <string name="notification_alert_title" msgid="3656229781017543655">"Standard"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Automatisk"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"Ingen lyd eller vibration"</string>
-    <!-- no translation found for notification_conversation_summary_low (3855696451919728790) -->
-    <skip />
+    <string name="notification_conversation_summary_low" msgid="3855696451919728790">"Ingen lyd eller vibration, men vises stadig i samtalesektionen"</string>
     <string name="notification_channel_summary_default" msgid="777294388712200605">"Kan ringe eller vibrere baseret på enhedens indstillinger"</string>
     <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Kan ringe eller vibrere baseret på enhedens indstillinger. Samtaler fra <xliff:g id="APP_NAME">%1$s</xliff:g> vises som standard i bobler."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Få systemet til at afgøre, om denne notifikation skal vibrere eller afspille en lyd"</string>
@@ -790,8 +790,7 @@
     <string name="keyboard_key_page_up" msgid="173914303254199845">"Page Up"</string>
     <string name="keyboard_key_page_down" msgid="9035902490071829731">"Page Down"</string>
     <string name="keyboard_key_forward_del" msgid="5325501825762733459">"Delete"</string>
-    <!-- no translation found for keyboard_key_esc (6230365950511411322) -->
-    <skip />
+    <string name="keyboard_key_esc" msgid="6230365950511411322">"Esc"</string>
     <string name="keyboard_key_move_home" msgid="3496502501803911971">"Home"</string>
     <string name="keyboard_key_move_end" msgid="99190401463834854">"End"</string>
     <string name="keyboard_key_insert" msgid="4621692715704410493">"Insert"</string>
@@ -1233,7 +1232,6 @@
     <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>
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobildata"</string>
-    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Forbundet"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Midlertidigt forbundet"</string>
     <string name="mobile_data_poor_connection" msgid="819617772268371434">"Dårlig forbindelse"</string>
@@ -1374,8 +1372,7 @@
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Opdelt skærm"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Input"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Appgenveje"</string>
-    <!-- no translation found for shortcut_helper_category_current_app_shortcuts (4017840565974573628) -->
-    <skip />
+    <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Aktuel app"</string>
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Hjælpefunktioner"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Tastaturgenveje"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Genveje til søgning"</string>
@@ -1388,13 +1385,27 @@
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Udfør"</string>
     <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Sådan!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Gå tilbage"</string>
-    <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"Du kan gå tilbage ved at stryge mod venstre eller højre med tre fingre et vilkårligt sted på touchpladen."</string>
+    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
+    <skip />
+    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
+    <skip />
     <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Touchplade viser tre fingre, der bevæger sig til højre og venstre"</string>
     <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Enhedsskærm, der viser en animation, som demonstrerer, hvordan man går tilbage"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Tastaturets baggrundslys"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Niveau %1$d af %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Hjemmestyring"</string>
     <string name="home_controls_dream_description" msgid="4644150952104035789">"Tilgå hurtigt hjemmestyring via din pauseskærm"</string>
-    <!-- no translation found for volume_undo_action (5815519725211877114) -->
-    <skip />
+    <string name="volume_undo_action" msgid="5815519725211877114">"Fortryd"</string>
+    <string name="back_edu_toast_content" msgid="4530314597378982956">"Du kan gå tilbage ved at stryge til venstre eller højre med tre fingre på touchpladen"</string>
+    <string name="home_edu_toast_content" msgid="3381071147871955415">"Du kan gå til startskærmen ved at stryge opad med tre fingre på touchpladen"</string>
+    <string name="overview_edu_toast_content" msgid="5797030644017804518">"Du kan se nyligt brugte apps ved at stryge opad og holde tre fingre nede på touchpladen"</string>
+    <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Du kan se alle dine apps ved at trykke på handlingstasten på dit tastatur"</string>
+    <string name="back_edu_notification_title" msgid="5624780717751357278">"Brug din touchplade til at gå tilbage"</string>
+    <string name="back_edu_notification_content" msgid="2497557451540954068">"Stryg til venstre eller højre med tre fingre. Tryk for at lære flere bevægelser."</string>
+    <string name="home_edu_notification_title" msgid="6097902076909654045">"Brug din touchplade til at gå til startskærmen"</string>
+    <string name="home_edu_notification_content" msgid="6631697734535766588">"Stryg opad med tre fingre. Tryk for at lære flere bevægelser."</string>
+    <string name="overview_edu_notification_title" msgid="1265824157319562406">"Brug din touchplade til at se nyligt brugte apps"</string>
+    <string name="overview_edu_notification_content" msgid="3578204677648432500">"Stryg opad, og hold tre fingre nede. Tryk for at lære flere bevægelser."</string>
+    <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Brug dit tastatur til at se alle apps"</string>
+    <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Tryk på handlingstasten når som helst. Tryk for at lære flere bevægelser."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-da/tiles_states_strings.xml b/packages/SystemUI/res/values-da/tiles_states_strings.xml
index 3a9533a..2c3b053 100644
--- a/packages/SystemUI/res/values-da/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-da/tiles_states_strings.xml
@@ -56,9 +56,6 @@
     <item msgid="5376619709702103243">"Fra"</item>
     <item msgid="4875147066469902392">"Til"</item>
   </string-array>
-    <!-- no translation found for tile_states_modes:0 (7764936419245199023) -->
-    <!-- no translation found for tile_states_modes:1 (2004750556637773692) -->
-    <!-- no translation found for tile_states_modes:2 (8968530753931637871) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Ikke tilgængelig"</item>
     <item msgid="5044688398303285224">"Fra"</item>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index 710ae57..62ede78 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -126,38 +126,25 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"Zum Ansehen tippen"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"Fehler beim Speichern der Bildschirmaufzeichnung"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Fehler beim Start der Bildschirmaufzeichnung"</string>
-    <!-- no translation found for screenrecord_stop_dialog_title (8716193661764511095) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message (6262768207331626817) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5995770227684523244) -->
-    <skip />
+    <string name="screenrecord_stop_dialog_title" msgid="8716193661764511095">"Aufzeichnung beenden?"</string>
+    <string name="screenrecord_stop_dialog_message" msgid="6262768207331626817">"Du zeichnest momentan deinen gesamten Bildschirm auf"</string>
+    <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Du zeichnest momentan Inhalte der <xliff:g id="APP_NAME">%1$s</xliff:g> auf"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Aufnahme beenden"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Bildschirm wird geteilt"</string>
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Bildschirmfreigabe beenden?"</string>
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen_with_host_app (522823522115375414) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen (5090115386271179270) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_specific (5923772039347985172) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_generic (6681016774654578261) -->
-    <skip />
+    <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Du teilst momentan deinen gesamten Bildschirm mit der <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
+    <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Du teilst momentan deinen gesamten Bildschirm mit einer App"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Du teilst momentan Inhalte der <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Du teilst momentan Inhalte einer App"</string>
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Freigabe beenden"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Bildschirm wird übertragen"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Übertragung abbrechen?"</string>
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen_with_device (1474703115926205251) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen (8419219169553867625) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app_with_device (2715934698604085519) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (8616103075630934513) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic_with_device (9213582497852420203) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic (4100272100480415076) -->
-    <skip />
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen_with_device" msgid="1474703115926205251">"Du überträgst momentan deinen gesamten Bildschirm auf das Gerät „<xliff:g id="DEVICE_NAME">%1$s</xliff:g>“"</string>
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen" msgid="8419219169553867625">"Du überträgst momentan deinen gesamten Bildschirm auf ein Gerät in der Nähe"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app_with_device" msgid="2715934698604085519">"Du überträgst momentan Inhalte der <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> auf das Gerät „<xliff:g id="DEVICE_NAME">%2$s</xliff:g>“"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="8616103075630934513">"Du überträgst momentan Inhalte der <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> auf ein Gerät in der Nähe"</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic_with_device" msgid="9213582497852420203">"Du überträgst momentan Inhalte auf das Gerät „<xliff:g id="DEVICE_NAME">%1$s</xliff:g>“"</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic" msgid="4100272100480415076">"Du überträgst momentan Inhalte auf ein Gerät in der Nähe"</string>
     <string name="cast_to_other_device_stop_dialog_button" msgid="6420183747435521834">"Streaming beenden"</string>
     <string name="close_dialog_button" msgid="4749497706540104133">"Schließen"</string>
     <string name="issuerecord_title" msgid="286627115110121849">"Problem aufzeichnen"</string>
@@ -303,8 +290,7 @@
     <string name="start_dreams" msgid="9131802557946276718">"Bildschirmschoner"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"Bitte nicht stören"</string>
-    <!-- no translation found for quick_settings_modes_label (5407025818652750501) -->
-    <skip />
+    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Prioritätsmodi"</string>
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Keine gekoppelten Geräte verfügbar"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Zum Verbinden oder Trennen eines Geräts tippen"</string>
@@ -402,6 +388,9 @@
     <string name="performance" msgid="6552785217174378320">"Leistung"</string>
     <string name="user_interface" msgid="3712869377953950887">"Benutzeroberfläche"</string>
     <string name="thermal" msgid="6758074791325414831">"Überhitzung"</string>
+    <string name="custom" msgid="3337456985275158299">"Benutzerdefiniert"</string>
+    <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Benutzerdefinierte Trace-Einstellungen"</string>
+    <string name="restore_default" msgid="5259420807486239755">"Standard wiederherstellen"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Einhandmodus"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Hörgeräte"</string>
     <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktiv"</string>
@@ -440,6 +429,17 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Einstellungen öffnen"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Sonstiges Gerät"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Übersicht ein-/ausblenden"</string>
+    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Prioritätsmodi"</string>
+    <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Fertig"</string>
+    <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Einstellungen"</string>
+    <string name="zen_mode_on" msgid="9085304934016242591">"An"</string>
+    <string name="zen_mode_off" msgid="1736604456618147306">"Aus"</string>
+    <!-- no translation found for zen_mode_set_up (7457957033034460064) -->
+    <skip />
+    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
+    <skip />
+    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
+    <skip />
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Klingeltöne und die Vibration werden deaktiviert, außer für Weckrufe, Erinnerungen, Termine sowie Anrufe von zuvor von dir festgelegten Personen. Du hörst jedoch weiterhin Sound, wenn du dir Musik anhörst, Videos ansiehst oder Spiele spielst."</string>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Klingeltöne und die Vibration werden deaktiviert, außer für Weckrufe. Du hörst jedoch weiterhin Sound, wenn du dir Musik anhörst, Videos ansiehst oder Spiele spielst."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Anpassen"</string>
@@ -478,6 +478,8 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Wird langsam geladen • Voll in <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Wird geladen • Voll in <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Widgets auf dem Sperrbildschirm"</string>
+    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
+    <skip />
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Wische nach links, um das gemeinsame Tutorial zu starten"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Anpassen"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Schließen"</string>
@@ -504,10 +506,9 @@
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"Widget auswählen"</string>
     <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"Widget entfernen"</string>
     <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"ausgewähltes Widget in den Bearbeitungsmodus versetzen"</string>
-    <!-- no translation found for communal_widget_picker_title (1953369090475731663) -->
-    <skip />
-    <!-- no translation found for communal_widget_picker_description (490515450110487871) -->
-    <skip />
+    <string name="communal_widget_picker_title" msgid="1953369090475731663">"Sperrbildschirm-Widgets"</string>
+    <string name="communal_widget_picker_description" msgid="490515450110487871">"Jeder kann Widgets auf deinem Sperrbildschirm sehen, auch bei gesperrtem Tablet."</string>
+    <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"Auswahl für Widget aufheben"</string>
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Sperrbildschirm-Widgets"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Wenn du eine App mit einem Widget öffnen möchtest, musst du deine Identität bestätigen. Beachte auch, dass jeder die Widgets sehen kann, auch wenn dein Tablet gesperrt ist. Einige Widgets sind möglicherweise nicht für den Sperrbildschirm vorgesehen, sodass es unsicher sein kann, sie hier hinzuzufügen."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Ok"</string>
@@ -564,8 +565,8 @@
     <string name="media_projection_action_text" msgid="3634906766918186440">"Jetzt starten"</string>
     <string name="empty_shade_text" msgid="8935967157319717412">"Keine Benachrichtigungen"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"Keine neuen Benachrichtigungen"</string>
-    <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"Adaptive Benachrichtigungen an"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"Wenn du in kurzer Zeit viele Benachrichtigungen erhältst, reduziert dein Gerät jetzt die Lautstärke und Pop-ups bis zu 2 Minuten lang."</string>
+    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Verringern von Lautstärke und Vibration bei Benachrichtigungen ist an"</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Wenn du in kurzer Zeit zu viele Benachrichtigungen erhältst, wird automatisch für bis zu 2 Minuten die Lautstärke des Geräts verringert und Benachrichtigungen werden reduziert."</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Deaktivieren"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Für ältere Benachrichtigungen entsperren"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Dieses Gerät wird von deinen Eltern verwaltet"</string>
@@ -732,8 +733,7 @@
     <string name="notification_alert_title" msgid="3656229781017543655">"Standard"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Automatisch"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"Kein Ton und keine Vibration"</string>
-    <!-- no translation found for notification_conversation_summary_low (3855696451919728790) -->
-    <skip />
+    <string name="notification_conversation_summary_low" msgid="3855696451919728790">"Weder Ton noch Vibration, erscheint aber dennoch im Bereich „Unterhaltungen“"</string>
     <string name="notification_channel_summary_default" msgid="777294388712200605">"Kann je nach Geräteeinstellungen klingeln oder vibrieren"</string>
     <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Kann je nach Geräteeinstellungen klingeln oder vibrieren. Unterhaltungen von <xliff:g id="APP_NAME">%1$s</xliff:g> werden standardmäßig als Bubble angezeigt."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Das System entscheiden lassen, ob bei dieser Benachrichtigung ein Ton oder eine Vibration ausgegeben wird"</string>
@@ -790,8 +790,7 @@
     <string name="keyboard_key_page_up" msgid="173914303254199845">"Nach oben"</string>
     <string name="keyboard_key_page_down" msgid="9035902490071829731">"Nach unten"</string>
     <string name="keyboard_key_forward_del" msgid="5325501825762733459">"Entf"</string>
-    <!-- no translation found for keyboard_key_esc (6230365950511411322) -->
-    <skip />
+    <string name="keyboard_key_esc" msgid="6230365950511411322">"Esc"</string>
     <string name="keyboard_key_move_home" msgid="3496502501803911971">"Pos1"</string>
     <string name="keyboard_key_move_end" msgid="99190401463834854">"Ende"</string>
     <string name="keyboard_key_insert" msgid="4621692715704410493">"Einfg"</string>
@@ -1233,7 +1232,6 @@
     <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>
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobile Daten"</string>
-    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Verbunden"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Vorübergehend verbunden"</string>
     <string name="mobile_data_poor_connection" msgid="819617772268371434">"Schwache Verbindung"</string>
@@ -1374,8 +1372,7 @@
     <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üpfungen"</string>
-    <!-- no translation found for shortcut_helper_category_current_app_shortcuts (4017840565974573628) -->
-    <skip />
+    <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_search_placeholder" msgid="5488547526269871819">"Tastenkürzel suchen"</string>
@@ -1388,13 +1385,27 @@
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Fertig"</string>
     <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Gut gemacht!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Zurück"</string>
-    <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"Wenn du zurückgehen möchtest, wische an einer beliebigen Stelle des Touchpads mit drei Fingern nach links oder rechts."</string>
+    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
+    <skip />
+    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
+    <skip />
     <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Touchpad mit drei Fingern, die sich nach links und rechts bewegen"</string>
     <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Bildschirm zeigt eine Animation der Touch-Geste „Zurück“"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Tastaturbeleuchtung"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Level %1$d von %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Smart-Home-Steuerung"</string>
     <string name="home_controls_dream_description" msgid="4644150952104035789">"Smart-Home-Steuerung als Bildschirmschoner nutzen"</string>
-    <!-- no translation found for volume_undo_action (5815519725211877114) -->
-    <skip />
+    <string name="volume_undo_action" msgid="5815519725211877114">"Rückgängig machen"</string>
+    <string name="back_edu_toast_content" msgid="4530314597378982956">"Wenn du zurückgehen möchtest, wische auf dem Touchpad mit drei Fingern nach links oder rechts"</string>
+    <string name="home_edu_toast_content" msgid="3381071147871955415">"Wenn du den Startbildschirm aufrufen möchtest, wische auf dem Touchpad mit drei Fingern nach oben"</string>
+    <string name="overview_edu_toast_content" msgid="5797030644017804518">"Um zuletzt verwendete Apps aufzurufen, wische mit 3 Fingern nach oben und halte das Touchpad gedrückt"</string>
+    <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Wenn du alle deine Apps aufrufen möchtest, drücke auf der Tastatur die Aktionstaste"</string>
+    <string name="back_edu_notification_title" msgid="5624780717751357278">"Über das Touchpad zurückgehen"</string>
+    <string name="back_edu_notification_content" msgid="2497557451540954068">"Wische mit drei Fingern nach links oder rechts. Tippe für mehr Infos zu Touch-Gesten."</string>
+    <string name="home_edu_notification_title" msgid="6097902076909654045">"Über das Touchpad zum Startbildschirm zurückkehren"</string>
+    <string name="home_edu_notification_content" msgid="6631697734535766588">"Wische mit drei Fingern nach oben. Tippe für mehr Infos zu Touch-Gesten."</string>
+    <string name="overview_edu_notification_title" msgid="1265824157319562406">"Über das Touchpad zuletzt verwendete Apps aufrufen"</string>
+    <string name="overview_edu_notification_content" msgid="3578204677648432500">"Wische mit 3 Fingern nach oben und halte das Touchpad gedrückt. Tippe für mehr Infos zu Touch-Gesten."</string>
+    <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Über die Tastatur alle Apps aufrufen"</string>
+    <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Du kannst jederzeit die Aktionstaste drücken. Tippe für mehr Infos zu Touch-Gesten."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-de/tiles_states_strings.xml b/packages/SystemUI/res/values-de/tiles_states_strings.xml
index 8d9c793..0606cc7 100644
--- a/packages/SystemUI/res/values-de/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-de/tiles_states_strings.xml
@@ -56,9 +56,6 @@
     <item msgid="5376619709702103243">"Aus"</item>
     <item msgid="4875147066469902392">"An"</item>
   </string-array>
-    <!-- no translation found for tile_states_modes:0 (7764936419245199023) -->
-    <!-- no translation found for tile_states_modes:1 (2004750556637773692) -->
-    <!-- no translation found for tile_states_modes:2 (8968530753931637871) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Nicht verfügbar"</item>
     <item msgid="5044688398303285224">"Aus"</item>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index b77a9f1..5025f6d 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -126,38 +126,25 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"Πατήστε για προβολή"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"Σφάλμα κατά την αποθήκευση της εγγραφής οθόνης"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Σφάλμα κατά την έναρξη της εγγραφής οθόνης"</string>
-    <!-- no translation found for screenrecord_stop_dialog_title (8716193661764511095) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message (6262768207331626817) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5995770227684523244) -->
-    <skip />
+    <string name="screenrecord_stop_dialog_title" msgid="8716193661764511095">"Να διακοπεί η εγγραφή;"</string>
+    <string name="screenrecord_stop_dialog_message" msgid="6262768207331626817">"Αυτή τη στιγμή εγγράφετε ολόκληρη την οθόνη σας"</string>
+    <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Αυτή τη στιγμή εγγράφετε το <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Διακοπή εγγραφής"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Γίνεται κοινοποίηση οθόνης"</string>
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Διακοπή κοινής χρήσης οθόνης;"</string>
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen_with_host_app (522823522115375414) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen (5090115386271179270) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_specific (5923772039347985172) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_generic (6681016774654578261) -->
-    <skip />
+    <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Αυτή τη στιγμή μοιράζεστε ολόκληρη την οθόνη σας με το <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
+    <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Αυτή τη στιγμή μοιράζεστε ολόκληρη την οθόνη σας με μια εφαρμογή"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Αυτή τη στιγμή μοιράζεστε το <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Αυτή τη στιγμή μοιράζεστε μια εφαρμογή"</string>
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Διακοπή κοινής χρήσης"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Μετάδοση οθόνης"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Τερματισμός μετάδοσης;"</string>
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen_with_device (1474703115926205251) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen (8419219169553867625) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app_with_device (2715934698604085519) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (8616103075630934513) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic_with_device (9213582497852420203) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic (4100272100480415076) -->
-    <skip />
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen_with_device" msgid="1474703115926205251">"Αυτή τη στιγμή μεταδίδετε ολόκληρη την οθόνη σας στη <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen" msgid="8419219169553867625">"Αυτή τη στιγμή μεταδίδετε ολόκληρη την οθόνη σας σε μια συσκευή σε κοντινή απόσταση"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app_with_device" msgid="2715934698604085519">"Αυτή τη στιγμή μεταδίδετε το <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> στο <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="8616103075630934513">"Αυτή τη στιγμή μεταδίδετε την <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> σε μια συσκευή σε κοντινή απόσταση"</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic_with_device" msgid="9213582497852420203">"Αυτή τη στιγμή κάνετε μετάδοση στη <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic" msgid="4100272100480415076">"Αυτή τη στιγμή κάνετε μετάδοση σε μια συσκευή σε κοντινή απόσταση"</string>
     <string name="cast_to_other_device_stop_dialog_button" msgid="6420183747435521834">"Διακοπή μετάδοσης"</string>
     <string name="close_dialog_button" msgid="4749497706540104133">"Κλείσιμο"</string>
     <string name="issuerecord_title" msgid="286627115110121849">"Εργαλείο καταγραφής προβλημάτων"</string>
@@ -303,8 +290,7 @@
     <string name="start_dreams" msgid="9131802557946276718">"Προφύλαξη οθόνης"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"Μην ενοχλείτε"</string>
-    <!-- no translation found for quick_settings_modes_label (5407025818652750501) -->
-    <skip />
+    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Λειτουργίες προτεραιότητας"</string>
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Δεν υπάρχουν διαθέσιμες συσκευές σε σύζευξη"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Πατήστε για σύνδεση ή αποσύνδεση μιας συσκευής"</string>
@@ -402,6 +388,9 @@
     <string name="performance" msgid="6552785217174378320">"Απόδοση"</string>
     <string name="user_interface" msgid="3712869377953950887">"Διεπαφή χρήστη"</string>
     <string name="thermal" msgid="6758074791325414831">"Θερμικό"</string>
+    <string name="custom" msgid="3337456985275158299">"Προσαρμοσμένο"</string>
+    <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Ρυθμίσεις προσαρμοσμένου ίχνους"</string>
+    <string name="restore_default" msgid="5259420807486239755">"Επαναφορά προεπιλογής"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Λειτουργία ενός χεριού"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Συσκευές ακοής"</string>
     <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Ενεργές"</string>
@@ -440,6 +429,17 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Άνοιγμα Ρυθμίσεων"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Άλλη συσκευή"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Εναλλαγή επισκόπησης"</string>
+    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Λειτουργίες προτεραιότητας"</string>
+    <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_off" msgid="1736604456618147306">"Ανενεργό"</string>
+    <!-- no translation found for zen_mode_set_up (7457957033034460064) -->
+    <skip />
+    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
+    <skip />
+    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
+    <skip />
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Δεν θα ενοχλείστε από ήχους και δονήσεις, παρά μόνο από ξυπνητήρια, υπενθυμίσεις, συμβάντα και καλούντες που έχετε καθορίσει. Θα εξακολουθείτε να ακούτε όλο το περιεχόμενο που επιλέγετε να αναπαραγάγετε, συμπεριλαμβανομένης της μουσικής, των βίντεο και των παιχνιδιών."</string>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Δεν θα ενοχλείστε από ήχους και δονήσεις, παρά μόνο από ξυπνητήρια. Θα εξακολουθείτε να ακούτε όλο το περιεχόμενο που επιλέγετε να αναπαραγάγετε, συμπεριλαμβανομένης της μουσικής, των βίντεο και των παιχνιδιών."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Προσαρμογή"</string>
@@ -478,6 +478,8 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Αργή φόρτιση • Πλήρης φόρτιση σε <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Φόρτιση • Πλήρης φόρτιση σε <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Γραφικά στοιχεία στην οθόνη κλειδώματος"</string>
+    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
+    <skip />
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Σύρετε προς τα αριστερά για να ξεκινήσετε τον κοινόχρηστο οδηγό"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Προσαρμογή"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Παράβλεψη"</string>
@@ -504,10 +506,9 @@
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"επιλογή γραφικού στοιχείου"</string>
     <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"κατάργηση γραφικού στοιχείου"</string>
     <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"τοποθέτηση επιλεγμένου γραφικού στοιχείου"</string>
-    <!-- no translation found for communal_widget_picker_title (1953369090475731663) -->
-    <skip />
-    <!-- no translation found for communal_widget_picker_description (490515450110487871) -->
-    <skip />
+    <string name="communal_widget_picker_title" msgid="1953369090475731663">"Γραφικά στοιχεία οθόνης κλειδώματος"</string>
+    <string name="communal_widget_picker_description" msgid="490515450110487871">"Όλοι μπορούν να δουν γραφικά στοιχεία στην οθόνη κλειδώματος, ακόμα και αν το tablet είναι κλειδωμένο."</string>
+    <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"αποεπιλογή γραφικού στοιχείου"</string>
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Γραφικά στοιχεία οθόνης κλειδώματος"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Για να ανοίξετε μια εφαρμογή χρησιμοποιώντας ένα γραφικό στοιχείο, θα πρέπει να επαληθεύσετε την ταυτότητά σας. Επίσης, λάβετε υπόψη ότι η προβολή τους είναι δυνατή από οποιονδήποτε, ακόμα και όταν το tablet σας είναι κλειδωμένο. Ορισμένα γραφικά στοιχεία μπορεί να μην προορίζονται για την οθόνη κλειδώματος και η προσθήκη τους εδώ ενδέχεται να μην είναι ασφαλής."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Το κατάλαβα"</string>
@@ -564,8 +565,8 @@
     <string name="media_projection_action_text" msgid="3634906766918186440">"Έναρξη τώρα"</string>
     <string name="empty_shade_text" msgid="8935967157319717412">"Δεν υπάρχουν ειδοποιήσεις"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"Δεν υπάρχουν νέες ειδοποιήσεις"</string>
-    <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"Προσαρμοστ. ειδοπ. – Ενεργές"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"Όταν λαμβάνετε πολλές ειδοποιήσεις σε σύντομο χρονικό διάστημα, η συσκευή θα χαμηλώνει την ένταση και θα μειώνει τα αναδυόμενα για έως και 2 λεπτά."</string>
+    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Η περίοδος cooldown ειδοποιήσεων είναι ενεργή"</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Αυτόματη μείωση έντασης ήχου συσκευής και ειδοποιήσεων για έως 2 λεπτά όταν λαμβάνετε πολλές ειδοποιήσεις ταυτόχρονα."</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Απενεργοποίηση"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Ξεκλειδώστε για εμφάνιση παλαιότ. ειδοπ."</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Αυτή η συσκευή είναι διαχειριζόμενη από τον γονέα σου"</string>
@@ -732,8 +733,7 @@
     <string name="notification_alert_title" msgid="3656229781017543655">"Προεπιλογή"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Αυτόματο"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"Χωρίς ήχο ή δόνηση"</string>
-    <!-- no translation found for notification_conversation_summary_low (3855696451919728790) -->
-    <skip />
+    <string name="notification_conversation_summary_low" msgid="3855696451919728790">"Δεν προκαλέι ήχο ή δόνηση, αλλά εξακολουθεί να εμφανίζεται στην ενότητα συζητήσεων"</string>
     <string name="notification_channel_summary_default" msgid="777294388712200605">"Ενδέχεται να κουδουνίζει ή να δονείται βάσει των ρυθμίσεων συσκευής"</string>
     <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Ενδέχεται να κουδουνίζει ή να δονείται βάσει των ρυθμίσεων συσκευής. Οι συζητήσεις από την εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g> εμφανίζονται σε συννεφάκι από προεπιλογή."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Επιτρέψτε στο σύστημα να αποφασίσει αν αυτή η ειδοποίηση θα αναπαράγει έναν ήχο ή θα ενεργοποιήσει τη δόνηση της συσκευής"</string>
@@ -790,8 +790,7 @@
     <string name="keyboard_key_page_up" msgid="173914303254199845">"Προηγούμενη σελίδα"</string>
     <string name="keyboard_key_page_down" msgid="9035902490071829731">"Επόμενη σελίδα"</string>
     <string name="keyboard_key_forward_del" msgid="5325501825762733459">"Delete"</string>
-    <!-- no translation found for keyboard_key_esc (6230365950511411322) -->
-    <skip />
+    <string name="keyboard_key_esc" msgid="6230365950511411322">"Esc"</string>
     <string name="keyboard_key_move_home" msgid="3496502501803911971">"Home"</string>
     <string name="keyboard_key_move_end" msgid="99190401463834854">"Λήξη"</string>
     <string name="keyboard_key_insert" msgid="4621692715704410493">"Insert"</string>
@@ -1233,7 +1232,6 @@
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Απαιτείται έλεγχος ταυτότητας. Αγγίξτε τον αισθητήρα δακτυλικών αποτυπωμάτων για έλεγχο ταυτότητας."</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Τηλεφωνική κλήση σε εξέλιξη"</string>
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Δεδομένα κινητής τηλεφωνίας"</string>
-    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Συνδέθηκε"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Προσωρινή σύνδεση"</string>
     <string name="mobile_data_poor_connection" msgid="819617772268371434">"Ασθενής σύνδεση"</string>
@@ -1374,8 +1372,7 @@
     <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>
-    <!-- no translation found for shortcut_helper_category_current_app_shortcuts (4017840565974573628) -->
-    <skip />
+    <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_search_placeholder" msgid="5488547526269871819">"Συντομεύσεις αναζήτησης"</string>
@@ -1388,13 +1385,27 @@
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Τέλος"</string>
     <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Μπράβο!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Επιστροφή"</string>
-    <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"Για επιστροφή, σύρετε προς τα αριστερά ή προς τα δεξιά χρησιμοποιώντας τρία δάχτυλα οπουδήποτε στην επιφάνεια αφής."</string>
+    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
+    <skip />
+    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
+    <skip />
     <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Επιφάνεια αφής στην οποία εμφανίζονται τρία δάχτυλα να κινούνται δεξιά και αριστερά"</string>
     <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Οθόνη συσκευής που εμφανίζει μια κινούμενη εικόνα σχετικά με την κίνηση επιστροφής"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Οπίσθιος φωτισμός πληκτρολογίου"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Επίπεδο %1$d από %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Οικιακοί έλεγχοι"</string>
     <string name="home_controls_dream_description" msgid="4644150952104035789">"Γρήγ. πρόσβαση σε οικιακ. ελέγχους ως προφ. οθόνης"</string>
-    <!-- no translation found for volume_undo_action (5815519725211877114) -->
-    <skip />
+    <string name="volume_undo_action" msgid="5815519725211877114">"Αναίρεση"</string>
+    <string name="back_edu_toast_content" msgid="4530314597378982956">"Για επιστροφή, σύρετε προς τα αριστερά ή τα δεξιά με τρία δάχτυλα στην επιφάνεια αφής"</string>
+    <string name="home_edu_toast_content" msgid="3381071147871955415">"Για μετάβαση στην αρχική οθόνη, σύρετε προς τα επάνω στην επιφάνεια αφής με τρία δάχτυλα"</string>
+    <string name="overview_edu_toast_content" msgid="5797030644017804518">"Για πρόσφατες εφαρμογές, σαρώστε προς τα πάνω με τρία δάχτυλα και κρατήστε τα στην επιφάνεια αφής"</string>
+    <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Για να δείτε όλες τις εφαρμογές, πιέστε το πλήκτρο ενέργειας στο πληκτρολόγιό σας"</string>
+    <string name="back_edu_notification_title" msgid="5624780717751357278">"Χρήση της επιφάνειας αφής για επιστροφή"</string>
+    <string name="back_edu_notification_content" msgid="2497557451540954068">"Σύρετε προς τα αριστερά ή τα δεξιά με τρία δάχτυλα. Πατήστε για να μάθετε περισσότερες κινήσεις."</string>
+    <string name="home_edu_notification_title" msgid="6097902076909654045">"Χρήση της επιφάνειας αφής για μετάβαση στην αρχική οθόνη"</string>
+    <string name="home_edu_notification_content" msgid="6631697734535766588">"Σύρετε προς τα πάνω με τρία δάχτυλα. Πατήστε για να μάθετε περισσότερες κινήσεις."</string>
+    <string name="overview_edu_notification_title" msgid="1265824157319562406">"Χρήση της επιφάνειας αφής για προβολή πρόσφατων εφαρμογών"</string>
+    <string name="overview_edu_notification_content" msgid="3578204677648432500">"Σύρετε προς τα πάνω με τρία δάχτυλα και μην τα σηκώσετε. Πατήστε για να δείτε περισσότερες κινήσεις."</string>
+    <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Χρήση του πληκτρολογίου για προβολή όλων των εφαρμογών"</string>
+    <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Πιέστε το πλήκτρο ενέργειας ανά πάσα στιγμή. Πατήστε για να μάθετε περισσότερες κινήσεις."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-el/tiles_states_strings.xml b/packages/SystemUI/res/values-el/tiles_states_strings.xml
index 3f27fb4..d4545ff 100644
--- a/packages/SystemUI/res/values-el/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-el/tiles_states_strings.xml
@@ -56,9 +56,6 @@
     <item msgid="5376619709702103243">"Ανενεργό"</item>
     <item msgid="4875147066469902392">"Ενεργό"</item>
   </string-array>
-    <!-- no translation found for tile_states_modes:0 (7764936419245199023) -->
-    <!-- no translation found for tile_states_modes:1 (2004750556637773692) -->
-    <!-- no translation found for tile_states_modes:2 (8968530753931637871) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Μη διαθέσιμο"</item>
     <item msgid="5044688398303285224">"Ανενεργό"</item>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index 1b1ae97..f2ba704 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -126,38 +126,25 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"Tap to view"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"Error saving screen recording"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Error starting screen recording"</string>
-    <!-- no translation found for screenrecord_stop_dialog_title (8716193661764511095) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message (6262768207331626817) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5995770227684523244) -->
-    <skip />
+    <string name="screenrecord_stop_dialog_title" msgid="8716193661764511095">"Stop recording?"</string>
+    <string name="screenrecord_stop_dialog_message" msgid="6262768207331626817">"You\'re currently recording your entire screen"</string>
+    <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"You\'re currently recording <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Stop recording"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Sharing screen"</string>
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Stop sharing screen?"</string>
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen_with_host_app (522823522115375414) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen (5090115386271179270) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_specific (5923772039347985172) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_generic (6681016774654578261) -->
-    <skip />
+    <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"You\'re currently sharing your entire screen with <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
+    <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"You\'re currently sharing your entire screen with an app"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"You\'re currently sharing <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"You\'re currently sharing an app"</string>
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Stop sharing"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Casting screen"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Stop casting?"</string>
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen_with_device (1474703115926205251) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen (8419219169553867625) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app_with_device (2715934698604085519) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (8616103075630934513) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic_with_device (9213582497852420203) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic (4100272100480415076) -->
-    <skip />
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen_with_device" msgid="1474703115926205251">"You\'re currently casting your entire screen to <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen" msgid="8419219169553867625">"You\'re currently casting your entire screen to a nearby device"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app_with_device" msgid="2715934698604085519">"You\'re currently casting <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> to <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="8616103075630934513">"You\'re currently casting <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> to a nearby device"</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic_with_device" msgid="9213582497852420203">"You\'re currently casting to <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic" msgid="4100272100480415076">"You\'re currently casting to a nearby device"</string>
     <string name="cast_to_other_device_stop_dialog_button" msgid="6420183747435521834">"Stop casting"</string>
     <string name="close_dialog_button" msgid="4749497706540104133">"Close"</string>
     <string name="issuerecord_title" msgid="286627115110121849">"Issue Recorder"</string>
@@ -303,8 +290,7 @@
     <string name="start_dreams" msgid="9131802557946276718">"Screen saver"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"Do Not Disturb"</string>
-    <!-- no translation found for quick_settings_modes_label (5407025818652750501) -->
-    <skip />
+    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Priority modes"</string>
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"No paired devices available"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Tap to connect or disconnect a device"</string>
@@ -402,6 +388,9 @@
     <string name="performance" msgid="6552785217174378320">"Performance"</string>
     <string name="user_interface" msgid="3712869377953950887">"User interface"</string>
     <string name="thermal" msgid="6758074791325414831">"Thermal"</string>
+    <string name="custom" msgid="3337456985275158299">"Custom"</string>
+    <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Custom trace settings"</string>
+    <string name="restore_default" msgid="5259420807486239755">"Restore default"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"One-handed mode"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Hearing devices"</string>
     <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Active"</string>
@@ -440,6 +429,17 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Open settings"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Other device"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Toggle Overview"</string>
+    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Priority modes"</string>
+    <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Done"</string>
+    <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Settings"</string>
+    <string name="zen_mode_on" msgid="9085304934016242591">"On"</string>
+    <string name="zen_mode_off" msgid="1736604456618147306">"Off"</string>
+    <!-- no translation found for zen_mode_set_up (7457957033034460064) -->
+    <skip />
+    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
+    <skip />
+    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
+    <skip />
     <string name="zen_priority_introduction" msgid="3159291973383796646">"You won\'t be disturbed by sounds and vibrations, except from alarms, reminders, events and callers you specify. You\'ll still hear anything you choose to play including music, videos and games."</string>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"You won\'t be disturbed by sounds and vibrations, except from alarms. You\'ll still hear anything you choose to play including music, videos and games."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Customise"</string>
@@ -478,6 +478,8 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Charging slowly • Full in <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Charging • Full in <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Widgets on lock screen"</string>
+    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
+    <skip />
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Swipe left to start the communal tutorial"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Customise"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Dismiss"</string>
@@ -504,10 +506,9 @@
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"select widget"</string>
     <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"remove widget"</string>
     <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"place selected widget"</string>
-    <!-- no translation found for communal_widget_picker_title (1953369090475731663) -->
-    <skip />
-    <!-- no translation found for communal_widget_picker_description (490515450110487871) -->
-    <skip />
+    <string name="communal_widget_picker_title" msgid="1953369090475731663">"Lock screen widgets"</string>
+    <string name="communal_widget_picker_description" msgid="490515450110487871">"Anyone can view widgets on your lock screen, even if your tablet\'s locked."</string>
+    <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"unselect widget"</string>
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Lock screen widgets"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"To open an app using a widget, you\'ll need to verify that it\'s you. Also, bear in mind that anyone can view them, even when your tablet\'s locked. Some widgets may not have been intended for your lock screen and may be unsafe to add here."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Got it"</string>
@@ -564,8 +565,8 @@
     <string name="media_projection_action_text" msgid="3634906766918186440">"Start now"</string>
     <string name="empty_shade_text" msgid="8935967157319717412">"No notifications"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"No new notifications"</string>
-    <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"Adaptive notifications is on"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"Your device now lowers the volume and reduces pop-ups on the screen for up to 2 minutes when you receive many notifications in a short time span."</string>
+    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Notification cooldown is on"</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Your device volume and alerts are reduced automatically for up to 2 minutes when you get too many notifications at once."</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Turn off"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Unlock to see older notifications"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"This device is managed by your parent"</string>
@@ -732,8 +733,7 @@
     <string name="notification_alert_title" msgid="3656229781017543655">"Default"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Automatic"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"No sound or vibration"</string>
-    <!-- no translation found for notification_conversation_summary_low (3855696451919728790) -->
-    <skip />
+    <string name="notification_conversation_summary_low" msgid="3855696451919728790">"No sound or vibration but still appears in the conversation section"</string>
     <string name="notification_channel_summary_default" msgid="777294388712200605">"May ring or vibrate based on device settings"</string>
     <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"May ring or vibrate based on device settings. Conversations from <xliff:g id="APP_NAME">%1$s</xliff:g> bubble by default."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Have the system determine if this notification should make sound or vibration"</string>
@@ -790,8 +790,7 @@
     <string name="keyboard_key_page_up" msgid="173914303254199845">"Page Up"</string>
     <string name="keyboard_key_page_down" msgid="9035902490071829731">"Page Down"</string>
     <string name="keyboard_key_forward_del" msgid="5325501825762733459">"Delete"</string>
-    <!-- no translation found for keyboard_key_esc (6230365950511411322) -->
-    <skip />
+    <string name="keyboard_key_esc" msgid="6230365950511411322">"Esc"</string>
     <string name="keyboard_key_move_home" msgid="3496502501803911971">"Home"</string>
     <string name="keyboard_key_move_end" msgid="99190401463834854">"End"</string>
     <string name="keyboard_key_insert" msgid="4621692715704410493">"Insert"</string>
@@ -1233,7 +1232,6 @@
     <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="mobile_data_settings_title" msgid="3955246641380064901">"Mobile data"</string>
-    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Connected"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Temporarily connected"</string>
     <string name="mobile_data_poor_connection" msgid="819617772268371434">"Poor connection"</string>
@@ -1374,8 +1372,7 @@
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Split screen"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Input"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"App shortcuts"</string>
-    <!-- no translation found for shortcut_helper_category_current_app_shortcuts (4017840565974573628) -->
-    <skip />
+    <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Current app"</string>
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accessibility"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Keyboard shortcuts"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Search shortcuts"</string>
@@ -1388,13 +1385,27 @@
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Done"</string>
     <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Great work!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Go back"</string>
-    <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"To go back, swipe left or right using 3 fingers anywhere on the touchpad."</string>
+    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
+    <skip />
+    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
+    <skip />
     <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Touchpad showing 3 fingers moving right and left"</string>
     <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Device screen showing animation for back gesture"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Keyboard backlight"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Level %1$d of %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Home controls"</string>
     <string name="home_controls_dream_description" msgid="4644150952104035789">"Quickly access your home controls as a screensaver"</string>
-    <!-- no translation found for volume_undo_action (5815519725211877114) -->
-    <skip />
+    <string name="volume_undo_action" msgid="5815519725211877114">"Undo"</string>
+    <string name="back_edu_toast_content" msgid="4530314597378982956">"To go back, swipe left or right with three fingers on the touchpad"</string>
+    <string name="home_edu_toast_content" msgid="3381071147871955415">"To go home, swipe up with three fingers on the touchpad"</string>
+    <string name="overview_edu_toast_content" msgid="5797030644017804518">"To view recent apps, swipe up and hold with three fingers on the touchpad"</string>
+    <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"To view all your apps, press the action key on your keyboard"</string>
+    <string name="back_edu_notification_title" msgid="5624780717751357278">"Use your touchpad to go back"</string>
+    <string name="back_edu_notification_content" msgid="2497557451540954068">"Swipe left or right using three fingers. Tap to learn more gestures."</string>
+    <string name="home_edu_notification_title" msgid="6097902076909654045">"Use your touchpad to go home"</string>
+    <string name="home_edu_notification_content" msgid="6631697734535766588">"Swipe up using three fingers. Tap to learn more gestures."</string>
+    <string name="overview_edu_notification_title" msgid="1265824157319562406">"Use your touchpad to view recent apps"</string>
+    <string name="overview_edu_notification_content" msgid="3578204677648432500">"Swipe up and hold using three fingers. Tap to learn more gestures."</string>
+    <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Use your keyboard to view all apps"</string>
+    <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Press the action key at any time. Tap to learn more gestures."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-en-rAU/tiles_states_strings.xml b/packages/SystemUI/res/values-en-rAU/tiles_states_strings.xml
index 27c23aa..39dd7c8 100644
--- a/packages/SystemUI/res/values-en-rAU/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/tiles_states_strings.xml
@@ -56,9 +56,6 @@
     <item msgid="5376619709702103243">"Off"</item>
     <item msgid="4875147066469902392">"On"</item>
   </string-array>
-    <!-- no translation found for tile_states_modes:0 (7764936419245199023) -->
-    <!-- no translation found for tile_states_modes:1 (2004750556637773692) -->
-    <!-- no translation found for tile_states_modes:2 (8968530753931637871) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Unavailable"</item>
     <item msgid="5044688398303285224">"Off"</item>
diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index 0acb674..5680cb7 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -388,6 +388,9 @@
     <string name="performance" msgid="6552785217174378320">"Performance"</string>
     <string name="user_interface" msgid="3712869377953950887">"User Interface"</string>
     <string name="thermal" msgid="6758074791325414831">"Thermal"</string>
+    <string name="custom" msgid="3337456985275158299">"Custom"</string>
+    <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Custom Trace Settings"</string>
+    <string name="restore_default" msgid="5259420807486239755">"Restore Default"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"One-handed mode"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Hearing devices"</string>
     <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Active"</string>
@@ -426,6 +429,14 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Open Settings"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Other device"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Toggle Overview"</string>
+    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Priority modes"</string>
+    <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Done"</string>
+    <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Settings"</string>
+    <string name="zen_mode_on" msgid="9085304934016242591">"On"</string>
+    <string name="zen_mode_off" msgid="1736604456618147306">"Off"</string>
+    <string name="zen_mode_set_up" msgid="7457957033034460064">"Set up"</string>
+    <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Manage in settings"</string>
+    <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{No active modes}=1{{mode} is active}other{# modes are active}}"</string>
     <string name="zen_priority_introduction" msgid="3159291973383796646">"You won\'t be disturbed by sounds and vibrations, except from alarms, reminders, events, and callers you specify. You\'ll still hear anything you choose to play including music, videos, and games."</string>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"You won\'t be disturbed by sounds and vibrations, except from alarms. You\'ll still hear anything you choose to play including music, videos, and games."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Customize"</string>
@@ -464,6 +475,7 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Charging slowly • Full in <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Charging • Full in <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Widgets on lock screen"</string>
+    <string name="accessibility_announcement_communal_widget_added" msgid="6911593106099328271">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> widget added to lock screen"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Swipe left to start the communal tutorial"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Customize"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Dismiss"</string>
@@ -492,6 +504,7 @@
     <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"place selected widget"</string>
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"Lock screen widgets"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"Anyone can view widgets on your lock screen, even if your tablet\'s locked."</string>
+    <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"unselect widget"</string>
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Lock screen widgets"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"To open an app using a widget, you’ll need to verify it’s you. Also, keep in mind that anyone can view them, even when your tablet’s locked. Some widgets may not have been intended for your lock screen and may be unsafe to add here."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Got it"</string>
@@ -548,8 +561,8 @@
     <string name="media_projection_action_text" msgid="3634906766918186440">"Start now"</string>
     <string name="empty_shade_text" msgid="8935967157319717412">"No notifications"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"No new notifications"</string>
-    <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"Adaptive notifications is on"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"Your device now lowers the volume and reduces pop-ups on the screen for up to two minutes when you receive many notifications in a short time span."</string>
+    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Notification cooldown is on"</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Your device volume and alerts are reduced automatically for up to 2 minutes when you get too many notifications at once."</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Turn off"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Unlock to see older notifications"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"This device is managed by your parent"</string>
@@ -716,8 +729,7 @@
     <string name="notification_alert_title" msgid="3656229781017543655">"Default"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Automatic"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"No sound or vibration"</string>
-    <!-- no translation found for notification_conversation_summary_low (3855696451919728790) -->
-    <skip />
+    <string name="notification_conversation_summary_low" msgid="3855696451919728790">"No sound or vibration but still appears in the conversation section"</string>
     <string name="notification_channel_summary_default" msgid="777294388712200605">"May ring or vibrate based on device settings"</string>
     <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"May ring or vibrate based on device settings. Conversations from <xliff:g id="APP_NAME">%1$s</xliff:g> bubble by default."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Have the system determine if this notification should make sound or vibration"</string>
@@ -1216,7 +1228,6 @@
     <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="mobile_data_settings_title" msgid="3955246641380064901">"Mobile data"</string>
-    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Connected"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Temporarily connected"</string>
     <string name="mobile_data_poor_connection" msgid="819617772268371434">"Poor connection"</string>
@@ -1370,7 +1381,10 @@
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Done"</string>
     <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Great job!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Go back"</string>
-    <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"To go back, swipe left or right using three fingers anywhere on the touchpad."</string>
+    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
+    <skip />
+    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
+    <skip />
     <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Touchpad showing three fingers moving right and left"</string>
     <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Device screen showing animation for back gesture"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Keyboard backlight"</string>
@@ -1378,4 +1392,16 @@
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Home Controls"</string>
     <string name="home_controls_dream_description" msgid="4644150952104035789">"Quickly access your home controls as a screensaver"</string>
     <string name="volume_undo_action" msgid="5815519725211877114">"Undo"</string>
+    <string name="back_edu_toast_content" msgid="4530314597378982956">"To go back, swipe left or right with three fingers on the touchpad"</string>
+    <string name="home_edu_toast_content" msgid="3381071147871955415">"To go home, swipe up with three fingers on the touchpad"</string>
+    <string name="overview_edu_toast_content" msgid="5797030644017804518">"To view recent apps, swipe up and hold with three fingers on the touchpad"</string>
+    <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"To view all your apps, press the action key on your keyboard"</string>
+    <string name="back_edu_notification_title" msgid="5624780717751357278">"Use your touchpad to go back"</string>
+    <string name="back_edu_notification_content" msgid="2497557451540954068">"Swipe left or right using three fingers. Tap to learn more gestures."</string>
+    <string name="home_edu_notification_title" msgid="6097902076909654045">"Use your touchpad to go home"</string>
+    <string name="home_edu_notification_content" msgid="6631697734535766588">"Swipe up using three fingers. Tap to learn more gestures."</string>
+    <string name="overview_edu_notification_title" msgid="1265824157319562406">"Use your touchpad to view recent apps"</string>
+    <string name="overview_edu_notification_content" msgid="3578204677648432500">"Swipe up and hold using three fingers. Tap to learn more gestures."</string>
+    <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Use your keyboard to view all apps"</string>
+    <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Press the action key at any time. Tap to learn more gestures."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-en-rCA/tiles_states_strings.xml b/packages/SystemUI/res/values-en-rCA/tiles_states_strings.xml
index 0658ce5..39dd7c8 100644
--- a/packages/SystemUI/res/values-en-rCA/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/tiles_states_strings.xml
@@ -56,11 +56,6 @@
     <item msgid="5376619709702103243">"Off"</item>
     <item msgid="4875147066469902392">"On"</item>
   </string-array>
-  <string-array name="tile_states_modes">
-    <item msgid="7764936419245199023">"Unavailable"</item>
-    <item msgid="2004750556637773692">"Off"</item>
-    <item msgid="8968530753931637871">"On"</item>
-  </string-array>
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Unavailable"</item>
     <item msgid="5044688398303285224">"Off"</item>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index 1b1ae97..f2ba704 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -126,38 +126,25 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"Tap to view"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"Error saving screen recording"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Error starting screen recording"</string>
-    <!-- no translation found for screenrecord_stop_dialog_title (8716193661764511095) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message (6262768207331626817) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5995770227684523244) -->
-    <skip />
+    <string name="screenrecord_stop_dialog_title" msgid="8716193661764511095">"Stop recording?"</string>
+    <string name="screenrecord_stop_dialog_message" msgid="6262768207331626817">"You\'re currently recording your entire screen"</string>
+    <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"You\'re currently recording <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Stop recording"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Sharing screen"</string>
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Stop sharing screen?"</string>
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen_with_host_app (522823522115375414) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen (5090115386271179270) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_specific (5923772039347985172) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_generic (6681016774654578261) -->
-    <skip />
+    <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"You\'re currently sharing your entire screen with <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
+    <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"You\'re currently sharing your entire screen with an app"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"You\'re currently sharing <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"You\'re currently sharing an app"</string>
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Stop sharing"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Casting screen"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Stop casting?"</string>
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen_with_device (1474703115926205251) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen (8419219169553867625) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app_with_device (2715934698604085519) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (8616103075630934513) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic_with_device (9213582497852420203) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic (4100272100480415076) -->
-    <skip />
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen_with_device" msgid="1474703115926205251">"You\'re currently casting your entire screen to <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen" msgid="8419219169553867625">"You\'re currently casting your entire screen to a nearby device"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app_with_device" msgid="2715934698604085519">"You\'re currently casting <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> to <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="8616103075630934513">"You\'re currently casting <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> to a nearby device"</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic_with_device" msgid="9213582497852420203">"You\'re currently casting to <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic" msgid="4100272100480415076">"You\'re currently casting to a nearby device"</string>
     <string name="cast_to_other_device_stop_dialog_button" msgid="6420183747435521834">"Stop casting"</string>
     <string name="close_dialog_button" msgid="4749497706540104133">"Close"</string>
     <string name="issuerecord_title" msgid="286627115110121849">"Issue Recorder"</string>
@@ -303,8 +290,7 @@
     <string name="start_dreams" msgid="9131802557946276718">"Screen saver"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"Do Not Disturb"</string>
-    <!-- no translation found for quick_settings_modes_label (5407025818652750501) -->
-    <skip />
+    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Priority modes"</string>
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"No paired devices available"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Tap to connect or disconnect a device"</string>
@@ -402,6 +388,9 @@
     <string name="performance" msgid="6552785217174378320">"Performance"</string>
     <string name="user_interface" msgid="3712869377953950887">"User interface"</string>
     <string name="thermal" msgid="6758074791325414831">"Thermal"</string>
+    <string name="custom" msgid="3337456985275158299">"Custom"</string>
+    <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Custom trace settings"</string>
+    <string name="restore_default" msgid="5259420807486239755">"Restore default"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"One-handed mode"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Hearing devices"</string>
     <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Active"</string>
@@ -440,6 +429,17 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Open settings"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Other device"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Toggle Overview"</string>
+    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Priority modes"</string>
+    <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Done"</string>
+    <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Settings"</string>
+    <string name="zen_mode_on" msgid="9085304934016242591">"On"</string>
+    <string name="zen_mode_off" msgid="1736604456618147306">"Off"</string>
+    <!-- no translation found for zen_mode_set_up (7457957033034460064) -->
+    <skip />
+    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
+    <skip />
+    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
+    <skip />
     <string name="zen_priority_introduction" msgid="3159291973383796646">"You won\'t be disturbed by sounds and vibrations, except from alarms, reminders, events and callers you specify. You\'ll still hear anything you choose to play including music, videos and games."</string>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"You won\'t be disturbed by sounds and vibrations, except from alarms. You\'ll still hear anything you choose to play including music, videos and games."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Customise"</string>
@@ -478,6 +478,8 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Charging slowly • Full in <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Charging • Full in <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Widgets on lock screen"</string>
+    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
+    <skip />
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Swipe left to start the communal tutorial"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Customise"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Dismiss"</string>
@@ -504,10 +506,9 @@
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"select widget"</string>
     <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"remove widget"</string>
     <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"place selected widget"</string>
-    <!-- no translation found for communal_widget_picker_title (1953369090475731663) -->
-    <skip />
-    <!-- no translation found for communal_widget_picker_description (490515450110487871) -->
-    <skip />
+    <string name="communal_widget_picker_title" msgid="1953369090475731663">"Lock screen widgets"</string>
+    <string name="communal_widget_picker_description" msgid="490515450110487871">"Anyone can view widgets on your lock screen, even if your tablet\'s locked."</string>
+    <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"unselect widget"</string>
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Lock screen widgets"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"To open an app using a widget, you\'ll need to verify that it\'s you. Also, bear in mind that anyone can view them, even when your tablet\'s locked. Some widgets may not have been intended for your lock screen and may be unsafe to add here."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Got it"</string>
@@ -564,8 +565,8 @@
     <string name="media_projection_action_text" msgid="3634906766918186440">"Start now"</string>
     <string name="empty_shade_text" msgid="8935967157319717412">"No notifications"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"No new notifications"</string>
-    <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"Adaptive notifications is on"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"Your device now lowers the volume and reduces pop-ups on the screen for up to 2 minutes when you receive many notifications in a short time span."</string>
+    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Notification cooldown is on"</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Your device volume and alerts are reduced automatically for up to 2 minutes when you get too many notifications at once."</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Turn off"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Unlock to see older notifications"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"This device is managed by your parent"</string>
@@ -732,8 +733,7 @@
     <string name="notification_alert_title" msgid="3656229781017543655">"Default"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Automatic"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"No sound or vibration"</string>
-    <!-- no translation found for notification_conversation_summary_low (3855696451919728790) -->
-    <skip />
+    <string name="notification_conversation_summary_low" msgid="3855696451919728790">"No sound or vibration but still appears in the conversation section"</string>
     <string name="notification_channel_summary_default" msgid="777294388712200605">"May ring or vibrate based on device settings"</string>
     <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"May ring or vibrate based on device settings. Conversations from <xliff:g id="APP_NAME">%1$s</xliff:g> bubble by default."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Have the system determine if this notification should make sound or vibration"</string>
@@ -790,8 +790,7 @@
     <string name="keyboard_key_page_up" msgid="173914303254199845">"Page Up"</string>
     <string name="keyboard_key_page_down" msgid="9035902490071829731">"Page Down"</string>
     <string name="keyboard_key_forward_del" msgid="5325501825762733459">"Delete"</string>
-    <!-- no translation found for keyboard_key_esc (6230365950511411322) -->
-    <skip />
+    <string name="keyboard_key_esc" msgid="6230365950511411322">"Esc"</string>
     <string name="keyboard_key_move_home" msgid="3496502501803911971">"Home"</string>
     <string name="keyboard_key_move_end" msgid="99190401463834854">"End"</string>
     <string name="keyboard_key_insert" msgid="4621692715704410493">"Insert"</string>
@@ -1233,7 +1232,6 @@
     <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="mobile_data_settings_title" msgid="3955246641380064901">"Mobile data"</string>
-    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Connected"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Temporarily connected"</string>
     <string name="mobile_data_poor_connection" msgid="819617772268371434">"Poor connection"</string>
@@ -1374,8 +1372,7 @@
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Split screen"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Input"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"App shortcuts"</string>
-    <!-- no translation found for shortcut_helper_category_current_app_shortcuts (4017840565974573628) -->
-    <skip />
+    <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Current app"</string>
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accessibility"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Keyboard shortcuts"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Search shortcuts"</string>
@@ -1388,13 +1385,27 @@
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Done"</string>
     <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Great work!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Go back"</string>
-    <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"To go back, swipe left or right using 3 fingers anywhere on the touchpad."</string>
+    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
+    <skip />
+    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
+    <skip />
     <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Touchpad showing 3 fingers moving right and left"</string>
     <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Device screen showing animation for back gesture"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Keyboard backlight"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Level %1$d of %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Home controls"</string>
     <string name="home_controls_dream_description" msgid="4644150952104035789">"Quickly access your home controls as a screensaver"</string>
-    <!-- no translation found for volume_undo_action (5815519725211877114) -->
-    <skip />
+    <string name="volume_undo_action" msgid="5815519725211877114">"Undo"</string>
+    <string name="back_edu_toast_content" msgid="4530314597378982956">"To go back, swipe left or right with three fingers on the touchpad"</string>
+    <string name="home_edu_toast_content" msgid="3381071147871955415">"To go home, swipe up with three fingers on the touchpad"</string>
+    <string name="overview_edu_toast_content" msgid="5797030644017804518">"To view recent apps, swipe up and hold with three fingers on the touchpad"</string>
+    <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"To view all your apps, press the action key on your keyboard"</string>
+    <string name="back_edu_notification_title" msgid="5624780717751357278">"Use your touchpad to go back"</string>
+    <string name="back_edu_notification_content" msgid="2497557451540954068">"Swipe left or right using three fingers. Tap to learn more gestures."</string>
+    <string name="home_edu_notification_title" msgid="6097902076909654045">"Use your touchpad to go home"</string>
+    <string name="home_edu_notification_content" msgid="6631697734535766588">"Swipe up using three fingers. Tap to learn more gestures."</string>
+    <string name="overview_edu_notification_title" msgid="1265824157319562406">"Use your touchpad to view recent apps"</string>
+    <string name="overview_edu_notification_content" msgid="3578204677648432500">"Swipe up and hold using three fingers. Tap to learn more gestures."</string>
+    <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Use your keyboard to view all apps"</string>
+    <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Press the action key at any time. Tap to learn more gestures."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-en-rGB/tiles_states_strings.xml b/packages/SystemUI/res/values-en-rGB/tiles_states_strings.xml
index 27c23aa..39dd7c8 100644
--- a/packages/SystemUI/res/values-en-rGB/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/tiles_states_strings.xml
@@ -56,9 +56,6 @@
     <item msgid="5376619709702103243">"Off"</item>
     <item msgid="4875147066469902392">"On"</item>
   </string-array>
-    <!-- no translation found for tile_states_modes:0 (7764936419245199023) -->
-    <!-- no translation found for tile_states_modes:1 (2004750556637773692) -->
-    <!-- no translation found for tile_states_modes:2 (8968530753931637871) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Unavailable"</item>
     <item msgid="5044688398303285224">"Off"</item>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index 1b1ae97..f2ba704 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -126,38 +126,25 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"Tap to view"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"Error saving screen recording"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Error starting screen recording"</string>
-    <!-- no translation found for screenrecord_stop_dialog_title (8716193661764511095) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message (6262768207331626817) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5995770227684523244) -->
-    <skip />
+    <string name="screenrecord_stop_dialog_title" msgid="8716193661764511095">"Stop recording?"</string>
+    <string name="screenrecord_stop_dialog_message" msgid="6262768207331626817">"You\'re currently recording your entire screen"</string>
+    <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"You\'re currently recording <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Stop recording"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Sharing screen"</string>
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Stop sharing screen?"</string>
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen_with_host_app (522823522115375414) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen (5090115386271179270) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_specific (5923772039347985172) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_generic (6681016774654578261) -->
-    <skip />
+    <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"You\'re currently sharing your entire screen with <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
+    <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"You\'re currently sharing your entire screen with an app"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"You\'re currently sharing <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"You\'re currently sharing an app"</string>
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Stop sharing"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Casting screen"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Stop casting?"</string>
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen_with_device (1474703115926205251) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen (8419219169553867625) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app_with_device (2715934698604085519) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (8616103075630934513) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic_with_device (9213582497852420203) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic (4100272100480415076) -->
-    <skip />
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen_with_device" msgid="1474703115926205251">"You\'re currently casting your entire screen to <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen" msgid="8419219169553867625">"You\'re currently casting your entire screen to a nearby device"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app_with_device" msgid="2715934698604085519">"You\'re currently casting <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> to <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="8616103075630934513">"You\'re currently casting <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> to a nearby device"</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic_with_device" msgid="9213582497852420203">"You\'re currently casting to <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic" msgid="4100272100480415076">"You\'re currently casting to a nearby device"</string>
     <string name="cast_to_other_device_stop_dialog_button" msgid="6420183747435521834">"Stop casting"</string>
     <string name="close_dialog_button" msgid="4749497706540104133">"Close"</string>
     <string name="issuerecord_title" msgid="286627115110121849">"Issue Recorder"</string>
@@ -303,8 +290,7 @@
     <string name="start_dreams" msgid="9131802557946276718">"Screen saver"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"Do Not Disturb"</string>
-    <!-- no translation found for quick_settings_modes_label (5407025818652750501) -->
-    <skip />
+    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Priority modes"</string>
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"No paired devices available"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Tap to connect or disconnect a device"</string>
@@ -402,6 +388,9 @@
     <string name="performance" msgid="6552785217174378320">"Performance"</string>
     <string name="user_interface" msgid="3712869377953950887">"User interface"</string>
     <string name="thermal" msgid="6758074791325414831">"Thermal"</string>
+    <string name="custom" msgid="3337456985275158299">"Custom"</string>
+    <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Custom trace settings"</string>
+    <string name="restore_default" msgid="5259420807486239755">"Restore default"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"One-handed mode"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Hearing devices"</string>
     <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Active"</string>
@@ -440,6 +429,17 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Open settings"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Other device"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Toggle Overview"</string>
+    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Priority modes"</string>
+    <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Done"</string>
+    <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Settings"</string>
+    <string name="zen_mode_on" msgid="9085304934016242591">"On"</string>
+    <string name="zen_mode_off" msgid="1736604456618147306">"Off"</string>
+    <!-- no translation found for zen_mode_set_up (7457957033034460064) -->
+    <skip />
+    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
+    <skip />
+    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
+    <skip />
     <string name="zen_priority_introduction" msgid="3159291973383796646">"You won\'t be disturbed by sounds and vibrations, except from alarms, reminders, events and callers you specify. You\'ll still hear anything you choose to play including music, videos and games."</string>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"You won\'t be disturbed by sounds and vibrations, except from alarms. You\'ll still hear anything you choose to play including music, videos and games."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Customise"</string>
@@ -478,6 +478,8 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Charging slowly • Full in <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Charging • Full in <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Widgets on lock screen"</string>
+    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
+    <skip />
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Swipe left to start the communal tutorial"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Customise"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Dismiss"</string>
@@ -504,10 +506,9 @@
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"select widget"</string>
     <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"remove widget"</string>
     <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"place selected widget"</string>
-    <!-- no translation found for communal_widget_picker_title (1953369090475731663) -->
-    <skip />
-    <!-- no translation found for communal_widget_picker_description (490515450110487871) -->
-    <skip />
+    <string name="communal_widget_picker_title" msgid="1953369090475731663">"Lock screen widgets"</string>
+    <string name="communal_widget_picker_description" msgid="490515450110487871">"Anyone can view widgets on your lock screen, even if your tablet\'s locked."</string>
+    <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"unselect widget"</string>
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Lock screen widgets"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"To open an app using a widget, you\'ll need to verify that it\'s you. Also, bear in mind that anyone can view them, even when your tablet\'s locked. Some widgets may not have been intended for your lock screen and may be unsafe to add here."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Got it"</string>
@@ -564,8 +565,8 @@
     <string name="media_projection_action_text" msgid="3634906766918186440">"Start now"</string>
     <string name="empty_shade_text" msgid="8935967157319717412">"No notifications"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"No new notifications"</string>
-    <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"Adaptive notifications is on"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"Your device now lowers the volume and reduces pop-ups on the screen for up to 2 minutes when you receive many notifications in a short time span."</string>
+    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Notification cooldown is on"</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Your device volume and alerts are reduced automatically for up to 2 minutes when you get too many notifications at once."</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Turn off"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Unlock to see older notifications"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"This device is managed by your parent"</string>
@@ -732,8 +733,7 @@
     <string name="notification_alert_title" msgid="3656229781017543655">"Default"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Automatic"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"No sound or vibration"</string>
-    <!-- no translation found for notification_conversation_summary_low (3855696451919728790) -->
-    <skip />
+    <string name="notification_conversation_summary_low" msgid="3855696451919728790">"No sound or vibration but still appears in the conversation section"</string>
     <string name="notification_channel_summary_default" msgid="777294388712200605">"May ring or vibrate based on device settings"</string>
     <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"May ring or vibrate based on device settings. Conversations from <xliff:g id="APP_NAME">%1$s</xliff:g> bubble by default."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Have the system determine if this notification should make sound or vibration"</string>
@@ -790,8 +790,7 @@
     <string name="keyboard_key_page_up" msgid="173914303254199845">"Page Up"</string>
     <string name="keyboard_key_page_down" msgid="9035902490071829731">"Page Down"</string>
     <string name="keyboard_key_forward_del" msgid="5325501825762733459">"Delete"</string>
-    <!-- no translation found for keyboard_key_esc (6230365950511411322) -->
-    <skip />
+    <string name="keyboard_key_esc" msgid="6230365950511411322">"Esc"</string>
     <string name="keyboard_key_move_home" msgid="3496502501803911971">"Home"</string>
     <string name="keyboard_key_move_end" msgid="99190401463834854">"End"</string>
     <string name="keyboard_key_insert" msgid="4621692715704410493">"Insert"</string>
@@ -1233,7 +1232,6 @@
     <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="mobile_data_settings_title" msgid="3955246641380064901">"Mobile data"</string>
-    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Connected"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Temporarily connected"</string>
     <string name="mobile_data_poor_connection" msgid="819617772268371434">"Poor connection"</string>
@@ -1374,8 +1372,7 @@
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Split screen"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Input"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"App shortcuts"</string>
-    <!-- no translation found for shortcut_helper_category_current_app_shortcuts (4017840565974573628) -->
-    <skip />
+    <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Current app"</string>
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accessibility"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Keyboard shortcuts"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Search shortcuts"</string>
@@ -1388,13 +1385,27 @@
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Done"</string>
     <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Great work!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Go back"</string>
-    <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"To go back, swipe left or right using 3 fingers anywhere on the touchpad."</string>
+    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
+    <skip />
+    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
+    <skip />
     <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Touchpad showing 3 fingers moving right and left"</string>
     <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Device screen showing animation for back gesture"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Keyboard backlight"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Level %1$d of %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Home controls"</string>
     <string name="home_controls_dream_description" msgid="4644150952104035789">"Quickly access your home controls as a screensaver"</string>
-    <!-- no translation found for volume_undo_action (5815519725211877114) -->
-    <skip />
+    <string name="volume_undo_action" msgid="5815519725211877114">"Undo"</string>
+    <string name="back_edu_toast_content" msgid="4530314597378982956">"To go back, swipe left or right with three fingers on the touchpad"</string>
+    <string name="home_edu_toast_content" msgid="3381071147871955415">"To go home, swipe up with three fingers on the touchpad"</string>
+    <string name="overview_edu_toast_content" msgid="5797030644017804518">"To view recent apps, swipe up and hold with three fingers on the touchpad"</string>
+    <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"To view all your apps, press the action key on your keyboard"</string>
+    <string name="back_edu_notification_title" msgid="5624780717751357278">"Use your touchpad to go back"</string>
+    <string name="back_edu_notification_content" msgid="2497557451540954068">"Swipe left or right using three fingers. Tap to learn more gestures."</string>
+    <string name="home_edu_notification_title" msgid="6097902076909654045">"Use your touchpad to go home"</string>
+    <string name="home_edu_notification_content" msgid="6631697734535766588">"Swipe up using three fingers. Tap to learn more gestures."</string>
+    <string name="overview_edu_notification_title" msgid="1265824157319562406">"Use your touchpad to view recent apps"</string>
+    <string name="overview_edu_notification_content" msgid="3578204677648432500">"Swipe up and hold using three fingers. Tap to learn more gestures."</string>
+    <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Use your keyboard to view all apps"</string>
+    <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Press the action key at any time. Tap to learn more gestures."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-en-rIN/tiles_states_strings.xml b/packages/SystemUI/res/values-en-rIN/tiles_states_strings.xml
index 27c23aa..39dd7c8 100644
--- a/packages/SystemUI/res/values-en-rIN/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/tiles_states_strings.xml
@@ -56,9 +56,6 @@
     <item msgid="5376619709702103243">"Off"</item>
     <item msgid="4875147066469902392">"On"</item>
   </string-array>
-    <!-- no translation found for tile_states_modes:0 (7764936419245199023) -->
-    <!-- no translation found for tile_states_modes:1 (2004750556637773692) -->
-    <!-- no translation found for tile_states_modes:2 (8968530753931637871) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Unavailable"</item>
     <item msgid="5044688398303285224">"Off"</item>
diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml
index e4a6603..f99ab9e 100644
--- a/packages/SystemUI/res/values-en-rXC/strings.xml
+++ b/packages/SystemUI/res/values-en-rXC/strings.xml
@@ -388,6 +388,9 @@
     <string name="performance" msgid="6552785217174378320">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‏‎‏‏‏‏‎‎‎‎‎‎‏‎‏‎‏‏‎‏‏‎‏‏‏‏‏‏‎‏‎‏‏‏‎‎‎‏‏‏‎‎‎‎‎‏‎‏‏‏‎‏‎‏‎‎‎‎‎Performance‎‏‎‎‏‎"</string>
     <string name="user_interface" msgid="3712869377953950887">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‏‏‏‎‎‎‎‏‏‎‏‏‎‎‎‎‏‏‎‎‎‎‎‏‏‏‎‎‎‏‎‎‏‎‎‏‎‏‏‏‏‎‏‏‏‎‏‏‎‎‏‎‏‎‎‏‏‏‎User Interface‎‏‎‎‏‎"</string>
     <string name="thermal" msgid="6758074791325414831">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‎‏‏‏‎‎‏‎‎‏‏‎‎‎‎‎‎‏‎‎‏‏‎‏‏‏‏‎‎‎‎‏‏‎‎‏‏‏‏‎‎‏‎‎‏‏‎‏‎‏‏‎‏‎‏‏‏‏‎Thermal‎‏‎‎‏‎"</string>
+    <string name="custom" msgid="3337456985275158299">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‏‎‎‏‎‏‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‎‎‏‎‎‎‎‎‏‏‎‎‎‏‏‏‏‎‎‎‏‏‎‎‏‏‏‏‎‎‎‏‏‎‏‏‎Custom‎‏‎‎‏‎"</string>
+    <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‎‎‎‎‏‏‎‎‏‏‏‎‎‎‎‎‎‏‎‎‎‎‏‏‎‎‏‎‎‏‎‎‎‎‏‎‏‏‎‎‏‎‎‎‎‏‏‎‎‎‎‏‎‏‏‎‏‎‎Custom Trace Settings‎‏‎‎‏‎"</string>
+    <string name="restore_default" msgid="5259420807486239755">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‎‎‏‏‏‏‏‏‎‏‎‎‏‏‎‏‏‏‎‏‎‏‏‎‏‏‏‏‎‏‏‎‏‎‏‎‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‎‎‎‏‎‏‏‎Restore Default‎‏‎‎‏‎"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‎‏‏‎‎‎‏‎‎‏‎‏‎‎‎‏‎‎‎‏‏‏‎‎‎‏‏‎‏‏‎‏‏‎‏‎‎‏‎‏‏‎‎‎‏‏‏‏‏‏‏‎‏‎‏‏‏‏‎One-handed mode‎‏‎‎‏‎"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‎‎‏‏‏‏‏‏‎‏‏‎‏‏‎‎‏‏‏‏‏‏‎‎‎‎‏‏‎‎‏‎‏‎‏‏‏‏‏‎‎‎‏‎‏‎‎‎‎‎‎‏‏‎‎‎‎‏‎Hearing devices‎‏‎‎‏‎"</string>
     <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‏‎‎‏‏‏‏‎‎‎‎‏‏‎‎‎‏‏‎‎‏‎‏‏‎‎‎‏‎‎‎‏‎‎‎‏‏‏‎‏‎‏‎‎‏‏‏‏‎‎‏‏‎‏‎‏‎‏‎Active‎‏‎‎‏‎"</string>
@@ -426,6 +429,14 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‏‎‎‎‏‏‎‏‏‎‏‎‎‏‏‏‏‎‎‎‏‎‏‏‎‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‎‏‏‏‎‏‎‏‏‎‎‏‎‎‏‏‎Open Settings‎‏‎‎‏‎"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‎‎‏‎‎‏‏‎‎‏‎‏‎‏‏‎‎‏‎‎‏‏‏‎‏‏‎‎‏‏‏‎‏‎‏‏‎‏‎‏‏‎‎‏‏‏‏‏‏‏‏‏‎‎‎‎‎‏‎Other device‎‏‎‎‏‎"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‎‏‏‏‎‎‎‎‏‎‎‎‏‏‏‏‎‎‎‎‎‏‏‎‎‏‏‎‏‏‎‏‎‏‎‎‏‎‏‏‏‎‎‏‏‎‎‏‏‏‎‎‏‎‎‎‏‏‎Toggle Overview‎‏‎‎‏‎"</string>
+    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‎‏‏‎‏‏‏‎‎‎‎‎‏‏‏‎‏‎‎‎‏‎‏‏‏‏‎‎‏‏‏‎‏‏‏‏‎‏‏‎‎‎‎‏‏‏‎‎‎‎‏‏‎‏‎‏‏‏‎Priority modes‎‏‎‎‏‎"</string>
+    <string name="zen_modes_dialog_done" msgid="6654130880256438950">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‎‎‎‏‎‏‏‎‎‎‎‎‏‏‏‎‎‎‏‏‎‎‎‏‏‏‏‏‎‎‏‏‏‎‏‎‏‎‎‏‎‎‎‏‏‏‎‎‏‎‏‎‏‎‎‏‏‎‎Done‎‏‎‎‏‎"</string>
+    <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‎‎‎‎‎‎‏‏‏‏‏‎‏‎‎‏‏‎‎‏‎‏‎‏‏‎‎‏‎‏‏‏‎‏‎‏‏‎‏‎‎‏‎‏‎‏‎‏‏‎‏‏‏‏‏‎‎‏‎Settings‎‏‎‎‏‎"</string>
+    <string name="zen_mode_on" msgid="9085304934016242591">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‏‎‎‎‎‏‎‏‎‏‎‏‏‏‏‏‎‎‏‎‏‏‎‎‏‏‏‎‎‏‎‏‎‎‎‎‏‎‎‏‎‎‎‏‎‏‎‎‏‏‏‎‎‏‏‏‏‏‎On‎‏‎‎‏‎"</string>
+    <string name="zen_mode_off" msgid="1736604456618147306">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‎‎‎‎‎‏‏‎‎‏‏‎‏‎‏‎‎‎‏‎‎‎‏‏‎‏‎‏‏‎‏‎‎‎‎‎‎‏‎‏‎‏‏‎‏‏‏‏‎‏‏‏‏‎‏‎‏‎‎Off‎‏‎‎‏‎"</string>
+    <string name="zen_mode_set_up" msgid="7457957033034460064">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‏‏‎‏‏‏‏‏‏‏‏‏‏‏‏‏‎‎‎‏‏‎‏‎‎‎‎‏‎‏‏‎‎‎‎‎‏‎‎‏‎‏‎‎‏‎‏‎‏‏‏‎‏‎‎‎‎‎‎Set up‎‏‎‎‏‎"</string>
+    <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‎‎‏‎‎‏‎‎‎‎‎‎‏‏‎‏‏‏‏‎‎‎‏‏‏‏‎‎‎‏‏‎‏‏‏‏‏‎‎‏‏‏‏‏‎‏‎‎‏‏‎‏‎‎‏‎‎‎‎Manage in settings‎‏‎‎‏‎"</string>
+    <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‏‎‏‎‎‏‎‎‎‎‎‎‏‎‏‏‏‎‎‏‎‏‎‏‎‏‏‏‎‏‎‎‎‎‎‎‎‎‏‏‏‎‏‏‏‏‏‎‎‎‎‎‎‏‏‎‎‎‎No active modes‎‏‎‎‏‎}=1{‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‏‎‏‎‎‏‎‎‎‎‎‎‏‎‏‏‏‎‎‏‎‏‎‏‎‏‏‏‎‏‎‎‎‎‎‎‎‎‏‏‏‎‏‏‏‏‏‎‎‎‎‎‎‏‏‎‎‎‎‎‏‎‎‏‏‎{mode}‎‏‎‎‏‏‏‎ is active‎‏‎‎‏‎}other{‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‏‎‏‎‎‏‎‎‎‎‎‎‏‎‏‏‏‎‎‏‎‏‎‏‎‏‏‏‎‏‎‎‎‎‎‎‎‎‏‏‏‎‏‏‏‏‏‎‎‎‎‎‎‏‏‎‎‎‎# modes are active‎‏‎‎‏‎}}"</string>
     <string name="zen_priority_introduction" msgid="3159291973383796646">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‏‏‏‏‎‏‏‎‎‎‎‎‎‎‏‏‏‏‎‏‎‎‏‏‏‏‏‎‏‎‎‏‏‏‎‏‎‎‏‎‎‎‏‎‏‏‎‏‏‏‏‎‏‎‎‏‏‎‎You won\'t be disturbed by sounds and vibrations, except from alarms, reminders, events, and callers you specify. You\'ll still hear anything you choose to play including music, videos, and games.‎‏‎‎‏‎"</string>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‏‏‎‏‎‏‎‏‎‏‏‎‎‏‏‏‎‏‎‏‎‏‎‏‏‏‎‎‏‎‎‎‏‏‎‎‎‎‏‏‏‎‎‏‎‏‏‎‎‎‎‎‏‏‎‏‏‎‎You won\'t be disturbed by sounds and vibrations, except from alarms. You\'ll still hear anything you choose to play including music, videos, and games.‎‏‎‎‏‎"</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‎‏‎‎‏‎‏‎‏‎‎‏‏‎‎‎‏‎‏‎‎‏‎‎‎‏‏‎‏‏‏‏‎‎‏‎‎‏‎‎‎‏‏‏‎‏‏‎‏‎‎‎‏‎‎‏‏‏‎Customize‎‏‎‎‏‎"</string>
@@ -464,6 +475,7 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‎‎‎‎‏‏‎‎‎‎‏‎‏‏‎‎‏‎‎‎‎‎‏‏‏‎‎‏‎‏‏‎‎‏‎‏‎‏‎‎‏‎‏‏‎‎‎‎‏‎‎‏‎‎‏‎‎‏‎‎‏‎‎‏‏‎<xliff:g id="PERCENTAGE">%2$s</xliff:g>‎‏‎‎‏‏‏‎ • Charging slowly • Full in ‎‏‎‎‏‏‎<xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‏‏‏‎‏‏‎‏‎‎‏‎‏‎‏‎‎‏‏‏‏‏‎‎‏‎‏‎‎‏‏‏‎‏‏‎‏‏‎‎‏‎‏‏‎‏‎‎‏‎‎‏‎‏‎‏‏‏‎‎‏‎‎‏‏‎<xliff:g id="PERCENTAGE">%2$s</xliff:g>‎‏‎‎‏‏‏‎ • Charging • Full in ‎‏‎‎‏‏‎<xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‏‎‏‏‎‎‎‏‎‎‎‏‏‎‏‎‎‎‎‏‎‏‏‏‎‎‏‎‏‏‎‏‎‏‎‎‏‎‎‏‏‏‎‎‏‏‎‏‏‎‏‏‎‎‏‎‎‏‎Widgets on lock screen‎‏‎‎‏‎"</string>
+    <string name="accessibility_announcement_communal_widget_added" msgid="6911593106099328271">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‏‏‏‏‏‎‏‎‏‎‏‏‏‎‏‎‎‏‎‏‎‎‏‏‏‏‏‎‎‏‎‎‎‏‎‎‎‏‎‏‏‎‏‏‎‏‎‏‎‏‎‎‎‎‏‏‏‏‎‎‏‎‎‏‏‎<xliff:g id="WIDGET_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ widget added to lock screen‎‏‎‎‏‎"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‏‎‎‏‏‏‏‏‎‏‏‏‏‎‏‎‎‎‎‎‎‎‏‏‏‏‎‎‎‎‎‎‎‏‏‎‏‏‏‎‎‏‎‎‏‎‏‎‏‏‏‏‏‎‏‎‏‏‎Swipe left to start the communal tutorial‎‏‎‎‏‎"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‎‏‏‎‏‏‏‎‏‎‏‎‎‎‏‏‎‏‎‏‏‏‎‏‎‎‏‏‎‎‎‏‏‏‏‎‎‏‏‎‏‏‏‏‎‏‎‏‎‎‏‎‏‎‎‏‏‎‎Customize‎‏‎‎‏‎"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‏‎‏‏‎‏‏‏‏‏‏‎‏‎‎‎‏‏‎‏‎‏‏‏‏‏‎‏‏‏‏‎‎‎‏‏‏‏‎‏‏‏‎‏‏‎‎‏‏‏‎‏‏‏‏‎‏‎‎Dismiss‎‏‎‎‏‎"</string>
@@ -492,6 +504,7 @@
     <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‏‎‏‎‎‏‎‎‎‎‏‎‎‏‏‎‎‎‎‏‏‏‎‎‏‎‎‏‏‎‎‎‎‎‎‏‏‏‎‎‎‎‏‏‏‏‎‎‎‏‎‎‎‏‎‎‏‎‎place selected widget‎‏‎‎‏‎"</string>
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‏‏‎‎‎‏‏‎‏‏‏‏‎‎‎‎‏‎‏‏‎‏‎‏‏‎‏‎‏‎‎‎‎‏‎‎‎‎‏‎‎‎‏‎‏‎‏‎‏‎‏‏‎‎‏‏‏‏‎Lock screen widgets‎‏‎‎‏‎"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‏‎‏‏‎‎‏‏‏‎‏‎‏‎‏‎‎‏‎‎‏‏‎‎‏‏‏‏‏‏‏‎‎‎‏‎‏‎‏‏‎‏‎‎‏‎‏‏‎‏‎‎‏‏‏‏‏‏‎Anyone can view widgets on your lock screen, even if your tablet\'s locked.‎‏‎‎‏‎"</string>
+    <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‏‎‎‏‏‏‎‏‎‏‎‏‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‎‎‎‏‏‏‎‏‎‏‏‏‏‏‏‏‏‎‏‎‏‏‎‎‏‏‎‏‎‎unselect widget‎‏‎‎‏‎"</string>
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‏‏‏‏‏‏‏‎‎‏‎‎‎‎‎‎‏‎‏‏‏‎‏‏‎‎‏‎‏‏‏‏‎‏‏‎‎‎‏‎‎‏‎‏‏‎‏‎‎‎‏‏‎‎‎‏‎‎‎Lock screen widgets‎‏‎‎‏‎"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‏‏‏‏‎‎‎‎‎‏‎‏‏‏‎‎‏‏‎‎‎‏‎‎‎‏‎‏‎‎‎‎‎‎‏‎‎‎‏‏‏‎‎‏‏‏‏‏‏‏‏‏‏‎‏‏‎‏‎To open an app using a widget, you’ll need to verify it’s you. Also, keep in mind that anyone can view them, even when your tablet’s locked. Some widgets may not have been intended for your lock screen and may be unsafe to add here.‎‏‎‎‏‎"</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‎‏‎‏‏‎‎‎‎‏‏‏‎‏‏‏‎‏‎‏‏‎‏‏‎‎‎‎‏‎‏‏‎‏‎‏‎‎‏‏‎‏‎‎‏‏‏‏‎‎‏‏‎‏‎‎‎‏‎Got it‎‏‎‎‏‎"</string>
@@ -548,8 +561,8 @@
     <string name="media_projection_action_text" msgid="3634906766918186440">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‏‎‎‏‏‏‎‎‎‏‏‏‎‎‏‎‎‎‎‏‏‏‎‎‏‎‎‎‏‎‏‎‎‏‏‏‎‏‎‎‏‎‎‏‏‏‎‏‎‏‏‏‎‎‏‎‎‎‎Start now‎‏‎‎‏‎"</string>
     <string name="empty_shade_text" msgid="8935967157319717412">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‎‎‎‎‎‎‎‎‏‎‏‏‏‎‏‏‏‎‏‏‎‎‏‎‎‎‏‏‏‎‏‎‎‎‏‎‎‏‏‎‎‏‎‏‎‏‎‎‏‎‎‎‏‎‎‏‎‎‎No notifications‎‏‎‎‏‎"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‎‏‎‏‏‏‏‏‎‏‎‎‏‎‎‏‎‎‏‎‎‏‏‎‎‏‏‏‏‏‏‏‏‏‏‏‎‏‏‏‏‎‏‏‏‏‎‎‎‏‎‎‎‎‏‎‏‎‎No new notifications‎‏‎‎‏‎"</string>
-    <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‏‏‎‏‏‎‎‏‎‎‏‎‏‎‏‎‎‎‎‎‏‏‎‏‎‏‎‎‏‏‏‎‏‏‏‎‎‎‎‎‎‎‏‎‎‏‏‎‏‎‎‏‎‏‎‏‎‏‎Adaptive notifications is on‎‏‎‎‏‎"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‏‏‎‎‏‎‎‎‎‎‎‏‏‏‎‏‏‏‎‎‏‎‎‎‏‎‎‎‏‎‏‎‎‏‏‎‎‎‎‏‏‏‎‏‏‎‎‎‏‏‎‏‎‏‏‎‎‏‎Your device now lowers the volume and reduces pop-ups on the screen for up to two minutes when you receive many notifications in a short time span.‎‏‎‎‏‎"</string>
+    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‎‎‎‎‎‏‏‏‏‎‎‏‎‎‎‎‏‏‎‎‎‏‎‎‏‎‎‎‎‎‏‎‏‏‎‏‏‎‎‏‏‏‎‏‎‏‎‎‏‏‎‎‏‎‏‎‎‎‎Notification cooldown is on‎‏‎‎‏‎"</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‏‏‎‏‏‏‎‏‎‏‏‏‏‏‎‏‏‏‏‏‏‎‏‏‏‏‏‏‏‎‏‎‏‎‎‎‎‎‏‏‏‏‎‎‎‏‎‎‏‎‎‏‏‎‎‎‏‎‎Your device volume and alerts are reduced automatically for up to 2 minutes when you get too many notifications at once.‎‏‎‎‏‎"</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‎‏‏‎‏‎‎‎‏‎‏‎‏‎‏‏‎‏‎‏‎‏‎‎‏‏‏‏‎‏‏‏‎‎‏‏‎‏‏‏‏‏‏‏‏‎‎‎‎‎‎‏‏‎‏‏‏‏‎Turn off‎‏‎‎‏‎"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‏‏‎‎‏‏‏‏‎‎‏‏‎‎‎‎‎‏‏‏‏‎‏‏‎‎‏‏‎‎‎‏‎‎‏‏‎‎‏‏‏‏‎‎‎‎‎‏‏‎‎‎‎‎‏‎‏‏‎Unlock to see older notifications‎‏‎‎‏‎"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‎‏‎‏‎‏‎‏‏‎‏‏‎‎‏‏‎‏‎‏‎‏‏‏‎‎‎‏‎‎‏‏‏‎‏‎‏‏‎‏‎‏‎‏‏‎‏‎‎‎‏‏‏‏‎‎‎‎‎This device is managed by your parent‎‏‎‎‏‎"</string>
@@ -716,8 +729,7 @@
     <string name="notification_alert_title" msgid="3656229781017543655">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‏‎‏‎‏‏‏‏‎‏‏‎‎‎‏‎‎‏‏‎‎‏‏‏‎‏‎‏‎‎‎‎‏‎‏‏‎‎‎‎‎‏‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‏‎Default‎‏‎‎‏‎"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‏‏‏‏‏‏‏‎‏‎‏‎‎‏‎‎‎‎‏‏‏‎‎‏‏‏‏‎‎‏‏‏‏‎‎‎‏‏‏‏‏‎‏‏‏‏‏‏‏‏‏‎‎‏‏‏‎‎‎Automatic‎‏‎‎‏‎"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‏‏‎‏‏‏‎‏‎‎‎‏‏‎‎‎‏‎‎‏‎‎‏‏‏‎‎‏‏‏‎‎‏‎‏‎‎‏‎‏‎‎‏‏‎‎‎‏‎‎‎‏‎‏‎‏‏‎‎No sound or vibration‎‏‎‎‏‎"</string>
-    <!-- no translation found for notification_conversation_summary_low (3855696451919728790) -->
-    <skip />
+    <string name="notification_conversation_summary_low" msgid="3855696451919728790">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‎‏‏‎‎‎‎‎‏‎‎‎‏‎‏‏‏‏‎‏‏‏‏‏‏‎‏‎‏‎‎‎‏‎‎‏‏‎‏‎‎‎‏‏‎‏‎‎‎‎‏‎‎‏‎‏‏‎‎No sound or vibration but still appears in the conversation section‎‏‎‎‏‎"</string>
     <string name="notification_channel_summary_default" msgid="777294388712200605">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‎‏‏‎‎‏‎‎‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‎‏‎‏‎‏‎‎‎‏‏‎‎‎‎‏‎‏‏‎‎‏‏‎‎‏‏‏‎‏‎May ring or vibrate based on device settings‎‏‎‎‏‎"</string>
     <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‎‎‎‏‎‏‎‏‎‎‎‏‎‎‎‎‏‏‏‏‏‎‏‎‏‎‎‎‎‏‎‎‎‏‎‎‎‎‏‎‏‎‏‎‏‎‏‎‎‎‎‎‎‏‎‎‎‎‎May ring or vibrate based on device settings. Conversations from ‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ bubble by default.‎‏‎‎‏‎"</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‎‎‏‎‏‎‏‏‎‎‎‏‎‏‎‎‎‎‎‎‎‏‎‏‎‎‏‎‎‎‎‎‏‎‏‎‏‎‏‎‏‎‎‏‎‎‏‏‏‏‏‎‎‎‏‎‏‏‎Have the system determine if this notification should make sound or vibration‎‏‎‎‏‎"</string>
@@ -1216,7 +1228,6 @@
     <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="mobile_data_settings_title" msgid="3955246641380064901">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‏‎‏‏‏‎‎‎‏‏‏‏‎‏‏‎‏‏‏‏‎‏‏‏‎‏‎‏‎‏‎‎‏‎‏‎‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‎‎‎‏‎‏‎Mobile data‎‏‎‎‏‎"</string>
-    <string name="preference_summary_default_combination" msgid="8453246369903749670">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‏‎‏‎‎‏‏‏‏‏‏‏‏‎‏‏‎‏‏‎‎‏‎‎‏‎‏‎‎‎‏‎‎‏‏‏‏‏‎‎‏‎‎‏‎‏‎‏‎‎‎‏‎‎‏‏‎‎‎‏‎‎‏‏‎<xliff:g id="STATE">%1$s</xliff:g>‎‏‎‎‏‏‏‎ / ‎‏‎‎‏‏‎<xliff:g id="NETWORKMODE">%2$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‎‏‎‎‎‏‏‎‏‏‏‎‎‎‎‎‎‎‏‎‏‎‏‎‎‏‏‎‏‏‏‎‎‎‏‎‎‏‎‏‏‏‏‎‎‎‏‎‎‏‏‏‏‏‎‎‏‏‎Connected‎‏‎‎‏‎"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‏‏‏‎‏‏‎‎‏‏‏‎‏‏‏‏‏‏‎‎‏‏‏‏‏‏‏‏‎‎‏‎‎‏‏‎‏‏‏‎‎‎‎‎‏‏‏‏‎‎‏‎‏‎‏‎‎‎‎Temporarily connected‎‏‎‎‏‎"</string>
     <string name="mobile_data_poor_connection" msgid="819617772268371434">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‏‎‏‎‏‏‏‏‏‏‏‎‏‏‏‏‎‎‎‎‎‎‏‎‏‎‏‏‏‎‏‏‏‏‎‏‎‎‏‏‏‎‎‏‏‏‏‎‏‏‏‏‎‏‎‏‎‎Poor connection‎‏‎‎‏‎"</string>
@@ -1370,7 +1381,10 @@
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‎‏‎‎‏‏‏‎‎‎‏‏‏‏‎‎‎‎‎‎‏‎‎‏‏‏‎‏‎‏‎‎‏‎‎‏‏‎‏‎‏‏‎‏‎‎‎‏‏‏‎‎‏‏‎‏‏‏‏‎Done‎‏‎‎‏‎"</string>
     <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‏‎‎‏‏‎‎‏‎‏‏‎‏‏‏‏‎‏‎‏‎‏‎‏‏‏‎‎‎‎‏‏‎‏‎‏‏‏‏‎‎‎‎‏‏‏‎‎‎‎‎‎‎‎‎‏‏‏‎Great job!‎‏‎‎‏‎"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‏‏‏‏‏‎‏‎‎‎‎‎‏‏‏‎‎‏‏‎‎‎‎‏‎‏‏‎‏‎‏‏‎‏‎‏‏‎‎‏‏‎‎‏‎‎‎‏‏‏‎‎‏‏‏‏‏‏‎Go back‎‏‎‎‏‎"</string>
-    <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‏‎‏‎‎‏‏‎‎‏‎‎‎‏‎‏‏‎‎‎‏‏‏‏‏‏‏‎‏‏‏‎‏‎‎‎‏‏‎‏‏‏‏‏‏‎‎‏‏‏‎‏‏‎‏‎‎‎‎To go back, swipe left or right using three fingers anywhere on the touchpad.‎‏‎‎‏‎"</string>
+    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
+    <skip />
+    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
+    <skip />
     <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‎‎‏‎‏‏‏‎‎‎‏‏‎‏‏‏‎‎‏‎‏‏‎‏‎‎‏‏‏‏‏‎‎‏‏‎‏‏‏‏‏‎‎‎‏‎‏‎‏‎‏‎‎‎‏‏‏‎‎Touchpad showing three fingers moving right and left‎‏‎‎‏‎"</string>
     <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‎‎‎‎‎‎‎‎‏‏‏‎‏‏‎‎‏‏‏‏‏‎‎‏‏‎‏‎‏‎‎‏‎‎‎‏‏‏‏‏‏‏‎‎‏‏‏‏‎‏‏‏‏‎‎‏‏‎‎Device screen showing animation for back gesture‎‏‎‎‏‎"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‏‎‏‏‎‎‏‏‏‏‏‏‏‏‎‏‏‏‎‏‎‎‏‎‎‎‏‏‎‎‎‎‎‎‏‎‏‏‎‎‏‏‎‎‏‎‏‏‏‎‎‎‏‏‎‏‎‎‎Keyboard backlight‎‏‎‎‏‎"</string>
@@ -1378,4 +1392,16 @@
     <string name="home_controls_dream_label" msgid="6567105701292324257">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‏‏‎‎‏‎‎‎‏‏‎‎‎‎‏‎‏‏‏‏‎‏‎‏‏‏‎‏‎‏‏‏‏‏‏‏‏‏‏‎‎‎‏‎‏‏‎‏‎‏‏‎‏‎‎‎‎‏‎Home Controls‎‏‎‎‏‎"</string>
     <string name="home_controls_dream_description" msgid="4644150952104035789">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‎‎‎‏‏‏‎‎‏‏‎‏‎‏‎‏‏‎‏‎‏‎‏‏‏‏‎‏‏‎‎‏‏‏‎‏‏‏‎‏‎‎‎‎‎‎‎‏‎‏‏‏‎‎‏‏‎‏‎Quickly access your home controls as a screensaver‎‏‎‎‏‎"</string>
     <string name="volume_undo_action" msgid="5815519725211877114">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‎‎‏‎‏‏‎‏‎‎‏‏‏‎‎‎‎‎‎‏‏‎‎‎‎‎‏‏‎‎‏‏‎‏‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‏‏‏‏‎‏‎‎Undo‎‏‎‎‏‎"</string>
+    <string name="back_edu_toast_content" msgid="4530314597378982956">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‏‎‏‏‎‏‏‏‏‎‏‏‏‎‏‎‎‏‎‎‎‏‏‏‏‏‏‎‏‎‏‎‎‏‏‎‎‎‏‎‏‎‏‎‎‎‎‏‎‎‎‎‏‎‏‏‎‎‎To go back, swipe left or right with three fingers on the touchpad‎‏‎‎‏‎"</string>
+    <string name="home_edu_toast_content" msgid="3381071147871955415">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‏‎‏‏‏‎‏‎‏‏‏‏‏‏‏‎‏‎‎‏‎‎‏‎‏‏‏‎‎‏‏‎‎‏‎‎‏‏‏‎‏‎‏‏‏‎‏‎‎‏‏‏‎‏‎‏‏‏‎To go home, swipe up with three fingers on the touchpad‎‏‎‎‏‎"</string>
+    <string name="overview_edu_toast_content" msgid="5797030644017804518">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‎‎‎‏‏‏‎‎‏‏‎‎‏‏‎‎‎‎‏‎‏‎‏‎‎‎‎‎‏‎‎‎‎‎‏‎‏‏‏‏‎‎‎‎‏‏‎‏‎‎‏‏‏‎‎‏‏‎‎To view recent apps, swipe up and hold with three fingers on the touchpad‎‏‎‎‏‎"</string>
+    <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‏‎‎‎‏‏‏‎‏‎‏‎‎‎‎‎‏‎‏‏‏‏‎‏‏‎‎‎‎‏‎‎‏‎‏‏‏‎‏‎‎‎‎‏‏‎‎‎‏‏‎‎‏‎‏‎‏‎‎To view all your apps, press the action key on your keyboard‎‏‎‎‏‎"</string>
+    <string name="back_edu_notification_title" msgid="5624780717751357278">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‏‎‎‎‎‎‏‏‏‏‎‎‏‏‏‏‎‎‎‏‎‎‎‎‏‎‏‏‏‎‏‏‎‏‎‎‎‎‏‎‎‏‎‏‎‎‏‏‏‏‎‏‎‏‏‏‏‎‎Use your touchpad to go back‎‏‎‎‏‎"</string>
+    <string name="back_edu_notification_content" msgid="2497557451540954068">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‏‎‏‎‏‎‏‎‎‏‎‎‎‏‏‎‏‏‎‏‎‎‎‏‎‏‎‎‎‎‎‎‎‏‎‎‏‏‏‎‏‎‎‏‏‏‏‎‏‏‏‏‎‏‎‏‎‎‎Swipe left or right using three fingers. Tap to learn more gestures.‎‏‎‎‏‎"</string>
+    <string name="home_edu_notification_title" msgid="6097902076909654045">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‎‎‏‎‏‎‎‎‎‎‎‎‎‏‏‎‎‏‏‎‏‎‎‎‎‏‏‎‎‎‎‎‎‏‎‏‎‏‎‎‎‏‎‎‎‏‎‎‎‎‎‎‎‏‏‏‎‏‎Use your touchpad to go home‎‏‎‎‏‎"</string>
+    <string name="home_edu_notification_content" msgid="6631697734535766588">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‎‎‎‎‏‎‏‏‏‏‏‎‏‎‎‎‎‎‎‏‏‎‎‏‏‎‎‎‏‎‎‎‏‏‎‎‎‏‎‎‎‏‏‏‏‎‎‎Swipe up using three fingers. Tap to learn more gestures.‎‏‎‎‏‎"</string>
+    <string name="overview_edu_notification_title" msgid="1265824157319562406">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‎‏‏‎‎‏‎‎‎‏‎‎‎‏‏‏‎‎‎‏‎‏‏‏‎‏‎‏‎‎‏‏‎‏‎‎‏‎‎‏‎‎‎‎‎‎‎‏‎‎‏‎‏‎‎‏‏‎‎Use your touchpad to view recent apps‎‏‎‎‏‎"</string>
+    <string name="overview_edu_notification_content" msgid="3578204677648432500">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‎‏‏‎‏‎‏‎‎‎‎‏‎‏‎‏‏‎‎‎‏‏‎‎‏‎‎‎‏‏‏‎‎‎‏‏‏‎‎‎‎‎‎‎‏‏‏‏‎‏‎‏‏‏‎‏‎‎‎Swipe up and hold using three fingers. Tap to learn more gestures.‎‏‎‎‏‎"</string>
+    <string name="all_apps_edu_notification_title" msgid="372262997265569063">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‎‏‎‎‏‎‏‎‏‎‏‎‎‎‏‎‏‏‎‎‏‏‏‎‎‏‎‏‎‎‎‎‏‏‎‎‎‎‏‎‎‏‎‏‎‏‎‏‎‏‎‎‏‎‎‏‏‏‎Use your keyboard to view all apps‎‏‎‎‏‎"</string>
+    <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‎‏‎‎‏‎‏‏‎‎‎‏‎‏‎‏‎‏‎‏‏‏‎‎‎‏‎‏‎‎‎‏‏‏‎‎‎‏‎‎‎‏‏‎‎‏‎‎‏‏‎‏‏‏‎‎‎‏‎Press the action key at any time. Tap to learn more gestures.‎‏‎‎‏‎"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-en-rXC/tiles_states_strings.xml b/packages/SystemUI/res/values-en-rXC/tiles_states_strings.xml
index a6a5fdd..35ab88b 100644
--- a/packages/SystemUI/res/values-en-rXC/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-en-rXC/tiles_states_strings.xml
@@ -56,11 +56,6 @@
     <item msgid="5376619709702103243">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‏‎‏‎‎‏‏‏‎‏‏‎‎‏‎‏‏‏‎‎‏‎‎‎‏‏‏‎‏‏‎‎‎‏‎‎‏‎‏‎‎‎‎‏‏‎‎‎‎‎‏‏‎‎‏‎‏‏‎Off‎‏‎‎‏‎"</item>
     <item msgid="4875147066469902392">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‏‏‏‎‏‎‏‎‎‎‎‎‎‎‎‎‎‎‎‏‏‎‏‏‎‏‎‏‏‎‎‏‎‎‏‎‏‏‏‎‎‏‏‏‏‎‏‎‎‎‎‎‏‏‏‎‎‎‎On‎‏‎‎‏‎"</item>
   </string-array>
-  <string-array name="tile_states_modes">
-    <item msgid="7764936419245199023">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‏‏‏‏‎‎‎‎‏‎‏‎‎‏‏‎‎‎‏‎‎‎‏‎‎‎‎‏‏‎‏‏‎‎‏‎‎‏‏‎‏‎‏‎‎‎‏‏‏‎‏‎‏‎‏‏‏‏‎Unavailable‎‏‎‎‏‎"</item>
-    <item msgid="2004750556637773692">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‏‏‏‏‎‏‎‎‏‎‎‏‎‎‏‏‏‎‎‎‎‎‎‎‏‎‎‏‏‏‎‏‏‏‏‎‏‎‏‎‎‏‎‎‏‏‏‏‏‏‎‏‏‏‏‏‎‎‎Off‎‏‎‎‏‎"</item>
-    <item msgid="8968530753931637871">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‎‎‎‏‏‏‎‏‏‎‏‎‎‏‏‏‏‏‎‎‏‏‎‏‎‎‎‎‎‏‎‎‎‏‎‏‎‎‎‎‏‎‏‏‏‏‎‏‎‎‎‏‏‎‏‏‏‏‎On‎‏‎‎‏‎"</item>
-  </string-array>
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‎‎‎‎‎‏‎‏‏‏‎‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‏‎‎‎‎‎‎‎‏‏‏‎‎‎‏‏‏‎‏‎‏‎‎‎‎‎‎‏‎Unavailable‎‏‎‎‏‎"</item>
     <item msgid="5044688398303285224">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‏‎‎‎‎‎‎‎‏‎‎‏‎‏‎‏‎‏‎‏‎‏‏‏‏‎‏‏‎‎‏‏‏‏‎‏‏‎‎‏‏‎‏‏‎‎‏‏‏‏‏‏‏‎‏‎‎‎‎Off‎‏‎‎‏‎"</item>
diff --git a/packages/SystemUI/res/values-es-feminine/strings.xml b/packages/SystemUI/res/values-es-feminine/strings.xml
new file mode 100644
index 0000000..a227bf2
--- /dev/null
+++ b/packages/SystemUI/res/values-es-feminine/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="guest_wipe_session_title" msgid="7147965814683990944">"¡Bienvenida de nuevo, invitada!"</string>
+</resources>
diff --git a/packages/SystemUI/res/values-es-masculine/strings.xml b/packages/SystemUI/res/values-es-masculine/strings.xml
new file mode 100644
index 0000000..d684205
--- /dev/null
+++ b/packages/SystemUI/res/values-es-masculine/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="guest_wipe_session_title" msgid="7147965814683990944">"¡Bienvenido de nuevo, invitado!"</string>
+</resources>
diff --git a/packages/SystemUI/res/values-es-neuter/strings.xml b/packages/SystemUI/res/values-es-neuter/strings.xml
new file mode 100644
index 0000000..ea971b3
--- /dev/null
+++ b/packages/SystemUI/res/values-es-neuter/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="guest_wipe_session_title" msgid="7147965814683990944">"¡Te damos la bienvenida de nuevo a la sesión de invitados!"</string>
+</resources>
diff --git a/packages/SystemUI/res/values-es-rUS-feminine/strings.xml b/packages/SystemUI/res/values-es-rUS-feminine/strings.xml
new file mode 100644
index 0000000..0bc9262
--- /dev/null
+++ b/packages/SystemUI/res/values-es-rUS-feminine/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="guest_wipe_session_title" msgid="7147965814683990944">"¡Hola de nuevo, invitada!"</string>
+</resources>
diff --git a/packages/SystemUI/res/values-es-rUS-masculine/strings.xml b/packages/SystemUI/res/values-es-rUS-masculine/strings.xml
new file mode 100644
index 0000000..727431f
--- /dev/null
+++ b/packages/SystemUI/res/values-es-rUS-masculine/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="guest_wipe_session_title" msgid="7147965814683990944">"¡Hola de nuevo, invitado!"</string>
+</resources>
diff --git a/packages/SystemUI/res/values-es-rUS-neuter/strings.xml b/packages/SystemUI/res/values-es-rUS-neuter/strings.xml
new file mode 100644
index 0000000..727431f
--- /dev/null
+++ b/packages/SystemUI/res/values-es-rUS-neuter/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="guest_wipe_session_title" msgid="7147965814683990944">"¡Hola de nuevo, invitado!"</string>
+</resources>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index 1b751b4..31344fa 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -126,38 +126,25 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"Presiona para ver"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"Se produjo un error al guardar la grabación de pantalla"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Error al iniciar la grabación de pantalla"</string>
-    <!-- no translation found for screenrecord_stop_dialog_title (8716193661764511095) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message (6262768207331626817) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5995770227684523244) -->
-    <skip />
-    <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Detener la grabación"</string>
+    <string name="screenrecord_stop_dialog_title" msgid="8716193661764511095">"¿Quieres detener la grabación?"</string>
+    <string name="screenrecord_stop_dialog_message" msgid="6262768207331626817">"Actualmente, estás grabando toda la pantalla"</string>
+    <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Actualmente, estás grabando <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Detener grabación"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Compartiendo pantalla"</string>
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"¿Quieres dejar de compartir la pantalla?"</string>
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen_with_host_app (522823522115375414) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen (5090115386271179270) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_specific (5923772039347985172) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_generic (6681016774654578261) -->
-    <skip />
+    <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Actualmente, estás compartiendo toda la pantalla con <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
+    <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Actualmente, estás compartiendo toda la pantalla con una app"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Actualmente, estás compartiendo <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Actualmente, estás compartiendo una app"</string>
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Dejar de compartir"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Transmitiendo pantalla"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"¿Detener la transmisión?"</string>
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen_with_device (1474703115926205251) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen (8419219169553867625) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app_with_device (2715934698604085519) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (8616103075630934513) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic_with_device (9213582497852420203) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic (4100272100480415076) -->
-    <skip />
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen_with_device" msgid="1474703115926205251">"Actualmente, estás transmitiendo toda la pantalla a <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen" msgid="8419219169553867625">"Actualmente, estás transmitiendo toda la pantalla a un dispositivo cercano"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app_with_device" msgid="2715934698604085519">"Actualmente, estás transmitiendo <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> a <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="8616103075630934513">"Actualmente, estás transmitiendo <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> a un dispositivo cercano"</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic_with_device" msgid="9213582497852420203">"Actualmente, estás transmitiendo a <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic" msgid="4100272100480415076">"Actualmente, estás transmitiendo a un dispositivo cercano"</string>
     <string name="cast_to_other_device_stop_dialog_button" msgid="6420183747435521834">"Detener transmisión"</string>
     <string name="close_dialog_button" msgid="4749497706540104133">"Cerrar"</string>
     <string name="issuerecord_title" msgid="286627115110121849">"Grabadora de errores"</string>
@@ -296,15 +283,14 @@
     <string name="accessibility_sensors_off_active" msgid="2619725434618911551">"Sensores desactivados sí"</string>
     <string name="accessibility_clear_all" msgid="970525598287244592">"Eliminar todas las notificaciones"</string>
     <string name="notification_group_overflow_indicator" msgid="7605120293801012648">"<xliff:g id="NUMBER">%s</xliff:g> más"</string>
-    <string name="notification_group_overflow_description" msgid="7176322877233433278">"{count,plural, =1{# notificación más en el grupo.}many{# notificaciones más en el grupo.}other{# notificaciones más en el grupo.}}"</string>
+    <string name="notification_group_overflow_description" msgid="7176322877233433278">"{count,plural, =1{# notificación más en el grupo.}many{# de notificaciones más en el grupo.}other{# notificaciones más en el grupo.}}"</string>
     <string name="accessibility_rotation_lock_on_landscape" msgid="936972553861524360">"La pantalla está bloqueada en modo horizontal."</string>
     <string name="accessibility_rotation_lock_on_portrait" msgid="2356633398683813837">"La pantalla está bloqueada en modo vertical."</string>
     <string name="dessert_case" msgid="9104973640704357717">"Caja para postres"</string>
     <string name="start_dreams" msgid="9131802557946276718">"Protector pantalla"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"No interrumpir"</string>
-    <!-- no translation found for quick_settings_modes_label (5407025818652750501) -->
-    <skip />
+    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Modos de prioridad"</string>
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"No hay dispositivos sincronizados disponibles"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Presiona para conectar o desconectar un dispositivo"</string>
@@ -402,6 +388,9 @@
     <string name="performance" msgid="6552785217174378320">"Rendimiento"</string>
     <string name="user_interface" msgid="3712869377953950887">"Interfaz de usuario"</string>
     <string name="thermal" msgid="6758074791325414831">"Térmico"</string>
+    <string name="custom" msgid="3337456985275158299">"Personalizado"</string>
+    <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Configuración de rastreo personalizada"</string>
+    <string name="restore_default" msgid="5259420807486239755">"Restablecer configuración predeterminada"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Modo una mano"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Dispositivos auditivos"</string>
     <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Activos"</string>
@@ -440,6 +429,17 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Abrir Configuración"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Otro dispositivo"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Ocultar o mostrar Recientes"</string>
+    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Modos de prioridad"</string>
+    <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Listo"</string>
+    <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Configuración"</string>
+    <string name="zen_mode_on" msgid="9085304934016242591">"Activado"</string>
+    <string name="zen_mode_off" msgid="1736604456618147306">"Desactivado"</string>
+    <!-- no translation found for zen_mode_set_up (7457957033034460064) -->
+    <skip />
+    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
+    <skip />
+    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
+    <skip />
     <string name="zen_priority_introduction" msgid="3159291973383796646">"No te molestarán los sonidos ni las vibraciones, excepto las alarmas, los recordatorios, los eventos y las llamadas de los emisores que especifiques. Podrás escuchar el contenido que reproduzcas, como música, videos y juegos."</string>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"No te molestarán los sonidos ni las vibraciones, excepto las alarmas. Podrás escuchar el contenido que reproduzcas, como música, videos y juegos."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Personalizar"</string>
@@ -478,6 +478,8 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Cargando lento • Se completará en <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Cargando • Se completará en <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Widgets en la pantalla de bloqueo"</string>
+    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
+    <skip />
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Desliza el dedo a la izquierda para iniciar el instructivo comunal"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Personalizar"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Descartar"</string>
@@ -504,10 +506,9 @@
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"Seleccionar widget"</string>
     <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"quitar widget"</string>
     <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"colocar widget seleccionado"</string>
-    <!-- no translation found for communal_widget_picker_title (1953369090475731663) -->
-    <skip />
-    <!-- no translation found for communal_widget_picker_description (490515450110487871) -->
-    <skip />
+    <string name="communal_widget_picker_title" msgid="1953369090475731663">"Widgets en la pantalla de bloqueo"</string>
+    <string name="communal_widget_picker_description" msgid="490515450110487871">"Los widgets de la pantalla de bloqueo podrán verse incluso si bloqueas la tablet."</string>
+    <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"anular la selección del widget"</string>
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widgets en la pantalla de bloqueo"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Para abrir una app usando un widget, debes verificar tu identidad. Además, ten en cuenta que cualquier persona podrá verlo, incluso cuando la tablet esté bloqueada. Es posible que algunos widgets no se hayan diseñados para la pantalla de bloqueo y podría ser peligroso agregarlos allí."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Entendido"</string>
@@ -522,7 +523,7 @@
     <string name="guest_notification_session_active" msgid="5567273684713471450">"Estás en el modo de invitado"</string>
     <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Si agregas un usuario nuevo, se desactivará el modo de invitado y se borrarán todas las apps y los datos de la sesión de invitado actual."</string>
     <string name="user_limit_reached_title" msgid="2429229448830346057">"Alcanzaste el límite de usuarios"</string>
-    <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{Solo se puede crear un usuario.}many{Puedes agregar hasta # usuarios.}other{Puedes agregar hasta # usuarios.}}"</string>
+    <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{Solo se puede crear un usuario.}many{Puedes agregar hasta # de usuarios.}other{Puedes agregar hasta # usuarios.}}"</string>
     <string name="user_remove_user_title" msgid="9124124694835811874">"¿Confirmas que quieres quitar el usuario?"</string>
     <string name="user_remove_user_message" msgid="6702834122128031833">"Se borrarán todas las aplicaciones y los datos de este usuario."</string>
     <string name="user_remove_user_remove" msgid="8387386066949061256">"Quitar"</string>
@@ -564,8 +565,8 @@
     <string name="media_projection_action_text" msgid="3634906766918186440">"Comenzar ahora"</string>
     <string name="empty_shade_text" msgid="8935967157319717412">"No hay notificaciones"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"No hay notificaciones nuevas"</string>
-    <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"Notific. adaptables activadas"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"Tu dispositivo ahora baja el volumen y reduce las ventanas emergentes en la pantalla por hasta dos minutos si recibes muchas notificaciones en poco tiempo."</string>
+    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Reducción de sonido de notificaciones activada"</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"El volumen y las alertas se reducen por hasta 2 minutos si recibes muchas notificaciones a la vez."</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Desactivar"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Desbloquea para ver notificaciones anteriores"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Tu padre o madre administra este dispositivo"</string>
@@ -714,7 +715,7 @@
     <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satélite, conexión disponible"</string>
     <string name="satellite_connected_carrier_text" msgid="118524195198532589">"SOS por satélite"</string>
     <string name="accessibility_managed_profile" msgid="4703836746209377356">"Perfil de trabajo"</string>
-    <string name="tuner_warning_title" msgid="7721976098452135267">"Diversión para algunos, pero no para todos"</string>
+    <string name="tuner_warning_title" msgid="7721976098452135267">"Diversión solo para algunas personas"</string>
     <string name="tuner_warning" msgid="1861736288458481650">"El sintonizador de IU del sistema te brinda más formas para editar y personalizar la interfaz de usuario de Android. Estas funciones experimentales pueden cambiar, dejar de funcionar o no incluirse en futuras versiones. Procede con precaución."</string>
     <string name="tuner_persistent_warning" msgid="230466285569307806">"Estas funciones experimentales pueden cambiar, dejar de funcionar o no incluirse en futuras versiones. Procede con precaución."</string>
     <string name="got_it" msgid="477119182261892069">"Entendido"</string>
@@ -732,8 +733,7 @@
     <string name="notification_alert_title" msgid="3656229781017543655">"Predeterminada"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Automática"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"Sin sonido ni vibración"</string>
-    <!-- no translation found for notification_conversation_summary_low (3855696451919728790) -->
-    <skip />
+    <string name="notification_conversation_summary_low" msgid="3855696451919728790">"No suena ni vibra, pero aparece en la sección de conversaciones"</string>
     <string name="notification_channel_summary_default" msgid="777294388712200605">"Puede sonar o vibrar según la configuración del dispositivo"</string>
     <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Puede sonar o vibrar según la configuración del dispositivo. Conversaciones de la burbuja de <xliff:g id="APP_NAME">%1$s</xliff:g> de forma predeterminada."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Dejar que el sistema determine si esta notificación debe emitir un sonido o una vibración"</string>
@@ -771,8 +771,8 @@
     <string name="snooze_undo" msgid="2738844148845992103">"Deshacer"</string>
     <string name="snooze_undo_content_description" msgid="2711656788917580801">"Deshacer la acción de posponer notificaciones"</string>
     <string name="snoozed_for_time" msgid="7586689374860469469">"Posponer <xliff:g id="TIME_AMOUNT">%1$s</xliff:g>"</string>
-    <string name="snoozeHourOptions" msgid="2332819756222425558">"{count,plural, =1{# hora}=2{# horas}many{# horas}other{# horas}}"</string>
-    <string name="snoozeMinuteOptions" msgid="2222082405822030979">"{count,plural, =1{# minuto}many{# minutos}other{# minutos}}"</string>
+    <string name="snoozeHourOptions" msgid="2332819756222425558">"{count,plural, =1{# hora}=2{# horas}many{# de horas}other{# horas}}"</string>
+    <string name="snoozeMinuteOptions" msgid="2222082405822030979">"{count,plural, =1{# minuto}many{# de minutos}other{# minutos}}"</string>
     <string name="battery_detail_switch_title" msgid="6940976502957380405">"Ahorro de batería"</string>
     <string name="keyboard_key_button_template" msgid="8005673627272051429">"Botón <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="keyboard_key_home" msgid="3734400625170020657">"Inicio"</string>
@@ -790,8 +790,7 @@
     <string name="keyboard_key_page_up" msgid="173914303254199845">"Re Pág"</string>
     <string name="keyboard_key_page_down" msgid="9035902490071829731">"Av Pág"</string>
     <string name="keyboard_key_forward_del" msgid="5325501825762733459">"Borrar"</string>
-    <!-- no translation found for keyboard_key_esc (6230365950511411322) -->
-    <skip />
+    <string name="keyboard_key_esc" msgid="6230365950511411322">"Esc"</string>
     <string name="keyboard_key_move_home" msgid="3496502501803911971">"Inicio"</string>
     <string name="keyboard_key_move_end" msgid="99190401463834854">"Fin"</string>
     <string name="keyboard_key_insert" msgid="4621692715704410493">"Insertar"</string>
@@ -1233,7 +1232,6 @@
     <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>
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Datos móviles"</string>
-    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</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>
     <string name="mobile_data_poor_connection" msgid="819617772268371434">"Conexión deficiente"</string>
@@ -1285,7 +1283,7 @@
     <string name="dream_overlay_status_bar_camera_off" msgid="5273073778969890823">"La cámara está desactivada"</string>
     <string name="dream_overlay_status_bar_mic_off" msgid="8366534415013819396">"El micrófono está desactivado"</string>
     <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"La cámara y el micrófono están apagados"</string>
-    <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# notificación}many{# notificaciones}other{# notificaciones}}"</string>
+    <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# notificación}many{# de notificaciones}other{# notificaciones}}"</string>
     <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string>
     <string name="note_task_button_label" msgid="230135078402003532">"Tomar notas"</string>
     <string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Tomar notas, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string>
@@ -1374,8 +1372,7 @@
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Pantalla dividida"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Entrada"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Accesos directos a aplicaciones"</string>
-    <!-- no translation found for shortcut_helper_category_current_app_shortcuts (4017840565974573628) -->
-    <skip />
+    <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"App actual"</string>
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accesibilidad"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Combinaciones de teclas"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Buscar combinaciones de teclas"</string>
@@ -1388,13 +1385,27 @@
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Listo"</string>
     <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"¡Bien hecho!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Atrás"</string>
-    <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"Para volver, desliza tres dedos hacia la derecha o izquierda en cualquier lugar del panel táctil."</string>
+    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
+    <skip />
+    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
+    <skip />
     <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Panel táctil en el que aparecen tres dedos que se mueven hacia la derecha y la izquierda"</string>
     <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Pantalla de un dispositivo en la que aparece una animación del gesto atrás"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Retroiluminación del teclado"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Nivel %1$d de %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Controles de la casa"</string>
     <string name="home_controls_dream_description" msgid="4644150952104035789">"Usa rápidamente los controles de la casa como protector de pantalla"</string>
-    <!-- no translation found for volume_undo_action (5815519725211877114) -->
-    <skip />
+    <string name="volume_undo_action" msgid="5815519725211877114">"Deshacer"</string>
+    <string name="back_edu_toast_content" msgid="4530314597378982956">"Para ir hacia atrás, desliza hacia la izquierda o la derecha con tres dedos en el panel táctil"</string>
+    <string name="home_edu_toast_content" msgid="3381071147871955415">"Para ir a la pantalla principal, desliza hacia arriba con tres dedos en el panel táctil"</string>
+    <string name="overview_edu_toast_content" msgid="5797030644017804518">"Para ver las apps recientes, desliza hacia arriba con tres dedos y mantén presionado el panel táctil"</string>
+    <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Para ver todas las apps, presiona la tecla de acción en el teclado"</string>
+    <string name="back_edu_notification_title" msgid="5624780717751357278">"Usa el panel táctil para ir hacia atrás"</string>
+    <string name="back_edu_notification_content" msgid="2497557451540954068">"Desliza hacia la izquierda o la derecha con tres dedos. Presiona para aprender más gestos."</string>
+    <string name="home_edu_notification_title" msgid="6097902076909654045">"Usa el panel táctil para ir a la pantalla principal"</string>
+    <string name="home_edu_notification_content" msgid="6631697734535766588">"Desliza hacia arriba con tres dedos. Presiona para aprender más gestos."</string>
+    <string name="overview_edu_notification_title" msgid="1265824157319562406">"Usa el panel táctil para ver las apps recientes"</string>
+    <string name="overview_edu_notification_content" msgid="3578204677648432500">"Desliza hacia arriba con tres dedos y mantenlos presionados. Presiona para aprender más gestos."</string>
+    <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Usa el teclado para ver todas las apps"</string>
+    <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Presiona la tecla de acción en cualquier momento. Presiona para aprender más gestos."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-es-rUS/tiles_states_strings.xml b/packages/SystemUI/res/values-es-rUS/tiles_states_strings.xml
index b82fafe..869efff 100644
--- a/packages/SystemUI/res/values-es-rUS/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/tiles_states_strings.xml
@@ -56,9 +56,6 @@
     <item msgid="5376619709702103243">"Desactivado"</item>
     <item msgid="4875147066469902392">"Activado"</item>
   </string-array>
-    <!-- no translation found for tile_states_modes:0 (7764936419245199023) -->
-    <!-- no translation found for tile_states_modes:1 (2004750556637773692) -->
-    <!-- no translation found for tile_states_modes:2 (8968530753931637871) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"No disponible"</item>
     <item msgid="5044688398303285224">"Desactivada"</item>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index 56985bc..3aff0fa 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -126,38 +126,25 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"Toca para verla"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"No se ha podido guardar la grabación de pantalla"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"No se ha podido empezar a grabar la pantalla"</string>
-    <!-- no translation found for screenrecord_stop_dialog_title (8716193661764511095) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message (6262768207331626817) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5995770227684523244) -->
-    <skip />
+    <string name="screenrecord_stop_dialog_title" msgid="8716193661764511095">"¿Detener grabación?"</string>
+    <string name="screenrecord_stop_dialog_message" msgid="6262768207331626817">"Estás grabando toda la pantalla"</string>
+    <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Estás grabando <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Detener grabación"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Compartiendo pantalla"</string>
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"¿Dejar de compartir pantalla?"</string>
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen_with_host_app (522823522115375414) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen (5090115386271179270) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_specific (5923772039347985172) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_generic (6681016774654578261) -->
-    <skip />
+    <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Estás compartiendo toda tu pantalla con <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
+    <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Estás compartiendo toda tu pantalla con una aplicación"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Estás compartiendo <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Estás compartiendo una aplicación"</string>
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Dejar de compartir"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Enviando pantalla"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"¿Dejar de enviar?"</string>
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen_with_device (1474703115926205251) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen (8419219169553867625) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app_with_device (2715934698604085519) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (8616103075630934513) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic_with_device (9213582497852420203) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic (4100272100480415076) -->
-    <skip />
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen_with_device" msgid="1474703115926205251">"Estás enviando toda la pantalla a <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen" msgid="8419219169553867625">"Estás enviando toda la pantalla a un dispositivo cercano"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app_with_device" msgid="2715934698604085519">"Estás enviando <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> a <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="8616103075630934513">"Estás enviando <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> a un dispositivo cercano"</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic_with_device" msgid="9213582497852420203">"Estás enviando a <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic" msgid="4100272100480415076">"Estás enviando a un dispositivo cercano"</string>
     <string name="cast_to_other_device_stop_dialog_button" msgid="6420183747435521834">"Dejar de enviar contenido"</string>
     <string name="close_dialog_button" msgid="4749497706540104133">"Cerrar"</string>
     <string name="issuerecord_title" msgid="286627115110121849">"Grabadora de problemas"</string>
@@ -256,7 +243,7 @@
     <string name="accessibility_battery_unknown" msgid="1807789554617976440">"Porcentaje de batería desconocido."</string>
     <string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Conectado a <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
     <string name="accessibility_cast_name" msgid="7344437925388773685">"Conectado a <xliff:g id="CAST">%s</xliff:g>."</string>
-    <string name="accessibility_not_connected" msgid="4061305616351042142">"No conectado"</string>
+    <string name="accessibility_not_connected" msgid="4061305616351042142">"No conectado."</string>
     <string name="data_connection_roaming" msgid="375650836665414797">"Roaming"</string>
     <string name="cell_data_off" msgid="4886198950247099526">"Desactivados"</string>
     <string name="accessibility_airplane_mode" msgid="1899529214045998505">"Modo Avión"</string>
@@ -303,8 +290,7 @@
     <string name="start_dreams" msgid="9131802557946276718">"Salvapantallas"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"No molestar"</string>
-    <!-- no translation found for quick_settings_modes_label (5407025818652750501) -->
-    <skip />
+    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Modos prioritarios"</string>
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"No hay dispositivos vinculados disponibles"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Toca para conectar o desconectar un dispositivo"</string>
@@ -402,6 +388,9 @@
     <string name="performance" msgid="6552785217174378320">"Rendimiento"</string>
     <string name="user_interface" msgid="3712869377953950887">"Interfaz de usuario"</string>
     <string name="thermal" msgid="6758074791325414831">"Temperatura"</string>
+    <string name="custom" msgid="3337456985275158299">"Personalizado"</string>
+    <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Ajustes de rastreo personalizados"</string>
+    <string name="restore_default" msgid="5259420807486239755">"Restaurar ajustes predeterminados"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Modo Una mano"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Audífonos"</string>
     <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Activos"</string>
@@ -440,6 +429,17 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Abrir Ajustes"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Otro dispositivo"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Mostrar u ocultar aplicaciones recientes"</string>
+    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Modos prioritarios"</string>
+    <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Hecho"</string>
+    <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Ajustes"</string>
+    <string name="zen_mode_on" msgid="9085304934016242591">"Activado"</string>
+    <string name="zen_mode_off" msgid="1736604456618147306">"Desactivado"</string>
+    <!-- no translation found for zen_mode_set_up (7457957033034460064) -->
+    <skip />
+    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
+    <skip />
+    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
+    <skip />
     <string name="zen_priority_introduction" msgid="3159291973383796646">"No te molestarán los sonidos ni las vibraciones, excepto las alarmas, los recordatorios, los eventos y las llamadas que especifiques. Seguirás escuchando el contenido que quieras reproducir, como música, vídeos y juegos."</string>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"No te molestarán los sonidos ni las vibraciones, excepto las alarmas. Seguirás escuchando el contenido que quieras reproducir, como música, vídeos y juegos."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Personalizar"</string>
@@ -478,6 +478,8 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Carga lenta • En <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> terminará de cargarse"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Cargando • Carga completa en <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Widgets en la pantalla de bloqueo"</string>
+    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
+    <skip />
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Desliza hacia la izquierda para iniciar el tutorial de la comunidad"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Personalizar"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Cerrar"</string>
@@ -504,25 +506,24 @@
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"seleccionar widget"</string>
     <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"eliminar widget"</string>
     <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"colocar widget seleccionado"</string>
-    <!-- no translation found for communal_widget_picker_title (1953369090475731663) -->
-    <skip />
-    <!-- no translation found for communal_widget_picker_description (490515450110487871) -->
-    <skip />
+    <string name="communal_widget_picker_title" msgid="1953369090475731663">"Widgets para la pantalla de bloqueo"</string>
+    <string name="communal_widget_picker_description" msgid="490515450110487871">"Cualquiera puede ver los widgets de tu pantalla de bloqueo, aunque tu tablet esté bloqueada."</string>
+    <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"deseleccionar widget"</string>
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widgets para la pantalla de bloqueo"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Para abrir una aplicación usando un widget, deberás verificar que eres tú. Además, ten en cuenta que cualquier persona podrá verlos, incluso aunque tu tablet esté bloqueada. Es posible que algunos widgets no estén pensados para la pantalla de bloqueo y no sea seguro añadirlos aquí."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Entendido"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Cambiar de usuario"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menú desplegable"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Se eliminarán todas las aplicaciones y datos de esta sesión."</string>
-    <string name="guest_wipe_session_title" msgid="7147965814683990944">"¡Hola de nuevo, invitado!"</string>
+    <string name="guest_wipe_session_title" msgid="7147965814683990944">"¡Te damos la bienvenida de nuevo a la sesión de invitados!"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"¿Quieres continuar con tu sesión?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Volver a empezar"</string>
     <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Sí, continuar"</string>
     <string name="guest_notification_app_name" msgid="2110425506754205509">"Modo Invitado"</string>
     <string name="guest_notification_session_active" msgid="5567273684713471450">"Estás en modo Invitado"</string>
-    <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Si añades un nuevo usuario, saldrás del modo Invitado y se eliminarán todas las aplicaciones y datos de la sesión de invitado actual."</string>
+    <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Si añades un nuevo usuario, saldrás del modo Invitado y se eliminarán todas las aplicaciones y los datos de la sesión de invitado actual."</string>
     <string name="user_limit_reached_title" msgid="2429229448830346057">"Has alcanzado el límite de usuarios"</string>
-    <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{Solo se puede crear un usuario.}many{Puedes añadir # usuarios como máximo.}other{Puedes añadir # usuarios como máximo.}}"</string>
+    <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{Solo se puede crear 1 usuario.}many{Puedes añadir # usuarios como máximo.}other{Puedes añadir # usuarios como máximo.}}"</string>
     <string name="user_remove_user_title" msgid="9124124694835811874">"¿Quitar usuario?"</string>
     <string name="user_remove_user_message" msgid="6702834122128031833">"Se eliminarán todas las aplicaciones y datos de este usuario."</string>
     <string name="user_remove_user_remove" msgid="8387386066949061256">"Quitar"</string>
@@ -564,8 +565,8 @@
     <string name="media_projection_action_text" msgid="3634906766918186440">"Empezar ahora"</string>
     <string name="empty_shade_text" msgid="8935967157319717412">"No hay notificaciones"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"No hay notificaciones nuevas"</string>
-    <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"Notificaciones adaptativas activadas"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"Tu dispositivo baja el volumen y reduce las ventanas emergentes durante un máximo de 2 minutos cuando recibes muchas notificaciones en poco tiempo."</string>
+    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Bajar el volumen de notificaciones está activado"</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"El volumen y las alertas de tu dispositivo se reducen durante hasta 2 minutos si recibes muchas notificaciones a la vez."</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Desactivar"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Desbloquea para ver notificaciones anteriores"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Este dispositivo lo gestionan tu padre o tu madre"</string>
@@ -732,8 +733,7 @@
     <string name="notification_alert_title" msgid="3656229781017543655">"Predeterminado"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Automática"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"Sin sonido ni vibración"</string>
-    <!-- no translation found for notification_conversation_summary_low (3855696451919728790) -->
-    <skip />
+    <string name="notification_conversation_summary_low" msgid="3855696451919728790">"No suena ni vibra, pero aparece en la sección de conversación"</string>
     <string name="notification_channel_summary_default" msgid="777294388712200605">"Puede sonar o vibrar según los ajustes del dispositivo"</string>
     <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Puede sonar o vibrar según los ajustes del dispositivo. Las conversaciones de <xliff:g id="APP_NAME">%1$s</xliff:g> aparecen como burbujas de forma predeterminada."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Haz que el sistema determine si con esta notificación el dispositivo debe sonar o vibrar"</string>
@@ -790,8 +790,7 @@
     <string name="keyboard_key_page_up" msgid="173914303254199845">"Re Pág"</string>
     <string name="keyboard_key_page_down" msgid="9035902490071829731">"Av Pág"</string>
     <string name="keyboard_key_forward_del" msgid="5325501825762733459">"Supr"</string>
-    <!-- no translation found for keyboard_key_esc (6230365950511411322) -->
-    <skip />
+    <string name="keyboard_key_esc" msgid="6230365950511411322">"Esc"</string>
     <string name="keyboard_key_move_home" msgid="3496502501803911971">"Inicio"</string>
     <string name="keyboard_key_move_end" msgid="99190401463834854">"Fin"</string>
     <string name="keyboard_key_insert" msgid="4621692715704410493">"Insert"</string>
@@ -1233,9 +1232,8 @@
     <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>
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Datos móviles"</string>
-    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Conectado"</string>
-    <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Conectado temporalmente"</string>
+    <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Conectada temporalmente"</string>
     <string name="mobile_data_poor_connection" msgid="819617772268371434">"Conexión inestable"</string>
     <string name="mobile_data_off_summary" msgid="3663995422004150567">"Los datos móviles no se conectarán automáticamente"</string>
     <string name="mobile_data_no_connection" msgid="1713872434869947377">"Sin conexión"</string>
@@ -1254,7 +1252,7 @@
     <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> quiere añadir el siguiente recuadro a ajustes rápidos"</string>
     <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Añadir recuadro"</string>
     <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"No añadir recuadro"</string>
-    <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Selecciona un usuario"</string>
+    <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Seleccionar usuario"</string>
     <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# aplicación activa}many{# aplicaciones activas}other{# aplicaciones activas}}"</string>
     <string name="fgs_dot_content_description" msgid="2865071539464777240">"Información nueva"</string>
     <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Aplicaciones activas"</string>
@@ -1277,7 +1275,7 @@
     <string name="clipboard_image_preview" msgid="2156475174343538128">"Vista previa de la imagen"</string>
     <string name="clipboard_edit" msgid="4500155216174011640">"editar"</string>
     <string name="add" msgid="81036585205287996">"Añadir"</string>
-    <string name="manage_users" msgid="1823875311934643849">"Gest. usuarios"</string>
+    <string name="manage_users" msgid="1823875311934643849">"Gestionar usuarios"</string>
     <string name="drag_split_not_supported" msgid="7173481676120546121">"Esta notificación no se puede arrastrar a la pantalla dividida"</string>
     <string name="dream_overlay_status_bar_wifi_off" msgid="4497069245055003582">"Wi‑Fi no disponible"</string>
     <string name="dream_overlay_status_bar_priority_mode" msgid="5428462123314728739">"Modo prioritario"</string>
@@ -1374,8 +1372,7 @@
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Pantalla dividida"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Entrada"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Accesos directos a aplicaciones"</string>
-    <!-- no translation found for shortcut_helper_category_current_app_shortcuts (4017840565974573628) -->
-    <skip />
+    <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Aplicación en uso"</string>
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accesibilidad"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Combinaciones de teclas"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Atajos de búsqueda"</string>
@@ -1388,13 +1385,27 @@
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Hecho"</string>
     <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"¡Bien hecho!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Atrás"</string>
-    <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"Para volver, desliza tres dedos hacia la izquierda o la derecha en cualquier punto del panel táctil."</string>
+    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
+    <skip />
+    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
+    <skip />
     <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Panel táctil con tres dedos moviéndose hacia la derecha y la izquierda"</string>
     <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Pantalla del dispositivo que muestra una animación del gesto para volver atrás"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Retroiluminación del teclado"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Nivel %1$d de %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Controles de la casa"</string>
     <string name="home_controls_dream_description" msgid="4644150952104035789">"Usa los controles de tu casa como salvapantallas"</string>
-    <!-- no translation found for volume_undo_action (5815519725211877114) -->
-    <skip />
+    <string name="volume_undo_action" msgid="5815519725211877114">"Deshacer"</string>
+    <string name="back_edu_toast_content" msgid="4530314597378982956">"Para volver atrás, desliza hacia la izquierda o la derecha con tres dedos en el panel táctil"</string>
+    <string name="home_edu_toast_content" msgid="3381071147871955415">"Para ir a la pantalla de inicio, desliza hacia arriba con tres dedos en el panel táctil"</string>
+    <string name="overview_edu_toast_content" msgid="5797030644017804518">"Para ver las aplicaciones recientes, desliza hacia arriba y mantén pulsado el panel táctil con tres dedos"</string>
+    <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Para ver todas tus aplicaciones, pulsa la tecla de acción de tu teclado"</string>
+    <string name="back_edu_notification_title" msgid="5624780717751357278">"Usa el panel táctil para volver atrás"</string>
+    <string name="back_edu_notification_content" msgid="2497557451540954068">"Desliza hacia la izquierda o hacia la derecha con tres dedos. Toca para aprender a usar más gestos."</string>
+    <string name="home_edu_notification_title" msgid="6097902076909654045">"Usa el panel táctil para ir a la pantalla de inicio"</string>
+    <string name="home_edu_notification_content" msgid="6631697734535766588">"Desliza hacia arriba con tres dedos. Toca para aprender a usar más gestos."</string>
+    <string name="overview_edu_notification_title" msgid="1265824157319562406">"Usa el panel táctil para ver las aplicaciones recientes"</string>
+    <string name="overview_edu_notification_content" msgid="3578204677648432500">"Desliza hacia arriba y mantén pulsado con tres dedos. Toca para aprender a usar más gestos."</string>
+    <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Usa el teclado para ver todas las aplicaciones"</string>
+    <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Pulsa la tecla de acción en cualquier momento. Toca para aprender a usar más gestos."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-es/tiles_states_strings.xml b/packages/SystemUI/res/values-es/tiles_states_strings.xml
index 02365e5..08644e1 100644
--- a/packages/SystemUI/res/values-es/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-es/tiles_states_strings.xml
@@ -43,7 +43,7 @@
   </string-array>
   <string-array name="tile_states_cell">
     <item msgid="1235899788959500719">"No disponible"</item>
-    <item msgid="2074416252859094119">"Desactivadas"</item>
+    <item msgid="2074416252859094119">"Desactivado"</item>
     <item msgid="287997784730044767">"Activado"</item>
   </string-array>
   <string-array name="tile_states_battery">
@@ -56,9 +56,6 @@
     <item msgid="5376619709702103243">"Desactivado"</item>
     <item msgid="4875147066469902392">"Activado"</item>
   </string-array>
-    <!-- no translation found for tile_states_modes:0 (7764936419245199023) -->
-    <!-- no translation found for tile_states_modes:1 (2004750556637773692) -->
-    <!-- no translation found for tile_states_modes:2 (8968530753931637871) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"No disponible"</item>
     <item msgid="5044688398303285224">"Desactivado"</item>
@@ -192,6 +189,6 @@
   <string-array name="tile_states_hearing_devices">
     <item msgid="1235334096484287173">"No disponibles"</item>
     <item msgid="3079622119444911877">"Desactivados"</item>
-    <item msgid="3028994095749238254">"Activados"</item>
+    <item msgid="3028994095749238254">"Activado"</item>
   </string-array>
 </resources>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index 111480f..5b38ff6 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -126,38 +126,25 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"Puudutage kuvamiseks"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"Viga ekraanisalvestise salvestamisel"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Viga ekraanikuva salvestamise alustamisel"</string>
-    <!-- no translation found for screenrecord_stop_dialog_title (8716193661764511095) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message (6262768207331626817) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5995770227684523244) -->
-    <skip />
+    <string name="screenrecord_stop_dialog_title" msgid="8716193661764511095">"Kas peatada salvestamine?"</string>
+    <string name="screenrecord_stop_dialog_message" msgid="6262768207331626817">"Salvestate praegu kogu oma ekraanikuva"</string>
+    <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Salvestate praegu rakendust <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Peata salvestamine"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Ekraani jagamine"</string>
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Kas lõpetada ekraanikuva jagamine?"</string>
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen_with_host_app (522823522115375414) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen (5090115386271179270) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_specific (5923772039347985172) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_generic (6681016774654578261) -->
-    <skip />
+    <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Jagate praegu kogu oma ekraanikuva rakendusega <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
+    <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Jagate praegu kogu oma ekraanikuva rakendusega"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Jagate praegu rakenduse <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> kuva"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Jagate praegu rakenduse kuva"</string>
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Lõpeta jagamine"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Ekraanikuva ülekandmine"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Kas peatada ülekandmine?"</string>
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen_with_device (1474703115926205251) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen (8419219169553867625) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app_with_device (2715934698604085519) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (8616103075630934513) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic_with_device (9213582497852420203) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic (4100272100480415076) -->
-    <skip />
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen_with_device" msgid="1474703115926205251">"Kannate praegu kogu oma ekraanikuva üle seadmesse <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen" msgid="8419219169553867625">"Kannate praegu kogu oma ekraanikuva üle lähedalasuvasse seadmesse"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app_with_device" msgid="2715934698604085519">"Kannate praegu rakendust <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> üle seadmesse <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="8616103075630934513">"Kannate praegu rakendust <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> üle lähedalasuvasse seadmesse"</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic_with_device" msgid="9213582497852420203">"Kannate praegu üle seadmesse <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic" msgid="4100272100480415076">"Kannate praegu üle lähedalasuvasse seadmesse"</string>
     <string name="cast_to_other_device_stop_dialog_button" msgid="6420183747435521834">"Peata ülekandmine"</string>
     <string name="close_dialog_button" msgid="4749497706540104133">"Sule"</string>
     <string name="issuerecord_title" msgid="286627115110121849">"Probleemisalvesti"</string>
@@ -303,8 +290,7 @@
     <string name="start_dreams" msgid="9131802557946276718">"Ekraanisäästja"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"Mitte segada"</string>
-    <!-- no translation found for quick_settings_modes_label (5407025818652750501) -->
-    <skip />
+    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Prioriteetsed režiimid"</string>
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Ühtegi seotud seadet pole saadaval"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Puudutage seadme ühendamiseks või ühenduse katkestamiseks"</string>
@@ -402,6 +388,9 @@
     <string name="performance" msgid="6552785217174378320">"Jõudlus"</string>
     <string name="user_interface" msgid="3712869377953950887">"Kasutajaliides"</string>
     <string name="thermal" msgid="6758074791325414831">"Soojus"</string>
+    <string name="custom" msgid="3337456985275158299">"Kohandatud"</string>
+    <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Kohandatud jälgimise seaded"</string>
+    <string name="restore_default" msgid="5259420807486239755">"Vaikeseadete taastamine"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Ühekäerežiim"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Kuuldeseadmed"</string>
     <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Ühendatud"</string>
@@ -440,6 +429,17 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Ava menüü Seaded"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Muu seade"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Lehe Ülevaade sisse- ja väljalülitamine"</string>
+    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Prioriteetsed režiimid"</string>
+    <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Valmis"</string>
+    <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Seaded"</string>
+    <string name="zen_mode_on" msgid="9085304934016242591">"Sees"</string>
+    <string name="zen_mode_off" msgid="1736604456618147306">"Väljas"</string>
+    <!-- no translation found for zen_mode_set_up (7457957033034460064) -->
+    <skip />
+    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
+    <skip />
+    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
+    <skip />
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Helid ja värinad ei sega teid. Kuulete siiski enda määratud äratusi, meeldetuletusi, sündmusi ja helistajaid. Samuti kuulete kõike, mille esitamise ise valite, sh muusika, videod ja mängud."</string>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Helid ja värinad ei sega teid. Kuulete siiski äratusi. Samuti kuulete kõike, mille esitamise ise valite, sh muusika, videod ja mängud."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Kohanda"</string>
@@ -478,6 +478,8 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Aeglane laadimine • Täis <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> pärast"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Laadimine • Täis <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> pärast"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Lukustuskuva vidinad"</string>
+    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
+    <skip />
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Ühise õpetuse käivitamiseks pühkige vasakule"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Kohandage"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Loobuge"</string>
@@ -504,10 +506,9 @@
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"vidina valimine"</string>
     <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"eemaldage vidin"</string>
     <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"asetage valitud vidin"</string>
-    <!-- no translation found for communal_widget_picker_title (1953369090475731663) -->
-    <skip />
-    <!-- no translation found for communal_widget_picker_description (490515450110487871) -->
-    <skip />
+    <string name="communal_widget_picker_title" msgid="1953369090475731663">"Lukustuskuva vidinad"</string>
+    <string name="communal_widget_picker_description" msgid="490515450110487871">"Igaüks saab vaadata luk.kuval olevaid vidinaid, isegi kui tahvelarvuti on lukus."</string>
+    <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"tühistage vidina valimine"</string>
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Lukustuskuva vidinad"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Rakenduse avamiseks vidina abil peate kinnitama, et see olete teie. Samuti pidage meeles, et kõik saavad vidinaid vaadata, isegi kui teie tahvelarvuti on lukus. Mõni vidin ei pruugi olla ette nähtud teie lukustuskuva jaoks ja seda pole turvaline siia lisada."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Selge"</string>
@@ -564,8 +565,8 @@
     <string name="media_projection_action_text" msgid="3634906766918186440">"Alusta kohe"</string>
     <string name="empty_shade_text" msgid="8935967157319717412">"Märguandeid pole"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"Uusi märguandeid ei ole"</string>
-    <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"Kohanduvad märguanded on sees"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"Seade vähendab nüüd helitugevust ja ekraanil kuvatavaid hüpikaknaid kuni kaheks minutiks, kui saate lühikese aja jooksul palju märguandeid."</string>
+    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Märguannete summutamine on sees"</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Kui saate korraga liiga palju märguandeid, vähendab seade automaatselt helitugevust ja minimeerib märguanded kuni kaheks minutiks."</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Lülita välja"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Vanemate märguannete nägemiseks avage"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Seda seadet haldab sinu vanem"</string>
@@ -732,8 +733,7 @@
     <string name="notification_alert_title" msgid="3656229781017543655">"Vaikeseade"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Automaatne"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"Ilma heli ja vibreerimiseta"</string>
-    <!-- no translation found for notification_conversation_summary_low (3855696451919728790) -->
-    <skip />
+    <string name="notification_conversation_summary_low" msgid="3855696451919728790">"Heli ja vibreerimist pole, aga märguanne kuvatakse siiski vestlusjaotises"</string>
     <string name="notification_channel_summary_default" msgid="777294388712200605">"Võib seadme seadete põhjal heliseda või vibreerida"</string>
     <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Võib seadme seadete põhjal heliseda või vibreerida. Rakenduse <xliff:g id="APP_NAME">%1$s</xliff:g> vestlused kuvatakse vaikimisi mullis."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Laske süsteemil määrata, kas selle märguande puhul peaks esitama heli või vibreerima"</string>
@@ -790,8 +790,7 @@
     <string name="keyboard_key_page_up" msgid="173914303254199845">"Lehe võrra üles"</string>
     <string name="keyboard_key_page_down" msgid="9035902490071829731">"Lehe võrra alla"</string>
     <string name="keyboard_key_forward_del" msgid="5325501825762733459">"Kustuta"</string>
-    <!-- no translation found for keyboard_key_esc (6230365950511411322) -->
-    <skip />
+    <string name="keyboard_key_esc" msgid="6230365950511411322">"Paoklahv"</string>
     <string name="keyboard_key_move_home" msgid="3496502501803911971">"Avakuva"</string>
     <string name="keyboard_key_move_end" msgid="99190401463834854">"Lõpp"</string>
     <string name="keyboard_key_insert" msgid="4621692715704410493">"Sisesta"</string>
@@ -1233,7 +1232,6 @@
     <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>
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobiilne andmeside"</string>
-    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Ühendatud"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Ajutiselt ühendatud"</string>
     <string name="mobile_data_poor_connection" msgid="819617772268371434">"Kehv ühendus"</string>
@@ -1374,8 +1372,7 @@
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Jagatud ekraanikuva"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Sisend"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Rakenduse otseteed"</string>
-    <!-- no translation found for shortcut_helper_category_current_app_shortcuts (4017840565974573628) -->
-    <skip />
+    <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Praegune rakendus"</string>
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Juurdepääsetavus"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Klaviatuuri otseteed"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Otsingu otseteed"</string>
@@ -1388,13 +1385,27 @@
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Valmis"</string>
     <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Väga hea!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Tagasi"</string>
-    <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"Tagasiliikumiseks pühkige puuteplaadil kolme sõrmega vasakule või paremale."</string>
+    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
+    <skip />
+    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
+    <skip />
     <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Puuteplaat kolme paremale ja vasakule liikuva sõrmega"</string>
     <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Seadme ekraanil näidatakse tagasiliigutuse animatsiooni"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Klaviatuuri taustavalgustus"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Tase %1$d/%2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Kodu juhtelemendid"</string>
     <string name="home_controls_dream_description" msgid="4644150952104035789">"Juurdepääs kodu juhtelementidele ekraanisäästjalt"</string>
-    <!-- no translation found for volume_undo_action (5815519725211877114) -->
-    <skip />
+    <string name="volume_undo_action" msgid="5815519725211877114">"Võta tagasi"</string>
+    <string name="back_edu_toast_content" msgid="4530314597378982956">"Tagasiliikumiseks pühkige puuteplaadil kolme sõrmega vasakule või paremale"</string>
+    <string name="home_edu_toast_content" msgid="3381071147871955415">"Avakuvale liikumiseks pühkige puuteplaadil kolme sõrmega üles"</string>
+    <string name="overview_edu_toast_content" msgid="5797030644017804518">"Hiljutiste rakenduste kuvamiseks pühkige puuteplaadil kolme sõrmega üles ja hoidke sõrmi puuteplaadil"</string>
+    <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Kõigi oma rakenduste kuvamiseks vajutage klaviatuuril toiminguklahvi"</string>
+    <string name="back_edu_notification_title" msgid="5624780717751357278">"Puuteplaadi kasutamine tagasiliikumiseks"</string>
+    <string name="back_edu_notification_content" msgid="2497557451540954068">"Pühkige kolme sõrmega vasakule või paremale. Puudutage žestide kohta lisateabe saamiseks."</string>
+    <string name="home_edu_notification_title" msgid="6097902076909654045">"Puuteplaadi kasutamine avakuvale liikumiseks"</string>
+    <string name="home_edu_notification_content" msgid="6631697734535766588">"Pühkige kolme sõrmega üles. Puudutage žestide kohta lisateabe saamiseks."</string>
+    <string name="overview_edu_notification_title" msgid="1265824157319562406">"Puuteplaadi kasutamine hiljutiste rakenduste kuvamiseks"</string>
+    <string name="overview_edu_notification_content" msgid="3578204677648432500">"Pühkige kolme sõrmega üles ja hoidke sõrmi plaadil. Puudutage žestide kohta lisateabe saamiseks."</string>
+    <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Klaviatuuri kasutamine kõigi rakenduste kuvamiseks"</string>
+    <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Vajutage soovitud ajal toiminguklahvi. Puudutage žestide kohta lisateabe saamiseks."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-et/tiles_states_strings.xml b/packages/SystemUI/res/values-et/tiles_states_strings.xml
index 3794f9a..704649e 100644
--- a/packages/SystemUI/res/values-et/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-et/tiles_states_strings.xml
@@ -56,9 +56,6 @@
     <item msgid="5376619709702103243">"Väljas"</item>
     <item msgid="4875147066469902392">"Sees"</item>
   </string-array>
-    <!-- no translation found for tile_states_modes:0 (7764936419245199023) -->
-    <!-- no translation found for tile_states_modes:1 (2004750556637773692) -->
-    <!-- no translation found for tile_states_modes:2 (8968530753931637871) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Pole saadaval"</item>
     <item msgid="5044688398303285224">"Väljas"</item>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index f082ea1..6471c3b 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -126,38 +126,25 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"Sakatu ikusteko"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"Errore bat gertatu da pantaila-grabaketa gordetzean"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Errore bat gertatu da pantaila grabatzen hastean"</string>
-    <!-- no translation found for screenrecord_stop_dialog_title (8716193661764511095) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message (6262768207331626817) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5995770227684523244) -->
-    <skip />
+    <string name="screenrecord_stop_dialog_title" msgid="8716193661764511095">"Grabaketa gelditu nahi duzu?"</string>
+    <string name="screenrecord_stop_dialog_message" msgid="6262768207331626817">"Pantaila osoa grabatzen ari zara"</string>
+    <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"<xliff:g id="APP_NAME">%1$s</xliff:g> grabatzen ari zara"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Utzi grabatzeari"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Pantaila partekatzen"</string>
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Pantaila partekatzeari utzi nahi diozu?"</string>
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen_with_host_app (522823522115375414) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen (5090115386271179270) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_specific (5923772039347985172) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_generic (6681016774654578261) -->
-    <skip />
+    <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Pantaila osoa <xliff:g id="HOST_APP_NAME">%1$s</xliff:g> aplikazioarekin partekatzen ari zara"</string>
+    <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Pantaila osoa aplikazio batekin partekatzen ari zara"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"<xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> partekatzen ari zara"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Aplikazio bat partekatzen ari zara"</string>
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Utzi partekatzeari"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Pantaila igortzen"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Igortzeari utzi nahi diozu?"</string>
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen_with_device (1474703115926205251) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen (8419219169553867625) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app_with_device (2715934698604085519) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (8616103075630934513) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic_with_device (9213582497852420203) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic (4100272100480415076) -->
-    <skip />
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen_with_device" msgid="1474703115926205251">"Pantaila osoa <xliff:g id="DEVICE_NAME">%1$s</xliff:g> gailura igortzen ari zara"</string>
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen" msgid="8419219169553867625">"Pantaila osoa inguruko gailu batera igortzen ari zara"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app_with_device" msgid="2715934698604085519">"<xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> <xliff:g id="DEVICE_NAME">%2$s</xliff:g> gailura igortzen ari zara"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="8616103075630934513">"<xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> inguruko gailu batera igortzen ari zara"</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic_with_device" msgid="9213582497852420203">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> gailura igortzen ari zara"</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic" msgid="4100272100480415076">"Inguruko gailu batera igortzen ari zara"</string>
     <string name="cast_to_other_device_stop_dialog_button" msgid="6420183747435521834">"Utzi igortzeari"</string>
     <string name="close_dialog_button" msgid="4749497706540104133">"Itxi"</string>
     <string name="issuerecord_title" msgid="286627115110121849">"Arazo-grabagailua"</string>
@@ -303,8 +290,7 @@
     <string name="start_dreams" msgid="9131802557946276718">"Pantaila-babeslea"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"Ez molestatzeko modua"</string>
-    <!-- no translation found for quick_settings_modes_label (5407025818652750501) -->
-    <skip />
+    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Lehentasunezko moduak"</string>
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetootha"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Ez dago parekatutako gailurik erabilgarri"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Sakatu hau gailu bat konektatu edo deskonektatzeko"</string>
@@ -402,6 +388,9 @@
     <string name="performance" msgid="6552785217174378320">"Errendimendua"</string>
     <string name="user_interface" msgid="3712869377953950887">"Erabiltzaile-interfazea"</string>
     <string name="thermal" msgid="6758074791325414831">"Termikoa"</string>
+    <string name="custom" msgid="3337456985275158299">"Pertsonalizatua"</string>
+    <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Arrasto pertsonalizatuen ezarpenak"</string>
+    <string name="restore_default" msgid="5259420807486239755">"Leheneratu balio lehenetsia"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Esku bakarreko modua"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Entzumen-gailuak"</string>
     <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktibo"</string>
@@ -440,6 +429,17 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Ireki Ezarpenak"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Beste gailu bat"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Aldatu ikuspegi orokorra"</string>
+    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Lehentasunezko moduak"</string>
+    <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Eginda"</string>
+    <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Ezarpenak"</string>
+    <string name="zen_mode_on" msgid="9085304934016242591">"Aktibatuta"</string>
+    <string name="zen_mode_off" msgid="1736604456618147306">"Desaktibatuta"</string>
+    <!-- no translation found for zen_mode_set_up (7457957033034460064) -->
+    <skip />
+    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
+    <skip />
+    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
+    <skip />
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Gailuak ez du egingo ez soinurik ez dardararik, baina alarmak, gertaera eta abisuen tonuak, eta aukeratzen dituzun deitzaileen dei-tonuak joko ditu. Bestalde, zuk erreproduzitutako guztia entzungo duzu, besteak beste, musika, bideoak eta jokoak."</string>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Gailuak ez du egingo ez soinurik ez dardararik, baina alarmak joko ditu. Hala ere, zuk erreproduzitutako guztia entzun ahal izango duzu, besteak beste, musika, bideoak eta jokoak."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Pertsonalizatu"</string>
@@ -478,6 +478,8 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Mantso kargatzen • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> guztiz kargatu arte"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Kargatzen • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> guztiz kargatu arte"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Pantaila blokeatuko widgetak"</string>
+    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
+    <skip />
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Tutorial komuna hasteko, pasatu hatza ezkerrera"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Pertsonalizatu"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Baztertu"</string>
@@ -504,10 +506,9 @@
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"hautatu widget bat"</string>
     <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"kendu widgeta"</string>
     <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"kokatu hautatutako widgeta"</string>
-    <!-- no translation found for communal_widget_picker_title (1953369090475731663) -->
-    <skip />
-    <!-- no translation found for communal_widget_picker_description (490515450110487871) -->
-    <skip />
+    <string name="communal_widget_picker_title" msgid="1953369090475731663">"Pantaila blokeatuko widgetak"</string>
+    <string name="communal_widget_picker_description" msgid="490515450110487871">"Edonork ikus ditzake pantaila blokeatuko widgetak, tableta blokeatuta badago ere."</string>
+    <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"desautatu widgeta"</string>
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Pantaila blokeatuko widgetak"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Aplikazio bat widget baten bidez irekitzeko, zeu zarela egiaztatu beharko duzu. Gainera, kontuan izan edonork ikusi ahalko dituela halako widgetak, tableta blokeatuta badago ere. Baliteke widget batzuk pantaila blokeaturako egokiak ez izatea, eta agian ez da segurua haiek bertan gehitzea."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Ados"</string>
@@ -564,8 +565,8 @@
     <string name="media_projection_action_text" msgid="3634906766918186440">"Hasi"</string>
     <string name="empty_shade_text" msgid="8935967157319717412">"Ez dago jakinarazpenik"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"Ez dago jakinarazpen berririk"</string>
-    <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"Jakinarazpen egokituak aktibatuta"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"Epe labur batean jakinarazpen ugari jasotzen badituzu, bolumena jaitsi, eta pantailako leiho gainerakorrak murriztuko ditu gailuak orain, gehienez 2 minutuz."</string>
+    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Jakinarazpenak arintzeko ezarpena aktibatuta dago"</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Aldi berean jakinarazpen gehiegi jasotzen badituzu, gailuaren bolumena eta alertak automatikoki murriztuko dira 2 minutuz (gehienez)."</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Desaktibatu"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Jakinarazpen zaharragoak ikusteko, desblokeatu"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Zure gurasoak kudeatzen du gailua"</string>
@@ -732,8 +733,7 @@
     <string name="notification_alert_title" msgid="3656229781017543655">"Lehenetsia"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Automatikoa"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"Ez du tonurik jotzen edo dar-dar egiten"</string>
-    <!-- no translation found for notification_conversation_summary_low (3855696451919728790) -->
-    <skip />
+    <string name="notification_conversation_summary_low" msgid="3855696451919728790">"Ez du tonurik jotzen edo dar-dar egiten, baina elkarrizketen atalean agertzen da"</string>
     <string name="notification_channel_summary_default" msgid="777294388712200605">"Baliteke tonua jotzea edo dardara egitea, gailuaren ezarpenen arabera"</string>
     <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Baliteke tonua jotzea edo dardara egitea, gailuaren ezarpenen arabera. Modu lehenetsian, <xliff:g id="APP_NAME">%1$s</xliff:g> aplikazioko elkarrizketak burbuila gisa agertzen dira."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Ezarri sistemak zehaztu dezala jakinarazpen honek soinua edo dardara egin behar duen ala ez"</string>
@@ -790,8 +790,7 @@
     <string name="keyboard_key_page_up" msgid="173914303254199845">"Orrian gora"</string>
     <string name="keyboard_key_page_down" msgid="9035902490071829731">"Orrian behera"</string>
     <string name="keyboard_key_forward_del" msgid="5325501825762733459">"Ezabatu"</string>
-    <!-- no translation found for keyboard_key_esc (6230365950511411322) -->
-    <skip />
+    <string name="keyboard_key_esc" msgid="6230365950511411322">"Ihes"</string>
     <string name="keyboard_key_move_home" msgid="3496502501803911971">"Hasiera"</string>
     <string name="keyboard_key_move_end" msgid="99190401463834854">"Amaitu"</string>
     <string name="keyboard_key_insert" msgid="4621692715704410493">"Txertatu"</string>
@@ -1233,7 +1232,6 @@
     <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>
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Datu-konexioa"</string>
-    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> (<xliff:g id="NETWORKMODE">%2$s</xliff:g>)"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Konektatuta"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Aldi baterako konektatuta"</string>
     <string name="mobile_data_poor_connection" msgid="819617772268371434">"Konexio ahula"</string>
@@ -1374,8 +1372,7 @@
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Pantaila zatitzea"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Sarrera"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Aplikazioetarako lasterbideak"</string>
-    <!-- no translation found for shortcut_helper_category_current_app_shortcuts (4017840565974573628) -->
-    <skip />
+    <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Oraingo aplikazioa"</string>
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Erabilerraztasuna"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Lasterbideak"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Bilatu lasterbideak"</string>
@@ -1388,13 +1385,27 @@
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Eginda"</string>
     <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Bikain!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Egin atzera"</string>
-    <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"Atzera egiteko, pasatu 3 hatz ezkerrera edo eskuinera ukipen-panelean."</string>
+    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
+    <skip />
+    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
+    <skip />
     <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"3 hatz ukipen-panel baten gainean eskuinera eta ezkerrera mugitzen erakusten duen irudia"</string>
     <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Gailuaren pantailan atzera egiteko keinuaren animazioa erakusten duen irudia"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Teklatuaren hondoko argia"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"%1$d/%2$d maila"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Etxeko gailuen kontrola"</string>
     <string name="home_controls_dream_description" msgid="4644150952104035789">"Kontrolatu etxeko gailuak pantaila-babesletik"</string>
-    <!-- no translation found for volume_undo_action (5815519725211877114) -->
-    <skip />
+    <string name="volume_undo_action" msgid="5815519725211877114">"Desegin"</string>
+    <string name="back_edu_toast_content" msgid="4530314597378982956">"Atzera egiteko, pasatu 3 hatz ukipen-panelean ezkerrera edo eskuinera"</string>
+    <string name="home_edu_toast_content" msgid="3381071147871955415">"Hasierako pantailara joateko, pasatu 3 hatz ukipen-pantailan"</string>
+    <string name="overview_edu_toast_content" msgid="5797030644017804518">"Azkenaldiko aplikazioak ikusteko, pasatu 3 hatz ukipen-panelean gora eta eduki sakatuta"</string>
+    <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Aplikazio guztiak ikusteko, sakatu teklatuko ekintza-tekla"</string>
+    <string name="back_edu_notification_title" msgid="5624780717751357278">"Erabili ukipen-panela atzera egiteko"</string>
+    <string name="back_edu_notification_content" msgid="2497557451540954068">"Pasatu 3 hatz ezkerrera edo eskuinera. Sakatu keinu gehiago ikasteko."</string>
+    <string name="home_edu_notification_title" msgid="6097902076909654045">"Erabili ukipen-panela hasierako pantailara joateko"</string>
+    <string name="home_edu_notification_content" msgid="6631697734535766588">"Pasatu 3 hatz. Sakatu keinu gehiago ikasteko."</string>
+    <string name="overview_edu_notification_title" msgid="1265824157319562406">"Erabili ukipen-panela azkenaldiko aplikazioak ikusteko"</string>
+    <string name="overview_edu_notification_content" msgid="3578204677648432500">"Pasatu 3 hatz gora eta eduki sakatuta. Sakatu keinu gehiago ikasteko."</string>
+    <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Erabili teklatua aplikazio guztiak ikusteko"</string>
+    <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Sakatu ekintza-tekla edonoiz. Sakatu keinu gehiago ikasteko."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-eu/tiles_states_strings.xml b/packages/SystemUI/res/values-eu/tiles_states_strings.xml
index 45d5121..13e14e0 100644
--- a/packages/SystemUI/res/values-eu/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-eu/tiles_states_strings.xml
@@ -56,9 +56,6 @@
     <item msgid="5376619709702103243">"Desaktibatuta"</item>
     <item msgid="4875147066469902392">"Aktibatuta"</item>
   </string-array>
-    <!-- no translation found for tile_states_modes:0 (7764936419245199023) -->
-    <!-- no translation found for tile_states_modes:1 (2004750556637773692) -->
-    <!-- no translation found for tile_states_modes:2 (8968530753931637871) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Ez dago erabilgarri"</item>
     <item msgid="5044688398303285224">"Desaktibatuta"</item>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index ceb62e4..f4b9d9e 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -126,38 +126,25 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"برای مشاهده تک‌ضرب بزنید"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"خطا در ذخیره‌سازی ضبط صفحه‌نمایش"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"خطا هنگام شروع ضبط صفحه‌نمایش"</string>
-    <!-- no translation found for screenrecord_stop_dialog_title (8716193661764511095) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message (6262768207331626817) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5995770227684523244) -->
-    <skip />
+    <string name="screenrecord_stop_dialog_title" msgid="8716193661764511095">"ضبط متوقف شود؟"</string>
+    <string name="screenrecord_stop_dialog_message" msgid="6262768207331626817">"اکنون درحال ضبط کل صفحه‌نمایشتان هستید"</string>
+    <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"اکنون درحال ضبط <xliff:g id="APP_NAME">%1$s</xliff:g> هستید"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"توقف ضبط"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"درحال هم‌رسانی صفحه‌نمایش"</string>
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"صفحه هم‌رسانی متوقف شود؟"</string>
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen_with_host_app (522823522115375414) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen (5090115386271179270) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_specific (5923772039347985172) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_generic (6681016774654578261) -->
-    <skip />
+    <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"اکنون درحال هم‌رسانی کل صفحه‌نمایشتان با <xliff:g id="HOST_APP_NAME">%1$s</xliff:g> هستید"</string>
+    <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"اکنون درحال هم‌رسانی کل صفحه‌نمایشتان با یک برنامه هستید"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"اکنون درحال هم‌رسانی <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> هستید"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"اکنون درحال هم‌رسانی با یک برنامه هستید"</string>
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"توقف هم‌رسانی"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"درحال پخش محتوای صفحه‌نمایش"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"پخش محتوا متوقف شود؟"</string>
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen_with_device (1474703115926205251) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen (8419219169553867625) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app_with_device (2715934698604085519) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (8616103075630934513) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic_with_device (9213582497852420203) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic (4100272100480415076) -->
-    <skip />
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen_with_device" msgid="1474703115926205251">"اکنون درحال پخش محتوای کل صفحه‌نمایشتان در <xliff:g id="DEVICE_NAME">%1$s</xliff:g> هستید"</string>
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen" msgid="8419219169553867625">"اکنون درحال پخش محتوای کل صفحه‌نمایشتان در یکی از دستگاه‌های اطراف هستید"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app_with_device" msgid="2715934698604085519">"اکنون درحال پخش محتوای <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> در <xliff:g id="DEVICE_NAME">%2$s</xliff:g> هستید"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="8616103075630934513">"اکنون درحال پخش محتوای <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> در یکی از دستگاه‌های اطراف هستید"</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic_with_device" msgid="9213582497852420203">"اکنون درحال پخش محتوا در <xliff:g id="DEVICE_NAME">%1$s</xliff:g> هستید"</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic" msgid="4100272100480415076">"اکنون درحال پخش محتوا در یکی از دستگاه‌های اطراف هستید"</string>
     <string name="cast_to_other_device_stop_dialog_button" msgid="6420183747435521834">"توقف پخش محتوا"</string>
     <string name="close_dialog_button" msgid="4749497706540104133">"بستن"</string>
     <string name="issuerecord_title" msgid="286627115110121849">"ضبط‌کننده مشکل"</string>
@@ -303,8 +290,7 @@
     <string name="start_dreams" msgid="9131802557946276718">"محافظ صفحه"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"اترنت"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"مزاحم نشوید"</string>
-    <!-- no translation found for quick_settings_modes_label (5407025818652750501) -->
-    <skip />
+    <string name="quick_settings_modes_label" msgid="5407025818652750501">"حالت‌های اولویت‌دار"</string>
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"بلوتوث"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"هیچ دستگاه مرتبط شده‌ای موجود نیست"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"برای اتصال یا قطع اتصال دستگاه، تک‌ضرب بزنید"</string>
@@ -402,6 +388,9 @@
     <string name="performance" msgid="6552785217174378320">"عملکرد"</string>
     <string name="user_interface" msgid="3712869377953950887">"میانای کاربر"</string>
     <string name="thermal" msgid="6758074791325414831">"حرارتی"</string>
+    <string name="custom" msgid="3337456985275158299">"سفارشی"</string>
+    <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"تنظیمات ردیابی سفارشی"</string>
+    <string name="restore_default" msgid="5259420807486239755">"بازیابی پیش‌فرض"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"حالت یک‌دستی"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"سمعک"</string>
     <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"فعال"</string>
@@ -440,6 +429,17 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"باز کردن تنظیمات"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"دستگاه دیگر"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"تغییر وضعیت نمای کلی"</string>
+    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"حالت‌های اولویت‌دار"</string>
+    <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_off" msgid="1736604456618147306">"خاموش"</string>
+    <!-- no translation found for zen_mode_set_up (7457957033034460064) -->
+    <skip />
+    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
+    <skip />
+    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
+    <skip />
     <string name="zen_priority_introduction" msgid="3159291973383796646">"به‌جز هشدارها، یادآوری‌ها، رویدادها و تماس‌گیرندگانی که خودتان مشخص می‌کنید، هیچ صدا و لرزشی نخواهید داشت. همچنان صدای مواردی را که پخش می‌کنید می‌شنوید (ازجمله صدای موسیقی، ویدیو و بازی)."</string>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"به‌جز هشدارها، هیچ صدا و لرزشی نخواهید داشت. همچنان صدای مواردی را که پخش می‌کنید می‌شنوید (ازجمله صدای موسیقی، ویدیو و بازی)."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"سفارشی کردن"</string>
@@ -478,6 +478,8 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • درحال شارژ کردن آهسته • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> تا شارژ کامل"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • درحال شارژ شدن • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> تا شارژ کامل"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"ابزارک‌ها در صفحه قفل"</string>
+    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
+    <skip />
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"برای شروع آموزش گام‌به‌گام عمومی، تند به‌چپ بکشید"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"سفارشی‌سازی"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"بستن"</string>
@@ -504,10 +506,9 @@
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"انتخاب ابزارک"</string>
     <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"برداشتن ابزارک"</string>
     <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"جای‌گذاری ابزارک انتخاب‌شده"</string>
-    <!-- no translation found for communal_widget_picker_title (1953369090475731663) -->
-    <skip />
-    <!-- no translation found for communal_widget_picker_description (490515450110487871) -->
-    <skip />
+    <string name="communal_widget_picker_title" msgid="1953369090475731663">"ابزاره‌های صفحه قفل"</string>
+    <string name="communal_widget_picker_description" msgid="490515450110487871">"همه می‌توانند ابزاره‌ها را در صفحه قفل شما ببینند، حتی اگر رایانه لوحی قفل باشد."</string>
+    <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"لغو انتخاب ابزاره"</string>
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"ابزارک‌های صفحه قفل"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"برای باز کردن برنامه بااستفاده از ابزارک، باید هویت خودتان را به‌تأیید برسانید. همچنین، به‌خاطر داشته باشید که همه می‌توانند آن‌ها را مشاهده کنند، حتی وقتی رایانه لوحی‌تان قفل است. برخی‌از ابزارک‌ها ممکن است برای صفحه قفل درنظر گرفته نشده باشند و ممکن است اضافه کردن آن‌ها در اینجا ناامن باشد."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"متوجه‌ام"</string>
@@ -564,8 +565,8 @@
     <string name="media_projection_action_text" msgid="3634906766918186440">"اکنون شروع کنید"</string>
     <string name="empty_shade_text" msgid="8935967157319717412">"اعلانی موجود نیست"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"اعلان جدیدی وجود ندارد"</string>
-    <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"اعلان‌های تطبیقی روشن است"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"ازاین‌پس هروقت اعلان‌های زیادی در یک بازه زمانی کوتاه دریافت کنید، دستگاهتان تا دو دقیقه صدا را کم می‌کند و تعداد پنجره‌های بالاپر را در صفحه‌نمایش کاهش می‌دهد."</string>
+    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"«استراحت اعلان‌ها» روشن است"</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"وقتی به‌طور هم‌زمان تعداد بسیار زیادی اعلان دریافت کنید، میزان صدای دستگاه و هشدارها به‌طور خودکار تا ۲ دقیقه کاهش می‌یابد."</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"خاموش کردن"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"برای دیدن اعلان‌های قبلی قفل را باز کنید"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"این دستگاه را ولی‌تان مدیریت می‌کند"</string>
@@ -732,8 +733,7 @@
     <string name="notification_alert_title" msgid="3656229781017543655">"پیش‌فرض"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"خودکار"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"بدون صدا یا لرزش"</string>
-    <!-- no translation found for notification_conversation_summary_low (3855696451919728790) -->
-    <skip />
+    <string name="notification_conversation_summary_low" msgid="3855696451919728790">"«بدون صدا یا لرزش» همچنان در بخش مکالمه نشان داده می‌شود"</string>
     <string name="notification_channel_summary_default" msgid="777294388712200605">"بسته به تنظیمات دستگاه ممکن است زنگ بزند یا بلرزد"</string>
     <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"بسته به تنظیمات دستگاه ممکن است زنگ بزند یا بلرزد. مکالمه‌های <xliff:g id="APP_NAME">%1$s</xliff:g> به‌طور پیش‌فرض در حبابک نشان داده می‌شوند."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"سیستم را تنظیم کنید که تشخیص دهد اعلان صدا و لرزش داشته باشد یا نه"</string>
@@ -790,8 +790,7 @@
     <string name="keyboard_key_page_up" msgid="173914303254199845">"صفحه بعد"</string>
     <string name="keyboard_key_page_down" msgid="9035902490071829731">"صفحه قبل"</string>
     <string name="keyboard_key_forward_del" msgid="5325501825762733459">"حذف"</string>
-    <!-- no translation found for keyboard_key_esc (6230365950511411322) -->
-    <skip />
+    <string name="keyboard_key_esc" msgid="6230365950511411322">"کلید گریز"</string>
     <string name="keyboard_key_move_home" msgid="3496502501803911971">"ابتدا"</string>
     <string name="keyboard_key_move_end" msgid="99190401463834854">"End"</string>
     <string name="keyboard_key_insert" msgid="4621692715704410493">"Insert"</string>
@@ -1233,7 +1232,6 @@
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"اصالت‌سنجی لازم است. برای اصالت‌سنجی، حسگر اثر انگشت را لمس کنید."</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"تماس تلفنی درحال انجام"</string>
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"داده تلفن همراه"</string>
-    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"متصل است"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"موقتاً متصل است"</string>
     <string name="mobile_data_poor_connection" msgid="819617772268371434">"اتصال ضعیف"</string>
@@ -1387,13 +1385,27 @@
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"تمام"</string>
     <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"عالی است!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"برگشتن"</string>
-    <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"برای برگشتن، در هر جایی از صفحه لمسی، با سه انگشت تند به‌چپ یا راست بکشید."</string>
+    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
+    <skip />
+    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
+    <skip />
     <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"صفحه لمسی که سه انگشت را درحال حرکت به‌سمت راست و چپ نشان می‌دهد"</string>
     <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"صفحه‌نمایش دستگاه درحال نمایش پویانمایی مربوط به اشاره برگشتن"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"نور پس‌زمینه صفحه‌کلید"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"‏سطح %1$d از %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"کنترل خانه هوشمند"</string>
     <string name="home_controls_dream_description" msgid="4644150952104035789">"به کنترل خانه هوشمند به‌عنوان محافظ صفحه‌نمایش دسترسی سریع دارید"</string>
-    <!-- no translation found for volume_undo_action (5815519725211877114) -->
-    <skip />
+    <string name="volume_undo_action" msgid="5815519725211877114">"واگرداندن"</string>
+    <string name="back_edu_toast_content" msgid="4530314597378982956">"برای برگشتن، در صفحه لمسی با سه انگشت تند به‌چپ یا راست بکشید"</string>
+    <string name="home_edu_toast_content" msgid="3381071147871955415">"برای رفتن به صفحه اصلی، در صفحه لمسی با سه انگشت تند به‌بالا بکشید"</string>
+    <string name="overview_edu_toast_content" msgid="5797030644017804518">"برای مشاهده برنامه‌های اخیر، در صفحه لمسی با سه انگشت تند به‌بالا بکشید و نگه دارید"</string>
+    <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"برای مشاهده همه برنامه‌ها، دکمه کنش در صفحه‌کلید را فشار دهید"</string>
+    <string name="back_edu_notification_title" msgid="5624780717751357278">"برای برگشتن از صفحه لمسی استفاده کنید"</string>
+    <string name="back_edu_notification_content" msgid="2497557451540954068">"با سه انگشت تند به‌چپ یا راست بکشید. برای آشنایی با اشاره‌های بیشتر، تک‌ضرب بزنید."</string>
+    <string name="home_edu_notification_title" msgid="6097902076909654045">"برای رفتن به صفحه اصلی از صفحه لمسی استفاده کنید"</string>
+    <string name="home_edu_notification_content" msgid="6631697734535766588">"با سه انگشت تند به‌بالا بکشید. برای آشنایی با اشاره‌های بیشتر، تک‌ضرب بزنید."</string>
+    <string name="overview_edu_notification_title" msgid="1265824157319562406">"برای مشاهده برنامه‌های اخیر از صفحه لمسی استفاده کنید"</string>
+    <string name="overview_edu_notification_content" msgid="3578204677648432500">"با سه انگشت تند به‌بالا بکشید و نگه دارید. برای آشنایی با اشاره‌های بیشتر، تک‌ضرب بزنید."</string>
+    <string name="all_apps_edu_notification_title" msgid="372262997265569063">"برای مشاهده همه برنامه‌ها، از صفحه‌کلید استفاده کنید"</string>
+    <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"در هرزمانی دکمه کنش را فشار دهید. برای آشنایی با اشاره‌های بیشتر، تک‌ضرب بزنید."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-fa/tiles_states_strings.xml b/packages/SystemUI/res/values-fa/tiles_states_strings.xml
index d6e0e82..756b442 100644
--- a/packages/SystemUI/res/values-fa/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-fa/tiles_states_strings.xml
@@ -56,9 +56,6 @@
     <item msgid="5376619709702103243">"خاموش"</item>
     <item msgid="4875147066469902392">"روشن"</item>
   </string-array>
-    <!-- no translation found for tile_states_modes:0 (7764936419245199023) -->
-    <!-- no translation found for tile_states_modes:1 (2004750556637773692) -->
-    <!-- no translation found for tile_states_modes:2 (8968530753931637871) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"دردسترس نیست"</item>
     <item msgid="5044688398303285224">"خاموش"</item>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index 5ba52d5..79962f0 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -126,38 +126,25 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"Katso napauttamalla"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"Virhe näyttötallenteen tallentamisessa"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Virhe näytön tallennuksen aloituksessa"</string>
-    <!-- no translation found for screenrecord_stop_dialog_title (8716193661764511095) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message (6262768207331626817) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5995770227684523244) -->
-    <skip />
+    <string name="screenrecord_stop_dialog_title" msgid="8716193661764511095">"Lopetetaanko tallennus?"</string>
+    <string name="screenrecord_stop_dialog_message" msgid="6262768207331626817">"Tallennat tällä hetkellä koko näyttöä"</string>
+    <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Laite, jonka sisältöä tallennat: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Lopeta tallennus"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Näyttöä jaetaan"</string>
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Lopetetaanko näytön jakaminen?"</string>
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen_with_host_app (522823522115375414) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen (5090115386271179270) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_specific (5923772039347985172) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_generic (6681016774654578261) -->
-    <skip />
+    <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Jaat tällä hetkellä koko näyttöä: <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
+    <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Jaat tällä hetkellä koko näyttöä sovellukselle"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Jaat tällä hetkellä tätä: <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Jaat tällä hetkellä sovellusta"</string>
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Lopeta jakaminen"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Näyttöä striimataan"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Lopetetaanko striimaus?"</string>
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen_with_device (1474703115926205251) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen (8419219169553867625) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app_with_device (2715934698604085519) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (8616103075630934513) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic_with_device (9213582497852420203) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic (4100272100480415076) -->
-    <skip />
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen_with_device" msgid="1474703115926205251">"Striimaat tällä hetkellä koko näyttöä: <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen" msgid="8419219169553867625">"Striimaat tällä hetkellä koko näyttöä lähellä olevalle laitteelle"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app_with_device" msgid="2715934698604085519">"Striimaat (<xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>) tällä hetkellä: <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="8616103075630934513">"Striimaat (<xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>) tällä hetkellä lähellä olevalle laitteelle"</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic_with_device" msgid="9213582497852420203">"Striimaat tällä hetkellä: <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic" msgid="4100272100480415076">"Striimaat tällä hetkellä lähellä olevalle laitteelle"</string>
     <string name="cast_to_other_device_stop_dialog_button" msgid="6420183747435521834">"Lopeta striimaus"</string>
     <string name="close_dialog_button" msgid="4749497706540104133">"Sulje"</string>
     <string name="issuerecord_title" msgid="286627115110121849">"Ongelman tallentaja"</string>
@@ -303,8 +290,7 @@
     <string name="start_dreams" msgid="9131802557946276718">"Näytönsäästäjä"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"Älä häiritse"</string>
-    <!-- no translation found for quick_settings_modes_label (5407025818652750501) -->
-    <skip />
+    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Prioriteettitilat"</string>
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Laitepareja ei ole käytettävissä"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Muodosta yhteys laitteeseen tai katkaise yhteys napauttamalla"</string>
@@ -402,6 +388,9 @@
     <string name="performance" msgid="6552785217174378320">"Suorituskyky"</string>
     <string name="user_interface" msgid="3712869377953950887">"Käyttöliittymä"</string>
     <string name="thermal" msgid="6758074791325414831">"Lämpökamera"</string>
+    <string name="custom" msgid="3337456985275158299">"Yksilöity"</string>
+    <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Yksilöidyt jäljittämisasetukset"</string>
+    <string name="restore_default" msgid="5259420807486239755">"Palauta oletus"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Yhden käden moodi"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Kuulolaitteet"</string>
     <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktiivinen"</string>
@@ -440,6 +429,17 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Avaa Asetukset"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Muu laite"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Näytä/piilota viimeisimmät"</string>
+    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Prioriteettitilat"</string>
+    <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Valmis"</string>
+    <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Asetukset"</string>
+    <string name="zen_mode_on" msgid="9085304934016242591">"Päällä"</string>
+    <string name="zen_mode_off" msgid="1736604456618147306">"Pois päältä"</string>
+    <!-- no translation found for zen_mode_set_up (7457957033034460064) -->
+    <skip />
+    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
+    <skip />
+    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
+    <skip />
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Äänet ja värinät eivät häiritse sinua, paitsi jos ne ovat hälytyksiä, muistutuksia, tapahtumia tai määrittämiäsi soittajia. Kuulet edelleen kaiken valitsemasi sisällön, kuten musiikin, videot ja pelit."</string>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Äänet ja värinät eivät häiritse sinua, paitsi jos ne ovat hälytyksiä. Kuulet edelleen kaiken valitsemasi sisällön, kuten musiikin, videot ja pelit."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Muokkaa"</string>
@@ -478,6 +478,8 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Latautuu hitaasti • Täynnä <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> päästä"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Latautuu • Täynnä <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> päästä"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Widgetit lukitusnäytöllä"</string>
+    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
+    <skip />
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Aloita yhteisöesittely pyyhkäisemällä vasemmalle"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Muokkaa"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Hylkää"</string>
@@ -504,10 +506,9 @@
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"valitse widget"</string>
     <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"poista widget"</string>
     <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"aseta valittu widget"</string>
-    <!-- no translation found for communal_widget_picker_title (1953369090475731663) -->
-    <skip />
-    <!-- no translation found for communal_widget_picker_description (490515450110487871) -->
-    <skip />
+    <string name="communal_widget_picker_title" msgid="1953369090475731663">"Lukitusnäytön widgetit"</string>
+    <string name="communal_widget_picker_description" msgid="490515450110487871">"Kaikki voivat nähdä widgetit lukitusnäytöllä, vaikka tabletti olisi lukittuna."</string>
+    <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"poista widgetin valinta"</string>
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Lukitusnäytön widgetit"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Jos haluat avata sovelluksen käyttämällä widgetiä, sinun täytyy vahvistaa henkilöllisyytesi. Muista myös, että widgetit näkyvät kaikille, vaikka tabletti olisi lukittuna. Jotkin widgetit on ehkä tarkoitettu lukitusnäytölle, ja niiden lisääminen tänne ei välttämättä ole turvallista."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Selvä"</string>
@@ -564,8 +565,8 @@
     <string name="media_projection_action_text" msgid="3634906766918186440">"Aloita nyt"</string>
     <string name="empty_shade_text" msgid="8935967157319717412">"Ei ilmoituksia"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"Ei uusia ilmoituksia"</string>
-    <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"Mukautuvat ilmoitukset päällä"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"Laite vähentää nyt äänenvoimakkuutta ja ponnahdusikkunoiden määrää enintään kahdeksi minuutiksi, kun saat monta ilmoitusta lyhyellä aikavälillä."</string>
+    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Ilmoitusten vaimennus on päällä"</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Äänenvoimakkuus ja ilmoitukset vaimennetaan enintään 2 minuutiksi, kun saat paljon ilmoituksia."</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Laita pois päältä"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Avaa lukitus niin näet ilmoituksia"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Vanhempasi ylläpitää tätä laitetta"</string>
@@ -732,8 +733,7 @@
     <string name="notification_alert_title" msgid="3656229781017543655">"Oletus"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Automaattinen"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"Ei ääntä tai värinää"</string>
-    <!-- no translation found for notification_conversation_summary_low (3855696451919728790) -->
-    <skip />
+    <string name="notification_conversation_summary_low" msgid="3855696451919728790">"Ei ääntä tai värinää, mutta näkyy yhä keskusteluosiossa"</string>
     <string name="notification_channel_summary_default" msgid="777294388712200605">"Voi soida tai väristä laitteen asetuksista riippuen"</string>
     <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Voi soida tai väristä laitteen asetuksista riippuen. Keskusteluista (<xliff:g id="APP_NAME">%1$s</xliff:g>) luodaan oletuksena kuplia."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Järjestelmä valitsee, kuuluuko tästä ilmoituksesta ääntä tai väriseekö se"</string>
@@ -790,8 +790,7 @@
     <string name="keyboard_key_page_up" msgid="173914303254199845">"Page Up"</string>
     <string name="keyboard_key_page_down" msgid="9035902490071829731">"Page Down"</string>
     <string name="keyboard_key_forward_del" msgid="5325501825762733459">"Delete"</string>
-    <!-- no translation found for keyboard_key_esc (6230365950511411322) -->
-    <skip />
+    <string name="keyboard_key_esc" msgid="6230365950511411322">"Esc"</string>
     <string name="keyboard_key_move_home" msgid="3496502501803911971">"Home"</string>
     <string name="keyboard_key_move_end" msgid="99190401463834854">"End"</string>
     <string name="keyboard_key_insert" msgid="4621692715704410493">"Insert"</string>
@@ -1233,7 +1232,6 @@
     <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>
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobiilidata"</string>
-    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Yhdistetty"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Väliaikaisesti yhdistetty"</string>
     <string name="mobile_data_poor_connection" msgid="819617772268371434">"Heikko yhteys"</string>
@@ -1374,8 +1372,7 @@
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Jaettu näyttö"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Syöte"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Sovellusten pikakuvakkeet"</string>
-    <!-- no translation found for shortcut_helper_category_current_app_shortcuts (4017840565974573628) -->
-    <skip />
+    <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Nykyinen sovellus"</string>
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Saavutettavuus"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Pikanäppäimet"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Pikahaut"</string>
@@ -1388,13 +1385,27 @@
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Valmis"</string>
     <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Hienoa!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Takaisin"</string>
-    <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"Jos haluat siirtyä takaisin, pyyhkäise kosketuslevyllä vasemmalle tai oikealle kolmella sormella."</string>
+    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
+    <skip />
+    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
+    <skip />
     <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Kosketuslevy, jolla kolme sormea liikkuu oikealle ja vasemmalle"</string>
     <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Laitteen näyttö, jolla näkyy animaatio takaisin-eleestä"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Näppämistön taustavalo"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Taso %1$d/%2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Kodin ohjaus"</string>
     <string name="home_controls_dream_description" msgid="4644150952104035789">"Avaa kodin ohjaus nopeasti näytönsäästäjän kautta"</string>
-    <!-- no translation found for volume_undo_action (5815519725211877114) -->
-    <skip />
+    <string name="volume_undo_action" msgid="5815519725211877114">"Kumoa"</string>
+    <string name="back_edu_toast_content" msgid="4530314597378982956">"Jos haluat siirtyä takaisin, pyyhkäise kosketuslevyllä vasemmalle tai oikealle kolmella sormella."</string>
+    <string name="home_edu_toast_content" msgid="3381071147871955415">"Jos haluat siirtyä aloitussivulle, pyyhkäise kosketuslevyllä ylös kolmella sormella"</string>
+    <string name="overview_edu_toast_content" msgid="5797030644017804518">"Näet äskeiset sovellukset, kun pyyhkäiset ylös ja pidät kosketuslevyä painettuna kolmella sormella."</string>
+    <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Jos haluat nähdä kaikki sovellukset, paina näppäimistön toimintonäppäintä"</string>
+    <string name="back_edu_notification_title" msgid="5624780717751357278">"Takaisin siirtyminen kosketuslevyn avulla"</string>
+    <string name="back_edu_notification_content" msgid="2497557451540954068">"Pyyhkäise vasemmalle tai oikealle kolmella sormella. Lue lisää eleistä napauttamalla."</string>
+    <string name="home_edu_notification_title" msgid="6097902076909654045">"Aloitusnäytölle siirtyminen kosketuslevyn avulla"</string>
+    <string name="home_edu_notification_content" msgid="6631697734535766588">"Pyyhkäise ylös kolmella sormella. Lue lisää eleistä napauttamalla."</string>
+    <string name="overview_edu_notification_title" msgid="1265824157319562406">"Äskettäin käytettyjen sovellusten näkeminen kosketuslevyn avulla"</string>
+    <string name="overview_edu_notification_content" msgid="3578204677648432500">"Pyyhkäise ylös ja pidä kosketuslevyä painettuna kolmella sormella. Lue lisää eleistä napauttamalla."</string>
+    <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Kaikkien sovellusten näkeminen näppäimistön avulla"</string>
+    <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Voit painaa toimintonäppäintä milloin tahansa. Lue lisää eleistä napauttamalla."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-fi/tiles_states_strings.xml b/packages/SystemUI/res/values-fi/tiles_states_strings.xml
index 6290062..5ecc9595 100644
--- a/packages/SystemUI/res/values-fi/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-fi/tiles_states_strings.xml
@@ -56,9 +56,6 @@
     <item msgid="5376619709702103243">"Poissa päältä"</item>
     <item msgid="4875147066469902392">"Päällä"</item>
   </string-array>
-    <!-- no translation found for tile_states_modes:0 (7764936419245199023) -->
-    <!-- no translation found for tile_states_modes:1 (2004750556637773692) -->
-    <!-- no translation found for tile_states_modes:2 (8968530753931637871) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Ei saatavilla"</item>
     <item msgid="5044688398303285224">"Poissa päältä"</item>
diff --git a/packages/SystemUI/res/values-fr-rCA-feminine/strings.xml b/packages/SystemUI/res/values-fr-rCA-feminine/strings.xml
new file mode 100644
index 0000000..c179235
--- /dev/null
+++ b/packages/SystemUI/res/values-fr-rCA-feminine/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="accessibility_quick_settings_user" msgid="505821942882668619">"Connectée en tant que <xliff:g id="ID_1">%s</xliff:g>"</string>
+    <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Choisir l\'utilisatrice"</string>
+</resources>
diff --git a/packages/SystemUI/res/values-fr-rCA-masculine/strings.xml b/packages/SystemUI/res/values-fr-rCA-masculine/strings.xml
new file mode 100644
index 0000000..a658bee
--- /dev/null
+++ b/packages/SystemUI/res/values-fr-rCA-masculine/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="accessibility_quick_settings_user" msgid="505821942882668619">"Connecté en tant que <xliff:g id="ID_1">%s</xliff:g>"</string>
+    <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Choisir l\'utilisateur"</string>
+</resources>
diff --git a/packages/SystemUI/res/values-fr-rCA-neuter/strings.xml b/packages/SystemUI/res/values-fr-rCA-neuter/strings.xml
new file mode 100644
index 0000000..325f484
--- /dev/null
+++ b/packages/SystemUI/res/values-fr-rCA-neuter/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="accessibility_quick_settings_user" msgid="505821942882668619">"Personne connectée en tant que <xliff:g id="ID_1">%s</xliff:g>"</string>
+    <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Choisir l\'utilisateur·trice"</string>
+</resources>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index a72cdc7..f34ce98 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -126,38 +126,25 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"Touchez pour afficher"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"Erreur d\'enregistrement de l\'écran"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Une erreur s\'est produite lors du démarrage de l\'enregistrement d\'écran"</string>
-    <!-- no translation found for screenrecord_stop_dialog_title (8716193661764511095) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message (6262768207331626817) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5995770227684523244) -->
-    <skip />
+    <string name="screenrecord_stop_dialog_title" msgid="8716193661764511095">"Arrêter l\'enregistrement?"</string>
+    <string name="screenrecord_stop_dialog_message" msgid="6262768207331626817">"Vous êtes en train d\'enregistrer l\'intégralité de votre écran"</string>
+    <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Vous êtes en train d\'enregistrer <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Arrêter l\'enregistrement"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Partage d\'écran en cours…"</string>
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Arrêter le partage d\'écran?"</string>
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen_with_host_app (522823522115375414) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen (5090115386271179270) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_specific (5923772039347985172) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_generic (6681016774654578261) -->
-    <skip />
+    <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Vous partagez actuellement l\'intégralité de votre écran avec <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
+    <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Vous partagez actuellement l\'intégralité de votre écran avec une appli"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Vous partagez actuellement <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Vous partagez actuellement une appli"</string>
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Arrêter le partage"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Diffusion de l\'écran en cours…"</string>
-    <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Arrêter de diffuser?"</string>
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen_with_device (1474703115926205251) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen (8419219169553867625) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app_with_device (2715934698604085519) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (8616103075630934513) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic_with_device (9213582497852420203) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic (4100272100480415076) -->
-    <skip />
+    <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Arrêter la diffusion?"</string>
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen_with_device" msgid="1474703115926205251">"Vous diffusez actuellement l\'intégralité de votre écran sur <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen" msgid="8419219169553867625">"Vous diffusez actuellement l\'intégralité de votre écran sur un appareil à proximité"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app_with_device" msgid="2715934698604085519">"Vous diffusez actuellement <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> sur <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="8616103075630934513">"Vous diffusez actuellement <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> sur un appareil à proximité"</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic_with_device" msgid="9213582497852420203">"Vous diffusez actuellement du contenu sur <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic" msgid="4100272100480415076">"Vous diffusez actuellement du contenu sur un appareil à proximité"</string>
     <string name="cast_to_other_device_stop_dialog_button" msgid="6420183747435521834">"Arrêter la diffusion"</string>
     <string name="close_dialog_button" msgid="4749497706540104133">"Fermer"</string>
     <string name="issuerecord_title" msgid="286627115110121849">"Enregistreur de problèmes"</string>
@@ -223,7 +210,7 @@
     <string name="biometric_dialog_last_pattern_attempt_before_wipe_user" msgid="8400180746043407270">"Si vous entrez un schéma incorrect à la prochaine tentative, cet utilisateur sera supprimé."</string>
     <string name="biometric_dialog_last_pin_attempt_before_wipe_user" msgid="4159878829962411168">"Si vous entrez un NIP incorrect à la prochaine tentative, cet utilisateur sera supprimé."</string>
     <string name="biometric_dialog_last_password_attempt_before_wipe_user" msgid="4695682515465063885">"Si vous entrez un mot de passe incorrect à la prochaine tentative, cet utilisateur sera supprimé."</string>
-    <string name="biometric_dialog_last_pattern_attempt_before_wipe_profile" msgid="6045224069529284686">"Si vous entrez un schéma incorrect à la prochaine tentative suivante, votre profil professionnel et ses données seront supprimés."</string>
+    <string name="biometric_dialog_last_pattern_attempt_before_wipe_profile" msgid="6045224069529284686">"Si vous entrez un schéma incorrect à la prochaine tentative, votre profil professionnel et ses données seront supprimés."</string>
     <string name="biometric_dialog_last_pin_attempt_before_wipe_profile" msgid="545567685899091757">"Si vous entrez un NIP incorrect à la prochaine tentative, votre profil professionnel et ses données seront supprimés."</string>
     <string name="biometric_dialog_last_password_attempt_before_wipe_profile" msgid="8538032972389729253">"Si vous entrez un mot de passe incorrect à la prochaine tentative suivante, votre profil professionnel et ses données seront supprimés."</string>
     <string name="biometric_re_enroll_dialog_confirm" msgid="3049858021857801836">"Configuration"</string>
@@ -303,8 +290,7 @@
     <string name="start_dreams" msgid="9131802557946276718">"Écran de veille"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"Ne pas déranger"</string>
-    <!-- no translation found for quick_settings_modes_label (5407025818652750501) -->
-    <skip />
+    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Modes prioritaires"</string>
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Aucun des appareils associés n\'est disponible"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Touchez pour connecter ou déconnecter un appareil"</string>
@@ -402,6 +388,9 @@
     <string name="performance" msgid="6552785217174378320">"Performance"</string>
     <string name="user_interface" msgid="3712869377953950887">"Interface utilisateur"</string>
     <string name="thermal" msgid="6758074791325414831">"Thermique"</string>
+    <string name="custom" msgid="3337456985275158299">"Personnalisé"</string>
+    <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Paramètres personnalisés de la trace"</string>
+    <string name="restore_default" msgid="5259420807486239755">"Restaurer la valeur par défaut"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Mode Une main"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Appareils auditifs"</string>
     <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Actives"</string>
@@ -440,6 +429,17 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Ouvrir les paramètres"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Autre appareil"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Basculer l\'aperçu"</string>
+    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Modes prioritaires"</string>
+    <string name="zen_modes_dialog_done" msgid="6654130880256438950">"OK"</string>
+    <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Paramètres"</string>
+    <string name="zen_mode_on" msgid="9085304934016242591">"Activé"</string>
+    <string name="zen_mode_off" msgid="1736604456618147306">"Désactivé"</string>
+    <!-- no translation found for zen_mode_set_up (7457957033034460064) -->
+    <skip />
+    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
+    <skip />
+    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
+    <skip />
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Vous ne serez pas dérangé par les sons et les vibrations, sauf pour les alarmes, les rappels, les événements et les appelants que vous sélectionnez. Vous entendrez tout ce que vous choisissez d\'écouter, y compris la musique, les vidéos et les jeux."</string>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Vous ne serez pas dérangé par les sons et les vibrations, sauf pour les alarmes. Vous entendrez tout ce que vous choisissez d\'écouter, y compris la musique, les vidéos et les jeux."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Personnaliser"</string>
@@ -478,6 +478,8 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"En recharge lente : <xliff:g id="PERCENTAGE">%2$s</xliff:g> • Terminée <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Recharge en cours… • Se terminera dans <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Widgets sur l\'écran de verrouillage"</string>
+    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
+    <skip />
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Balayer l\'écran vers la gauche pour démarrer le tutoriel communautaire"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Personnaliser"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Fermer"</string>
@@ -504,10 +506,9 @@
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"sélectionner le widget"</string>
     <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"retirer le widget"</string>
     <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"placer le widget sélectionné"</string>
-    <!-- no translation found for communal_widget_picker_title (1953369090475731663) -->
-    <skip />
-    <!-- no translation found for communal_widget_picker_description (490515450110487871) -->
-    <skip />
+    <string name="communal_widget_picker_title" msgid="1953369090475731663">"Widgets de l\'écran de verrouillage"</string>
+    <string name="communal_widget_picker_description" msgid="490515450110487871">"N\'importe qui peut voir les widgets sur votre écran de verrouillage, même si votre tablette est verrouillée."</string>
+    <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"désélectionner le widget"</string>
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widgets de l\'écran de verrouillage"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Pour ouvrir une appli à l\'aide d\'un widget, vous devrez confirmer votre identité. En outre, gardez à l\'esprit que tout le monde peut les voir, même lorsque votre tablette est verrouillée. Certains widgets n\'ont peut-être pas été conçus pour votre écran de verrouillage et il pourrait être dangereux de les ajouter ici."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"OK"</string>
@@ -522,7 +523,7 @@
     <string name="guest_notification_session_active" msgid="5567273684713471450">"Vous êtes en mode Invité"</string>
     <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Si vous ajoutez un nouvel utilisateur, vous quitterez le mode Invité, et toutes les applis et données de la session d\'invité en cours seront supprimées."</string>
     <string name="user_limit_reached_title" msgid="2429229448830346057">"Limite d\'utilisateurs atteinte"</string>
-    <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{Vous ne pouvez créer qu\'un seul utilisateur.}one{Vous pouvez ajouter jusqu\'à # utilisateur.}many{Vous pouvez ajouter jusqu\'à # d\'utilisateurs.}other{Vous pouvez ajouter jusqu\'à # utilisateurs.}}"</string>
+    <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{Un seul utilisateur peut être créé}one{Vous pouvez ajouter jusqu\'à # utilisateur.}many{Vous pouvez ajouter jusqu\'à # d\'utilisateurs.}other{Vous pouvez ajouter jusqu\'à # utilisateurs.}}"</string>
     <string name="user_remove_user_title" msgid="9124124694835811874">"Supprimer l\'utilisateur?"</string>
     <string name="user_remove_user_message" msgid="6702834122128031833">"Toutes les applis et les données de cet utilisateur seront supprimées."</string>
     <string name="user_remove_user_remove" msgid="8387386066949061256">"Supprimer"</string>
@@ -564,8 +565,8 @@
     <string name="media_projection_action_text" msgid="3634906766918186440">"Commencer"</string>
     <string name="empty_shade_text" msgid="8935967157319717412">"Aucune notification"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"Aucune nouvelle notification"</string>
-    <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"Notifications adaptatives actives"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"Votre appareil réduit le volume et les fenêtres contextuelles à l\'écran pendant 2 minutes max. quand vous recevez plusieurs notifications rapidement."</string>
+    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"L\'atténuation des notifications est activée"</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Le volume de votre appareil est réduit pendant deux minutes si vous recevez trop de notifications."</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Désactiver"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Déverr. pour voir les anciennes notif."</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Cet appareil est géré par ton parent"</string>
@@ -732,8 +733,7 @@
     <string name="notification_alert_title" msgid="3656229781017543655">"Par défaut"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Automatique"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"Aucun son ni vibration"</string>
-    <!-- no translation found for notification_conversation_summary_low (3855696451919728790) -->
-    <skip />
+    <string name="notification_conversation_summary_low" msgid="3855696451919728790">"Pas de son ni de vibration, mais s\'affiche tout de même dans la section « conversations »"</string>
     <string name="notification_channel_summary_default" msgid="777294388712200605">"Peut sonner ou vibrer, selon les paramètres de l\'appareil"</string>
     <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Peut sonner ou vibrer, selon les paramètres de l\'appareil. Conversations des bulles de <xliff:g id="APP_NAME">%1$s</xliff:g> par défaut."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Faire en sorte que le système détermine si cette notification devrait émettre un son ou vibrer"</string>
@@ -790,8 +790,7 @@
     <string name="keyboard_key_page_up" msgid="173914303254199845">"Page précédente"</string>
     <string name="keyboard_key_page_down" msgid="9035902490071829731">"Page suivante"</string>
     <string name="keyboard_key_forward_del" msgid="5325501825762733459">"Supprimer"</string>
-    <!-- no translation found for keyboard_key_esc (6230365950511411322) -->
-    <skip />
+    <string name="keyboard_key_esc" msgid="6230365950511411322">"Échap"</string>
     <string name="keyboard_key_move_home" msgid="3496502501803911971">"Accueil"</string>
     <string name="keyboard_key_move_end" msgid="99190401463834854">"Fin"</string>
     <string name="keyboard_key_insert" msgid="4621692715704410493">"Insérer"</string>
@@ -924,7 +923,7 @@
     <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Ouvrir les paramètres."</string>
     <string name="accessibility_quick_settings_expand" msgid="2609275052412521467">"Ouvrir les réglages rapides."</string>
     <string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"Fermer les réglages rapides."</string>
-    <string name="accessibility_quick_settings_user" msgid="505821942882668619">"Connecté comme <xliff:g id="ID_1">%s</xliff:g>"</string>
+    <string name="accessibility_quick_settings_user" msgid="505821942882668619">"Personne connectée en tant que <xliff:g id="ID_1">%s</xliff:g>"</string>
     <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>
@@ -1013,7 +1012,7 @@
     <string name="privacy_type_media_projection" msgid="8136723828804251547">"enregistrement d\'écran"</string>
     <string name="music_controls_no_title" msgid="4166497066552290938">"Sans titre"</string>
     <string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"Veille"</string>
-    <string name="font_scaling_dialog_title" msgid="6273107303850248375">"Taille de police"</string>
+    <string name="font_scaling_dialog_title" msgid="6273107303850248375">"Taille de la police"</string>
     <string name="font_scaling_smaller" msgid="1012032217622008232">"Rapetisser"</string>
     <string name="font_scaling_larger" msgid="5476242157436806760">"Agrandir"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Fenêtre d\'agrandissement"</string>
@@ -1233,7 +1232,6 @@
     <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>
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Données cellulaires"</string>
-    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</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>
     <string name="mobile_data_poor_connection" msgid="819617772268371434">"Connexion faible"</string>
@@ -1374,8 +1372,7 @@
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Écran divisé"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Entrée"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Raccourcis des applis"</string>
-    <!-- no translation found for shortcut_helper_category_current_app_shortcuts (4017840565974573628) -->
-    <skip />
+    <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Appli actuelle"</string>
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accessibilité"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Raccourcis-clavier"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Recherchez des raccourcis"</string>
@@ -1388,13 +1385,27 @@
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"OK"</string>
     <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Bon travail!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Retour"</string>
-    <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"Pour revenir en arrière, balayez vers la gauche ou la droite en utilisant trois doigts n\'importe où sur le pavé tactile."</string>
+    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
+    <skip />
+    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
+    <skip />
     <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Pavé tactile montrant trois doigts se déplaçant à droite et à gauche"</string>
     <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Écran de l\'appareil montrant l\'animation pour le geste de retour"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Rétroéclairage du clavier"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Niveau %1$d de %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Domotique"</string>
     <string name="home_controls_dream_description" msgid="4644150952104035789">"Accès rapide : domotique sous forme d\'Écran de veille"</string>
-    <!-- no translation found for volume_undo_action (5815519725211877114) -->
-    <skip />
+    <string name="volume_undo_action" msgid="5815519725211877114">"Annuler"</string>
+    <string name="back_edu_toast_content" msgid="4530314597378982956">"Pour revenir en arrière, balayez l\'écran vers la gauche ou vers la droite avec trois doigts sur le pavé tactile"</string>
+    <string name="home_edu_toast_content" msgid="3381071147871955415">"Pour accéder à l\'écran d\'accueil, balayez l\'écran vers le haut avec trois doigts sur le pavé tactile"</string>
+    <string name="overview_edu_toast_content" msgid="5797030644017804518">"Pour afficher les applis récentes, balayez l\'écran vers le haut avec trois doigts sur le pavé tactile et maintenez-les en place"</string>
+    <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Pour afficher toutes vos applis, appuyez sur la touche d\'action de votre clavier"</string>
+    <string name="back_edu_notification_title" msgid="5624780717751357278">"Utiliser votre pavé tactile pour revenir en arrière"</string>
+    <string name="back_edu_notification_content" msgid="2497557451540954068">"Balayez vers la gauche ou vers la droite avec trois doigts. Touchez pour apprendre d\'autres gestes."</string>
+    <string name="home_edu_notification_title" msgid="6097902076909654045">"Utilisez votre pavé tactile pour accéder à l\'écran d\'accueil"</string>
+    <string name="home_edu_notification_content" msgid="6631697734535766588">"Balayez l\'écran vers le haut avec trois doigts. Touchez pour apprendre d\'autres gestes."</string>
+    <string name="overview_edu_notification_title" msgid="1265824157319562406">"Utiliser le pavé tactile pour afficher les applis récentes"</string>
+    <string name="overview_edu_notification_content" msgid="3578204677648432500">"Balayez l\'écran vers le haut avec trois doigts et maintenez-les en place. Touchez pour apprendre d\'autres gestes."</string>
+    <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Utiliser votre clavier pour afficher toutes les applis"</string>
+    <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Appuyez sur la touche d\'action à tout moment. Touchez pour apprendre d\'autres gestes."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-fr-rCA/tiles_states_strings.xml b/packages/SystemUI/res/values-fr-rCA/tiles_states_strings.xml
index e0445fa..f12634f 100644
--- a/packages/SystemUI/res/values-fr-rCA/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/tiles_states_strings.xml
@@ -38,7 +38,7 @@
   </string-array>
   <string-array name="tile_states_wifi">
     <item msgid="8054147400538405410">"Non disponible"</item>
-    <item msgid="4293012229142257455">"Désactivé"</item>
+    <item msgid="4293012229142257455">"Désactivée"</item>
     <item msgid="6221288736127914861">"Activé"</item>
   </string-array>
   <string-array name="tile_states_cell">
@@ -53,12 +53,9 @@
   </string-array>
   <string-array name="tile_states_dnd">
     <item msgid="467587075903158357">"Non disponible"</item>
-    <item msgid="5376619709702103243">"Désactivé"</item>
+    <item msgid="5376619709702103243">"Désactivée"</item>
     <item msgid="4875147066469902392">"Activé"</item>
   </string-array>
-    <!-- no translation found for tile_states_modes:0 (7764936419245199023) -->
-    <!-- no translation found for tile_states_modes:1 (2004750556637773692) -->
-    <!-- no translation found for tile_states_modes:2 (8968530753931637871) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Non disponible"</item>
     <item msgid="5044688398303285224">"Désactivée"</item>
@@ -71,12 +68,12 @@
   </string-array>
   <string-array name="tile_states_bt">
     <item msgid="5330252067413512277">"Non disponible"</item>
-    <item msgid="5315121904534729843">"Désactivé"</item>
+    <item msgid="5315121904534729843">"Désactivée"</item>
     <item msgid="503679232285959074">"Activé"</item>
   </string-array>
   <string-array name="tile_states_airplane">
     <item msgid="1985366811411407764">"Non disponible"</item>
-    <item msgid="4801037224991420996">"Désactivé"</item>
+    <item msgid="4801037224991420996">"Désactivée"</item>
     <item msgid="1982293347302546665">"Activé"</item>
   </string-array>
   <string-array name="tile_states_location">
@@ -86,7 +83,7 @@
   </string-array>
   <string-array name="tile_states_hotspot">
     <item msgid="3145597331197351214">"Non disponible"</item>
-    <item msgid="5715725170633593906">"Désactivé"</item>
+    <item msgid="5715725170633593906">"Désactivée"</item>
     <item msgid="2075645297847971154">"Activé"</item>
   </string-array>
   <string-array name="tile_states_color_correction">
@@ -106,7 +103,7 @@
   </string-array>
   <string-array name="tile_states_dark">
     <item msgid="2762596907080603047">"Non disponible"</item>
-    <item msgid="400477985171353">"Désactivé"</item>
+    <item msgid="400477985171353">"Désactivée"</item>
     <item msgid="630890598801118771">"Activé"</item>
   </string-array>
   <string-array name="tile_states_work">
@@ -146,7 +143,7 @@
   </string-array>
   <string-array name="tile_states_cameratoggle">
     <item msgid="6680671247180519913">"Non disponible"</item>
-    <item msgid="4765607635752003190">"Désactivé"</item>
+    <item msgid="4765607635752003190">"Désactivée"</item>
     <item msgid="1697460731949649844">"Activé"</item>
   </string-array>
   <string-array name="tile_states_mictoggle">
@@ -156,7 +153,7 @@
   </string-array>
   <string-array name="tile_states_controls">
     <item msgid="8199009425335668294">"Non disponibles"</item>
-    <item msgid="4544919905196727508">"Désactivées"</item>
+    <item msgid="4544919905196727508">"Désactivée"</item>
     <item msgid="3422023746567004609">"Activées"</item>
   </string-array>
   <string-array name="tile_states_wallet">
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index 240f013..ad63e9e 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -126,39 +126,26 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"Appuyez pour afficher"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"Erreur lors de l\'enregistrement de l\'écran"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Erreur lors du démarrage de l\'enregistrement de l\'écran"</string>
-    <!-- no translation found for screenrecord_stop_dialog_title (8716193661764511095) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message (6262768207331626817) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5995770227684523244) -->
-    <skip />
+    <string name="screenrecord_stop_dialog_title" msgid="8716193661764511095">"Arrêter l\'enregistrement ?"</string>
+    <string name="screenrecord_stop_dialog_message" msgid="6262768207331626817">"Vous enregistrez actuellement l\'intégralité de votre écran"</string>
+    <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Vous enregistrez actuellement <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Arrêter l\'enregistrement"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Partage de l\'écran…"</string>
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Arrêter le partage d\'écran ?"</string>
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen_with_host_app (522823522115375414) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen (5090115386271179270) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_specific (5923772039347985172) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_generic (6681016774654578261) -->
-    <skip />
+    <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Vous partagez actuellement l\'intégralité de votre écran avec <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
+    <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Vous partagez actuellement l\'intégralité de votre écran avec une appli"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Vous partagez actuellement <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Vous partagez actuellement une appli"</string>
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Arrêter le partage"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Cast de l\'écran"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Arrêter de caster ?"</string>
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen_with_device (1474703115926205251) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen (8419219169553867625) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app_with_device (2715934698604085519) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (8616103075630934513) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic_with_device (9213582497852420203) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic (4100272100480415076) -->
-    <skip />
-    <string name="cast_to_other_device_stop_dialog_button" msgid="6420183747435521834">"Arrêter de caster du contenu"</string>
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen_with_device" msgid="1474703115926205251">"Vous castez actuellement l\'intégralité de votre écran sur <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen" msgid="8419219169553867625">"Vous castez actuellement l\'intégralité de votre écran sur un appareil à proximité"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app_with_device" msgid="2715934698604085519">"Vous castez actuellement <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> sur <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="8616103075630934513">"Vous castez actuellement <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> sur un appareil à proximité"</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic_with_device" msgid="9213582497852420203">"Vous castez actuellement du contenu sur <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic" msgid="4100272100480415076">"Vous castez actuellement du contenu sur un appareil à proximité"</string>
+    <string name="cast_to_other_device_stop_dialog_button" msgid="6420183747435521834">"Arrêter de caster"</string>
     <string name="close_dialog_button" msgid="4749497706540104133">"Fermer"</string>
     <string name="issuerecord_title" msgid="286627115110121849">"Enregistreur de problèmes"</string>
     <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Enregistrement du problème"</string>
@@ -303,8 +290,7 @@
     <string name="start_dreams" msgid="9131802557946276718">"Économiseur d\'écran"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"Ne pas déranger"</string>
-    <!-- no translation found for quick_settings_modes_label (5407025818652750501) -->
-    <skip />
+    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Modes prioritaires"</string>
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Aucun appareil associé disponible."</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Appuyez pour connecter ou déconnecter un appareil"</string>
@@ -402,6 +388,9 @@
     <string name="performance" msgid="6552785217174378320">"Performances"</string>
     <string name="user_interface" msgid="3712869377953950887">"Interface utilisateur"</string>
     <string name="thermal" msgid="6758074791325414831">"Thermique"</string>
+    <string name="custom" msgid="3337456985275158299">"Personnalisé"</string>
+    <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Paramètres Trace personnalisés"</string>
+    <string name="restore_default" msgid="5259420807486239755">"Rétablir les paramètres par défaut"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Mode une main"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Appareils auditifs"</string>
     <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Actifs"</string>
@@ -440,6 +429,17 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Ouvrir les paramètres"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Autre appareil"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Activer/Désactiver l\'écran Récents"</string>
+    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Modes prioritaires"</string>
+    <string name="zen_modes_dialog_done" msgid="6654130880256438950">"OK"</string>
+    <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Paramètres"</string>
+    <string name="zen_mode_on" msgid="9085304934016242591">"Activé"</string>
+    <string name="zen_mode_off" msgid="1736604456618147306">"Désactivé"</string>
+    <!-- no translation found for zen_mode_set_up (7457957033034460064) -->
+    <skip />
+    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
+    <skip />
+    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
+    <skip />
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Vous ne serez pas dérangé par des sons ou des vibrations, hormis ceux des alarmes, des rappels, des événements et des appelants de votre choix. Vous entendrez encore les sons que vous choisirez de jouer, notamment la musique, les vidéos et les jeux."</string>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Vous ne serez pas dérangé par des sons ou des vibrations, hormis ceux des alarmes. Vous entendrez encore les sons que vous choisirez de jouer, comme la musique, les vidéos et les jeux."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Personnaliser"</string>
@@ -478,6 +478,8 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Recharge lente • Temps restant : <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Recharge • Temps restant : <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Widgets sur l\'écran de verrouillage"</string>
+    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
+    <skip />
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Balayer vers la gauche pour démarrer le tutoriel collectif"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Personnaliser"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Fermer"</string>
@@ -504,10 +506,9 @@
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"sélectionner un widget"</string>
     <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"supprimer le widget"</string>
     <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"positionner le widget sélectionné"</string>
-    <!-- no translation found for communal_widget_picker_title (1953369090475731663) -->
-    <skip />
-    <!-- no translation found for communal_widget_picker_description (490515450110487871) -->
-    <skip />
+    <string name="communal_widget_picker_title" msgid="1953369090475731663">"Widgets sur l\'écran de verrouillage"</string>
+    <string name="communal_widget_picker_description" msgid="490515450110487871">"N\'importe qui peut consulter les widgets sur votre écran de verrouillage, même si votre tablette est verrouillée."</string>
+    <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"désélectionner le widget"</string>
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widgets pour l\'écran de verrouillage"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Pour ouvrir une appli à l\'aide d\'un widget, vous devez confirmer qu\'il s\'agit bien de vous. N\'oubliez pas non plus que tout le monde peut voir vos widgets, même lorsque votre tablette est verrouillée. Certains d\'entre eux n\'ont pas été conçus pour l\'écran de verrouillage et les ajouter à cet endroit peut s\'avérer dangereux."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"OK"</string>
@@ -564,8 +565,8 @@
     <string name="media_projection_action_text" msgid="3634906766918186440">"Commencer"</string>
     <string name="empty_shade_text" msgid="8935967157319717412">"Aucune notification"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"Aucune nouvelle notification"</string>
-    <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"Les notifications intelligentes sont activées"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"Désormais, lorsque vous recevez de nombreuses notifications en peu de temps, votre appareil baisse le volume et réduit les pop-up qui apparaissent à l\'écran pendant deux minutes maximum."</string>
+    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"La limitation des notifications est activée"</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Les alertes et le volume de l\'appareil sont réduits automatiquement pendant 2 minutes max. quand vous recevez trop de notifications à la fois."</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Désactiver"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Déverrouiller pour voir anciennes notifications"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Cet appareil est géré par tes parents"</string>
@@ -732,8 +733,7 @@
     <string name="notification_alert_title" msgid="3656229781017543655">"Par défaut"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Automatique"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"Ni son, ni vibreur"</string>
-    <!-- no translation found for notification_conversation_summary_low (3855696451919728790) -->
-    <skip />
+    <string name="notification_conversation_summary_low" msgid="3855696451919728790">"Pas de son ni de vibration, mais toujours visible dans la section de la conversation"</string>
     <string name="notification_channel_summary_default" msgid="777294388712200605">"Peut sonner ou vibrer en fonction des paramètres de l\'appareil"</string>
     <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Peut sonner ou vibrer en fonction des paramètres de l\'appareil. Les conversations provenant de <xliff:g id="APP_NAME">%1$s</xliff:g> s\'affichent sous forme de bulles par défaut."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Laisser le système déterminer si cette notification doit être accompagnée d\'un son ou d\'une vibration"</string>
@@ -790,8 +790,7 @@
     <string name="keyboard_key_page_up" msgid="173914303254199845">"Page précédente"</string>
     <string name="keyboard_key_page_down" msgid="9035902490071829731">"Page suivante"</string>
     <string name="keyboard_key_forward_del" msgid="5325501825762733459">"Supprimer"</string>
-    <!-- no translation found for keyboard_key_esc (6230365950511411322) -->
-    <skip />
+    <string name="keyboard_key_esc" msgid="6230365950511411322">"Échap"</string>
     <string name="keyboard_key_move_home" msgid="3496502501803911971">"Accueil"</string>
     <string name="keyboard_key_move_end" msgid="99190401463834854">"Fin"</string>
     <string name="keyboard_key_insert" msgid="4621692715704410493">"Insérer"</string>
@@ -1233,7 +1232,6 @@
     <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>
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Données mobiles"</string>
-    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Connecté"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Connexion temporaire"</string>
     <string name="mobile_data_poor_connection" msgid="819617772268371434">"Connexion médiocre"</string>
@@ -1374,8 +1372,7 @@
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Écran partagé"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Entrée"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Raccourcis d\'application"</string>
-    <!-- no translation found for shortcut_helper_category_current_app_shortcuts (4017840565974573628) -->
-    <skip />
+    <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Appli actuelle"</string>
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accessibilité"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Raccourcis clavier"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Raccourcis de recherche"</string>
@@ -1388,13 +1385,27 @@
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"OK"</string>
     <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Bravo !"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Retour"</string>
-    <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"Pour retourner en arrière, balayez vers la gauche avec trois doigts n\'importe où sur le pavé tactile."</string>
+    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
+    <skip />
+    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
+    <skip />
     <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Pavé tactile sur lequel trois doigts glissent vers la droite, puis vers la gauche"</string>
     <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Écran d\'un appareil affichant l\'animation du geste Retour"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Rétroéclairage du clavier"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Niveau %1$d sur %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Contrôle de la maison"</string>
     <string name="home_controls_dream_description" msgid="4644150952104035789">"Domotique sous forme d\'économiseur d\'écran"</string>
-    <!-- no translation found for volume_undo_action (5815519725211877114) -->
-    <skip />
+    <string name="volume_undo_action" msgid="5815519725211877114">"Annuler"</string>
+    <string name="back_edu_toast_content" msgid="4530314597378982956">"Pour retourner en arrière, glissez vers la gauche ou la droite avec trois doigts sur le pavé tactile"</string>
+    <string name="home_edu_toast_content" msgid="3381071147871955415">"Pour revenir à l\'accueil, balayez vers le haut avec trois doigts sur le pavé tactile"</string>
+    <string name="overview_edu_toast_content" msgid="5797030644017804518">"Pour afficher les applis récentes, balayez vers le haut avec trois doigts sur le pavé tactile et maintenez-les."</string>
+    <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Pour afficher toutes vos applis, appuyez sur la touche d\'action de votre clavier"</string>
+    <string name="back_edu_notification_title" msgid="5624780717751357278">"Utilisez votre pavé tactile pour revenir en arrière"</string>
+    <string name="back_edu_notification_content" msgid="2497557451540954068">"Balayez vers la gauche ou la droite en utilisant trois doigts. Appuyez pour apprendre d\'autres gestes."</string>
+    <string name="home_edu_notification_title" msgid="6097902076909654045">"Utilisez votre pavé tactile pour revenir à l\'accueil"</string>
+    <string name="home_edu_notification_content" msgid="6631697734535766588">"Balayez vers le haut avec trois doigts. Appuyez pour apprendre d\'autres gestes."</string>
+    <string name="overview_edu_notification_title" msgid="1265824157319562406">"Utilisez votre pavé tactile pour afficher les applis récentes"</string>
+    <string name="overview_edu_notification_content" msgid="3578204677648432500">"Balayez vers le haut en utilisant trois doigts et maintenez. Appuyez pour apprendre d\'autres gestes."</string>
+    <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Utilisez votre clavier pour afficher toutes les applis"</string>
+    <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Appuyez sur la touche d\'action à tout moment. Appuyez pour apprendre d\'autres gestes."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-fr/tiles_states_strings.xml b/packages/SystemUI/res/values-fr/tiles_states_strings.xml
index adc9cb3..fcdd9f0 100644
--- a/packages/SystemUI/res/values-fr/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-fr/tiles_states_strings.xml
@@ -56,9 +56,6 @@
     <item msgid="5376619709702103243">"Désactivé"</item>
     <item msgid="4875147066469902392">"Activé"</item>
   </string-array>
-    <!-- no translation found for tile_states_modes:0 (7764936419245199023) -->
-    <!-- no translation found for tile_states_modes:1 (2004750556637773692) -->
-    <!-- no translation found for tile_states_modes:2 (8968530753931637871) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Indisponible"</item>
     <item msgid="5044688398303285224">"Désactivé"</item>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index 5388b09..913e589 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -129,17 +129,14 @@
     <string name="screenrecord_stop_dialog_title" msgid="8716193661764511095">"Queres deter a gravación?"</string>
     <string name="screenrecord_stop_dialog_message" msgid="6262768207331626817">"Estás gravando toda a pantalla"</string>
     <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Estás gravando <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
-    <!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
-    <skip />
+    <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Deter gravación"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Compartindo pantalla"</string>
-    <!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
-    <skip />
+    <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Queres deixar de compartir a pantalla?"</string>
     <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Estás compartindo toda a pantalla con <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Estás compartindo toda a pantalla cunha aplicación"</string>
     <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Estás compartindo <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Estás compartindo unha aplicación"</string>
-    <!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
-    <skip />
+    <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Deixar de compartir"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Emitindo pantalla"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Queres deter a emisión?"</string>
     <string name="cast_to_other_device_stop_dialog_message_entire_screen_with_device" msgid="1474703115926205251">"Estás emitindo toda a pantalla en <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
@@ -148,10 +145,8 @@
     <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="8616103075630934513">"Estás emitindo <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> nun dispositivo próximo"</string>
     <string name="cast_to_other_device_stop_dialog_message_generic_with_device" msgid="9213582497852420203">"Estás emitindo contido en <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="cast_to_other_device_stop_dialog_message_generic" msgid="4100272100480415076">"Estás emitindo contido nun dispositivo próximo"</string>
-    <!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
-    <skip />
-    <!-- no translation found for close_dialog_button (4749497706540104133) -->
-    <skip />
+    <string name="cast_to_other_device_stop_dialog_button" msgid="6420183747435521834">"Deter emisión"</string>
+    <string name="close_dialog_button" msgid="4749497706540104133">"Pechar"</string>
     <string name="issuerecord_title" msgid="286627115110121849">"Gravadora de problemas"</string>
     <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Procesando gravación problema"</string>
     <string name="issuerecord_channel_description" msgid="6142326363431474632">"Notificación de actividade en curso para unha sesión de rexistro dun problema"</string>
@@ -162,8 +157,7 @@
     <string name="issuerecord_save_error" msgid="6913040083446722726">"Produciuse un erro ao gardar a gravación do problema"</string>
     <string name="issuerecord_start_error" msgid="3402782952722871190">"Produciuse un erro ao iniciar a gravación do problema"</string>
     <string name="immersive_cling_title" msgid="8372056499315585941">"Vendo pantalla completa"</string>
-    <!-- no translation found for immersive_cling_description (2717426731830851921) -->
-    <skip />
+    <string name="immersive_cling_description" msgid="2717426731830851921">"Para saír, pasa o dedo cara abaixo desde a parte superior da pantalla"</string>
     <string name="immersive_cling_positive" msgid="3076681691468978568">"Entendido"</string>
     <string name="accessibility_back" msgid="6530104400086152611">"Volver"</string>
     <string name="accessibility_home" msgid="5430449841237966217">"Inicio"</string>
@@ -296,8 +290,7 @@
     <string name="start_dreams" msgid="9131802557946276718">"Protector pantalla"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"Non molestar"</string>
-    <!-- no translation found for quick_settings_modes_label (5407025818652750501) -->
-    <skip />
+    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Modos de prioridade"</string>
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Non hai dispositivos vinculados dispoñibles"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Toca para conectar ou desconectar un dispositivo"</string>
@@ -309,8 +302,7 @@
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Gardouse"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"desconectar"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"activar"</string>
-    <!-- no translation found for turn_on_bluetooth_auto_tomorrow (3345758139235739006) -->
-    <skip />
+    <string name="turn_on_bluetooth_auto_tomorrow" msgid="3345758139235739006">"Activar mañá automaticamente"</string>
     <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"As funcións como Quick Share e Localizar o meu dispositivo utilizan o Bluetooth"</string>
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"O Bluetooth activarase mañá á mañá"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Compartir audio"</string>
@@ -396,6 +388,9 @@
     <string name="performance" msgid="6552785217174378320">"Rendemento"</string>
     <string name="user_interface" msgid="3712869377953950887">"Interface de usuario"</string>
     <string name="thermal" msgid="6758074791325414831">"Térmico"</string>
+    <string name="custom" msgid="3337456985275158299">"Personalizada"</string>
+    <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Configuración de rastrexo personalizada"</string>
+    <string name="restore_default" msgid="5259420807486239755">"Restaurar configuración predeterminada"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Modo dunha soa man"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Dispositivos auditivos"</string>
     <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Activos"</string>
@@ -434,6 +429,17 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Abrir Configuración"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Outro dispositivo"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Activar/desactivar Visión xeral"</string>
+    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Modos de prioridade"</string>
+    <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Feito"</string>
+    <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Configuración"</string>
+    <string name="zen_mode_on" msgid="9085304934016242591">"Activado"</string>
+    <string name="zen_mode_off" msgid="1736604456618147306">"Desactivado"</string>
+    <!-- no translation found for zen_mode_set_up (7457957033034460064) -->
+    <skip />
+    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
+    <skip />
+    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
+    <skip />
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Non te molestará ningún son nin vibración, agás os procedentes de alarmas, recordatorios, eventos e os emisores de chamada especificados. Seguirás escoitando todo aquilo que decidas reproducir, mesmo a música, os vídeos e os xogos."</string>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Non te molestará ningún son nin vibración, agás os procedentes de alarmas. Seguirás escoitando todo aquilo que decidas reproducir, mesmo a música, os vídeos e os xogos."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Personalizar"</string>
@@ -472,6 +478,8 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Cargando lentamente • A carga completarase en <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Cargando • A carga completarase en <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Widgets na pantalla de bloqueo"</string>
+    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
+    <skip />
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Pasa o dedo cara á esquerda para iniciar o titorial comunitario"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Personalizar"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Pechar"</string>
@@ -500,12 +508,10 @@
     <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"colocar o widget seleccionado"</string>
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"Widgets da pantalla de bloqueo"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"Calquera pode ver os widgets na pantalla de bloqueo, mesmo coa tableta bloqueada"</string>
-    <!-- no translation found for communal_widgets_disclaimer_title (1150954395585308868) -->
-    <skip />
-    <!-- no translation found for communal_widgets_disclaimer_text (1423545475160506349) -->
-    <skip />
-    <!-- no translation found for communal_widgets_disclaimer_button (4423059765740780753) -->
-    <skip />
+    <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"anular a selección do widget"</string>
+    <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widgets da pantalla de bloqueo"</string>
+    <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Para abrir unha aplicación mediante un widget, tes que verificar a túa identidade. Ten en conta que pode velos calquera persoa, mesmo coa tableta bloqueada. Pode ser que algúns widgets non estean pensados para a túa pantalla de bloqueo, polo que talvez non sexa seguro engadilos aquí."</string>
+    <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Entendido"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Cambiar usuario"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menú despregable"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Eliminaranse todas as aplicacións e datos desta sesión."</string>
@@ -559,8 +565,8 @@
     <string name="media_projection_action_text" msgid="3634906766918186440">"Iniciar agora"</string>
     <string name="empty_shade_text" msgid="8935967157319717412">"Non hai notificacións"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"Non hai notificacións novas"</string>
-    <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"Notificacións intelix. activas"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"O dispositivo baixa o volume e reduce as ventás emerxentes na pantalla durante 2 min como máximo cando recibes moitas notificacións en pouco tempo."</string>
+    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"A opción Amainar notificacións está activada"</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Cando recibas moitas notificacións, o volume e as alertas reduciranse automaticamente durante ata dous minutos."</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Desactivar"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Desbloquea para ver máis notificacións"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"O teu pai ou nai xestiona este dispositivo"</string>
@@ -727,8 +733,7 @@
     <string name="notification_alert_title" msgid="3656229781017543655">"Configuración predeterminada"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Automática"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"Sen son nin vibración"</string>
-    <!-- no translation found for notification_conversation_summary_low (3855696451919728790) -->
-    <skip />
+    <string name="notification_conversation_summary_low" msgid="3855696451919728790">"Sen son nin vibración, pero aínda aparece na sección de conversas"</string>
     <string name="notification_channel_summary_default" msgid="777294388712200605">"Poderían facer que o dispositivo soe ou vibre en función da súa configuración"</string>
     <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Poderían facer que o dispositivo soe ou vibre en función da súa configuración. As conversas de <xliff:g id="APP_NAME">%1$s</xliff:g> móstranse en burbullas de forma predeterminada."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Fai que o sistema determine se a notificación debe emitir un son ou unha vibración"</string>
@@ -1227,7 +1232,6 @@
     <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>
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Datos móbiles"</string>
-    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Conectada"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Conectada temporalmente"</string>
     <string name="mobile_data_poor_connection" msgid="819617772268371434">"Conexión deficiente"</string>
@@ -1375,23 +1379,33 @@
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Icona de contraer"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Icona de despregar"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ou"</string>
-    <!-- no translation found for touchpad_tutorial_back_gesture_button (2746834288077265946) -->
-    <skip />
-    <!-- no translation found for touchpad_tutorial_home_gesture_button (7640544867625955304) -->
-    <skip />
-    <!-- no translation found for touchpad_tutorial_action_key_button (3220074511852927267) -->
-    <skip />
-    <!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
-    <skip />
+    <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Xesto para volver"</string>
+    <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Xesto para ir ao inicio"</string>
+    <string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Tecla de acción"</string>
+    <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Feito"</string>
     <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Moi ben!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Volver"</string>
-    <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"Para volver, pasa tres dedos cara á esquerda ou cara á dereita en calquera lugar do panel táctil."</string>
+    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
+    <skip />
+    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
+    <skip />
     <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Panel táctil que mostra tres dedos movéndose á dereita e á esquerda"</string>
     <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Pantalla do dispositivo que mostra unha animación do xesto de retroceso"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Retroiluminación do teclado"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Nivel %1$d de %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Controis domóticos"</string>
     <string name="home_controls_dream_description" msgid="4644150952104035789">"Usa os controis domóticos como protector de pantalla"</string>
-    <!-- no translation found for volume_undo_action (5815519725211877114) -->
-    <skip />
+    <string name="volume_undo_action" msgid="5815519725211877114">"Desfacer"</string>
+    <string name="back_edu_toast_content" msgid="4530314597378982956">"Para volver, pasa tres dedos cara á esquerda ou cara á dereita no panel táctil"</string>
+    <string name="home_edu_toast_content" msgid="3381071147871955415">"Para ir ao inicio, pasa tres dedos cara arriba no panel táctil"</string>
+    <string name="overview_edu_toast_content" msgid="5797030644017804518">"Para ver as aplicacións recentes, pasa tres dedos cara arriba no panel táctil e mantenos premidos"</string>
+    <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Para ver todas as aplicacións, preme a tecla de acción do teclado"</string>
+    <string name="back_edu_notification_title" msgid="5624780717751357278">"Usa o panel táctil para volver"</string>
+    <string name="back_edu_notification_content" msgid="2497557451540954068">"Pasa tres dedos cara á esquerda ou cara á dereita. Toca para obter máis información sobre os xestos."</string>
+    <string name="home_edu_notification_title" msgid="6097902076909654045">"Usa o panel táctil para volver ao inicio"</string>
+    <string name="home_edu_notification_content" msgid="6631697734535766588">"Pasa tres dedos cara arriba. Toca para obter máis información sobre os xestos."</string>
+    <string name="overview_edu_notification_title" msgid="1265824157319562406">"Usa o panel táctil para ver as aplicacións recentes"</string>
+    <string name="overview_edu_notification_content" msgid="3578204677648432500">"Pasa tres dedos cara arriba e mantenos premidos. Toca para obter máis información sobre os xestos."</string>
+    <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Usa o teclado para ver todas as aplicacións"</string>
+    <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Preme a tecla de acción cando queiras. Toca para obter máis información sobre os xestos."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-gl/tiles_states_strings.xml b/packages/SystemUI/res/values-gl/tiles_states_strings.xml
index d2c8ea0..03b934e 100644
--- a/packages/SystemUI/res/values-gl/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-gl/tiles_states_strings.xml
@@ -56,9 +56,6 @@
     <item msgid="5376619709702103243">"Non"</item>
     <item msgid="4875147066469902392">"Si"</item>
   </string-array>
-    <!-- no translation found for tile_states_modes:0 (7764936419245199023) -->
-    <!-- no translation found for tile_states_modes:1 (2004750556637773692) -->
-    <!-- no translation found for tile_states_modes:2 (8968530753931637871) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Non dispoñible"</item>
     <item msgid="5044688398303285224">"Non"</item>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index 66495f3..8fd8b3e 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -126,38 +126,25 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"જોવા માટે ટૅપ કરો"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"સ્ક્રીન રેકોર્ડિંગ સાચવવામાં ભૂલ આવી"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"સ્ક્રીનને રેકૉર્ડ કરવાનું શરૂ કરવામાં ભૂલ"</string>
-    <!-- no translation found for screenrecord_stop_dialog_title (8716193661764511095) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message (6262768207331626817) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5995770227684523244) -->
-    <skip />
+    <string name="screenrecord_stop_dialog_title" msgid="8716193661764511095">"રેકોર્ડિંગ રોકીએ?"</string>
+    <string name="screenrecord_stop_dialog_message" msgid="6262768207331626817">"તમે હાલમાં તમારી પૂર્ણ સ્ક્રીન રેકોર્ડ કરી રહ્યાં છો"</string>
+    <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"તમે હાલમાં <xliff:g id="APP_NAME">%1$s</xliff:g> રેકોર્ડ કરી રહ્યાં છો"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"રેકોર્ડિંગ રોકો"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"સ્ક્રીન શેર કરી રહ્યાં છીએ"</string>
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"સ્ક્રીન શેર કરવાનું રોકીએ?"</string>
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen_with_host_app (522823522115375414) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen (5090115386271179270) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_specific (5923772039347985172) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_generic (6681016774654578261) -->
-    <skip />
+    <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"તમે હાલમાં <xliff:g id="HOST_APP_NAME">%1$s</xliff:g> વડે તમારી પૂર્ણ સ્ક્રીન શેર કરી રહ્યાં છો"</string>
+    <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"તમે હાલમાં ઍપ વડે તમારી પૂર્ણ સ્ક્રીન શેર કરી રહ્યાં છો"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"તમે હાલમાં <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> શેર કરી રહ્યાં છો"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"તમે હાલમાં ઍપ શેર કરી રહ્યાં છો"</string>
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"શેર કરવાનું રોકો"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"સ્ક્રીન કાસ્ટ કરી રહ્યાં છીએ"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"શું કાસ્ટ કરવાનું બંધ કરીએ?"</string>
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen_with_device (1474703115926205251) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen (8419219169553867625) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app_with_device (2715934698604085519) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (8616103075630934513) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic_with_device (9213582497852420203) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic (4100272100480415076) -->
-    <skip />
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen_with_device" msgid="1474703115926205251">"તમે હાલમાં તમારી પૂર્ણ સ્ક્રીન <xliff:g id="DEVICE_NAME">%1$s</xliff:g> પર કાસ્ટ કરી રહ્યાં છો"</string>
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen" msgid="8419219169553867625">"તમે હાલમાં તમારી પૂર્ણ સ્ક્રીન નજીકના ડિવાઇસ પર કાસ્ટ કરી રહ્યાં છો"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app_with_device" msgid="2715934698604085519">"તમે હાલમાં <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>ને <xliff:g id="DEVICE_NAME">%2$s</xliff:g> પર કાસ્ટ કરી રહ્યાં છો"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="8616103075630934513">"તમે હાલમાં <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>ને નજીકના ડિવાઇસ પર કાસ્ટ કરી રહ્યાં છો"</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic_with_device" msgid="9213582497852420203">"તમે હાલમાં <xliff:g id="DEVICE_NAME">%1$s</xliff:g> પર કાસ્ટ કરી રહ્યાં છો"</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic" msgid="4100272100480415076">"તમે હાલમાં નજીકના ડિવાઇસ પર કાસ્ટ કરી રહ્યાં છો"</string>
     <string name="cast_to_other_device_stop_dialog_button" msgid="6420183747435521834">"કાસ્ટ કરવાનું રોકો"</string>
     <string name="close_dialog_button" msgid="4749497706540104133">"બંધ કરો"</string>
     <string name="issuerecord_title" msgid="286627115110121849">"સમસ્યા રેકોર્ડર"</string>
@@ -401,6 +388,9 @@
     <string name="performance" msgid="6552785217174378320">"પર્ફોર્મન્સ"</string>
     <string name="user_interface" msgid="3712869377953950887">"યૂઝર ઇન્ટરફેસ"</string>
     <string name="thermal" msgid="6758074791325414831">"થર્મલ"</string>
+    <string name="custom" msgid="3337456985275158299">"કસ્ટમ"</string>
+    <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"કસ્ટમ ટ્રેસ સેટિંગ"</string>
+    <string name="restore_default" msgid="5259420807486239755">"ડિફૉલ્ટ રિસ્ટોર કરો"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"એક-હાથે વાપરો મોડ"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"સાંભળવામાં મદદ આપતા ડિવાઇસ"</string>
     <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"સક્રિય"</string>
@@ -439,6 +429,17 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"સેટિંગ ખોલો"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"અન્ય ડિવાઇસ"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"ઝલકને ટૉગલ કરો"</string>
+    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"પ્રાધાન્યતાના મોડ"</string>
+    <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_off" msgid="1736604456618147306">"બંધ"</string>
+    <!-- no translation found for zen_mode_set_up (7457957033034460064) -->
+    <skip />
+    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
+    <skip />
+    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
+    <skip />
     <string name="zen_priority_introduction" msgid="3159291973383796646">"અલાર્મ, રિમાઇન્ડર, ઇવેન્ટ અને તમે ઉલ્લેખ કરો તે કૉલર સિવાય તમને ધ્વનિ કે વાઇબ્રેશન દ્વારા ખલેલ પહોંચાડવામાં આવશે નહીં. સંગીત, વીડિઓ અને રમતો સહિત તમે જે કંઈપણ ચલાવવાનું પસંદ કરશો તે સંભળાતું રહેશે."</string>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"અલાર્મ સિવાય તમને ધ્વનિ કે વાઇબ્રેશન દ્વારા ખલેલ પહોંચાડવામાં આવશે નહીં. સંગીત, વીડિઓ અને રમતો સહિત તમે જે કંઈપણ ચલાવવાનું પસંદ કરશો તે સંભળાતું રહેશે."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"કસ્ટમાઇઝ કરો"</string>
@@ -477,6 +478,8 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ધીમેથી ચાર્જ થઈ રહ્યું છે • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>માં ચાર્જ થઈ જશે"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ચાર્જ થઈ રહ્યું છે • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>માં પૂરું ચાર્જ થઈ જશે"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"લૉક સ્ક્રીન પર વિજેટ"</string>
+    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
+    <skip />
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"કૉમ્યુનલ ટ્યૂટૉરિઅલ શરૂ કરવા માટે ડાબે સ્વાઇપ કરો"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"કસ્ટમાઇઝ કરો"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"છોડી દો"</string>
@@ -503,10 +506,9 @@
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"વિજેટ પસંદ કરો"</string>
     <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"વિજેટ કાઢી નાખો"</string>
     <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"પસંદ કરેલું વિજેટ મૂકો"</string>
-    <!-- no translation found for communal_widget_picker_title (1953369090475731663) -->
-    <skip />
-    <!-- no translation found for communal_widget_picker_description (490515450110487871) -->
-    <skip />
+    <string name="communal_widget_picker_title" msgid="1953369090475731663">"લૉક સ્ક્રીન વિજેટ"</string>
+    <string name="communal_widget_picker_description" msgid="490515450110487871">"તમારું ટૅબ્લેટ લૉક કરેલું હોય તો પણ કોઈપણ તમારી લૉક સ્ક્રીન પર વિજેટ જોઈ શકે છે."</string>
+    <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"વિજેટ નાપસંદ કરો"</string>
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"લૉક સ્ક્રીન વિજેટ"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"વિજેટનો ઉપયોગ કરીને ઍપ ખોલવા માટે, તમારે એ ચકાસણી કરવાની જરૂર રહેશે કે આ તમે જ છો. તે ઉપરાંત, ધ્યાનમાં રાખો કે તમારું ટૅબ્લેટ લૉક કરેલું હોય તો પણ કોઈપણ વ્યક્તિ તેમને જોઈ શકે છે. અમુક વિજેટ કદાચ તમારી લૉક સ્ક્રીન માટે બનાવવામાં આવ્યા ન હોઈ શકે છે અને તેમને અહીં ઉમેરવાનું અસલામત હોઈ શકે છે."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"સમજાઈ ગયું"</string>
@@ -563,8 +565,8 @@
     <string name="media_projection_action_text" msgid="3634906766918186440">"હવે શરૂ કરો"</string>
     <string name="empty_shade_text" msgid="8935967157319717412">"કોઈ નોટિફિકેશન નથી"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"કોઈ નવું નોટિફિકેશન નથી"</string>
-    <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"અડૅપ્ટિવ નોટિફિકેશન ચાલુ છે"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"તમને ટૂંકા સમયગાળામાં ઘણાં નોટિફિકેશન મળે, ત્યારે ડિવાઇસ બે મિનિટ સુધી વૉલ્યૂમ ઓછું કરે છે અને સ્ક્રીન પરના પૉપ-અપની સંખ્યા ઓછી કરે છે."</string>
+    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"નોટિફિકેશન કૂલડાઉન ચાલુ છે"</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"જ્યારે તમને એકસાથે ઘણા બધા નોટિફિકેશન મળે ત્યારે તમારા ડિવાઇસનું વૉલ્યૂમ અને અલર્ટ ઑટોમૅટિક રીતે 2 મિનિટ જેટલા સમય માટે ઘટાડવામાં આવે છે."</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"બંધ કરો"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"જૂના નોટિફિકેશન જોવા માટે અનલૉક કરો"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"આ ડિવાઇસ તમારા માતાપિતા દ્વારા મેનેજ કરવામાં આવે છે"</string>
@@ -731,8 +733,7 @@
     <string name="notification_alert_title" msgid="3656229781017543655">"ડિફૉલ્ટ"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"ઑટોમૅટિક રીતે"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"કોઈપણ સાઉન્ડ અથવા વાઇબ્રેશન નથી"</string>
-    <!-- no translation found for notification_conversation_summary_low (3855696451919728790) -->
-    <skip />
+    <string name="notification_conversation_summary_low" msgid="3855696451919728790">"કોઈ સાઉન્ડ કે વાઇબ્રેશન ન હોવા છતાં વાતચીત વિભાગમાં જોવા મળે છે"</string>
     <string name="notification_channel_summary_default" msgid="777294388712200605">"ડિવાઇસના સેટિંગના આધારે રિંગ અથવા વાઇબ્રેટ થઈ શકે છે"</string>
     <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"ડિવાઇસના સેટિંગના આધારે રિંગ અથવા વાઇબ્રેટ થઈ શકે છે. ડિફૉલ્ટ તરીકે <xliff:g id="APP_NAME">%1$s</xliff:g> બબલની વાતચીત."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"આ નોટિફિકેશન સાઉન્ડ અથવા વાઇબ્રેટ કરી શકશે કે નહીં તે સિસ્ટમને નક્કી કરવા દો"</string>
@@ -789,8 +790,7 @@
     <string name="keyboard_key_page_up" msgid="173914303254199845">"Page Up"</string>
     <string name="keyboard_key_page_down" msgid="9035902490071829731">"Page Down"</string>
     <string name="keyboard_key_forward_del" msgid="5325501825762733459">"Delete"</string>
-    <!-- no translation found for keyboard_key_esc (6230365950511411322) -->
-    <skip />
+    <string name="keyboard_key_esc" msgid="6230365950511411322">"Esc કી"</string>
     <string name="keyboard_key_move_home" msgid="3496502501803911971">"Home"</string>
     <string name="keyboard_key_move_end" msgid="99190401463834854">"End"</string>
     <string name="keyboard_key_insert" msgid="4621692715704410493">"Insert"</string>
@@ -1232,7 +1232,6 @@
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"પ્રમાણીકરણ આવશ્યક છે. પ્રમાણિત કરવા માટે ફિંગરપ્રિન્ટ સેન્સરને ટચ કરો."</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"ફોન કૉલ ચાલુ છે"</string>
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"મોબાઇલ ડેટા"</string>
-    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"કનેક્ટ કરેલું"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"હંગામી રીતે કનેક્ટ કર્યું"</string>
     <string name="mobile_data_poor_connection" msgid="819617772268371434">"નબળું કનેક્શન"</string>
@@ -1386,7 +1385,10 @@
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"થઈ ગયું"</string>
     <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"ખૂબ સરસ કામ!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"પાછા જાઓ"</string>
-    <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"પાછા જવા માટે, ત્રણ આંગળીઓનો ઉપયોગ કરીને ટચપૅડ પર કોઈપણ જગ્યાએ ડાબે કે જમણે સ્વાઇપ કરો."</string>
+    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
+    <skip />
+    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
+    <skip />
     <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"જમણી અને ડાબી તરફ ખસી રહેલી ત્રણ આંગળીઓ બતાવતું ટચપૅડ"</string>
     <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"પાછા જવા માટેના સંકેત માટેનું ઍનિમેશન બતાવતી ડિવાઇસ સ્ક્રીન"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"કીબોર્ડની બૅકલાઇટ"</string>
@@ -1394,4 +1396,16 @@
     <string name="home_controls_dream_label" msgid="6567105701292324257">"ઘરેલું સાધનોના નિયંત્રણો"</string>
     <string name="home_controls_dream_description" msgid="4644150952104035789">"સ્ક્રીનસેવર તરીકે તમારા ઘરેલું સાધનોના નિયંત્રણો ઝડપથી ઍક્સેસ કરો"</string>
     <string name="volume_undo_action" msgid="5815519725211877114">"છેલ્લો ફેરફાર રદ કરો"</string>
+    <string name="back_edu_toast_content" msgid="4530314597378982956">"પાછા જવા માટે, ટચપૅડ પર ત્રણ આંગળી વડે ડાબે અથવા જમણે સ્વાઇપ કરો"</string>
+    <string name="home_edu_toast_content" msgid="3381071147871955415">"હોમ સ્ક્રીન પર જવા માટે, ટચપૅડ પર ત્રણ આંગળીઓ વડે ઉપર સ્વાઇપ કરો"</string>
+    <string name="overview_edu_toast_content" msgid="5797030644017804518">"તાજેતરની ઍપ જોવા માટે, ટચપૅડ પર ત્રણ આંગળીઓ વડે ઉપર સ્વાઇપ કરો અને દબાવી રાખો"</string>
+    <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"તમારી બધી ઍપ જોવા માટે, તમારા કીબોર્ડ પર ઍક્શન કી દબાવો"</string>
+    <string name="back_edu_notification_title" msgid="5624780717751357278">"પાછા જવા માટે તમારા ટચપૅડનો ઉપયોગ કરો"</string>
+    <string name="back_edu_notification_content" msgid="2497557451540954068">"ત્રણ આંગળીનો ઉપયોગ કરીને ડાબે અથવા જમણે સ્વાઇપ કરો. સંકેતો વિશે વધુ જાણવા માટે ટૅપ કરો."</string>
+    <string name="home_edu_notification_title" msgid="6097902076909654045">"હોમ સ્ક્રીન પર જવા માટે તમારા ટચપૅડનો ઉપયોગ કરો"</string>
+    <string name="home_edu_notification_content" msgid="6631697734535766588">"ત્રણ આંગળીનો ઉપયોગ કરીને ઉપર સ્વાઇપ કરો. સંકેતો વિશે વધુ જાણવા માટે ટૅપ કરો."</string>
+    <string name="overview_edu_notification_title" msgid="1265824157319562406">"તમારી તાજેતરની ઍપ જોવા માટે તમારા ટચપૅડનો ઉપયોગ કરો"</string>
+    <string name="overview_edu_notification_content" msgid="3578204677648432500">"ત્રણ આંગળીઓનો ઉપયોગ કરીને ઉપર સ્વાઇપ કરો અને દબાવી રાખો. સંકેતો વિશે વધુ જાણવા માટે ટૅપ કરો."</string>
+    <string name="all_apps_edu_notification_title" msgid="372262997265569063">"બધી ઍપ જોવા માટે તમારા કીબોર્ડનો ઉપયોગ કરો"</string>
+    <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"કોઈપણ સમયે ઍક્શન કી દબાવો. સંકેતો વિશે વધુ જાણવા માટે ટૅપ કરો."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-gu/tiles_states_strings.xml b/packages/SystemUI/res/values-gu/tiles_states_strings.xml
index 196a05b..5c4a478 100644
--- a/packages/SystemUI/res/values-gu/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-gu/tiles_states_strings.xml
@@ -56,11 +56,6 @@
     <item msgid="5376619709702103243">"બંધ છે"</item>
     <item msgid="4875147066469902392">"ચાલુ છે"</item>
   </string-array>
-  <string-array name="tile_states_modes">
-    <item msgid="7764936419245199023">"અનુપલબ્ધ"</item>
-    <item msgid="2004750556637773692">"બંધ"</item>
-    <item msgid="8968530753931637871">"ચાલુ"</item>
-  </string-array>
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"ઉપલબ્ધ નથી"</item>
     <item msgid="5044688398303285224">"બંધ છે"</item>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index 1c95672..6d96376 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -126,38 +126,25 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"देखने के लिए टैप करें"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"स्क्रीन रिकॉर्डिंग सेव करते समय गड़बड़ी हुई"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"स्क्रीन को रिकॉर्ड करने में गड़बड़ी आ रही है"</string>
-    <!-- no translation found for screenrecord_stop_dialog_title (8716193661764511095) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message (6262768207331626817) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5995770227684523244) -->
-    <skip />
+    <string name="screenrecord_stop_dialog_title" msgid="8716193661764511095">"क्या रिकॉर्डिंग बंद करनी है?"</string>
+    <string name="screenrecord_stop_dialog_message" msgid="6262768207331626817">"फ़िलहाल, पूरी स्क्रीन रिकॉर्ड की जा रही है"</string>
+    <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"फ़िलहाल, <xliff:g id="APP_NAME">%1$s</xliff:g> की रिकॉर्डिंग की जा रही है"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"रिकॉर्ड करना बंद करें"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"स्क्रीन शेयर की जा रही है"</string>
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"क्या स्क्रीन शेयरिंग बंद करनी है?"</string>
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen_with_host_app (522823522115375414) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen (5090115386271179270) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_specific (5923772039347985172) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_generic (6681016774654578261) -->
-    <skip />
+    <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"फ़िलहाल, <xliff:g id="HOST_APP_NAME">%1$s</xliff:g> पर पूरी स्क्रीन शेयर की जा रही है"</string>
+    <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"फ़िलहाल, किसी ऐप्लिकेशन पर पूरी स्क्रीन शेयर की जा रही है"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"फ़िलहाल, <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> शेयर किया जा रहा है"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"फ़िलहाल, कोई ऐप्लिकेशन शेयर किया जा रहा है"</string>
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"शेयर करना बंद करें"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"स्क्रीन कास्ट की जा रही है"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"क्या कास्टिंग बंद करनी है?"</string>
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen_with_device (1474703115926205251) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen (8419219169553867625) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app_with_device (2715934698604085519) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (8616103075630934513) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic_with_device (9213582497852420203) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic (4100272100480415076) -->
-    <skip />
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen_with_device" msgid="1474703115926205251">"फ़िलहाल, <xliff:g id="DEVICE_NAME">%1$s</xliff:g> पर पूरी स्क्रीन कास्ट की जा रही है"</string>
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen" msgid="8419219169553867625">"फ़िलहाल, आस-पास मौजूद किसी डिवाइस पर पूरी स्क्रीन कास्ट की जा रही है"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app_with_device" msgid="2715934698604085519">"फ़िलहाल, <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> को <xliff:g id="DEVICE_NAME">%2$s</xliff:g> पर कास्ट किया जा रहा है"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="8616103075630934513">"फ़िलहाल, <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> को आस-पास मौजूद किसी डिवाइस पर कास्ट किया जा रहा है"</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic_with_device" msgid="9213582497852420203">"फ़िलहाल, <xliff:g id="DEVICE_NAME">%1$s</xliff:g> पर कास्ट किया जा रहा है"</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic" msgid="4100272100480415076">"फ़िलहाल, आस-पास मौजूद किसी डिवाइस पर कास्ट किया जा रहा है"</string>
     <string name="cast_to_other_device_stop_dialog_button" msgid="6420183747435521834">"कास्ट करना बंद करें"</string>
     <string name="close_dialog_button" msgid="4749497706540104133">"बंद करें"</string>
     <string name="issuerecord_title" msgid="286627115110121849">"समस्या का डेटा सेव करने वाला टूल"</string>
@@ -331,9 +318,9 @@
     <string name="quick_settings_location_label" msgid="2621868789013389163">"जगह की जानकारी"</string>
     <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"स्क्रीन सेवर"</string>
     <string name="quick_settings_camera_label" msgid="5612076679385269339">"कैमरे का ऐक्सेस"</string>
-    <string name="quick_settings_mic_label" msgid="8392773746295266375">"माइक्रोफ़ोन का ऐक्सेस"</string>
+    <string name="quick_settings_mic_label" msgid="8392773746295266375">"माइक का ऐक्सेस"</string>
     <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"उपलब्ध है"</string>
-    <string name="quick_settings_camera_mic_blocked" msgid="4710884905006788281">"ब्लॉक किया गया है"</string>
+    <string name="quick_settings_camera_mic_blocked" msgid="4710884905006788281">"ब्लॉक है"</string>
     <string name="quick_settings_media_device_label" msgid="8034019242363789941">"मीडिया डिवाइस"</string>
     <string name="quick_settings_user_title" msgid="8673045967216204537">"उपयोगकर्ता"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"वाई-फ़ाई"</string>
@@ -401,6 +388,9 @@
     <string name="performance" msgid="6552785217174378320">"परफ़ॉर्मेंस"</string>
     <string name="user_interface" msgid="3712869377953950887">"यूज़र इंटरफ़ेस"</string>
     <string name="thermal" msgid="6758074791325414831">"थर्मल"</string>
+    <string name="custom" msgid="3337456985275158299">"कस्टम"</string>
+    <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"कस्टम ट्रेस सेटिंग"</string>
+    <string name="restore_default" msgid="5259420807486239755">"डिफ़ॉल्ट सेटिंग वापस लाएं"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"वन-हैंडेड मोड"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"कान की मशीनें"</string>
     <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"ऐक्टिव"</string>
@@ -439,6 +429,17 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"सेटिंग खोलें"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"अन्य डिवाइस"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"खास जानकारी टॉगल करें"</string>
+    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"अहम मोड"</string>
+    <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_off" msgid="1736604456618147306">"बंद है"</string>
+    <!-- no translation found for zen_mode_set_up (7457957033034460064) -->
+    <skip />
+    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
+    <skip />
+    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
+    <skip />
     <string name="zen_priority_introduction" msgid="3159291973383796646">"आपको अलार्म, रिमाइंडर, इवेंट और चुनिंदा कॉल करने वालों के अलावा किसी और तरह से (आवाज़ करके और थरथरा कर ) परेशान नहीं किया जाएगा. आप फिर भी संगीत, वीडियो और गेम सहित अपना चुना हुआ सब कुछ सुन सकते हैं."</string>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"आपको अलार्म छोड़कर दूसरी आवाज़ों और कंपनों से परेशान नहीं किया जाएगा. आपको अब भी संगीत, वीडियो और गेम सहित वह सब कुछ सुनाई देगा जो आपने चलाने के लिए चुना है."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"अपनी पसंद के मुताबिक बनाएं"</string>
@@ -477,6 +478,8 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • धीरे चार्ज हो रहा है • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> में पूरा चार्ज हो जाएगा"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • चार्ज हो रहा है • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> में पूरा चार्ज हो जाएगा"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"लॉक स्क्रीन पर विजेट"</string>
+    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
+    <skip />
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"कम्यूनिटी ट्यूटोरियल शुरू करने के लिए, बाईं ओर स्वाइप करें"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"पसंद के मुताबिक बनाएं"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"खारिज करें"</string>
@@ -503,10 +506,9 @@
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"विजेट चुनें"</string>
     <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"विजेट हटाएं"</string>
     <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"चुने गए विजेट के लिए जगह चुनें"</string>
-    <!-- no translation found for communal_widget_picker_title (1953369090475731663) -->
-    <skip />
-    <!-- no translation found for communal_widget_picker_description (490515450110487871) -->
-    <skip />
+    <string name="communal_widget_picker_title" msgid="1953369090475731663">"लॉक स्क्रीन विजेट"</string>
+    <string name="communal_widget_picker_description" msgid="490515450110487871">"टैबलेट लॉक होने के बावजूद, कोई भी व्यक्ति इसकी लॉक स्क्रीन पर विजेट देख सकता है."</string>
+    <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"विजेट से चुने हुए का निशान हटाएं"</string>
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"लॉक स्क्रीन विजेट"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"किसी विजेट से कोई ऐप्लिकेशन खोलने के लिए, आपको अपनी पहचान की पुष्टि करनी होगी. ध्यान रखें कि टैबलेट के लॉक होने पर भी कोई व्यक्ति विजेट देख सकता है. ऐसा हो सकता है कि कुछ विजेट लॉक स्क्रीन पर दिखाने के लिए न बने हों. इन्हें लॉक स्क्रीन पर जोड़ना असुरक्षित हो सकता है."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"ठीक है"</string>
@@ -563,8 +565,8 @@
     <string name="media_projection_action_text" msgid="3634906766918186440">"अभी शुरू करें"</string>
     <string name="empty_shade_text" msgid="8935967157319717412">"कोई सूचना नहीं है"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"कोई नई सूचना नहीं है"</string>
-    <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"अडैप्टिव नोटिफ़िकेशन चालू हैं"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"अब आपका डिवाइस, तुरंत कई सूचनाएं मिलने पर दो मिनट तक, इनसे होने वाली आवाज़ें कम कर सकता है और स्क्रीन पर कम पॉप-अप दिखा सकता है."</string>
+    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"नोटिफ़िकेशन कूलडाउन की सेटिंग चालू है"</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"एक साथ कई सूचनाएं मिलने पर, डिवाइस में सूचनाओं से होने वाली आवाज़ और सूचनाएं, दो मिनट के लिए अपने-आप कम हो जाएंगी."</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"बंद करें"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"पुरानी सूचाएं देखने के लिए अनलॉक करें"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"इस डिवाइस का प्रबंधन आपके अभिभावक करते हैं"</string>
@@ -731,8 +733,7 @@
     <string name="notification_alert_title" msgid="3656229781017543655">"डिफ़ॉल्ट"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"अपने-आप"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"आवाज़ या वाइब्रेशन न हो"</string>
-    <!-- no translation found for notification_conversation_summary_low (3855696451919728790) -->
-    <skip />
+    <string name="notification_conversation_summary_low" msgid="3855696451919728790">"कोई आवाज़ या वाइब्रेशन नहीं, लेकिन फिर भी बातचीत वाले सेक्शन में दिखता है"</string>
     <string name="notification_channel_summary_default" msgid="777294388712200605">"डिवाइस की सेटिंग के आधार पर, सूचना आने पर घंटी बज सकती है या वाइब्रेशन हो सकता है"</string>
     <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"डिवाइस की सेटिंग के आधार पर, सूचना आने पर घंटी बज सकती है या वाइब्रेशन हो सकता है. <xliff:g id="APP_NAME">%1$s</xliff:g> पर होने वाली बातचीत, डिफ़ॉल्ट रूप से बबल के तौर पर दिखती है."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"सिस्टम को यह तय करने की अनुमति दें कि इस सूचना के मिलने पर आवाज़ हो या वाइब्रेशन हो"</string>
@@ -789,8 +790,7 @@
     <string name="keyboard_key_page_up" msgid="173914303254199845">"Page Up"</string>
     <string name="keyboard_key_page_down" msgid="9035902490071829731">"Page Down"</string>
     <string name="keyboard_key_forward_del" msgid="5325501825762733459">"Delete"</string>
-    <!-- no translation found for keyboard_key_esc (6230365950511411322) -->
-    <skip />
+    <string name="keyboard_key_esc" msgid="6230365950511411322">"Esc"</string>
     <string name="keyboard_key_move_home" msgid="3496502501803911971">"Home"</string>
     <string name="keyboard_key_move_end" msgid="99190401463834854">"End"</string>
     <string name="keyboard_key_insert" msgid="4621692715704410493">"Insert"</string>
@@ -1232,7 +1232,6 @@
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"पुष्टि करना ज़रूरी है. पुष्टि करने के लिए, फ़िंगरप्रिंट सेंसर को छुएं."</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"फ़ोन कॉल चल रहा है"</string>
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"मोबाइल डेटा"</string>
-    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"कनेक्ट हो गया"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"इंटरनेट कनेक्शन कुछ समय के लिए है"</string>
     <string name="mobile_data_poor_connection" msgid="819617772268371434">"इंटरनेट कनेक्शन खराब है"</string>
@@ -1386,7 +1385,10 @@
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"हो गया"</string>
     <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"बहुत बढ़िया!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"वापस जाएं"</string>
-    <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"वापस जाने के लिए, टचपैड पर कहीं भी तीन उंगलियों से बाईं या दाईं तरफ़ स्वाइप करें."</string>
+    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
+    <skip />
+    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
+    <skip />
     <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"टचपैड पर तीन उंगलियों को दाईं और बाईं तरफ़ ले जाया जा रहा है"</string>
     <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"डिवाइस की स्क्रीन पर, पिछले पेज पर जाने के लिए हाथ के जेस्चर का ऐनिमेशन दिख रहा है"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"कीबोर्ड की बैकलाइट"</string>
@@ -1394,4 +1396,16 @@
     <string name="home_controls_dream_label" msgid="6567105701292324257">"होम कंट्रोल"</string>
     <string name="home_controls_dream_description" msgid="4644150952104035789">"स्क्रीनसेवर से तुरंत होम कंट्रोल ऐक्सेस करें"</string>
     <string name="volume_undo_action" msgid="5815519725211877114">"पहले जैसा करें"</string>
+    <string name="back_edu_toast_content" msgid="4530314597378982956">"वापस जाने के लिए, अपने डिवाइस के टचपैड पर तीन उंगलियों से बाईं या दाईं ओर स्वाइप करें"</string>
+    <string name="home_edu_toast_content" msgid="3381071147871955415">"होम पर जाने के लिए, अपने डिवाइस के टचपैड पर तीन उंगलियों से ऊपर की ओर स्वाइप करें"</string>
+    <string name="overview_edu_toast_content" msgid="5797030644017804518">"हाल ही में इस्तेमाल हुए ऐप देखने के लिए, टचपैड पर तीन उंगलियों से ऊपर की ओर स्वाइप करके दबाकर रखें"</string>
+    <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"सभी ऐप्लिकेशन देखने के लिए, कीबोर्ड पर ऐक्शन बटन दबाएं"</string>
+    <string name="back_edu_notification_title" msgid="5624780717751357278">"वापस जाने के लिए, अपने डिवाइस के टचपैड का इस्तेमाल करें"</string>
+    <string name="back_edu_notification_content" msgid="2497557451540954068">"तीन उंगलियों से बाईं या दाईं ओर स्वाइप करें. जेस्चर के बारे में ज़्यादा जानने के लिए टैप करें."</string>
+    <string name="home_edu_notification_title" msgid="6097902076909654045">"होम पर जाने के लिए, अपने डिवाइस के टचपैड का इस्तेमाल करें"</string>
+    <string name="home_edu_notification_content" msgid="6631697734535766588">"तीन उंगलियों से ऊपर की ओर स्वाइप करें. जेस्चर के बारे में ज़्यादा जानने के लिए टैप करें."</string>
+    <string name="overview_edu_notification_title" msgid="1265824157319562406">"हाल ही में इस्तेमाल हुए ऐप्लिकेशन देखने के लिए, अपने डिवाइस के टचपैड का इस्तेमाल करें"</string>
+    <string name="overview_edu_notification_content" msgid="3578204677648432500">"तीन उंगलियों से ऊपर की ओर स्वाइप करें और दबाकर रखें. जेस्चर की ज़्यादा जानकारी पाने के लिए टैप करें."</string>
+    <string name="all_apps_edu_notification_title" msgid="372262997265569063">"सभी ऐप्लिकेशन देखने के लिए, कीबोर्ड का इस्तेमाल करें"</string>
+    <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"किसी भी समय ऐक्शन बटन दबाएं. हाथ के जेस्चर के बारे में ज़्यादा जानने के लिए टैप करें."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-hi/tiles_states_strings.xml b/packages/SystemUI/res/values-hi/tiles_states_strings.xml
index b988d72..b89eeb3 100644
--- a/packages/SystemUI/res/values-hi/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-hi/tiles_states_strings.xml
@@ -56,11 +56,6 @@
     <item msgid="5376619709702103243">"बंद है"</item>
     <item msgid="4875147066469902392">"चालू है"</item>
   </string-array>
-  <string-array name="tile_states_modes">
-    <item msgid="7764936419245199023">"उपलब्ध नहीं है"</item>
-    <item msgid="2004750556637773692">"बंद है"</item>
-    <item msgid="8968530753931637871">"चालू है"</item>
-  </string-array>
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"उपलब्ध नहीं है"</item>
     <item msgid="5044688398303285224">"बंद है"</item>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index fce4ac0..1d5051c 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -290,8 +290,7 @@
     <string name="start_dreams" msgid="9131802557946276718">"Čuvar zaslona"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"Ne uznemiravaj"</string>
-    <!-- no translation found for quick_settings_modes_label (5407025818652750501) -->
-    <skip />
+    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Prioritetni načini"</string>
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Upareni uređaji nisu dostupni"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Dodirnite da biste povezali uređaj ili prekinuli vezu s njim"</string>
@@ -389,6 +388,9 @@
     <string name="performance" msgid="6552785217174378320">"Izvedba"</string>
     <string name="user_interface" msgid="3712869377953950887">"Korisničko sučelje"</string>
     <string name="thermal" msgid="6758074791325414831">"Termalno"</string>
+    <string name="custom" msgid="3337456985275158299">"Prilagođeno"</string>
+    <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Postavke prilagođenog praćenja"</string>
+    <string name="restore_default" msgid="5259420807486239755">"Vrati na zadano"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Način rada jednom rukom"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Slušna pomagala"</string>
     <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktivno"</string>
@@ -427,6 +429,17 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Otvori postavke"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Ostali uređaji"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Uključivanje/isključivanje pregleda"</string>
+    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Prioritetni načini"</string>
+    <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Gotovo"</string>
+    <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Postavke"</string>
+    <string name="zen_mode_on" msgid="9085304934016242591">"Uključeno"</string>
+    <string name="zen_mode_off" msgid="1736604456618147306">"Isključeno"</string>
+    <!-- no translation found for zen_mode_set_up (7457957033034460064) -->
+    <skip />
+    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
+    <skip />
+    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
+    <skip />
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Neće vas ometati zvukovi i vibracije, osim alarma, podsjetnika, događaja i pozivatelja koje navedete. I dalje ćete čuti sve što želite reproducirati, uključujući glazbu, videozapise i igre."</string>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Neće vas ometati zvukovi i vibracije, osim alarma. I dalje ćete čuti sve što želite reproducirati, uključujući glazbu, videozapise i igre."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Prilagodi"</string>
@@ -465,6 +478,8 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • sporo punjenje • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> do napunjenosti"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • punjenje • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> do napunjenosti"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Widgeti na zaključanom zaslonu"</string>
+    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
+    <skip />
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Prijeđite prstom ulijevo da biste pokrenuli zajednički vodič"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Prilagodi"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Odbaci"</string>
@@ -493,6 +508,7 @@
     <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"postavi odabrani widget"</string>
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"Widgeti zaključanog zaslona"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"Svi vide widgete na vašem zaključanom zaslonu, čak i ako je tablet zaključan."</string>
+    <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"poništavanje odabira widgeta"</string>
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widgeti na zaključanom zaslonu"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Da biste otvorili aplikaciju pomoću widgeta, trebate potvrditi da ste to vi. Također napominjemo da ih svatko može vidjeti, čak i ako je vaš tablet zaključan. Neki widgeti možda nisu namijenjeni za zaključani zaslon, pa ih možda nije sigurno dodati ovdje."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Shvaćam"</string>
@@ -549,8 +565,8 @@
     <string name="media_projection_action_text" msgid="3634906766918186440">"Pokreni"</string>
     <string name="empty_shade_text" msgid="8935967157319717412">"Nema obavijesti"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"Nema novih obavijesti"</string>
-    <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"Prilagodljive obavijesti uklj."</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"Uređaj sada stišava zvuk i smanjuje broj skočnih prozora na zaslonu na 2 minute kada primite jako puno obavijesti u kratkom vremenskom razdoblju."</string>
+    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Stišavanje obavijesti je uključeno"</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Glasnoća/upozorenja uređaja automatski se stišavaju do 2 min kad primite previše obavijesti odjednom"</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Isključi"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Otključajte za starije obavijesti"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Ovim uređajem upravlja tvoj roditelj"</string>
@@ -717,8 +733,7 @@
     <string name="notification_alert_title" msgid="3656229781017543655">"Zadano"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Automatski"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"Bez zvuka ili vibracije"</string>
-    <!-- no translation found for notification_conversation_summary_low (3855696451919728790) -->
-    <skip />
+    <string name="notification_conversation_summary_low" msgid="3855696451919728790">"Nema zvuka ni vibracije, no i dalje se prikazuje u odjeljku razgovora"</string>
     <string name="notification_channel_summary_default" msgid="777294388712200605">"Može zvoniti ili vibrirati ovisno o postavkama uređaja"</string>
     <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Možda će zvoniti ili vibrirati, ovisno o postavkama uređaja. Razgovori iz aplikacije <xliff:g id="APP_NAME">%1$s</xliff:g> prikazuju se u oblačiću prema zadanim postavkama."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Neka sustav odredi treba li obavijest najaviti zvukom ili vibracijom"</string>
@@ -1217,7 +1232,6 @@
     <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>
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobilni podaci"</string>
-    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Povezano"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Privremeno povezano"</string>
     <string name="mobile_data_poor_connection" msgid="819617772268371434">"Slaba veza"</string>
@@ -1358,8 +1372,7 @@
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Podijeljeni zaslon"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Unos"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Prečaci aplikacija"</string>
-    <!-- no translation found for shortcut_helper_category_current_app_shortcuts (4017840565974573628) -->
-    <skip />
+    <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Trenutačna aplikacija"</string>
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Pristupačnost"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Tipkovni prečaci"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Prečaci za pretraživanje"</string>
@@ -1372,13 +1385,27 @@
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Gotovo"</string>
     <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Sjajno!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Natrag"</string>
-    <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"Za povratak trima prstima prijeđite ulijevo ili udesno bilo gdje na dodirnoj podlozi."</string>
+    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
+    <skip />
+    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
+    <skip />
     <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Dodirna podloga prikazuje tri prsta koji se kreću udesno i ulijevo"</string>
     <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Na zaslonu uređaja prikazuje se animacija za pokret za povratak"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Pozadinsko osvjetljenje tipkovnice"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Razina %1$d od %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Upravljanje uređajima"</string>
     <string name="home_controls_dream_description" msgid="4644150952104035789">"Brzo upravljajte uređajima putem čuvara zaslona"</string>
-    <!-- no translation found for volume_undo_action (5815519725211877114) -->
-    <skip />
+    <string name="volume_undo_action" msgid="5815519725211877114">"Poništi"</string>
+    <string name="back_edu_toast_content" msgid="4530314597378982956">"Za povratak prijeđite ulijevo ili udesno trima prstima na dodirnoj podlozi"</string>
+    <string name="home_edu_toast_content" msgid="3381071147871955415">"Da biste se vratili na početni zaslon, prijeđite prema gore trima prstima na dodirnoj podlozi."</string>
+    <string name="overview_edu_toast_content" msgid="5797030644017804518">"Za prikaz nedavnih aplikacija prijeđite prema gore trima prstima i zadržite pritisak na dodirnoj podlozi"</string>
+    <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Za prikaz svojih svih aplikacija pritisnite tipku za radnju na tipkovnici"</string>
+    <string name="back_edu_notification_title" msgid="5624780717751357278">"Upotrijebite dodirnu podlogu za povratak"</string>
+    <string name="back_edu_notification_content" msgid="2497557451540954068">"Prijeđite ulijevo ili udesno trima prstima. Dodirnite da biste naučili više pokreta."</string>
+    <string name="home_edu_notification_title" msgid="6097902076909654045">"Uz pomoć dodirne podloge vratite se na početni zaslon"</string>
+    <string name="home_edu_notification_content" msgid="6631697734535766588">"Prijeđite prema gore trima prstima. Dodirnite da biste naučili više pokreta."</string>
+    <string name="overview_edu_notification_title" msgid="1265824157319562406">"Upotrijebite dodirnu podlogu za prikaz nedavnih aplikacija"</string>
+    <string name="overview_edu_notification_content" msgid="3578204677648432500">"Prijeđite prema gore trima prstima i zadržite pritisak. Dodirnite da biste naučili više pokreta."</string>
+    <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Upotrijebite tipkovnicu za prikaz svih aplikacija"</string>
+    <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Pritisnite tipku za radnju u bilo kojem trenutku. Dodirnite da biste naučili više pokreta."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-hr/tiles_states_strings.xml b/packages/SystemUI/res/values-hr/tiles_states_strings.xml
index 6833c27..df0b786 100644
--- a/packages/SystemUI/res/values-hr/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-hr/tiles_states_strings.xml
@@ -56,9 +56,6 @@
     <item msgid="5376619709702103243">"Isključeno"</item>
     <item msgid="4875147066469902392">"Uključeno"</item>
   </string-array>
-    <!-- no translation found for tile_states_modes:0 (7764936419245199023) -->
-    <!-- no translation found for tile_states_modes:1 (2004750556637773692) -->
-    <!-- no translation found for tile_states_modes:2 (8968530753931637871) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Nedostupno"</item>
     <item msgid="5044688398303285224">"Isključeno"</item>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index f59622c..0604064 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -126,38 +126,25 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"Koppintson a megtekintéshez"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"Hiba történt a képernyőrögzítés mentése során"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Hiba a képernyőrögzítés indításakor"</string>
-    <!-- no translation found for screenrecord_stop_dialog_title (8716193661764511095) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message (6262768207331626817) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5995770227684523244) -->
-    <skip />
+    <string name="screenrecord_stop_dialog_title" msgid="8716193661764511095">"Leállítja a felvételt?"</string>
+    <string name="screenrecord_stop_dialog_message" msgid="6262768207331626817">"Jelenleg a teljes képernyőről készít felvételt"</string>
+    <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Jelenleg a következőről készít felvételt: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Felvétel leállítása"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Képernyő megosztása…"</string>
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Leállítja a képernyőmegosztást?"</string>
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen_with_host_app (522823522115375414) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen (5090115386271179270) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_specific (5923772039347985172) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_generic (6681016774654578261) -->
-    <skip />
+    <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Jelenleg megosztja a teljes képernyőt a következővel: <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
+    <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Jelenleg megosztja a teljes képernyőt egy alkalmazással"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Jelenleg megosztja a következőt: <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Jelenleg megoszt egy alkalmazást"</string>
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Megosztás leállítása"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Képernyőtartalom átküldése…"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Leállítja az átküldést?"</string>
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen_with_device (1474703115926205251) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen (8419219169553867625) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app_with_device (2715934698604085519) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (8616103075630934513) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic_with_device (9213582497852420203) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic (4100272100480415076) -->
-    <skip />
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen_with_device" msgid="1474703115926205251">"Jelenleg a teljes képernyőjét átküldi a következő eszközre: <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen" msgid="8419219169553867625">"Jelenleg a teljes képernyőjét átküldi egy közeli eszközre"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app_with_device" msgid="2715934698604085519">"Jelenleg átküldi a következőt a(z) <xliff:g id="DEVICE_NAME">%2$s</xliff:g> eszközre: <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="8616103075630934513">"Jelenleg átküldi a következőt egy közeli eszközre: <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic_with_device" msgid="9213582497852420203">"Jelenleg tartalmat küld át a következő eszközre: <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic" msgid="4100272100480415076">"Jelenleg tartalmat küld át egy közeli eszközre"</string>
     <string name="cast_to_other_device_stop_dialog_button" msgid="6420183747435521834">"Átküldés leállítása"</string>
     <string name="close_dialog_button" msgid="4749497706540104133">"Bezárás"</string>
     <string name="issuerecord_title" msgid="286627115110121849">"Problémafelvevő"</string>
@@ -303,8 +290,7 @@
     <string name="start_dreams" msgid="9131802557946276718">"Képernyővédő"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"Ne zavarjanak"</string>
-    <!-- no translation found for quick_settings_modes_label (5407025818652750501) -->
-    <skip />
+    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Prioritási módok"</string>
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Nem áll rendelkezésre párosított eszköz"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Koppintson egy eszköz csatlakoztatásához vagy leválasztásához"</string>
@@ -402,6 +388,9 @@
     <string name="performance" msgid="6552785217174378320">"Teljesítmény"</string>
     <string name="user_interface" msgid="3712869377953950887">"Kezelőfelület"</string>
     <string name="thermal" msgid="6758074791325414831">"Termikus"</string>
+    <string name="custom" msgid="3337456985275158299">"Egyéni"</string>
+    <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Egyéni nyombeállítások"</string>
+    <string name="restore_default" msgid="5259420807486239755">"Alapértelmezett érték visszaállítása"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Egykezes mód"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Hallókészülékek"</string>
     <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktív"</string>
@@ -440,6 +429,17 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Beállítások megnyitása"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Más eszköz"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Áttekintés be- és kikapcsolása"</string>
+    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Prioritási módok"</string>
+    <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Kész"</string>
+    <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Beállítások"</string>
+    <string name="zen_mode_on" msgid="9085304934016242591">"Be"</string>
+    <string name="zen_mode_off" msgid="1736604456618147306">"Ki"</string>
+    <!-- no translation found for zen_mode_set_up (7457957033034460064) -->
+    <skip />
+    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
+    <skip />
+    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
+    <skip />
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Az Ön által meghatározott ébresztéseken, emlékeztetőkön, eseményeken és hívókon kívül nem fogja Önt más hang vagy rezgés megzavarni. Továbbra is lesz hangjuk azoknak a tartalmaknak, amelyeket Ön elindít, például zenék, videók és játékok."</string>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Az ébresztéseken kívül nem fogja Önt más hang és rezgés megzavarni. Továbbra is lesz hangjuk azoknak a tartalmaknak, amelyeket Ön elindít, például zenék, videók és játékok."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Személyre szabás"</string>
@@ -478,6 +478,8 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Lassú töltés • A teljes töltöttségig: <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Töltés • A teljes töltöttségig: <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Modulok a lezárási képernyőn"</string>
+    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
+    <skip />
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Csúsztasson gyorsan balra a közösségi útmutató elindításához"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Személyre szabás"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Elvetés"</string>
@@ -504,10 +506,9 @@
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"modul kiválasztása"</string>
     <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"modul törlése"</string>
     <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"kijelölt modul áthelyezése"</string>
-    <!-- no translation found for communal_widget_picker_title (1953369090475731663) -->
-    <skip />
-    <!-- no translation found for communal_widget_picker_description (490515450110487871) -->
-    <skip />
+    <string name="communal_widget_picker_title" msgid="1953369090475731663">"A lezárási képernyő moduljai"</string>
+    <string name="communal_widget_picker_description" msgid="490515450110487871">"Bárki megtekintheti a modulokat a lezárási képernyőjén, még ha a táblagépe zárolva is van."</string>
+    <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"modul kijelölésének megszüntetése"</string>
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"A lezárási képernyő moduljai"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Ha modul használatával szeretne megnyitni egy alkalmazást, igazolnia kell a személyazonosságát. Ne felejtse továbbá, hogy bárki megtekintheti a modulokat, még akkor is, amikor zárolva van a táblagép. Előfordulhat, hogy bizonyos modulokat nem a lezárási képernyőn való használatra terveztek, ezért nem biztonságos a hozzáadásuk."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Értem"</string>
@@ -564,8 +565,8 @@
     <string name="media_projection_action_text" msgid="3634906766918186440">"Indítás most"</string>
     <string name="empty_shade_text" msgid="8935967157319717412">"Nincs értesítés"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"Nincsenek új értesítések"</string>
-    <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"Alkalmazkodó értesítések bekapcsolva"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"Eszköze most legalább két percre csökkenti a hangerőt és a képernyőn előugró ablakok számát, amikor rövid időn belül sok értesítést kap."</string>
+    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Értesítések befagyasztása bekapcsolva"</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Az eszköz hangerejét és értesítéseit a rendszer automatikusan legfeljebb két percig csökkenti, ha egyszerre túl sok értesítést kap."</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Igen"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"A régebbiek feloldás után láthatók"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Az eszközt a szülőd felügyeli"</string>
@@ -732,8 +733,7 @@
     <string name="notification_alert_title" msgid="3656229781017543655">"Alapértelmezett"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Automatikus"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"Nincs hang és rezgés"</string>
-    <!-- no translation found for notification_conversation_summary_low (3855696451919728790) -->
-    <skip />
+    <string name="notification_conversation_summary_low" msgid="3855696451919728790">"Nincs hang és rezgés, de továbbra is megjelenik a beszélgetések szakaszában"</string>
     <string name="notification_channel_summary_default" msgid="777294388712200605">"Az eszközbeállítások alapján csöröghet és rezeghet"</string>
     <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Az eszközbeállítások alapján csöröghet és rezeghet. A(z) <xliff:g id="APP_NAME">%1$s</xliff:g> alkalmazásban lévő beszélgetések alapértelmezés szerint buborékban jelennek meg."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"A rendszer határozza meg, hogy ez az értesítés adjon-e ki hangot, illetve rezegjen-e"</string>
@@ -790,8 +790,7 @@
     <string name="keyboard_key_page_up" msgid="173914303254199845">"Page Up"</string>
     <string name="keyboard_key_page_down" msgid="9035902490071829731">"Page Down"</string>
     <string name="keyboard_key_forward_del" msgid="5325501825762733459">"Delete"</string>
-    <!-- no translation found for keyboard_key_esc (6230365950511411322) -->
-    <skip />
+    <string name="keyboard_key_esc" msgid="6230365950511411322">"Esc"</string>
     <string name="keyboard_key_move_home" msgid="3496502501803911971">"Kezdőképernyő"</string>
     <string name="keyboard_key_move_end" msgid="99190401463834854">"End"</string>
     <string name="keyboard_key_insert" msgid="4621692715704410493">"Insert"</string>
@@ -1233,7 +1232,6 @@
     <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>
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobiladat"</string>
-    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="NETWORKMODE">%2$s</xliff:g>/<xliff:g id="STATE">%1$s</xliff:g>"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Csatlakozva"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Ideiglenesen csatlakoztatva"</string>
     <string name="mobile_data_poor_connection" msgid="819617772268371434">"Gyenge kapcsolat"</string>
@@ -1374,8 +1372,7 @@
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Osztott képernyő"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Bevitel"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Alkalmazás-parancsikonok"</string>
-    <!-- no translation found for shortcut_helper_category_current_app_shortcuts (4017840565974573628) -->
-    <skip />
+    <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Jelenlegi alkalmazás"</string>
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Kisegítő lehetőségek"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Billentyűparancsok"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Billentyűparancsok keresése"</string>
@@ -1388,13 +1385,27 @@
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Kész"</string>
     <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Kiváló!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Vissza"</string>
-    <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"A visszalépéshez csúsztasson három ujjal gyorsan balra vagy a jobbra az érintőpad tetszőleges területén."</string>
+    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
+    <skip />
+    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
+    <skip />
     <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Három, jobbra és balra mozgó ujjat ábrázoló érintőpad"</string>
     <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"A Vissza kézmozdulat animációját megjelenítő eszközképernyő"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"A billentyűzet háttérvilágítása"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Fényerő: %2$d/%1$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Otthon vezérlése"</string>
     <string name="home_controls_dream_description" msgid="4644150952104035789">"Gyorsan vezérelheti otthonát képernyőkímélővel"</string>
-    <!-- no translation found for volume_undo_action (5815519725211877114) -->
-    <skip />
+    <string name="volume_undo_action" msgid="5815519725211877114">"Visszavonás"</string>
+    <string name="back_edu_toast_content" msgid="4530314597378982956">"A visszalépéshez csúsztasson három ujjal balra vagy a jobbra az érintőpadon."</string>
+    <string name="home_edu_toast_content" msgid="3381071147871955415">"A kezdőképernyőre való ugráshoz csúsztasson felfelé három ujjal az érintőpadon."</string>
+    <string name="overview_edu_toast_content" msgid="5797030644017804518">"A legutóbbi appokért csúsztasson lefelé három ujjal az érintőpadon, és tartsa lenyomva ujjait."</string>
+    <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Az összes alkalmazás megtekintéséhez nyomja meg a billentyűzet műveletbillentyűjét."</string>
+    <string name="back_edu_notification_title" msgid="5624780717751357278">"A visszalépéshez használja az érintőpadot"</string>
+    <string name="back_edu_notification_content" msgid="2497557451540954068">"Csúsztatasson gyorsan három ujjal balra vagy jobbra. Koppintson a további kézmozdulatokért."</string>
+    <string name="home_edu_notification_title" msgid="6097902076909654045">"A kezdőképernyő megnyitásához használja az érintőpadot"</string>
+    <string name="home_edu_notification_content" msgid="6631697734535766588">"Túsztasson felfelé három ujjal. Koppintson a további kézmozdulatokért."</string>
+    <string name="overview_edu_notification_title" msgid="1265824157319562406">"A legutóbbi alkalmazások megtekintéséhez használja az érintőpadot"</string>
+    <string name="overview_edu_notification_content" msgid="3578204677648432500">"Gyúsztason felfelé három ujjal, és tartsa lenyomva az ujjait. Koppintson a további kézmozdulatokért."</string>
+    <string name="all_apps_edu_notification_title" msgid="372262997265569063">"A billentyűzet használatával valamennyi alkalmazás megtekinthető"</string>
+    <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"A műveletbillentyű bármikor használható. Koppintson a további kézmozdulatokért."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-hu/tiles_states_strings.xml b/packages/SystemUI/res/values-hu/tiles_states_strings.xml
index 6b54c52..bbd6bc0 100644
--- a/packages/SystemUI/res/values-hu/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-hu/tiles_states_strings.xml
@@ -56,9 +56,6 @@
     <item msgid="5376619709702103243">"Ki"</item>
     <item msgid="4875147066469902392">"Be"</item>
   </string-array>
-    <!-- no translation found for tile_states_modes:0 (7764936419245199023) -->
-    <!-- no translation found for tile_states_modes:1 (2004750556637773692) -->
-    <!-- no translation found for tile_states_modes:2 (8968530753931637871) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Nem áll rendelkezésre"</item>
     <item msgid="5044688398303285224">"Ki"</item>
diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index 2be9a3c..6f32125 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -290,8 +290,7 @@
     <string name="start_dreams" msgid="9131802557946276718">"Էկրանապահ"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"Չանհանգստացնել"</string>
-    <!-- no translation found for quick_settings_modes_label (5407025818652750501) -->
-    <skip />
+    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Կարևոր ռեժիմներ"</string>
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Զուգակցված սարքեր չկան"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Հպեք՝ սարք միացնելու կամ անջատելու համար"</string>
@@ -389,6 +388,9 @@
     <string name="performance" msgid="6552785217174378320">"Արդյունավետություն"</string>
     <string name="user_interface" msgid="3712869377953950887">"Օգտատիրական ինտերֆեյս"</string>
     <string name="thermal" msgid="6758074791325414831">"Ջերմատեսիլ"</string>
+    <string name="custom" msgid="3337456985275158299">"Հատուկ"</string>
+    <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Հետագծման հատուկ կարգավորումներ"</string>
+    <string name="restore_default" msgid="5259420807486239755">"Վերականգնել կանխադրված կարգավորումները"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Մեկ ձեռքի ռեժիմ"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Լսողական սարքեր"</string>
     <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Ակտիվ է"</string>
@@ -427,6 +429,17 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Բացել կարգավորումները"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Այլ սարք"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Միացնել/անջատել համատեսքը"</string>
+    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Կարևոր ռեժիմներ"</string>
+    <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_off" msgid="1736604456618147306">"Անջատված է"</string>
+    <!-- no translation found for zen_mode_set_up (7457957033034460064) -->
+    <skip />
+    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
+    <skip />
+    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
+    <skip />
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Ձայները և թրթռոցները չեն անհանգստացնի ձեզ, բացի ձեր կողմից նշված զարթուցիչները, հիշեցումները, միջոցառումների ծանուցումները և զանգերը։ Դուք կլսեք ձեր ընտրածի նվագարկումը, այդ թվում՝ երաժշտություն, տեսանյութեր և խաղեր:"</string>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Ձայները և թրթռոցները, բացի զարթուցիչներից, չեն անհանգստացնի ձեզ: Դուք կլսեք ձեր ընտրածի նվագարկումը, այդ թվում՝ երաժշտություն, տեսանյութեր և խաղեր:"</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Հարմարեցնել"</string>
@@ -465,6 +478,8 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Դանդաղ լիցքավորում • Մնացել է <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Լիցքավորում • Մնացել է <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Վիջեթներ կողպէկրանին"</string>
+    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
+    <skip />
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Թերթեք ձախ՝ ուղեցույցը գործարկելու համար"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Անհատականացնել"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Փակել"</string>
@@ -493,6 +508,7 @@
     <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"տեղադրել ընտրված վիջեթը"</string>
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"Կողպէկրանի վիջեթներ"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"Բոլորը կարող են դիտել ձեր կողպէկրանի վիջեթները, նույնիսկ եթե պլանշետը կողպված է"</string>
+    <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"չեղարկել վիջեթի ընտրությունը"</string>
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Կողպէկրանի վիջեթներ"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Վիջեթի միջոցով հավելված բացելու համար դուք պետք է հաստատեք ձեր ինքնությունը։ Նաև նկատի ունեցեք, որ ցանկացած ոք կարող է դիտել վիջեթները, նույնիսկ երբ ձեր պլանշետը կողպված է։ Որոշ վիջեթներ կարող են նախատեսված չլինել ձեր կողպէկրանի համար, և այստեղ դրանց ավելացնելը կարող է վտանգավոր լինել։"</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Եղավ"</string>
@@ -549,8 +565,8 @@
     <string name="media_projection_action_text" msgid="3634906766918186440">"Սկսել հիմա"</string>
     <string name="empty_shade_text" msgid="8935967157319717412">"Ծանուցումներ չկան"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"Նոր ծանուցումներ չկան"</string>
-    <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"Հարմարվող ծանուցումները միացված են"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"Եթե կարճ ժամանակամիջոցում շատ ծանուցումներ ստանաք, ձայնը և ելնող պատուհանների թիվը կնվազեցվի մինչև երկու րոպեով։"</string>
+    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Ծանուցումների ձայնի իջեցումը միացված է"</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Սարքի և ծանուցումների ձայնն ավտոմատ իջեցվում է մինչև 2 րոպե, երբ շատ ծանուցումներ եք ստանում։"</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Անջատել"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Ապակողպեք՝ տեսնելու հին ծանուցումները"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Այս սարքը կառավարում է ձեր ծնողը"</string>
@@ -717,8 +733,7 @@
     <string name="notification_alert_title" msgid="3656229781017543655">"Կանխադրված"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Ավտոմատ"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"Առանց ձայնի կամ թրթռոցի"</string>
-    <!-- no translation found for notification_conversation_summary_low (3855696451919728790) -->
-    <skip />
+    <string name="notification_conversation_summary_low" msgid="3855696451919728790">"Ձայն կամ թրթռոց չկա, սակայն դեռ հայտնվում է զրույցների բաժնում"</string>
     <string name="notification_channel_summary_default" msgid="777294388712200605">"Կարող է զնգալ կամ թրթռալ՝ կախված սարքի կարգավորումներից"</string>
     <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Կարող է զնգալ կամ թրթռալ՝ կախված սարքի կարգավորումներից։ <xliff:g id="APP_NAME">%1$s</xliff:g>-ի զրույցներն ըստ կանխադրման հայտնվում են ամպիկների տեսքով։"</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Թող համակարգն ավտոմատ որոշի՝ արդյոք այս ծանուցումը ձայնով, թե թրթռոցով է պետք մատուցել"</string>
@@ -1217,7 +1232,6 @@
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Պահանջվում է նույնականացում։ Դրա համար մատը հպեք մատնահետքի սկաներին։"</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Ընթացիկ հեռախոսազանգ"</string>
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Բջջային ինտերնետ"</string>
-    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Միացած է"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Ժամանակավոր կապ"</string>
     <string name="mobile_data_poor_connection" msgid="819617772268371434">"Թույլ կապ"</string>
@@ -1358,8 +1372,7 @@
     <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>
-    <!-- no translation found for shortcut_helper_category_current_app_shortcuts (4017840565974573628) -->
-    <skip />
+    <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_search_placeholder" msgid="5488547526269871819">"Դյուրանցումների որոնում"</string>
@@ -1372,13 +1385,27 @@
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Պատրաստ է"</string>
     <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Կեցցե՛ք"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Հետ գնալ"</string>
-    <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"Հետ գնալու համար հպահարթակի վրա երեք մատով սահեցրեք ձախ կամ աջ։"</string>
+    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
+    <skip />
+    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
+    <skip />
     <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Երեք մատները աջ ու ձախ են շարժվում հպահարթակի վրա"</string>
     <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Սարքի էկրանին ցուցադրվում է շարժանկար՝ հետ գնալու ժեստի համար"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Հետին լուսավորությամբ ստեղնաշար"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"%1$d՝ %2$d-ից"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Տան կառավարման տարրեր"</string>
     <string name="home_controls_dream_description" msgid="4644150952104035789">"Տան կառավարման տարրերը դարձրեք էկրանապահ"</string>
-    <!-- no translation found for volume_undo_action (5815519725211877114) -->
-    <skip />
+    <string name="volume_undo_action" msgid="5815519725211877114">"Հետարկել"</string>
+    <string name="back_edu_toast_content" msgid="4530314597378982956">"Հետ գնալու համար հպահարթակի վրա երեք մատը սահեցրեք ձախ կամ աջ"</string>
+    <string name="home_edu_toast_content" msgid="3381071147871955415">"Հիմնական էկրան անցնելու համար հպահարթակի վրա երեք մատը սահեցրեք ձախ կամ աջ"</string>
+    <string name="overview_edu_toast_content" msgid="5797030644017804518">"Վերջերս օգտագործված հավելվածները դիտելու համար երեք մատը սահեցրեք վերև և սեղմած պահեք"</string>
+    <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Բոլոր հավելվածները դիտելու համար սեղմեք գործողության ստեղնը ստեղնաշարի վրա"</string>
+    <string name="back_edu_notification_title" msgid="5624780717751357278">"Օգտագործեք ձեր հպահարթակը՝ վերադառնալու համար"</string>
+    <string name="back_edu_notification_content" msgid="2497557451540954068">"Երեք մատը սահեցրեք ձախ կամ աջ։ Հպեք՝ ավելի շատ ժեստերի ծանոթանալու համար։"</string>
+    <string name="home_edu_notification_title" msgid="6097902076909654045">"Օգտագործեք ձեր հպահարթակը՝ հիմնական էկրան անցնելու համար"</string>
+    <string name="home_edu_notification_content" msgid="6631697734535766588">"Երեք մատը սահեցրեք վերև։ Հպեք՝ ավելի շատ ժեստերի ծանոթանալու համար։"</string>
+    <string name="overview_edu_notification_title" msgid="1265824157319562406">"Օգտագործեք ձեր հպահարթակը՝ վերջերս օգտագործված հավելվածները դիտելու համար"</string>
+    <string name="overview_edu_notification_content" msgid="3578204677648432500">"Երեք մատը սահեցրեք վերև և սեղմած պահեք։ Հպեք՝ ավելի շատ ժեստերի ծանոթանալու համար։"</string>
+    <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Օգտագործեք ձեր ստեղնաշարը՝ բոլոր հավելվածները դիտելու համար"</string>
+    <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Ցանկացած ժամանակ սեղմեք գործողության ստեղնը։ Հպեք՝ ավելի շատ ժեստերի ծանոթանալու համար։"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-hy/tiles_states_strings.xml b/packages/SystemUI/res/values-hy/tiles_states_strings.xml
index 248840e..eb77ccf 100644
--- a/packages/SystemUI/res/values-hy/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-hy/tiles_states_strings.xml
@@ -56,9 +56,6 @@
     <item msgid="5376619709702103243">"Անջատված է"</item>
     <item msgid="4875147066469902392">"Միացված է"</item>
   </string-array>
-    <!-- no translation found for tile_states_modes:0 (7764936419245199023) -->
-    <!-- no translation found for tile_states_modes:1 (2004750556637773692) -->
-    <!-- no translation found for tile_states_modes:2 (8968530753931637871) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Հասանելի չէ"</item>
     <item msgid="5044688398303285224">"Անջատված է"</item>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index 8f5ecfa..873372b 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -126,38 +126,25 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"Ketuk untuk melihat"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"Terjadi error saat menyimpan rekaman layar"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Terjadi error saat memulai perekaman layar"</string>
-    <!-- no translation found for screenrecord_stop_dialog_title (8716193661764511095) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message (6262768207331626817) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5995770227684523244) -->
-    <skip />
+    <string name="screenrecord_stop_dialog_title" msgid="8716193661764511095">"Berhenti merekam?"</string>
+    <string name="screenrecord_stop_dialog_message" msgid="6262768207331626817">"Anda sedang merekam seluruh layar"</string>
+    <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Anda sedang merekam <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Berhenti merekam"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Membagikan layar"</string>
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Hentikan berbagi layar?"</string>
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen_with_host_app (522823522115375414) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen (5090115386271179270) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_specific (5923772039347985172) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_generic (6681016774654578261) -->
-    <skip />
+    <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Anda sedang berbagi seluruh layar dengan <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
+    <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Anda sedang berbagi seluruh layar dengan aplikasi"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Anda sedang berbagi <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Anda sedang berbagi aplikasi"</string>
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Berhenti berbagi"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Mentransmisikan layar"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Hentikan transmisi?"</string>
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen_with_device (1474703115926205251) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen (8419219169553867625) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app_with_device (2715934698604085519) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (8616103075630934513) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic_with_device (9213582497852420203) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic (4100272100480415076) -->
-    <skip />
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen_with_device" msgid="1474703115926205251">"Anda sedang mentransmisikan seluruh layar ke <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen" msgid="8419219169553867625">"Anda sedang mentransmisikan seluruh layar ke perangkat di sekitar"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app_with_device" msgid="2715934698604085519">"Anda sedang mentransmisikan <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> ke <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="8616103075630934513">"Anda sedang mentransmisikan <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> ke perangkat di sekitar"</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic_with_device" msgid="9213582497852420203">"Anda sedang mentransmisikan ke <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic" msgid="4100272100480415076">"Anda sedang mentransmisikan ke perangkat di sekitar"</string>
     <string name="cast_to_other_device_stop_dialog_button" msgid="6420183747435521834">"Hentikan transmisi"</string>
     <string name="close_dialog_button" msgid="4749497706540104133">"Tutup"</string>
     <string name="issuerecord_title" msgid="286627115110121849">"Perekam Masalah"</string>
@@ -303,8 +290,7 @@
     <string name="start_dreams" msgid="9131802557946276718">"Screensaver"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"Jangan Ganggu"</string>
-    <!-- no translation found for quick_settings_modes_label (5407025818652750501) -->
-    <skip />
+    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Mode prioritas"</string>
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Perangkat yang disandingkan tak tersedia"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Ketuk untuk memulai atau menghentikan koneksi perangkat"</string>
@@ -402,6 +388,9 @@
     <string name="performance" msgid="6552785217174378320">"Performa"</string>
     <string name="user_interface" msgid="3712869377953950887">"Antarmuka Pengguna"</string>
     <string name="thermal" msgid="6758074791325414831">"Termal"</string>
+    <string name="custom" msgid="3337456985275158299">"Kustom"</string>
+    <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Setelan Rekaman Aktivitas Kustom"</string>
+    <string name="restore_default" msgid="5259420807486239755">"Pulihkan Default"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Mode satu tangan"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Alat bantu dengar"</string>
     <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktif"</string>
@@ -440,6 +429,17 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Buka Setelan"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Perangkat lainnya"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Aktifkan Ringkasan"</string>
+    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Mode prioritas"</string>
+    <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Selesai"</string>
+    <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Setelan"</string>
+    <string name="zen_mode_on" msgid="9085304934016242591">"Aktif"</string>
+    <string name="zen_mode_off" msgid="1736604456618147306">"Nonaktif"</string>
+    <!-- no translation found for zen_mode_set_up (7457957033034460064) -->
+    <skip />
+    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
+    <skip />
+    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
+    <skip />
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Anda tidak akan terganggu oleh suara dan getaran, kecuali dari alarm, pengingat, acara, dan penelepon yang Anda tentukan. Anda akan tetap mendengar apa pun yang telah dipilih untuk diputar, termasuk musik, video, dan game."</string>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Anda tidak akan terganggu oleh suara dan getaran, kecuali dari alarm. Anda akan tetap mendengar apa pun yang telah dipilih untuk diputar, termasuk musik, video, dan game."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Sesuaikan"</string>
@@ -478,6 +478,8 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Mengisi daya dengan lambat • Penuh dalam waktu <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Mengisi daya • Penuh dalam waktu <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Widget di layar kunci"</string>
+    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
+    <skip />
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Geser ke kiri untuk memulai tutorial komunal"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Sesuaikan"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Tutup"</string>
@@ -504,10 +506,9 @@
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"pilih widget"</string>
     <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"hapus widget"</string>
     <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"letakkan widget yang dipilih"</string>
-    <!-- no translation found for communal_widget_picker_title (1953369090475731663) -->
-    <skip />
-    <!-- no translation found for communal_widget_picker_description (490515450110487871) -->
-    <skip />
+    <string name="communal_widget_picker_title" msgid="1953369090475731663">"Widget layar kunci"</string>
+    <string name="communal_widget_picker_description" msgid="490515450110487871">"Siapa saja dapat melihat widget di layar kunci Anda, meskipun tablet terkunci."</string>
+    <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"batalkan pilihan widget"</string>
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widget layar kunci"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Untuk membuka aplikasi menggunakan widget, Anda perlu memverifikasi diri Anda. Selain itu, harap ingat bahwa siapa saja dapat melihatnya, bahkan saat tablet Anda terkunci. Beberapa widget mungkin tidak dirancang untuk layar kunci Anda dan mungkin tidak aman untuk ditambahkan di sini."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Oke"</string>
@@ -564,8 +565,8 @@
     <string name="media_projection_action_text" msgid="3634906766918186440">"Mulai sekarang"</string>
     <string name="empty_shade_text" msgid="8935967157319717412">"Tidak ada notifikasi"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"Tidak ada notifikasi baru"</string>
-    <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"Notifikasi adaptif aktif"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"Perangkat Anda kini akan menurunkan volume dan mengurangi jendela pop-up di layar hingga dua menit saat Anda menerima banyak notifikasi dalam waktu singkat."</string>
+    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Pengurangan suara dan getaran notifikasi aktif"</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Saat Anda menerima terlalu banyak notifikasi sekaligus, volume dan notifikasi perangkat akan otomatis dikurangi hingga selama 2 menit."</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Nonaktifkan"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Buka kunci untuk melihat notifikasi lama"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Perangkat ini dikelola oleh orang tuamu"</string>
@@ -732,8 +733,7 @@
     <string name="notification_alert_title" msgid="3656229781017543655">"Default"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Otomatis"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"Tidak ada suara atau getaran"</string>
-    <!-- no translation found for notification_conversation_summary_low (3855696451919728790) -->
-    <skip />
+    <string name="notification_conversation_summary_low" msgid="3855696451919728790">"Tanpa suara atau getaran tetapi tetap muncul di bagian percakapan"</string>
     <string name="notification_channel_summary_default" msgid="777294388712200605">"Dapat berdering atau bergetar berdasarkan setelan perangkat"</string>
     <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Dapat berdering atau bergetar berdasarkan setelan perangkat. Percakapan <xliff:g id="APP_NAME">%1$s</xliff:g> ditampilkan sebagai balon secara default."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Biarkan sistem menentukan apakah notifikasi ini akan berbunyi atau bergetar"</string>
@@ -790,8 +790,7 @@
     <string name="keyboard_key_page_up" msgid="173914303254199845">"Page Up"</string>
     <string name="keyboard_key_page_down" msgid="9035902490071829731">"Page Down"</string>
     <string name="keyboard_key_forward_del" msgid="5325501825762733459">"Delete"</string>
-    <!-- no translation found for keyboard_key_esc (6230365950511411322) -->
-    <skip />
+    <string name="keyboard_key_esc" msgid="6230365950511411322">"Esc"</string>
     <string name="keyboard_key_move_home" msgid="3496502501803911971">"Home"</string>
     <string name="keyboard_key_move_end" msgid="99190401463834854">"End"</string>
     <string name="keyboard_key_insert" msgid="4621692715704410493">"Insert"</string>
@@ -1233,7 +1232,6 @@
     <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>
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Data seluler"</string>
-    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Terhubung"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Terhubung sementara"</string>
     <string name="mobile_data_poor_connection" msgid="819617772268371434">"Koneksi buruk"</string>
@@ -1374,8 +1372,7 @@
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Layar terpisah"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Input"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Pintasan aplikasi"</string>
-    <!-- no translation found for shortcut_helper_category_current_app_shortcuts (4017840565974573628) -->
-    <skip />
+    <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_search_placeholder" msgid="5488547526269871819">"Pintasan penelusuran"</string>
@@ -1388,13 +1385,27 @@
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Selesai"</string>
     <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Bagus!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Kembali"</string>
-    <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"Untuk kembali, geser ke kiri atau ke kanan menggunakan tiga jari ke mana saja di touchpad."</string>
+    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
+    <skip />
+    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
+    <skip />
     <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Touchpad menampilkan tiga jari yang bergerak ke kanan dan ke kiri"</string>
     <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Layar perangkat menampilkan animasi untuk gestur kembali"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Lampu latar keyboard"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Tingkat %1$d dari %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Kontrol Rumah"</string>
     <string name="home_controls_dream_description" msgid="4644150952104035789">"Akses cepat kontrol rumah Anda sebagai screensaver"</string>
-    <!-- no translation found for volume_undo_action (5815519725211877114) -->
-    <skip />
+    <string name="volume_undo_action" msgid="5815519725211877114">"Urungkan"</string>
+    <string name="back_edu_toast_content" msgid="4530314597378982956">"Untuk kembali, geser ke kiri atau kanan menggunakan tiga jari di touchpad"</string>
+    <string name="home_edu_toast_content" msgid="3381071147871955415">"Untuk membuka layar utama, geser ke atas menggunakan tiga jari di touchpad"</string>
+    <string name="overview_edu_toast_content" msgid="5797030644017804518">"Untuk melihat aplikasi terkini, geser ke atas dan tahan menggunakan tiga jari di touchpad"</string>
+    <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Untuk melihat semua aplikasi, tekan tombol tindakan di keyboard"</string>
+    <string name="back_edu_notification_title" msgid="5624780717751357278">"Gunakan touchpad untuk kembali"</string>
+    <string name="back_edu_notification_content" msgid="2497557451540954068">"Geser ke kiri atau kanan menggunakan tiga jari. Ketuk untuk mempelajari gestur lainnya."</string>
+    <string name="home_edu_notification_title" msgid="6097902076909654045">"Gunakan touchpad untuk membuka layar utama"</string>
+    <string name="home_edu_notification_content" msgid="6631697734535766588">"Geser ke atas menggunakan tiga jari. Ketuk untuk mempelajari gestur lainnya."</string>
+    <string name="overview_edu_notification_title" msgid="1265824157319562406">"Gunakan touchpad untuk melihat aplikasi terkini"</string>
+    <string name="overview_edu_notification_content" msgid="3578204677648432500">"Geser ke atas dan tahan menggunakan tiga jari. Ketuk untuk mempelajari gestur lainnya."</string>
+    <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Gunakan keyboard untuk melihat semua aplikasi"</string>
+    <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Tekan tombol tindakan kapan saja. Ketuk untuk mempelajari gestur lainnya."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-in/tiles_states_strings.xml b/packages/SystemUI/res/values-in/tiles_states_strings.xml
index de505ca..a415f64 100644
--- a/packages/SystemUI/res/values-in/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-in/tiles_states_strings.xml
@@ -56,9 +56,6 @@
     <item msgid="5376619709702103243">"Nonaktif"</item>
     <item msgid="4875147066469902392">"Aktif"</item>
   </string-array>
-    <!-- no translation found for tile_states_modes:0 (7764936419245199023) -->
-    <!-- no translation found for tile_states_modes:1 (2004750556637773692) -->
-    <!-- no translation found for tile_states_modes:2 (8968530753931637871) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Tidak tersedia"</item>
     <item msgid="5044688398303285224">"Mati"</item>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index c47475b..50145b0 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -126,38 +126,25 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"Ýttu til að skoða"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"Villa við að vista skjáupptöku"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Villa við að hefja upptöku skjás"</string>
-    <!-- no translation found for screenrecord_stop_dialog_title (8716193661764511095) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message (6262768207331626817) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5995770227684523244) -->
-    <skip />
+    <string name="screenrecord_stop_dialog_title" msgid="8716193661764511095">"Stöðva upptöku?"</string>
+    <string name="screenrecord_stop_dialog_message" msgid="6262768207331626817">"Þú ert að taka upp á öllum skjánum"</string>
+    <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Þú ert að taka upp <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Stöðva upptöku"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Deilir skjá"</string>
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Hætta að deila skjá?"</string>
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen_with_host_app (522823522115375414) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen (5090115386271179270) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_specific (5923772039347985172) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_generic (6681016774654578261) -->
-    <skip />
+    <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Þú ert að deila öllum skjánum með <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
+    <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Þú ert að deila öllum skjánum með forriti"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Þú ert að deila <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Þú ert að deila forriti"</string>
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Hætta að deila"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Varpar skjá"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Hætta að varpa?"</string>
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen_with_device (1474703115926205251) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen (8419219169553867625) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app_with_device (2715934698604085519) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (8616103075630934513) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic_with_device (9213582497852420203) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic (4100272100480415076) -->
-    <skip />
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen_with_device" msgid="1474703115926205251">"Þú ert að varpa öllum skjánum yfir í <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen" msgid="8419219169553867625">"Þú ert að varpa öllum skjánum yfir í nálægt tæki"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app_with_device" msgid="2715934698604085519">"Þú ert að varpa <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> yfir í <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="8616103075630934513">"Þú ert að varpa <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> yfir í nálægt tæki"</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic_with_device" msgid="9213582497852420203">"Þú ert að varpa yfir í <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic" msgid="4100272100480415076">"Þú ert að varpa yfir í nálægt tæki"</string>
     <string name="cast_to_other_device_stop_dialog_button" msgid="6420183747435521834">"Hætta að varpa"</string>
     <string name="close_dialog_button" msgid="4749497706540104133">"Loka"</string>
     <string name="issuerecord_title" msgid="286627115110121849">"Upptökutæki fyrir vandamál"</string>
@@ -303,8 +290,7 @@
     <string name="start_dreams" msgid="9131802557946276718">"Skjávari"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"Ónáðið ekki"</string>
-    <!-- no translation found for quick_settings_modes_label (5407025818652750501) -->
-    <skip />
+    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Forgangsstillingar"</string>
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Engin pöruð tæki til staðar"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Ýttu til að tengja eða aftengja tæki"</string>
@@ -402,6 +388,9 @@
     <string name="performance" msgid="6552785217174378320">"Afköst"</string>
     <string name="user_interface" msgid="3712869377953950887">"Notandaviðmót"</string>
     <string name="thermal" msgid="6758074791325414831">"Varmi"</string>
+    <string name="custom" msgid="3337456985275158299">"Sérsnið"</string>
+    <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Sérsniðnar rakningarstillingar"</string>
+    <string name="restore_default" msgid="5259420807486239755">"Endurheimta sjálfgildi"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Einhent stilling"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Heyrnartæki"</string>
     <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Virk"</string>
@@ -440,6 +429,17 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Opna stillingar"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Annað tæki"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Kveikja/slökkva á yfirliti"</string>
+    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Forgangsstillingar"</string>
+    <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Lokið"</string>
+    <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Stillingar"</string>
+    <string name="zen_mode_on" msgid="9085304934016242591">"Kveikt"</string>
+    <string name="zen_mode_off" msgid="1736604456618147306">"Slökkt"</string>
+    <!-- no translation found for zen_mode_set_up (7457957033034460064) -->
+    <skip />
+    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
+    <skip />
+    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
+    <skip />
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Þú verður ekki fyrir truflunum frá hljóðmerkjum og titringi, fyrir utan vekjara, áminningar, viðburði og símtöl frá þeim sem þú leyfir fyrirfram. Þú heyrir áfram í öllu sem þú velur að spila, svo sem tónlist, myndskeiðum og leikjum."</string>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Þú verður ekki fyrir truflunum frá hljóðmerkjum og titringi, fyrir utan vekjara. Þú heyrir áfram í öllu sem þú velur að spila, svo sem tónlist, myndskeiðum og leikjum."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Sérsníða"</string>
@@ -478,6 +478,8 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Hæg hleðsla • Full hleðsla eftir <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Í hleðslu • Full hleðsla eftir <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Græjur á lásskjá"</string>
+    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
+    <skip />
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Strjúktu til vinstri til að hefja samfélagsleiðsögnina"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Sérsníða"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Hunsa"</string>
@@ -504,10 +506,9 @@
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"velja græju"</string>
     <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"fjarlægja græju"</string>
     <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"koma valinni græju fyrir"</string>
-    <!-- no translation found for communal_widget_picker_title (1953369090475731663) -->
-    <skip />
-    <!-- no translation found for communal_widget_picker_description (490515450110487871) -->
-    <skip />
+    <string name="communal_widget_picker_title" msgid="1953369090475731663">"Græjur á lásskjá"</string>
+    <string name="communal_widget_picker_description" msgid="490515450110487871">"Hver sem er getur séð græjur á lásskjánum þínum, jafnvel þótt spjaldtölvan sé læst."</string>
+    <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"afturkalla val á græju"</string>
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Græjur fyrir lásskjá"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Þú þarft að staðfesta að þetta sért þú til að geta opnað forrit með græju. Hafðu einnig í huga að hver sem er getur skoðað þær, jafnvel þótt spjaldtölvan sé læst. Sumar græjur eru hugsanlega ekki ætlaðar fyrir lásskjá og því gæti verið óöruggt að bæta þeim við hér."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Ég skil"</string>
@@ -564,8 +565,8 @@
     <string name="media_projection_action_text" msgid="3634906766918186440">"Byrja núna"</string>
     <string name="empty_shade_text" msgid="8935967157319717412">"Engar tilkynningar"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"Engar nýjar tilkynningar"</string>
-    <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"Kveikt á breytil. tilkynningum"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"Tækið þitt lækkar nú hljóðstyrkinn og fækkar sprettigluggum á skjánum í allt að tvær mínútur þegar þú færð margar tilkynningar á stuttum tíma."</string>
+    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Kveikt er á tilkynningadempun"</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Lækkað er sjálfkrafa í hljóðstyrk og áminningum tækisins í allt að tvær mínútur þegar þú færð of margar tilkynningar í einu."</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Slökkva"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Taktu úr lás til að sjá eldri tilkynningar"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Foreldri þitt stjórnar þessu tæki"</string>
@@ -732,8 +733,7 @@
     <string name="notification_alert_title" msgid="3656229781017543655">"Sjálfgefið"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Sjálfvirk"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"Ekkert hljóð eða titringur"</string>
-    <!-- no translation found for notification_conversation_summary_low (3855696451919728790) -->
-    <skip />
+    <string name="notification_conversation_summary_low" msgid="3855696451919728790">"Ekkert hljóð eða titringur en birtist samt sem áður í samtalshlutanum"</string>
     <string name="notification_channel_summary_default" msgid="777294388712200605">"Gæti hringt eða titrað en það fer eftir stillingum tækisins"</string>
     <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Gæti hringt eða titrað en það fer eftir stillingum tækisins. Samtöl frá <xliff:g id="APP_NAME">%1$s</xliff:g> birtast sjálfkrafa í blöðru."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Láta kerfið ákvarða hvort hljóð eða titringur fylgir þessari tilkynningu"</string>
@@ -790,8 +790,7 @@
     <string name="keyboard_key_page_up" msgid="173914303254199845">"Page Up"</string>
     <string name="keyboard_key_page_down" msgid="9035902490071829731">"Page Down"</string>
     <string name="keyboard_key_forward_del" msgid="5325501825762733459">"Delete"</string>
-    <!-- no translation found for keyboard_key_esc (6230365950511411322) -->
-    <skip />
+    <string name="keyboard_key_esc" msgid="6230365950511411322">"Esc-lykill"</string>
     <string name="keyboard_key_move_home" msgid="3496502501803911971">"Home"</string>
     <string name="keyboard_key_move_end" msgid="99190401463834854">"End"</string>
     <string name="keyboard_key_insert" msgid="4621692715704410493">"Insert"</string>
@@ -1233,7 +1232,6 @@
     <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>
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Farsímagögn"</string>
-    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Tengt"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Tímabundin tenging"</string>
     <string name="mobile_data_poor_connection" msgid="819617772268371434">"Léleg tenging"</string>
@@ -1374,8 +1372,7 @@
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Skjáskipting"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Inntak"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Flýtileiðir forrita"</string>
-    <!-- no translation found for shortcut_helper_category_current_app_shortcuts (4017840565974573628) -->
-    <skip />
+    <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Núverandi forrit"</string>
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Aðgengi"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Flýtilyklar"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Leitarflýtileiðir"</string>
@@ -1388,13 +1385,27 @@
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Lokið"</string>
     <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Vel gert!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Til baka"</string>
-    <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"Strjúktu til vinstri eða hægri með þrem fingrum hvar sem er á snertifletinum til að fara til baka."</string>
+    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
+    <skip />
+    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
+    <skip />
     <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Snertiflötur sem sýnir þrjá fingur færast til hægri og vinstri"</string>
     <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Skjár tækis sem sýnir hreyfimynd fyrir bendinguna „til baka“."</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Baklýsing lyklaborðs"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Stig %1$d af %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Heimastýringar"</string>
     <string name="home_controls_dream_description" msgid="4644150952104035789">"Fáðu skjótan aðgang að heimastýringum sem skjávara"</string>
-    <!-- no translation found for volume_undo_action (5815519725211877114) -->
-    <skip />
+    <string name="volume_undo_action" msgid="5815519725211877114">"Afturkalla"</string>
+    <string name="back_edu_toast_content" msgid="4530314597378982956">"Strjúktu til vinstri eða hægri með þremur fingrum á snertifletinum til að fara til baka"</string>
+    <string name="home_edu_toast_content" msgid="3381071147871955415">"Strjúktu upp með þremur fingrum á snertifletinum til að fara á heimaskjá"</string>
+    <string name="overview_edu_toast_content" msgid="5797030644017804518">"Strjúktu upp og haltu með þremur fingrum á snertifletinum til að sjá nýleg forrit"</string>
+    <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Ýttu á aðgerðalykilinn á lyklaborðinu til að sjá öll forritin þín"</string>
+    <string name="back_edu_notification_title" msgid="5624780717751357278">"Notaðu snertiflötinn til að fara til baka"</string>
+    <string name="back_edu_notification_content" msgid="2497557451540954068">"Strjúktu til vinstri eða hægri með þremur fingrum. Ýttu til að læra fleiri bendingar."</string>
+    <string name="home_edu_notification_title" msgid="6097902076909654045">"Notaðu snertiflötinn til að fara á heimaskjá"</string>
+    <string name="home_edu_notification_content" msgid="6631697734535766588">"Strjúktu upp með þremur fingrum. Ýttu til að læra fleiri bendingar."</string>
+    <string name="overview_edu_notification_title" msgid="1265824157319562406">"Notaðu snertiflötinn til að sjá nýleg forrit"</string>
+    <string name="overview_edu_notification_content" msgid="3578204677648432500">"Strjúktu upp og haltu með þremur fingrum. Ýttu til að læra fleiri bendingar."</string>
+    <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Notaðu lyklaborðið til að sjá öll forrit"</string>
+    <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Ýttu hvenær sem er á aðgerðalykilinn. Ýttu til að læra fleiri bendingar."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-is/tiles_states_strings.xml b/packages/SystemUI/res/values-is/tiles_states_strings.xml
index ee3b311..c9befd6 100644
--- a/packages/SystemUI/res/values-is/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-is/tiles_states_strings.xml
@@ -56,9 +56,6 @@
     <item msgid="5376619709702103243">"Slökkt"</item>
     <item msgid="4875147066469902392">"Kveikt"</item>
   </string-array>
-    <!-- no translation found for tile_states_modes:0 (7764936419245199023) -->
-    <!-- no translation found for tile_states_modes:1 (2004750556637773692) -->
-    <!-- no translation found for tile_states_modes:2 (8968530753931637871) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Ekki í boði"</item>
     <item msgid="5044688398303285224">"Slökkt"</item>
diff --git a/packages/SystemUI/res/values-it-feminine/strings.xml b/packages/SystemUI/res/values-it-feminine/strings.xml
new file mode 100644
index 0000000..99b9361
--- /dev/null
+++ b/packages/SystemUI/res/values-it-feminine/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="zen_priority_introduction" msgid="3159291973383796646">"Non verrai disturbata da suoni e vibrazioni, ad eccezione di sveglie, promemoria, eventi, chiamate da contatti da te specificati ed elementi che hai scelto di continuare a riprodurre, inclusi video, musica e giochi."</string>
+    <string name="zen_alarms_introduction" msgid="3987266042682300470">"Non verrai disturbata da suoni e vibrazioni, ad eccezione delle sveglie. Potrai comunque sentire qualunque cosa decidi di riprodurre, inclusi video, musica e giochi."</string>
+    <string name="empty_user_name" msgid="3389155775773578300">"Amiche"</string>
+</resources>
diff --git a/packages/SystemUI/res/values-it-masculine/strings.xml b/packages/SystemUI/res/values-it-masculine/strings.xml
new file mode 100644
index 0000000..5e78889
--- /dev/null
+++ b/packages/SystemUI/res/values-it-masculine/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="zen_priority_introduction" msgid="3159291973383796646">"Non verrai disturbato da suoni e vibrazioni, ad eccezione di sveglie, promemoria, eventi, chiamate da contatti da te specificati ed elementi che hai scelto di continuare a riprodurre, inclusi video, musica e giochi."</string>
+    <string name="zen_alarms_introduction" msgid="3987266042682300470">"Non verrai disturbato da suoni e vibrazioni, ad eccezione delle sveglie. Potrai comunque sentire qualunque cosa decidi di riprodurre, inclusi video, musica e giochi."</string>
+    <string name="empty_user_name" msgid="3389155775773578300">"Amici"</string>
+</resources>
diff --git a/packages/SystemUI/res/values-it-neuter/strings.xml b/packages/SystemUI/res/values-it-neuter/strings.xml
new file mode 100644
index 0000000..0e1c227
--- /dev/null
+++ b/packages/SystemUI/res/values-it-neuter/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="zen_priority_introduction" msgid="3159291973383796646">"Non verrai disturbatə da suoni e vibrazioni, ad eccezione di sveglie, promemoria, eventi, chiamate da contatti da te specificati ed elementi che hai scelto di continuare a riprodurre, inclusi video, musica e giochi."</string>
+    <string name="zen_alarms_introduction" msgid="3987266042682300470">"Non verrai disturbatə da suoni e vibrazioni, ad eccezione delle sveglie. Potrai comunque sentire qualunque cosa decidi di riprodurre, inclusi video, musica e giochi."</string>
+    <string name="empty_user_name" msgid="3389155775773578300">"Amicɜ"</string>
+</resources>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index 1cfc8d4..502678e 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -126,38 +126,25 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"Tocca per visualizzare"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"Errore durante il salvataggio della registrazione dello schermo"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Errore durante l\'avvio della registrazione dello schermo"</string>
-    <!-- no translation found for screenrecord_stop_dialog_title (8716193661764511095) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message (6262768207331626817) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5995770227684523244) -->
-    <skip />
+    <string name="screenrecord_stop_dialog_title" msgid="8716193661764511095">"Vuoi interrompere la registrazione?"</string>
+    <string name="screenrecord_stop_dialog_message" msgid="6262768207331626817">"Al momento stai registrando l\'intero schermo"</string>
+    <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Al momento stai registrando <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Interrompi registrazione"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Condivisione dello schermo in corso"</string>
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Vuoi interrompere la condivisione dello schermo?"</string>
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen_with_host_app (522823522115375414) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen (5090115386271179270) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_specific (5923772039347985172) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_generic (6681016774654578261) -->
-    <skip />
+    <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Al momento stai condividendo l\'intero schermo con <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
+    <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Al momento stai condividendo l\'intero schermo con un\'app"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Al momento stai condividendo <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Al momento stai condividendo un\'app"</string>
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Interrompi condivisione"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Trasmissione dello schermo in corso…"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Vuoi interrompere la trasmissione?"</string>
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen_with_device (1474703115926205251) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen (8419219169553867625) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app_with_device (2715934698604085519) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (8616103075630934513) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic_with_device (9213582497852420203) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic (4100272100480415076) -->
-    <skip />
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen_with_device" msgid="1474703115926205251">"Stai trasmettendo l\'intero schermo su <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen" msgid="8419219169553867625">"Al momento stai trasmettendo l\'intero schermo su un dispositivo nelle vicinanze"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app_with_device" msgid="2715934698604085519">"Al momento stai trasmettendo <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> su <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="8616103075630934513">"Al momento stai trasmettendo <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> su un dispositivo nelle vicinanze"</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic_with_device" msgid="9213582497852420203">"Al momento stai trasmettendo su <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic" msgid="4100272100480415076">"Al momento stai trasmettendo su un dispositivo nelle vicinanze"</string>
     <string name="cast_to_other_device_stop_dialog_button" msgid="6420183747435521834">"Interrompi trasmissione"</string>
     <string name="close_dialog_button" msgid="4749497706540104133">"Chiudi"</string>
     <string name="issuerecord_title" msgid="286627115110121849">"Registratore dei problemi"</string>
@@ -232,13 +219,13 @@
     <string name="fingerprint_re_enroll_notification_title" msgid="4539432429683916604">"Riconfigura lo Sblocco con l\'Impronta"</string>
     <string name="fingerprint_re_enroll_notification_name" msgid="630798657797645704">"Sblocco con l\'Impronta"</string>
     <string name="fingerprint_re_enroll_dialog_title" msgid="3526033128113925780">"Configura lo Sblocco con l\'Impronta"</string>
-    <string name="fingerprint_re_enroll_dialog_content" msgid="4866561176695984879">"Per riconfigurare lo Sblocco con l\'Impronta, i modelli e le immagini dell\'impronta correnti verranno eliminati.\n\nDopo la cancellazione, dovrai riconfigurare la funzionalità per usare l\'impronta per sbloccare il telefono o verificare la tua identità."</string>
-    <string name="fingerprint_re_enroll_dialog_content_singular" msgid="3083663339787381218">"Per riconfigurare lo Sblocco con l\'Impronta, il modello e le immagini dell\'impronta correnti verranno eliminati.\n\nDopo la cancellazione, dovrai riconfigurare la funzionalità per usare l\'impronta per sbloccare il telefono o verificare la tua identità."</string>
+    <string name="fingerprint_re_enroll_dialog_content" msgid="4866561176695984879">"Per riconfigurare lo Sblocco con l\'Impronta, i modelli e le immagini dell\'impronta correnti verranno eliminati.\n\nDopo la cancellazione, dovrai riconfigurare la funzionalità per usare l\'impronta per sbloccare lo smartphone o verificare la tua identità."</string>
+    <string name="fingerprint_re_enroll_dialog_content_singular" msgid="3083663339787381218">"Per riconfigurare lo Sblocco con l\'Impronta, il modello e le immagini dell\'impronta correnti verranno eliminati.\n\nDopo la cancellazione, dovrai riconfigurare la funzionalità per usare l\'impronta per sbloccare lo smartphone o verificare la tua identità."</string>
     <string name="fingerprint_reenroll_failure_dialog_content" msgid="4733768492747300666">"Impossibile configurare lo Sblocco con l\'Impronta. Vai alle Impostazioni e riprova."</string>
     <string name="face_re_enroll_notification_title" msgid="1850838867718410520">"Riconfigura lo Sblocco con il Volto"</string>
     <string name="face_re_enroll_notification_name" msgid="7384545252206120659">"Sblocco con il Volto"</string>
     <string name="face_re_enroll_dialog_title" msgid="6392173708176069994">"Configura lo Sblocco con il Volto"</string>
-    <string name="face_re_enroll_dialog_content" msgid="7353502359464038511">"Per riconfigurare lo Sblocco con il Volto, l\'attuale modello del volto verrà eliminato.\n\nDovrai riconfigurare questa funzionalità per usare il volto per sbloccare il telefono."</string>
+    <string name="face_re_enroll_dialog_content" msgid="7353502359464038511">"Per riconfigurare lo Sblocco con il Volto, l\'attuale modello del volto verrà eliminato.\n\nDovrai riconfigurare questa funzionalità per usare il volto per sbloccare lo smartphone."</string>
     <string name="face_reenroll_failure_dialog_content" msgid="7073947334397236935">"Impossibile configurare lo Sblocco con il Volto. Vai alle Impostazioni e riprova."</string>
     <string name="fingerprint_dialog_touch_sensor" msgid="2817887108047658975">"Tocca il sensore di impronte"</string>
     <string name="fingerprint_dialog_authenticated_confirmation" msgid="1603899612957562862">"Premi l\'icona Sblocca per continuare"</string>
@@ -262,8 +249,10 @@
     <string name="accessibility_airplane_mode" msgid="1899529214045998505">"Modalità aereo."</string>
     <string name="accessibility_vpn_on" msgid="8037549696057288731">"VPN attiva."</string>
     <string name="accessibility_battery_level" msgid="5143715405241138822">"Batteria: <xliff:g id="NUMBER">%d</xliff:g> percento."</string>
-    <string name="accessibility_battery_level_with_estimate" msgid="6548654589315074529">"Batteria a <xliff:g id="PERCENTAGE">%1$d</xliff:g> per cento, <xliff:g id="TIME">%2$s</xliff:g>"</string>
-    <string name="accessibility_battery_level_charging" msgid="8892191177774027364">"Batteria in carica, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g>%%."</string>
+    <string name="accessibility_battery_level_with_estimate" msgid="6548654589315074529">"Batteria rimanente: <xliff:g id="PERCENTAGE">%1$d</xliff:g> per cento, <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <!-- String.format failed for translation -->
+    <!-- no translation found for accessibility_battery_level_charging (8892191177774027364) -->
+    <skip />
     <string name="accessibility_battery_level_charging_paused" msgid="3560711496775146763">"Batteria a <xliff:g id="PERCENTAGE">%d</xliff:g> per cento; ricarica in pausa per proteggere la batteria."</string>
     <string name="accessibility_battery_level_charging_paused_with_estimate" msgid="2223541217743647858">"Batteria a <xliff:g id="PERCENTAGE">%1$d</xliff:g> per cento, <xliff:g id="TIME">%2$s</xliff:g>; ricarica in pausa per proteggere la batteria."</string>
     <string name="accessibility_overflow_action" msgid="8555835828182509104">"Visualizza tutte le notifiche"</string>
@@ -296,15 +285,14 @@
     <string name="accessibility_sensors_off_active" msgid="2619725434618911551">"Opzione Sensori disattivati attiva"</string>
     <string name="accessibility_clear_all" msgid="970525598287244592">"Cancella tutte le notifiche."</string>
     <string name="notification_group_overflow_indicator" msgid="7605120293801012648">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
-    <string name="notification_group_overflow_description" msgid="7176322877233433278">"{count,plural, =1{# altra notifica nel gruppo.}many{Altre # notifiche nel gruppo.}other{Altre # notifiche nel gruppo.}}"</string>
+    <string name="notification_group_overflow_description" msgid="7176322877233433278">"{count,plural, =1{# altra notifica nel gruppo.}many{Altre # di notifiche nel gruppo.}other{Altre # notifiche nel gruppo.}}"</string>
     <string name="accessibility_rotation_lock_on_landscape" msgid="936972553861524360">"Lo schermo è bloccato in orientamento orizzontale."</string>
     <string name="accessibility_rotation_lock_on_portrait" msgid="2356633398683813837">"Lo schermo è bloccato in orientamento verticale."</string>
     <string name="dessert_case" msgid="9104973640704357717">"Vetrina di dolci"</string>
     <string name="start_dreams" msgid="9131802557946276718">"Salvaschermo"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"Non disturbare"</string>
-    <!-- no translation found for quick_settings_modes_label (5407025818652750501) -->
-    <skip />
+    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Modalità priorità"</string>
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Nessun dispositivo accoppiato disponibile"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Tocca per connettere o disconnettere un dispositivo"</string>
@@ -398,10 +386,13 @@
     <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Segnalazione di bug"</string>
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Quali problemi ha l\'esperienza del dispositivo?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Seleziona il tipo di problema"</string>
-    <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Regis. dello schermo"</string>
+    <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Registrazione schermo"</string>
     <string name="performance" msgid="6552785217174378320">"Prestazioni"</string>
     <string name="user_interface" msgid="3712869377953950887">"Interfaccia utente"</string>
     <string name="thermal" msgid="6758074791325414831">"Termico"</string>
+    <string name="custom" msgid="3337456985275158299">"Personalizzate"</string>
+    <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Impostazioni monitoraggio personalizzate"</string>
+    <string name="restore_default" msgid="5259420807486239755">"Ripristina predefinite"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Modalità a una mano"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Apparecchi acustici"</string>
     <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Attivi"</string>
@@ -440,8 +431,16 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Apri Impostazioni"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Altro dispositivo"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Attiva/disattiva la panoramica"</string>
-    <string name="zen_priority_introduction" msgid="3159291973383796646">"Non verrai disturbato da suoni e vibrazioni, ad eccezione di sveglie, promemoria, eventi, chiamate da contatti da te specificati ed elementi che hai scelto di continuare a riprodurre, inclusi video, musica e giochi."</string>
-    <string name="zen_alarms_introduction" msgid="3987266042682300470">"Non verrai disturbato da suoni e vibrazioni, ad eccezione delle sveglie e degli elementi che hai scelto di continuare a riprodurre, inclusi video, musica e giochi."</string>
+    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Modalità priorità"</string>
+    <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Fine"</string>
+    <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Impostazioni"</string>
+    <string name="zen_mode_on" msgid="9085304934016242591">"On"</string>
+    <string name="zen_mode_off" msgid="1736604456618147306">"Off"</string>
+    <string name="zen_mode_set_up" msgid="7457957033034460064">"Configura"</string>
+    <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Gestisci nelle impostazioni"</string>
+    <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Nessuna modalità attiva}=1{{mode} è attiva}many{# di modalità sono attive}other{# modalità sono attive}}"</string>
+    <string name="zen_priority_introduction" msgid="3159291973383796646">"Suoni e vibrazioni non ti disturberanno, ad eccezione di sveglie, promemoria, eventi, chiamate da contatti da te specificati ed elementi che hai scelto di continuare a riprodurre, inclusi video, musica e giochi."</string>
+    <string name="zen_alarms_introduction" msgid="3987266042682300470">"Non riceverai distrazioni da suoni e vibrazioni, ad eccezione delle sveglie. Potrai comunque sentire qualunque cosa decidi di riprodurre, inclusi video, musica e giochi."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Personalizza"</string>
     <string name="zen_silence_introduction_voice" msgid="853573681302712348">"Verranno bloccati TUTTI i suoni e le vibrazioni, anche di sveglie, musica, video e giochi. Potrai ancora telefonare."</string>
     <string name="zen_silence_introduction" msgid="6117517737057344014">"Verranno bloccati TUTTI i suoni e le vibrazioni, anche di sveglie, musica, video e giochi."</string>
@@ -478,6 +477,8 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Ricarica lenta • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> alla ricarica completa"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • In carica • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> alla ricarica completa"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Widget su schermata di blocco"</string>
+    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
+    <skip />
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Scorri a sinistra per iniziare il tutorial della community"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Personalizza"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Chiudi"</string>
@@ -486,7 +487,7 @@
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Premi a lungo per personalizzare i widget"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Personalizza widget"</string>
     <string name="unlock_reason_to_customize_widgets" msgid="5011909432460546033">"Sblocca per personalizzare i widget"</string>
-    <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"Icona dell\'app per widget disattivati"</string>
+    <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"Icona dell\'app per widget disattivato"</string>
     <string name="icon_description_for_pending_widget" msgid="8413816401868001755">"Icona dell\'app per un widget in fase di installazione"</string>
     <string name="edit_widget" msgid="9030848101135393954">"Modifica widget"</string>
     <string name="button_to_remove_widget" msgid="3948204829181214098">"Rimuovi"</string>
@@ -504,10 +505,9 @@
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"seleziona widget"</string>
     <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"rimuovi widget"</string>
     <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"posiziona il widget selezionato"</string>
-    <!-- no translation found for communal_widget_picker_title (1953369090475731663) -->
-    <skip />
-    <!-- no translation found for communal_widget_picker_description (490515450110487871) -->
-    <skip />
+    <string name="communal_widget_picker_title" msgid="1953369090475731663">"Widget della schermata di blocco"</string>
+    <string name="communal_widget_picker_description" msgid="490515450110487871">"Chiunque può visualizzare i widget sulla tua schermata di blocco, anche se il tablet è bloccato."</string>
+    <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"deseleziona widget"</string>
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widget della schermata di blocco"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Per aprire un\'app utilizzando un widget, dovrai verificare la tua identità. Inoltre tieni presente che chiunque può vederlo, anche quando il tablet è bloccato. Alcuni widget potrebbero non essere stati progettati per la schermata di blocco e potrebbe non essere sicuro aggiungerli qui."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Ok"</string>
@@ -522,7 +522,7 @@
     <string name="guest_notification_session_active" msgid="5567273684713471450">"Sei in modalità Ospite"</string>
     <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Se aggiungi un nuovo utente, la modalità Ospite viene disattivata e vengono eliminati tutti i dati e le app della sessione Ospite corrente."</string>
     <string name="user_limit_reached_title" msgid="2429229448830346057">"Limite di utenti raggiunto"</string>
-    <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{Può essere creato un solo utente.}many{Puoi aggiungere fino a # utenti.}other{Puoi aggiungere fino a # utenti.}}"</string>
+    <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{Può essere creato un solo utente.}many{Puoi aggiungere fino a # di utenti.}other{Puoi aggiungere fino a # utenti.}}"</string>
     <string name="user_remove_user_title" msgid="9124124694835811874">"Rimuovere l\'utente?"</string>
     <string name="user_remove_user_message" msgid="6702834122128031833">"Tutte le app e i dati di questo utente verranno eliminati."</string>
     <string name="user_remove_user_remove" msgid="8387386066949061256">"Rimuovi"</string>
@@ -564,8 +564,8 @@
     <string name="media_projection_action_text" msgid="3634906766918186440">"Avvia adesso"</string>
     <string name="empty_shade_text" msgid="8935967157319717412">"Nessuna notifica"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"Nessuna nuova notifica"</string>
-    <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"Notifiche adattive attivate"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"Il dispositivo abbassa il volume e riduce i popup fino a 2 minuti quando ricevi molte notifiche."</string>
+    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Attenuazione delle notifiche attivata"</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Volume e avvisi sono ridotti automaticamente fino a 2 minuti quando ricevi troppe notifiche insieme."</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Disattiva"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Sblocca per vedere le notifiche meno recenti"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Questo dispositivo è gestito dai tuoi genitori"</string>
@@ -601,9 +601,9 @@
     <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Sul dispositivo è installata un\'autorità di certificazione. Il tuo traffico di rete protetto potrebbe essere monitorato o modificato."</string>
     <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"L\'amministratore ha attivato i log di rete, che consentono di monitorare il traffico sul dispositivo."</string>
     <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"L\'amministratore ha attivato i log di rete, che consentono di monitorare il traffico nel profilo di lavoro, ma non nel profilo personale."</string>
-    <string name="monitoring_description_named_vpn" msgid="8220190039787149671">"Questo dispositivo è connesso a internet tramite <xliff:g id="VPN_APP">%1$s</xliff:g>. La tua attività di rete, inclusi email e dati di navigazione, è visibile al provider VPN."</string>
-    <string name="monitoring_description_managed_device_named_vpn" msgid="7693648349547785255">"Questo dispositivo è connesso a internet tramite <xliff:g id="VPN_APP">%1$s</xliff:g>. La tua attività di rete, inclusi email e dati di navigazione, è visibile all\'amministratore IT."</string>
-    <string name="monitoring_description_two_named_vpns" msgid="6726394451199620634">"Questo dispositivo si connette a Internet tramite <xliff:g id="VPN_APP_0">%1$s</xliff:g> e <xliff:g id="VPN_APP_1">%2$s</xliff:g>. La tua attività di rete, inclusi email e dati di navigazione, è visibile all\'amministratore IT."</string>
+    <string name="monitoring_description_named_vpn" msgid="8220190039787149671">"Questo dispositivo si connette a internet tramite <xliff:g id="VPN_APP">%1$s</xliff:g>. La tua attività di rete, inclusi email e dati di navigazione, è visibile al provider VPN."</string>
+    <string name="monitoring_description_managed_device_named_vpn" msgid="7693648349547785255">"Questo dispositivo si connette a internet tramite <xliff:g id="VPN_APP">%1$s</xliff:g>. La tua attività di rete, inclusi email e dati di navigazione, è visibile all\'amministratore IT."</string>
+    <string name="monitoring_description_two_named_vpns" msgid="6726394451199620634">"Questo dispositivo si connette a internet tramite <xliff:g id="VPN_APP_0">%1$s</xliff:g> e <xliff:g id="VPN_APP_1">%2$s</xliff:g>. La tua attività di rete, inclusi email e dati di navigazione, è visibile all\'amministratore IT."</string>
     <string name="monitoring_description_managed_profile_named_vpn" msgid="7254359257263069766">"Le tue app di lavoro si connettono a Internet tramite <xliff:g id="VPN_APP">%1$s</xliff:g>. La tua attività di rete nelle app di lavoro, inclusi email e dati di navigazione, è visibile all\'amministratore IT e al provider VPN."</string>
     <string name="monitoring_description_personal_profile_named_vpn" msgid="5083909710727365452">"Le tue app personali si connettono a Internet tramite <xliff:g id="VPN_APP">%1$s</xliff:g>. La tua attività di rete, inclusi email e dati di navigazione, è visibile al provider VPN."</string>
     <string name="monitoring_description_vpn_settings_separator" msgid="8292589617720435430">" "</string>
@@ -732,8 +732,7 @@
     <string name="notification_alert_title" msgid="3656229781017543655">"Modalità predefinita"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Automatico"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"Nessun suono o vibrazione"</string>
-    <!-- no translation found for notification_conversation_summary_low (3855696451919728790) -->
-    <skip />
+    <string name="notification_conversation_summary_low" msgid="3855696451919728790">"Nessun suono o vibrazione, ma appare ancora nella sezione delle conversazioni"</string>
     <string name="notification_channel_summary_default" msgid="777294388712200605">"Potrebbero essere attivati lo squillo o la vibrazione in base alle impostazioni del dispositivo"</string>
     <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Potrebbero essere attivati lo squillo o la vibrazione in base alle impostazioni del dispositivo. Conversazioni dalla bolla <xliff:g id="APP_NAME">%1$s</xliff:g> per impostaz. predefinita."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Fai stabilire al sistema se questa notifica deve emettere suoni o vibrazioni"</string>
@@ -771,8 +770,8 @@
     <string name="snooze_undo" msgid="2738844148845992103">"Annulla"</string>
     <string name="snooze_undo_content_description" msgid="2711656788917580801">"Annulla ripetizione notifica"</string>
     <string name="snoozed_for_time" msgid="7586689374860469469">"Posticipato di <xliff:g id="TIME_AMOUNT">%1$s</xliff:g>"</string>
-    <string name="snoozeHourOptions" msgid="2332819756222425558">"{count,plural, =1{# ora}=2{# ore}many{# ore}other{# ore}}"</string>
-    <string name="snoozeMinuteOptions" msgid="2222082405822030979">"{count,plural, =1{# minuto}many{# minuti}other{# minuti}}"</string>
+    <string name="snoozeHourOptions" msgid="2332819756222425558">"{count,plural, =1{# ora}=2{# ore}many{# di ore}other{# ore}}"</string>
+    <string name="snoozeMinuteOptions" msgid="2222082405822030979">"{count,plural, =1{# minuto}many{# di minuti}other{# minuti}}"</string>
     <string name="battery_detail_switch_title" msgid="6940976502957380405">"Risparmio energetico"</string>
     <string name="keyboard_key_button_template" msgid="8005673627272051429">"Pulsante <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="keyboard_key_home" msgid="3734400625170020657">"Home page"</string>
@@ -790,8 +789,7 @@
     <string name="keyboard_key_page_up" msgid="173914303254199845">"Pagina su"</string>
     <string name="keyboard_key_page_down" msgid="9035902490071829731">"Pagina giù"</string>
     <string name="keyboard_key_forward_del" msgid="5325501825762733459">"Elimina"</string>
-    <!-- no translation found for keyboard_key_esc (6230365950511411322) -->
-    <skip />
+    <string name="keyboard_key_esc" msgid="6230365950511411322">"Esc"</string>
     <string name="keyboard_key_move_home" msgid="3496502501803911971">"Home page"</string>
     <string name="keyboard_key_move_end" msgid="99190401463834854">"Fine"</string>
     <string name="keyboard_key_insert" msgid="4621692715704410493">"INS"</string>
@@ -1059,7 +1057,7 @@
     <string name="accessibility_floating_button_hidden_notification_title" msgid="4115036997406994799">"Pulsante Accessibilità nascosto"</string>
     <string name="accessibility_floating_button_hidden_notification_text" msgid="1457021647040915658">"Tocca per mostrare il pulsante Accessibilità"</string>
     <string name="accessibility_floating_button_undo_message_label_text" msgid="9017658016426242640">"Scorciatoia <xliff:g id="FEATURE_NAME">%s</xliff:g> rimossa"</string>
-    <string name="accessibility_floating_button_undo_message_number_text" msgid="4909270290725226075">"{count,plural, =1{# scorciatoia rimossa}many{# scorciatoie rimosse}other{# scorciatoie rimosse}}"</string>
+    <string name="accessibility_floating_button_undo_message_number_text" msgid="4909270290725226075">"{count,plural, =1{# scorciatoia rimossa}many{# di scorciatoie rimosse}other{# scorciatoie rimosse}}"</string>
     <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Sposta in alto a sinistra"</string>
     <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Sposta in alto a destra"</string>
     <string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Sposta in basso a sinistra"</string>
@@ -1209,7 +1207,7 @@
     <string name="video_status" msgid="4548544654316843225">"Visione in corso"</string>
     <string name="audio_status" msgid="4237055636967709208">"Ascolto in corso"</string>
     <string name="game_status" msgid="1340694320630973259">"Gioco in corso"</string>
-    <string name="empty_user_name" msgid="3389155775773578300">"Amici"</string>
+    <string name="empty_user_name" msgid="3389155775773578300">"Persone amiche"</string>
     <string name="empty_status" msgid="5938893404951307749">"Chattiamo stasera."</string>
     <string name="status_before_loading" msgid="1500477307859631381">"I contenuti verranno mostrati a breve"</string>
     <string name="missed_call" msgid="4228016077700161689">"Chiamata persa"</string>
@@ -1233,7 +1231,6 @@
     <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>
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Dati mobili"</string>
-    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Connessa"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Connessa temporaneamente"</string>
     <string name="mobile_data_poor_connection" msgid="819617772268371434">"Connessione debole"</string>
@@ -1255,7 +1252,7 @@
     <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Aggiungi riquadro"</string>
     <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Non aggiungerlo"</string>
     <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Seleziona utente"</string>
-    <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# app è attiva}many{# di app sono attive}other{# app sono attive}}"</string>
+    <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{C\'è # app attiva}many{Ci sono # di app attive}other{Ci sono # app attive}}"</string>
     <string name="fgs_dot_content_description" msgid="2865071539464777240">"Nuove informazioni"</string>
     <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"App attive"</string>
     <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Queste app sono attive e in esecuzione, anche quando non le utilizzi. Questo migliora la loro funzionalità, ma influisce sulla durata della batteria."</string>
@@ -1285,7 +1282,7 @@
     <string name="dream_overlay_status_bar_camera_off" msgid="5273073778969890823">"La fotocamera è disattivata"</string>
     <string name="dream_overlay_status_bar_mic_off" msgid="8366534415013819396">"Il microfono è disattivato"</string>
     <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Fotocamera e microfono non attivi"</string>
-    <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# notifica}many{# notifiche}other{# notifiche}}"</string>
+    <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# notifica}many{# di notifiche}other{# notifiche}}"</string>
     <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string>
     <string name="note_task_button_label" msgid="230135078402003532">"Aggiunta di note"</string>
     <string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Aggiunta di note, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string>
@@ -1387,13 +1384,27 @@
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Fine"</string>
     <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Ottimo lavoro"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Indietro"</string>
-    <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"Per tornare indietro, scorri verso sinistra o verso destra utilizzando tre dita in un punto qualsiasi del touchpad."</string>
+    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
+    <skip />
+    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
+    <skip />
     <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Touchpad che mostra tre dita che si muovono verso destra e sinistra"</string>
     <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Schermata del dispositivo che mostra l\'animazione del gesto per tornare indietro"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Retroilluminazione della tastiera"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Livello %1$d di %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Controlli della casa"</string>
     <string name="home_controls_dream_description" msgid="4644150952104035789">"Accedi rapidamente ai controlli della casa dal salvaschermo"</string>
-    <!-- no translation found for volume_undo_action (5815519725211877114) -->
-    <skip />
+    <string name="volume_undo_action" msgid="5815519725211877114">"Annulla"</string>
+    <string name="back_edu_toast_content" msgid="4530314597378982956">"Per tornare indietro, scorri verso sinistra o verso destra con tre dita sul touchpad."</string>
+    <string name="home_edu_toast_content" msgid="3381071147871955415">"Per andare alla schermata Home, scorri verso l\'alto con tre dita sul touchpad"</string>
+    <string name="overview_edu_toast_content" msgid="5797030644017804518">"Per visualizzare le app recenti, scorri verso l\'alto e tieni premuto con tre dita sul touchpad"</string>
+    <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Per visualizzare tutte le tue app, premi il tasto azione sulla tastiera"</string>
+    <string name="back_edu_notification_title" msgid="5624780717751357278">"Usa il touchpad per tornare indietro"</string>
+    <string name="back_edu_notification_content" msgid="2497557451540954068">"Scorri verso sinistra o destra con tre dita. Tocca per scoprire altri gesti."</string>
+    <string name="home_edu_notification_title" msgid="6097902076909654045">"Usa il touchpad per andare alla schermata Home"</string>
+    <string name="home_edu_notification_content" msgid="6631697734535766588">"Scorri verso l\'altro con tre dita. Tocca per scoprire altri gesti."</string>
+    <string name="overview_edu_notification_title" msgid="1265824157319562406">"Usa il touchpad per visualizzare le app recenti"</string>
+    <string name="overview_edu_notification_content" msgid="3578204677648432500">"Scorri verso l\'alto e tieni premuto con tre dita. Tocca per scoprire altri gesti."</string>
+    <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Usa la tastiera per visualizzare tutte le app"</string>
+    <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Premi il tasto azione in qualsiasi momento. Tocca per scoprire altri gesti."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-it/tiles_states_strings.xml b/packages/SystemUI/res/values-it/tiles_states_strings.xml
index e7c626f..2fd4f6d 100644
--- a/packages/SystemUI/res/values-it/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-it/tiles_states_strings.xml
@@ -56,9 +56,6 @@
     <item msgid="5376619709702103243">"Off"</item>
     <item msgid="4875147066469902392">"On"</item>
   </string-array>
-    <!-- no translation found for tile_states_modes:0 (7764936419245199023) -->
-    <!-- no translation found for tile_states_modes:1 (2004750556637773692) -->
-    <!-- no translation found for tile_states_modes:2 (8968530753931637871) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Non disponibile"</item>
     <item msgid="5044688398303285224">"Off"</item>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index 2ff5f83..698d2ad 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -126,38 +126,25 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"יש להקיש כדי להציג"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"שגיאה בשמירה של הקלטת המסך"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"שגיאה בהפעלה של הקלטת המסך"</string>
-    <!-- no translation found for screenrecord_stop_dialog_title (8716193661764511095) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message (6262768207331626817) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5995770227684523244) -->
-    <skip />
+    <string name="screenrecord_stop_dialog_title" msgid="8716193661764511095">"לעצור את ההקלטה?"</string>
+    <string name="screenrecord_stop_dialog_message" msgid="6262768207331626817">"מתבצעת כרגע הקלטה של כל המסך שלך"</string>
+    <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"מתבצעת כרגע הקלטה של <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"הפסקת ההקלטה"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"שיתוף המסך מתבצע"</string>
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"להפסיק את שיתוף המסך?"</string>
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen_with_host_app (522823522115375414) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen (5090115386271179270) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_specific (5923772039347985172) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_generic (6681016774654578261) -->
-    <skip />
+    <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"מתבצע כרגע שיתוף של כל המסך שלך עם <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
+    <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"מתבצע כרגע שיתוף של כל המסך שלך עם אפליקציה"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"מתבצע כרגע שיתוף של <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"מתבצע כרגע שיתוף של אפליקציה"</string>
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"הפסקת השיתוף"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"‏הפעלת Cast של המסך מתבצעת"</string>
-    <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"‏להפסיק את הפעלת ה-Cast?"</string>
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen_with_device (1474703115926205251) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen (8419219169553867625) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app_with_device (2715934698604085519) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (8616103075630934513) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic_with_device (9213582497852420203) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic (4100272100480415076) -->
-    <skip />
+    <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"‏להפסיק את פעולת ה-Cast?"</string>
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen_with_device" msgid="1474703115926205251">"‏מתבצעת כרגע פעולת Cast של כל המסך שלך למכשיר <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen" msgid="8419219169553867625">"‏מתבצעת כרגע פעולת Cast של כל המסך שלך למכשיר בקרבת מקום"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app_with_device" msgid="2715934698604085519">"‏מתבצעת כרגע פעולת Cast של <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> למכשיר <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="8616103075630934513">"‏מתבצעת כרגע פעולת Cast של <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> למכשיר בקרבת מקום"</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic_with_device" msgid="9213582497852420203">"‏מתבצעת כרגע פעולת Cast למכשיר <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic" msgid="4100272100480415076">"‏מתבצעת כרגע פעולת Cast למכשיר בקרבת מקום"</string>
     <string name="cast_to_other_device_stop_dialog_button" msgid="6420183747435521834">"‏הפסקת ה-Cast"</string>
     <string name="close_dialog_button" msgid="4749497706540104133">"סגירה"</string>
     <string name="issuerecord_title" msgid="286627115110121849">"בעיה במכשיר ההקלטה"</string>
@@ -303,8 +290,7 @@
     <string name="start_dreams" msgid="9131802557946276718">"שומר מסך"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"אתרנט"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"נא לא להפריע"</string>
-    <!-- no translation found for quick_settings_modes_label (5407025818652750501) -->
-    <skip />
+    <string name="quick_settings_modes_label" msgid="5407025818652750501">"מצבי עדיפות"</string>
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"אין מכשירים מותאמים זמינים"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"אפשר להקיש כדי להתחבר למכשיר או להתנתק ממנו"</string>
@@ -402,6 +388,9 @@
     <string name="performance" msgid="6552785217174378320">"ביצועים"</string>
     <string name="user_interface" msgid="3712869377953950887">"ממשק משתמש"</string>
     <string name="thermal" msgid="6758074791325414831">"תרמי"</string>
+    <string name="custom" msgid="3337456985275158299">"בהתאמה אישית"</string>
+    <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"הגדרות מעקב מותאמות אישית"</string>
+    <string name="restore_default" msgid="5259420807486239755">"שחזור של ברירת המחדל"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"מצב שימוש ביד אחת"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"מכשירי שמיעה"</string>
     <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"פעיל"</string>
@@ -440,6 +429,17 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"פתיחת ההגדרות"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"מכשיר אחר"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"החלפת מצב של מסכים אחרונים"</string>
+    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"מצבי עדיפות"</string>
+    <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_off" msgid="1736604456618147306">"מצב מושבת"</string>
+    <!-- no translation found for zen_mode_set_up (7457957033034460064) -->
+    <skip />
+    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
+    <skip />
+    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
+    <skip />
     <string name="zen_priority_introduction" msgid="3159291973383796646">"כדי לא להפריע לך, המכשיר לא ירטוט ולא ישמיע שום צליל, חוץ מהתראות, תזכורות, אירועים ושיחות ממתקשרים מסוימים לבחירתך. המצב הזה לא ישפיע על צלילים שהם חלק מתוכן שבחרת להפעיל, כמו מוזיקה, סרטונים ומשחקים."</string>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"כדי לא להפריע לך, המכשיר לא ירטוט ולא ישמיע שום צליל, חוץ מהתראות. המצב הזה לא ישפיע על צלילים שהם חלק מתוכן שבחרת להפעיל, כמו מוזיקה, סרטונים ומשחקים."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"התאמה אישית"</string>
@@ -478,6 +478,8 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • בטעינה איטית • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> עד לסיום"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • בטעינה • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> עד לסיום"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"ווידג\'טים במסך הנעילה"</string>
+    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
+    <skip />
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"אפשר להחליק שמאלה כדי להפעיל את המדריך המשותף"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"התאמה אישית"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"סגירה"</string>
@@ -504,10 +506,9 @@
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"צריך לבחור ווידג\'ט"</string>
     <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"הסרת הווידג\'ט"</string>
     <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"צריך למקם את הווידג\'ט שנבחר"</string>
-    <!-- no translation found for communal_widget_picker_title (1953369090475731663) -->
-    <skip />
-    <!-- no translation found for communal_widget_picker_description (490515450110487871) -->
-    <skip />
+    <string name="communal_widget_picker_title" msgid="1953369090475731663">"ווידג\'טים במסך הנעילה"</string>
+    <string name="communal_widget_picker_description" msgid="490515450110487871">"כולם יכולים לראות את הווידג\'טים במסך הנעילה שלך, גם אם הטאבלט נעול."</string>
+    <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"ביטול הבחירה בווידג\'ט"</string>
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"ווידג\'טים במסך הנעילה"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"כדי לפתוח אפליקציה באמצעות ווידג\'ט, עליך לאמת את זהותך. בנוסף, כדאי לזכור שכל אחד יכול לראות את הווידג\'טים גם כשהטאבלט שלך נעול. יכול להיות שחלק מהווידג\'טים לא נועדו למסך הנעילה ושלא בטוח להוסיף אותם לכאן."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"הבנתי"</string>
@@ -564,8 +565,8 @@
     <string name="media_projection_action_text" msgid="3634906766918186440">"כן, אפשר להתחיל"</string>
     <string name="empty_shade_text" msgid="8935967157319717412">"אין התראות"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"אין התראות חדשות"</string>
-    <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"ההתראות המותאמות מופעלות"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"מעכשיו, כשיש התראות רבות בזמן קצר, עוצמת הקול מופחתת ומופיעים פחות חלונות קופצים במשך עד שתי דקות."</string>
+    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"הפוגת ההתראות מופעלת"</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"עוצמת הקול וההתראות במכשיר מופחתות אוטומטית למשך עד 2 דקות כשמתקבלות יותר מדי התראות בבת אחת."</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"השבתה"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"יש לבטל את הנעילה כדי לראות התראות ישנות"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"המכשיר הזה מנוהל על ידי ההורה שלך"</string>
@@ -732,8 +733,7 @@
     <string name="notification_alert_title" msgid="3656229781017543655">"ברירת מחדל"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"באופן אוטומטי"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"ללא צליל או רטט"</string>
-    <!-- no translation found for notification_conversation_summary_low (3855696451919728790) -->
-    <skip />
+    <string name="notification_conversation_summary_low" msgid="3855696451919728790">"ללא צליל או רטט אבל עדיין מופיע בקטע השיחה"</string>
     <string name="notification_channel_summary_default" msgid="777294388712200605">"ייתכן שיופעל צלצול או רטט בהתאם להגדרות במכשיר"</string>
     <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"ייתכן שיופעל צלצול או רטט בהתאם להגדרות במכשיר. שיחות מהאפליקציה <xliff:g id="APP_NAME">%1$s</xliff:g> מופיעות בבועות כברירת מחדל."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"אפשר לתת למערכת לקבוע אם ההתראה הזאת צריכה להיות מלווה בצליל או ברטט"</string>
@@ -790,8 +790,7 @@
     <string name="keyboard_key_page_up" msgid="173914303254199845">"דפדוף למעלה"</string>
     <string name="keyboard_key_page_down" msgid="9035902490071829731">"דפדוף למטה"</string>
     <string name="keyboard_key_forward_del" msgid="5325501825762733459">"מחיקה"</string>
-    <!-- no translation found for keyboard_key_esc (6230365950511411322) -->
-    <skip />
+    <string name="keyboard_key_esc" msgid="6230365950511411322">"Esc"</string>
     <string name="keyboard_key_move_home" msgid="3496502501803911971">"דף הבית"</string>
     <string name="keyboard_key_move_end" msgid="99190401463834854">"סיום"</string>
     <string name="keyboard_key_insert" msgid="4621692715704410493">"הוספה"</string>
@@ -1233,7 +1232,6 @@
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"נדרש אימות. יש לגעת בחיישן טביעות האצבע כדי לבצע אימות."</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"מתקיימת שיחה"</string>
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"חבילת גלישה"</string>
-    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"מחובר"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"מחובר באופן זמני"</string>
     <string name="mobile_data_poor_connection" msgid="819617772268371434">"חיבור באיכות ירודה"</string>
@@ -1387,13 +1385,27 @@
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"סיום"</string>
     <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"מעולה!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"חזרה"</string>
-    <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"כדי לחזור אחורה, מחליקים שמאלה או ימינה עם שלוש אצבעות בכל מקום על לוח המגע."</string>
+    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
+    <skip />
+    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
+    <skip />
     <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"לוח מגע שמראה שלוש אצבעות זזות ימינה ושמאלה"</string>
     <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"מסך מכשיר שמראה אנימציה לתנועה אחורה"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"התאורה האחורית במקלדת"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"‏רמה %1$d מתוך %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"שליטה במכשירים"</string>
     <string name="home_controls_dream_description" msgid="4644150952104035789">"גישה מהירה לממשק השליטה במכשירים כשומר מסך"</string>
-    <!-- no translation found for volume_undo_action (5815519725211877114) -->
-    <skip />
+    <string name="volume_undo_action" msgid="5815519725211877114">"ביטול"</string>
+    <string name="back_edu_toast_content" msgid="4530314597378982956">"כדי לחזור אחורה, מחליקים שמאלה או ימינה עם שלוש אצבעות על לוח המגע."</string>
+    <string name="home_edu_toast_content" msgid="3381071147871955415">"כדי לעבור למסך הבית, מחליקים למעלה עם שלוש אצבעות על לוח המגע"</string>
+    <string name="overview_edu_toast_content" msgid="5797030644017804518">"כדי לראות את האפליקציות האחרונות, מחליקים למעלה עם שלוש אצבעות על לוח המגע ולוחצים לחיצה ארוכה"</string>
+    <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"כדי לראות את כל האפליקציות, מקישים על מקש הפעולה במקלדת"</string>
+    <string name="back_edu_notification_title" msgid="5624780717751357278">"איך להשתמש בלוח המגע כדי לחזור אחורה"</string>
+    <string name="back_edu_notification_content" msgid="2497557451540954068">"מחליקים ימינה או שמאלה עם שלוש אצבעות. ניתן להקיש כדי לקבל מידע נוסף על התנועות."</string>
+    <string name="home_edu_notification_title" msgid="6097902076909654045">"איך להשתמש בלוח המגע כדי לעבור למסך הבית"</string>
+    <string name="home_edu_notification_content" msgid="6631697734535766588">"מחליקים למעלה עם שלוש אצבעות. ניתן להקיש כדי לקבל מידע נוסף על התנועות."</string>
+    <string name="overview_edu_notification_title" msgid="1265824157319562406">"איך להשתמש בלוח המגע כדי לראות את האפליקציות האחרונות"</string>
+    <string name="overview_edu_notification_content" msgid="3578204677648432500">"מחליקים למעלה ולוחצים לחיצה ארוכה עם שלוש אצבעות. ניתן להקיש כדי לקבל מידע נוסף על התנועות."</string>
+    <string name="all_apps_edu_notification_title" msgid="372262997265569063">"איך להשתמש במקלדת כדי לראות את כל האפליקציות"</string>
+    <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"בכל שלב אפשר ללחוץ על מקש הפעולה. ניתן להקיש כדי לקבל מידע נוסף על התנועות."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-iw/tiles_states_strings.xml b/packages/SystemUI/res/values-iw/tiles_states_strings.xml
index a81cda4..b5cb476 100644
--- a/packages/SystemUI/res/values-iw/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-iw/tiles_states_strings.xml
@@ -56,9 +56,6 @@
     <item msgid="5376619709702103243">"כבוי"</item>
     <item msgid="4875147066469902392">"פועל"</item>
   </string-array>
-    <!-- no translation found for tile_states_modes:0 (7764936419245199023) -->
-    <!-- no translation found for tile_states_modes:1 (2004750556637773692) -->
-    <!-- no translation found for tile_states_modes:2 (8968530753931637871) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"לא זמין"</item>
     <item msgid="5044688398303285224">"כבוי"</item>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index 4290be9f..7e09b94 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -290,8 +290,7 @@
     <string name="start_dreams" msgid="9131802557946276718">"スクリーン セーバー"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"イーサネット"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"サイレント モード"</string>
-    <!-- no translation found for quick_settings_modes_label (5407025818652750501) -->
-    <skip />
+    <string name="quick_settings_modes_label" msgid="5407025818652750501">"優先モード"</string>
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"ペア設定されたデバイスがありません"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"タップしてデバイスを接続または接続解除します"</string>
@@ -389,6 +388,9 @@
     <string name="performance" msgid="6552785217174378320">"パフォーマンス"</string>
     <string name="user_interface" msgid="3712869377953950887">"ユーザー インターフェース"</string>
     <string name="thermal" msgid="6758074791325414831">"温度"</string>
+    <string name="custom" msgid="3337456985275158299">"カスタム"</string>
+    <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"カスタム トレース設定"</string>
+    <string name="restore_default" msgid="5259420807486239755">"デフォルトに戻す"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"片手モード"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"補聴器"</string>
     <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"アクティブ"</string>
@@ -427,6 +429,14 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"設定を開く"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"その他のデバイス"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"概要を切り替え"</string>
+    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"優先モード"</string>
+    <string name="zen_modes_dialog_done" msgid="6654130880256438950">"完了"</string>
+    <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"設定"</string>
+    <string name="zen_mode_on" msgid="9085304934016242591">"ON"</string>
+    <string name="zen_mode_off" msgid="1736604456618147306">"OFF"</string>
+    <string name="zen_mode_set_up" msgid="7457957033034460064">"設定"</string>
+    <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"設定で管理"</string>
+    <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{アクティブなモードはありません}=1{{mode} がアクティブです}other{# 個のモードがアクティブです}}"</string>
     <string name="zen_priority_introduction" msgid="3159291973383796646">"アラーム、リマインダー、予定、指定した人からの着信以外の音やバイブレーションに煩わされることはありません。音楽、動画、ゲームなど再生対象として選択したコンテンツは引き続き再生されます。"</string>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"アラーム以外の音やバイブレーションに煩わされることはありません。音楽、動画、ゲームなど再生対象として選択したコンテンツは引き続き再生されます。"</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"カスタマイズ"</string>
@@ -465,6 +475,8 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • 低速充電中 • 完了まで <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • 充電中 • フル充電まで <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"ロック画面のウィジェット"</string>
+    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
+    <skip />
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"左にスワイプすると、コミュニティ チュートリアルが開始します"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"カスタマイズ"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"閉じる"</string>
@@ -493,6 +505,7 @@
     <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"選択したウィジェットを配置"</string>
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"ロック画面ウィジェット"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"タブレットがロックされていても、ロック画面のウィジェットは誰でも確認できます。"</string>
+    <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"ウィジェットの選択を解除する"</string>
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"ロック画面ウィジェット"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"ウィジェットを使用してアプリを起動するには、本人確認が必要です。タブレットがロックされた状態でも他のユーザーにウィジェットが表示されますので、注意してください。一部のウィジェットについてはロック画面での使用を想定していないため、ロック画面への追加は危険な場合があります。"</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"OK"</string>
@@ -549,8 +562,8 @@
     <string name="media_projection_action_text" msgid="3634906766918186440">"今すぐ開始"</string>
     <string name="empty_shade_text" msgid="8935967157319717412">"通知はありません"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"新しい通知はありません"</string>
-    <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"通知の自動調整は ON です"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"短期間に多くの通知が届いたときに最長 2 分間デバイスの音量を下げて画面上のポップアップを減らします。"</string>
+    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"通知のクールダウンが ON になっています"</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"一度に多くの通知が届いたときに、最大 2 分間自動的にデバイスの音量が小さくなりアラートも減ります。"</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"OFF にする"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"ロック解除して以前の通知を表示"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"このデバイスは保護者によって管理されています"</string>
@@ -717,8 +730,7 @@
     <string name="notification_alert_title" msgid="3656229781017543655">"デフォルト"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"自動"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"着信音もバイブレーションも OFF になります"</string>
-    <!-- no translation found for notification_conversation_summary_low (3855696451919728790) -->
-    <skip />
+    <string name="notification_conversation_summary_low" msgid="3855696451919728790">"通知音もバイブレーションも OFF になりますが、会話セクションには引き続き表示されます"</string>
     <string name="notification_channel_summary_default" msgid="777294388712200605">"デバイスの設定を基に着信音またはバイブレーションが有効になります"</string>
     <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"デバイスの設定を基に着信音またはバイブレーションが有効になります。デフォルトでは <xliff:g id="APP_NAME">%1$s</xliff:g> からの会話がふきだしで表示されます。"</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"この通知を音またはバイブレーションで知らせるかどうかの自動判断"</string>
@@ -1217,7 +1229,6 @@
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"認証が必要です。指紋認証センサーをタッチして認証してください。"</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"通話中"</string>
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"モバイルデータ"</string>
-    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"接続済み"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"一時的に接続されています"</string>
     <string name="mobile_data_poor_connection" msgid="819617772268371434">"接続が不安定です"</string>
@@ -1358,8 +1369,7 @@
     <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>
-    <!-- no translation found for shortcut_helper_category_current_app_shortcuts (4017840565974573628) -->
-    <skip />
+    <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_search_placeholder" msgid="5488547526269871819">"検索ショートカット"</string>
@@ -1372,13 +1382,27 @@
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"完了"</string>
     <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"よくできました。"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"戻る"</string>
-    <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"戻るには、3 本の指でタッチパッドを左右にスワイプします。"</string>
+    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
+    <skip />
+    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
+    <skip />
     <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"タッチパッドで 3 本の指を左右に動かしている様子"</string>
     <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"デバイスの画面で「戻る」ジェスチャーのアニメーションが表示されている様子"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"キーボード バックライト"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"レベル %1$d/%2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"ホーム コントロール"</string>
     <string name="home_controls_dream_description" msgid="4644150952104035789">"ホーム コントロールにスクリーンセーバーからすばやくアクセス"</string>
-    <!-- no translation found for volume_undo_action (5815519725211877114) -->
-    <skip />
+    <string name="volume_undo_action" msgid="5815519725211877114">"元に戻す"</string>
+    <string name="back_edu_toast_content" msgid="4530314597378982956">"戻るには、3 本の指でタッチパッドを左右にスワイプします"</string>
+    <string name="home_edu_toast_content" msgid="3381071147871955415">"ホームに移動するには、3 本の指でタッチパッドを上にスワイプします"</string>
+    <string name="overview_edu_toast_content" msgid="5797030644017804518">"最近使ったアプリを表示するには、3 本の指でタッチパッドを上にスワイプして長押しします"</string>
+    <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"すべてのアプリを表示するには、キーボードのアクションキーを押してください"</string>
+    <string name="back_edu_notification_title" msgid="5624780717751357278">"タッチパッドを使用して、前の画面に戻る"</string>
+    <string name="back_edu_notification_content" msgid="2497557451540954068">"3 本の指で左または右にスワイプします。ジェスチャーの詳細を確認するにはタップしてください。"</string>
+    <string name="home_edu_notification_title" msgid="6097902076909654045">"タッチパッドを使用して、ホームに移動する"</string>
+    <string name="home_edu_notification_content" msgid="6631697734535766588">"3 本の指で上にスワイプします。ジェスチャーの詳細を確認するにはタップしてください。"</string>
+    <string name="overview_edu_notification_title" msgid="1265824157319562406">"タッチパッドを使用して、最近使ったアプリを表示する"</string>
+    <string name="overview_edu_notification_content" msgid="3578204677648432500">"3 本の指で上にスワイプして長押しします。ジェスチャーの詳細を確認するにはタップしてください。"</string>
+    <string name="all_apps_edu_notification_title" msgid="372262997265569063">"キーボードを使用して、すべてのアプリを表示する"</string>
+    <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"アクションキーを押せばいつでも機能します。ジェスチャーの詳細を確認するにはタップしてください。"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ja/tiles_states_strings.xml b/packages/SystemUI/res/values-ja/tiles_states_strings.xml
index 610385e..790445c 100644
--- a/packages/SystemUI/res/values-ja/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ja/tiles_states_strings.xml
@@ -56,9 +56,6 @@
     <item msgid="5376619709702103243">"OFF"</item>
     <item msgid="4875147066469902392">"ON"</item>
   </string-array>
-    <!-- no translation found for tile_states_modes:0 (7764936419245199023) -->
-    <!-- no translation found for tile_states_modes:1 (2004750556637773692) -->
-    <!-- no translation found for tile_states_modes:2 (8968530753931637871) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"使用不可"</item>
     <item msgid="5044688398303285224">"OFF"</item>
diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml
index b1b01b0..4d0e90c 100644
--- a/packages/SystemUI/res/values-ka/strings.xml
+++ b/packages/SystemUI/res/values-ka/strings.xml
@@ -388,6 +388,9 @@
     <string name="performance" msgid="6552785217174378320">"ეფექტურობა"</string>
     <string name="user_interface" msgid="3712869377953950887">"სამომხმარებლო ინტერფეისი"</string>
     <string name="thermal" msgid="6758074791325414831">"თერმული"</string>
+    <string name="custom" msgid="3337456985275158299">"მორგებული"</string>
+    <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"თვალის დევნების მორგებული პარამეტრები"</string>
+    <string name="restore_default" msgid="5259420807486239755">"ნაგულისხმევის აღდგენა"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"ცალი ხელის რეჟიმი"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"სმენის აპარატები"</string>
     <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"აქტიური"</string>
@@ -426,6 +429,17 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"პარამეტრების გახსნა"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"სხვა მოწყობილობა"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"მიმოხილვის გადართვა"</string>
+    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"პრიორიტეტული რეჟიმები"</string>
+    <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_off" msgid="1736604456618147306">"გამორთული"</string>
+    <!-- no translation found for zen_mode_set_up (7457957033034460064) -->
+    <skip />
+    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
+    <skip />
+    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
+    <skip />
     <string name="zen_priority_introduction" msgid="3159291973383796646">"თქვენ მიერ მითითებული მაღვიძარების, შეხსენებების, მოვლენებისა და ზარების გარდა, არავითარი ხმა და ვიბრაცია არ შეგაწუხებთ. თქვენ მაინც შეძლებთ სასურველი კონტენტის, მაგალითად, მუსიკის, ვიდეოებისა და თამაშების აუდიოს მოსმენა."</string>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"მაღვიძარების გარდა, არავითარი ხმა და ვიბრაცია არ შეგაწუხებთ. თქვენ მაინც შეძლებთ სასურველი კონტენტის, მაგალითად, მუსიკის, ვიდეოებისა და თამაშების აუდიოს მოსმენა."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"მორგება"</string>
@@ -464,6 +478,8 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ნელა იტენება • სრულ დატენვამდე <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • იტენება • სრულ დატენვამდე <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"ვიჯეტები ჩაკეტილ ეკრანზე"</string>
+    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
+    <skip />
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"გადაფურცლეთ მარცხნივ, რათა დაიწყოთ საერთო სახელმძღვანელო"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"მორგება"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"უარყოფა"</string>
@@ -492,6 +508,7 @@
     <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"არჩეული ვიჯეტის განთავსება"</string>
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"ჩაკეტილი ეკრანის ვიჯეტები"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"ნებისმიერს შეუძლია თქვენს ჩაკეტილ ეკრანზე ვიჯეტების ნახვა, თუნდაც ტაბლეტი ჩაკეტილი იყოს."</string>
+    <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"ვიჯეტის არჩევის გაუქმება"</string>
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"დაბლოკილი ეკრანის ვიჯეტები"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"უნდა დაადასტუროთ თქვენი ვინაობა, რათა გახსნათ აპი ვიჯეტის გამოყენებით. გაითვალისწინეთ, რომ ნებისმიერს შეუძლია მათი ნახვა, მაშინაც კი, როცა ტაბლეტი დაბლოკილია. ზოგი ვიჯეტი შეიძლება არ იყოს გათვლილი თქვენი დაბლოკილი ეკრანისთვის და მათი აქ დამატება შეიძლება სახიფათო იყოს."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"გასაგებია"</string>
@@ -548,8 +565,8 @@
     <string name="media_projection_action_text" msgid="3634906766918186440">"დაწყება ახლავე"</string>
     <string name="empty_shade_text" msgid="8935967157319717412">"შეტყობინებები არ არის."</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"ახალი შეტყობინებები არ არის"</string>
-    <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"მორგებადი შეტყობინებები ჩართულია"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"მოკლე დროში ბევრი შეტყობინების მიღებისას თქვენი მოწყობილობა ახლა უკვე ამცირებს ხმას და ეკრანზე ამომხტარი ფანჯრების რაოდენობას."</string>
+    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"შეტყობინების განტვირთვის პერიოდი ჩართულია"</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"მოწყობილობის ხმა და გაფრთხილებები მცირდება 2 წუთის განმავლობაში, როდესაც ბევრ შეტყობინებას მიიღებთ ერთდროულად."</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"გამორთვა"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"განბლოკეთ ძველი შეტყობინებების სანახავად"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"მოწყობილობას თქვენი მშობელი მართავს"</string>
@@ -716,8 +733,7 @@
     <string name="notification_alert_title" msgid="3656229781017543655">"ნაგულისხმევი"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"ავტომატური"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"ხმისა და ვიბრაციის გარეშე"</string>
-    <!-- no translation found for notification_conversation_summary_low (3855696451919728790) -->
-    <skip />
+    <string name="notification_conversation_summary_low" msgid="3855696451919728790">"ხმა ან ვიბრაცია არ არის, მაგრამ მაინც ჩანს საუბრის სექციაში"</string>
     <string name="notification_channel_summary_default" msgid="777294388712200605">"დარეკვა ან ვიბრაცია მოწყობილობის პარამეტრების მიხედვით"</string>
     <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"დარეკვა ან ვიბრაცია მოწყობილობის პარამეტრების მიხედვით. მიმოწერები <xliff:g id="APP_NAME">%1$s</xliff:g>-ის ბუშტიდან, ნაგულისხმევად."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"სისტემისთვის ისეთი უფლების მინიჭება, რომ მან განსაზღვროს, ამ შეტყობინებამ ხმოვანი სიგნალი უნდა აამოქმედოს თუ ვიბრაცია"</string>
@@ -1216,7 +1232,6 @@
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"საჭიროა ავტორიზაცია. ავტორიზაციისთვის შეეხეთ თითის ანაბეჭდის სენსორს."</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"მიმდინარე სატელეფონო ზარი"</string>
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"მობილური ინტერნეტი"</string>
-    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"დაკავშირებული"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"დროებით დაკავშირებული"</string>
     <string name="mobile_data_poor_connection" msgid="819617772268371434">"სუსტი კავშირი"</string>
@@ -1370,7 +1385,10 @@
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"მზადაა"</string>
     <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"შესანიშნავია!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"უკან დაბრუნება"</string>
-    <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"უკან დასაბრუნებლად, სენსორულ პანელზე ნებისმიერ ადგილას სამი თითის გამოყენებით გადაფურცლეთ მარცხნივ ან მარჯვნივ."</string>
+    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
+    <skip />
+    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
+    <skip />
     <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"სენსორული პანელი, რომელიც აჩვენებს მარჯვენა და მარცხენა მიმართულებით მოძრავ სამ თითს"</string>
     <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"მოწყობილობის ეკრანი, რომელიც აჩვენებს უკან დაბრუნების ჟესტის ანიმაციას"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"კლავიატურის შენათება"</string>
@@ -1378,4 +1396,16 @@
     <string name="home_controls_dream_label" msgid="6567105701292324257">"სახლის კონტროლი"</string>
     <string name="home_controls_dream_description" msgid="4644150952104035789">"სწრაფი წვდომა სახლის კონტროლზე ეკრანმზოგის სახით"</string>
     <string name="volume_undo_action" msgid="5815519725211877114">"მოქმედების გაუქმება"</string>
+    <string name="back_edu_toast_content" msgid="4530314597378982956">"უკან დასაბრუნებლად სენსორულ პანელზე სამი თითით გადაფურცლეთ მარცხნივ ან მარჯვნივ"</string>
+    <string name="home_edu_toast_content" msgid="3381071147871955415">"მთავარ გვერდზე გადასასვლელად სენსორულ პანელზე გადაფურცლეთ ზემოთ სამი თითით"</string>
+    <string name="overview_edu_toast_content" msgid="5797030644017804518">"ბოლო აპების სანახავად სენსორულ პანელზე სამი თითით გადაფურცლეთ ზემოთ და მოიცადეთ"</string>
+    <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"ყველა აპის სანახავად დააჭირეთ მოქმედების კლავიშს თქვენს კლავიატურაზე"</string>
+    <string name="back_edu_notification_title" msgid="5624780717751357278">"უკან დასაბრუნებლად გამოიყენეთ სენსორული პანელი"</string>
+    <string name="back_edu_notification_content" msgid="2497557451540954068">"გადაფურცლეთ მარცხნივ ან მარჯვნივ სამი თითით. შეეხეთ მეტი ჟესტის შესასწავლად."</string>
+    <string name="home_edu_notification_title" msgid="6097902076909654045">"მთავარ გვერდზე გადასასვლელად გამოიყენეთ სენსორული პანელი"</string>
+    <string name="home_edu_notification_content" msgid="6631697734535766588">"გადაფურცლეთ ზემოთ სამი თითით. შეეხეთ მეტი ჟესტის შესასწავლად."</string>
+    <string name="overview_edu_notification_title" msgid="1265824157319562406">"ბოლო აპების სანახავად გამოიყენეთ სენსორული პანელი"</string>
+    <string name="overview_edu_notification_content" msgid="3578204677648432500">"სამი თითით გადაფურცლეთ ზემოთ და მოიცადეთ. შეეხეთ მეტი ჟესტის შესასწავლად."</string>
+    <string name="all_apps_edu_notification_title" msgid="372262997265569063">"ყველა აპის სანახავად გამოიყენეთ თქვენი კლავიატურა"</string>
+    <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"ნებისმიერ დროს დააჭირეთ მოქმედების კლავიშს. შეეხეთ მეტი ჟესტის შესასწავლად."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ka/tiles_states_strings.xml b/packages/SystemUI/res/values-ka/tiles_states_strings.xml
index 2b11a15..21f8102 100644
--- a/packages/SystemUI/res/values-ka/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ka/tiles_states_strings.xml
@@ -56,11 +56,6 @@
     <item msgid="5376619709702103243">"გამორთულია"</item>
     <item msgid="4875147066469902392">"ჩართულია"</item>
   </string-array>
-  <string-array name="tile_states_modes">
-    <item msgid="7764936419245199023">"მიუწვდომელია"</item>
-    <item msgid="2004750556637773692">"გამორთული"</item>
-    <item msgid="8968530753931637871">"ჩართული"</item>
-  </string-array>
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"მიუწვდომელია"</item>
     <item msgid="5044688398303285224">"გამორთულია"</item>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index c07b6a0..9d84c73 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -126,38 +126,25 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"Көру үшін түртіңіз."</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"Экран жазбасын сақтау кезінде қате шықты."</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Экрандағы бейнені жазу кезінде қате шықты."</string>
-    <!-- no translation found for screenrecord_stop_dialog_title (8716193661764511095) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message (6262768207331626817) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5995770227684523244) -->
-    <skip />
+    <string name="screenrecord_stop_dialog_title" msgid="8716193661764511095">"Жазу тоқтатылсын ба?"</string>
+    <string name="screenrecord_stop_dialog_message" msgid="6262768207331626817">"Қазір бүкіл экранды жазып жатырсыз."</string>
+    <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Қазір қолданбадағы (<xliff:g id="APP_NAME">%1$s</xliff:g>) контентті жазып жатырсыз."</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Жазуды тоқтату"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Экранды бөлісіп жатыр."</string>
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Экранды бөлісуді тоқтатасыз ба?"</string>
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen_with_host_app (522823522115375414) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen (5090115386271179270) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_specific (5923772039347985172) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_generic (6681016774654578261) -->
-    <skip />
+    <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Қазір бүкіл экранды қолданбамен (<xliff:g id="HOST_APP_NAME">%1$s</xliff:g>) бөлісіп жатырсыз."</string>
+    <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Қазір бүкіл экранды қолданбамен бөлісіп жатырсыз."</string>
+    <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Қазір қолданбадағы (<xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>) контентті бөлісіп жатырсыз."</string>
+    <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Қазір қолданбаны бөлісіп жатырсыз."</string>
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Бөлісуді тоқтату"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Экранды трансляциялап жатырсыз."</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Трансляциялау тоқтасын ба?"</string>
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen_with_device (1474703115926205251) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen (8419219169553867625) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app_with_device (2715934698604085519) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (8616103075630934513) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic_with_device (9213582497852420203) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic (4100272100480415076) -->
-    <skip />
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen_with_device" msgid="1474703115926205251">"Қазір бүкіл экранды құрылғыға (<xliff:g id="DEVICE_NAME">%1$s</xliff:g>) трансляциялап жатырсыз."</string>
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen" msgid="8419219169553867625">"Қазір бүкіл экранды маңайдағы құрылғыға трансляциялап жатырсыз."</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app_with_device" msgid="2715934698604085519">"Қазір қолданбаны (<xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>) құрылғыға (<xliff:g id="DEVICE_NAME">%2$s</xliff:g>) трансляциялап жатырсыз."</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="8616103075630934513">"Қазір қолданбаны (<xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>) маңайдағы құрылғыға трансляциялап жатырсыз."</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic_with_device" msgid="9213582497852420203">"Қазір құрылғыға (<xliff:g id="DEVICE_NAME">%1$s</xliff:g>) трансляциялап жатырсыз."</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic" msgid="4100272100480415076">"Қазір маңайдағы құрылғыға трансляциялап жатырсыз."</string>
     <string name="cast_to_other_device_stop_dialog_button" msgid="6420183747435521834">"Трансляцияны тоқтату"</string>
     <string name="close_dialog_button" msgid="4749497706540104133">"Жабу"</string>
     <string name="issuerecord_title" msgid="286627115110121849">"Мәселені жазу құралы"</string>
@@ -303,8 +290,7 @@
     <string name="start_dreams" msgid="9131802557946276718">"Скринсейвер"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Этернет"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"Мазаламау"</string>
-    <!-- no translation found for quick_settings_modes_label (5407025818652750501) -->
-    <skip />
+    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Басымдық режимдері"</string>
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Жұптасқан құрылғылар жоқ"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Құрылғыны жалғау не ажырату үшін түртіңіз."</string>
@@ -334,7 +320,7 @@
     <string name="quick_settings_camera_label" msgid="5612076679385269339">"Камераны пайдалану"</string>
     <string name="quick_settings_mic_label" msgid="8392773746295266375">"Микрофонды пайдалану"</string>
     <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Қолжетімді"</string>
-    <string name="quick_settings_camera_mic_blocked" msgid="4710884905006788281">"Бөгелген"</string>
+    <string name="quick_settings_camera_mic_blocked" msgid="4710884905006788281">"Блокталған"</string>
     <string name="quick_settings_media_device_label" msgid="8034019242363789941">"Meдиа құрылғысы"</string>
     <string name="quick_settings_user_title" msgid="8673045967216204537">"Пайдаланушы"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
@@ -402,6 +388,9 @@
     <string name="performance" msgid="6552785217174378320">"Өнімділік"</string>
     <string name="user_interface" msgid="3712869377953950887">"Пайдаланушы интерфейсі"</string>
     <string name="thermal" msgid="6758074791325414831">"Термовизия"</string>
+    <string name="custom" msgid="3337456985275158299">"Басқа"</string>
+    <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Басқа трассалау параметрлері"</string>
+    <string name="restore_default" msgid="5259420807486239755">"Әдепкі қалпына келтіру"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Бір қолмен басқару режимі"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Есту құрылғылары"</string>
     <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Қосулы"</string>
@@ -440,6 +429,17 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Параметрлерді ашу"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Басқа құрылғы"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Шолуды қосу/өшіру"</string>
+    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Басымдық режимдері"</string>
+    <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_off" msgid="1736604456618147306">"Өшірулі"</string>
+    <!-- no translation found for zen_mode_set_up (7457957033034460064) -->
+    <skip />
+    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
+    <skip />
+    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
+    <skip />
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Оятқыш, еске салғыш, іс-шара мен өзіңіз көрсеткен контактілердің қоңырауларынан басқа дыбыстар мен дірілдер мазаламайтын болады. Музыка, бейне және ойын сияқты медиафайлдарды қоссаңыз, оларды естисіз."</string>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Дабылдардан басқа ешқандай дыбыстар мен дірілдер мазаламайтын болады. Музыка, бейне және ойындар сияқты ойнатылатын контентті ести алатын боласыз."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Реттеу"</string>
@@ -478,6 +478,8 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Баяу зарядталуда • Толуына <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> қалды."</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Зарядталып жатыр. • Толуына <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> қалды."</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Құлыптаулы экрандағы виджеттер"</string>
+    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
+    <skip />
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Ортақ оқулықты ашу үшін солға қарай сырғытыңыз."</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Бейімдеу"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Жабу"</string>
@@ -504,10 +506,9 @@
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"виджет таңдау"</string>
     <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"виджетті өшіру"</string>
     <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"таңдалған виджетті орналастыру"</string>
-    <!-- no translation found for communal_widget_picker_title (1953369090475731663) -->
-    <skip />
-    <!-- no translation found for communal_widget_picker_description (490515450110487871) -->
-    <skip />
+    <string name="communal_widget_picker_title" msgid="1953369090475731663">"Құлып экранының виджеттері"</string>
+    <string name="communal_widget_picker_description" msgid="490515450110487871">"Планшет құлыпталып тұрса да, құлып экранындағы виджеттерді кез келген адам көре алады."</string>
+    <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"виджетті таңдаудан алу"</string>
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Құлып экранының виджеттері"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Қолданбаны виджет көмегімен ашу үшін жеке басыңызды растауыңыз керек. Сондай-ақ басқалар оларды планшетіңіз құлыптаулы кезде де көре алатынын ескеріңіз. Кейбір виджеттер құлып экранына арналмаған болады, сондықтан оларды мұнда қосу қауіпсіз болмауы мүмкін."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Түсінікті"</string>
@@ -564,8 +565,8 @@
     <string name="media_projection_action_text" msgid="3634906766918186440">"Қазір бастау"</string>
     <string name="empty_shade_text" msgid="8935967157319717412">"Хабарландырулар жоқ"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"Жаңа хабарландырулар жоқ"</string>
-    <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"Бейімделетін хабарландырулар қосулы"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"Қысқа уақыт аралығында көп хабарландыру алсаңыз, құрылғыңыз дыбыс деңгейін және экранда шығатын қалқымалы терезелердің уақытын екі минутқа дейін азайтады."</string>
+    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Хабарландыру дыбысын азайту параметрі қосулы"</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Бір уақытта тым көп хабарландыру келсе, дыбыс деңгейі автоматты түрде азайтылып, хабарландырулар 2 минутқа кідіртіледі."</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Өшіру"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Ескі хабарландырулар үшін құлыпты ашыңыз"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Бұл құрылғыны ата-анаңыз басқарады."</string>
@@ -732,8 +733,7 @@
     <string name="notification_alert_title" msgid="3656229781017543655">"Әдепкі"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Автоматты"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"Дыбыс не діріл болмайды."</string>
-    <!-- no translation found for notification_conversation_summary_low (3855696451919728790) -->
-    <skip />
+    <string name="notification_conversation_summary_low" msgid="3855696451919728790">"Дыбысы не дірілі өшіріледі, алайда әңгімелер бөлімінде көрсетіле береді"</string>
     <string name="notification_channel_summary_default" msgid="777294388712200605">"Құрылғы параметрлеріне байланысты шырылдауы не дірілдеуі мүмкін"</string>
     <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Құрылғы параметрлеріне байланысты шырылдауы не дірілдеуі мүмкін. <xliff:g id="APP_NAME">%1$s</xliff:g> чаттары әдепкісінше қалқымалы етіп көрсетіледі."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Хабарландыру дыбысының немесе дірілдің қосылуын жүйе анықтайтын болады"</string>
@@ -790,8 +790,7 @@
     <string name="keyboard_key_page_up" msgid="173914303254199845">"Page Up"</string>
     <string name="keyboard_key_page_down" msgid="9035902490071829731">"Page Down"</string>
     <string name="keyboard_key_forward_del" msgid="5325501825762733459">"Delete"</string>
-    <!-- no translation found for keyboard_key_esc (6230365950511411322) -->
-    <skip />
+    <string name="keyboard_key_esc" msgid="6230365950511411322">"Esc"</string>
     <string name="keyboard_key_move_home" msgid="3496502501803911971">"Home"</string>
     <string name="keyboard_key_move_end" msgid="99190401463834854">"End"</string>
     <string name="keyboard_key_insert" msgid="4621692715704410493">"Insert"</string>
@@ -1233,7 +1232,6 @@
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Аутентификациядан өту қажет. Ол үшін саусақ ізін оқу сканерін түртіңіз."</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Телефон қоңырауы бар"</string>
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Мобильдік интернет"</string>
-    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Жалғанды"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Уақытша байланыс орнатылды."</string>
     <string name="mobile_data_poor_connection" msgid="819617772268371434">"Байланыс нашар."</string>
@@ -1374,8 +1372,7 @@
     <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>
-    <!-- no translation found for shortcut_helper_category_current_app_shortcuts (4017840565974573628) -->
-    <skip />
+    <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_search_placeholder" msgid="5488547526269871819">"Іздеу жылдам пәрмендері"</string>
@@ -1388,13 +1385,27 @@
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Дайын"</string>
     <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Жарайсыз!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Артқа"</string>
-    <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"Артқа қайту үшін сенсорлық тақтаның кез келген жерінен үш саусақпен солға немесе оңға сырғытыңыз."</string>
+    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
+    <skip />
+    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
+    <skip />
     <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Оңға және солға жылжитын үш саусақты көрсетіп тұрған сенсорлық тақта."</string>
     <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Артқа қайту қимылын көрсетіп тұрған құрылғы экраны."</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Пернетақта жарығы"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Деңгей: %1$d/%2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Үй басқару элементтері"</string>
     <string name="home_controls_dream_description" msgid="4644150952104035789">"Үй басқару элементтерін скринсейверден қолданыңыз."</string>
-    <!-- no translation found for volume_undo_action (5815519725211877114) -->
-    <skip />
+    <string name="volume_undo_action" msgid="5815519725211877114">"Қайтару"</string>
+    <string name="back_edu_toast_content" msgid="4530314597378982956">"Сенсорлық тақтада үш саусақпен солға не оңға сырғытсаңыз, артқа қайтасыз."</string>
+    <string name="home_edu_toast_content" msgid="3381071147871955415">"Сенсорлық сақтада үш саусақпен жоғары сырғытсаңыз, негізгі бетке өтесіз."</string>
+    <string name="overview_edu_toast_content" msgid="5797030644017804518">"Сенсорлық тақтада үш саусақпен жоғары сырғытып, басып тұрсаңыз, соңғы ашылған қолданбаларды көресіз."</string>
+    <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Пернетақтада әрекет пернесін басып, барлық қолданбаны көре аласыз."</string>
+    <string name="back_edu_notification_title" msgid="5624780717751357278">"Артқа қайту үшін сенсорлық тақтаны қолданыңыз"</string>
+    <string name="back_edu_notification_content" msgid="2497557451540954068">"Үш саусақпен солға не оңға сырғытыңыз. Басқа қимылдарды үйрену үшін түртіңіз."</string>
+    <string name="home_edu_notification_title" msgid="6097902076909654045">"Негізгі бетке өту үшін сенсорлық тақтаны қолданыңыз"</string>
+    <string name="home_edu_notification_content" msgid="6631697734535766588">"Үш саусақпен жоғары сырғытыңыз. Басқа қимылдарды үйрену үшін түртіңіз."</string>
+    <string name="overview_edu_notification_title" msgid="1265824157319562406">"Соңғы ашылған қолданбаларды көру үшін сенсорлық тақтаны қолданыңыз"</string>
+    <string name="overview_edu_notification_content" msgid="3578204677648432500">"Үш саусақпен жоғары сырғытып, басып тұрыңыз. Басқа қимылдарды үйрену үшін түртіңіз."</string>
+    <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Барлық қолданбаны көру үшін пернетақтаны қолданыңыз"</string>
+    <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Әрекет пернесін кез келген уақытта баса аласыз. Басқа қимылдарды үйрену үшін түртіңіз."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-kk/tiles_states_strings.xml b/packages/SystemUI/res/values-kk/tiles_states_strings.xml
index 6410460..cf3aa69 100644
--- a/packages/SystemUI/res/values-kk/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-kk/tiles_states_strings.xml
@@ -56,9 +56,6 @@
     <item msgid="5376619709702103243">"Өшірулі"</item>
     <item msgid="4875147066469902392">"Қосулы"</item>
   </string-array>
-    <!-- no translation found for tile_states_modes:0 (7764936419245199023) -->
-    <!-- no translation found for tile_states_modes:1 (2004750556637773692) -->
-    <!-- no translation found for tile_states_modes:2 (8968530753931637871) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Қолжетімсіз"</item>
     <item msgid="5044688398303285224">"Өшірулі"</item>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index bd31568..b84b6b6 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -290,8 +290,7 @@
     <string name="start_dreams" msgid="9131802557946276718">"ធាតុរក្សាអេក្រង់"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"អ៊ីសឺរណិត"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"កុំ​រំខាន"</string>
-    <!-- no translation found for quick_settings_modes_label (5407025818652750501) -->
-    <skip />
+    <string name="quick_settings_modes_label" msgid="5407025818652750501">"មុខងារអាទិភាព"</string>
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"ប៊្លូធូស"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"មិន​មាន​ឧបករណ៍​ផ្គូផ្គង​ដែល​អាច​ប្រើ​បាន"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"ចុចដើម្បីភ្ជាប់ ឬផ្ដាច់ឧបករណ៍"</string>
@@ -389,6 +388,9 @@
     <string name="performance" msgid="6552785217174378320">"ប្រតិបត្តិការ"</string>
     <string name="user_interface" msgid="3712869377953950887">"ផ្ទៃប៉ះ"</string>
     <string name="thermal" msgid="6758074791325414831">"កម្ដៅ"</string>
+    <string name="custom" msgid="3337456985275158299">"ផ្ទាល់ខ្លួន"</string>
+    <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"ការកំណត់ដានផ្ទាល់ខ្លួន"</string>
+    <string name="restore_default" msgid="5259420807486239755">"ស្ដារទៅលំនាំដើម"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"មុខងារប្រើដៃម្ខាង"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"ឧបករណ៍ជំនួយការស្ដាប់"</string>
     <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"សកម្ម"</string>
@@ -427,6 +429,17 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"បើកការកំណត់"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"ឧបករណ៍ផ្សេងទៀត"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"បិទ/បើក​ទិដ្ឋភាពរួម"</string>
+    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"មុខងារអាទិភាព"</string>
+    <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_off" msgid="1736604456618147306">"បិទ"</string>
+    <!-- no translation found for zen_mode_set_up (7457957033034460064) -->
+    <skip />
+    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
+    <skip />
+    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
+    <skip />
     <string name="zen_priority_introduction" msgid="3159291973383796646">"សំឡេង និងរំញ័រនឹងមិន​រំខានដល់អ្នកឡើយ លើកលែងតែម៉ោងរោទ៍ ការរំលឹក ព្រឹត្តិការណ៍ និងអ្នកហៅទូរសព្ទដែលអ្នកបញ្ជាក់ប៉ុណ្ណោះ។ អ្នកនឹងនៅតែឮសំឡេងសកម្មភាពគ្រប់យ៉ាងដែលអ្នកលេងដដែល រួមទាំងតន្រ្តី វីដេអូ និងហ្គេម។"</string>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"សំឡេង និងរំញ័រនឹងមិន​រំខានដល់អ្នកឡើយ លើកលែងតែម៉ោងរោទ៍ប៉ុណ្ណោះ។ អ្នកនឹងនៅតែឮសំឡេងសកម្មភាពគ្រប់យ៉ាងដែលអ្នកលេងដដែល រួមទាំងតន្រ្តី វីដេអូ និងហ្គេម។"</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"ប្ដូរតាមបំណង"</string>
@@ -465,6 +478,8 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • កំពុង​សាកថ្ម​យឺត • ពេញក្នុងរយៈពេល <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • កំពុងសាកថ្ម • ពេញក្នុងរយៈពេល <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"ធាតុ​ក្រាហ្វិកនៅលើអេក្រង់ចាក់សោ"</string>
+    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
+    <skip />
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"អូសទៅឆ្វេង ដើម្បីចាប់ផ្ដើមមេរៀនសហគមន៍"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"ប្ដូរតាមបំណង"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"ច្រានចោល"</string>
@@ -493,6 +508,7 @@
     <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"ដាក់ធាតុ​ក្រាហ្វិកដែលបានជ្រើសរើស"</string>
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"ធាតុ​ក្រាហ្វិកអេក្រង់ចាក់សោ"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"អ្នកគ្រប់គ្នាអាចមើលធាតុក្រាហ្វិកលើអេក្រង់ចាក់សោរបស់អ្នក ទោះបីជាថេប្លេតរបស់អ្នកត្រូវបានចាក់សោក៏ដោយ។"</string>
+    <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"ដក​ការ​ជ្រើសរើសធាតុ​ក្រាហ្វិក"</string>
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"ធាតុ​ក្រាហ្វិកលើអេក្រង់ចាក់សោ"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"ដើម្បីបើកកម្មវិធីដោយប្រើធាតុ​ក្រាហ្វិក អ្នកនឹងត្រូវផ្ទៀងផ្ទាត់ថាជាអ្នក។ ទន្ទឹមនឹងនេះ សូមចងចាំថា នរណាក៏អាចមើលធាតុក្រាហ្វិកបាន សូម្បីពេលថេប្លេតរបស់អ្នកជាប់សោក៏ដោយ។ ធាតុ​ក្រាហ្វិកមួយចំនួនប្រហែលមិនត្រូវបានរចនាឡើងសម្រាប់អេក្រង់ចាក់សោរបស់អ្នកទេ និងមិនមានសុវត្ថិភាពឡើយ បើបញ្ចូលទៅទីនេះ។"</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"យល់ហើយ"</string>
@@ -549,8 +565,8 @@
     <string name="media_projection_action_text" msgid="3634906766918186440">"ចាប់ផ្ដើម​ឥឡូវ"</string>
     <string name="empty_shade_text" msgid="8935967157319717412">"គ្មាន​ការ​ជូនដំណឹង"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"គ្មាន​ការ​ជូន​ដំណឹង​​ថ្មីៗទេ"</string>
-    <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"ការជូនដំណឹងដែលបត់បែនត្រូវបានបើក"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"ឥឡូវនេះ ឧបករណ៍របស់អ្នកបន្ថយកម្រិតសំឡេង និងកាត់បន្ថយផ្ទាំងលោតឡើងនៅលើអេក្រង់រយៈពេលរហូតដល់ពីរនាទី នៅពេលអ្នកទទួលបានការជូនដំណឹងច្រើនក្នុងរយៈពេលខ្លី។"</string>
+    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"ការបន្ថយសំឡេងការជូនដំណឹងត្រូវបានបើក"</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"កម្រិតសំឡេង និងការជូនដំណឹងនៅលើឧបករណ៍របស់អ្នកត្រូវបានកាត់បន្ថយដោយស្វ័យប្រវត្តិរហូតដល់ 2 នាទី នៅពេលអ្នកទទួលបានការជូនដំណឹងច្រើនពេកក្នុងពេលតែមួយ។"</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"បិទ"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"ដោះសោដើម្បីមើលការជូនដំណឹងចាស់ៗ"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"ឧបករណ៍​នេះ​ស្ថិត​ក្រោម​ការ​គ្រប់គ្រង​របស់មាតាបិតាអ្នក"</string>
@@ -717,8 +733,7 @@
     <string name="notification_alert_title" msgid="3656229781017543655">"លំនាំដើម"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"ស្វ័យប្រវត្តិ"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"គ្មាន​សំឡេង ឬការញ័រទេ"</string>
-    <!-- no translation found for notification_conversation_summary_low (3855696451919728790) -->
-    <skip />
+    <string name="notification_conversation_summary_low" msgid="3855696451919728790">"មិនមានសំឡេង ឬការ​ញ័រទេ ប៉ុន្តែនៅតែបង្ហាញនៅក្នុងផ្នែកសន្ទនាដដែល"</string>
     <string name="notification_channel_summary_default" msgid="777294388712200605">"អាចរោទ៍ ឬញ័រ ដោយផ្អែកលើ​ការកំណត់ឧបករណ៍"</string>
     <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"អាចរោទ៍ ឬញ័រ ដោយផ្អែកលើ​ការកំណត់ឧបករណ៍។ ការសន្ទនា​ពីផ្ទាំងអណ្ដែត <xliff:g id="APP_NAME">%1$s</xliff:g> តាម​លំនាំដើម​។"</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"ឱ្យប្រព័ន្ធកំណត់ថាតើ​ការជូនដំណឹងនេះ​គួរតែបន្លឺសំឡេង ឬញ័រ"</string>
@@ -1217,7 +1232,6 @@
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"តម្រូវឱ្យ​មាន​ការផ្ទៀងផ្ទាត់។ សូមចុច​ឧបករណ៍​ចាប់ស្នាមម្រាមដៃ ដើម្បី​ផ្ទៀងផ្ទាត់​។"</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"ការហៅទូរសព្ទ​ដែលកំពុង​ដំណើរការ"</string>
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"ទិន្នន័យ​ទូរសព្ទចល័ត"</string>
-    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"បានភ្ជាប់"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"បានភ្ជាប់ជាបណ្ដោះអាសន្ន"</string>
     <string name="mobile_data_poor_connection" msgid="819617772268371434">"ការតភ្ជាប់​ខ្សោយ"</string>
@@ -1358,8 +1372,7 @@
     <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>
-    <!-- no translation found for shortcut_helper_category_current_app_shortcuts (4017840565974573628) -->
-    <skip />
+    <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_search_placeholder" msgid="5488547526269871819">"ផ្លូវ​កាត់ការស្វែងរក"</string>
@@ -1372,13 +1385,27 @@
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"រួចរាល់"</string>
     <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"ធ្វើបានល្អ!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"ថយ​ក្រោយ"</string>
-    <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"ដើម្បីថយក្រោយ សូមអូសទៅឆ្វេង ឬស្ដាំដោយប្រើម្រាមដៃបីនៅកន្លែងណាមួយនៅលើផ្ទាំងប៉ះ។"</string>
+    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
+    <skip />
+    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
+    <skip />
     <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"ផ្ទាំងប៉ះដែលបង្ហាញម្រាមដៃបីដែលផ្លាស់ទីទៅស្ដាំ និងឆ្វេង"</string>
     <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"អេក្រង់ឧបករណ៍ដែលបង្ហាញរូបមានចលនាសម្រាប់ចលនាថយក្រោយ"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"ពន្លឺក្រោយក្ដារចុច"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"កម្រិតទី %1$d នៃ %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"ការគ្រប់គ្រង​ផ្ទះ"</string>
     <string name="home_controls_dream_description" msgid="4644150952104035789">"ចូលប្រើការគ្រប់គ្រងផ្ទះអ្នកបានលឿនជាធាតុរក្សាអេក្រង់"</string>
-    <!-- no translation found for volume_undo_action (5815519725211877114) -->
-    <skip />
+    <string name="volume_undo_action" msgid="5815519725211877114">"ត្រឡប់វិញ"</string>
+    <string name="back_edu_toast_content" msgid="4530314597378982956">"ដើម្បីថយក្រោយ សូមអូសទៅឆ្វេង ឬស្ដាំដោយប្រើ​ម្រាមដៃបីនៅលើផ្ទាំងប៉ះ"</string>
+    <string name="home_edu_toast_content" msgid="3381071147871955415">"ដើម្បីចូលទៅទំព័រដើម សូមអូសឡើងលើដោយប្រើម្រាមដៃបីនៅលើផ្ទាំងប៉ះ"</string>
+    <string name="overview_edu_toast_content" msgid="5797030644017804518">"ដើម្បីមើលកម្មវិធីថ្មីៗ សូមអូសឡើងលើ ហើយសង្កត់ឱ្យជាប់ដោយប្រើម្រាមដៃបីនៅលើផ្ទាំងប៉ះ"</string>
+    <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"ដើម្បីមើលកម្មវិធីទាំងអស់របស់អ្នក សូមចុចគ្រាប់ចុចសកម្មភាពនៅលើក្ដារចុចរបស់អ្នក"</string>
+    <string name="back_edu_notification_title" msgid="5624780717751357278">"ប្រើផ្ទាំងប៉ះរបស់អ្នក ដើម្បីថយក្រោយ"</string>
+    <string name="back_edu_notification_content" msgid="2497557451540954068">"អូសទៅឆ្វេង ឬស្ដាំដោយប្រើ​ម្រាមដៃបី។ ចុច ដើម្បីស្វែងយល់បន្ថែមអំពីចលនា។"</string>
+    <string name="home_edu_notification_title" msgid="6097902076909654045">"ប្រើផ្ទាំងប៉ះរបស់អ្នក ដើម្បីចូលទៅទំព័រដើម"</string>
+    <string name="home_edu_notification_content" msgid="6631697734535766588">"អូសឡើងលើដោយប្រើម្រាមដៃបី។ ចុច ដើម្បីស្វែងយល់បន្ថែមអំពីចលនា។"</string>
+    <string name="overview_edu_notification_title" msgid="1265824157319562406">"ប្រើផ្ទាំងប៉ះរបស់អ្នក ដើម្បីមើលកម្មវិធីថ្មីៗ"</string>
+    <string name="overview_edu_notification_content" msgid="3578204677648432500">"អូសឡើងលើ ហើយសង្កត់ឱ្យជាប់ដោយប្រើម្រាមដៃបី។ ចុច ដើម្បីស្វែងយល់បន្ថែមអំពីចលនា។"</string>
+    <string name="all_apps_edu_notification_title" msgid="372262997265569063">"ប្រើក្ដារចុចរបស់អ្នក ដើម្បីមើលកម្មវិធីទាំងអស់"</string>
+    <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"ចុចគ្រាប់ចុចសកម្មភាពនៅពេលណាក៏បាន។ ចុច ដើម្បីស្វែងយល់បន្ថែមអំពីចលនា។"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-km/tiles_states_strings.xml b/packages/SystemUI/res/values-km/tiles_states_strings.xml
index 25f7485..54790f6 100644
--- a/packages/SystemUI/res/values-km/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-km/tiles_states_strings.xml
@@ -56,9 +56,6 @@
     <item msgid="5376619709702103243">"បិទ"</item>
     <item msgid="4875147066469902392">"បើក"</item>
   </string-array>
-    <!-- no translation found for tile_states_modes:0 (7764936419245199023) -->
-    <!-- no translation found for tile_states_modes:1 (2004750556637773692) -->
-    <!-- no translation found for tile_states_modes:2 (8968530753931637871) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"មិនមានទេ"</item>
     <item msgid="5044688398303285224">"បិទ"</item>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index 4bf89e1..8ad74af 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -388,6 +388,9 @@
     <string name="performance" msgid="6552785217174378320">"ಪರ್ಫಾರ್ಮೆನ್ಸ್"</string>
     <string name="user_interface" msgid="3712869377953950887">"ಬಳಕೆದಾರ ಇಂಟರ್‌ಫೇಸ್"</string>
     <string name="thermal" msgid="6758074791325414831">"ಥರ್ಮಲ್"</string>
+    <string name="custom" msgid="3337456985275158299">"ಕಸ್ಟಮ್"</string>
+    <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"ಕಸ್ಟಮ್ ಜಾಡಿನ ಸೆಟ್ಟಿಂಗ್‌ಗಳು"</string>
+    <string name="restore_default" msgid="5259420807486239755">"ಡೀಫಾಲ್ಟ್ ಅನ್ನು ಮರುಸ್ಥಾಪಿಸಿ"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"ಒಂದು ಕೈ ಮೋಡ್"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"ಶ್ರವಣ ಸಾಧನಗಳು"</string>
     <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"ಸಕ್ರಿಯವಾಗಿದೆ"</string>
@@ -426,6 +429,17 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"ಸೆಟ್ಟಿಂಗ್‌ಗಳನ್ನು ತೆರೆಯಿರಿ"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"ಅನ್ಯ ಸಾಧನ"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"ಟಾಗಲ್ ನ ಅವಲೋಕನ"</string>
+    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"ಆದ್ಯತೆಯ ಮೋಡ್‌ಗಳು"</string>
+    <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_off" msgid="1736604456618147306">"ಆಫ್ ಆಗಿದೆ"</string>
+    <!-- no translation found for zen_mode_set_up (7457957033034460064) -->
+    <skip />
+    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
+    <skip />
+    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
+    <skip />
     <string name="zen_priority_introduction" msgid="3159291973383796646">"ಅಲಾರಾಂಗಳು, ಜ್ಞಾಪನೆಗಳು, ಈವೆಂಟ್‌ಗಳು ಹಾಗೂ ನೀವು ಸೂಚಿಸಿರುವ ಕರೆದಾರರನ್ನು ಹೊರತುಪಡಿಸಿ ಬೇರಾವುದೇ ಸದ್ದುಗಳು ಅಥವಾ ವೈಬ್ರೇಶನ್‌ಗಳು ನಿಮಗೆ ತೊಂದರೆ ನೀಡುವುದಿಲ್ಲ. ಹಾಗಿದ್ದರೂ, ನೀವು ಪ್ಲೇ ಮಾಡುವ ಸಂಗೀತ, ವೀಡಿಯೊಗಳು ಮತ್ತು ಆಟಗಳ ಆಡಿಯೊವನ್ನು ನೀವು ಕೇಳಿಸಿಕೊಳ್ಳುತ್ತೀರಿ."</string>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"ಅಲಾರಾಂಗಳನ್ನು ಹೊರತುಪಡಿಸಿ, ಬೇರಾವುದೇ ಸದ್ದುಗಳು ಅಥವಾ ವೈಬ್ರೇಶನ್‌ಗಳು ನಿಮಗೆ ತೊಂದರೆ ನೀಡುವುದಿಲ್ಲ. ಹಾಗಿದ್ದರೂ, ನೀವು ಪ್ಲೇ ಮಾಡುವ ಸಂಗೀತ, ವೀಡಿಯೊಗಳು ಮತ್ತು ಆಟಗಳ ಆಡಿಯೊವನ್ನು ಕೇಳಿಸಿಕೊಳ್ಳುತ್ತೀರಿ."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"ಕಸ್ಟಮೈಸ್ ಮಾಡು"</string>
@@ -464,6 +478,8 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ನಿಧಾನವಾಗಿ ಚಾರ್ಜ್ ಆಗುತ್ತಿದೆ • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> ಸಮಯದಲ್ಲಿ ಪೂರ್ಣಗೊಳ್ಳುತ್ತದೆ"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ಚಾರ್ಜ್ ಆಗುತ್ತಿದೆ • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> ದಲ್ಲಿ ಪೂರ್ಣಗೊಳ್ಳುತ್ತದೆ"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"ಲಾಕ್ ಸ್ಕ್ರೀನ್‌ನಲ್ಲಿ ವಿಜೆಟ್‌ಗಳು"</string>
+    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
+    <skip />
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"ಸಮುದಾಯದ ಟ್ಯುಟೋರಿಯಲ್ ಅನ್ನು ಪ್ರಾರಂಭಿಸಲು ಎಡಕ್ಕೆ ಸ್ವೈಪ್ ಮಾಡಿ"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"ಕಸ್ಟಮೈಸ್ ಮಾಡಿ"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"ವಜಾಗೊಳಿಸಿ"</string>
@@ -492,6 +508,7 @@
     <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"ಆಯ್ಕೆಮಾಡಿದ ವಿಜೆಟ್ ಅನ್ನು ಇರಿಸಿ"</string>
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"ಲಾಕ್ ಸ್ಕ್ರೀನ್ ವಿಜೆಟ್‌ಗಳು"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"ನಿಮ್ಮ ಟ್ಯಾಬ್ಲೆಟ್ ಲಾಕ್ ಆಗಿದ್ದರೂ ಸಹ ಯಾರಾದರೂ ನಿಮ್ಮ ಲಾಕ್ ಸ್ಕ್ರೀನ್‌ನಲ್ಲಿ ವಿಜೆಟ್‌ಗಳನ್ನು ವೀಕ್ಷಿಸಬಹುದು."</string>
+    <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"ವಿಜೆಟ್ ಅನ್ನು ಆಯ್ಕೆ ಮಾಡಬೇಡಿ"</string>
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"ಲಾಕ್ ಸ್ಕ್ರೀನ್ ವಿಜೆಟ್‌ಗಳು"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"ವಿಜೆಟ್ ಅನ್ನು ಬಳಸಿಕೊಂಡು ಆ್ಯಪ್ ತೆರೆಯಲು, ಇದು ನೀವೇ ಎಂದು ನೀವು ದೃಢೀಕರಿಸಬೇಕಾಗುತ್ತದೆ. ಅಲ್ಲದೆ, ನಿಮ್ಮ ಟ್ಯಾಬ್ಲೆಟ್ ಲಾಕ್ ಆಗಿದ್ದರೂ ಸಹ ಯಾರಾದರೂ ಅವುಗಳನ್ನು ವೀಕ್ಷಿಸಬಹುದು ಎಂಬುದನ್ನು ನೆನಪಿನಲ್ಲಿಡಿ. ಕೆಲವು ವಿಜೆಟ್‌ಗಳು ನಿಮ್ಮ ಲಾಕ್ ಸ್ಕ್ರೀನ್‌ಗಾಗಿ ಉದ್ದೇಶಿಸದೇ ಇರಬಹುದು ಮತ್ತು ಇಲ್ಲಿ ಸೇರಿಸುವುದು ಸುರಕ್ಷಿತವಲ್ಲದಿರಬಹುದು."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"ಅರ್ಥವಾಯಿತು"</string>
@@ -548,8 +565,8 @@
     <string name="media_projection_action_text" msgid="3634906766918186440">"ಈಗ ಪ್ರಾರಂಭಿಸಿ"</string>
     <string name="empty_shade_text" msgid="8935967157319717412">"ಯಾವುದೇ ಅಧಿಸೂಚನೆಗಳಿಲ್ಲ"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"ಯಾವುದೇ ಹೊಸ ಅಧಿಸೂಚನೆಗಳಿಲ್ಲ"</string>
-    <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"ಅಡಾಪ್ಟಿವ್ ನೋಟಿಫಿಕೇಶನ್ ಆನ್ ಇದೆ"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"ಅಲ್ಪಾವಧಿಯಲ್ಲಿ ಹಲವು ನೋಟಿಫಿಕೇಶನ್ ಬಂದಾಗ 2 ನಿಮಿಷದವರೆಗೆ ಸಾಧನವು ಈಗ ವಾಲ್ಯೂಮ್ ಕಡಿಮೆ ಮಾಡುತ್ತದೆ ಹಾಗೂ ಸ್ಕ್ರೀನ್ ಮೇಲಿನ ಪಾಪ್-ಅಪ್ ಕಡಿಮೆ ಮಾಡುತ್ತದೆ."</string>
+    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"ನೋಟಿಫಿಕೇಶನ್ ಕೂಲ್‌ಡೌನ್ ಆನ್ ಆಗಿದೆ"</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"ನೀವು ಏಕಕಾಲದಲ್ಲಿ ತೀರಾ ಹೆಚ್ಚು ನೋಟಿಫಿಕೇಶನ್‌ಗಳನ್ನು ಪಡೆದಾಗ 2 ನಿಮಿಷಗಳವರೆಗೆ ನಿಮ್ಮ ಸಾಧನದ ವಾಲ್ಯೂಮ್ ಮತ್ತು ಅಲರ್ಟ್‌ಗಳನ್ನು ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಕಡಿಮೆ ಮಾಡಲಾಗುತ್ತದೆ."</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"ಆಫ್ ಮಾಡಿ"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"ಹಳೆಯ ಅಧಿಸೂಚನೆಗಳನ್ನು ನೋಡಲು ಅನ್‌ಲಾಕ್ ಮಾಡಿ"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"ಈ ಸಾಧನವನ್ನು ನಿಮ್ಮ ಪೋಷಕರು ನಿರ್ವಹಿಸುತ್ತಿದ್ದಾರೆ"</string>
@@ -716,8 +733,7 @@
     <string name="notification_alert_title" msgid="3656229781017543655">"ಡೀಫಾಲ್ಟ್"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"ಸ್ವಯಂಚಾಲಿತ"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"ಯಾವುದೇ ಧ್ವನಿ ಅಥವಾ ವೈಬ್ರೇಷನ್‌ ಆಗುವುದಿಲ್ಲ"</string>
-    <!-- no translation found for notification_conversation_summary_low (3855696451919728790) -->
-    <skip />
+    <string name="notification_conversation_summary_low" msgid="3855696451919728790">"ಯಾವುದೇ ಸೌಂಡ್ ಅಥವಾ ವೈಬ್ರೇಷನ್‌ ಇಲ್ಲ, ಆದರೆ ಇದು ಇನ್ನೂ ಸಂಭಾಷಣೆಗಳ ವಿಭಾಗದಲ್ಲಿ ತೋರಿಸುತ್ತದೆ"</string>
     <string name="notification_channel_summary_default" msgid="777294388712200605">"ಸೆಟ್ಟಿಂಗ್‌ಗಳನ್ನು ಆಧರಿಸಿ ಸಾಧನ ರಿಂಗ್ ಅಥವಾ ವೈಬ್ರೇಟ್ ಆಗುತ್ತದೆ"</string>
     <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"ಸೆಟ್ಟಿಂಗ್‌ಗಳನ್ನು ಆಧರಿಸಿ ಫೋನ್ ರಿಂಗ್ ಅಥವಾ ವೈಬ್ರೇಟ್ ಆಗುತ್ತದೆ. ಡಿಫಾಲ್ಟ್ ಆಗಿ, <xliff:g id="APP_NAME">%1$s</xliff:g> ಬಬಲ್ ಸಂಭಾಷಣೆಗಳು."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"ಈ ಅಧಿಸೂಚನೆಯು ಶಬ್ದ ಮಾಡಬೇಕೇ ಅಥವಾ ವೈಬ್ರೇಟ್ ಮಾಡಬೇಕೇ ಎಂಬುದನ್ನು ನಿರ್ಧರಿಸುವ ಅವಕಾಶವನ್ನು ಸಿಸ್ಟಂಗೆ ನೀಡಿ"</string>
@@ -1216,7 +1232,6 @@
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"ದೃಢೀಕರಣದ ಅವಶ್ಯಕತೆಯಿದೆ. ದೃಢೀಕರಿಸಲು ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಸೆನ್ಸರ್ ಅನ್ನು ಸ್ಪರ್ಶಿಸಿ."</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"ಚಾಲ್ತಿಯಲ್ಲಿರುವ ಫೋನ್ ಕರೆ"</string>
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"ಮೊಬೈಲ್ ಡೇಟಾ"</string>
-    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"ಕನೆಕ್ಟ್ ಆಗಿದೆ"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"ತಾತ್ಕಾಲಿಕವಾಗಿ ಕನೆಕ್ಟ್ ಮಾಡಲಾಗಿದೆ"</string>
     <string name="mobile_data_poor_connection" msgid="819617772268371434">"ಕಳಪೆ ಸಂಪರ್ಕ"</string>
@@ -1370,7 +1385,10 @@
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"ಮುಗಿದಿದೆ"</string>
     <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"ಭೇಷ್!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"ಹಿಂತಿರುಗಿ"</string>
-    <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"ಹಿಂತಿರುಗಲು, ಟಚ್‌ಪ್ಯಾಡ್‌ನಲ್ಲಿ ಎಲ್ಲಿಯಾದರೂ ಮೂರು ಬೆರಳುಗಳನ್ನು ಬಳಸಿ ಎಡ ಅಥವಾ ಬಲಕ್ಕೆ ಸ್ವೈಪ್ ಮಾಡಿ."</string>
+    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
+    <skip />
+    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
+    <skip />
     <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"ಮೂರು ಬೆರಳುಗಳು ಬಲಕ್ಕೆ ಮತ್ತು ಎಡಕ್ಕೆ ಚಲಿಸುತ್ತಿರುವುದನ್ನು ತೋರಿಸುತ್ತಿರುವ ಟಚ್‌ಪ್ಯಾಡ್"</string>
     <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"ಬ್ಯಾಕ್ ಗೆಸ್ಚರ್‌ಗೆ ಸಂಬಂಧಿಸಿದ ಆ್ಯನಿಮೇಶನ್ ಅನ್ನು ತೋರಿಸುತ್ತಿರುವ ಸಾಧನದ ಸ್ಕ್ರೀನ್"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"ಕೀಬೋರ್ಡ್ ಬ್ಯಾಕ್‌ಲೈಟ್"</string>
@@ -1378,4 +1396,16 @@
     <string name="home_controls_dream_label" msgid="6567105701292324257">"ಮನೆ ನಿಯಂತ್ರಣಗಳು"</string>
     <string name="home_controls_dream_description" msgid="4644150952104035789">"ಮನೆ ನಿಯಂತ್ರಣವನ್ನು ಸ್ಕ್ರೀನ್‌ಸೇವರ್‌ನಂತೆ ಬೇಗ ಆ್ಯಕ್ಸೆಸ್ ಮಾಡಿ"</string>
     <string name="volume_undo_action" msgid="5815519725211877114">"ರದ್ದುಗೊಳಿಸಿ"</string>
+    <string name="back_edu_toast_content" msgid="4530314597378982956">"ಹಿಂತಿರುಗಲು, ಟಚ್‌ಪ್ಯಾಡ್‌ನಲ್ಲಿ ಮೂರು ಬೆರಳುಗಳಿಂದ ಎಡಕ್ಕೆ ಅಥವಾ ಬಲಕ್ಕೆ ಸ್ವೈಪ್ ಮಾಡಿ"</string>
+    <string name="home_edu_toast_content" msgid="3381071147871955415">"ಹೋಮ್‌ಗೆ ಹೋಗಲು, ಟಚ್‌ಪ್ಯಾಡ್‌ನಲ್ಲಿ ಮೂರು ಬೆರಳುಗಳಿಂದ ಮೇಲಕ್ಕೆ ಸ್ವೈಪ್ ಮಾಡಿ"</string>
+    <string name="overview_edu_toast_content" msgid="5797030644017804518">"ಇತ್ತೀಚಿನ ಆ್ಯಪ್‌ಗಳನ್ನು ನೋಡಲು, ಟಚ್‌ಪ್ಯಾಡ್‌ನಲ್ಲಿ ಮೂರು ಬೆರಳುಗಳಿಂದ ಮೇಲಕ್ಕೆ ಸ್ವೈಪ್ ಮಾಡಿ ಹಾಗೂ ಹೋಲ್ಡ್ ಮಾಡಿ"</string>
+    <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"ನಿಮ್ಮ ಎಲ್ಲಾ ಆ್ಯಪ್‌ಗಳನ್ನು ವೀಕ್ಷಿಸಲು, ನಿಮ್ಮ ಕೀಬೋರ್ಡ್‌ನಲ್ಲಿರುವ ಆ್ಯಕ್ಷನ್‌ ಕೀಯನ್ನು ಒತ್ತಿರಿ"</string>
+    <string name="back_edu_notification_title" msgid="5624780717751357278">"ಹಿಂತಿರುಗಲು ನಿಮ್ಮ ಟಚ್‌ಪ್ಯಾಡ್ ಅನ್ನು ಬಳಸಿ"</string>
+    <string name="back_edu_notification_content" msgid="2497557451540954068">"ಮೂರು ಬೆರಳುಗಳಿಂದ ಎಡಕ್ಕೆ ಅಥವಾ ಬಲಕ್ಕೆ ಸ್ವೈಪ್ ಮಾಡಿ. ಇನ್ನಷ್ಟು ಗೆಸ್ಚರ್‌ಗಳನ್ನು ತಿಳಿಯಲು ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
+    <string name="home_edu_notification_title" msgid="6097902076909654045">"ಹೋಮ್‌ಗೆ ಹೋಗಲು ನಿಮ್ಮ ಟಚ್‌ಪ್ಯಾಡ್ ಅನ್ನು ಬಳಸಿ"</string>
+    <string name="home_edu_notification_content" msgid="6631697734535766588">"ಮೂರು ಬೆರಳುಗಳಿಂದ ಮೇಲಕ್ಕೆ ಸ್ವೈಪ್ ಮಾಡಿ. ಇನ್ನಷ್ಟು ಗೆಸ್ಚರ್‌ಗಳನ್ನು ತಿಳಿಯಲು ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
+    <string name="overview_edu_notification_title" msgid="1265824157319562406">"ಇತ್ತೀಚಿನ ಆ್ಯಪ್‌ಗಳನ್ನು ವೀಕ್ಷಿಸಲು ನಿಮ್ಮ ಟಚ್‌ಪ್ಯಾಡ್ ಅನ್ನು ಬಳಸಿ"</string>
+    <string name="overview_edu_notification_content" msgid="3578204677648432500">"ಮೂರು ಬೆರಳುಗಳಿಂದ ಮೇಲಕ್ಕೆ ಸ್ವೈಪ್ ಮಾಡಿ ಹಾಗೂ ಹೋಲ್ಡ್ ಮಾಡಿ. ಇನ್ನಷ್ಟು ಗೆಸ್ಚರ್‌ಗಳನ್ನು ತಿಳಿಯಲು ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
+    <string name="all_apps_edu_notification_title" msgid="372262997265569063">"ಎಲ್ಲಾ ಆ್ಯಪ್‌ಗಳನ್ನು ವೀಕ್ಷಿಸಲು ನಿಮ್ಮ ಕೀಬೋರ್ಡ್ ಅನ್ನು ಬಳಸಿ"</string>
+    <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"ಯಾವಾಗ ಬೇಕಾದರೂ ಆ್ಯಕ್ಷನ್‌ ಕೀಯನ್ನು ಒತ್ತಿರಿ. ಇನ್ನಷ್ಟು ಗೆಸ್ಚರ್‌ಗಳನ್ನು ತಿಳಿಯಲು ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-kn/tiles_states_strings.xml b/packages/SystemUI/res/values-kn/tiles_states_strings.xml
index b2a7523..c7cb2b1 100644
--- a/packages/SystemUI/res/values-kn/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-kn/tiles_states_strings.xml
@@ -56,11 +56,6 @@
     <item msgid="5376619709702103243">"ಆಫ್"</item>
     <item msgid="4875147066469902392">"ಆನ್"</item>
   </string-array>
-  <string-array name="tile_states_modes">
-    <item msgid="7764936419245199023">"ಲಭ್ಯವಿಲ್ಲ"</item>
-    <item msgid="2004750556637773692">"ಆಫ್ ಆಗಿದೆ"</item>
-    <item msgid="8968530753931637871">"ಆನ್ ಆಗಿದೆ"</item>
-  </string-array>
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"ಲಭ್ಯವಿಲ್ಲ"</item>
     <item msgid="5044688398303285224">"ಆಫ್"</item>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index 7647486..b904fa8 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -126,38 +126,25 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"탭하여 보기"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"화면 녹화 저장 중에 오류가 발생했습니다."</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"화면 녹화 시작 중 오류 발생"</string>
-    <!-- no translation found for screenrecord_stop_dialog_title (8716193661764511095) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message (6262768207331626817) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5995770227684523244) -->
-    <skip />
+    <string name="screenrecord_stop_dialog_title" msgid="8716193661764511095">"녹화를 중지하시겠습니까?"</string>
+    <string name="screenrecord_stop_dialog_message" msgid="6262768207331626817">"현재 전체 화면을 녹화 중입니다."</string>
+    <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"현재 <xliff:g id="APP_NAME">%1$s</xliff:g>의 콘텐츠를 녹화 중입니다"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"녹화 중지"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"화면 공유 중"</string>
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"화면 공유를 중지하시겠습니까?"</string>
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen_with_host_app (522823522115375414) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen (5090115386271179270) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_specific (5923772039347985172) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_generic (6681016774654578261) -->
-    <skip />
+    <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"현재 전체 화면을 <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>과 공유 중입니다"</string>
+    <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"현재 전체 화면을 앱과 공유 중입니다"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"현재 <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>의 콘텐츠를 공유 중입니다"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"현재 앱을 공유 중입니다"</string>
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"공유 중지"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"화면 전송 중"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"전송을 중지할까요?"</string>
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen_with_device (1474703115926205251) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen (8419219169553867625) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app_with_device (2715934698604085519) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (8616103075630934513) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic_with_device (9213582497852420203) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic (4100272100480415076) -->
-    <skip />
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen_with_device" msgid="1474703115926205251">"현재 전체 화면을 <xliff:g id="DEVICE_NAME">%1$s</xliff:g>로 전송 중입니다."</string>
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen" msgid="8419219169553867625">"현재 전체 화면을 근처 기기로 전송 중입니다."</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app_with_device" msgid="2715934698604085519">"현재 <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>의 콘텐츠를 <xliff:g id="DEVICE_NAME">%2$s</xliff:g>로 전송 중입니다."</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="8616103075630934513">"현재 <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>의 콘텐츠를 근처 기기로 전송 중입니다."</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic_with_device" msgid="9213582497852420203">"현재 <xliff:g id="DEVICE_NAME">%1$s</xliff:g>로 전송 중입니다."</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic" msgid="4100272100480415076">"현재 근처 기기로 전송 중입니다."</string>
     <string name="cast_to_other_device_stop_dialog_button" msgid="6420183747435521834">"전송 중지"</string>
     <string name="close_dialog_button" msgid="4749497706540104133">"닫기"</string>
     <string name="issuerecord_title" msgid="286627115110121849">"문제 녹화 도구"</string>
@@ -303,8 +290,7 @@
     <string name="start_dreams" msgid="9131802557946276718">"화면 보호기"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"이더넷"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"방해 금지 모드"</string>
-    <!-- no translation found for quick_settings_modes_label (5407025818652750501) -->
-    <skip />
+    <string name="quick_settings_modes_label" msgid="5407025818652750501">"우선순위 모드"</string>
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"블루투스"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"페어링된 기기가 없습니다"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"기기를 연결 또는 연결 해제하려면 탭하세요"</string>
@@ -402,6 +388,9 @@
     <string name="performance" msgid="6552785217174378320">"성능"</string>
     <string name="user_interface" msgid="3712869377953950887">"사용자 인터페이스"</string>
     <string name="thermal" msgid="6758074791325414831">"열화상"</string>
+    <string name="custom" msgid="3337456985275158299">"맞춤"</string>
+    <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"맞춤 트레이스 설정"</string>
+    <string name="restore_default" msgid="5259420807486239755">"기본값 복원"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"한 손 사용 모드"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"청각 보조 기기"</string>
     <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"활성"</string>
@@ -440,6 +429,17 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"설정 열기"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"기타 기기"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"최근 사용 버튼 전환"</string>
+    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"우선순위 모드"</string>
+    <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_off" msgid="1736604456618147306">"사용 안함"</string>
+    <!-- no translation found for zen_mode_set_up (7457957033034460064) -->
+    <skip />
+    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
+    <skip />
+    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
+    <skip />
     <string name="zen_priority_introduction" msgid="3159291973383796646">"알람, 알림, 일정 및 지정한 발신자로부터 받은 전화를 제외한 소리와 진동을 끕니다. 음악, 동영상, 게임 등 재생하도록 선택한 소리는 정상적으로 들립니다."</string>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"알람을 제외한 소리와 진동을 끕니다. 음악, 동영상, 게임 등 재생하도록 선택한 소리는 정상적으로 들립니다."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"맞춤설정"</string>
@@ -478,6 +478,8 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • 저속 충전 중 • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> 후 충전 완료"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • 충전 중 • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> 후 충전 완료"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"잠금 화면의 위젯"</string>
+    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
+    <skip />
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"공동 튜토리얼을 시작하려면 왼쪽으로 스와이프하세요"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"맞춤설정"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"닫기"</string>
@@ -504,10 +506,9 @@
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"위젯 선택"</string>
     <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"위젯 삭제"</string>
     <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"선택한 위젯 배치"</string>
-    <!-- no translation found for communal_widget_picker_title (1953369090475731663) -->
-    <skip />
-    <!-- no translation found for communal_widget_picker_description (490515450110487871) -->
-    <skip />
+    <string name="communal_widget_picker_title" msgid="1953369090475731663">"잠금 화면 위젯"</string>
+    <string name="communal_widget_picker_description" msgid="490515450110487871">"태블릿이 잠겨 있어도 누구나 잠금 화면에서 위젯을 볼 수 있습니다."</string>
+    <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"위젯 선택 해제"</string>
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"잠금 화면 위젯"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"위젯을 사용하여 앱을 열려면 본인 인증을 해야 합니다. 또한 태블릿이 잠겨 있더라도 누구나 볼 수 있다는 점을 유의해야 합니다. 일부 위젯은 잠금 화면에 적합하지 않고 여기에 추가하기에 안전하지 않을 수 있습니다."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"확인"</string>
@@ -564,8 +565,8 @@
     <string name="media_projection_action_text" msgid="3634906766918186440">"시작하기"</string>
     <string name="empty_shade_text" msgid="8935967157319717412">"알림 없음"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"새로운 알림 없음"</string>
-    <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"적응형 알림 사용 중"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"이제 짧은 시간 동안 많은 알림을 받으면 최대 2분 동안 기기의 볼륨을 낮추고 화면의 팝업을 줄입니다."</string>
+    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"알림 쿨다운 사용 중"</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"한 번에 너무 많은 알림을 받으면 최대 2분간 자동으로 기기 볼륨이 줄어들고 알림이 최소화됩니다."</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"사용 중지"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"잠금 해제하여 이전 알림 보기"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"부모님이 관리하는 기기입니다."</string>
@@ -732,8 +733,7 @@
     <string name="notification_alert_title" msgid="3656229781017543655">"기본값"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"자동"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"소리 또는 진동 없음"</string>
-    <!-- no translation found for notification_conversation_summary_low (3855696451919728790) -->
-    <skip />
+    <string name="notification_conversation_summary_low" msgid="3855696451919728790">"소리나 진동이 울리지 않지만 대화 섹션에는 표시됨"</string>
     <string name="notification_channel_summary_default" msgid="777294388712200605">"기기 설정에 따라 벨소리나 진동이 울릴 수 있음"</string>
     <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"기기 설정에 따라 벨소리나 진동이 울릴 수 있습니다. 기본적으로 <xliff:g id="APP_NAME">%1$s</xliff:g>의 대화는 대화창으로 표시됩니다."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"시스템에서 알림 시 소리 또는 진동을 사용할지 결정하도록 허용합니다."</string>
@@ -790,8 +790,7 @@
     <string name="keyboard_key_page_up" msgid="173914303254199845">"Page Up"</string>
     <string name="keyboard_key_page_down" msgid="9035902490071829731">"Page Down"</string>
     <string name="keyboard_key_forward_del" msgid="5325501825762733459">"Delete"</string>
-    <!-- no translation found for keyboard_key_esc (6230365950511411322) -->
-    <skip />
+    <string name="keyboard_key_esc" msgid="6230365950511411322">"Esc"</string>
     <string name="keyboard_key_move_home" msgid="3496502501803911971">"Home"</string>
     <string name="keyboard_key_move_end" msgid="99190401463834854">"End"</string>
     <string name="keyboard_key_insert" msgid="4621692715704410493">"Insert"</string>
@@ -1233,7 +1232,6 @@
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"인증이 필요합니다. 지문 센서를 터치하여 인증하세요."</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"진행 중인 전화 통화"</string>
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"모바일 데이터"</string>
-    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"연결됨"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"일시적으로 연결됨"</string>
     <string name="mobile_data_poor_connection" msgid="819617772268371434">"연결 상태 나쁨"</string>
@@ -1374,8 +1372,7 @@
     <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>
-    <!-- no translation found for shortcut_helper_category_current_app_shortcuts (4017840565974573628) -->
-    <skip />
+    <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_search_placeholder" msgid="5488547526269871819">"검색 바로가기"</string>
@@ -1388,13 +1385,27 @@
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"완료"</string>
     <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"아주 좋습니다."</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"뒤로"</string>
-    <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"돌아가려면 세 손가락을 사용해 터치패드의 아무 곳이나 왼쪽 또는 오른쪽으로 스와이프합니다."</string>
+    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
+    <skip />
+    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
+    <skip />
     <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"터치패드에서 세 손가락을 좌우로 움직이는 모습"</string>
     <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"뒤로 동작 애니메이션을 보여 주는 기기 화면"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"키보드 백라이트"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d단계 중 %1$d단계"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"홈 컨트롤"</string>
     <string name="home_controls_dream_description" msgid="4644150952104035789">"화면 보호기로 홈 컨트롤에 빠르게 액세스하기"</string>
-    <!-- no translation found for volume_undo_action (5815519725211877114) -->
-    <skip />
+    <string name="volume_undo_action" msgid="5815519725211877114">"실행취소"</string>
+    <string name="back_edu_toast_content" msgid="4530314597378982956">"돌아가려면 터치패드에서 세 손가락을 사용해 왼쪽 또는 오른쪽으로 스와이프하세요"</string>
+    <string name="home_edu_toast_content" msgid="3381071147871955415">"홈으로 이동하려면 터치패드에서 세 손가락을 사용해 위로 스와이프합니다"</string>
+    <string name="overview_edu_toast_content" msgid="5797030644017804518">"최근 앱을 보려면 터치패드에서 세 손가락으로 위로 스와이프한 후 잠시 기다리세요"</string>
+    <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"모든 앱을 보려면 키보드의 작업 키를 누르세요"</string>
+    <string name="back_edu_notification_title" msgid="5624780717751357278">"터치패드를 사용하여 돌아가기"</string>
+    <string name="back_edu_notification_content" msgid="2497557451540954068">"세 손가락을 사용해 왼쪽 또는 오른쪽으로 스와이프하세요. 더 많은 동작을 알아보려면 탭하세요."</string>
+    <string name="home_edu_notification_title" msgid="6097902076909654045">"터치패드를 사용하여 홈으로 이동"</string>
+    <string name="home_edu_notification_content" msgid="6631697734535766588">"세 손가락을 사용해 위로 스와이프하세요. 더 많은 동작을 알아보려면 탭하세요."</string>
+    <string name="overview_edu_notification_title" msgid="1265824157319562406">"터치패드를 사용하여 최근 앱 보기"</string>
+    <string name="overview_edu_notification_content" msgid="3578204677648432500">"세 손가락을 사용해 위로 스와이프한 다음 잠시 기다리세요. 더 많은 동작을 알아보려면 탭하세요."</string>
+    <string name="all_apps_edu_notification_title" msgid="372262997265569063">"키보드를 사용하여 모든 앱 보기"</string>
+    <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"언제든지 작업 키를 누릅니다. 더 많은 동작을 알아보려면 탭하세요."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ko/tiles_states_strings.xml b/packages/SystemUI/res/values-ko/tiles_states_strings.xml
index 9116085..bc4740d 100644
--- a/packages/SystemUI/res/values-ko/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ko/tiles_states_strings.xml
@@ -56,9 +56,6 @@
     <item msgid="5376619709702103243">"꺼짐"</item>
     <item msgid="4875147066469902392">"켜짐"</item>
   </string-array>
-    <!-- no translation found for tile_states_modes:0 (7764936419245199023) -->
-    <!-- no translation found for tile_states_modes:1 (2004750556637773692) -->
-    <!-- no translation found for tile_states_modes:2 (8968530753931637871) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"이용 불가"</item>
     <item msgid="5044688398303285224">"꺼짐"</item>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index d1aa663..7cea137 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -290,8 +290,7 @@
     <string name="start_dreams" msgid="9131802557946276718">"Көшөгө"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"Тынчымды алба"</string>
-    <!-- no translation found for quick_settings_modes_label (5407025818652750501) -->
-    <skip />
+    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Маанилүүлүк режимдери"</string>
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Жупташкан түзмөктөр жок"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Түзмөктү туташтыруу же ажыратуу үчүн таптаңыз"</string>
@@ -389,6 +388,9 @@
     <string name="performance" msgid="6552785217174378320">"Иштин майнаптуулугу"</string>
     <string name="user_interface" msgid="3712869377953950887">"Колдонуучунун интерфейси"</string>
     <string name="thermal" msgid="6758074791325414831">"Жылуулук"</string>
+    <string name="custom" msgid="3337456985275158299">"Жеке"</string>
+    <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Көз салуунун жеке параметрлери"</string>
+    <string name="restore_default" msgid="5259420807486239755">"Демейки параметрди калыбына келтирүү"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Бир кол режими"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Угуу аппараттары"</string>
     <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Иштеп жатат"</string>
@@ -427,6 +429,17 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Параметрлерди ачуу"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Башка түзмөк"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Назар режимин өчүрүү/күйгүзүү"</string>
+    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Маанилүүлүк режимдери"</string>
+    <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_off" msgid="1736604456618147306">"Өчүк"</string>
+    <!-- no translation found for zen_mode_set_up (7457957033034460064) -->
+    <skip />
+    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
+    <skip />
+    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
+    <skip />
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Ойготкучтардан, эскертүүлөрдөн, жылнаамадагы иш-чараларды эстеткичтерден жана белгиленген байланыштардын чалууларынан тышкары башка үндөр жана дирилдөөлөр тынчыңызды албайт. Бирок ойнотулуп жаткан музыканы, видеолорду жана оюндарды мурдагыдай эле уга бересиз."</string>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Ойготкучтардан башка үндөр жана дирилдөөлөр тынчыңызды албайт. Бирок ойнотулуп жаткан музыканы, видеолорду жана оюндарды мурдагыдай эле уга бересиз."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Ыңгайлаштыруу"</string>
@@ -465,6 +478,8 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Жай кубатталууда • Толгонго чейин <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> калды"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Кубатталууда • Толгонго чейин <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> калды"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Кулпуланган экрандагы виджеттер"</string>
+    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
+    <skip />
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Жалпы үйрөткүчтү иштетүү үчүн солго сүрүңүз"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Ыңгайлаштыруу"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Жабуу"</string>
@@ -493,6 +508,7 @@
     <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"тандалган виджетти жайгаштыруу"</string>
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"Кулпуланган экрандагы виджеттер"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"Планшетиңиз кулпуланган болсо да, кулпуланган экраныңыздан виджеттерди бардыгы көрө алат."</string>
+    <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"виджетти тандоодон чыгаруу"</string>
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Кулпуланган экрандагы виджеттер"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Колдонмону виджет аркылуу ачуу үчүн бул сиз экениңизди ырасташыңыз керек. Аларды планшетиңиз кулпуланып турса да, баары көрө аларын эске алыңыз. Айрым виджеттер кулпуланган экранда колдонууга арналган эмес жана аларды бул жерге кошуу кооптуу болушу мүмкүн."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Түшүндүм"</string>
@@ -549,8 +565,8 @@
     <string name="media_projection_action_text" msgid="3634906766918186440">"Азыр баштоо"</string>
     <string name="empty_shade_text" msgid="8935967157319717412">"Билдирме жок"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"Жаңы билдирмелер жок"</string>
-    <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"Адаптивдүү билдирмелер күйүк"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"Эгер кыска убакыттын ичинде билдирмелер көп келсе, үн көлөмү жана экрандагы калкыма терезелердин саны эки мүнөткө чейин азайтылат."</string>
+    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Билдирмелердин үнүн басаңдатуу күйүк"</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Бир убакта өтө көп билдирмелер келгенде, түзмөктүн үнү жана эскертүүлөрдүн саны 2 мүнөткө азайтылат."</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Өчүрүү"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Билдирмелерди көрүү үчүн кулпуну ачыңыз"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Бул түзмөктү ата-энең башкарат"</string>
@@ -717,8 +733,7 @@
     <string name="notification_alert_title" msgid="3656229781017543655">"Демейки"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Автоматтык"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"Үнү чыкпайт жана дирилдебейт"</string>
-    <!-- no translation found for notification_conversation_summary_low (3855696451919728790) -->
-    <skip />
+    <string name="notification_conversation_summary_low" msgid="3855696451919728790">"Үнү өчүрүлөт же дирилдебейт, бирок маектер бөлүмүндө көрүнө берет"</string>
     <string name="notification_channel_summary_default" msgid="777294388712200605">"Түзмөктүн параметрлерине жараша шыңгырап же дирилдеши мүмкүн"</string>
     <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Түзмөктүн параметрлерине жараша шыңгырап же дирилдеши мүмкүн. <xliff:g id="APP_NAME">%1$s</xliff:g> колдонмосундагы сүйлөшүүлөр демейки шартта калкып чыкма билдирмелер болуп көрүнөт."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Билдирменин үнүн чыгартууну же басууну системага тапшырыңыз"</string>
@@ -1217,7 +1232,6 @@
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Аныктыкты текшерүү талап кылынат. Аныктыгын текшерүү үчүн манжа изинин сенсоруна тийип коюңуз."</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Учурдагы телефон чалуу"</string>
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Мобилдик трафик"</string>
-    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Туташты"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Убактылуу туташып турат"</string>
     <string name="mobile_data_poor_connection" msgid="819617772268371434">"Байланыш начар"</string>
@@ -1358,8 +1372,7 @@
     <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>
-    <!-- no translation found for shortcut_helper_category_current_app_shortcuts (4017840565974573628) -->
-    <skip />
+    <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_search_placeholder" msgid="5488547526269871819">"Ыкчам баскычтарды издөө"</string>
@@ -1372,13 +1385,27 @@
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Бүттү"</string>
     <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Азаматсыз!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Артка кайтуу"</string>
-    <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"Кайтуу үчүн сенсордук тактанын каалаган жерин үч манжаңыз менен солго же оңго сүрүңүз."</string>
+    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
+    <skip />
+    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
+    <skip />
     <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Үч манжанын оңго жана солго жылып жатканы көрсөтүлгөн сенсордук такта"</string>
     <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Артка жаңсоосунун анимациясы көрсөтүлгөн түзмөктүн экраны"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Баскычтоптун жарыгы"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d ичинен %1$d-деңгээл"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Үйдөгү түзмөктөрдү тескөө"</string>
     <string name="home_controls_dream_description" msgid="4644150952104035789">"Үйдөгү түзмөктөрдү көшөгөдөн ыкчам тескеңиз"</string>
-    <!-- no translation found for volume_undo_action (5815519725211877114) -->
-    <skip />
+    <string name="volume_undo_action" msgid="5815519725211877114">"Кайтаруу"</string>
+    <string name="back_edu_toast_content" msgid="4530314597378982956">"Артка кайтуу үчүн сенсордук тактаны үч манжаңыз менен солго же оңго сүрүңүз"</string>
+    <string name="home_edu_toast_content" msgid="3381071147871955415">"Башкы бетке өтүү үчүн сенсордук тактаны үч манжаңыз менен өйдө сүрүңүз"</string>
+    <string name="overview_edu_toast_content" msgid="5797030644017804518">"Акыркы колдонмолорду көрүү үчүн сенсордук тактаны үч манжаңыз менен өйдө сүрүп, кармап туруңуз"</string>
+    <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Бардык колдонмолоруңузду көрүү үчүн баскычтобуңуздагы аракет баскычын басыңыз"</string>
+    <string name="back_edu_notification_title" msgid="5624780717751357278">"Артка кайтуу үчүн сенсордук тактаны колдонуңуз"</string>
+    <string name="back_edu_notification_content" msgid="2497557451540954068">"Үч манжаңыз менен солго же оңго сүрүңүз. Башка жаңсоолорду үйрөнүү үчүн таптаңыз."</string>
+    <string name="home_edu_notification_title" msgid="6097902076909654045">"Башкы бетке өтүү үчүн сенсордук тактаны колдонуңуз"</string>
+    <string name="home_edu_notification_content" msgid="6631697734535766588">"Үч манжаңыз менен өйдө сүрүңүз. Башка жаңсоолорду үйрөнүү үчүн таптаңыз."</string>
+    <string name="overview_edu_notification_title" msgid="1265824157319562406">"Акыркы колдонмолорду көрүү үчүн сенсордук тактаны колдонуңуз"</string>
+    <string name="overview_edu_notification_content" msgid="3578204677648432500">"Үч манжаңыз менен өйдө сүрүп, кармап туруңуз. Башка жаңсоолорду үйрөнүү үчүн таптаңыз."</string>
+    <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Бардык колдонмолорду көрүү үчүн баскычтобуңузду колдонуңуз"</string>
+    <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Каалаганда аракет баскычын басыңыз. Башка жаңсоолорду үйрөнүү үчүн таптаңыз."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ky/tiles_states_strings.xml b/packages/SystemUI/res/values-ky/tiles_states_strings.xml
index d92f787..694967e 100644
--- a/packages/SystemUI/res/values-ky/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ky/tiles_states_strings.xml
@@ -56,9 +56,6 @@
     <item msgid="5376619709702103243">"Өчүк"</item>
     <item msgid="4875147066469902392">"Күйүк"</item>
   </string-array>
-    <!-- no translation found for tile_states_modes:0 (7764936419245199023) -->
-    <!-- no translation found for tile_states_modes:1 (2004750556637773692) -->
-    <!-- no translation found for tile_states_modes:2 (8968530753931637871) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Жеткиликсиз"</item>
     <item msgid="5044688398303285224">"Өчүк"</item>
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index 5abef94..2c490ac 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -290,8 +290,7 @@
     <string name="start_dreams" msgid="9131802557946276718">"ພາບພັກໜ້າຈໍ"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"ຫ້າມລົບກວນ"</string>
-    <!-- no translation found for quick_settings_modes_label (5407025818652750501) -->
-    <skip />
+    <string name="quick_settings_modes_label" msgid="5407025818652750501">"ໂໝດຄວາມສຳຄັນ"</string>
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"ບໍ່​ມີ​ອຸ​ປະ​ກອນ​ທີ່​ສາ​ມາດ​ຈັບ​ຄູ່​ໄດ້"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"ແຕະເພື່ອເຊື່ອມຕໍ່ ຫຼື ຕັດການເຊື່ອມຕໍ່ອຸປະກອນ"</string>
@@ -389,6 +388,9 @@
     <string name="performance" msgid="6552785217174378320">"ປະສິດທິພາບ"</string>
     <string name="user_interface" msgid="3712869377953950887">"ສ່ວນຕິດຕໍ່ຜູ້ໃຊ້"</string>
     <string name="thermal" msgid="6758074791325414831">"ຄວາມຮ້ອນ"</string>
+    <string name="custom" msgid="3337456985275158299">"ກຳນົດເອງ"</string>
+    <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"ການຕັ້ງຄ່າການຕິດຕາມແບບກຳນົດເອງ"</string>
+    <string name="restore_default" msgid="5259420807486239755">"ກູ້ຄືນຄ່າເລີ່ມຕົ້ນ"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"ໂໝດມືດຽວ"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"ອຸປະກອນຊ່ວຍຟັງ"</string>
     <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"ນຳໃຊ້ຢູ່"</string>
@@ -427,6 +429,17 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"ເປີດການຕັ້ງຄ່າ"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"ອຸປະກອນອື່ນໆ"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"ສະຫຼັບພາບຮວມ"</string>
+    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"ໂໝດຄວາມສຳຄັນ"</string>
+    <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_off" msgid="1736604456618147306">"ປິດ"</string>
+    <!-- no translation found for zen_mode_set_up (7457957033034460064) -->
+    <skip />
+    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
+    <skip />
+    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
+    <skip />
     <string name="zen_priority_introduction" msgid="3159291973383796646">"ທ່ານຈະບໍ່ໄດ້ຮັບການລົບກວນຈາກສຽງ ແລະ ການສັ່ນເຕືອນ, ຍົກເວັ້ນໃນເວລາໂມງປຸກດັງ, ມີການແຈ້ງເຕືອນ ຫຼື ມີສາຍໂທເຂົ້າຈາກຜູ້ໂທທີ່ທ່ານລະບຸໄວ້. ທ່ານອາດຍັງຄົງໄດ້ຍິນຫາກທ່ານເລືອກຫຼິ້ນເພງ, ວິດີໂອ ແລະ ເກມ."</string>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"ທ່ານຈະບໍ່ໄດ້ຮັບການລົບກວນຈາກສຽງ ແລະ ການສັ່ນເຕືອນ, ຍົກເວັ້ນໃນເວລາໂມງປຸກດັງ. ທ່ານອາດຍັງຄົງໄດ້ຍິນຫາກທ່ານເລືອກຫຼິ້ນເພງ, ວິດີໂອ ແລະ ເກມ."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"ປັບແຕ່ງ"</string>
@@ -465,6 +478,8 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ກຳລັງສາກໄຟແບບຊ້າ • ຈະເຕັມໃນອີກ <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ກຳລັງສາກໄຟ • ຈະເຕັມໃນອີກ <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"ວິດເຈັດຢູ່ໜ້າຈໍລັອກ"</string>
+    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
+    <skip />
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"ປັດຊ້າຍເພື່ອເລີ່ມບົດແນະນຳສ່ວນກາງ"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"ປັບແຕ່ງ"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"ປ່ອຍ​ໄປ"</string>
@@ -493,6 +508,7 @@
     <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"ວາງວິດເຈັດທີ່ເລືອກ"</string>
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"ວິດເຈັດໃນໜ້າຈໍລັອກ"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"ທຸກຄົນສາມາດເບິ່ງວິດເຈັດຢູ່ໜ້າຈໍລັອກຂອງທ່ານໄດ້, ເຖິງແມ່ນວ່າແທັບເລັດຂອງທ່ານຈະລັອກຢູ່ກໍຕາມ."</string>
+    <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"ຍົກເລີກການເລືອກວິດເຈັດ"</string>
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"ວິດເຈັດໃນໜ້າຈໍລັອກ"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"ເພື່ອເປີດແອັບໂດຍໃຊ້ວິດເຈັດ, ທ່ານຈະຕ້ອງຢັ້ງຢືນວ່າແມ່ນທ່ານ. ນອກຈາກນັ້ນ, ກະລຸນາຮັບຊາບວ່າທຸກຄົນສາມາດເບິ່ງຂໍ້ມູນດັ່ງກ່າວໄດ້, ເຖິງແມ່ນວ່າແທັບເລັດຂອງທ່ານຈະລັອກຢູ່ກໍຕາມ. ວິດເຈັດບາງຢ່າງອາດບໍ່ໄດ້ມີໄວ້ສຳລັບໜ້າຈໍລັອກຂອງທ່ານ ແລະ ອາດບໍ່ປອດໄພທີ່ຈະເພີ່ມໃສ່ບ່ອນນີ້."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"ເຂົ້າໃຈແລ້ວ"</string>
@@ -549,8 +565,8 @@
     <string name="media_projection_action_text" msgid="3634906766918186440">"ເລີ່ມດຽວນີ້"</string>
     <string name="empty_shade_text" msgid="8935967157319717412">"ບໍ່ມີການແຈ້ງເຕືອນ"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"ບໍ່ມີການແຈ້ງເຕືອນໃໝ່"</string>
-    <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"ການແຈ້ງເຕືອນແບບປັບອັດຕະໂນມັດເປີດຢູ່"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"ຈາກນີ້ໄປອຸປະກອນຂອງທ່ານຈະຫຼຸດລະດັບສຽງ ແລະ ຈຳນວນປັອບອັບຢູ່ໜ້າຈໍເປັນເວລາສູງສຸດ 2 ນາທີເມື່ອທ່ານໄດ້ຮັບການແຈ້ງເຕືອນຈຳນວນຫຼາຍໃນໄລຍະເວລາສັ້ນໆ."</string>
+    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"ຄູດາວການແຈ້ງເຕືອນເປີດຢູ່"</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"ສຽງ ແລະ ແຈ້ງເຕືອນອຸປະກອນຂອງທ່ານຖືກຫຼຸດລົງໂດຍອັດຕະໂນມັດເປັນເວລາເຖິງ 2 ນາທີເມື່ອທ່ານໄດ້ຮັບການແຈ້ງເຕືອນຫຼາຍເກີນໄປໃນຄັ້ງດຽວ."</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"ປິດ"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"ປົດລັອກເພື່ອເບິ່ງການແຈ້ງເຕືອນເກົ່າ"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"ອຸປະກອນນີ້ແມ່ນຈັດການໂດຍພໍ່ແມ່ຂອງທ່ານ"</string>
@@ -717,8 +733,7 @@
     <string name="notification_alert_title" msgid="3656229781017543655">"ຄ່າເລີ່ມຕົ້ນ"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"ອັດຕະໂນມັດ"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"ບໍ່ມີສຽງ ຫຼື ການສັ່ນເຕືອນ"</string>
-    <!-- no translation found for notification_conversation_summary_low (3855696451919728790) -->
-    <skip />
+    <string name="notification_conversation_summary_low" msgid="3855696451919728790">"ບໍ່ມີສຽງ ຫຼື ການສັ່ນເຕືອນແຕ່ຍັງຄົງປາກົດໃນພາກສ່ວນການສົນທະນາ"</string>
     <string name="notification_channel_summary_default" msgid="777294388712200605">"ອາດສົ່ງສຽງ ຫຼື ສັ່ນເຕືອນໂດຍອ້າງອີງຈາກການຕັ້ງຄ່າອຸປະກອນ"</string>
     <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"ອາດສົ່ງສຽງ ຫຼື ສັ່ນເຕືອນໂດຍອ້າງອີງຈາກການຕັ້ງຄ່າອຸປະກອນ. ການສົນທະນາຈາກ <xliff:g id="APP_NAME">%1$s</xliff:g> ຈະເປັນ bubble ຕາມຄ່າເລີ່ມຕົ້ນ."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"ໃຫ້ລະບົບກຳນົດວ່າການແຈ້ງເຕືອນນິ້ຄວນມີສຽງ ຫຼື ສັ່ນເຕືອນຫຼືບໍ່"</string>
@@ -1217,7 +1232,6 @@
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"ຕ້ອງພິສູດຢືນຢັນ. ແຕະໃສ່ເຊັນເຊີລາຍນິ້ວມືເພື່ອພິສູດຢືນຢັນ."</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"ສາຍໂທອອກ"</string>
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"ອິນເຕີເນັດມືຖື"</string>
-    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"ເຊື່ອມຕໍ່ແລ້ວ"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"ເຊື່ອມຕໍ່ແລ້ວຊົ່ວຄາວ"</string>
     <string name="mobile_data_poor_connection" msgid="819617772268371434">"ສັນຍານເຊື່ອມຕໍ່ຊ້າ"</string>
@@ -1358,8 +1372,7 @@
     <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>
-    <!-- no translation found for shortcut_helper_category_current_app_shortcuts (4017840565974573628) -->
-    <skip />
+    <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_search_placeholder" msgid="5488547526269871819">"ທາງລັດການຊອກຫາ"</string>
@@ -1372,13 +1385,27 @@
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"ແລ້ວໆ"</string>
     <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"ດີຫຼາຍ!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"ກັບຄືນ"</string>
-    <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"ເພື່ອກັບຄືນ, ໃຫ້ໃຊ້ 3 ນິ້ວປັດຊ້າຍ ຫຼື ຂວາບ່ອນໃດກໍໄດ້ເທິງແຜ່ນສໍາຜັດ."</string>
+    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
+    <skip />
+    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
+    <skip />
     <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"ແຜ່ນສໍາຜັດສະແດງພາບ 3 ນິ້ວເລື່ອນໄປທາງຂວາ ແລະ ຊ້າຍ"</string>
     <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"ໜ້າຈໍອຸປະກອນສະແດງພາບເຄື່ອນໄຫວຂອງທ່າທາງກັບຄືນ"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"ໄຟປຸ່ມແປ້ນພິມ"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"ລະດັບທີ %1$d ຈາກ %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"ການຄວບຄຸມເຮືອນ"</string>
     <string name="home_controls_dream_description" msgid="4644150952104035789">"ເຂົ້າເຖິງການຄວບຄຸມເຮືອນຂອງທ່ານໄດ້ຢ່າງວ່ອງໄວຢູ່ພາບພັກໜ້າຈໍ"</string>
-    <!-- no translation found for volume_undo_action (5815519725211877114) -->
-    <skip />
+    <string name="volume_undo_action" msgid="5815519725211877114">"ຍົກເລີກ"</string>
+    <string name="back_edu_toast_content" msgid="4530314597378982956">"ເພື່ອກັບຄືນ, ໃຫ້ໃຊ້ 3 ນິ້ວປັດຊ້າຍ ຫຼື ຂວາເທິງແຜ່ນສໍາຜັດ"</string>
+    <string name="home_edu_toast_content" msgid="3381071147871955415">"ເພື່ອໄປຫາໜ້າຫຼັກ, ໃຫ້ໃຊ້ 3 ນິ້ວປັດຂຶ້ນເທິງແຜ່ນສໍາຜັດ"</string>
+    <string name="overview_edu_toast_content" msgid="5797030644017804518">"ເພື່ອເບິ່ງແອັບຫຼ້າສຸດ, ໃຫ້ໃຊ້ 3 ນິ້ວປັດຂຶ້ນ ແລ້ວຄ້າງໄວ້ເທິງແຜ່ນສໍາຜັດ"</string>
+    <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"ເພື່ອເບິ່ງແອັບທັງໝົດຂອງທ່ານ, ໃຫ້ກົດປຸ່ມຄຳສັ່ງຢູ່ແປ້ນພິມຂອງທ່ານ"</string>
+    <string name="back_edu_notification_title" msgid="5624780717751357278">"ໃຊ້ແຜ່ນສໍາຜັດຂອງທ່ານເພື່ອກັບຄືນ"</string>
+    <string name="back_edu_notification_content" msgid="2497557451540954068">"ໃຊ້ 3 ນິ້ວປັດຊ້າຍ ຫຼື ຂວາ. ແຕະເພື່ອສຶກສາທ່າທາງເພີ່ມເຕີມ."</string>
+    <string name="home_edu_notification_title" msgid="6097902076909654045">"ໃຊ້ແຜ່ນສໍາຜັດຂອງທ່ານເພື່ອໄປຫາໜ້າຫຼັກ"</string>
+    <string name="home_edu_notification_content" msgid="6631697734535766588">"ໃຊ້ 3 ນິ້ວປັດຂຶ້ນ. ແຕະເພື່ອສຶກສາທ່າທາງເພີ່ມເຕີມ."</string>
+    <string name="overview_edu_notification_title" msgid="1265824157319562406">"ໃຊ້ແຜ່ນສໍາຜັດຂອງທ່ານເພື່ອເບິ່ງແອັບຫຼ້າສຸດ"</string>
+    <string name="overview_edu_notification_content" msgid="3578204677648432500">"ໃຊ້ 3 ນິ້ວປັດຂຶ້ນ ແລ້ວຄ້າງໄວ້. ແຕະເພື່ອສຶກສາທ່າທາງເພີ່ມເຕີມ."</string>
+    <string name="all_apps_edu_notification_title" msgid="372262997265569063">"ໃຊ້ແປ້ນພິມຂອງທ່ານເພື່ອເບິ່ງແອັບທັງໝົດ"</string>
+    <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"ກົດປຸ່ມຄຳສັ່ງໄດ້ທຸກເວລາ. ແຕະເພື່ອສຶກສາທ່າທາງເພີ່ມເຕີມ."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-lo/tiles_states_strings.xml b/packages/SystemUI/res/values-lo/tiles_states_strings.xml
index f33514a..9386e00 100644
--- a/packages/SystemUI/res/values-lo/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-lo/tiles_states_strings.xml
@@ -56,9 +56,6 @@
     <item msgid="5376619709702103243">"ປິດ"</item>
     <item msgid="4875147066469902392">"ເປີດ"</item>
   </string-array>
-    <!-- no translation found for tile_states_modes:0 (7764936419245199023) -->
-    <!-- no translation found for tile_states_modes:1 (2004750556637773692) -->
-    <!-- no translation found for tile_states_modes:2 (8968530753931637871) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"ບໍ່ສາມາດໃຊ້ໄດ້"</item>
     <item msgid="5044688398303285224">"ປິດ"</item>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index a395f2d..6b97dc9 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -126,38 +126,25 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"Palieskite, kad peržiūrėtumėte"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"Išsaugant ekrano įrašą įvyko klaida"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Pradedant ekrano vaizdo įrašymą iškilo problema"</string>
-    <!-- no translation found for screenrecord_stop_dialog_title (8716193661764511095) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message (6262768207331626817) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5995770227684523244) -->
-    <skip />
+    <string name="screenrecord_stop_dialog_title" msgid="8716193661764511095">"Sustabdyti įrašymą?"</string>
+    <string name="screenrecord_stop_dialog_message" msgid="6262768207331626817">"Šiuo metu įrašote visą ekraną"</string>
+    <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Šiuo metu įrašote šią programą: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Sustabdyti įrašymą"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Bendrinamas ekranas"</string>
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Nebebendrinti ekrano?"</string>
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen_with_host_app (522823522115375414) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen (5090115386271179270) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_specific (5923772039347985172) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_generic (6681016774654578261) -->
-    <skip />
+    <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Šiuo metu bendrinate visą ekraną su šia programa: <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
+    <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Šiuo metu bendrinate visą ekraną su programa"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Šiuo metu bendrinate šią programą: <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Šiuo metu bendrinate programą"</string>
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Nebebendrinti"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Perduodamas ekranas"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Sustabdyti perdavimą?"</string>
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen_with_device (1474703115926205251) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen (8419219169553867625) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app_with_device (2715934698604085519) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (8616103075630934513) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic_with_device (9213582497852420203) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic (4100272100480415076) -->
-    <skip />
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen_with_device" msgid="1474703115926205251">"Šiuo metu perduodate visą ekraną į šį įrenginį: <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen" msgid="8419219169553867625">"Šiuo metu perduodate visą ekraną į įrenginį netoliese"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app_with_device" msgid="2715934698604085519">"Šiuo metu perduodate programą (<xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>) į šį įrenginį: <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="8616103075630934513">"Šiuo metu į įrenginį netoliese perduodate šią programą: <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic_with_device" msgid="9213582497852420203">"Šiuo metu perduodate į šį įrenginį: <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic" msgid="4100272100480415076">"Šiuo metu perduodate į įrenginį netoliese"</string>
     <string name="cast_to_other_device_stop_dialog_button" msgid="6420183747435521834">"Sustabdyti perdavimą"</string>
     <string name="close_dialog_button" msgid="4749497706540104133">"Uždaryti"</string>
     <string name="issuerecord_title" msgid="286627115110121849">"Problemų įrašytuvas"</string>
@@ -303,8 +290,7 @@
     <string name="start_dreams" msgid="9131802557946276718">"Ekrano užsklanda"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Eternetas"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"Netrukdymo režimas"</string>
-    <!-- no translation found for quick_settings_modes_label (5407025818652750501) -->
-    <skip />
+    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Prioriteto režimai"</string>
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Nėra pasiekiamų susietų įrenginių"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Palieskite, kad prijungtumėte ar atjungtumėte įrenginį"</string>
@@ -402,6 +388,9 @@
     <string name="performance" msgid="6552785217174378320">"Našumas"</string>
     <string name="user_interface" msgid="3712869377953950887">"Naudotojo sąsaja"</string>
     <string name="thermal" msgid="6758074791325414831">"Šiluminis"</string>
+    <string name="custom" msgid="3337456985275158299">"Tinkinta"</string>
+    <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Tinkinti pėdsako nustatymai"</string>
+    <string name="restore_default" msgid="5259420807486239755">"Atkurti numatytuosius nustatymus"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Vienos rankos režimas"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Klausos įrenginiai"</string>
     <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktyvus"</string>
@@ -440,6 +429,17 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Atidaryti nustatymus"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Kitas įrenginys"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Perjungti apžvalgą"</string>
+    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Prioriteto režimai"</string>
+    <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Atlikta"</string>
+    <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Nustatymai"</string>
+    <string name="zen_mode_on" msgid="9085304934016242591">"Įjungta"</string>
+    <string name="zen_mode_off" msgid="1736604456618147306">"Išjungta"</string>
+    <!-- no translation found for zen_mode_set_up (7457957033034460064) -->
+    <skip />
+    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
+    <skip />
+    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
+    <skip />
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Jūsų netrikdys garsai ir vibravimas, išskyrus nurodytų signalų, priminimų, įvykių ir skambintojų garsus. Vis tiek girdėsite viską, ką pasirinksite leisti, įskaitant muziką, vaizdo įrašus ir žaidimus."</string>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Jūsų netrikdys garsai ir vibravimas, išskyrus signalus. Vis tiek girdėsite viską, ką pasirinksite leisti, įskaitant muziką, vaizdo įrašus ir žaidimus."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Tinkinti"</string>
@@ -478,6 +478,8 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Lėtai įkraunama • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> iki visiško įkrovimo"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Įkraunama • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> iki visiško įkrovimo"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Valdikliai užrakinimo ekrane"</string>
+    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
+    <skip />
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Perbraukite kairėn, paleistumėte bendruomenės mokomąją medžiagą"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Tinkinti"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Atsisakyti"</string>
@@ -504,10 +506,9 @@
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"pasirinkite valdiklį"</string>
     <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"pašalinti valdiklį"</string>
     <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"padėti pasirinktą valdiklį"</string>
-    <!-- no translation found for communal_widget_picker_title (1953369090475731663) -->
-    <skip />
-    <!-- no translation found for communal_widget_picker_description (490515450110487871) -->
-    <skip />
+    <string name="communal_widget_picker_title" msgid="1953369090475731663">"Užrakinimo ekrano valdikliai"</string>
+    <string name="communal_widget_picker_description" msgid="490515450110487871">"Visi gali žr. valdiklius užrakinimo ekrane, net užrakinus planšetinį kompiuterį."</string>
+    <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"atšaukti valdiklio pasirinkimą"</string>
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Užrakinimo ekrano valdikliai"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Kad galėtumėte atidaryti programą naudodami valdiklį, turėsite patvirtinti savo tapatybę. Be to, atminkite, kad bet kas gali peržiūrėti valdiklius net tada, kai planšetinis kompiuteris užrakintas. Kai kurie valdikliai gali būti neskirti jūsų užrakinimo ekranui ir gali būti nesaugu juos čia pridėti."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Supratau"</string>
@@ -564,8 +565,8 @@
     <string name="media_projection_action_text" msgid="3634906766918186440">"Pradėti dabar"</string>
     <string name="empty_shade_text" msgid="8935967157319717412">"Nėra įspėjimų"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"Naujų pranešimų nėra"</string>
-    <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"Adaptyvieji pranešimai įjungti"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"Dabar, gaunant daug pranešimų per trumpą laiką, sumažinamas įrenginio garsumas ir iššokančiųjų langų skaičius ekrane iki dviejų minučių."</string>
+    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Pranešimų neaktyvumo laikotarpis įjungtas"</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Jūsų įrenginio garsumas ir įspėjimai automatiškai sumažinami iki dviejų minučių, kai iš karto gaunate per daug pranešimų."</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Išjungti"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Atrakinę matykite senesnius pranešimus"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Šį įrenginį tvarko vienas iš tavo tėvų"</string>
@@ -732,8 +733,7 @@
     <string name="notification_alert_title" msgid="3656229781017543655">"Numatytasis"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Automatinis"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"Neskamba ir nevibruoja"</string>
-    <!-- no translation found for notification_conversation_summary_low (3855696451919728790) -->
-    <skip />
+    <string name="notification_conversation_summary_low" msgid="3855696451919728790">"Nėra garso ir vibravimo, bet vis tiek rodoma pokalbių skiltyje"</string>
     <string name="notification_channel_summary_default" msgid="777294388712200605">"Gali skambėti arba vibruoti, atsižvelgiant į įrenginio nustatymus"</string>
     <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Gali skambėti arba vibruoti, atsižvelgiant į įrenginio nustatymus. Pokalbiai iš „<xliff:g id="APP_NAME">%1$s</xliff:g>“ debesėlio pagal numatytuosius nustatymus."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Nustatykite, kad sistema aptiktų, ar šis pranešimas turi skambėti, ar vibruoti"</string>
@@ -790,8 +790,7 @@
     <string name="keyboard_key_page_up" msgid="173914303254199845">"Ankstesnis puslapis"</string>
     <string name="keyboard_key_page_down" msgid="9035902490071829731">"Tolesnis puslapis"</string>
     <string name="keyboard_key_forward_del" msgid="5325501825762733459">"Ištrinti"</string>
-    <!-- no translation found for keyboard_key_esc (6230365950511411322) -->
-    <skip />
+    <string name="keyboard_key_esc" msgid="6230365950511411322">"Esc"</string>
     <string name="keyboard_key_move_home" msgid="3496502501803911971">"Pagrindinis"</string>
     <string name="keyboard_key_move_end" msgid="99190401463834854">"Baigti"</string>
     <string name="keyboard_key_insert" msgid="4621692715704410493">"Įterpti"</string>
@@ -1233,7 +1232,6 @@
     <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>
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobiliojo ryšio duomenys"</string>
-    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Prisijungta"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Laikinai prijungta"</string>
     <string name="mobile_data_poor_connection" msgid="819617772268371434">"Prastas ryšys"</string>
@@ -1374,8 +1372,7 @@
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Išskaidyto ekrano režimas"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Įvestis"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Programos šaukiniai"</string>
-    <!-- no translation found for shortcut_helper_category_current_app_shortcuts (4017840565974573628) -->
-    <skip />
+    <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Esama programa"</string>
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Pritaikomumas"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Spartieji klavišai"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Paieškos šaukiniai"</string>
@@ -1388,13 +1385,27 @@
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Atlikta"</string>
     <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Puiku!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Grįžti"</string>
-    <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"Jei norite grįžti, trimis pirštais perbraukite kairėn arba dešinėn bet kurioje jutiklinės dalies vietoje."</string>
+    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
+    <skip />
+    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
+    <skip />
     <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Jutiklinė dalis, kurioje rodomi trys dešinėn ir kairėn judantys pirštai"</string>
     <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Įrenginio ekranas, kuriame rodoma grįžimo gesto animacija"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Klaviatūros foninis apšvietimas"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"%1$d lygis iš %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Namų sistemos valdymas"</string>
     <string name="home_controls_dream_description" msgid="4644150952104035789">"Pasiekite namų sistemos valdymą ekrano užsklandoje"</string>
-    <!-- no translation found for volume_undo_action (5815519725211877114) -->
-    <skip />
+    <string name="volume_undo_action" msgid="5815519725211877114">"Anuliuoti"</string>
+    <string name="back_edu_toast_content" msgid="4530314597378982956">"Jei norite grįžti, jutiklinėje dalyje trimis pirštais perbraukite kairėn arba dešinėn"</string>
+    <string name="home_edu_toast_content" msgid="3381071147871955415">"Jei norite eiti į pagrindinį ekraną, jutiklinėje dalyje perbraukite aukštyn trimis pirštais"</string>
+    <string name="overview_edu_toast_content" msgid="5797030644017804518">"Peržiūrėkite naujausias programas, jutiklinėje dalyje perbraukę aukštyn trimis pirštais ir palaikę"</string>
+    <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Jei norite peržiūrėti visas programas, paspauskite klaviatūros veiksmų klavišą"</string>
+    <string name="back_edu_notification_title" msgid="5624780717751357278">"Naudokite klaviatūrą, kad grįžtumėte atgal"</string>
+    <string name="back_edu_notification_content" msgid="2497557451540954068">"Perbraukite į kairę ar dešinę trimis pirštais. Palieskite, kad sužinotumėte daugiau gestų."</string>
+    <string name="home_edu_notification_title" msgid="6097902076909654045">"Naudokite jutiklinę dalį, jei norite eiti į pagrindinį ekraną"</string>
+    <string name="home_edu_notification_content" msgid="6631697734535766588">"Perbraukite aukštyn trimis pirštais. Palieskite, kad sužinotumėte daugiau gestų."</string>
+    <string name="overview_edu_notification_title" msgid="1265824157319562406">"Naudokite klaviatūrą, kad peržiūrėtumėte naujausias programas"</string>
+    <string name="overview_edu_notification_content" msgid="3578204677648432500">"Perbraukite aukštyn trimis pirštais ir palaikykite. Palieskite, kad sužinotumėte daugiau gestų."</string>
+    <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Naudokite klaviatūrą, kad peržiūrėtumėte visas programas"</string>
+    <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Bet kuriuo metu paspauskite veiksmų klavišą. Palieskite, kad sužinotumėte daugiau gestų."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-lt/tiles_states_strings.xml b/packages/SystemUI/res/values-lt/tiles_states_strings.xml
index 30584f7..c975e7e 100644
--- a/packages/SystemUI/res/values-lt/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-lt/tiles_states_strings.xml
@@ -56,9 +56,6 @@
     <item msgid="5376619709702103243">"Išjungta"</item>
     <item msgid="4875147066469902392">"Įjungta"</item>
   </string-array>
-    <!-- no translation found for tile_states_modes:0 (7764936419245199023) -->
-    <!-- no translation found for tile_states_modes:1 (2004750556637773692) -->
-    <!-- no translation found for tile_states_modes:2 (8968530753931637871) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Nepasiekiama"</item>
     <item msgid="5044688398303285224">"Išjungta"</item>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index 3f54b4e..f191a28 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -126,38 +126,25 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"Pieskarieties, lai skatītu"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"Saglabājot ekrāna ierakstu, radās kļūda."</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Sākot ierakstīt ekrāna saturu, radās kļūda."</string>
-    <!-- no translation found for screenrecord_stop_dialog_title (8716193661764511095) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message (6262768207331626817) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5995770227684523244) -->
-    <skip />
+    <string name="screenrecord_stop_dialog_title" msgid="8716193661764511095">"Vai apturēt ierakstīšanu?"</string>
+    <string name="screenrecord_stop_dialog_message" msgid="6262768207331626817">"Pašlaik ierakstāt visu ekrānu"</string>
+    <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Pašlaik ierakstāt lietotni <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Apturēt ierakstīšanu"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Notiek ekrāna kopīgošana"</string>
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Vai apturēt ekrāna kopīgošanu?"</string>
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen_with_host_app (522823522115375414) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen (5090115386271179270) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_specific (5923772039347985172) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_generic (6681016774654578261) -->
-    <skip />
+    <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Pašlaik kopīgojat visu ekrānu ar lietotni <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
+    <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Pašlaik kopīgojat visu ekrānu ar lietotni"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Pašlaik kopīgojat lietotni <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Pašlaik kopīgojat lietotni"</string>
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Apturēt kopīgošanu"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Notiek ekrāna apraide"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Vai pārtraukt apraidi?"</string>
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen_with_device (1474703115926205251) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen (8419219169553867625) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app_with_device (2715934698604085519) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (8616103075630934513) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic_with_device (9213582497852420203) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic (4100272100480415076) -->
-    <skip />
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen_with_device" msgid="1474703115926205251">"Pašlaik apraidāt visu ekrānu ierīcē <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen" msgid="8419219169553867625">"Pašlaik apraidāt visu ekrānu tuvumā esošā ierīcē"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app_with_device" msgid="2715934698604085519">"Pašlaik apraidāt lietotni <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> ierīcē <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="8616103075630934513">"Pašlaik apraidāt lietotni <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> tuvumā esošā ierīcē"</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic_with_device" msgid="9213582497852420203">"Pašlaik apraidāt saturu ierīcē <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic" msgid="4100272100480415076">"Pašlaik apraidāt saturu tuvumā esošā ierīcē"</string>
     <string name="cast_to_other_device_stop_dialog_button" msgid="6420183747435521834">"Apturēt apraidi"</string>
     <string name="close_dialog_button" msgid="4749497706540104133">"Aizvērt"</string>
     <string name="issuerecord_title" msgid="286627115110121849">"Problēmu ierakstītājs"</string>
@@ -303,8 +290,7 @@
     <string name="start_dreams" msgid="9131802557946276718">"Ekrānsaudzētājs"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Tīkls Ethernet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"Režīms “Netraucēt”"</string>
-    <!-- no translation found for quick_settings_modes_label (5407025818652750501) -->
-    <skip />
+    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Prioritātes režīmi"</string>
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Nav pieejama neviena pārī savienota ierīce."</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Lai pievienotu vai atvienotu kādu ierīci, pieskarieties."</string>
@@ -402,6 +388,9 @@
     <string name="performance" msgid="6552785217174378320">"Veiktspēja"</string>
     <string name="user_interface" msgid="3712869377953950887">"Lietotāja saskarne"</string>
     <string name="thermal" msgid="6758074791325414831">"Ierīces temperatūra"</string>
+    <string name="custom" msgid="3337456985275158299">"Pielāgots"</string>
+    <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Pielāgoti izsekošanas iestatījumi"</string>
+    <string name="restore_default" msgid="5259420807486239755">"Atjaunot noklusējuma vērtību"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Vienas rokas režīms"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Dzirdes aparāti"</string>
     <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktīvs"</string>
@@ -440,6 +429,17 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Atvērt iestatījumus"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Cita ierīce"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Pārskata pārslēgšana"</string>
+    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Prioritātes režīmi"</string>
+    <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Gatavs"</string>
+    <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Iestatījumi"</string>
+    <string name="zen_mode_on" msgid="9085304934016242591">"Ieslēgts"</string>
+    <string name="zen_mode_off" msgid="1736604456618147306">"Izslēgts"</string>
+    <!-- no translation found for zen_mode_set_up (7457957033034460064) -->
+    <skip />
+    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
+    <skip />
+    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
+    <skip />
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Jūs netraucēs skaņas un vibrācija, izņemot signālus, atgādinājumus, pasākumus un zvanītājus, ko būsiet norādījis. Jūs joprojām dzirdēsiet atskaņošanai izvēlētos vienumus, tostarp mūziku, videoklipus un spēles."</string>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Jūs netraucēs skaņas un vibrācija, izņemot signālus. Jūs joprojām dzirdēsiet atskaņošanai izvēlētos vienumus, tostarp mūziku, videoklipus un spēles."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Pielāgot"</string>
@@ -478,6 +478,8 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Lēnā uzlāde • Laiks līdz pilnai uzlādei: <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Notiek uzlāde • Laiks līdz pilnai uzlādei: <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Logrīki bloķēšanas ekrānā"</string>
+    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
+    <skip />
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Velciet pa kreisi, lai palaistu kopienas pamācību."</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Pielāgot"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Nerādīt"</string>
@@ -504,10 +506,9 @@
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"atlasīt logrīku"</string>
     <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"noņemt logrīku"</string>
     <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"novietot atlasīto logrīku"</string>
-    <!-- no translation found for communal_widget_picker_title (1953369090475731663) -->
-    <skip />
-    <!-- no translation found for communal_widget_picker_description (490515450110487871) -->
-    <skip />
+    <string name="communal_widget_picker_title" msgid="1953369090475731663">"Bloķēšanas ekrāna logrīki"</string>
+    <string name="communal_widget_picker_description" msgid="490515450110487871">"Jebkurš var skatīt logrīkus bloķēšanas ekrānā, pat ja planšetdators ir bloķēts."</string>
+    <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"noņemt logrīka atlasi"</string>
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Bloķēšanas ekrāna logrīki"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Lai atvērtu lietotni, izmantojot logrīku, jums būs jāapstiprina sava identitāte. Turklāt ņemiet vērā, ka ikviens var skatīt logrīkus, pat ja planšetdators ir bloķēts. Iespējams, daži logrīki nav paredzēti izmantošanai bloķēšanas ekrānā, un var nebūt droši tos šeit pievienot."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Labi"</string>
@@ -564,8 +565,8 @@
     <string name="media_projection_action_text" msgid="3634906766918186440">"Sākt tūlīt"</string>
     <string name="empty_shade_text" msgid="8935967157319717412">"Nav paziņojumu"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"Nav jaunu paziņojumu"</string>
-    <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"Adaptīvie paziņojumi ieslēgti"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"Jūsu ierīce tagad līdz pat 2 minūtēm samazina skaļuma līmeni un ierobežo uznirstošo elementu skaitu, kad īsā laika posmā saņemat daudzu paziņojumu."</string>
+    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Nogaidīšanas periods paziņojumiem ir ieslēgts"</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Saņemot par daudz paziņojumu uzreiz, skaļums un brīdinājumi tiek automātiski samazināti līdz 2 min."</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Izslēgt"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Atbloķējiet vecāku paziņojumu skatīšanai"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Šo ierīci pārvalda viens no jūsu vecākiem."</string>
@@ -732,8 +733,7 @@
     <string name="notification_alert_title" msgid="3656229781017543655">"Noklusējums"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Automātiski"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"Nav skaņas signāla vai vibrācijas"</string>
-    <!-- no translation found for notification_conversation_summary_low (3855696451919728790) -->
-    <skip />
+    <string name="notification_conversation_summary_low" msgid="3855696451919728790">"Netiek aktivizēts skaņas signāls vai vibrācija, tomēr paziņojums tiek rādīts sarunu sadaļā."</string>
     <string name="notification_channel_summary_default" msgid="777294388712200605">"Atkarībā no iestatījumiem var zvanīt vai vibrēt"</string>
     <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Atkarībā no ierīces iestatījumiem var zvanīt vai vibrēt. Sarunas no lietotnes <xliff:g id="APP_NAME">%1$s</xliff:g> pēc noklusējuma tiek parādītas burbulī."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Iestatiet, lai sistēma noteiktu, vai šim paziņojumam būs skaņa vai vibrācija"</string>
@@ -790,8 +790,7 @@
     <string name="keyboard_key_page_up" msgid="173914303254199845">"Lapa uz augšu"</string>
     <string name="keyboard_key_page_down" msgid="9035902490071829731">"Lapa uz leju"</string>
     <string name="keyboard_key_forward_del" msgid="5325501825762733459">"Dzēšanas taustiņš"</string>
-    <!-- no translation found for keyboard_key_esc (6230365950511411322) -->
-    <skip />
+    <string name="keyboard_key_esc" msgid="6230365950511411322">"Atsoļa taustiņš"</string>
     <string name="keyboard_key_move_home" msgid="3496502501803911971">"Sākumvietas taustiņš"</string>
     <string name="keyboard_key_move_end" msgid="99190401463834854">"Beigvietas taustiņš"</string>
     <string name="keyboard_key_insert" msgid="4621692715704410493">"Ievietošanas taustiņš"</string>
@@ -1233,7 +1232,6 @@
     <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>
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobilie dati"</string>
-    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</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>
     <string name="mobile_data_poor_connection" msgid="819617772268371434">"Vājš savienojums"</string>
@@ -1374,8 +1372,7 @@
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Ekrāna sadalīšana"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Ievade"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Lietotņu saīsnes"</string>
-    <!-- no translation found for shortcut_helper_category_current_app_shortcuts (4017840565974573628) -->
-    <skip />
+    <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Pašreizējā lietotne"</string>
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Pieejamība"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Īsinājumtaustiņi"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Meklēšanas saīsnes"</string>
@@ -1388,13 +1385,27 @@
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Gatavs"</string>
     <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Lieliski!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Atpakaļ"</string>
-    <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"Lai pārietu atpakaļ, ar trim pirkstiem velciet pa kreisi vai pa labi jebkurā vietā uz skārienpaliktņa"</string>
+    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
+    <skip />
+    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
+    <skip />
     <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Attēls ar skārienpaliktni, uz kura trīs pirksti kustas pa labi un pa kreisi"</string>
     <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Ierīces ekrāns, kurā redzama animācija ar žestu pāriešanai atpakaļ"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Tastatūras fona apgaismojums"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Līmenis numur %1$d, kopā ir %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Mājas kontrolierīces"</string>
     <string name="home_controls_dream_description" msgid="4644150952104035789">"Ātra piekļuve mājas kontrolierīcēm ekrānsaudzētājā"</string>
-    <!-- no translation found for volume_undo_action (5815519725211877114) -->
-    <skip />
+    <string name="volume_undo_action" msgid="5815519725211877114">"Atsaukt"</string>
+    <string name="back_edu_toast_content" msgid="4530314597378982956">"Lai atgrieztos, ar trīs pirkstiem velciet pa kreisi vai pa labi jebkurā vietā uz skārienpaliktņa."</string>
+    <string name="home_edu_toast_content" msgid="3381071147871955415">"Lai pārietu uz sākuma ekrāna, ar trīs pirkstiem uz skārienpaliktņa velciet augšup."</string>
+    <string name="overview_edu_toast_content" msgid="5797030644017804518">"Lai skatītu nesenās lietotnes, ar trīs pirkstiem uz skārienpaliktņa velciet augšup un turiet."</string>
+    <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Lai skatītu visas savas lietotnes, tastatūrā nospiediet darbību taustiņu."</string>
+    <string name="back_edu_notification_title" msgid="5624780717751357278">"Atgriešanās, izmantojot skārienpaliktni"</string>
+    <string name="back_edu_notification_content" msgid="2497557451540954068">"Ar trīs pirkstiem velciet pa kreisi vai pa labi. Lai apgūtu citus žestus, pieskarieties šeit."</string>
+    <string name="home_edu_notification_title" msgid="6097902076909654045">"Pāriešana uz sākuma ekrānu, izmantojot skārienpaliktni"</string>
+    <string name="home_edu_notification_content" msgid="6631697734535766588">"Ar trīs pirkstiem velciet augšup. Lai apgūtu citus žestus, pieskarieties šeit."</string>
+    <string name="overview_edu_notification_title" msgid="1265824157319562406">"Pēdējo lietotņu skatīšana, izmantojot skārienpaliktni"</string>
+    <string name="overview_edu_notification_content" msgid="3578204677648432500">"Ar trīs pirkstiem velciet augšup un turiet. Lai apgūtu citus žestus, pieskarieties šeit."</string>
+    <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Visu lietotņu skatīšana, izmantojot tastatūru"</string>
+    <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Jebkurā laikā varat nospiest darbību taustiņu. Lai apgūtu citus žestus, pieskarieties šeit."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-lv/tiles_states_strings.xml b/packages/SystemUI/res/values-lv/tiles_states_strings.xml
index 8d23ef1..c65a1d4 100644
--- a/packages/SystemUI/res/values-lv/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-lv/tiles_states_strings.xml
@@ -56,9 +56,6 @@
     <item msgid="5376619709702103243">"Izslēgts"</item>
     <item msgid="4875147066469902392">"Ieslēgts"</item>
   </string-array>
-    <!-- no translation found for tile_states_modes:0 (7764936419245199023) -->
-    <!-- no translation found for tile_states_modes:1 (2004750556637773692) -->
-    <!-- no translation found for tile_states_modes:2 (8968530753931637871) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Nav pieejams"</item>
     <item msgid="5044688398303285224">"Izslēgts"</item>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index 6eaf8b9..68f9a9d 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -126,38 +126,25 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"Допрете за прегледување"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"Грешка при зачувувањето на снимката од екранот"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Грешка при почетокот на снимањето на екранот"</string>
-    <!-- no translation found for screenrecord_stop_dialog_title (8716193661764511095) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message (6262768207331626817) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5995770227684523244) -->
-    <skip />
+    <string name="screenrecord_stop_dialog_title" msgid="8716193661764511095">"Да се сопре снимањето?"</string>
+    <string name="screenrecord_stop_dialog_message" msgid="6262768207331626817">"Во моментов го снимате целиот екран"</string>
+    <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Во моментов ја снимате <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Сопри го снимањето"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Се споделува екранот"</string>
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Да се сопре споделувањето на екранот?"</string>
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen_with_host_app (522823522115375414) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen (5090115386271179270) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_specific (5923772039347985172) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_generic (6681016774654578261) -->
-    <skip />
+    <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Во моментов го споделувате целиот екран со <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
+    <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Во моментов го споделувате целиот екран со апликација"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Во моментов ја споделувате <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Во моментов споделувате апликација"</string>
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Сопри го споделувањето"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Се емитува екранот"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Да се сопре емитувањето?"</string>
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen_with_device (1474703115926205251) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen (8419219169553867625) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app_with_device (2715934698604085519) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (8616103075630934513) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic_with_device (9213582497852420203) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic (4100272100480415076) -->
-    <skip />
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen_with_device" msgid="1474703115926205251">"Во моментов го емитувате целиот екран на <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen" msgid="8419219169553867625">"Во моментов го емитувате целиот екран на уред во близина"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app_with_device" msgid="2715934698604085519">"Во моментов ја емитувате <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> на <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="8616103075630934513">"Во моментов ја емитувате <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> на уред во близина"</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic_with_device" msgid="9213582497852420203">"Во моментов емитувате на <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic" msgid="4100272100480415076">"Во моментов емитувате на уред во близина"</string>
     <string name="cast_to_other_device_stop_dialog_button" msgid="6420183747435521834">"Сопри го емитувањето"</string>
     <string name="close_dialog_button" msgid="4749497706540104133">"Затвори"</string>
     <string name="issuerecord_title" msgid="286627115110121849">"Проблем со „Диктафон“"</string>
@@ -303,8 +290,7 @@
     <string name="start_dreams" msgid="9131802557946276718">"Штедач на екран"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Етернет"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"Не вознемирувај"</string>
-    <!-- no translation found for quick_settings_modes_label (5407025818652750501) -->
-    <skip />
+    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Приоритетни режими"</string>
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Нема достапни спарени уреди"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Допрете за да воспоставите или да прекинете врска со уред"</string>
@@ -402,6 +388,9 @@
     <string name="performance" msgid="6552785217174378320">"Изведба"</string>
     <string name="user_interface" msgid="3712869377953950887">"Кориснички интерфејс"</string>
     <string name="thermal" msgid="6758074791325414831">"Термално"</string>
+    <string name="custom" msgid="3337456985275158299">"Приспособено"</string>
+    <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Приспособени поставки за следење"</string>
+    <string name="restore_default" msgid="5259420807486239755">"Врати на стандардно"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Режим со една рака"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Слушни апарати"</string>
     <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Активно"</string>
@@ -440,6 +429,17 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Отворете „Поставки“"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Друг уред"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Вклучи/исклучи преглед"</string>
+    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Приоритетни режими"</string>
+    <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_off" msgid="1736604456618147306">"Исклучено"</string>
+    <!-- no translation found for zen_mode_set_up (7457957033034460064) -->
+    <skip />
+    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
+    <skip />
+    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
+    <skip />
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Нема да ве вознемируваат звуци и вибрации, освен од аларми, потсетници, настани и повикувачи што ќе ги наведете. Сѐ уште ќе слушате сѐ што ќе изберете да пуштите, како музика, видеа и игри."</string>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Нема да ве вознемируваат звуци и вибрации, освен од аларми. Сѐ уште ќе слушате сѐ што ќе изберете да пуштите, како музика, видеа и игри."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Приспособи"</string>
@@ -478,6 +478,8 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Се полни бавно • Полна по <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Се полни • Полна по <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Виџети на заклучен екран"</string>
+    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
+    <skip />
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Повлечете налево за да го започнете заедничкото упатство"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Приспособете"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Отфрли"</string>
@@ -504,10 +506,9 @@
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"изберете виџет"</string>
     <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"отстранете го виџетот"</string>
     <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"поставете го избраниот виџет"</string>
-    <!-- no translation found for communal_widget_picker_title (1953369090475731663) -->
-    <skip />
-    <!-- no translation found for communal_widget_picker_description (490515450110487871) -->
-    <skip />
+    <string name="communal_widget_picker_title" msgid="1953369090475731663">"Виџети на заклучен екран"</string>
+    <string name="communal_widget_picker_description" msgid="490515450110487871">"Секој може да гледа виџети на заклучениот екран, дури и ако таблетот е заклучен."</string>
+    <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"поништи го изборот на виџетот"</string>
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Виџети на заклучен екран"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"За да отворите апликација со помош на виџет, ќе треба да потврдите дека сте вие. Покрај тоа, имајте предвид дека секој може да ги гледа виџетите, дури и кога вашиот таблет е заклучен. Некои виџети можеби не се наменети за вашиот заклучен екран, па можеби не е безбедно да се додадат овде."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Сфатив"</string>
@@ -564,8 +565,8 @@
     <string name="media_projection_action_text" msgid="3634906766918186440">"Започни сега"</string>
     <string name="empty_shade_text" msgid="8935967157319717412">"Нема известувања"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"Нема нови известувања"</string>
-    <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"Адаптивни известув.: вклучено"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"Уредот сега ја намалува јачината на звукот и ги намалува скокачките прозорци на екранот до 2 мин. кога добивате многу известувања за кратко време."</string>
+    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"„Подискретни известувања“ е вклучена"</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Јачината на звукот и известувањата на уредот се намалуваат автоматски до 2 минути кога добивате премногу известувања одеднаш."</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Исклучи"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Отклучете за да ги видите старите известувања"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Родителот управува со уредов"</string>
@@ -732,8 +733,7 @@
     <string name="notification_alert_title" msgid="3656229781017543655">"Стандардно"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Автоматски"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"Без звук или вибрации"</string>
-    <!-- no translation found for notification_conversation_summary_low (3855696451919728790) -->
-    <skip />
+    <string name="notification_conversation_summary_low" msgid="3855696451919728790">"Нема звук или вибрации, но сепак се појавува во делот за разговор"</string>
     <string name="notification_channel_summary_default" msgid="777294388712200605">"Може да ѕвони или да вибрира во зависност од поставките за уредот"</string>
     <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Може да ѕвони или да вибрира во зависност од поставките за уредот. Стандардно, разговорите од <xliff:g id="APP_NAME">%1$s</xliff:g> се во балончиња."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Дозволете системот да определи дали известувањево треба да испушти звук или да вибрира"</string>
@@ -790,8 +790,7 @@
     <string name="keyboard_key_page_up" msgid="173914303254199845">"Страница нагоре"</string>
     <string name="keyboard_key_page_down" msgid="9035902490071829731">"Страница надолу"</string>
     <string name="keyboard_key_forward_del" msgid="5325501825762733459">"Избриши"</string>
-    <!-- no translation found for keyboard_key_esc (6230365950511411322) -->
-    <skip />
+    <string name="keyboard_key_esc" msgid="6230365950511411322">"Esc"</string>
     <string name="keyboard_key_move_home" msgid="3496502501803911971">"Home-копче"</string>
     <string name="keyboard_key_move_end" msgid="99190401463834854">"Крај"</string>
     <string name="keyboard_key_insert" msgid="4621692715704410493">"Вметни"</string>
@@ -1233,7 +1232,6 @@
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Потребна е проверка. Допрете го сензорот за отпечаток за да автентицирате."</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Тековен телефонски повик"</string>
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Мобилен интернет"</string>
-    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Поврзано"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Привремено поврзано"</string>
     <string name="mobile_data_poor_connection" msgid="819617772268371434">"Слаба интернет-врска"</string>
@@ -1374,8 +1372,7 @@
     <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>
-    <!-- no translation found for shortcut_helper_category_current_app_shortcuts (4017840565974573628) -->
-    <skip />
+    <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_search_placeholder" msgid="5488547526269871819">"Кратенки за пребарување"</string>
@@ -1388,13 +1385,27 @@
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Готово"</string>
     <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Одлично сторено!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Назад"</string>
-    <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"За да се вратите назад, повлечете налево или надесно со три прста каде било на допирната подлога."</string>
+    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
+    <skip />
+    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
+    <skip />
     <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Допирната подлога покажува три прста што се движат десно и лево"</string>
     <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Екранот на уредот покажува анимација за движење назад"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Осветлување на тастатура"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Ниво %1$d од %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Контроли за домот"</string>
     <string name="home_controls_dream_description" msgid="4644150952104035789">"Контролите за домот како штедач на екран"</string>
-    <!-- no translation found for volume_undo_action (5815519725211877114) -->
-    <skip />
+    <string name="volume_undo_action" msgid="5815519725211877114">"Врати"</string>
+    <string name="back_edu_toast_content" msgid="4530314597378982956">"За да се вратите назад, повлечете налево или надесно со три прста на допирната подлога"</string>
+    <string name="home_edu_toast_content" msgid="3381071147871955415">"За да одите на почетниот екран, повлечете нагоре со три прста на допирната подлога"</string>
+    <string name="overview_edu_toast_content" msgid="5797030644017804518">"За да ги видите скорешните апликации, повлечете нагоре и задржете со три прста на допирната подлога"</string>
+    <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Притиснете го копчето за дејство на тастатурата за да ги видите сите апликации"</string>
+    <string name="back_edu_notification_title" msgid="5624780717751357278">"Користете ја допирната подлога за да се вратите назад"</string>
+    <string name="back_edu_notification_content" msgid="2497557451540954068">"Повлечете налево или надесно со три прста. Допрете за да научите повеќе движења."</string>
+    <string name="home_edu_notification_title" msgid="6097902076909654045">"Користете ја допирната подлога за да одите на почетниот екран"</string>
+    <string name="home_edu_notification_content" msgid="6631697734535766588">"Повлечете нагоре со три прста. Допрете за да научите повеќе движења."</string>
+    <string name="overview_edu_notification_title" msgid="1265824157319562406">"Користете ја допирната подлога за да ги видите скорешните апликации"</string>
+    <string name="overview_edu_notification_content" msgid="3578204677648432500">"Повлечете нагоре и задржете со три прста. Допрете за да научите повеќе движења."</string>
+    <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Користете ја тастатурата за да ги видите сите апликации"</string>
+    <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Притиснете го копчето за дејство кога сакате. Допрете за да научите повеќе движења."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-mk/tiles_states_strings.xml b/packages/SystemUI/res/values-mk/tiles_states_strings.xml
index 08cdc09..a8d9695 100644
--- a/packages/SystemUI/res/values-mk/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-mk/tiles_states_strings.xml
@@ -56,9 +56,6 @@
     <item msgid="5376619709702103243">"Исклучено"</item>
     <item msgid="4875147066469902392">"Вклучено"</item>
   </string-array>
-    <!-- no translation found for tile_states_modes:0 (7764936419245199023) -->
-    <!-- no translation found for tile_states_modes:1 (2004750556637773692) -->
-    <!-- no translation found for tile_states_modes:2 (8968530753931637871) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Недостапно"</item>
     <item msgid="5044688398303285224">"Исклучено"</item>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index 5877d2b..8711604 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -290,8 +290,7 @@
     <string name="start_dreams" msgid="9131802557946276718">"സ്ക്രീൻ സേവർ"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"ഇതർനെറ്റ്"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"ശല്യപ്പെടുത്തരുത്"</string>
-    <!-- no translation found for quick_settings_modes_label (5407025818652750501) -->
-    <skip />
+    <string name="quick_settings_modes_label" msgid="5407025818652750501">"മുൻഗണനാ മോഡുകൾ"</string>
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"ജോടിയാക്കിയ ഉപകരണങ്ങളൊന്നും ലഭ്യമല്ല"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"ഒരു ഉപകരണം കണക്റ്റ് ചെയ്യാനോ വിച്ഛേദിക്കാനോ ടാപ്പ് ചെയ്യുക"</string>
@@ -389,6 +388,9 @@
     <string name="performance" msgid="6552785217174378320">"പ്രകടനം"</string>
     <string name="user_interface" msgid="3712869377953950887">"ഉപയോക്തൃ ഇന്റർഫേസ്"</string>
     <string name="thermal" msgid="6758074791325414831">"തെർമൽ"</string>
+    <string name="custom" msgid="3337456985275158299">"ഇഷ്ടാനുസൃതം"</string>
+    <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"ഇഷ്ടാനുസൃത അടയാള ക്രമീകരണം"</string>
+    <string name="restore_default" msgid="5259420807486239755">"ഡിഫോൾട്ട് പുനഃസ്ഥാപിക്കുക"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"ഒറ്റക്കൈ മോഡ്"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"കേൾവിക്കുള്ള ഉപകരണങ്ങൾ"</string>
     <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"സജീവം"</string>
@@ -427,6 +429,17 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"ക്രമീകരണം തുറക്കുക"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"മറ്റ് ഉപകരണം"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"അവലോകനം മാറ്റുക"</string>
+    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"മുൻഗണനാ മോഡുകൾ"</string>
+    <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_off" msgid="1736604456618147306">"ഓഫാണ്"</string>
+    <!-- no translation found for zen_mode_set_up (7457957033034460064) -->
+    <skip />
+    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
+    <skip />
+    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
+    <skip />
     <string name="zen_priority_introduction" msgid="3159291973383796646">"നിങ്ങൾ സജ്ജീകരിച്ച അലാറങ്ങൾ, റിമൈൻഡറുകൾ, ഇവന്റുകൾ, കോളർമാർ എന്നിവയിൽ നിന്നുള്ള ശബ്‌ദങ്ങളും വൈബ്രേഷനുകളുമൊഴികെ മറ്റൊന്നും നിങ്ങളെ ശല്യപ്പെടുത്തുകയില്ല. സംഗീതം, വീഡിയോകൾ, ഗെയിമുകൾ എന്നിവയുൾപ്പെടെ പ്ലേ ചെയ്യുന്നതെന്തും നിങ്ങൾക്ക് ‌തുടർന്നും കേൾക്കാൻ കഴിയും."</string>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"അലാറങ്ങളിൽ നിന്നുള്ള ശബ്‌ദങ്ങളും വൈബ്രേഷനുകളുമൊഴികെ മറ്റൊന്നും നിങ്ങളെ ശല്യപ്പെടുത്തുകയില്ല. സംഗീതം, വീഡിയോകൾ, ഗെയിമുകൾ എന്നിവയുൾപ്പെടെ പ്ലേ ചെയ്യുന്നതെന്തും നിങ്ങൾക്ക് ‌തുടർന്നും കേൾക്കാൻ കഴിയും."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"ഇഷ്‌ടാനുസൃതമാക്കുക"</string>
@@ -465,6 +478,8 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • പതുക്കെ ചാർജ് ചെയ്യുന്നു • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>-ൽ പൂർത്തിയാകും"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ചാർജ് ചെയ്യുന്നു • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>-ൽ പൂർത്തിയാകും"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"ലോക്ക് സ്‌ക്രീനിൽ വിജറ്റുകൾ"</string>
+    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
+    <skip />
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"കമ്മ്യൂണൽ ട്യൂട്ടോറിയൽ ആരംഭിക്കാൻ ഇടത്തോട്ട് സ്വൈപ്പ് ചെയ്യുക"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"ഇഷ്‌ടാനുസൃതമാക്കുക"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"ഡിസ്‌മിസ് ചെയ്യുക"</string>
@@ -493,6 +508,7 @@
     <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"തിരഞ്ഞെടുത്ത വിജറ്റ് നൽകുക"</string>
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"ലോക്ക് സ്‌ക്രീൻ വിജറ്റുകൾ"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"ടാബ്‌ലെറ്റ് ലോക്കാണെങ്കിൽ പോലും ലോക്ക് സ്ക്രീനിൽ ആർക്കും വിജറ്റുകൾ കാണാം."</string>
+    <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"വിജറ്റ് തിരഞ്ഞെടുത്തത് മാറ്റുക"</string>
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"ലോക്ക് സ്‌ക്രീൻ വിജറ്റുകൾ"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"വിജറ്റ് ഉപയോഗിച്ച് ഒരു ആപ്പ് തുറക്കാൻ, ഇത് നിങ്ങൾ തന്നെയാണെന്ന് പരിശോധിച്ചുറപ്പിക്കേണ്ടതുണ്ട്. നിങ്ങളുടെ ടാബ്‌ലെറ്റ് ലോക്കായിരിക്കുമ്പോഴും എല്ലാവർക്കും അത് കാണാനാകുമെന്നതും ഓർക്കുക. ചില വിജറ്റുകൾ നിങ്ങളുടെ ലോക്ക് സ്‌ക്രീനിന് ഉള്ളതായിരിക്കില്ല, അവ ഇവിടെ ചേർക്കുന്നത് സുരക്ഷിതവുമായിരിക്കില്ല."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"മനസ്സിലായി"</string>
@@ -549,8 +565,8 @@
     <string name="media_projection_action_text" msgid="3634906766918186440">"ഇപ്പോൾ ആരംഭിക്കുക"</string>
     <string name="empty_shade_text" msgid="8935967157319717412">"അറിയിപ്പുകൾ ഒന്നുമില്ല"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"പുതിയ അറിയിപ്പുകളൊന്നുമില്ല"</string>
-    <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"അഡാപ്‌റ്റീവ് അറിയിപ്പുകൾ ഓണാണ്"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"ചെറിയ കാലയളവിനുള്ളിൽ നിങ്ങൾക്ക് നിരവധി അറിയിപ്പുകൾ ലഭിക്കുമ്പോൾ ഉപകരണം ഇപ്പോൾ വോളിയം കുറയ്ക്കുകയും സ്ക്രീനിൽ പോപ്പ് അപ്പുകൾ രണ്ട് മിനിറ്റ് വരെ ചുരുക്കുകയും ചെയ്യുന്നു."</string>
+    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"അറിയിപ്പിന്റെ ശബ്ദം കുറയ്ക്കൽ ഓണാണ്"</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"നിരവധി അറിയിപ്പ് ഒരുമിച്ച് ലഭിക്കുമ്പോൾ, ഉപകരണത്തിന്റെ ശബ്‌ദവും മുന്നറിയിപ്പും 2 മിനിറ്റ് വരെ സ്വയമേവ കുറയ്ക്കും."</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"ഓഫാക്കുക"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"പഴയ അറിയിപ്പുകൾ കാണാൻ അൺലോക്ക് ചെയ്യുക"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"ഈ ഉപകരണം മാനേജ് ചെയ്യുന്നത് നിങ്ങളുടെ രക്ഷിതാവാണ്"</string>
@@ -717,8 +733,7 @@
     <string name="notification_alert_title" msgid="3656229781017543655">"ഡിഫോൾട്ട്"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"സ്വയമേവ"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"ശബ്ദമോ വൈബ്രേഷനോ ഇല്ല"</string>
-    <!-- no translation found for notification_conversation_summary_low (3855696451919728790) -->
-    <skip />
+    <string name="notification_conversation_summary_low" msgid="3855696451919728790">"ശബ്ദമോ വൈബ്രേഷനോ ഇല്ലെങ്കിലും സംഭാഷണ വിഭാഗത്തിൽ ഇപ്പോഴും ദൃശ്യമാകുന്നു"</string>
     <string name="notification_channel_summary_default" msgid="777294388712200605">"ഉപകരണ ക്രമീകരണം അടിസ്ഥാനമാക്കി റിംഗ് ചെയ്യും അല്ലെങ്കിൽ വൈബ്രേറ്റ് ചെയ്യും"</string>
     <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"ഉപകരണ ക്രമീകരണം അടിസ്ഥാനമാക്കി റിംഗ് ചെയ്യും അല്ലെങ്കിൽ വൈബ്രേറ്റ് ചെയ്യും. <xliff:g id="APP_NAME">%1$s</xliff:g> എന്ന ആപ്പിൽ നിന്നുള്ള സംഭാഷണങ്ങൾ ഡിഫോൾട്ടായി ബബിൾ ചെയ്യുന്നു."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"ഈ അറിയിപ്പ് വരുമ്പോൾ ശബ്‌ദിക്കുകയാണോ വൈബ്രേറ്റ് ചെയ്യുകയാണോ വേണ്ടതെന്ന് നിർണ്ണയിക്കാൻ സിസ്‌റ്റത്തെ അനുവദിക്കുക"</string>
@@ -1217,7 +1232,6 @@
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"പരിശോധിച്ചുറപ്പിക്കേണ്ടതുണ്ട്. പരിശോധിച്ചുറപ്പിക്കാൻ, വിരലടയാള സെൻസറിൽ സ്‌പർശിക്കുക."</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"സജീവമായ ഫോൺ കോൾ"</string>
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"മൊബൈൽ ഡാറ്റ"</string>
-    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"കണക്റ്റ് ചെയ്തു"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"താൽക്കാലികമായി കണക്റ്റ് ചെയ്തിരിക്കുന്നു"</string>
     <string name="mobile_data_poor_connection" msgid="819617772268371434">"ദുർബലമായ കണക്ഷൻ"</string>
@@ -1371,13 +1385,27 @@
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"പൂർത്തിയായി"</string>
     <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"കൊള്ളാം!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"മടങ്ങുക"</string>
-    <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"തിരികെ പോകാൻ, ടച്ച്പാഡിൽ എവിടെയെങ്കിലും മൂന്ന് വിരലുകൾ ഉപയോഗിച്ച് ഇടത്തേക്കോ വലത്തേക്കോ സ്വൈപ്പ് ചെയ്യുക."</string>
+    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
+    <skip />
+    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
+    <skip />
     <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"വലത്തേക്കും ഇടത്തേക്കും ചലിക്കുന്ന മൂന്ന് വിരലുകൾ കാണിക്കുന്ന ടച്ച്പാഡ്"</string>
     <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"ബാക്ക് ജെസ്ച്ചറിനായി ആനിമേഷൻ കാണിക്കുന്ന ഉപകരണ സ്‌ക്രീൻ"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"കീബോഡ് ബാക്ക്‌ലൈറ്റ്"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d-ൽ %1$d-ാമത്തെ ലെവൽ"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"ഹോം കൺട്രോളുകൾ"</string>
     <string name="home_controls_dream_description" msgid="4644150952104035789">"സ്‌ക്രീൻസേവറായി ഹോം കൺട്രോളുകൾ പെട്ടെന്ന് ആക്‌സസ് ചെയ്യൂ"</string>
-    <!-- no translation found for volume_undo_action (5815519725211877114) -->
-    <skip />
+    <string name="volume_undo_action" msgid="5815519725211877114">"പഴയപടിയാക്കുക"</string>
+    <string name="back_edu_toast_content" msgid="4530314597378982956">"തിരികെ പോകാൻ, മൂന്ന് വിരലുകൾ ഉപയോഗിച്ച് ടച്ച്‌പാഡിൽ ഇടത്തേക്കോ വലത്തേക്കോ സ്വൈപ്പ് ചെയ്യുക"</string>
+    <string name="home_edu_toast_content" msgid="3381071147871955415">"ഹോമിലേക്ക് പോകാൻ, മൂന്ന് വിരലുകൾ ഉപയോഗിച്ച് ടച്ച്‌പാഡിൽ മുകളിലേക്ക് സ്വൈപ്പ് ചെയ്യുക"</string>
+    <string name="overview_edu_toast_content" msgid="5797030644017804518">"അടുത്തിടെ ഉപയോഗിച്ച ആപ്പുകൾ കാണാൻ, മൂന്ന് വിരലുകൾ ഉപയോഗിച്ച് ടച്ച്‌പാഡിൽ സ്വൈപ്പ് ചെയ്ത് പിടിക്കുക"</string>
+    <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"എല്ലാ ആപ്പുകളും കാണാൻ, നിങ്ങളുടെ കീബോർഡിലെ ആക്‌ഷൻ കീ അമർത്തുക"</string>
+    <string name="back_edu_notification_title" msgid="5624780717751357278">"തിരികെ പോകാൻ നിങ്ങളുടെ ടച്ച്‌പാഡ് ഉപയോഗിക്കുക"</string>
+    <string name="back_edu_notification_content" msgid="2497557451540954068">"മൂന്ന് വിരലുകളാൽ ഇടത്തേക്കോ വലത്തേക്കോ സ്വൈപ്പുചെയ്യൂ. കൂടുതൽ ജെസ്ച്ചറുകൾ മനസ്സിലാക്കാൻ ടാപ്പ് ചെയ്യുക."</string>
+    <string name="home_edu_notification_title" msgid="6097902076909654045">"ഹോമിലേക്ക് പോകാൻ നിങ്ങളുടെ ടച്ച്‌പാഡ് ഉപയോഗിക്കുക"</string>
+    <string name="home_edu_notification_content" msgid="6631697734535766588">"മൂന്ന് വിരലുകൾ കൊണ്ട് മുകളിലേക്ക് സ്വൈപ്പ് ചെയ്യുക. കൂടുതൽ ജെസ്ച്ചറുകൾ മനസ്സിലാക്കാൻ ടാപ്പ് ചെയ്യുക."</string>
+    <string name="overview_edu_notification_title" msgid="1265824157319562406">"അടുത്തിടെ ഉപയോഗിച്ച ആപ്പുകൾ കാണാൻ നിങ്ങളുടെ ടച്ച്‌പാഡ് ഉപയോഗിക്കുക"</string>
+    <string name="overview_edu_notification_content" msgid="3578204677648432500">"മൂന്ന് വിരലുകൾ കൊണ്ട് മുകളിലേക്ക് സ്വൈപ്പ് ചെയ്‌ത് പിടിക്കുക. കൂടുതൽ ജെസ്ച്ചറുകൾ മനസ്സിലാക്കാൻ ടാപ്പ് ചെയ്യുക."</string>
+    <string name="all_apps_edu_notification_title" msgid="372262997265569063">"എല്ലാ ആപ്പുകളും കാണാൻ നിങ്ങളുടെ കീബോർഡ് ഉപയോഗിക്കുക"</string>
+    <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"ഏതുസമയത്തും ആക്ഷൻ കീ അമർത്തുക. കൂടുതൽ ജെസ്ച്ചറുകൾ മനസ്സിലാക്കാൻ ടാപ്പ് ചെയ്യുക."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ml/tiles_states_strings.xml b/packages/SystemUI/res/values-ml/tiles_states_strings.xml
index 5999e3cf..609fdde 100644
--- a/packages/SystemUI/res/values-ml/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ml/tiles_states_strings.xml
@@ -56,9 +56,6 @@
     <item msgid="5376619709702103243">"ഓഫാണ്"</item>
     <item msgid="4875147066469902392">"ഓണാണ്"</item>
   </string-array>
-    <!-- no translation found for tile_states_modes:0 (7764936419245199023) -->
-    <!-- no translation found for tile_states_modes:1 (2004750556637773692) -->
-    <!-- no translation found for tile_states_modes:2 (8968530753931637871) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"ലഭ്യമല്ല"</item>
     <item msgid="5044688398303285224">"ഓഫാണ്"</item>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index 2e0115f..95b4e3f 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -126,38 +126,25 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"Харахын тулд товшино уу"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"Дэлгэцийн бичлэгийг хадгалахад алдаа гарлаа"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Дэлгэцийн бичлэгийг эхлүүлэхэд алдаа гарлаа"</string>
-    <!-- no translation found for screenrecord_stop_dialog_title (8716193661764511095) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message (6262768207331626817) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5995770227684523244) -->
-    <skip />
+    <string name="screenrecord_stop_dialog_title" msgid="8716193661764511095">"Бичлэгийг зогсоох уу?"</string>
+    <string name="screenrecord_stop_dialog_message" msgid="6262768207331626817">"Та одоогоор дэлгэцээ бүтнээр нь бичиж байна"</string>
+    <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Та одоогоор <xliff:g id="APP_NAME">%1$s</xliff:g>-г бичиж байна"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Бичихийг зогсоох"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Дэлгэцийг хуваалцаж байна"</string>
-    <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Дэлгэцийг хуваалцахыг зогсоох уу?"</string>
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen_with_host_app (522823522115375414) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen (5090115386271179270) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_specific (5923772039347985172) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_generic (6681016774654578261) -->
-    <skip />
+    <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Дэлгэц хуваалцахыг зогсоох уу?"</string>
+    <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Та одоогоор дэлгэцээ бүтнээр нь <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>-тай хуваалцаж байна"</string>
+    <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Та одоогоор дэлгэцээ бүтнээр нь нэг апптай хуваалцаж байна"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Та одоогоор <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>-г хуваалцаж байна"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Та одоогоор нэг аппыг хуваалцаж байна"</string>
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Хуваалцахыг зогсоох"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Дэлгэцийг дамжуулж байна"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Дамжуулахaa болих уу?"</string>
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen_with_device (1474703115926205251) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen (8419219169553867625) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app_with_device (2715934698604085519) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (8616103075630934513) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic_with_device (9213582497852420203) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic (4100272100480415076) -->
-    <skip />
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen_with_device" msgid="1474703115926205251">"Та одоогоор дэлгэцээ бүтнээр нь <xliff:g id="DEVICE_NAME">%1$s</xliff:g> руу дамжуулж байна"</string>
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen" msgid="8419219169553867625">"Та одоогоор дэлгэцээ бүтнээр нь ойролцоох төхөөрөмж рүү дамжуулж байна"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app_with_device" msgid="2715934698604085519">"Та одоогоор <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>-г <xliff:g id="DEVICE_NAME">%2$s</xliff:g> руу дамжуулж байна"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="8616103075630934513">"Та одоогоор <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>-г ойролцоох төхөөрөмж рүү дамжуулж байна"</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic_with_device" msgid="9213582497852420203">"Та одоогоор <xliff:g id="DEVICE_NAME">%1$s</xliff:g> руу дамжуулж байна"</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic" msgid="4100272100480415076">"Та одоогоор ойролцоох төхөөрөмж рүү дамжуулж байна"</string>
     <string name="cast_to_other_device_stop_dialog_button" msgid="6420183747435521834">"Дамжуулахыг зогсоох"</string>
     <string name="close_dialog_button" msgid="4749497706540104133">"Хаах"</string>
     <string name="issuerecord_title" msgid="286627115110121849">"Асуудал бичигч"</string>
@@ -303,8 +290,7 @@
     <string name="start_dreams" msgid="9131802557946276718">"Дэлгэц амраагч"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Этернет"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"Бүү саад бол"</string>
-    <!-- no translation found for quick_settings_modes_label (5407025818652750501) -->
-    <skip />
+    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Чухал байдлаар нь ангилах горим"</string>
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Хослуулсан төхөөрөмж байхгүй"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Төхөөрөмжийг холбох эсвэл салгахын тулд товшино уу"</string>
@@ -402,6 +388,9 @@
     <string name="performance" msgid="6552785217174378320">"Гүйцэтгэл"</string>
     <string name="user_interface" msgid="3712869377953950887">"Хэрэглэгчийн интерфейс"</string>
     <string name="thermal" msgid="6758074791325414831">"Дулааны"</string>
+    <string name="custom" msgid="3337456985275158299">"Захиалгат"</string>
+    <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Захиалгат ул мөрийн тохиргоо"</string>
+    <string name="restore_default" msgid="5259420807486239755">"Өгөгдмөлийг сэргээх"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Нэг гарын горим"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Сонсголын төхөөрөмжүүд"</string>
     <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Идэвхтэй"</string>
@@ -440,6 +429,17 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Тохиргоог нээх"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Бусад төхөөрөмж"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Тоймыг асаах/унтраах"</string>
+    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Чухал байдлаар нь ангилах горим"</string>
+    <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_off" msgid="1736604456618147306">"Унтраалттай"</string>
+    <!-- no translation found for zen_mode_set_up (7457957033034460064) -->
+    <skip />
+    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
+    <skip />
+    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
+    <skip />
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Танд сэрүүлэг, сануулга, арга хэмжээ, таны сонгосон дуудлага илгээгчээс бусад дуу, чичиргээ саад болохгүй. Та хөгжим, видео, тоглоом зэрэг тоглуулахыг хүссэн бүх зүйлээ сонсох боломжтой хэвээр байна."</string>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Танд сэрүүлгээс бусад дуу, чичиргээ саад болохгүй. Та хөгжим, видео, тоглоом зэрэг тоглуулахыг хүссэн бүх зүйлээ сонсох боломжтой хэвээр байна."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Тохируулах"</string>
@@ -478,6 +478,8 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Удаан цэнэглэж байна • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>-н дараа дүүрнэ"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Цэнэглэж байна • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>-н дараа дүүрнэ"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Түгжээтэй дэлгэц дээрх виджетүүд"</string>
+    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
+    <skip />
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Нийтийн практик хичээлийг эхлүүлэхийн тулд зүүн тийш шударна уу"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Өөрчлөх"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Хаах"</string>
@@ -504,10 +506,9 @@
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"виджет сонгох"</string>
     <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"виджетийг хасах"</string>
     <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"сонгосон виджетийг байрлуулах"</string>
-    <!-- no translation found for communal_widget_picker_title (1953369090475731663) -->
-    <skip />
-    <!-- no translation found for communal_widget_picker_description (490515450110487871) -->
-    <skip />
+    <string name="communal_widget_picker_title" msgid="1953369090475731663">"Түгжээтэй дэлгэцийн виджет"</string>
+    <string name="communal_widget_picker_description" msgid="490515450110487871">"Таны таблет түгжээтэй байсан ч түгжээтэй дэлгэцийн виджетийг тань дурын хүн үзнэ"</string>
+    <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"виджетийн сонголтыг болиулах"</string>
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Түгжээтэй дэлгэцийн виджет"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Виджет ашиглан аппыг нээхийн тулд та өөрийгөө мөн болохыг баталгаажуулах шаардлагатай болно. Мөн таны таблет түгжээтэй байсан ч тэдгээрийг дурын хүн үзэж болохыг санаарай. Зарим виджет таны түгжээтэй дэлгэцэд зориулагдаагүй байж магадгүй ба энд нэмэхэд аюултай байж болзошгүй."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Ойлголоо"</string>
@@ -564,8 +565,8 @@
     <string name="media_projection_action_text" msgid="3634906766918186440">"Одоо эхлүүлэх"</string>
     <string name="empty_shade_text" msgid="8935967157319717412">"Мэдэгдэл байхгүй"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"Шинэ мэдэгдэл алга"</string>
-    <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"Дасан зохицох мэдэгдэл ассан"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"Таныг богино хугацаанд олон мэдэгдэл авахад таны төхөөрөмж одоо дууны түвшнийг багасгаж, дэлгэц дээрх попапыг хоёр хүртэлх минутын турш багасгана."</string>
+    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Мэдэгдлийн хөргөлт асаалттай байна"</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Таныг хэт олон мэдэгдэл нэг дор авахад таны төхөөрөмжийн дууны түвшин болон сэрэмжлүүлэг/дохиог 2 хүртэлх минутын турш автоматаар багасгадаг."</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Унтраах"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Хуучин мэдэгдлийг харах бол түгжээг тайл"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Энэ төхөөрөмжийг таны эцэг эх удирддаг"</string>
@@ -732,8 +733,7 @@
     <string name="notification_alert_title" msgid="3656229781017543655">"Өгөгдмөл"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Автомат"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"Дуу эсвэл чичиргээ байхгүй"</string>
-    <!-- no translation found for notification_conversation_summary_low (3855696451919728790) -->
-    <skip />
+    <string name="notification_conversation_summary_low" msgid="3855696451919728790">"Дуу чимээ эсвэл чичиргээгүй хэдий ч харилцан ярианы хэсэгт харагдсаар байна"</string>
     <string name="notification_channel_summary_default" msgid="777294388712200605">"Төхөөрөмжийн тохиргоонд тулгуурлан хонх дуугаргах эсвэл чичиргэж болзошгүй"</string>
     <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Төхөөрөмжийн тохиргоонд тулгуурлан хонх дуугаргах эсвэл чичиргэж болзошгүй. <xliff:g id="APP_NAME">%1$s</xliff:g>-н харилцан яриаг өгөгдмөлөөр бөмбөлөг болгоно."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Энэ мэдэгдэл дуу гаргах эсвэл чичрэх эсэхийг системээр тодорхойлуулаарай"</string>
@@ -790,8 +790,7 @@
     <string name="keyboard_key_page_up" msgid="173914303254199845">"Хуудас дээш"</string>
     <string name="keyboard_key_page_down" msgid="9035902490071829731">"Хуудас доош"</string>
     <string name="keyboard_key_forward_del" msgid="5325501825762733459">"Устгах"</string>
-    <!-- no translation found for keyboard_key_esc (6230365950511411322) -->
-    <skip />
+    <string name="keyboard_key_esc" msgid="6230365950511411322">"Esc"</string>
     <string name="keyboard_key_move_home" msgid="3496502501803911971">"Нүүр хуудас"</string>
     <string name="keyboard_key_move_end" msgid="99190401463834854">"Төгсгөл"</string>
     <string name="keyboard_key_insert" msgid="4621692715704410493">"Оруулах"</string>
@@ -1233,7 +1232,6 @@
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Баталгаажуулалт шаардлагатай. Баталгаажуулахын тулд хурууны хээ мэдрэгчид хүрнэ үү."</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Үргэлжилж буй утасны дуудлага"</string>
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Мобайл дата"</string>
-    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Холбогдсон"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Түр зуур холбогдсон"</string>
     <string name="mobile_data_poor_connection" msgid="819617772268371434">"Холболт сул байна"</string>
@@ -1374,8 +1372,7 @@
     <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>
-    <!-- no translation found for shortcut_helper_category_current_app_shortcuts (4017840565974573628) -->
-    <skip />
+    <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_search_placeholder" msgid="5488547526269871819">"Товчлолууд хайх"</string>
@@ -1388,13 +1385,27 @@
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Болсон"</string>
     <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Үнэхээр сайн ажиллалаа!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Буцах"</string>
-    <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"Буцаж очихын тулд мэдрэгч самбар дээр гурван хуруугаараа хүссэн газраа зүүн эсвэл баруун тийш шударна уу."</string>
+    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
+    <skip />
+    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
+    <skip />
     <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Гурван хуруу баруун болон зүүн тийш хөдөлж буйг харуулсан мэдрэгч самбар"</string>
     <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Буцах зангаанд зориулсан анимацийг харуулсан төхөөрөмжийн дэлгэц"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Гарын арын гэрэл"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d-с %1$d-р түвшин"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Гэрийн удирдлага"</string>
     <string name="home_controls_dream_description" msgid="4644150952104035789">"Гэрийн удирдлагадаа дэлгэц амраагчаар шуурхай ханд"</string>
-    <!-- no translation found for volume_undo_action (5815519725211877114) -->
-    <skip />
+    <string name="volume_undo_action" msgid="5815519725211877114">"Болих"</string>
+    <string name="back_edu_toast_content" msgid="4530314597378982956">"Буцахын тулд мэдрэгч самбар дээр гурван хуруугаараа зүүн эсвэл баруун тийш шударна уу"</string>
+    <string name="home_edu_toast_content" msgid="3381071147871955415">"Нүүр хуудас руу очихын тулд мэдрэгч самбар дээр гурван хуруугаараа дээш шударна уу"</string>
+    <string name="overview_edu_toast_content" msgid="5797030644017804518">"Саяхны аппуудыг харахын тулд мэдрэгч самбар дээр гурван хуруугаараа дээш шудраад, удаан дарна уу"</string>
+    <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Бүх аппаа харахын тулд гар дээр тань байх тусгай товчлуурыг дарна уу"</string>
+    <string name="back_edu_notification_title" msgid="5624780717751357278">"Буцахын тулд мэдрэгч самбараа ашиглах"</string>
+    <string name="back_edu_notification_content" msgid="2497557451540954068">"Гурван хуруугаараа зүүн эсвэл баруун тийш шударна уу. Илүү олон зангаа сурахын тулд товшино уу."</string>
+    <string name="home_edu_notification_title" msgid="6097902076909654045">"Нүүр хуудас руу очихын тулд мэдрэгч самбараа ашиглах"</string>
+    <string name="home_edu_notification_content" msgid="6631697734535766588">"Гурван хуруугаа ашиглан дээш шударна уу. Илүү олон зангаа сурахын тулд товшино уу."</string>
+    <string name="overview_edu_notification_title" msgid="1265824157319562406">"Саяхны аппуудыг харахын тулд мэдрэгч самбараа ашиглах"</string>
+    <string name="overview_edu_notification_content" msgid="3578204677648432500">"Гурван хуруугаа ашиглан дээш шудраад, удаан дарна уу. Илүү олон зангаа сурахын тулд товшино уу."</string>
+    <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Бүх аппыг харахын тулд гараа ашиглах"</string>
+    <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Тусгай товчлуурыг хүссэн үедээ дарна уу. Илүү олон зангаа сурахын тулд товшино уу."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-mn/tiles_states_strings.xml b/packages/SystemUI/res/values-mn/tiles_states_strings.xml
index b394574..a3f5454 100644
--- a/packages/SystemUI/res/values-mn/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-mn/tiles_states_strings.xml
@@ -56,9 +56,6 @@
     <item msgid="5376619709702103243">"Унтраалттай"</item>
     <item msgid="4875147066469902392">"Асаалттай"</item>
   </string-array>
-    <!-- no translation found for tile_states_modes:0 (7764936419245199023) -->
-    <!-- no translation found for tile_states_modes:1 (2004750556637773692) -->
-    <!-- no translation found for tile_states_modes:2 (8968530753931637871) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Боломжгүй"</item>
     <item msgid="5044688398303285224">"Унтраалттай"</item>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index ed870c7..e3510ad 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -388,6 +388,9 @@
     <string name="performance" msgid="6552785217174378320">"परफॉर्मन्स"</string>
     <string name="user_interface" msgid="3712869377953950887">"यूझर इंटरफेस"</string>
     <string name="thermal" msgid="6758074791325414831">"थर्मल"</string>
+    <string name="custom" msgid="3337456985275158299">"कस्टम"</string>
+    <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"मागाच्या कस्टम सेटिंग्ज"</string>
+    <string name="restore_default" msgid="5259420807486239755">"डीफॉल्ट रिस्टोअर करा"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"एकहाती मोड"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"श्रवणयंत्रे"</string>
     <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"ॲक्टिव्ह आहे"</string>
@@ -426,6 +429,17 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"सेटिंग्ज उघडा"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"इतर डिव्हाइस"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"अवलोकन टॉगल करा."</string>
+    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"प्राधान्य मोड"</string>
+    <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_off" msgid="1736604456618147306">"बंद आहे"</string>
+    <!-- no translation found for zen_mode_set_up (7457957033034460064) -->
+    <skip />
+    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
+    <skip />
+    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
+    <skip />
     <string name="zen_priority_introduction" msgid="3159291973383796646">"अलार्म, रिमाइंडर, इव्‍हेंट आणि तुम्ही निश्चित केलेल्या कॉलर व्यतिरिक्त तुम्हाला कोणत्याही आवाज आणि कंपनांचा व्यत्त्यय आणला जाणार नाही. तरीही तुम्ही प्ले करायचे ठरवलेले कोणतेही संगीत, व्हिडिओ आणि गेमचे आवाज ऐकू शकतात."</string>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"अलार्म व्यतिरिक्त तुम्हाला कोणत्याही आवाज आणि कंपनांचा व्यत्त्यय आणला जाणार नाही. तरीही तुम्ही प्ले करायचे ठरवलेले कोणतेही संगीत, व्हिडिओ आणि गेमचे आवाज ऐकू शकतात."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"सानुकूलित करा"</string>
@@ -464,6 +478,8 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • हळू चार्ज होत आहे • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> मध्ये पूर्ण होईल"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • चार्ज होत आहे • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> मध्ये पूर्ण होईल"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"लॉक स्क्रीनवरील विजेट"</string>
+    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
+    <skip />
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"सामुदायिक ट्यूटोरियल सुरू करण्यासाठी डावीकडे स्वाइप करा"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"कस्टमाइझ करा"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"डिसमिस करा"</string>
@@ -492,6 +508,7 @@
     <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"निवडलेले विजेट ठेवा"</string>
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"लॉक स्‍क्रीन विजेट"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"तुमचा टॅबलेट लॉक केला, तरी कोणीही तुमच्या लॉक स्क्रीनवरील विजेट पाहू शकतो."</string>
+    <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"विजेटची निवड रद्द करा"</string>
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"लॉक स्‍क्रीन विजेट"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"विजेट वापरून अ‍ॅप उघडण्यासाठी, तुम्हाला हे तुम्हीच असल्याची पडताळणी करावी लागेल. तसेच, लक्षात ठेवा, तुमचा टॅबलेट लॉक असतानादेखील कोणीही ती पाहू शकते. काही विजेट कदाचित तुमच्या लॉक स्‍क्रीनसाठी नाहीत आणि ती इथे जोडणे असुरक्षित असू शकते."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"समजले"</string>
@@ -548,8 +565,8 @@
     <string name="media_projection_action_text" msgid="3634906766918186440">"आता सुरू करा"</string>
     <string name="empty_shade_text" msgid="8935967157319717412">"सूचना नाहीत"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"नवीन सूचना नाहीत"</string>
-    <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"अडॅप्टिव्ह नोटिफिकेशन सुरू आहे"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"कमी वेळेत खूप नोटिफिकेशन मिळल्यास डिव्हाइस आता कमाल २ मिनिटे व्हॉल्यूम, स्क्रीनवरील पॉप-अप कमी करते."</string>
+    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"नोटिफिकेशन कूलडाउन सुरू आहे"</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"एकाच वेळी अनेक नोटिफिकेशन मिळाल्यास, डिव्हाइसचा आवाज आणि सूचना आपोआप कमाल २ मिनिटांपर्यंत कमी होतात."</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"बंद करा"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"जुन्या सूचना पाहण्यासाठी अनलॉक करा"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"हे डिव्हाइस तुमच्या पालकाने व्यवस्थापित केले आहे"</string>
@@ -716,8 +733,7 @@
     <string name="notification_alert_title" msgid="3656229781017543655">"डीफॉल्ट"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"ऑटोमॅटिक"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"आवाज किंवा व्हायब्रेशन नाही"</string>
-    <!-- no translation found for notification_conversation_summary_low (3855696451919728790) -->
-    <skip />
+    <string name="notification_conversation_summary_low" msgid="3855696451919728790">"आवाज किंवा व्हायब्रेशन नाही, पण तरीही संभाषण विभागामध्ये दिसते"</string>
     <string name="notification_channel_summary_default" msgid="777294388712200605">"डिव्‍हाइस सेटिंग्जनुसार रिंग किंवा व्हायब्रेट होऊ शकतो"</string>
     <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"डिव्‍हाइस सेटिंग्जनुसार रिंग किंवा व्हायब्रेट होऊ शकतो. <xliff:g id="APP_NAME">%1$s</xliff:g> मधील संभाषणे बाय डीफॉल्ट बबल होतात."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"ही सूचना मिळाल्‍यावर आवाज व्‍हावा की व्हायब्रेशन व्‍हावे ते सिस्‍टममध्ये नमूद करा"</string>
@@ -1216,7 +1232,6 @@
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"ऑथेंटिकेशन आवश्यक आहे. ऑथेंटिकेट करण्यासाठी फिंगरप्रिंट सेन्सरला स्पर्श करा."</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"फोन कॉल सुरू आहे"</string>
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"मोबाइल डेटा"</string>
-    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"कनेक्ट केले आहे"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"तात्पुरते कनेक्ट केलेले"</string>
     <string name="mobile_data_poor_connection" msgid="819617772268371434">"खराब कनेक्शन"</string>
@@ -1370,7 +1385,10 @@
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"पूर्ण झाले"</string>
     <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"उत्तम कामगिरी!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"मागे जा"</string>
-    <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"मागे जाण्यासाठी, टचपॅडवर कुठेही तीन बोटांनी डावीकडे किंवा उजवीकडे स्‍वाइप करा."</string>
+    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
+    <skip />
+    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
+    <skip />
     <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"तीन बोट उजवीकडे आणि डावीकडे हलताना दाखवणारे टचपॅड"</string>
     <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"मागील जेश्चरसाठी अ‍ॅनिमेशन दाखवणारी डिव्हाइस स्क्रीन"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"कीबोर्ड बॅकलाइट"</string>
@@ -1378,4 +1396,16 @@
     <string name="home_controls_dream_label" msgid="6567105701292324257">"होम कंट्रोल"</string>
     <string name="home_controls_dream_description" msgid="4644150952104035789">"स्क्रीनसेव्हर म्हणून होम कंट्रोल झटपट ॲक्सेस करा"</string>
     <string name="volume_undo_action" msgid="5815519725211877114">"पहिल्यासारखे करा"</string>
+    <string name="back_edu_toast_content" msgid="4530314597378982956">"मागे जाण्यासाठी, टचपॅडवर तीन बोटांनी डावीकडे किंवा उजवीकडे स्वाइप करा"</string>
+    <string name="home_edu_toast_content" msgid="3381071147871955415">"होमवर जाण्यासाठी, टचपॅडवर तीन बोटांनी वरती स्वाइप करा"</string>
+    <string name="overview_edu_toast_content" msgid="5797030644017804518">"अलीकडील ॲप्स पाहण्यासाठी, टचपॅडवर तीन बोटांनी वरती स्वाइप करा आणि धरून ठेवा"</string>
+    <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"तुमची सर्व ॲप्स पाहण्यासाठी, तुमच्या कीबोर्डवरील अ‍ॅक्शन की प्रेस करा"</string>
+    <string name="back_edu_notification_title" msgid="5624780717751357278">"मागे जाण्यासाठी तुमचा टचपॅड वापरा"</string>
+    <string name="back_edu_notification_content" msgid="2497557451540954068">"तीन बोटांनी डावीकडे किंवा उजवीकडे स्वाइप करा. आणखी जेश्चर जाणून घेण्यासाठी टॅप करा."</string>
+    <string name="home_edu_notification_title" msgid="6097902076909654045">"होमवर जाण्यासाठी तुमचा टचपॅड वापरा"</string>
+    <string name="home_edu_notification_content" msgid="6631697734535766588">"तीन बोटांनी वरती स्‍वाइप करा. आणखी जेश्चर जाणून घेण्यासाठी टॅप करा."</string>
+    <string name="overview_edu_notification_title" msgid="1265824157319562406">"अलीकडील अ‍ॅप्स पाहण्यासाठी तुमचा टचपॅड वापरा"</string>
+    <string name="overview_edu_notification_content" msgid="3578204677648432500">"तीन बोटांनी वरती आणि खाली स्वाइप करा. आणखी जेश्चर जाणून घेण्यासाठी टॅप करा."</string>
+    <string name="all_apps_edu_notification_title" msgid="372262997265569063">"सर्व ॲप्स पाहण्यासाठी तुमचा कीबोर्ड वापरा"</string>
+    <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"अ‍ॅक्शन की कधीही प्रेस करा. आणखी जेश्चर जाणून घेण्यासाठी टॅप करा."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-mr/tiles_states_strings.xml b/packages/SystemUI/res/values-mr/tiles_states_strings.xml
index fdb1f40..54c320c 100644
--- a/packages/SystemUI/res/values-mr/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-mr/tiles_states_strings.xml
@@ -56,11 +56,6 @@
     <item msgid="5376619709702103243">"बंद आहे"</item>
     <item msgid="4875147066469902392">"सुरू आहे"</item>
   </string-array>
-  <string-array name="tile_states_modes">
-    <item msgid="7764936419245199023">"उपलब्ध नाही"</item>
-    <item msgid="2004750556637773692">"बंद आहे"</item>
-    <item msgid="8968530753931637871">"सुरू आहे"</item>
-  </string-array>
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"उपलब्ध नाही"</item>
     <item msgid="5044688398303285224">"बंद आहे"</item>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index 43e9716..0e91cb4 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -388,6 +388,9 @@
     <string name="performance" msgid="6552785217174378320">"Prestasi"</string>
     <string name="user_interface" msgid="3712869377953950887">"Antara Muka Pengguna"</string>
     <string name="thermal" msgid="6758074791325414831">"Terma"</string>
+    <string name="custom" msgid="3337456985275158299">"Tersuai"</string>
+    <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Tetapan Surih Tersuai"</string>
+    <string name="restore_default" msgid="5259420807486239755">"Pemulihan Lalai"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Mod sebelah tangan"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Peranti pendengaran"</string>
     <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktif"</string>
@@ -426,6 +429,14 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Buka Tetapan"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Peranti lain"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Togol Ikhtisar"</string>
+    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Mod keutamaan"</string>
+    <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Selesai"</string>
+    <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Tetapan"</string>
+    <string name="zen_mode_on" msgid="9085304934016242591">"Hidup"</string>
+    <string name="zen_mode_off" msgid="1736604456618147306">"Mati"</string>
+    <string name="zen_mode_set_up" msgid="7457957033034460064">"Sediakan"</string>
+    <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Urus dalam tetapan"</string>
+    <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Tiada mod yang aktif}=1{{mode} aktif}other{# mod aktif}}"</string>
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Anda tidak akan diganggu oleh bunyi dan getaran, kecuali daripada penggera, peringatan, acara dan pemanggil yang anda tetapkan. Anda masih mendengar item lain yang anda pilih untuk dimainkan termasuk muzik, video dan permainan."</string>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Anda tidak akan diganggu oleh bunyi dan getaran, kecuali daripada penggera. Anda masih mendengar item lain yang anda pilih untuk dimainkan termasuk muzik, video dan permainan."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Peribadikan"</string>
@@ -464,6 +475,8 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Mengecas dengan perlahan • Penuh dalam masa <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Mengecas • Penuh dalam masa <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Widget pada skrin kunci"</string>
+    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
+    <skip />
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Leret ke kiri untuk memulakan tutorial umum"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Sesuaikan"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Ketepikan"</string>
@@ -492,6 +505,7 @@
     <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"letakkan widget dipilih"</string>
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"Widget skrin kunci"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"Sesiapa sahaja boleh melihat widget pada skrin kunci, walaupun tablet dikunci."</string>
+    <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"nyahpilih widget"</string>
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widget skrin kunci"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Untuk membuka apl menggunakan widget, anda perlu mengesahkan identiti anda. Selain itu, perlu diingat bahawa sesiapa sahaja boleh melihat widget tersebut, walaupun semasa tablet anda dikunci. Sesetengah widget mungkin tidak sesuai untuk skrin kunci anda dan mungkin tidak selamat untuk ditambahkan di sini."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"OK"</string>
@@ -548,8 +562,8 @@
     <string name="media_projection_action_text" msgid="3634906766918186440">"Mulakan sekarang"</string>
     <string name="empty_shade_text" msgid="8935967157319717412">"Tiada pemberitahuan"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"Tiada pemberitahuan baharu"</string>
-    <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"Pemberitahuan boleh suai dihidupkan"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"Kelantangan &amp; tetingkap timbul pada skrin peranti anda dikurangkan selama dua minit apabila banyak pemberitahuan diterima dalam masa yang singkat."</string>
+    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Tempoh bertenang pemberitahuan dihidupkan"</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Kelantangan, makluman peranti dikurangkan secara automatik hingga 2 minit apabila menerima banyak pemberitahuan serentak."</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Matikan"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Buka kunci untuk melihat pemberitahuan lama"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Peranti ini diurus oleh ibu bapa anda"</string>
@@ -716,8 +730,7 @@
     <string name="notification_alert_title" msgid="3656229781017543655">"Lalai"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Automatik"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"Tiada bunyi atau getaran"</string>
-    <!-- no translation found for notification_conversation_summary_low (3855696451919728790) -->
-    <skip />
+    <string name="notification_conversation_summary_low" msgid="3855696451919728790">"Tiada bunyi atau getaran tetapi masih dipaparkan dalam bahagian perbualan"</string>
     <string name="notification_channel_summary_default" msgid="777294388712200605">"Mungkin berbunyi atau bergetar berdasarkan tetapan peranti"</string>
     <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Mungkin berbunyi atau bergetar berdasarkan tetapan peranti. Perbualan daripada gelembung <xliff:g id="APP_NAME">%1$s</xliff:g> secara lalai."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Minta sistem menentukan jika pemberitahuan ini patut menghasilkan bunyi atau getaran"</string>
@@ -1216,7 +1229,6 @@
     <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>
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Data mudah alih"</string>
-    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</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>
     <string name="mobile_data_poor_connection" msgid="819617772268371434">"Sambungan lemah"</string>
@@ -1370,7 +1382,10 @@
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Selesai"</string>
     <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Syabas!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Kembali"</string>
-    <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"Untuk kembali, leret ke kiri atau kanan menggunakan tiga jari di mana-mana sahaja pada pad sentuh."</string>
+    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
+    <skip />
+    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
+    <skip />
     <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Pad sentuh menunjukkan tiga jari bergerak ke kanan dan kiri"</string>
     <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Skrin peranti menunjukkan animasi untuk gerak isyarat kembali"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Cahaya latar papan kekunci"</string>
@@ -1378,4 +1393,16 @@
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Kawalan Rumah"</string>
     <string name="home_controls_dream_description" msgid="4644150952104035789">"Jadikan kawalan rumah anda sebagai penyelamat skrin"</string>
     <string name="volume_undo_action" msgid="5815519725211877114">"Buat asal"</string>
+    <string name="back_edu_toast_content" msgid="4530314597378982956">"Untuk kembali, leret ke kiri atau ke kanan dengan tiga jari pada pad sentuh"</string>
+    <string name="home_edu_toast_content" msgid="3381071147871955415">"Untuk mengakses laman utama, leret ke atas dengan tiga jari pada pad sentuh"</string>
+    <string name="overview_edu_toast_content" msgid="5797030644017804518">"Untuk melihat apl terbaharu, leret ke atas dan tahan dengan tiga jari pada pad sentuh"</string>
+    <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Untuk melihat semua apl anda, tekan kekunci tindakan pada papan kekunci anda"</string>
+    <string name="back_edu_notification_title" msgid="5624780717751357278">"Gunakan pad sentuh anda untuk kembali"</string>
+    <string name="back_edu_notification_content" msgid="2497557451540954068">"Leret ke kiri atau ke kanan dengan tiga jari. Ketik dan ketahui lebih lanjut tentang gerak isyarat."</string>
+    <string name="home_edu_notification_title" msgid="6097902076909654045">"Gunakan pad sentuh untuk mengakses laman utama"</string>
+    <string name="home_edu_notification_content" msgid="6631697734535766588">"Leret ke atas menggunakan tiga jari. Ketik untuk mengetahui lebih lanjut tentang gerak isyarat."</string>
+    <string name="overview_edu_notification_title" msgid="1265824157319562406">"Gunakan pad sentuh anda untuk melihat apl terbaharu"</string>
+    <string name="overview_edu_notification_content" msgid="3578204677648432500">"Leret ke atas, tahan dengan tiga jari. Ketik untuk mengetahui lebih lanjut tentang gerak isyarat."</string>
+    <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Gunakan papan kekunci anda untuk melihat semua apl"</string>
+    <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Tekan kekunci tindakan pada bila-bila masa. Ketik dan ketahui lebih lanjut tentang gerak isyarat."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ms/tiles_states_strings.xml b/packages/SystemUI/res/values-ms/tiles_states_strings.xml
index db63e2c..174e416e 100644
--- a/packages/SystemUI/res/values-ms/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ms/tiles_states_strings.xml
@@ -56,11 +56,6 @@
     <item msgid="5376619709702103243">"Mati"</item>
     <item msgid="4875147066469902392">"Hidup"</item>
   </string-array>
-  <string-array name="tile_states_modes">
-    <item msgid="7764936419245199023">"Tidak tersedia"</item>
-    <item msgid="2004750556637773692">"Mati"</item>
-    <item msgid="8968530753931637871">"Hidup"</item>
-  </string-array>
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Tidak tersedia"</item>
     <item msgid="5044688398303285224">"Mati"</item>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index c1f186a..d7b20ef 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -126,38 +126,25 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"ကြည့်ရှုရန် တို့ပါ"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"ဖန်သားပြင်ရိုက်ကူးမှုကို သိမ်းရာတွင် အမှားရှိသည်"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"ဖန်သားပြင် ရိုက်ကူးမှု စတင်ရာတွင် အမှားအယွင်းရှိနေသည်"</string>
-    <!-- no translation found for screenrecord_stop_dialog_title (8716193661764511095) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message (6262768207331626817) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5995770227684523244) -->
-    <skip />
+    <string name="screenrecord_stop_dialog_title" msgid="8716193661764511095">"ရိုက်ကူးခြင်း ရပ်မလား။"</string>
+    <string name="screenrecord_stop_dialog_message" msgid="6262768207331626817">"သင်သည် လက်ရှိတွင် ဖန်သားပြင်တစ်ခုလုံးကို ရိုက်ကူးနေသည်"</string>
+    <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"သင်သည် လက်ရှိတွင် <xliff:g id="APP_NAME">%1$s</xliff:g> ကို ရိုက်ကူးနေသည်"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"ရိုက်ကူးမှု ရပ်ရန်"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"ဖန်သားပြင်ကို မျှဝေနေသည်"</string>
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"ဖန်သားပြင်မျှဝေခြင်း ရပ်မလား။"</string>
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen_with_host_app (522823522115375414) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen (5090115386271179270) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_specific (5923772039347985172) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_generic (6681016774654578261) -->
-    <skip />
+    <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"သင်သည် လက်ရှိတွင် <xliff:g id="HOST_APP_NAME">%1$s</xliff:g> ဖြင့် ဖန်သားပြင်တစ်ခုလုံးကို မျှဝေနေသည်"</string>
+    <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"သင်သည် လက်ရှိတွင် အက်ပ်တစ်ခုဖြင့် ဖန်သားပြင်တစ်ခုလုံးကို မျှဝေနေသည်"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"သင်သည် လက်ရှိတွင် <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> ကို မျှဝေနေသည်"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"သင်သည် လက်ရှိတွင် အက်ပ်ကို မျှဝေနေသည်"</string>
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"မျှဝေခြင်း ရပ်ရန်"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"ဖန်သားပြင်ကို ကာစ်လုပ်နေသည်"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"ကာစ်လုပ်ခြင်းကို ရပ်လိုသလား။"</string>
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen_with_device (1474703115926205251) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen (8419219169553867625) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app_with_device (2715934698604085519) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (8616103075630934513) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic_with_device (9213582497852420203) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic (4100272100480415076) -->
-    <skip />
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen_with_device" msgid="1474703115926205251">"သင်သည် လက်ရှိတွင် ဖန်သားပြင်တစ်ခုလုံးကို <xliff:g id="DEVICE_NAME">%1$s</xliff:g> သို့ ကာစ်လုပ်နေသည်"</string>
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen" msgid="8419219169553867625">"သင်သည် လက်ရှိတွင် ဖန်သားပြင်တစ်ခုလုံးကို အနီးတစ်ဝိုက်ရှိ စက်သို့ ကာစ်လုပ်နေသည်"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app_with_device" msgid="2715934698604085519">"သင်သည် လက်ရှိတွင် <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> ကို <xliff:g id="DEVICE_NAME">%2$s</xliff:g> သို့ ကာစ်လုပ်နေသည်"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="8616103075630934513">"သင်သည် လက်ရှိတွင် <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> ကို အနီးတစ်ဝိုက်ရှိ စက်သို့ ကာစ်လုပ်နေသည်"</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic_with_device" msgid="9213582497852420203">"သင်သည် လက်ရှိတွင် <xliff:g id="DEVICE_NAME">%1$s</xliff:g> သို့ ကာစ်လုပ်နေသည်"</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic" msgid="4100272100480415076">"သင်သည် လက်ရှိတွင် အနီးတစ်ဝိုက်ရှိ စက်သို့ ကာစ်လုပ်နေသည်"</string>
     <string name="cast_to_other_device_stop_dialog_button" msgid="6420183747435521834">"ကာစ် ရပ်ရန်"</string>
     <string name="close_dialog_button" msgid="4749497706540104133">"ပိတ်ရန်"</string>
     <string name="issuerecord_title" msgid="286627115110121849">"ပြဿနာရိုက်ကူးစနစ်"</string>
@@ -303,8 +290,7 @@
     <string name="start_dreams" msgid="9131802557946276718">"စခရင်နားချိန်ပုံ"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"အီသာနက်"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"မနှောင့်ယှက်ရ"</string>
-    <!-- no translation found for quick_settings_modes_label (5407025818652750501) -->
-    <skip />
+    <string name="quick_settings_modes_label" msgid="5407025818652750501">"ဦးစားပေးမုဒ်"</string>
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"ဘလူးတုသ်"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"ချိတ်တွဲထားသည့် ကိရိယာများ မရှိ"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"စက်ကို ချိတ်ဆက်ရန် (သို့) ချိတ်ဆက်မှုဖြုတ်ရန် တို့ပါ"</string>
@@ -402,6 +388,9 @@
     <string name="performance" msgid="6552785217174378320">"စွမ်းဆောင်ရည်"</string>
     <string name="user_interface" msgid="3712869377953950887">"သုံးသူအတွက် ကြားခံစနစ်"</string>
     <string name="thermal" msgid="6758074791325414831">"အပူဓာတ်"</string>
+    <string name="custom" msgid="3337456985275158299">"စိတ်ကြိုက်"</string>
+    <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"စိတ်ကြိုက်လုပ်ဆောင်ချက်မှတ်တမ်း ဆက်တင်များ"</string>
+    <string name="restore_default" msgid="5259420807486239755">"မူရင်းအတိုင်း ပြန်ပြောင်းရန်"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"လက်တစ်ဖက်သုံးမုဒ်"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"နားကြားကိရိယာ"</string>
     <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"သုံးနေသည်"</string>
@@ -440,6 +429,14 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"ဆက်တင်များဖွင့်ရန်"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"အခြားစက်ပစ္စည်း"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"ဖွင့်၊ ပိတ် အနှစ်ချုပ်"</string>
+    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"ဦးစားပေးမုဒ်"</string>
+    <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_off" msgid="1736604456618147306">"ပိတ်"</string>
+    <string name="zen_mode_set_up" msgid="7457957033034460064">"စနစ်ထည့်သွင်းရန်"</string>
+    <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"ဆက်တင်များတွင် စီမံရန်"</string>
+    <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{အသုံးပြုနေသော မုဒ်မရှိပါ}=1{{mode} ကို အသုံးပြုနေသည်}other{မုဒ် # ခုကို အသုံးပြုနေသည်}}"</string>
     <string name="zen_priority_introduction" msgid="3159291973383796646">"နှိုးစက်သံ၊ သတိပေးချက်အသံများ၊ ပွဲစဉ်သတိပေးသံများနှင့် သင်ခွင့်ပြုထားသူများထံမှ ဖုန်းခေါ်မှုများမှလွဲ၍ အခြားအသံများနှင့် တုန်ခါမှုများက သင့်ကို အနှောင့်အယှက်ပြုမည် မဟုတ်ပါ။ သို့သော်လည်း သီချင်း၊ ဗီဒီယိုနှင့် ဂိမ်းများအပါအဝင် သင်ကရွေးချယ်ဖွင့်ထားသည့် အရာတိုင်း၏ အသံကိုမူ ကြားနေရဆဲဖြစ်ပါလိမ့်မည်။"</string>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"နှိုးစက်သံမှလွဲ၍ အခြားအသံများနှင့် တုန်ခါမှုများက သင့်ကို အနှောင့်အယှက်ပြုမည် မဟုတ်ပါ။ သို့သော်လည်း သီချင်း၊ ဗီဒီယိုနှင့် ဂိမ်းများအပါအဝင် သင်ကရွေးချယ်ဖွင့်ထားသည့် အရာတိုင်း၏ အသံကိုမူ ကြားနေရဆဲဖြစ်ပါလိမ့်မည်။"</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"စိတ်ကြိုက် ပြုလုပ်ရန်"</string>
@@ -478,6 +475,8 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • နှေးကွေးစွာ အားသွင်းနေသည် • အားပြည့်ရန် <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> လိုသည်"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • အားသွင်းနေသည် • အားပြည့်ရန် <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> လိုသည်"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"လော့ခ်မျက်နှာပြင်ရှိ ဝိဂျက်များ"</string>
+    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
+    <skip />
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"အများသုံးရှင်းလင်းပို့ချချက် စတင်ရန် ဘယ်သို့ပွတ်ဆွဲပါ"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"စိတ်ကြိုက်လုပ်ရန်"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"ပယ်ရန်"</string>
@@ -504,10 +503,9 @@
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"ဝိဂျက် ရွေးရန်"</string>
     <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"ဝိဂျက် ဖယ်ရှားရန်"</string>
     <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"ရွေးချယ်ထားသော ဝိဂျက်ကို တင်ရန်"</string>
-    <!-- no translation found for communal_widget_picker_title (1953369090475731663) -->
-    <skip />
-    <!-- no translation found for communal_widget_picker_description (490515450110487871) -->
-    <skip />
+    <string name="communal_widget_picker_title" msgid="1953369090475731663">"လော့ခ်မျက်နှာပြင် ဝိဂျက်များ"</string>
+    <string name="communal_widget_picker_description" msgid="490515450110487871">"တက်ဘလက်လော့ခ်ချထားသော်လည်း မည်သူမဆို လော့ခ်မျက်နှာပြင်ဝိဂျက်ကို ကြည့်နိုင်သည်။"</string>
+    <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"ဝိဂျက် ပြန်ဖြုတ်ရန်"</string>
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"လော့ခ်မျက်နှာပြင် ဝိဂျက်များ"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"ဝိဂျက်သုံး၍ အက်ပ်ဖွင့်ရန်အတွက် သင်ဖြစ်ကြောင်း အတည်ပြုရန်လိုသည်။ ထို့ပြင် သင့်တက်ဘလက် လော့ခ်ချထားချိန်၌ပင် မည်သူမဆို ၎င်းတို့ကို ကြည့်နိုင်ကြောင်း သတိပြုပါ။ ဝိဂျက်အချို့ကို လော့ခ်မျက်နှာပြင်အတွက် ရည်ရွယ်ထားခြင်း မရှိသဖြင့် ဤနေရာတွင် ထည့်ပါက မလုံခြုံနိုင်ပါ။"</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"နားလည်ပြီ"</string>
@@ -564,8 +562,8 @@
     <string name="media_projection_action_text" msgid="3634906766918186440">"ယခု စတင်ပါ"</string>
     <string name="empty_shade_text" msgid="8935967157319717412">"အကြောင်းကြားချက် မရှိပါ"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"အကြောင်းကြားချက်သစ် မရှိပါ"</string>
-    <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"အလိုက်သင့်အကြောင်းကြားချက် ဖွင့်ထားသည်"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"သင့်စက်သည် အချိန်တိုအတွင်း အကြောင်းကြားချက်များစွာ ရပါက အသံတိုးပြီး စခရင်ရှိ ပေါ့ပ်အပ်များကို နှစ်မိနစ်အထိ လျှော့ပေးသည်။"</string>
+    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"အကြောင်းကြားချက် သတိပေးမှု လျှော့ချခြင်း ဖွင့်ထားသည်"</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"အကြောင်းကြားချက်များစွာ တစ်ပြိုင်နက်ရပါက သင့်စက်၏ အသံအတိုးအကျယ်နှင့် သတိပေးချက်ကို ၂ မိနစ်ကြာသည်အထိ အလိုအလျောက်လျှော့ချသည်။"</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"ပိတ်ရန်"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"အကြောင်းကြားချက်ဟောင်းကြည့်ရန် လော့ခ်ဖွင့်ပါ"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"ဤစက်ပစ္စည်းကို သင့်မိဘက စီမံခန့်ခွဲသည်"</string>
@@ -732,8 +730,7 @@
     <string name="notification_alert_title" msgid="3656229781017543655">"မူလ"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"အလိုအလျောက်"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"အသံ သို့မဟုတ် တုန်ခါမှုမရှိပါ"</string>
-    <!-- no translation found for notification_conversation_summary_low (3855696451919728790) -->
-    <skip />
+    <string name="notification_conversation_summary_low" msgid="3855696451919728790">"အသံမကြားရ (သို့) တုန်ခါမှုမရှိသော်လည်း စကားဝိုင်းကဏ္ဍတွင် မြင်ရပါသေးသည်"</string>
     <string name="notification_channel_summary_default" msgid="777294388712200605">"စက်ပစ္စည်း ဆက်တင်များပေါ် အခြေခံပြီး အသံမြည်နိုင်သည် (သို့) တုန်ခါနိုင်သည်"</string>
     <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"စက်ပစ္စည်းဆက်တင်များပေါ် အခြေခံပြီး အသံမြည်နိုင်သည် (သို့) တုန်ခါနိုင်သည်။ မူရင်းသတ်မှတ်ချက်အဖြစ် <xliff:g id="APP_NAME">%1$s</xliff:g> မှ စကားဝိုင်းများကို ပူဖောင်းကွက်ဖြင့် ပြသည်။"</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"ဤအကြောင်းကြားချက်က အသံ သို့မဟုတ် တုန်ခါမှု ပေးရန် သင့်/မသင့်ကို စနစ်က ဆုံးဖြတ်ပါစေ"</string>
@@ -790,8 +787,7 @@
     <string name="keyboard_key_page_up" msgid="173914303254199845">"အပေါ်စာမျက်နှာသို့သွားပါ"</string>
     <string name="keyboard_key_page_down" msgid="9035902490071829731">"အောက်စာမျက်နှာသို့သွားပါ"</string>
     <string name="keyboard_key_forward_del" msgid="5325501825762733459">"ဖျက်ရန်"</string>
-    <!-- no translation found for keyboard_key_esc (6230365950511411322) -->
-    <skip />
+    <string name="keyboard_key_esc" msgid="6230365950511411322">"Esc"</string>
     <string name="keyboard_key_move_home" msgid="3496502501803911971">"ပင်မ"</string>
     <string name="keyboard_key_move_end" msgid="99190401463834854">"ပြီးပါပြီ"</string>
     <string name="keyboard_key_insert" msgid="4621692715704410493">"ထည့်ပါ"</string>
@@ -1233,7 +1229,6 @@
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"အထောက်အထားစိစစ်ခြင်း လိုအပ်သည်။ အထောက်အထားစိစစ်ရန် လက်ဗွေ အာရုံခံကိရိယာကို ထိပါ။"</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"လက်ရှိ ဖုန်းခေါ်ဆိုမှု"</string>
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"မိုဘိုင်းဒေတာ"</string>
-    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"ချိတ်ဆက်ထားသည်"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"ယာယီချိတ်ဆက်ထားသည်"</string>
     <string name="mobile_data_poor_connection" msgid="819617772268371434">"ချိတ်ဆက်မှုအားနည်းသည်"</string>
@@ -1387,13 +1382,27 @@
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"ပြီးပြီ"</string>
     <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"တော်ပါပေသည်။"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"ပြန်သွားရန်"</string>
-    <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"ပြန်သွားရန်အတွက် တာ့ချ်ပက်တွင် မည်သည့်နေရာ၌မဆို လက်သုံးချောင်းသုံး၍ ဘယ် (သို့) ညာသို့ ပွတ်ဆွဲပါ။"</string>
+    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
+    <skip />
+    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
+    <skip />
     <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"တာ့ချ်ပက်တွင် ဘယ်ညာရွှေ့နေသော လက်သုံးချောင်းကို ပြထားသည်"</string>
     <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"စက်စခရင်တွင် နောက်သို့လက်ဟန်အတွက် လှုပ်ရှားသက်ဝင်ပုံကို ပြထားသည်"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"ကီးဘုတ်နောက်မီး"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"အဆင့် %2$d အနက် %1$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"အိမ်ထိန်းချုပ်မှုများ"</string>
     <string name="home_controls_dream_description" msgid="4644150952104035789">"အိမ်ထိန်းချုပ်မှုများကို စခရင်နားချိန်ပုံအဖြစ် အမြန်ဝင်ကြည့်ရန်"</string>
-    <!-- no translation found for volume_undo_action (5815519725211877114) -->
-    <skip />
+    <string name="volume_undo_action" msgid="5815519725211877114">"နောက်ပြန်ရန်"</string>
+    <string name="back_edu_toast_content" msgid="4530314597378982956">"နောက်ပြန်သွားရန် တာ့ချ်ပက်ပေါ်တွင် လက်သုံးချောင်းဖြင့် ဘယ် (သို့) ညာသို့ ပွတ်ဆွဲပါ"</string>
+    <string name="home_edu_toast_content" msgid="3381071147871955415">"ပင်မစာမျက်နှာသို့ သွားရန် တာ့ချ်ပက်ပေါ်တွင် လက်သုံးချောင်းဖြင့် အပေါ်သို့ပွတ်ဆွဲပါ"</string>
+    <string name="overview_edu_toast_content" msgid="5797030644017804518">"လတ်တလောအက်ပ်များကို ကြည့်ရန် တာ့ချ်ပက်ပေါ်တွင် လက်သုံးချောင်းဖြင့် အပေါ်သို့ပွတ်ဆွဲပြီး ဖိထားပါ"</string>
+    <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"သင့်အက်ပ်အားလုံးကြည့်ရန် ကီးဘုတ်ပေါ်ရှိ လုပ်ဆောင်ချက်ကီးကို နှိပ်ပါ"</string>
+    <string name="back_edu_notification_title" msgid="5624780717751357278">"နောက်ပြန်သွားရန် သင့်တာ့ချ်ပက်ကို သုံးပါ"</string>
+    <string name="back_edu_notification_content" msgid="2497557451540954068">"လက်သုံးချောင်းဖြင့် ဘယ် (သို့) ညာသို့ ပွတ်ဆွဲပါ။ လက်ဟန်များ ပိုမိုလေ့လာရန် တို့ပါ။"</string>
+    <string name="home_edu_notification_title" msgid="6097902076909654045">"ပင်မစာမျက်နှာသို့ သွားရန် သင့်တာ့ချ်ပက်ကို သုံးပါ"</string>
+    <string name="home_edu_notification_content" msgid="6631697734535766588">"လက်သုံးချောင်းဖြင့် အပေါ်သို့ပွတ်ဆွဲပါ။ လက်ဟန်များ ပိုမိုလေ့လာရန် တို့ပါ။"</string>
+    <string name="overview_edu_notification_title" msgid="1265824157319562406">"လတ်တလောအက်ပ်များကို ကြည့်ရန် သင့်တာ့ချ်ပက်ကို သုံးပါ"</string>
+    <string name="overview_edu_notification_content" msgid="3578204677648432500">"လက်သုံးချောင်းဖြင့် အပေါ်သို့ပွတ်ဆွဲပြီး ဖိထားပါ။ လက်ဟန်များ ပိုမိုလေ့လာရန် တို့ပါ။"</string>
+    <string name="all_apps_edu_notification_title" msgid="372262997265569063">"အက်ပ်အားလုံးကြည့်ရန် သင့်ကီးဘုတ်ကို သုံးပါ"</string>
+    <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"လုပ်ဆောင်ချက်ကီးကို အချိန်မရွေးနှိပ်ပါ။ လက်ဟန်များ ပိုမိုလေ့လာရန် တို့ပါ။"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-my/tiles_states_strings.xml b/packages/SystemUI/res/values-my/tiles_states_strings.xml
index 3628c06..f665a00a 100644
--- a/packages/SystemUI/res/values-my/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-my/tiles_states_strings.xml
@@ -56,9 +56,6 @@
     <item msgid="5376619709702103243">"ပိတ်"</item>
     <item msgid="4875147066469902392">"ဖွင့်"</item>
   </string-array>
-    <!-- no translation found for tile_states_modes:0 (7764936419245199023) -->
-    <!-- no translation found for tile_states_modes:1 (2004750556637773692) -->
-    <!-- no translation found for tile_states_modes:2 (8968530753931637871) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"မရနိုင်ပါ"</item>
     <item msgid="5044688398303285224">"ပိတ်"</item>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index 199b359..07b979f 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -126,38 +126,25 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"Trykk for å se"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"Feil ved lagring av skjermopptaket"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Feil ved start av skjermopptaket"</string>
-    <!-- no translation found for screenrecord_stop_dialog_title (8716193661764511095) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message (6262768207331626817) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5995770227684523244) -->
-    <skip />
+    <string name="screenrecord_stop_dialog_title" msgid="8716193661764511095">"Vil du stoppe opptaket?"</string>
+    <string name="screenrecord_stop_dialog_message" msgid="6262768207331626817">"Du tar nå opp hele skjermen"</string>
+    <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Du tar nå opp <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Stopp opptaket"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Deler skjermen"</string>
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Vil du slutte å dele skjermen?"</string>
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen_with_host_app (522823522115375414) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen (5090115386271179270) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_specific (5923772039347985172) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_generic (6681016774654578261) -->
-    <skip />
+    <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Du deler nå hele skjermen med <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
+    <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Du deler nå hele skjermen med en app"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Du deler nå <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Du deler nå en app"</string>
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Slutt å dele"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Caster skjermen"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Vil du stoppe castingen?"</string>
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen_with_device (1474703115926205251) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen (8419219169553867625) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app_with_device (2715934698604085519) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (8616103075630934513) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic_with_device (9213582497852420203) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic (4100272100480415076) -->
-    <skip />
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen_with_device" msgid="1474703115926205251">"Du caster nå hele skjermen til <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen" msgid="8419219169553867625">"Du caster nå hele skjermen til en enhet i nærheten"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app_with_device" msgid="2715934698604085519">"Du caster nå <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> til <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="8616103075630934513">"Du caster nå <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> til en enhet i nærheten"</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic_with_device" msgid="9213582497852420203">"Du caster nå til <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic" msgid="4100272100480415076">"Du caster nå skjermen til en enhet i nærheten"</string>
     <string name="cast_to_other_device_stop_dialog_button" msgid="6420183747435521834">"Stopp castingen"</string>
     <string name="close_dialog_button" msgid="4749497706540104133">"Lukk"</string>
     <string name="issuerecord_title" msgid="286627115110121849">"Funksjon for opptak av problemer"</string>
@@ -303,8 +290,7 @@
     <string name="start_dreams" msgid="9131802557946276718">"Skjermsparer"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"Ikke forstyrr"</string>
-    <!-- no translation found for quick_settings_modes_label (5407025818652750501) -->
-    <skip />
+    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Prioritetsmoduser"</string>
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Ingen sammenkoblede enheter er tilgjengelige"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Trykk for å koble en enhet til eller fra"</string>
@@ -402,6 +388,9 @@
     <string name="performance" msgid="6552785217174378320">"Ytelse"</string>
     <string name="user_interface" msgid="3712869377953950887">"Brukergrensesnitt"</string>
     <string name="thermal" msgid="6758074791325414831">"Termisk"</string>
+    <string name="custom" msgid="3337456985275158299">"Egendefinert"</string>
+    <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Innstillinger for egendefinert spor"</string>
+    <string name="restore_default" msgid="5259420807486239755">"Gjenopprett standard"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Enhåndsmodus"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Høreapparater"</string>
     <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktiv"</string>
@@ -440,6 +429,17 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Åpne Innstillinger"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Annen enhet"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Slå oversikten av eller på"</string>
+    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Prioritetsmoduser"</string>
+    <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Ferdig"</string>
+    <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Innstillinger"</string>
+    <string name="zen_mode_on" msgid="9085304934016242591">"På"</string>
+    <string name="zen_mode_off" msgid="1736604456618147306">"Av"</string>
+    <!-- no translation found for zen_mode_set_up (7457957033034460064) -->
+    <skip />
+    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
+    <skip />
+    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
+    <skip />
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Du blir ikke forstyrret av lyder og vibrasjoner, med unntak av alarmer, påminnelser, aktiviteter og oppringere du angir. Du kan fremdeles høre alt du velger å spille av, for eksempel musikk, videoer og spill."</string>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Du blir ikke forstyrret av lyder og vibrasjoner, med unntak av alarmer. Du kan fremdeles høre alt du velger å spille av, for eksempel musikk, videoer og spill."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Tilpass"</string>
@@ -478,6 +478,8 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Lader sakte • Fulladet om <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Lader • Fulladet om <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Moduler på låseskjermen"</string>
+    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
+    <skip />
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Sveip til venstre for å starte fellesveiledningen"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Tilpass"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Lukk"</string>
@@ -504,10 +506,9 @@
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"velg modul"</string>
     <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"fjern modul"</string>
     <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"plasser den valgte modulen"</string>
-    <!-- no translation found for communal_widget_picker_title (1953369090475731663) -->
-    <skip />
-    <!-- no translation found for communal_widget_picker_description (490515450110487871) -->
-    <skip />
+    <string name="communal_widget_picker_title" msgid="1953369090475731663">"Moduler på låseskjermen"</string>
+    <string name="communal_widget_picker_description" msgid="490515450110487871">"Hvem som helst kan se moduler på låseskjermen – selv om nettbrettet er låst."</string>
+    <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"velg bort modul"</string>
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Låseskjermmoduler"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"For å åpne en app ved hjelp av en modul må du bekrefte at det er deg. Husk også at hvem som helst kan se dem, selv om nettbrettet er låst. Noen moduler er kanskje ikke laget for å være på låseskjermen og kan være utrygge å legge til der."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Greit"</string>
@@ -564,8 +565,8 @@
     <string name="media_projection_action_text" msgid="3634906766918186440">"Start nå"</string>
     <string name="empty_shade_text" msgid="8935967157319717412">"Ingen varsler"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"Ingen nye varsler"</string>
-    <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"Tilpassbare varsler er på"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"Den nye enheten din senker volumet og reduserer antall forgrunnsvinduer på skjermen i opptil to minutter når du mottar mange varsler på kort tid."</string>
+    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Varseldemping er slått på"</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Enhetsvolumet og varsler reduseres automatisk i opptil 2 min når du får for mange varsler samtidig."</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Slå av"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Lås opp for å se eldre varsler"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Denne enheten administreres av forelderen din"</string>
@@ -732,8 +733,7 @@
     <string name="notification_alert_title" msgid="3656229781017543655">"Standard"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Automatisk"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"Ingen lyd eller vibrering"</string>
-    <!-- no translation found for notification_conversation_summary_low (3855696451919728790) -->
-    <skip />
+    <string name="notification_conversation_summary_low" msgid="3855696451919728790">"Ingen lyd eller vibrering, men vises fortsatt i samtaledelen"</string>
     <string name="notification_channel_summary_default" msgid="777294388712200605">"Kan ringe eller vibrere basert på enhetsinnstillingene"</string>
     <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Kan ringe eller vibrere basert på enhetsinnstillingene. Samtaler fra <xliff:g id="APP_NAME">%1$s</xliff:g> vises som standard som bobler."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"La systemet velge om dette varselet skal lage lyd eller vibrere"</string>
@@ -790,8 +790,7 @@
     <string name="keyboard_key_page_up" msgid="173914303254199845">"Page Up"</string>
     <string name="keyboard_key_page_down" msgid="9035902490071829731">"Page Down"</string>
     <string name="keyboard_key_forward_del" msgid="5325501825762733459">"Delete"</string>
-    <!-- no translation found for keyboard_key_esc (6230365950511411322) -->
-    <skip />
+    <string name="keyboard_key_esc" msgid="6230365950511411322">"Esc"</string>
     <string name="keyboard_key_move_home" msgid="3496502501803911971">"Startskjerm"</string>
     <string name="keyboard_key_move_end" msgid="99190401463834854">"End"</string>
     <string name="keyboard_key_insert" msgid="4621692715704410493">"Insert"</string>
@@ -1233,7 +1232,6 @@
     <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>
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobildata"</string>
-    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Tilkoblet"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Koblet til midlertidig"</string>
     <string name="mobile_data_poor_connection" msgid="819617772268371434">"Dårlig forbindelse"</string>
@@ -1374,8 +1372,7 @@
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Delt skjerm"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Inndata"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"App-snarveier"</string>
-    <!-- no translation found for shortcut_helper_category_current_app_shortcuts (4017840565974573628) -->
-    <skip />
+    <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Aktiv app"</string>
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Tilgjengelighet"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Hurtigtaster"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Snarveier til søk"</string>
@@ -1388,13 +1385,27 @@
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Ferdig"</string>
     <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Bra jobbet!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Gå tilbake"</string>
-    <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"For å gå tilbake, sveip til venstre eller høyre med tre fingre hvor som helst på styreflaten."</string>
+    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
+    <skip />
+    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
+    <skip />
     <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"En styreflate med tre fingre som beveger seg til høyre og venstre"</string>
     <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Enhetsskjerm med animasjonen for tilbakebevegelse"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Bakgrunnslys for tastatur"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Nivå %1$d av %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Hjemkontroller"</string>
     <string name="home_controls_dream_description" msgid="4644150952104035789">"Gå raskt til hjemkontrollene som skjermsparer"</string>
-    <!-- no translation found for volume_undo_action (5815519725211877114) -->
-    <skip />
+    <string name="volume_undo_action" msgid="5815519725211877114">"Angre"</string>
+    <string name="back_edu_toast_content" msgid="4530314597378982956">"For å gå tilbake, sveip til venstre eller høyre med tre fingre på styreflaten"</string>
+    <string name="home_edu_toast_content" msgid="3381071147871955415">"For å gå til startsiden, sveip opp med tre fingre på styreflaten"</string>
+    <string name="overview_edu_toast_content" msgid="5797030644017804518">"For å se nylige apper, sveip opp og hold med tre fingre på styreflaten"</string>
+    <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"For å se alle appene dine, trykk på handlingstasten på tastaturet"</string>
+    <string name="back_edu_notification_title" msgid="5624780717751357278">"Bruk styreflaten for å gå tilbake"</string>
+    <string name="back_edu_notification_content" msgid="2497557451540954068">"Sveip til venstre eller høyre med tre fingre. Trykk for å lære flere bevegelser."</string>
+    <string name="home_edu_notification_title" msgid="6097902076909654045">"Bruk styreflaten for å gå til startsiden"</string>
+    <string name="home_edu_notification_content" msgid="6631697734535766588">"Sveip opp med tre fingre. Trykk for å lære flere bevegelser."</string>
+    <string name="overview_edu_notification_title" msgid="1265824157319562406">"Bruk styreflaten for å se nylige apper"</string>
+    <string name="overview_edu_notification_content" msgid="3578204677648432500">"Sveip opp og hold med tre fingre. Trykk for å lære flere bevegelser."</string>
+    <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Bruk tastaturet for å se alle apper"</string>
+    <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Trykk på handlingstasten når som helst. Trykk for å lære flere bevegelser."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-nb/tiles_states_strings.xml b/packages/SystemUI/res/values-nb/tiles_states_strings.xml
index 2a57ecf..a9efd1d 100644
--- a/packages/SystemUI/res/values-nb/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-nb/tiles_states_strings.xml
@@ -56,9 +56,6 @@
     <item msgid="5376619709702103243">"Av"</item>
     <item msgid="4875147066469902392">"På"</item>
   </string-array>
-    <!-- no translation found for tile_states_modes:0 (7764936419245199023) -->
-    <!-- no translation found for tile_states_modes:1 (2004750556637773692) -->
-    <!-- no translation found for tile_states_modes:2 (8968530753931637871) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Utilgjengelig"</item>
     <item msgid="5044688398303285224">"Av"</item>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index 672609a..67843fd 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -171,7 +171,7 @@
     <string name="accessibility_wallet_button" msgid="1458258783460555507">"Wallet"</string>
     <string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"QR कोड स्क्यानर"</string>
     <string name="accessibility_unlock_button" msgid="3613812140816244310">"अनलक गरिएको छ"</string>
-    <string name="accessibility_lock_icon" msgid="661492842417875775">"यन्त्र लक गरिएको छ"</string>
+    <string name="accessibility_lock_icon" msgid="661492842417875775">"डिभाइस लक गरिएको छ"</string>
     <string name="accessibility_scanning_face" msgid="3093828357921541387">"अनुहार स्क्यान गर्दै"</string>
     <string name="accessibility_send_smart_reply" msgid="8885032190442015141">"पठाउनुहोस्"</string>
     <string name="cancel" msgid="1089011503403416730">"रद्द गर्नुहोस्"</string>
@@ -290,8 +290,7 @@
     <string name="start_dreams" msgid="9131802557946276718">"स्क्रिन सेभर"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"बाधा नपुऱ्याउनुहोस्"</string>
-    <!-- no translation found for quick_settings_modes_label (5407025818652750501) -->
-    <skip />
+    <string name="quick_settings_modes_label" msgid="5407025818652750501">"महत्त्वपूर्ण मोडहरू"</string>
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"ब्लुटुथ"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"जोडी उपकरणहरू उपलब्ध छैन"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"कुनै डिभाइस कनेक्ट गर्न वा डिस्कनेक्ट गर्न ट्याप गर्नुहोस्"</string>
@@ -389,6 +388,9 @@
     <string name="performance" msgid="6552785217174378320">"पर्फर्मेन्स"</string>
     <string name="user_interface" msgid="3712869377953950887">"युजर इन्टरफेस"</string>
     <string name="thermal" msgid="6758074791325414831">"थर्मल"</string>
+    <string name="custom" msgid="3337456985275158299">"कस्टम"</string>
+    <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"कस्टम ट्रेस सेटिङ"</string>
+    <string name="restore_default" msgid="5259420807486239755">"डिफल्ट सेटिङ रिस्टोर गर्नुहोस्"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"एक हाते मोड"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"हियरिङ डिभाइसहरू"</string>
     <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"सक्रिय छ"</string>
@@ -427,6 +429,17 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"सेटिङ खोल्नुहोस्"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"अर्को डिभाइड"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"परिदृश्य टगल गर्नुहोस्"</string>
+    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"महत्त्वपूर्ण मोडहरू"</string>
+    <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_off" msgid="1736604456618147306">"अफ छ"</string>
+    <!-- no translation found for zen_mode_set_up (7457957033034460064) -->
+    <skip />
+    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
+    <skip />
+    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
+    <skip />
     <string name="zen_priority_introduction" msgid="3159291973383796646">"तपाईंलाई अलार्म, रिमाइन्डर, कार्यक्रम र तपाईंले निर्दिष्ट गर्नुभएका कलरहरू बाहेकका ध्वनि र कम्पनहरूले बाधा पुऱ्याउने छैनन्। तपाईंले अझै सङ्गीत, भिडियो र खेलहरू लगायत आफूले प्ले गर्न छनौट गरेका जुनसुकै कुरा सुन्न सक्नुहुनेछ।"</string>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"तपाईंलाई अलार्महरू बाहेकका ध्वनि र कम्पनहरूले बाधा पुऱ्याउने छैनन्। तपाईंले अझै सङ्गीत, भिडियो र खेलहरू लगायत आफूले प्ले गर्न छनौट गरेका जुनसुकै कुरा सुन्न सक्नुहुनेछ।"</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">" कस्टम बनाउनुहोस्"</string>
@@ -465,6 +478,8 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • बिस्तारै चार्ज हुँदै छ • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> मा पूरै चार्ज हुन्छ"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • चार्ज हुँदै छ • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> मा फुल चार्ज हुने छ"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"लक स्क्रिनमा भएका विजेटहरू"</string>
+    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
+    <skip />
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"कम्युनल ट्युटोरियल सुरु गर्न बायाँतिर स्वाइप गर्नुहोस्"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"कस्टमाइज गर्नुहोस्"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"खारेज गर्नुहोस्"</string>
@@ -493,6 +508,7 @@
     <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"चयन गरिएका विजेटका लागि ठाउँ चयन गर्नुहोस्"</string>
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"लक स्क्रिन विजेटहरू"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"तपाईंको ट्याब्लेट लक भएका बेला पनि सबैले लक स्क्रिनमा भएका विजेट हेर्न सक्छन्।"</string>
+    <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"विजेटको चयन रद्द गर्नुहोस्"</string>
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"लक स्क्रिन विजेटहरू"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"विजेट प्रयोग गरी एप खोल्न तपाईंले आफ्नो पहिचान पुष्टि गर्नु पर्ने हुन्छ। साथै, तपाईंको ट्याब्लेट लक भएका बेला पनि सबै जनाले तिनलाई देख्न सक्छन् भन्ने कुरा ख्याल गर्नुहोस्। केही विजेटहरू लक स्क्रिनमा प्रयोग गर्ने उद्देश्यले नबनाइएका हुन सक्छन् र तिनलाई यहाँ हाल्नु सुरक्षित नहुन सक्छ।"</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"बुझेँ"</string>
@@ -549,8 +565,8 @@
     <string name="media_projection_action_text" msgid="3634906766918186440">"अहिले न"</string>
     <string name="empty_shade_text" msgid="8935967157319717412">"कुनै सूचनाहरू छैनन्"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"कुनै पनि नयाँ सूचना छैन"</string>
-    <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"एड्याप्टिभ नोटिफिकेसन अन छ"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"तपाईंले छोटो समयमा धेरै नोटिफिकेसन प्राप्त गर्दा तपाईंको डिभाइसले अब दुई मिनेटसम्म भोल्युम र स्क्रिनमा देखिने पप-अपको सङ्ख्या घटाउँछ।"</string>
+    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"नोटिफिकेसन कुलडाउन अन छ"</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"तपाईंले एकै पटक धेरै नोटिफिकेसन प्राप्त गर्दा बढीमा २ मिनेटसम्म तपाईंको डिभाइसको भोल्युम र अलर्टहरूको सङ्ख्या स्वतः घटाइन्छ।"</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"अफ गर्नुहोस्"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"पुराना सूचनाहरू हेर्न अनलक गर्नुहोस्"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"यो डिभाइस तपाईंका अभिभावक व्यवस्थापन गर्नुहुन्छ"</string>
@@ -717,8 +733,7 @@
     <string name="notification_alert_title" msgid="3656229781017543655">"डिफल्ट"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"स्वचालित"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"बज्दैन पनि, भाइब्रेट पनि हुँदैन"</string>
-    <!-- no translation found for notification_conversation_summary_low (3855696451919728790) -->
-    <skip />
+    <string name="notification_conversation_summary_low" msgid="3855696451919728790">"नोटिफिकेसन आउँदा भाइब्रेसन वा साउन्ड आउँदैन तर अझै पनि \"वार्तालाप\" खण्डमा देखिन्छ"</string>
     <string name="notification_channel_summary_default" msgid="777294388712200605">"डिभाइसको सेटिङका आधारमा घन्टी बज्न वा भाइब्रेट हुन सक्छ"</string>
     <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"डिभाइसको सेटिङका आधारमा घन्टी बज्न वा कम्पन हुन सक्छ। <xliff:g id="APP_NAME">%1$s</xliff:g> मार्फत गरिएका वार्तालापहरू स्वतः बबलमा देखिन्छन्।"</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"सिस्टमलाई यो सूचना आउँदा ध्वनि बज्नु पर्छ वा कम्पन हुनु पर्छ भन्ने कुराको निधो गर्न दिनुहोस्"</string>
@@ -1082,7 +1097,7 @@
     <string name="controls_dialog_ok" msgid="2770230012857881822">"थप्नुहोस्"</string>
     <string name="controls_dialog_remove" msgid="3775288002711561936">"हटाउनुहोस्"</string>
     <string name="controls_dialog_message" msgid="342066938390663844">"<xliff:g id="APP">%s</xliff:g> ले सिफारिस गरेको"</string>
-    <string name="controls_tile_locked" msgid="731547768182831938">"यन्त्र लक गरिएको छ"</string>
+    <string name="controls_tile_locked" msgid="731547768182831938">"डिभाइस लक गरिएको छ"</string>
     <string name="controls_settings_show_controls_dialog_title" msgid="3357852503553809554">"लक स्क्रिनमै डिभाइसहरू देखाउने र लक स्क्रिनबाटै ती डिभाइसहरू नियन्त्रण गर्ने हो?"</string>
     <string name="controls_settings_show_controls_dialog_message" msgid="7666211700524587969">"तपाईं आफ्ना बाह्य डिभाइसहरूका कन्ट्रोलहरू लक स्क्रिनमा हाल्न सक्नुहुन्छ।\n\nतपाईंको डिभाइसको एपले तपाईंलाई आफ्नो फोन वा ट्याब्लेट अनलक नगरिकनै केही डिभाइसहरू नियन्त्रण गर्ने अनुमति दिन सक्छ।\n\nतपाईं जुनसुकै बेला सेटिङमा गई यी कुराहरू बदल्न सक्नुहुन्छ।"</string>
     <string name="controls_settings_trivial_controls_dialog_title" msgid="7593188157655036677">"लक स्क्रिनबाटै डिभाइसहरू नियन्त्रण गर्ने हो?"</string>
@@ -1217,7 +1232,6 @@
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"पुष्टि गर्नु पर्ने हुन्छ। पुष्टि गर्न फिंगरप्रिन्ट सेन्सर छुनुहोस्।"</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"जारी फोन कल"</string>
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"मोबाइल डेटा"</string>
-    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"इन्टरनेटमा कनेक्ट गरिएको छ"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"यसमा केही समयका लागि कनेक्ट गरिएको हो"</string>
     <string name="mobile_data_poor_connection" msgid="819617772268371434">"इन्टरनेट राम्री चलेको छैन"</string>
@@ -1371,13 +1385,27 @@
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"सम्पन्न भयो"</string>
     <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"अद्भुत!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"पछाडि जानुहोस्"</string>
-    <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"पछाडि जान तिन वटा औँलाले टचप्याडमा कतै छोएर बायाँ वा दायाँतिर स्वाइप गर्नुहोस्।"</string>
+    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
+    <skip />
+    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
+    <skip />
     <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"तिन वटा औँला दायाँ र बायाँ सारेको देखाइएको टचप्याड"</string>
     <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"पछाडि जाने जेस्चरको एनिमेसन देखाइएको डिभाइसको स्क्रिन"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"किबोर्ड ब्याकलाइट"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d मध्ये %1$d औँ स्तर"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"होम कन्ट्रोलहरू"</string>
     <string name="home_controls_dream_description" msgid="4644150952104035789">"होम कन्ट्रोललाई तुरुन्तै स्क्रिनसेभरका रूपमा एक्सेस गर्नुहोस्"</string>
-    <!-- no translation found for volume_undo_action (5815519725211877114) -->
-    <skip />
+    <string name="volume_undo_action" msgid="5815519725211877114">"अन्डू गर्नुहोस्"</string>
+    <string name="back_edu_toast_content" msgid="4530314597378982956">"पछाडि जान तिन वटा औँलाले टचप्याडमा बायाँ वा दायाँतिर स्वाइप गर्नुहोस्"</string>
+    <string name="home_edu_toast_content" msgid="3381071147871955415">"होममा जान तिन वटा औँलाले टचप्याडमा माथितिर स्वाइप गर्नुहोस्"</string>
+    <string name="overview_edu_toast_content" msgid="5797030644017804518">"आफूले हालसालै चलाएका एपहरू हेर्न तिन वटा औँलाले टचप्याडमा माथितिर स्वाइप गर्नुहोस् र होल्ड गर्नुहोस्"</string>
+    <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"आफ्ना सबै एपहरू हेर्न आफ्नो किबोर्डमा भएको एक्सन की थिच्नुहोस्"</string>
+    <string name="back_edu_notification_title" msgid="5624780717751357278">"पछाडि जान आफ्नो टचप्याड प्रयोग गर्नुहोस्"</string>
+    <string name="back_edu_notification_content" msgid="2497557451540954068">"तिन वटा औँला प्रयोग गरी बायाँ वा दायाँतिर स्वाइप गर्नुहोस्। थप जेस्चर प्रयोग गर्ने तरिका सिक्न ट्याप गर्नुहोस्।"</string>
+    <string name="home_edu_notification_title" msgid="6097902076909654045">"होममा जान आफ्नो टचप्याड प्रयोग गर्नुहोस्"</string>
+    <string name="home_edu_notification_content" msgid="6631697734535766588">"तिन वटा औँला प्रयोग गरी माथितिर स्वाइप गर्नुहोस्। थप जेस्चर प्रयोग गर्ने तरिका सिक्न ट्याप गर्नुहोस्।"</string>
+    <string name="overview_edu_notification_title" msgid="1265824157319562406">"आफूले हालसालै चलाएका एपहरू हेर्न आफ्नो टचप्याड प्रयोग गर्नुहोस्"</string>
+    <string name="overview_edu_notification_content" msgid="3578204677648432500">"तिन वटा औँला प्रयोग गरी माथितिर स्वाइप गर्नुहोस् र होल्ड गर्नुहोस्। थप जेस्चर प्रयोग गर्ने तरिका सिक्न ट्याप गर्नुहोस्।"</string>
+    <string name="all_apps_edu_notification_title" msgid="372262997265569063">"सबै एपहरू हेर्न आफ्नो किबोर्ड प्रयोग गर्नुहोस्"</string>
+    <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"जुनसुकै बेला एक्सन की थिच्नुहोस्। थप जेस्चर प्रयोग गर्ने तरिका सिक्न ट्याप गर्नुहोस्।"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ne/tiles_states_strings.xml b/packages/SystemUI/res/values-ne/tiles_states_strings.xml
index 7edd8bf..c1b2f34 100644
--- a/packages/SystemUI/res/values-ne/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ne/tiles_states_strings.xml
@@ -56,9 +56,6 @@
     <item msgid="5376619709702103243">"अफ छ"</item>
     <item msgid="4875147066469902392">"अन छ"</item>
   </string-array>
-    <!-- no translation found for tile_states_modes:0 (7764936419245199023) -->
-    <!-- no translation found for tile_states_modes:1 (2004750556637773692) -->
-    <!-- no translation found for tile_states_modes:2 (8968530753931637871) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"उपलब्ध छैन"</item>
     <item msgid="5044688398303285224">"अफ छ"</item>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index 7f86f23..d259a3d 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -290,8 +290,7 @@
     <string name="start_dreams" msgid="9131802557946276718">"Screensaver"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"Niet storen"</string>
-    <!-- no translation found for quick_settings_modes_label (5407025818652750501) -->
-    <skip />
+    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Prioriteitsmodi"</string>
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Geen gekoppelde apparaten beschikbaar"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Tik om een apparaat te verbinden of de verbinding te verbreken"</string>
@@ -389,6 +388,9 @@
     <string name="performance" msgid="6552785217174378320">"Prestaties"</string>
     <string name="user_interface" msgid="3712869377953950887">"Gebruikersinterface"</string>
     <string name="thermal" msgid="6758074791325414831">"Thermisch"</string>
+    <string name="custom" msgid="3337456985275158299">"Aangepast"</string>
+    <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Aangepaste traceringsinstellingen"</string>
+    <string name="restore_default" msgid="5259420807486239755">"Standaardinstellingen terugzetten"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Bediening met 1 hand"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Hoortoestellen"</string>
     <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Actief"</string>
@@ -427,6 +429,17 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Instellingen openen"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Ander apparaat"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Overzicht aan- of uitzetten"</string>
+    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Prioriteitsmodi"</string>
+    <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Klaar"</string>
+    <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Instellingen"</string>
+    <string name="zen_mode_on" msgid="9085304934016242591">"Aan"</string>
+    <string name="zen_mode_off" msgid="1736604456618147306">"Uit"</string>
+    <!-- no translation found for zen_mode_set_up (7457957033034460064) -->
+    <skip />
+    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
+    <skip />
+    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
+    <skip />
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Je wordt niet gestoord door geluiden en trillingen, behalve bij wekkers, herinneringen, afspraken en specifieke bellers die je selecteert. Je kunt nog steeds alles horen wat je wilt afspelen, waaronder muziek, video\'s en games."</string>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Je wordt niet gestoord door geluiden en trillingen, behalve bij wekkers. Je kunt nog steeds alles horen wat je wilt afspelen, waaronder muziek, video\'s en games."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Aanpassen"</string>
@@ -465,6 +478,8 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Langzaam opladen • Vol over <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Opladen • Vol over <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Widgets op het vergrendelscherm"</string>
+    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
+    <skip />
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Swipe naar links om de communitytutorial te starten"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Aanpassen"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Sluiten"</string>
@@ -493,6 +508,7 @@
     <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"geselecteerde widget plaatsen"</string>
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"Widgets op het vergrendelscherm"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"Iedereen kan widgets op je vergrendelscherm bekijken, ook als je tablet is vergrendeld."</string>
+    <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"widget deselecteren"</string>
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widgets op het vergrendelscherm"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Als je een app wilt openen met een widget, moet je laten verifiëren dat jij het bent. Houd er ook rekening mee dat iedereen ze kan bekijken, ook als je tablet vergrendeld is. Bepaalde widgets zijn misschien niet bedoeld voor je vergrendelscherm en kunnen hier niet veilig worden toegevoegd."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"OK"</string>
@@ -549,8 +565,8 @@
     <string name="media_projection_action_text" msgid="3634906766918186440">"Nu starten"</string>
     <string name="empty_shade_text" msgid="8935967157319717412">"Geen meldingen"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"Geen nieuwe meldingen"</string>
-    <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"Aanpasbare meldingen staan aan"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"Je apparaat verlaagt nu het volume en vermindert pop-ups op het scherm gedurende maximaal 2 minuten als je in korte tijd veel meldingen krijgt."</string>
+    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Afkoelperiode van meldingen staat aan"</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Je apparaatvolume en meldingen worden automatisch maximaal 2 minuten beperkt als je te veel meldingen tegelijk krijgt."</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Uitzetten"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Ontgrendel om oudere meldingen te zien"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Dit apparaat wordt beheerd door je ouder"</string>
@@ -717,8 +733,7 @@
     <string name="notification_alert_title" msgid="3656229781017543655">"Standaard"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Automatisch"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"Geen geluid of trilling"</string>
-    <!-- no translation found for notification_conversation_summary_low (3855696451919728790) -->
-    <skip />
+    <string name="notification_conversation_summary_low" msgid="3855696451919728790">"Geen geluid of trilling, maar verschijnt wel in het gespreksgedeelte"</string>
     <string name="notification_channel_summary_default" msgid="777294388712200605">"Kan overgaan of trillen op basis van de apparaatinstellingen"</string>
     <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Kan overgaan of trillen op basis van de apparaatinstellingen. Gesprekken uit <xliff:g id="APP_NAME">%1$s</xliff:g> worden standaard als bubbels weergegeven."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Het systeem laten bepalen of deze melding geluid moet maken of moet trillen"</string>
@@ -1217,7 +1232,6 @@
     <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>
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobiele data"</string>
-    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Verbonden"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Tijdelijk verbonden"</string>
     <string name="mobile_data_poor_connection" msgid="819617772268371434">"Matige verbinding"</string>
@@ -1371,13 +1385,27 @@
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Klaar"</string>
     <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Goed werk!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Terug"</string>
-    <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"Als je wilt teruggaan, swipe je met 3 vingers naar links of rechts op de touchpad."</string>
+    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
+    <skip />
+    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
+    <skip />
     <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Touchpad met 3 vingers die naar rechts en links bewegen"</string>
     <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Apparaatscherm met animatie voor teruggebaar"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Achtergrondverlichting van toetsenbord"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Niveau %1$d van %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Bediening voor in huis"</string>
     <string name="home_controls_dream_description" msgid="4644150952104035789">"Gebruik bediening voor in huis als screensaver"</string>
-    <!-- no translation found for volume_undo_action (5815519725211877114) -->
-    <skip />
+    <string name="volume_undo_action" msgid="5815519725211877114">"Ongedaan maken"</string>
+    <string name="back_edu_toast_content" msgid="4530314597378982956">"Als je wilt teruggaan, swipe je met 3 vingers naar links of rechts op de touchpad"</string>
+    <string name="home_edu_toast_content" msgid="3381071147871955415">"Als je naar het startscherm wilt gaan, swipe je met 3 vingers omhoog op de touchpad"</string>
+    <string name="overview_edu_toast_content" msgid="5797030644017804518">"Als je recente apps wilt bekijken, swipe je met 3 vingers omhoog op de touchpad en houd je vast"</string>
+    <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Als je alle apps wilt bekijken, druk je op de actietoets op je toetsenbord"</string>
+    <string name="back_edu_notification_title" msgid="5624780717751357278">"Je touchpad gebruiken om terug te gaan"</string>
+    <string name="back_edu_notification_content" msgid="2497557451540954068">"Swipe met 3 vingers naar links of rechts. Tik voor meer gebaren."</string>
+    <string name="home_edu_notification_title" msgid="6097902076909654045">"Je touchpad gebruiken om naar het startscherm te gaan"</string>
+    <string name="home_edu_notification_content" msgid="6631697734535766588">"Swipe met 3 vingers omhoog. Tik voor meer gebaren."</string>
+    <string name="overview_edu_notification_title" msgid="1265824157319562406">"Je touchpad gebruiken om recente apps te bekijken"</string>
+    <string name="overview_edu_notification_content" msgid="3578204677648432500">"Swipe met 3 vingers omhoog en houd vast. Tik voor meer gebaren."</string>
+    <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Je toetsenbord gebruiken om alle apps te bekijken"</string>
+    <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Druk op de actietoets wanneer je wilt. Tik voor meer gebaren."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-nl/tiles_states_strings.xml b/packages/SystemUI/res/values-nl/tiles_states_strings.xml
index 45664b8..c5d9361 100644
--- a/packages/SystemUI/res/values-nl/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-nl/tiles_states_strings.xml
@@ -56,9 +56,6 @@
     <item msgid="5376619709702103243">"Uit"</item>
     <item msgid="4875147066469902392">"Aan"</item>
   </string-array>
-    <!-- no translation found for tile_states_modes:0 (7764936419245199023) -->
-    <!-- no translation found for tile_states_modes:1 (2004750556637773692) -->
-    <!-- no translation found for tile_states_modes:2 (8968530753931637871) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Niet beschikbaar"</item>
     <item msgid="5044688398303285224">"Uit"</item>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index 88aea15..11681c9 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -126,38 +126,25 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"ଦେଖିବାକୁ ଟାପ୍ କରନ୍ତୁ"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"ସ୍କ୍ରିନ ରେକର୍ଡିଂ ସେଭ କରିବାରେ ତ୍ରୁଟି"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"ସ୍କ୍ରିନ୍ ରେକର୍ଡିଂ ଆରମ୍ଭ କରିବାରେ ତ୍ରୁଟି"</string>
-    <!-- no translation found for screenrecord_stop_dialog_title (8716193661764511095) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message (6262768207331626817) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5995770227684523244) -->
-    <skip />
+    <string name="screenrecord_stop_dialog_title" msgid="8716193661764511095">"ରେକର୍ଡିଂ ବନ୍ଦ କରିବେ?"</string>
+    <string name="screenrecord_stop_dialog_message" msgid="6262768207331626817">"ଆପଣ ବର୍ତ୍ତମାନ ଆପଣଙ୍କର ସମ୍ପୂର୍ଣ୍ଣ ସ୍କ୍ରିନକୁ ରେକର୍ଡ କରୁଛନ୍ତି"</string>
+    <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"ଆପଣ ବର୍ତ୍ତମାନ <xliff:g id="APP_NAME">%1$s</xliff:g>ର ବିଷୟବସ୍ତୁକୁ ରେକର୍ଡ କରୁଛନ୍ତି"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"ରେକର୍ଡିଂ ବନ୍ଦ କରନ୍ତୁ"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"ସ୍କ୍ରିନ ସେୟାର କରାଯାଉଛି"</string>
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"ସ୍କ୍ରିନ ସେୟାର କରିବା ବନ୍ଦ କରିବେ?"</string>
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen_with_host_app (522823522115375414) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen (5090115386271179270) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_specific (5923772039347985172) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_generic (6681016774654578261) -->
-    <skip />
+    <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"ଆପଣ ବର୍ତ୍ତମାନ ଆପଣଙ୍କର ସମ୍ପୂର୍ଣ୍ଣ ସ୍କ୍ରିନକୁ <xliff:g id="HOST_APP_NAME">%1$s</xliff:g> ସହ ସେୟାର କରୁଛନ୍ତି"</string>
+    <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"ଆପଣ ବର୍ତ୍ତମାନ ଆପଣଙ୍କର ସମ୍ପୂର୍ଣ୍ଣ ସ୍କ୍ରିନକୁ ଏକ ଆପ ସହ ସେୟାର କରୁଛନ୍ତି"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"ଆପଣ ବର୍ତ୍ତମାନ <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>କୁ ସେୟାର କରୁଛନ୍ତି"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"ଆପଣ ବର୍ତ୍ତମାନ ଏକ ଆପକୁ ସେୟାର କରୁଛନ୍ତି"</string>
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"ସେୟାର କରିବା ବନ୍ଦ କରନ୍ତୁ"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"ସ୍କ୍ରିନ କାଷ୍ଟ କରାଯାଉଛି"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"କାଷ୍ଟ କରିବା ବନ୍ଦ କରିବେ?"</string>
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen_with_device (1474703115926205251) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen (8419219169553867625) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app_with_device (2715934698604085519) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (8616103075630934513) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic_with_device (9213582497852420203) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic (4100272100480415076) -->
-    <skip />
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen_with_device" msgid="1474703115926205251">"ଆପଣ ବର୍ତ୍ତମାନ ଆପଣଙ୍କର ସମ୍ପୂର୍ଣ୍ଣ ସ୍କ୍ରିନକୁ <xliff:g id="DEVICE_NAME">%1$s</xliff:g>ରେ କାଷ୍ଟ କରୁଛନ୍ତି"</string>
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen" msgid="8419219169553867625">"ଆପଣ ବର୍ତ୍ତମାନ ଆପଣଙ୍କର ସମ୍ପୂର୍ଣ୍ଣ ସ୍କ୍ରିନକୁ ଆଖପାଖର ଏକ ଡିଭାଇସରେ କାଷ୍ଟ କରୁଛନ୍ତି"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app_with_device" msgid="2715934698604085519">"ଆପଣ ବର୍ତ୍ତମାନ <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>କୁ <xliff:g id="DEVICE_NAME">%2$s</xliff:g>ରେ କାଷ୍ଟ କରୁଛନ୍ତି"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="8616103075630934513">"ଆପଣ ବର୍ତ୍ତମାନ <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>କୁ ଆଖପାଖର ଏକ ଡିଭାଇସରେ କାଷ୍ଟ କରୁଛନ୍ତି"</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic_with_device" msgid="9213582497852420203">"ଆପଣ ବର୍ତ୍ତମାନ <xliff:g id="DEVICE_NAME">%1$s</xliff:g>ରେ କାଷ୍ଟ କରୁଛନ୍ତି"</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic" msgid="4100272100480415076">"ଆପଣ ବର୍ତ୍ତମାନ ଆଖପାଖର ଏକ ଡିଭାଇସରେ କାଷ୍ଟ କରୁଛନ୍ତି"</string>
     <string name="cast_to_other_device_stop_dialog_button" msgid="6420183747435521834">"କାଷ୍ଟ କରିବା ବନ୍ଦ କରନ୍ତୁ"</string>
     <string name="close_dialog_button" msgid="4749497706540104133">"ବନ୍ଦ କରନ୍ତୁ"</string>
     <string name="issuerecord_title" msgid="286627115110121849">"ସମସ୍ୟା ରେକର୍ଡର"</string>
@@ -303,8 +290,7 @@
     <string name="start_dreams" msgid="9131802557946276718">"ସ୍କ୍ରିନ୍‌ ସେଭର୍‌"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"ଇଥରନେଟ୍‌"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"ବିରକ୍ତ କରନ୍ତୁ ନାହିଁ"</string>
-    <!-- no translation found for quick_settings_modes_label (5407025818652750501) -->
-    <skip />
+    <string name="quick_settings_modes_label" msgid="5407025818652750501">"ପ୍ରାଥମିକତା ମୋଡ"</string>
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"ବ୍ଲୁଟୁଥ"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"ପେୟାର୍‍ ହୋଇଥିବା କୌଣସି ଡିଭାଇସ୍ ଉପଲବ୍ଧ ନାହିଁ"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"ଏକ ଡିଭାଇସ କନେକ୍ଟ କିମ୍ବା ଡିସକନେକ୍ଟ କରିବାକୁ ଟାପ କରନ୍ତୁ"</string>
@@ -402,6 +388,9 @@
     <string name="performance" msgid="6552785217174378320">"ପରଫରମାନ୍ସ"</string>
     <string name="user_interface" msgid="3712869377953950887">"ୟୁଜର ଇଣ୍ଟରଫେସ"</string>
     <string name="thermal" msgid="6758074791325414831">"ଥର୍ମାଲ"</string>
+    <string name="custom" msgid="3337456985275158299">"କଷ୍ଟମ"</string>
+    <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"କଷ୍ଟମ ଟ୍ରେସ ସେଟିଂସ"</string>
+    <string name="restore_default" msgid="5259420807486239755">"ଡିଫଲ୍ଟ ରିଷ୍ଟୋର କରନ୍ତୁ"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"ଏକ-ହାତ ମୋଡ"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"ଶ୍ରବଣ ଡିଭାଇସଗୁଡ଼ିକ"</string>
     <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"ସକ୍ରିୟ"</string>
@@ -440,6 +429,17 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"ସେଟିଂସ ଖୋଲନ୍ତୁ"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"ଅନ୍ୟ ଡିଭାଇସ୍"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"ସଂକ୍ଷିପ୍ତ ବିବରଣୀକୁ ଟୋଗଲ୍ କରନ୍ତୁ"</string>
+    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"ପ୍ରାଥମିକତା ମୋଡ"</string>
+    <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_off" msgid="1736604456618147306">"ବନ୍ଦ ଅଛି"</string>
+    <!-- no translation found for zen_mode_set_up (7457957033034460064) -->
+    <skip />
+    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
+    <skip />
+    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
+    <skip />
     <string name="zen_priority_introduction" msgid="3159291973383796646">"ଆଲାର୍ମ, ରିମାଇଣ୍ଡର୍‌, ଇଭେଣ୍ଟ ଏବଂ ଆପଣ ନିର୍ଦ୍ଦିଷ୍ଟ କରିଥିବା କଲର୍‌ଙ୍କ ବ୍ୟତୀତ ଆପଣଙ୍କ ଧ୍ୟାନ ଅନ୍ୟ କୌଣସି ଧ୍ୱନୀ ଏବଂ ଭାଇବ୍ରେଶନ୍‌ରେ ଆକର୍ଷଣ କରାଯିବନାହିଁ। ମ୍ୟୁଜିକ୍‍, ଭିଡିଓ ଏବଂ ଗେମ୍‌ ସମେତ ନିଜେ ଚଲାଇବାକୁ ବାଛିଥିବା ଅନ୍ୟ ସବୁକିଛି ଆପଣ ଶୁଣିପାରିବେ।"</string>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"ଆଲାର୍ମ ବ୍ୟତୀତ ଆପଣଙ୍କ ଧ୍ୟାନ ଅନ୍ୟ କୌଣସି ଧ୍ୱନୀ ଏବଂ ଭାଇବ୍ରେଶନ୍‌ରେ ଆକର୍ଷଣ କରାଯିବନାହିଁ। ମ୍ୟୁଜିକ୍‍, ଭିଡିଓ ଏବଂ ଗେମ୍‌ ସମେତ ନିଜେ ଚଲାଇବାକୁ ବାଛିଥିବା ଅନ୍ୟ ସବୁକିଛି ଆପଣ ଶୁଣିପାରିବେ।"</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"କଷ୍ଟମାଇଜ୍‌ କରନ୍ତୁ"</string>
@@ -478,6 +478,8 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ଧୀରେ ଚାର୍ଜ ହେଉଛି • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>ରେ ସମ୍ପୂର୍ଣ୍ଣ ହେବ"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ଚାର୍ଜ ହେଉଛି • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>ରେ ସମ୍ପୂର୍ଣ୍ଣ ହେବ"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"ଲକ ସ୍କ୍ରିନରେ ଥିବା ୱିଜେଟଗୁଡ଼ିକ"</string>
+    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
+    <skip />
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"କମ୍ୟୁନାଲ ଟ୍ୟୁଟୋରିଆଲ ଆରମ୍ଭ କରିବା ପାଇଁ ବାମକୁ ସ୍ୱାଇପ କରନ୍ତୁ"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"କଷ୍ଟମାଇଜ କରନ୍ତୁ"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"ଖାରଜ କରନ୍ତୁ"</string>
@@ -504,11 +506,10 @@
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"ୱିଜେଟ ଚୟନ କରନ୍ତୁ"</string>
     <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"ୱିଜେଟକୁ କାଢ଼ି ଦିଅନ୍ତୁ"</string>
     <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"ଚୟନିତ ୱିଜେଟ ରଖନ୍ତୁ"</string>
-    <!-- no translation found for communal_widget_picker_title (1953369090475731663) -->
-    <skip />
-    <!-- no translation found for communal_widget_picker_description (490515450110487871) -->
-    <skip />
-    <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"ଲକ ସ୍କ୍ରିନ ୱିଜେଟଗୁଡ଼ିକ"</string>
+    <string name="communal_widget_picker_title" msgid="1953369090475731663">"ଲକ ସ୍କ୍ରିନ ୱିଜେଟ"</string>
+    <string name="communal_widget_picker_description" msgid="490515450110487871">"ଆପଣଙ୍କ ଟାବଲେଟ ଲକ ଥିଲେ ମଧ୍ୟ ଯେ କୌଣସି ବ୍ୟକ୍ତି ଲକ ସ୍କ୍ରିନରେ ୱିଜେଟକୁ ଭ୍ୟୁ କରିପାରିବେ।"</string>
+    <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"ୱିଜେଟକୁ ଅଚୟନ କରନ୍ତୁ"</string>
+    <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"ଲକ ସ୍କ୍ରିନ ୱିଜେଟ"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"ଏକ ୱିଜେଟ ବ୍ୟବହାର କରି ଗୋଟିଏ ଆପ ଖୋଲିବା ପାଇଁ ଏହା ଆପଣ ଅଟନ୍ତି ବୋଲି ଆପଣଙ୍କୁ ଯାଞ୍ଚ କରିବାକୁ ହେବ। ଆହୁରି ମଧ୍ୟ, ଆପଣଙ୍କ ଟାବଲେଟ ଲକ ଥିଲେ ମଧ୍ୟ ଯେ କୌଣସି ବ୍ୟକ୍ତି ଏହାକୁ ଭ୍ୟୁ କରିପାରିବେ ବୋଲି ମନେ ରଖନ୍ତୁ। କିଛି ୱିଜେଟ ଆପଣଙ୍କ ଲକ ସ୍କ୍ରିନ ପାଇଁ ଉଦ୍ଦିଷ୍ଟ ହୋଇନଥାଇପାରେ ଏବଂ ଏଠାରେ ଯୋଗ କରିବା ଅସୁରକ୍ଷିତ ହୋଇପାରେ।"</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"ବୁଝିଗଲି"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ୟୁଜର୍‍ ବଦଳାନ୍ତୁ"</string>
@@ -564,8 +565,8 @@
     <string name="media_projection_action_text" msgid="3634906766918186440">"ବର୍ତ୍ତମାନ ଆରମ୍ଭ କରନ୍ତୁ"</string>
     <string name="empty_shade_text" msgid="8935967157319717412">"କୌଣସି ବିଜ୍ଞପ୍ତି ନାହିଁ"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"କୌଣସି ନୂଆ ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକ ନାହିଁ"</string>
-    <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"ଆଡେପ୍ଟିଭ ବିଜ୍ଞପ୍ତି ଚାଲୁ ଅଛି"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"କମ ସମୟ ମଧ୍ୟରେ ଅନେକ ବିଜ୍ଞପ୍ତି ପାଇଲେ ଆପଣଙ୍କ ଡିଭାଇସ ଏବେ ଦୁଇ ମିନିଟ ପର୍ଯ୍ୟନ୍ତ ଭଲ୍ୟୁମକୁ କମ କରି ସ୍କ୍ରିନରେ ଥିବା ପପ-ଅପକୁ କମ କରେ।"</string>
+    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"ବିଜ୍ଞପ୍ତି କୁଲଡାଉନ ଚାଲୁ ଅଛି"</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"ଆପଣ ଥରକେ ଏକାଧିକ ବିଜ୍ଞପ୍ତି ପ୍ରାପ୍ତ କଲେ ଆପଣଙ୍କ ଡିଭାଇସର ଭଲ୍ୟୁମ ଓ ଆଲର୍ଟ ସ୍ୱତଃ 2 ମିନିଟ ପର୍ଯ୍ୟନ୍ତ କମ ହୁଏ।"</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"ବନ୍ଦ କରନ୍ତୁ"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"ପୁରୁଣା ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକ ଦେଖିବାକୁ ଅନଲକ କରନ୍ତୁ"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"ଏହି ଡିଭାଇସ୍ ଆପଣଙ୍କ ବାପାମାଙ୍କ ଦ୍ୱାରା ପରିଚାଳିତ"</string>
@@ -732,8 +733,7 @@
     <string name="notification_alert_title" msgid="3656229781017543655">"ଡିଫଲ୍ଟ"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"ସ୍ୱଚାଳିତ"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"କୌଣସି ସାଉଣ୍ଡ କିମ୍ବା ଭାଇବ୍ରେସନ୍ ନାହିଁ"</string>
-    <!-- no translation found for notification_conversation_summary_low (3855696451919728790) -->
-    <skip />
+    <string name="notification_conversation_summary_low" msgid="3855696451919728790">"କୌଣସି ସାଉଣ୍ଡ ବା ଭାଇବ୍ରେସନ ନାହିଁ କିନ୍ତୁ ଏବେ ବି ବାର୍ତ୍ତାଳାପ ବିଭାଗରେ ଦେଖାଯାଏ"</string>
     <string name="notification_channel_summary_default" msgid="777294388712200605">"ଡିଭାଇସ ସେଟିଂସ ଆଧାରରେ ରିଂ କିମ୍ବା ଭାଇବ୍ରେଟ ହୋଇପାରେ"</string>
     <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"ଡିଭାଇସ ସେଟିଂସ ଆଧାରରେ ରିଂ କିମ୍ବା ଭାଇବ୍ରେଟ ହୋଇପାରେ। ଡିଫଲ୍ଟ ଭାବରେ <xliff:g id="APP_NAME">%1$s</xliff:g>ରୁ ବାର୍ତ୍ତାଳାପଗୁଡ଼ିକ ବବଲ ଭାବେ ଦେଖାଯାଏ।"</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"ଏହି ବିଜ୍ଞପ୍ତି ପ୍ରାପ୍ତ ହେବା ସମୟରେ ସାଉଣ୍ଡ ହେବା ଉଚିତ ନା ଭାଇବ୍ରେସନ୍ ତାହା ସିଷ୍ଟମକୁ ସ୍ଥିର କରିବାକୁ ଦିଅନ୍ତୁ"</string>
@@ -790,8 +790,7 @@
     <string name="keyboard_key_page_up" msgid="173914303254199845">"ଉପର ପୃଷ୍ଠା"</string>
     <string name="keyboard_key_page_down" msgid="9035902490071829731">"ତଳ ପୃଷ୍ଠା"</string>
     <string name="keyboard_key_forward_del" msgid="5325501825762733459">"ଡିଲିଟ କରନ୍ତୁ"</string>
-    <!-- no translation found for keyboard_key_esc (6230365950511411322) -->
-    <skip />
+    <string name="keyboard_key_esc" msgid="6230365950511411322">"ଏସକେପ"</string>
     <string name="keyboard_key_move_home" msgid="3496502501803911971">"ହୋମ"</string>
     <string name="keyboard_key_move_end" msgid="99190401463834854">"ସମାପ୍ତ"</string>
     <string name="keyboard_key_insert" msgid="4621692715704410493">"ଇନ୍‌ସର୍ଟ"</string>
@@ -1233,7 +1232,6 @@
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"ପ୍ରମାଣୀକରଣ ଆବଶ୍ୟକ। ପ୍ରମାଣୀକରଣ କରିବାକୁ ଟିପଚିହ୍ନ ସେନ୍ସରକୁ ସ୍ପର୍ଶ କରନ୍ତୁ।"</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"ଚାଲୁଥିବା ଫୋନ୍ କଲ୍"</string>
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"ମୋବାଇଲ ଡାଟା"</string>
-    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"ସଂଯୋଗ କରାଯାଇଛି"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"ଅସ୍ଥାୟୀ ରୂପେ କନେକ୍ଟ କରାଯାଇଛି"</string>
     <string name="mobile_data_poor_connection" msgid="819617772268371434">"ଦୁର୍ବଳ କନେକ୍ସନ"</string>
@@ -1374,8 +1372,7 @@
     <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>
-    <!-- no translation found for shortcut_helper_category_current_app_shortcuts (4017840565974573628) -->
-    <skip />
+    <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_search_placeholder" msgid="5488547526269871819">"ସର୍ଚ୍ଚ ସର୍ଟକଟ"</string>
@@ -1388,13 +1385,27 @@
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"ହୋଇଗଲା"</string>
     <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"ବଢ଼ିଆ କାମ!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"ପଛକୁ ଫେରନ୍ତୁ"</string>
-    <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"ପଛକୁ ଫେରିବା ପାଇଁ ଯେ କୌଣସି ସ୍ଥାନରେ ତିନି ଆଙ୍ଗୁଠି ବ୍ୟବହାର କରି ବାମ କିମ୍ବା ଡାହାଣକୁ ସ୍ୱାଇପ କରନ୍ତୁ।"</string>
+    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
+    <skip />
+    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
+    <skip />
     <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"ଡାହାଣ ଏବଂ ବାମକୁ ତିନି ଆଙ୍ଗୁଠି ମୁଭ କରୁଥିବା ଟଚପେଡ ଦେଖାଉଛି"</string>
     <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"ଡିଭାଇସ ସ୍କ୍ରିନ ବେକ ଜେଶ୍ଚର ପାଇଁ ଆନିମେସନ ଦେଖାଉଛି"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"କୀବୋର୍ଡ ବେକଲାଇଟ"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$dରୁ %1$d ନମ୍ବର ଲେଭେଲ"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"ହୋମ କଣ୍ଟ୍ରୋଲ୍ସ"</string>
     <string name="home_controls_dream_description" msgid="4644150952104035789">"ସ୍କ୍ରିନସେଭର ଭାବେ ହୋମ କଣ୍ଟ୍ରୋଲ୍ସକୁ ଶୀଘ୍ର ଆକ୍ସେସ କରନ୍ତୁ"</string>
-    <!-- no translation found for volume_undo_action (5815519725211877114) -->
-    <skip />
+    <string name="volume_undo_action" msgid="5815519725211877114">"ଅନଡୁ କରନ୍ତୁ"</string>
+    <string name="back_edu_toast_content" msgid="4530314597378982956">"ପଛକୁ ଫେରିବା ପାଇଁ ଟଚପେଡରେ ତିନୋଟି ଆଙ୍ଗୁଠିରେ ବାମ କିମ୍ବା ଡାହାଣ ସ୍ୱାଇପ କରନ୍ତୁ"</string>
+    <string name="home_edu_toast_content" msgid="3381071147871955415">"ହୋମକୁ ଯିବା ପାଇଁ ଟଚପେଡରେ ତିନୋଟି ଆଙ୍ଗୁଠିରେ ଉପରକୁ ସ୍ୱାଇପ କରନ୍ତୁ"</string>
+    <string name="overview_edu_toast_content" msgid="5797030644017804518">"ବର୍ତ୍ତମାନର ଆପ୍ସ ଭ୍ୟୁ କରିବାକୁ, ଟଚପେଡରେ ତିନୋଟି ଆଙ୍ଗୁଠିରେ ଉପରକୁ ସ୍ୱାଇପ କରି ଧରି ରଖନ୍ତୁ"</string>
+    <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"ଆପଣଙ୍କ ସମସ୍ତ ଆପ୍ସ ଭ୍ୟୁ କରିବା ପାଇଁ ଆପଣଙ୍କ କୀବୋର୍ଡରେ ଆକ୍ସନ କୀ\'କୁ ଦବାନ୍ତୁ"</string>
+    <string name="back_edu_notification_title" msgid="5624780717751357278">"ପଛକୁ ଫେରିବା ପାଇଁ ଆପଣଙ୍କ ଟଚପେଡକୁ ବ୍ୟବହାର କରନ୍ତୁ"</string>
+    <string name="back_edu_notification_content" msgid="2497557451540954068">"ତିନୋଟି ଆଙ୍ଗୁଠିରେ ବାମ ବା ଡାହାଣକୁ ସ୍ୱାଇପ କରନ୍ତୁ। ଜେଶ୍ଚରଗୁଡ଼ିକ ବିଷୟରେ ଅଧିକ ଜାଣିବାକୁ ଟାପ କରନ୍ତୁ।"</string>
+    <string name="home_edu_notification_title" msgid="6097902076909654045">"ହୋମକୁ ଯିବା ପାଇଁ ଆପଣଙ୍କ ଟଚପେଡ ବ୍ୟବହାର କରନ୍ତୁ"</string>
+    <string name="home_edu_notification_content" msgid="6631697734535766588">"ତିନୋଟି ଆଙ୍ଗୁଠି ବ୍ୟବହାର କରି ଉପରକୁ ସ୍ୱାଇପ କରନ୍ତୁ। ଜେଶ୍ଚରଗୁଡ଼ିକ ବିଷୟରେ ଅଧିକ ଜାଣିବାକୁ ଟାପ କରନ୍ତୁ।"</string>
+    <string name="overview_edu_notification_title" msgid="1265824157319562406">"ବର୍ତ୍ତମାନର ଆପ୍ସ ଭ୍ୟୁ କରିବାକୁ ଆପଣଙ୍କ ଟଚପେଡକୁ ବ୍ୟବହାର କରନ୍ତୁ"</string>
+    <string name="overview_edu_notification_content" msgid="3578204677648432500">"ତିନୋଟି ଆଙ୍ଗୁଠିରେ ଉପରକୁ ସ୍ୱାଇପ କରି ଧରି ରଖନ୍ତୁ। ଜେଶ୍ଚରଗୁଡ଼ିକ ବିଷୟରେ ଅଧିକ ଜାଣିବାକୁ ଟାପ କରନ୍ତୁ।"</string>
+    <string name="all_apps_edu_notification_title" msgid="372262997265569063">"ସମସ୍ତ ଆପ୍ସ ଭ୍ୟୁ କରିବା ପାଇଁ ଆପଣଙ୍କ କୀବୋର୍ଡକୁ ବ୍ୟବହାର କରନ୍ତୁ"</string>
+    <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"ଯେ କୌଣସି ସମୟରେ ଆକ୍ସନ କୀ\'କୁ ଦବାନ୍ତୁ। ଜେଶ୍ଚରଗୁଡ଼ିକ ବିଷୟରେ ଅଧିକ ଜାଣିବାକୁ ଟାପ କରନ୍ତୁ।"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-or/tiles_states_strings.xml b/packages/SystemUI/res/values-or/tiles_states_strings.xml
index 1bc369b..fe187c2 100644
--- a/packages/SystemUI/res/values-or/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-or/tiles_states_strings.xml
@@ -56,9 +56,6 @@
     <item msgid="5376619709702103243">"ବନ୍ଦ ଅଛି"</item>
     <item msgid="4875147066469902392">"ଚାଲୁ ଅଛି"</item>
   </string-array>
-    <!-- no translation found for tile_states_modes:0 (7764936419245199023) -->
-    <!-- no translation found for tile_states_modes:1 (2004750556637773692) -->
-    <!-- no translation found for tile_states_modes:2 (8968530753931637871) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"ଉପଲବ୍ଧ ନାହିଁ"</item>
     <item msgid="5044688398303285224">"ବନ୍ଦ ଅଛି"</item>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index 3f06376..ef642ff 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -126,38 +126,25 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"ਦੇਖਣ ਲਈ ਟੈਪ ਕਰੋ"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"ਸਕ੍ਰੀਨ ਰਿਕਾਰਡਿੰਗ ਨੂੰ ਰੱਖਿਅਤ ਕਰਨ ਵੇਲੇ ਗੜਬੜ ਹੋ ਗਈ"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"ਸਕ੍ਰੀਨ ਰਿਕਾਰਡਿੰਗ ਨੂੰ ਸ਼ੁਰੂ ਕਰਨ ਵੇਲੇ ਗੜਬੜ ਹੋਈ"</string>
-    <!-- no translation found for screenrecord_stop_dialog_title (8716193661764511095) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message (6262768207331626817) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5995770227684523244) -->
-    <skip />
+    <string name="screenrecord_stop_dialog_title" msgid="8716193661764511095">"ਕੀ ਰਿਕਾਰਡਿੰਗ ਬੰਦ ਕਰਨੀ ਹੈ?"</string>
+    <string name="screenrecord_stop_dialog_message" msgid="6262768207331626817">"ਤੁਸੀਂ ਫ਼ਿਲਹਾਲ ਆਪਣੀ ਪੂਰੀ ਸਕ੍ਰੀਨ ਨੂੰ ਰਿਕਾਰਡ ਕਰ ਰਹੇ ਹੋ"</string>
+    <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"ਤੁਸੀਂ ਫ਼ਿਲਹਾਲ <xliff:g id="APP_NAME">%1$s</xliff:g> ਨੂੰ ਰਿਕਾਰਡ ਕਰ ਰਹੇ ਹੋ"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"ਰਿਕਾਰਡਿੰਗ ਬੰਦ ਕਰੋ"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"ਸਕ੍ਰੀਨ ਸਾਂਝੀ ਕੀਤੀ ਜਾ ਰਹੀ ਹੈ"</string>
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"ਕੀ ਸਕ੍ਰੀਨ ਨੂੰ ਸਾਂਝਾ ਕਰਨਾ ਬੰਦ ਕਰਨਾ ਹੈ?"</string>
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen_with_host_app (522823522115375414) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen (5090115386271179270) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_specific (5923772039347985172) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_generic (6681016774654578261) -->
-    <skip />
+    <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"ਤੁਸੀਂ ਫ਼ਿਲਹਾਲ <xliff:g id="HOST_APP_NAME">%1$s</xliff:g> ਨਾਲ ਆਪਣੀ ਪੂਰੀ ਸਕ੍ਰੀਨ ਨੂੰ ਸਾਂਝਾ ਕਰ ਰਹੇ ਹੋ"</string>
+    <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"ਤੁਸੀਂ ਫ਼ਿਲਹਾਲ ਕਿਸੇ ਐਪ ਨਾਲ ਆਪਣੀ ਪੂਰੀ ਸਕ੍ਰੀਨ ਨੂੰ ਸਾਂਝਾ ਕਰ ਰਹੇ ਹੋ"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"ਤੁਸੀਂ ਫ਼ਿਲਹਾਲ <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> ਨੂੰ ਸਾਂਝਾ ਕਰ ਰਹੇ ਹੋ"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"ਤੁਸੀਂ ਫ਼ਿਲਹਾਲ ਕਿਸੇ ਐਪ ਨੂੰ ਸਾਂਝਾ ਕਰ ਰਹੇ ਹੋ"</string>
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"ਸਾਂਝਾਕਰਨ ਬੰਦ ਕਰੋ"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"ਸਕ੍ਰੀਨ \'ਤੇ ਕਾਸਟ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"ਕੀ ਕਾਸਟ ਕਰਨਾ ਬੰਦ ਕਰਨਾ ਹੈ?"</string>
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen_with_device (1474703115926205251) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen (8419219169553867625) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app_with_device (2715934698604085519) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (8616103075630934513) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic_with_device (9213582497852420203) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic (4100272100480415076) -->
-    <skip />
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen_with_device" msgid="1474703115926205251">"ਤੁਸੀਂ ਫ਼ਿਲਹਾਲ ਆਪਣੀ ਪੂਰੀ ਸਕ੍ਰੀਨ ਨੂੰ <xliff:g id="DEVICE_NAME">%1$s</xliff:g> \'ਤੇ ਕਾਸਟ ਕਰ ਰਹੇ ਹੋ"</string>
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen" msgid="8419219169553867625">"ਤੁਸੀਂ ਫ਼ਿਲਹਾਲ ਆਪਣੀ ਪੂਰੀ ਸਕ੍ਰੀਨ ਨੂੰ ਨਜ਼ਦੀਕੀ ਡੀਵਾਈਸ \'ਤੇ ਕਾਸਟ ਕਰ ਰਹੇ ਹੋ"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app_with_device" msgid="2715934698604085519">"ਤੁਸੀਂ ਫ਼ਿਲਹਾਲ <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> ਨੂੰ <xliff:g id="DEVICE_NAME">%2$s</xliff:g> \'ਤੇ ਕਾਸਟ ਕਰ ਰਹੇ ਹੋ"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="8616103075630934513">"ਤੁਸੀਂ ਫ਼ਿਲਹਾਲ <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> ਨੂੰ ਨਜ਼ਦੀਕੀ ਡੀਵਾਈਸ \'ਤੇ ਕਾਸਟ ਕਰ ਰਹੇ ਹੋ"</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic_with_device" msgid="9213582497852420203">"ਤੁਸੀਂ ਫ਼ਿਲਹਾਲ <xliff:g id="DEVICE_NAME">%1$s</xliff:g> \'ਤੇ ਕਾਸਟ ਕਰ ਰਹੇ ਹੋ"</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic" msgid="4100272100480415076">"ਤੁਸੀਂ ਫ਼ਿਲਹਾਲ ਨਜ਼ਦੀਕੀ ਡੀਵਾਈਸ \'ਤੇ ਕਾਸਟ ਕਰ ਰਹੇ ਹੋ"</string>
     <string name="cast_to_other_device_stop_dialog_button" msgid="6420183747435521834">"ਕਾਸਟ ਕਰਨਾ ਬੰਦ ਕਰੋ"</string>
     <string name="close_dialog_button" msgid="4749497706540104133">"ਬੰਦ ਕਰੋ"</string>
     <string name="issuerecord_title" msgid="286627115110121849">"ਸਮੱਸਿਆ ਰਿਕਾਰਡਰ"</string>
@@ -303,8 +290,7 @@
     <string name="start_dreams" msgid="9131802557946276718">"ਸਕ੍ਰੀਨ ਸੇਵਰ"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"ਈਥਰਨੈਟ"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"ਪਰੇਸ਼ਾਨ ਨਾ ਕਰੋ"</string>
-    <!-- no translation found for quick_settings_modes_label (5407025818652750501) -->
-    <skip />
+    <string name="quick_settings_modes_label" msgid="5407025818652750501">"ਤਰਜੀਹ ਮੋਡ"</string>
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"ਬਲੂਟੁੱਥ"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"ਕੋਈ ਜੋੜਾਬੱਧ ਕੀਤੀਆਂ ਡੀਵਾਈਸਾਂ ਉਪਲਬਧ ਨਹੀਂ"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"ਡੀਵਾਈਸ ਨੂੰ ਕਨੈਕਟ ਜਾਂ ਡਿਸਕਨੈਕਟ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ"</string>
@@ -402,6 +388,9 @@
     <string name="performance" msgid="6552785217174378320">"ਕਾਰਗੁਜ਼ਾਰੀ"</string>
     <string name="user_interface" msgid="3712869377953950887">"ਯੂਜ਼ਰ ਇੰਟਰਫ਼ੇਸ"</string>
     <string name="thermal" msgid="6758074791325414831">"ਥਰਮਲ"</string>
+    <string name="custom" msgid="3337456985275158299">"ਵਿਉਂਤਬੱਧ"</string>
+    <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"ਵਿਉਂਤਬੱਧ ਟ੍ਰੇਸ ਸੈਟਿੰਗਾਂ"</string>
+    <string name="restore_default" msgid="5259420807486239755">"ਪੂਰਵ ਨਿਰਧਾਰਿਤ ਨੂੰ ਮੁੜ-ਬਹਾਲ ਕਰੋ"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"ਇੱਕ ਹੱਥ ਮੋਡ"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"ਸੁਣਨ ਵਾਲੇ ਡੀਵਾਈਸ"</string>
     <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"ਕਿਰਿਆਸ਼ੀਲ"</string>
@@ -440,6 +429,17 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"ਸੈਟਿੰਗਾਂ ਖੋਲ੍ਹੋ"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"ਹੋਰ ਡੀਵਾਈਸ"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"ਰੂਪ-ਰੇਖਾ ਨੂੰ ਟੌਗਲ ਕਰੋ"</string>
+    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"ਤਰਜੀਹ ਮੋਡ"</string>
+    <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_off" msgid="1736604456618147306">"ਬੰਦ"</string>
+    <!-- no translation found for zen_mode_set_up (7457957033034460064) -->
+    <skip />
+    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
+    <skip />
+    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
+    <skip />
     <string name="zen_priority_introduction" msgid="3159291973383796646">"ਧੁਨੀਆਂ ਅਤੇ ਥਰਥਰਾਹਟਾਂ ਤੁਹਾਨੂੰ ਪਰੇਸ਼ਾਨ ਨਹੀਂ ਕਰਨਗੀਆਂ, ਸਿਵਾਏ ਅਲਾਰਮਾਂ, ਯਾਦ-ਦਹਾਨੀਆਂ, ਵਰਤਾਰਿਆਂ, ਅਤੇ ਤੁਹਾਡੇ ਵੱਲੋਂ ਨਿਰਧਾਰਤ ਕੀਤੇ ਕਾਲਰਾਂ ਦੀ ਸੂਰਤ ਵਿੱਚ। ਤੁਸੀਂ ਅਜੇ ਵੀ ਸੰਗੀਤ, ਵੀਡੀਓ ਅਤੇ ਗੇਮਾਂ ਸਮੇਤ ਆਪਣੀ ਚੋਣ ਅਨੁਸਾਰ ਕੁਝ ਵੀ ਸੁਣ ਸਕਦੇ ਹੋ।"</string>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"ਧੁਨੀਆਂ ਅਤੇ ਥਰਥਰਾਹਟਾਂ ਤੁਹਾਨੂੰ ਪਰੇਸ਼ਾਨ ਨਹੀਂ ਕਰਨਗੀਆਂ, ਸਿਵਾਏ ਅਲਾਰਮਾਂ ਦੀ ਸੂਰਤ ਵਿੱਚ। ਤੁਸੀਂ ਅਜੇ ਵੀ ਸੰਗੀਤ, ਵੀਡੀਓ ਅਤੇ ਗੇਮਾਂ ਸਮੇਤ ਆਪਣੀ ਚੋਣ ਅਨੁਸਾਰ ਕੁਝ ਵੀ ਸੁਣ ਸਕਦੇ ਹੋ।"</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"ਵਿਉਂਤਬੱਧ ਕਰੋ"</string>
@@ -478,6 +478,8 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ਹੌਲੀ ਚਾਰਜ ਹੋ ਰਿਹਾ ਹੈ • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> ਵਿੱਚ ਪੂਰਾ ਚਾਰਜ ਹੋਵੇਗਾ"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ਚਾਰਜ ਹੋ ਰਿਹਾ ਹੈ • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> ਵਿੱਚ ਪੂਰਾ ਚਾਰਜ ਹੋਵੇਗਾ"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"ਲਾਕ ਸਕ੍ਰੀਨ \'ਤੇ ਵਿਜੇਟ"</string>
+    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
+    <skip />
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"ਭਾਈਚਾਰਕ ਟਿਊਟੋਰੀਅਲ ਸ਼ੁਰੂ ਕਰਨ ਲਈ ਖੱਬੇ ਪਾਸੇ ਵੱਲ ਸਵਾਈਪ ਕਰੋ"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"ਵਿਉਂਤਬੱਧ ਕਰੋ"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"ਖਾਰਜ ਕਰੋ"</string>
@@ -504,10 +506,9 @@
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"ਵਿਜੇਟ ਚੁਣੋ"</string>
     <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"ਵਿਜੇਟ ਹਟਾਓ"</string>
     <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"ਚੁਣੇ ਗਏ ਵਿਜੇਟ ਲਈ ਥਾਂ ਚੁਣੋ"</string>
-    <!-- no translation found for communal_widget_picker_title (1953369090475731663) -->
-    <skip />
-    <!-- no translation found for communal_widget_picker_description (490515450110487871) -->
-    <skip />
+    <string name="communal_widget_picker_title" msgid="1953369090475731663">"ਲਾਕ ਸਕ੍ਰੀਨ ਵਿਜੇਟ"</string>
+    <string name="communal_widget_picker_description" msgid="490515450110487871">"ਕੋਈ ਵੀ ਤੁਹਾਡੀ ਲਾਕ ਸਕ੍ਰੀਨ \'ਤੇ ਵਿਜੇਟ ਦੇਖ ਸਕਦਾ ਹੈ, ਭਾਵੇਂ ਤੁਹਾਡਾ ਟੈਬਲੈੱਟ ਲਾਕ ਹੋਵੇ।"</string>
+    <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"ਵਿਜੇਟ ਨੂੰ ਅਣਚੁਣਿਆ ਕਰੋ"</string>
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"ਲਾਕ ਸਕ੍ਰੀਨ ਵਿਜੇਟ"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"ਵਿਜੇਟ ਦੀ ਵਰਤੋਂ ਕਰ ਕੇ ਐਪ ਖੋਲ੍ਹਣ ਲਈ, ਤੁਹਾਨੂੰ ਇਹ ਪੁਸ਼ਟੀ ਕਰਨ ਦੀ ਲੋੜ ਪਵੇਗੀ ਕਿ ਇਹ ਤੁਸੀਂ ਹੀ ਹੋ। ਨਾਲ ਹੀ, ਇਹ ਵੀ ਧਿਆਨ ਵਿੱਚ ਰੱਖੋ ਕਿ ਕੋਈ ਵੀ ਉਨ੍ਹਾਂ ਨੂੰ ਦੇਖ ਸਕਦਾ ਹੈ, ਭਾਵੇਂ ਤੁਹਾਡੀ ਟੈਬਲੈੱਟ ਲਾਕ ਹੋਵੇ। ਹੋ ਸਕਦਾ ਹੈ ਕਿ ਕੁਝ ਵਿਜੇਟ ਤੁਹਾਡੀ ਲਾਕ ਸਕ੍ਰੀਨ ਲਈ ਨਾ ਬਣੇ ਹੋਣ ਅਤੇ ਉਨ੍ਹਾਂ ਨੂੰ ਇੱਥੇ ਸ਼ਾਮਲ ਕਰਨਾ ਅਸੁਰੱਖਿਅਤ ਹੋਵੇ।"</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"ਸਮਝ ਲਿਆ"</string>
@@ -564,8 +565,8 @@
     <string name="media_projection_action_text" msgid="3634906766918186440">"ਹੁਣੇ ਸ਼ੁਰੂ ਕਰੋ"</string>
     <string name="empty_shade_text" msgid="8935967157319717412">"ਕੋਈ ਸੂਚਨਾ ਨਹੀਂ"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"ਕੋਈ ਨਵੀਂ ਸੂਚਨਾ ਨਹੀਂ ਹੈ"</string>
-    <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"ਅਡੈਪਟਿਵ ਸੂਚਨਾਵਾਂ ਚਾਲੂ ਹਨ"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"ਘੱਟ ਸਮੇਂ \'ਚ ਕਈ ਸੂਚਨਾਵਾਂ ਮਿਲਣ \'ਤੇ, ਡੀਵਾਈਸ ਹੁਣ ਦੋ ਮਿੰਟਾਂ ਲਈ ਸਕ੍ਰੀਨ \'ਤੇ ਅਵਾਜ਼ ਅਤੇ ਪੌਪ-ਅੱਪ ਘਟਾ ਦਿੰਦਾ ਹੈ।"</string>
+    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"ਨੋਟੀਫ਼ਿਕੇਸ਼ਨ ਕੂਲਡਾਊਨ ਚਾਲੂ ਹੈ"</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"ਇੱਕ ਵਾਰ \'ਚ ਕਈ ਸੂਚਨਾਵਾਂ ਮਿਲਣ \'ਤੇ, ਡੀਵਾਈਸ ਦੀ ਅਵਾਜ਼ ਤੇ ਅਲਰਟ ਵੱਧੋ-ਵੱਧ 2 ਮਿੰਟਾਂ ਲਈ ਆਪਣੇ ਆਪ ਘਟ ਹੋ ਜਾਂਦੇ ਹਨ।"</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"ਬੰਦ ਕਰੋ"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"ਪੁਰਾਣੀਆਂ ਸੂਚਨਾਵਾਂ ਦੇਖਣ ਲਈ ਅਣਲਾਕ ਕਰੋ"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"ਇਸ ਡੀਵਾਈਸ ਦਾ ਪ੍ਰਬੰਧਨ ਤੁਹਾਡੇ ਮਾਂ-ਪਿਓ ਵੱਲੋਂ ਕੀਤਾ ਜਾਂਦਾ ਹੈ"</string>
@@ -732,8 +733,7 @@
     <string name="notification_alert_title" msgid="3656229781017543655">"ਪੂਰਵ-ਨਿਰਧਾਰਿਤ"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"ਸਵੈਚਲਿਤ"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"ਕੋਈ ਧੁਨੀ ਜਾਂ ਥਰਥਰਾਹਟ ਨਹੀਂ"</string>
-    <!-- no translation found for notification_conversation_summary_low (3855696451919728790) -->
-    <skip />
+    <string name="notification_conversation_summary_low" msgid="3855696451919728790">"ਕੋਈ ਅਵਾਜ਼ ਜਾਂ ਥਰਥਰਾਹਟ ਨਹੀਂ, ਪਰ ਫਿਰ ਵੀ ਗੱਲਬਾਤ ਸੈਕਸ਼ਨ ਵਿੱਚ ਦਿਖਾਈ ਦਿੰਦਾ ਹੈ"</string>
     <string name="notification_channel_summary_default" msgid="777294388712200605">"ਡੀਵਾਈਸ ਸੈਟਿੰਗਾਂ ਦੇ ਆਧਾਰ \'ਤੇ ਘੰਟੀ ਵੱਜ ਸਕਦੀ ਹੈ ਜਾਂ ਥਰਥਰਾਹਟ ਹੋ ਸਕਦੀ ਹੈ"</string>
     <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"ਡੀਵਾਈਸ ਸੈਟਿੰਗਾਂ ਦੇ ਆਧਾਰ \'ਤੇ ਘੰਟੀ ਵੱਜ ਸਕਦੀ ਹੈ ਜਾਂ ਥਰਥਰਾਹਟ ਹੋ ਸਕਦੀ ਹੈ। ਪੂਰਵ-ਨਿਰਧਾਰਿਤ ਤੌਰ \'ਤੇ <xliff:g id="APP_NAME">%1$s</xliff:g> ਬਬਲ ਤੋਂ ਗੱਲਾਂਬਾਤਾਂ।"</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"ਸਿਸਟਮ ਨੂੰ ਨਿਰਧਾਰਤ ਕਰਨ ਦਿਓ ਕਿ ਇਸ ਸੂਚਨਾ ਲਈ ਕੋਈ ਧੁਨੀ ਵਜਾਉਣੀ ਚਾਹੀਦੀ ਹੈ ਜਾਂ ਥਰਥਰਾਹਟ ਕਰਨੀ ਚਾਹੀਦੀ ਹੈ"</string>
@@ -790,8 +790,7 @@
     <string name="keyboard_key_page_up" msgid="173914303254199845">"Page Up"</string>
     <string name="keyboard_key_page_down" msgid="9035902490071829731">"Page Down"</string>
     <string name="keyboard_key_forward_del" msgid="5325501825762733459">"ਮਿਟਾਓ"</string>
-    <!-- no translation found for keyboard_key_esc (6230365950511411322) -->
-    <skip />
+    <string name="keyboard_key_esc" msgid="6230365950511411322">"Esc"</string>
     <string name="keyboard_key_move_home" msgid="3496502501803911971">"Home"</string>
     <string name="keyboard_key_move_end" msgid="99190401463834854">"End"</string>
     <string name="keyboard_key_insert" msgid="4621692715704410493">"Insert"</string>
@@ -1233,7 +1232,6 @@
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"ਪ੍ਰਮਾਣੀਕਰਨ ਲੋੜੀਂਦਾ ਹੈ। ਪ੍ਰਮਾਣਿਤ ਕਰਨ ਲਈ ਫਿੰਗਰਪ੍ਰਿੰਟ ਸੈਂਸਰ ਨੂੰ ਸਪਰਸ਼ ਕਰੋ।"</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"ਜਾਰੀ ਫ਼ੋਨ ਕਾਲ"</string>
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"ਮੋਬਾਈਲ ਡਾਟਾ"</string>
-    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"ਕਨੈਕਟ ਹੈ"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"ਕੁਝ ਸਮੇਂ ਲਈ ਕਨੈਕਟ ਹੈ"</string>
     <string name="mobile_data_poor_connection" msgid="819617772268371434">"ਖਰਾਬ ਕਨੈਕਸ਼ਨ"</string>
@@ -1374,8 +1372,7 @@
     <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>
-    <!-- no translation found for shortcut_helper_category_current_app_shortcuts (4017840565974573628) -->
-    <skip />
+    <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_search_placeholder" msgid="5488547526269871819">"ਖੋਜ ਸੰਬੰਧੀ ਸ਼ਾਰਟਕੱਟ"</string>
@@ -1388,13 +1385,27 @@
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"ਹੋ ਗਿਆ"</string>
     <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"ਬਹੁਤ ਵਧੀਆ!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"ਵਾਪਸ ਜਾਓ"</string>
-    <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"ਵਾਪਸ ਜਾਣ ਲਈ, ਟੱਚਪੈਡ \'ਤੇ ਕਿਤੇ ਵੀ ਤਿੰਨ ਉਂਗਲਾਂ ਦੀ ਵਰਤੋਂ ਕਰ ਕੇ ਖੱਬੇ ਜਾਂ ਸੱਜੇ ਪਾਸੇ ਵੱਲ ਸਵਾਈਪ ਕਰੋ।"</string>
+    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
+    <skip />
+    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
+    <skip />
     <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"ਟੱਚਪੈਡ \'ਤੇ ਤਿੰਨ ਉਂਗਲਾਂ ਨੂੰ ਸੱਜੇ ਅਤੇ ਖੱਬੇ ਪਾਸੇ ਵੱਲ ਲਿਜਾਂਦੇ ਹੋਏ ਦਿਖਾਇਆ ਜਾ ਰਿਹਾ ਹੈ"</string>
     <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"ਡੀਵਾਈਸ ਦੀ ਸਕ੍ਰੀਨ \'ਤੇ ਪਿੱਛੇ ਜਾਣ ਵਾਲੇ ਇਸ਼ਾਰੇ ਲਈ ਐਨੀਮੇਸ਼ਨ ਦਿਖਾਇਆ ਜਾ ਰਿਹਾ ਹੈ"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"ਕੀ-ਬੋਰਡ ਬੈਕਲਾਈਟ"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d ਵਿੱਚੋਂ %1$d ਪੱਧਰ"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"ਹੋਮ ਕੰਟਰੋਲ"</string>
     <string name="home_controls_dream_description" msgid="4644150952104035789">"ਸਕ੍ਰੀਨ-ਸੇਵਰ ਵਜੋਂ ਆਪਣੇ ਹੋਮ ਕੰਟਰੋਲਾਂ ਤੱਕ ਤੁਰੰਤ ਪਹੁੰਚ ਕਰੋ"</string>
-    <!-- no translation found for volume_undo_action (5815519725211877114) -->
-    <skip />
+    <string name="volume_undo_action" msgid="5815519725211877114">"ਅਣਕੀਤਾ ਕਰੋ"</string>
+    <string name="back_edu_toast_content" msgid="4530314597378982956">"ਪਿੱਛੇ ਜਾਣ ਲਈ, ਟੱਚਪੈਡ \'ਤੇ ਤਿੰਨ ਉਂਗਲਾਂ ਨਾਲ ਖੱਬੇ ਜਾਂ ਸੱਜੇ ਪਾਸੇ ਵੱਲ ਸਵਾਈਪ ਕਰੋ"</string>
+    <string name="home_edu_toast_content" msgid="3381071147871955415">"ਹੋਮ \'ਤੇ ਜਾਣ ਲਈ, ਟੱਚਪੈਡ \'ਤੇ ਤਿੰਨ ਉਂਗਲਾਂ ਨਾਲ ਉੱਪਰ ਵੱਲ ਸਵਾਈਪ ਕਰੋ"</string>
+    <string name="overview_edu_toast_content" msgid="5797030644017804518">"ਹਾਲੀਆ ਐਪਾਂ ਨੂੰ ਦੇਖਣ ਲਈ, ਟੱਚਪੈਡ \'ਤੇ ਤਿੰਨ ਉਂਗਲਾਂ ਨਾਲ ਉੱਪਰ ਵੱਲ ਸਵਾਈਪ ਕਰ ਕੇ ਦਬਾਈ ਰੱਖੋ"</string>
+    <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"ਆਪਣੀਆਂ ਸਾਰੀਆਂ ਐਪਾਂ ਨੂੰ ਦੇਖਣ ਲਈ, ਆਪਣੇ ਕੀ-ਬੋਰਡ \'ਤੇ ਕਾਰਵਾਈ ਕੁੰਜੀ ਨੂੰ ਦਬਾਓ"</string>
+    <string name="back_edu_notification_title" msgid="5624780717751357278">"ਪਿੱਛੇ ਜਾਣ ਲਈ ਆਪਣੇ ਟੱਚਪੈਡ ਦੀ ਵਰਤੋਂ ਕਰੋ"</string>
+    <string name="back_edu_notification_content" msgid="2497557451540954068">"ਤਿੰਨ ਉਂਗਲਾਂ ਦੀ ਵਰਤੋਂ ਕਰ ਕੇ ਖੱਬੇ ਜਾਂ ਸੱਜੇ ਪਾਸੇ ਵੱਲ ਸਵਾਈਪ ਕਰੋ। ਹੋਰ ਇਸ਼ਾਰਿਆਂ ਨੂੰ ਜਾਣਨ ਲਈ ਟੈਪ ਕਰੋ।"</string>
+    <string name="home_edu_notification_title" msgid="6097902076909654045">"ਹੋਮ \'ਤੇ ਜਾਣ ਲਈ ਆਪਣੇ ਟੱਚਪੈਡ ਦੀ ਵਰਤੋਂ ਕਰੋ"</string>
+    <string name="home_edu_notification_content" msgid="6631697734535766588">"ਤਿੰਨ ਉਂਗਲਾਂ ਦੀ ਵਰਤੋਂ ਕਰ ਕੇ ਉੱਪਰ ਵੱਲ ਸਵਾਈਪ ਕਰੋ। ਹੋਰ ਇਸ਼ਾਰਿਆਂ ਨੂੰ ਜਾਣਨ ਲਈ ਟੈਪ ਕਰੋ।"</string>
+    <string name="overview_edu_notification_title" msgid="1265824157319562406">"ਹਾਲੀਆ ਐਪਾਂ ਦੇਖਣ ਲਈ ਆਪਣੇ ਟੱਚਪੈਡ ਦੀ ਵਰਤੋਂ ਕਰੋ"</string>
+    <string name="overview_edu_notification_content" msgid="3578204677648432500">"ਤਿੰਨ ਉਂਗਲਾਂ ਦੀ ਵਰਤੋਂ ਕਰ ਕੇ ਉੱਪਰ ਵੱਲ ਸਵਾਈਪ ਕਰ ਕੇ ਦਬਾਈ ਰੱਖੋ। ਹੋਰ ਇਸ਼ਾਰਿਆਂ ਨੂੰ ਜਾਣਨ ਲਈ ਟੈਪ ਕਰੋ।"</string>
+    <string name="all_apps_edu_notification_title" msgid="372262997265569063">"ਸਾਰੀਆਂ ਐਪਾਂ ਨੂੰ ਦੇਖਣ ਲਈ ਆਪਣਾ ਕੀ-ਬੋਰਡ ਵਰਤੋ"</string>
+    <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"ਕਿਸੇ ਵੀ ਸਮੇਂ ਕਾਰਵਾਈ ਕੁੰਜੀ ਦਬਾਓ। ਹੋਰ ਇਸ਼ਾਰਿਆਂ ਨੂੰ ਜਾਣਨ ਲਈ ਟੈਪ ਕਰੋ।"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-pa/tiles_states_strings.xml b/packages/SystemUI/res/values-pa/tiles_states_strings.xml
index cc4c5c4..62dc05a 100644
--- a/packages/SystemUI/res/values-pa/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-pa/tiles_states_strings.xml
@@ -56,9 +56,6 @@
     <item msgid="5376619709702103243">"ਬੰਦ ਹੈ"</item>
     <item msgid="4875147066469902392">"ਚਾਲੂ ਹੈ"</item>
   </string-array>
-    <!-- no translation found for tile_states_modes:0 (7764936419245199023) -->
-    <!-- no translation found for tile_states_modes:1 (2004750556637773692) -->
-    <!-- no translation found for tile_states_modes:2 (8968530753931637871) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"ਅਣਉਪਲਬਧ ਹੈ"</item>
     <item msgid="5044688398303285224">"ਬੰਦ ਹੈ"</item>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index dedfc93..d0e1e11 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -388,6 +388,9 @@
     <string name="performance" msgid="6552785217174378320">"Wydajność"</string>
     <string name="user_interface" msgid="3712869377953950887">"Interfejs"</string>
     <string name="thermal" msgid="6758074791325414831">"Termografia"</string>
+    <string name="custom" msgid="3337456985275158299">"Niestandardowe"</string>
+    <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Niestandardowe ustawienia śladu"</string>
+    <string name="restore_default" msgid="5259420807486239755">"Przywróć wartości domyślne"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Tryb jednej ręki"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Urządzenia słuchowe"</string>
     <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktywne"</string>
@@ -426,6 +429,17 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Otwórz Ustawienia"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Inne urządzenie"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Przełącz Przegląd"</string>
+    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Tryby priorytetowe"</string>
+    <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Gotowe"</string>
+    <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Ustawienia"</string>
+    <string name="zen_mode_on" msgid="9085304934016242591">"Wł."</string>
+    <string name="zen_mode_off" msgid="1736604456618147306">"Wył."</string>
+    <!-- no translation found for zen_mode_set_up (7457957033034460064) -->
+    <skip />
+    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
+    <skip />
+    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
+    <skip />
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Nie będą Cię niepokoić żadne dźwięki ani wibracje z wyjątkiem alarmów, przypomnień, wydarzeń i połączeń od wybranych osób. Będziesz słyszeć wszystkie odtwarzane treści, takie jak muzyka, filmy czy gry."</string>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Nie będą Cię niepokoić żadne dźwięki ani wibracje z wyjątkiem alarmów. Będziesz słyszeć wszystkie odtwarzane treści, takie jak muzyka, filmy czy gry."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Dostosuj"</string>
@@ -464,6 +478,8 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Wolne ładowanie • Pełne naładowanie za <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Ładowanie • Pełne naładowanie za <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Widżety na ekranie blokady"</string>
+    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
+    <skip />
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Aby uruchomić wspólny samouczek, przeciągnij palcem w lewo"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Dostosuj"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Zamknij"</string>
@@ -492,6 +508,7 @@
     <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"umieść wybrany widżet"</string>
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"Widżety na ekranie blokady"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"Każdy zobaczy widżety na ekranie blokady, nawet gdy tablet jest zablokowany."</string>
+    <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"odznacz widżet"</string>
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widżety na ekranie blokady"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Aby otworzyć aplikację za pomocą widżetu, musisz potwierdzić swoją tożsamość. Pamiętaj też, że każdy będzie mógł wyświetlić widżety nawet wtedy, gdy tablet będzie zablokowany. Niektóre widżety mogą nie być przeznaczone do umieszczenia na ekranie blokady i ich dodanie w tym miejscu może być niebezpieczne."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"OK"</string>
@@ -548,8 +565,8 @@
     <string name="media_projection_action_text" msgid="3634906766918186440">"Rozpocznij teraz"</string>
     <string name="empty_shade_text" msgid="8935967157319717412">"Brak powiadomień"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"Brak nowych powiadomień"</string>
-    <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"Powiadomienia adaptacyjne włączone"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"Urządzenie zmniejszy teraz głośność i ograniczy wyskakujące okienka przez maks. 2 minuty, gdy w krótkim czasie otrzymujesz wiele powiadomień."</string>
+    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Wyciszanie powiadomień jest włączone"</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Gdy otrzymasz za dużo powiadomień, dźwięk i alerty zostaną automatycznie wyciszone na maks. 2 min."</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Wyłącz"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Odblokuj i zobacz starsze powiadomienia"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Tym urządzeniem zarządza Twój rodzic"</string>
@@ -716,8 +733,7 @@
     <string name="notification_alert_title" msgid="3656229781017543655">"Domyślne"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Automatycznie"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"Bez dźwięku i wibracji"</string>
-    <!-- no translation found for notification_conversation_summary_low (3855696451919728790) -->
-    <skip />
+    <string name="notification_conversation_summary_low" msgid="3855696451919728790">"Brak sygnału dźwiękowego lub wibracji, ale nadal pojawia się w sekcji rozmów"</string>
     <string name="notification_channel_summary_default" msgid="777294388712200605">"Mogą włączać dzwonek lub wibracje w zależności od ustawień urządzenia"</string>
     <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Mogą włączać dzwonek lub wibracje w zależności od ustawień urządzenia. Rozmowy z aplikacji <xliff:g id="APP_NAME">%1$s</xliff:g> są domyślnie wyświetlane jako dymki."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Pozwól systemowi decydować, czy o powiadomieniu powinien informować dźwięk czy wibracja"</string>
@@ -1216,7 +1232,6 @@
     <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>
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobilna transmisja danych"</string>
-    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</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>
     <string name="mobile_data_poor_connection" msgid="819617772268371434">"Słabe połączenie"</string>
@@ -1357,8 +1372,7 @@
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Podzielony ekran"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Wprowadzanie"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Skróty do aplikacji"</string>
-    <!-- no translation found for shortcut_helper_category_current_app_shortcuts (4017840565974573628) -->
-    <skip />
+    <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Bieżąca aplikacja"</string>
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Ułatwienia dostępu"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Skróty klawiszowe"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Skróty do wyszukiwania"</string>
@@ -1371,7 +1385,10 @@
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Gotowe"</string>
     <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Świetnie!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Wróć"</string>
-    <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"Aby wrócić, przesuń 3 palcami w lewo lub w prawo w dowolnym miejscu touchpada."</string>
+    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
+    <skip />
+    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
+    <skip />
     <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"3 palce na touchpadzie poruszające się w prawo i w lewo"</string>
     <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Ekran urządzenia z animacją gestu cofania"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Podświetlenie klawiatury"</string>
@@ -1379,4 +1396,16 @@
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Sterowanie domem"</string>
     <string name="home_controls_dream_description" msgid="4644150952104035789">"Szybki dostęp do sterowania domem na wygaszaczu ekranu"</string>
     <string name="volume_undo_action" msgid="5815519725211877114">"Cofnij"</string>
+    <string name="back_edu_toast_content" msgid="4530314597378982956">"Aby przejść wstecz, przesuń w prawo lub lewo za pomocą 3 palców na touchpadzie."</string>
+    <string name="home_edu_toast_content" msgid="3381071147871955415">"Aby przejść do ekranu głównego, przesuń w górę za pomocą 3 palców na touchpadzie"</string>
+    <string name="overview_edu_toast_content" msgid="5797030644017804518">"Aby wyświetlić ostatnie aplikacje, przesuń w górę za pomocą 3 palców na touchpadzie i przytrzymaj."</string>
+    <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Aby wyświetlić wszystkie swoje aplikacje, naciśnij klawisz działania na klawiaturze"</string>
+    <string name="back_edu_notification_title" msgid="5624780717751357278">"Przechodzenie wstecz za pomocą touchpada"</string>
+    <string name="back_edu_notification_content" msgid="2497557451540954068">"Przesuń w prawo lub lewo za pomocą 3 palców. Kliknij, aby poznać więcej gestów."</string>
+    <string name="home_edu_notification_title" msgid="6097902076909654045">"Przechodzenie do ekranu głównego za pomocą touchpada"</string>
+    <string name="home_edu_notification_content" msgid="6631697734535766588">"Przesuń w górę za pomocą 3 palców. Kliknij, aby poznać więcej gestów."</string>
+    <string name="overview_edu_notification_title" msgid="1265824157319562406">"Wyświetlanie ostatnio używanych aplikacji za pomocą touchpada"</string>
+    <string name="overview_edu_notification_content" msgid="3578204677648432500">"Przesuń w górę za pomocą 3 palców i przytrzymaj. Kliknij, aby poznać więcej gestów."</string>
+    <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Wyświetlanie wszystkich aplikacji za pomocą klawiatury"</string>
+    <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Naciśnij klawisz działania w dowolnym momencie. Kliknij, aby poznać więcej gestów."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-pl/tiles_states_strings.xml b/packages/SystemUI/res/values-pl/tiles_states_strings.xml
index 2190cf8..5aa719f 100644
--- a/packages/SystemUI/res/values-pl/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-pl/tiles_states_strings.xml
@@ -56,11 +56,6 @@
     <item msgid="5376619709702103243">"Wyłączone"</item>
     <item msgid="4875147066469902392">"Włączone"</item>
   </string-array>
-  <string-array name="tile_states_modes">
-    <item msgid="7764936419245199023">"Niedostępne"</item>
-    <item msgid="2004750556637773692">"Wyłączono"</item>
-    <item msgid="8968530753931637871">"Włączono"</item>
-  </string-array>
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Niedostępny"</item>
     <item msgid="5044688398303285224">"Wyłączona"</item>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index 1ab30c3..e74f578 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -218,7 +218,7 @@
     <string name="biometric_re_enroll_notification_content" msgid="8685925877186288180">"Ação necessária para melhorar a segurança e o desempenho"</string>
     <string name="fingerprint_re_enroll_notification_title" msgid="4539432429683916604">"Configurar o Desbloqueio por impressão digital de novo"</string>
     <string name="fingerprint_re_enroll_notification_name" msgid="630798657797645704">"Desbloqueio por impressão digital"</string>
-    <string name="fingerprint_re_enroll_dialog_title" msgid="3526033128113925780">"Configurar o Desbloqueio por impressão digital"</string>
+    <string name="fingerprint_re_enroll_dialog_title" msgid="3526033128113925780">"Configurar o Desbloqueio por impressão digital"</string>
     <string name="fingerprint_re_enroll_dialog_content" msgid="4866561176695984879">"Os modelos e as imagens atuais serão excluídos para reconfigurar o Desbloqueio por impressão digital.\n\nDepois disso, você vai precisar configurar esse recurso de novo para desbloquear o smartphone com o dedo ou confirmar sua identidade."</string>
     <string name="fingerprint_re_enroll_dialog_content_singular" msgid="3083663339787381218">"O modelo e as imagens atuais serão excluídos para reconfigurar o Desbloqueio por impressão digital.\n\nDepois disso, você vai precisar configurar esse recurso de novo para desbloquear o smartphone com o dedo ou confirmar sua identidade."</string>
     <string name="fingerprint_reenroll_failure_dialog_content" msgid="4733768492747300666">"Não foi possível configurar o Desbloqueio por impressão digital. Acesse as Configurações e tente de novo."</string>
@@ -290,8 +290,7 @@
     <string name="start_dreams" msgid="9131802557946276718">"Protetor de tela"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"Não perturbe"</string>
-    <!-- no translation found for quick_settings_modes_label (5407025818652750501) -->
-    <skip />
+    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Modos prioritários"</string>
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Não há dispositivos pareados disponíveis"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Toque para conectar ou desconectar um dispositivo"</string>
@@ -389,6 +388,9 @@
     <string name="performance" msgid="6552785217174378320">"Desempenho"</string>
     <string name="user_interface" msgid="3712869377953950887">"Interface do usuário"</string>
     <string name="thermal" msgid="6758074791325414831">"Térmico"</string>
+    <string name="custom" msgid="3337456985275158299">"Personalizado"</string>
+    <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Configurações de rastreamento personalizado"</string>
+    <string name="restore_default" msgid="5259420807486239755">"Restaurar padrão"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Modo uma mão"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Aparelhos auditivos"</string>
     <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Ativos"</string>
@@ -427,6 +429,17 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Abrir as Configurações"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Outro dispositivo"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Alternar Visão geral"</string>
+    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Modos prioritários"</string>
+    <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Concluído"</string>
+    <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Configurações"</string>
+    <string name="zen_mode_on" msgid="9085304934016242591">"Ativado"</string>
+    <string name="zen_mode_off" msgid="1736604456618147306">"Desativado"</string>
+    <!-- no translation found for zen_mode_set_up (7457957033034460064) -->
+    <skip />
+    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
+    <skip />
+    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
+    <skip />
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Você não será perturbado por sons e vibrações, exceto alarmes, lembretes, eventos e chamadas de pessoas especificadas. No entanto, você ouvirá tudo o que decidir reproduzir, como músicas, vídeos e jogos."</string>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Você não será perturbado por sons e vibrações, exceto alarmes. No entanto, você ouvirá tudo o que decidir reproduzir, como músicas, vídeos e jogos."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Personalizar"</string>
@@ -465,6 +478,8 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Carga lenta • Conclusão em <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Carregando • Conclusão em <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Widgets na tela de bloqueio"</string>
+    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
+    <skip />
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Deslize para a esquerda para iniciar o tutorial compartilhado"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Personalizar"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Dispensar"</string>
@@ -493,6 +508,7 @@
     <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"posicionar widget selecionado"</string>
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"Widgets da tela de bloqueio"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"Todos podem ver os widgets na tela de bloqueio, mesmo com o tablet bloqueado."</string>
+    <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"desmarcar widget"</string>
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widgets da tela de bloqueio"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Para abrir um app usando um widget, você precisará confirmar sua identidade. Além disso, não se esqueça que qualquer pessoa pode ver os widgets, mesmo quando o tablet está bloqueado. Alguns widgets podem não ter sido criados para ficar na tela de bloqueio e fazer isso talvez não seja seguro."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Entendi"</string>
@@ -549,8 +565,8 @@
     <string name="media_projection_action_text" msgid="3634906766918186440">"Iniciar agora"</string>
     <string name="empty_shade_text" msgid="8935967157319717412">"Sem notificações"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"Nenhuma notificação nova"</string>
-    <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"Notificações adaptáveis ativadas"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"O dispositivo agora diminui o volume e reduz os pop-ups na tela por até 2 minutos quando você recebe muitas notificações em pouco tempo."</string>
+    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"O recurso de atenuar notificações está ativo"</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Volume e alertas do disp. são reduzidos por até 2 min quando você recebe muitas notificações juntas."</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Desativar"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Desbloqueie p/ acessar notificações antigas"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Este dispositivo é gerenciado pelo seu familiar responsável"</string>
@@ -717,8 +733,7 @@
     <string name="notification_alert_title" msgid="3656229781017543655">"Padrão"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Automática"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"Som e vibração desativados"</string>
-    <!-- no translation found for notification_conversation_summary_low (3855696451919728790) -->
-    <skip />
+    <string name="notification_conversation_summary_low" msgid="3855696451919728790">"Nenhum som ou vibração, mas ainda aparece na seção de conversa"</string>
     <string name="notification_channel_summary_default" msgid="777294388712200605">"Pode vibrar ou tocar com base nas configurações do dispositivo"</string>
     <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Pode vibrar ou tocar com base nas configurações do dispositivo. As conversas do app <xliff:g id="APP_NAME">%1$s</xliff:g> aparecem em balões por padrão."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Faça com que o sistema determine se a notificação resultará em som ou vibração"</string>
@@ -1217,7 +1232,6 @@
     <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>
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Dados móveis"</string>
-    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Conectado"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Temporariamente conectado"</string>
     <string name="mobile_data_poor_connection" msgid="819617772268371434">"Conexão fraca"</string>
@@ -1371,13 +1385,27 @@
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Concluído"</string>
     <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Muito bem!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Voltar"</string>
-    <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"Para voltar, deslize para a esquerda ou direita usando 3 dedos em qualquer lugar do touchpad."</string>
+    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
+    <skip />
+    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
+    <skip />
     <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Touchpad mostrando 3 dedos deslizando para a direita e esquerda"</string>
     <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Tela do dispositivo mostrando a animação do gesto de volta"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Luz de fundo do teclado"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Nível %1$d de %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Automação residencial"</string>
     <string name="home_controls_dream_description" msgid="4644150952104035789">"Controles de automação residencial no protetor de tela"</string>
-    <!-- no translation found for volume_undo_action (5815519725211877114) -->
-    <skip />
+    <string name="volume_undo_action" msgid="5815519725211877114">"Desfazer"</string>
+    <string name="back_edu_toast_content" msgid="4530314597378982956">"Se quiser voltar, deslize para a esquerda ou direita com três dedos no touchpad"</string>
+    <string name="home_edu_toast_content" msgid="3381071147871955415">"Se quiser acessar a tela inicial, deslize para cima com três dedos no touchpad"</string>
+    <string name="overview_edu_toast_content" msgid="5797030644017804518">"Se quiser ver os apps recentes, deslize para cima e pressione com três dedos no touchpad"</string>
+    <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Para ver todos os apps, pressione a tecla de ação no teclado"</string>
+    <string name="back_edu_notification_title" msgid="5624780717751357278">"Use o touchpad para voltar"</string>
+    <string name="back_edu_notification_content" msgid="2497557451540954068">"Deslize para a esquerda ou direita usando três dedos. Toque para aprender outros gestos."</string>
+    <string name="home_edu_notification_title" msgid="6097902076909654045">"Use o touchpad para acessar a tela inicial"</string>
+    <string name="home_edu_notification_content" msgid="6631697734535766588">"Deslize para cima usando três dedos. Toque para aprender outros gestos."</string>
+    <string name="overview_edu_notification_title" msgid="1265824157319562406">"Use o touchpad para ver os apps recentes"</string>
+    <string name="overview_edu_notification_content" msgid="3578204677648432500">"Deslize para cima e pressione com três dedos. Toque para aprender outros gestos."</string>
+    <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Use o teclado para ver todos os apps"</string>
+    <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Pressione a tecla de ação a qualquer momento. Toque para aprender outros gestos."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-pt-rBR/tiles_states_strings.xml b/packages/SystemUI/res/values-pt-rBR/tiles_states_strings.xml
index b881142..3526c77 100644
--- a/packages/SystemUI/res/values-pt-rBR/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/tiles_states_strings.xml
@@ -56,9 +56,6 @@
     <item msgid="5376619709702103243">"Desativado"</item>
     <item msgid="4875147066469902392">"Ativado"</item>
   </string-array>
-    <!-- no translation found for tile_states_modes:0 (7764936419245199023) -->
-    <!-- no translation found for tile_states_modes:1 (2004750556637773692) -->
-    <!-- no translation found for tile_states_modes:2 (8968530753931637871) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Indisponível"</item>
     <item msgid="5044688398303285224">"Desativada"</item>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index 963b47c..a21f475 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -388,6 +388,9 @@
     <string name="performance" msgid="6552785217174378320">"Desempenho"</string>
     <string name="user_interface" msgid="3712869377953950887">"Interface do utilizador"</string>
     <string name="thermal" msgid="6758074791325414831">"Térmico"</string>
+    <string name="custom" msgid="3337456985275158299">"Personalizado"</string>
+    <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Definições de rastreio personalizadas"</string>
+    <string name="restore_default" msgid="5259420807486239755">"Restaurar predefinição"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Modo para uma mão"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Disp. auditivos"</string>
     <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Ativos"</string>
@@ -426,6 +429,17 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Abrir definições"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Outro dispositivo"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Ativar/desativar Vista geral"</string>
+    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Modos de prioridade"</string>
+    <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Concluir"</string>
+    <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Definições"</string>
+    <string name="zen_mode_on" msgid="9085304934016242591">"Ativado"</string>
+    <string name="zen_mode_off" msgid="1736604456618147306">"Desativado"</string>
+    <!-- no translation found for zen_mode_set_up (7457957033034460064) -->
+    <skip />
+    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
+    <skip />
+    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
+    <skip />
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Não é incomodado por sons e vibrações, exceto de alarmes, lembretes, eventos e autores de chamadas que especificar. Continua a ouvir tudo o que optar por reproduzir, incluindo música, vídeos e jogos."</string>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Não é incomodado por sons e vibrações, exceto de alarmes. Continua a ouvir tudo o que optar por reproduzir, incluindo música, vídeos e jogos."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Personalizar"</string>
@@ -464,6 +478,8 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • A carregar lentamente • Carga completa em <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • A carregar • Carga completa em <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Widgets no ecrã de bloqueio"</string>
+    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
+    <skip />
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Deslize rapidamente para a esquerda para iniciar o tutorial coletivo"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Personalizar"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Ignorar"</string>
@@ -492,6 +508,7 @@
     <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"posicionar widget selecionado"</string>
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"Widgets do ecrã de bloqueio"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"Todos podem pode ver widgets no ecrã de bloqueio, mesmo com o tablet bloqueado."</string>
+    <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"desmarcar widget"</string>
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widgets do ecrã de bloqueio"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Para abrir uma app através de um widget, vai ter de validar a sua identidade. Além disso, tenha em atenção que qualquer pessoa pode ver os widgets, mesmo quando o tablet estiver bloqueado. Alguns widgets podem não se destinar ao ecrã de bloqueio e pode ser inseguro adicioná-los aqui."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"OK"</string>
@@ -548,8 +565,8 @@
     <string name="media_projection_action_text" msgid="3634906766918186440">"Começar agora"</string>
     <string name="empty_shade_text" msgid="8935967157319717412">"Sem notificações"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"Não existem novas notificações"</string>
-    <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"Notificações adaptáveis ativas"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"O disp. diminui o vol. e reduz os pop-ups por até 2 min quando recebe muitas notificações seguidas."</string>
+    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"O repouso das notificações está ativado"</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"O volume e os alertas são reduzidos automaticamente durante até 2 minutos quando recebe muitas notificações de uma vez."</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Desativar"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Desbloqueie e veja notificações antigas"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Este dispositivo é gerido pelos teus pais"</string>
@@ -716,8 +733,7 @@
     <string name="notification_alert_title" msgid="3656229781017543655">"Predefinição"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Automática"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"Sem som ou vibração"</string>
-    <!-- no translation found for notification_conversation_summary_low (3855696451919728790) -->
-    <skip />
+    <string name="notification_conversation_summary_low" msgid="3855696451919728790">"Sem som nem vibração, mas ainda aparece na secção de conversas"</string>
     <string name="notification_channel_summary_default" msgid="777294388712200605">"Pode tocar ou vibrar com base nas definições do dispositivo"</string>
     <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Pode tocar ou vibrar com base nas definições do dispositivo. As conversas da app <xliff:g id="APP_NAME">%1$s</xliff:g> aparecem como um balão por predefinição."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Faça com que o sistema determine se esta notificação deve emitir um som ou uma vibração"</string>
@@ -1216,7 +1232,6 @@
     <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>
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Dados móveis"</string>
-    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Ligado"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Ligado temporariamente"</string>
     <string name="mobile_data_poor_connection" msgid="819617772268371434">"Ligação fraca"</string>
@@ -1370,7 +1385,10 @@
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Concluir"</string>
     <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Muito bem!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Voltar"</string>
-    <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"Para retroceder, deslize rapidamente para a esquerda ou direita com três dedos em qualquer parte do touchpad."</string>
+    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
+    <skip />
+    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
+    <skip />
     <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Touchpad a mostrar três dedos a moverem-se para a direita e esquerda"</string>
     <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Ecrã de dispositivo a mostrar uma animação do gesto para retroceder"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Luz do teclado"</string>
@@ -1378,4 +1396,16 @@
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Controlos domésticos"</string>
     <string name="home_controls_dream_description" msgid="4644150952104035789">"Use controlos domésticos como proteção de ecrã"</string>
     <string name="volume_undo_action" msgid="5815519725211877114">"Anular"</string>
+    <string name="back_edu_toast_content" msgid="4530314597378982956">"Para retroceder, deslize rapidamente para a esquerda ou direita com 3 dedos no touchpad"</string>
+    <string name="home_edu_toast_content" msgid="3381071147871955415">"Para aceder ao ecrã principal, deslize rapidamente para cima com 3 dedos no touchpad"</string>
+    <string name="overview_edu_toast_content" msgid="5797030644017804518">"Para ver as apps recentes, deslize rapidamente para cima e mantenha premido com 3 dedos no touchpad"</string>
+    <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Para ver todas as suas apps, prima a tecla de ação no teclado"</string>
+    <string name="back_edu_notification_title" msgid="5624780717751357278">"Use o touchpad para retroceder"</string>
+    <string name="back_edu_notification_content" msgid="2497557451540954068">"Deslize rapidamente para a esquerda ou direita com 3 dedos. Toque para aprender mais gestos."</string>
+    <string name="home_edu_notification_title" msgid="6097902076909654045">"Use o touchpad para aceder ao ecrã principal"</string>
+    <string name="home_edu_notification_content" msgid="6631697734535766588">"Deslize rapidamente para cima com 3 dedos. Toque para aprender mais gestos."</string>
+    <string name="overview_edu_notification_title" msgid="1265824157319562406">"Use o touchpad para ver as apps recentes"</string>
+    <string name="overview_edu_notification_content" msgid="3578204677648432500">"Deslize rapidamente para cima e mantenha premido com 3 dedos. Toque para aprender mais gestos."</string>
+    <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Use o teclado para ver todas as apps"</string>
+    <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Prima a tecla de ação em qualquer altura. Toque para aprender mais gestos."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-pt-rPT/tiles_states_strings.xml b/packages/SystemUI/res/values-pt-rPT/tiles_states_strings.xml
index d7a245a..34a5ed7 100644
--- a/packages/SystemUI/res/values-pt-rPT/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/tiles_states_strings.xml
@@ -56,11 +56,6 @@
     <item msgid="5376619709702103243">"Desligado"</item>
     <item msgid="4875147066469902392">"Ligado"</item>
   </string-array>
-  <string-array name="tile_states_modes">
-    <item msgid="7764936419245199023">"Indisponível"</item>
-    <item msgid="2004750556637773692">"Desativado"</item>
-    <item msgid="8968530753931637871">"Ativado"</item>
-  </string-array>
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Indisponível"</item>
     <item msgid="5044688398303285224">"Desligada"</item>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index 1ab30c3..e74f578 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -218,7 +218,7 @@
     <string name="biometric_re_enroll_notification_content" msgid="8685925877186288180">"Ação necessária para melhorar a segurança e o desempenho"</string>
     <string name="fingerprint_re_enroll_notification_title" msgid="4539432429683916604">"Configurar o Desbloqueio por impressão digital de novo"</string>
     <string name="fingerprint_re_enroll_notification_name" msgid="630798657797645704">"Desbloqueio por impressão digital"</string>
-    <string name="fingerprint_re_enroll_dialog_title" msgid="3526033128113925780">"Configurar o Desbloqueio por impressão digital"</string>
+    <string name="fingerprint_re_enroll_dialog_title" msgid="3526033128113925780">"Configurar o Desbloqueio por impressão digital"</string>
     <string name="fingerprint_re_enroll_dialog_content" msgid="4866561176695984879">"Os modelos e as imagens atuais serão excluídos para reconfigurar o Desbloqueio por impressão digital.\n\nDepois disso, você vai precisar configurar esse recurso de novo para desbloquear o smartphone com o dedo ou confirmar sua identidade."</string>
     <string name="fingerprint_re_enroll_dialog_content_singular" msgid="3083663339787381218">"O modelo e as imagens atuais serão excluídos para reconfigurar o Desbloqueio por impressão digital.\n\nDepois disso, você vai precisar configurar esse recurso de novo para desbloquear o smartphone com o dedo ou confirmar sua identidade."</string>
     <string name="fingerprint_reenroll_failure_dialog_content" msgid="4733768492747300666">"Não foi possível configurar o Desbloqueio por impressão digital. Acesse as Configurações e tente de novo."</string>
@@ -290,8 +290,7 @@
     <string name="start_dreams" msgid="9131802557946276718">"Protetor de tela"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"Não perturbe"</string>
-    <!-- no translation found for quick_settings_modes_label (5407025818652750501) -->
-    <skip />
+    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Modos prioritários"</string>
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Não há dispositivos pareados disponíveis"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Toque para conectar ou desconectar um dispositivo"</string>
@@ -389,6 +388,9 @@
     <string name="performance" msgid="6552785217174378320">"Desempenho"</string>
     <string name="user_interface" msgid="3712869377953950887">"Interface do usuário"</string>
     <string name="thermal" msgid="6758074791325414831">"Térmico"</string>
+    <string name="custom" msgid="3337456985275158299">"Personalizado"</string>
+    <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Configurações de rastreamento personalizado"</string>
+    <string name="restore_default" msgid="5259420807486239755">"Restaurar padrão"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Modo uma mão"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Aparelhos auditivos"</string>
     <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Ativos"</string>
@@ -427,6 +429,17 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Abrir as Configurações"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Outro dispositivo"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Alternar Visão geral"</string>
+    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Modos prioritários"</string>
+    <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Concluído"</string>
+    <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Configurações"</string>
+    <string name="zen_mode_on" msgid="9085304934016242591">"Ativado"</string>
+    <string name="zen_mode_off" msgid="1736604456618147306">"Desativado"</string>
+    <!-- no translation found for zen_mode_set_up (7457957033034460064) -->
+    <skip />
+    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
+    <skip />
+    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
+    <skip />
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Você não será perturbado por sons e vibrações, exceto alarmes, lembretes, eventos e chamadas de pessoas especificadas. No entanto, você ouvirá tudo o que decidir reproduzir, como músicas, vídeos e jogos."</string>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Você não será perturbado por sons e vibrações, exceto alarmes. No entanto, você ouvirá tudo o que decidir reproduzir, como músicas, vídeos e jogos."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Personalizar"</string>
@@ -465,6 +478,8 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Carga lenta • Conclusão em <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Carregando • Conclusão em <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Widgets na tela de bloqueio"</string>
+    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
+    <skip />
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Deslize para a esquerda para iniciar o tutorial compartilhado"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Personalizar"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Dispensar"</string>
@@ -493,6 +508,7 @@
     <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"posicionar widget selecionado"</string>
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"Widgets da tela de bloqueio"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"Todos podem ver os widgets na tela de bloqueio, mesmo com o tablet bloqueado."</string>
+    <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"desmarcar widget"</string>
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widgets da tela de bloqueio"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Para abrir um app usando um widget, você precisará confirmar sua identidade. Além disso, não se esqueça que qualquer pessoa pode ver os widgets, mesmo quando o tablet está bloqueado. Alguns widgets podem não ter sido criados para ficar na tela de bloqueio e fazer isso talvez não seja seguro."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Entendi"</string>
@@ -549,8 +565,8 @@
     <string name="media_projection_action_text" msgid="3634906766918186440">"Iniciar agora"</string>
     <string name="empty_shade_text" msgid="8935967157319717412">"Sem notificações"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"Nenhuma notificação nova"</string>
-    <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"Notificações adaptáveis ativadas"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"O dispositivo agora diminui o volume e reduz os pop-ups na tela por até 2 minutos quando você recebe muitas notificações em pouco tempo."</string>
+    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"O recurso de atenuar notificações está ativo"</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Volume e alertas do disp. são reduzidos por até 2 min quando você recebe muitas notificações juntas."</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Desativar"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Desbloqueie p/ acessar notificações antigas"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Este dispositivo é gerenciado pelo seu familiar responsável"</string>
@@ -717,8 +733,7 @@
     <string name="notification_alert_title" msgid="3656229781017543655">"Padrão"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Automática"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"Som e vibração desativados"</string>
-    <!-- no translation found for notification_conversation_summary_low (3855696451919728790) -->
-    <skip />
+    <string name="notification_conversation_summary_low" msgid="3855696451919728790">"Nenhum som ou vibração, mas ainda aparece na seção de conversa"</string>
     <string name="notification_channel_summary_default" msgid="777294388712200605">"Pode vibrar ou tocar com base nas configurações do dispositivo"</string>
     <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Pode vibrar ou tocar com base nas configurações do dispositivo. As conversas do app <xliff:g id="APP_NAME">%1$s</xliff:g> aparecem em balões por padrão."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Faça com que o sistema determine se a notificação resultará em som ou vibração"</string>
@@ -1217,7 +1232,6 @@
     <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>
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Dados móveis"</string>
-    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Conectado"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Temporariamente conectado"</string>
     <string name="mobile_data_poor_connection" msgid="819617772268371434">"Conexão fraca"</string>
@@ -1371,13 +1385,27 @@
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Concluído"</string>
     <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Muito bem!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Voltar"</string>
-    <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"Para voltar, deslize para a esquerda ou direita usando 3 dedos em qualquer lugar do touchpad."</string>
+    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
+    <skip />
+    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
+    <skip />
     <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Touchpad mostrando 3 dedos deslizando para a direita e esquerda"</string>
     <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Tela do dispositivo mostrando a animação do gesto de volta"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Luz de fundo do teclado"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Nível %1$d de %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Automação residencial"</string>
     <string name="home_controls_dream_description" msgid="4644150952104035789">"Controles de automação residencial no protetor de tela"</string>
-    <!-- no translation found for volume_undo_action (5815519725211877114) -->
-    <skip />
+    <string name="volume_undo_action" msgid="5815519725211877114">"Desfazer"</string>
+    <string name="back_edu_toast_content" msgid="4530314597378982956">"Se quiser voltar, deslize para a esquerda ou direita com três dedos no touchpad"</string>
+    <string name="home_edu_toast_content" msgid="3381071147871955415">"Se quiser acessar a tela inicial, deslize para cima com três dedos no touchpad"</string>
+    <string name="overview_edu_toast_content" msgid="5797030644017804518">"Se quiser ver os apps recentes, deslize para cima e pressione com três dedos no touchpad"</string>
+    <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Para ver todos os apps, pressione a tecla de ação no teclado"</string>
+    <string name="back_edu_notification_title" msgid="5624780717751357278">"Use o touchpad para voltar"</string>
+    <string name="back_edu_notification_content" msgid="2497557451540954068">"Deslize para a esquerda ou direita usando três dedos. Toque para aprender outros gestos."</string>
+    <string name="home_edu_notification_title" msgid="6097902076909654045">"Use o touchpad para acessar a tela inicial"</string>
+    <string name="home_edu_notification_content" msgid="6631697734535766588">"Deslize para cima usando três dedos. Toque para aprender outros gestos."</string>
+    <string name="overview_edu_notification_title" msgid="1265824157319562406">"Use o touchpad para ver os apps recentes"</string>
+    <string name="overview_edu_notification_content" msgid="3578204677648432500">"Deslize para cima e pressione com três dedos. Toque para aprender outros gestos."</string>
+    <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Use o teclado para ver todos os apps"</string>
+    <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Pressione a tecla de ação a qualquer momento. Toque para aprender outros gestos."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-pt/tiles_states_strings.xml b/packages/SystemUI/res/values-pt/tiles_states_strings.xml
index b881142..3526c77 100644
--- a/packages/SystemUI/res/values-pt/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-pt/tiles_states_strings.xml
@@ -56,9 +56,6 @@
     <item msgid="5376619709702103243">"Desativado"</item>
     <item msgid="4875147066469902392">"Ativado"</item>
   </string-array>
-    <!-- no translation found for tile_states_modes:0 (7764936419245199023) -->
-    <!-- no translation found for tile_states_modes:1 (2004750556637773692) -->
-    <!-- no translation found for tile_states_modes:2 (8968530753931637871) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Indisponível"</item>
     <item msgid="5044688398303285224">"Desativada"</item>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index 285a3ca..d0895ad 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -126,38 +126,25 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"Atinge pentru a afișa"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"Eroare la salvarea înregistrării ecranului"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Eroare la începerea înregistrării ecranului"</string>
-    <!-- no translation found for screenrecord_stop_dialog_title (8716193661764511095) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message (6262768207331626817) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5995770227684523244) -->
-    <skip />
+    <string name="screenrecord_stop_dialog_title" msgid="8716193661764511095">"Oprești înregistrarea?"</string>
+    <string name="screenrecord_stop_dialog_message" msgid="6262768207331626817">"Înregistrezi întregul ecran"</string>
+    <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Înregistrezi <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Oprește înregistrarea"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Se permite accesul la ecran"</string>
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Oprești accesul la ecran?"</string>
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen_with_host_app (522823522115375414) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen (5090115386271179270) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_specific (5923772039347985172) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_generic (6681016774654578261) -->
-    <skip />
+    <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Permiți accesul <xliff:g id="HOST_APP_NAME">%1$s</xliff:g> la întregul ecran"</string>
+    <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Permiți accesul unei aplicații la întregul ecran"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Permiți accesul la <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Permiți accesul la o aplicație"</string>
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Nu mai permite accesul"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Se proiectează ecranul"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Oprești proiectarea?"</string>
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen_with_device (1474703115926205251) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen (8419219169553867625) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app_with_device (2715934698604085519) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (8616103075630934513) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic_with_device (9213582497852420203) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic (4100272100480415076) -->
-    <skip />
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen_with_device" msgid="1474703115926205251">"Proiectezi întregul ecran pe <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen" msgid="8419219169553867625">"Proiectezi întregul ecran pe un dispozitiv din apropiere"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app_with_device" msgid="2715934698604085519">"Proiectezi <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> pe <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="8616103075630934513">"Proiectezi <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> pe un dispozitiv din apropiere"</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic_with_device" msgid="9213582497852420203">"Proiectezi pe <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic" msgid="4100272100480415076">"Proiectezi pe un dispozitiv din apropiere"</string>
     <string name="cast_to_other_device_stop_dialog_button" msgid="6420183747435521834">"Oprește proiectarea"</string>
     <string name="close_dialog_button" msgid="4749497706540104133">"Închide"</string>
     <string name="issuerecord_title" msgid="286627115110121849">"Instrument de înregistrare a problemelor"</string>
@@ -303,8 +290,7 @@
     <string name="start_dreams" msgid="9131802557946276718">"Screensaver"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"Nu deranja"</string>
-    <!-- no translation found for quick_settings_modes_label (5407025818652750501) -->
-    <skip />
+    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Moduri cu prioritate"</string>
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Niciun dispozitiv conectat disponibil"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Atinge pentru a conecta sau deconecta un dispozitiv"</string>
@@ -402,6 +388,9 @@
     <string name="performance" msgid="6552785217174378320">"Performanță"</string>
     <string name="user_interface" msgid="3712869377953950887">"Interfața de utilizare"</string>
     <string name="thermal" msgid="6758074791325414831">"Termal"</string>
+    <string name="custom" msgid="3337456985275158299">"Personalizat"</string>
+    <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Setări de urmărire personalizate"</string>
+    <string name="restore_default" msgid="5259420807486239755">"Revino la valoarea prestabilită"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Modul cu o mână"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Aparate auditive"</string>
     <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Activ"</string>
@@ -440,6 +429,17 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Deschide Setări"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Alt dispozitiv"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Comută secțiunea Recente"</string>
+    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Moduri cu prioritate"</string>
+    <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Gata"</string>
+    <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Setări"</string>
+    <string name="zen_mode_on" msgid="9085304934016242591">"Activat"</string>
+    <string name="zen_mode_off" msgid="1736604456618147306">"Dezactivat"</string>
+    <!-- no translation found for zen_mode_set_up (7457957033034460064) -->
+    <skip />
+    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
+    <skip />
+    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
+    <skip />
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Se vor anunța prin sunete și vibrații numai alarmele, mementourile, evenimentele și apelanții specificați de tine. Totuși, vei auzi tot ce alegi să redai, inclusiv muzică, videoclipuri și jocuri."</string>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Se vor anunța prin sunete și vibrații numai alarmele. Totuși, vei auzi tot ce alegi să redai, inclusiv muzică, videoclipuri și jocuri."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Personalizează"</string>
@@ -478,6 +478,8 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Se încarcă lent • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> până la încărcarea completă"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Se încarcă • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> până la încărcarea completă"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Widgeturi pe ecranul de blocare"</string>
+    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
+    <skip />
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Glisează spre stânga pentru a începe tutorialul pentru comunitate"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Personalizează"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Respinge"</string>
@@ -504,10 +506,9 @@
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"selectează un widget"</string>
     <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"elimină widgetul"</string>
     <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"plasează widgetul selectat"</string>
-    <!-- no translation found for communal_widget_picker_title (1953369090475731663) -->
-    <skip />
-    <!-- no translation found for communal_widget_picker_description (490515450110487871) -->
-    <skip />
+    <string name="communal_widget_picker_title" msgid="1953369090475731663">"Widgeturi pe ecranul de blocare"</string>
+    <string name="communal_widget_picker_description" msgid="490515450110487871">"Oricine vede widgeturile pe ecranul de blocare, chiar dacă tableta e blocată"</string>
+    <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"deselectează widgetul"</string>
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widgeturi pe ecranul de blocare"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Pentru a deschide o aplicație folosind un widget, va trebui să-ți confirmi identitatea. În plus, reține că oricine poate să vadă widgeturile, chiar dacă tableta este blocată. Este posibil ca unele widgeturi să nu fi fost create pentru ecranul de blocare și poate fi nesigur să le adaugi aici."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"OK"</string>
@@ -564,8 +565,8 @@
     <string name="media_projection_action_text" msgid="3634906766918186440">"Începe acum"</string>
     <string name="empty_shade_text" msgid="8935967157319717412">"Nicio notificare"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"Nicio notificare nouă"</string>
-    <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"Notificări adaptabile activate"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"Dispozitivul reduce acum volumul și numărul de ferestre pop-up de pe ecran timp de până la două minute când primești multe notificări într-un timp scurt"</string>
+    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Reducerea sunetului notificărilor este activată"</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Volumul și alertele dispozitivului sunt reduse automat timp de până la 2 min. când primești prea multe notificări odată"</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Dezactivează"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Deblochează ca să vezi notificări vechi"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Dispozitivul este gestionat de unul dintre părinți"</string>
@@ -732,8 +733,7 @@
     <string name="notification_alert_title" msgid="3656229781017543655">"Prestabilite"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Automat"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"Fără sunet sau vibrații"</string>
-    <!-- no translation found for notification_conversation_summary_low (3855696451919728790) -->
-    <skip />
+    <string name="notification_conversation_summary_low" msgid="3855696451919728790">"Fără sunete sau vibrații, dar apare în secțiunea de conversație"</string>
     <string name="notification_channel_summary_default" msgid="777294388712200605">"Poate să sune sau să vibreze, în funcție de setările dispozitivului"</string>
     <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Poate să sune sau să vibreze, în funcție de setările dispozitivului. Conversațiile din balonul <xliff:g id="APP_NAME">%1$s</xliff:g> în mod prestabilit."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Solicită-i sistemului să stabilească dacă această notificare e sonoră sau cu vibrații."</string>
@@ -790,8 +790,7 @@
     <string name="keyboard_key_page_up" msgid="173914303254199845">"O pagină mai sus"</string>
     <string name="keyboard_key_page_down" msgid="9035902490071829731">"O pagină mai jos"</string>
     <string name="keyboard_key_forward_del" msgid="5325501825762733459">"Șterge"</string>
-    <!-- no translation found for keyboard_key_esc (6230365950511411322) -->
-    <skip />
+    <string name="keyboard_key_esc" msgid="6230365950511411322">"Esc"</string>
     <string name="keyboard_key_move_home" msgid="3496502501803911971">"La început"</string>
     <string name="keyboard_key_move_end" msgid="99190401463834854">"La final"</string>
     <string name="keyboard_key_insert" msgid="4621692715704410493">"Inserează"</string>
@@ -1233,7 +1232,6 @@
     <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>
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Date mobile"</string>
-    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Conectat"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Conectat temporar"</string>
     <string name="mobile_data_poor_connection" msgid="819617772268371434">"Conexiune slabă"</string>
@@ -1374,8 +1372,7 @@
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Ecran împărțit"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Intrare"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Comenzi rapide pentru aplicații"</string>
-    <!-- no translation found for shortcut_helper_category_current_app_shortcuts (4017840565974573628) -->
-    <skip />
+    <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Aplicația actuală"</string>
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accesibilitate"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Comenzi rapide de la tastatură"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Comenzi directe de căutare"</string>
@@ -1388,13 +1385,27 @@
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Gata"</string>
     <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Excelent!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Înapoi"</string>
-    <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"Pentru a reveni, glisează spre stânga sau spre dreapta cu trei degete oriunde pe touchpad."</string>
+    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
+    <skip />
+    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
+    <skip />
     <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Touchpad cu trei degete care se mișcă spre dreapta și spre stânga"</string>
     <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Ecran de dispozitiv cu o animație pentru gestul Înapoi"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Iluminarea din spate a tastaturii"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Nivelul %1$d din %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Comenzi pentru locuință"</string>
     <string name="home_controls_dream_description" msgid="4644150952104035789">"Accesează comenzile pentru locuință ca screensaver"</string>
-    <!-- no translation found for volume_undo_action (5815519725211877114) -->
-    <skip />
+    <string name="volume_undo_action" msgid="5815519725211877114">"Anulează"</string>
+    <string name="back_edu_toast_content" msgid="4530314597378982956">"Pentru a reveni, glisează spre stânga sau spre dreapta cu trei degete pe touchpad"</string>
+    <string name="home_edu_toast_content" msgid="3381071147871955415">"Ca să accesezi pagina de pornire, glisează în sus cu trei degete pe touchpad"</string>
+    <string name="overview_edu_toast_content" msgid="5797030644017804518">"Ca să vezi aplicațiile recente, glisează în sus și ține apăsat cu trei degete pe touchpad"</string>
+    <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Ca să vezi toate aplicațiile, apasă tasta de acțiuni de pe tastatură"</string>
+    <string name="back_edu_notification_title" msgid="5624780717751357278">"Folosește-ți touchpadul ca să revii"</string>
+    <string name="back_edu_notification_content" msgid="2497557451540954068">"Glisează la stânga sau la dreapta cu trei degete. Atinge ca să înveți mai multe gesturi."</string>
+    <string name="home_edu_notification_title" msgid="6097902076909654045">"Folosește-ți touchpadul ca să accesezi pagina de pornire"</string>
+    <string name="home_edu_notification_content" msgid="6631697734535766588">"Glisează în sus cu trei degete. Atinge ca să înveți mai multe gesturi."</string>
+    <string name="overview_edu_notification_title" msgid="1265824157319562406">"Folosește-ți touchpadul ca să vezi aplicațiile recente"</string>
+    <string name="overview_edu_notification_content" msgid="3578204677648432500">"Glisează în sus și ține apăsat cu trei degete. Atinge ca să înveți mai multe gesturi."</string>
+    <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Folosește-ți tastatura ca să vezi toate aplicațiile"</string>
+    <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Apasă oricând tasta de acțiuni. Atinge ca să înveți mai multe gesturi."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ro/tiles_states_strings.xml b/packages/SystemUI/res/values-ro/tiles_states_strings.xml
index 25a2959..a68f140 100644
--- a/packages/SystemUI/res/values-ro/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ro/tiles_states_strings.xml
@@ -56,9 +56,6 @@
     <item msgid="5376619709702103243">"Dezactivată"</item>
     <item msgid="4875147066469902392">"Activată"</item>
   </string-array>
-    <!-- no translation found for tile_states_modes:0 (7764936419245199023) -->
-    <!-- no translation found for tile_states_modes:1 (2004750556637773692) -->
-    <!-- no translation found for tile_states_modes:2 (8968530753931637871) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Indisponibilă"</item>
     <item msgid="5044688398303285224">"Dezactivată"</item>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index 507b8d1..3811185 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -119,7 +119,7 @@
     <string name="screenrecord_continue" msgid="4055347133700593164">"Начать"</string>
     <string name="screenrecord_ongoing_screen_only" msgid="4459670242451527727">"Идет запись видео с экрана"</string>
     <string name="screenrecord_ongoing_screen_and_audio" msgid="5351133763125180920">"Идет запись видео с экрана и звука"</string>
-    <string name="screenrecord_taps_label" msgid="1595690528298857649">"Показывать прикосновения к экрану"</string>
+    <string name="screenrecord_taps_label" msgid="1595690528298857649">"Показывать прикосновения"</string>
     <string name="screenrecord_stop_label" msgid="72699670052087989">"Остановить"</string>
     <string name="screenrecord_share_label" msgid="5025590804030086930">"Поделиться"</string>
     <string name="screenrecord_save_title" msgid="1886652605520893850">"Видео с экрана сохранено"</string>
@@ -290,8 +290,7 @@
     <string name="start_dreams" msgid="9131802557946276718">"Заставка"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"Не беспокоить"</string>
-    <!-- no translation found for quick_settings_modes_label (5407025818652750501) -->
-    <skip />
+    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Режимы приоритета"</string>
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Нет доступных сопряженных устройств"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Нажмите, чтобы подключить или отключить устройство."</string>
@@ -379,7 +378,7 @@
     <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Запись видео с экрана"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Начать"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Остановить"</string>
-    <string name="qs_record_issue_label" msgid="8166290137285529059">"Запись неисправности"</string>
+    <string name="qs_record_issue_label" msgid="8166290137285529059">"Запись неполадки"</string>
     <string name="qs_record_issue_start" msgid="2979831312582567056">"Начать"</string>
     <string name="qs_record_issue_stop" msgid="3531747965741982657">"Остановить"</string>
     <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Отчет об ошибке"</string>
@@ -388,7 +387,10 @@
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Запись экрана"</string>
     <string name="performance" msgid="6552785217174378320">"Производительность"</string>
     <string name="user_interface" msgid="3712869377953950887">"Интерфейс"</string>
-    <string name="thermal" msgid="6758074791325414831">"Тепловизор"</string>
+    <string name="thermal" msgid="6758074791325414831">"Нагрев"</string>
+    <string name="custom" msgid="3337456985275158299">"Свой вариант"</string>
+    <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Свои настройки трассировки"</string>
+    <string name="restore_default" msgid="5259420807486239755">"Восстановить настройки по умолчанию"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Режим управления одной рукой"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Слуховые аппараты"</string>
     <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Активно"</string>
@@ -427,6 +429,17 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Открыть настройки"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Другое устройство"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Переключить режим обзора"</string>
+    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Режимы приоритета"</string>
+    <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_off" msgid="1736604456618147306">"Отключено"</string>
+    <!-- no translation found for zen_mode_set_up (7457957033034460064) -->
+    <skip />
+    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
+    <skip />
+    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
+    <skip />
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Вас не будут отвлекать звуки и вибрация, за исключением сигналов будильника, напоминаний, уведомлений о мероприятиях и звонков от помеченных контактов. Вы по-прежнему будете слышать включенную вами музыку, видео, игры и т. д."</string>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Вас не будут отвлекать звуки и вибрация, за исключением сигналов будильника. Вы по-прежнему будете слышать включенную вами музыку, видео, игры и т. д."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Настроить"</string>
@@ -465,6 +478,8 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Медленная зарядка • Осталось <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Зарядка • Осталось <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Виджеты на заблокированном экране"</string>
+    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
+    <skip />
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Чтобы ознакомиться с руководством, проведите по экрану влево"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Настроить"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Закрыть"</string>
@@ -493,6 +508,7 @@
     <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"разместить выбранный виджет"</string>
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"Виджеты на заблокированном экране"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"Они видны всем, даже если планшет заблокирован."</string>
+    <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"отменить выбор виджета"</string>
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Виджеты на заблокированном экране"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Чтобы открыть приложение, используя виджет, вам нужно будет подтвердить свою личность. Обратите внимание, что виджеты видны всем, даже если планшет заблокирован. Некоторые виджеты не предназначены для использования на заблокированном экране. Добавлять их туда может быть небезопасно."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"ОК"</string>
@@ -549,8 +565,8 @@
     <string name="media_projection_action_text" msgid="3634906766918186440">"Начать"</string>
     <string name="empty_shade_text" msgid="8935967157319717412">"Нет уведомлений."</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"Новых уведомлений нет"</string>
-    <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"Адаптивные уведомления включены"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"Если за короткий промежуток времени придет много уведомлений, громкость звуков и количество всплывающих окон будут уменьшены на срок до двух минут."</string>
+    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Снижение громкости уведомлений включено"</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Если придет слишком много уведомлений, на две минуты громкость и количество оповещений уменьшатся."</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Отключить"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Разблокируйте, чтобы увидеть уведомления"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Устройством управляет один из родителей."</string>
@@ -649,7 +665,7 @@
     <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Нажмите, чтобы выключить звук. Специальные возможности могут прекратить работу."</string>
     <string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Нажмите, чтобы включить вибрацию."</string>
     <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Нажмите, чтобы выключить звук."</string>
-    <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"Контроль уровня шума"</string>
+    <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"Контроль шума"</string>
     <string name="volume_panel_spatial_audio_title" msgid="3367048857932040660">"Пространственное звучание"</string>
     <string name="volume_panel_spatial_audio_off" msgid="4177490084606772989">"Отключено"</string>
     <string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Без отсле­живания"</string>
@@ -667,8 +683,8 @@
     <string name="volume_panel_hint_unmute" msgid="4831850937582282340">"Включить звук аудиопотока \"%s\""</string>
     <string name="volume_panel_hint_muted" msgid="1124844870181285320">"без звука"</string>
     <string name="volume_panel_hint_vibrate" msgid="4136223145435914132">"вибросигнал"</string>
-    <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g> – запущено здесь:"</string>
-    <string name="media_output_title_without_playing" msgid="3825663683169305013">"Проигрывание аудио:"</string>
+    <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g> – играет здесь:"</string>
+    <string name="media_output_title_without_playing" msgid="3825663683169305013">"Аудио будет звучать здесь"</string>
     <string name="media_output_title_ongoing_call" msgid="208426888064112006">"Настройки вызова"</string>
     <string name="system_ui_tuner" msgid="1471348823289954729">"System UI Tuner"</string>
     <string name="status_bar" msgid="4357390266055077437">"Строка состояния"</string>
@@ -717,8 +733,7 @@
     <string name="notification_alert_title" msgid="3656229781017543655">"По умолчанию"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Автоматически"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"Без звука и вибрации"</string>
-    <!-- no translation found for notification_conversation_summary_low (3855696451919728790) -->
-    <skip />
+    <string name="notification_conversation_summary_low" msgid="3855696451919728790">"Без звука и вибрации, но появляется в списке разговоров"</string>
     <string name="notification_channel_summary_default" msgid="777294388712200605">"Звонок или вибрация в зависимости от настроек устройства"</string>
     <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Звонок или вибрация в зависимости от настроек устройства. Разговоры из приложения \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" по умолчанию появляются в виде всплывающего чата."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Система будет сама определять, включать ли звуковой сигнал или вибрацию для уведомления"</string>
@@ -726,7 +741,7 @@
     <string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"&lt;b&gt;Статус:&lt;/b&gt; понижено до уровня \"Без звука\""</string>
     <string name="notification_channel_summary_automatic_promoted" msgid="1301710305149590426">"&lt;b&gt;Статус:&lt;/b&gt; уровень важности повышен"</string>
     <string name="notification_channel_summary_automatic_demoted" msgid="1831303964660807700">"&lt;b&gt;Статус:&lt;/b&gt; уровень важности понижен"</string>
-    <string name="notification_channel_summary_priority_baseline" msgid="46674690072551234">"Появляется в верхней части уведомлений о сообщениях, а также в качестве фото профиля на заблокированном экране"</string>
+    <string name="notification_channel_summary_priority_baseline" msgid="46674690072551234">"Вверху списка уведомлений о разговорах и в виде фото профиля на заблокированном экране"</string>
     <string name="notification_channel_summary_priority_bubble" msgid="1275413109619074576">"Появляется в верхней части уведомлений о сообщениях, в виде всплывающего чата, а также в качестве фото профиля на заблокированном экране."</string>
     <string name="notification_channel_summary_priority_dnd" msgid="6665395023264154361">"Появляется в верхней части уведомлений о сообщениях, а также в виде фото профиля на заблокированном экране, прерывает режим \"Не беспокоить\"."</string>
     <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Появляется в верхней части уведомлений о сообщениях, в виде всплывающего чата, а также в качестве фото профиля на заблокированном экране, прерывает режим \"Не беспокоить\"."</string>
@@ -1217,7 +1232,6 @@
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Требуется аутентификация. Приложите палец к сканеру отпечатков."</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Текущий вызов"</string>
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Мобильный интернет"</string>
-    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Подключено"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Временное подключение"</string>
     <string name="mobile_data_poor_connection" msgid="819617772268371434">"Слабый сигнал"</string>
@@ -1358,8 +1372,7 @@
     <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>
-    <!-- no translation found for shortcut_helper_category_current_app_shortcuts (4017840565974573628) -->
-    <skip />
+    <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_search_placeholder" msgid="5488547526269871819">"Найти быстрые клавиши"</string>
@@ -1372,13 +1385,27 @@
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Готово"</string>
     <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Отлично!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Назад"</string>
-    <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"Чтобы вернуться, проведите тремя пальцами влево или вправо по сенсорной панели."</string>
+    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
+    <skip />
+    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
+    <skip />
     <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Три пальца двигаются вправо и влево по сенсорной панели"</string>
     <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"На экране устройства показана анимация для жеста \"Назад\""</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Подсветка клавиатуры"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Уровень %1$d из %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Управление домом"</string>
     <string name="home_controls_dream_description" msgid="4644150952104035789">"Быстрый доступ к управлению домом через заставку"</string>
-    <!-- no translation found for volume_undo_action (5815519725211877114) -->
-    <skip />
+    <string name="volume_undo_action" msgid="5815519725211877114">"Отменить"</string>
+    <string name="back_edu_toast_content" msgid="4530314597378982956">"Чтобы вернуться назад, проведите по сенсорной панели тремя пальцами влево или вправо."</string>
+    <string name="home_edu_toast_content" msgid="3381071147871955415">"Чтобы перейти на главный экран, проведите по сенсорной панели тремя пальцами вверх."</string>
+    <string name="overview_edu_toast_content" msgid="5797030644017804518">"Чтобы увидеть недавние приложения, проведите по сенсорной панели тремя пальцами вверх и удерживайте."</string>
+    <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Чтобы открыть список всех своих приложений, нажмите клавишу действия."</string>
+    <string name="back_edu_notification_title" msgid="5624780717751357278">"Используйте сенсорную панель, чтобы возвращаться назад"</string>
+    <string name="back_edu_notification_content" msgid="2497557451540954068">"Для этого проведите тремя пальцами влево или вправо. Нажмите, чтобы посмотреть другие жесты."</string>
+    <string name="home_edu_notification_title" msgid="6097902076909654045">"Используйте сенсорную панель, чтобы переходить на главный экран"</string>
+    <string name="home_edu_notification_content" msgid="6631697734535766588">"Для этого проведите тремя пальцами вверх. Нажмите, чтобы посмотреть другие жесты."</string>
+    <string name="overview_edu_notification_title" msgid="1265824157319562406">"Используйте сенсорную панель, чтобы смотреть список недавних приложений"</string>
+    <string name="overview_edu_notification_content" msgid="3578204677648432500">"Для этого проведите тремя пальцами вверх и удерживайте. Нажмите, чтобы посмотреть другие жесты."</string>
+    <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Открывайте список всех приложений с помощью клавиатуры"</string>
+    <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Для этого можно использовать клавишу действия. Нажмите, чтобы посмотреть другие жесты."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ru/tiles_states_strings.xml b/packages/SystemUI/res/values-ru/tiles_states_strings.xml
index 2a0314e..592937c 100644
--- a/packages/SystemUI/res/values-ru/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ru/tiles_states_strings.xml
@@ -56,9 +56,6 @@
     <item msgid="5376619709702103243">"Откл."</item>
     <item msgid="4875147066469902392">"Вкл."</item>
   </string-array>
-    <!-- no translation found for tile_states_modes:0 (7764936419245199023) -->
-    <!-- no translation found for tile_states_modes:1 (2004750556637773692) -->
-    <!-- no translation found for tile_states_modes:2 (8968530753931637871) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Функция недоступна"</item>
     <item msgid="5044688398303285224">"Откл."</item>
diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml
index ebcfa9d..46b82e3 100644
--- a/packages/SystemUI/res/values-si/strings.xml
+++ b/packages/SystemUI/res/values-si/strings.xml
@@ -126,38 +126,25 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"බැලීමට තට්ටු කරන්න"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"තිර පටිගත කිරීම සුරැකීමේ දෝෂයකි"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"තිර පටිගත කිරීම ආරම්භ කිරීමේ දෝෂයකි"</string>
-    <!-- no translation found for screenrecord_stop_dialog_title (8716193661764511095) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message (6262768207331626817) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5995770227684523244) -->
-    <skip />
+    <string name="screenrecord_stop_dialog_title" msgid="8716193661764511095">"පටිගත කිරීම නවතන්න ද?"</string>
+    <string name="screenrecord_stop_dialog_message" msgid="6262768207331626817">"ඔබ දැනට ඔබේ සම්පූර්ණ තිරය පටිගත කරමින් සිටී"</string>
+    <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"ඔබ දැනට <xliff:g id="APP_NAME">%1$s</xliff:g> පටිගත කරමින් සිටී"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"පටිගත කිරීම නවත්වන්න"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"තිරය ​​බෙදා ගැනීම"</string>
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"තිරය ​​බෙදා ගැනීම නවත්වන්න ද?"</string>
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen_with_host_app (522823522115375414) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen (5090115386271179270) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_specific (5923772039347985172) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_generic (6681016774654578261) -->
-    <skip />
+    <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"ඔබ දැනට ඔබේ සම්පූර්ණ තිරය <xliff:g id="HOST_APP_NAME">%1$s</xliff:g> සමග බෙදා ගනිමින් සිටී"</string>
+    <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"ඔබ දැනට ඔබේ සම්පූර්ණ තිරය යෙදුමක් සමග බෙදා ගනිමින් සිටී"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"ඔබ දැනට <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> බෙදා ගනිමින් සිටී"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"ඔබ දැනට යෙදුමක් බෙදා ගනිමින් සිටී"</string>
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"බෙදා ගැනීම නවත්වන්න"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"විකාශ තිරය"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"විකාශය නවතන්න ද?"</string>
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen_with_device (1474703115926205251) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen (8419219169553867625) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app_with_device (2715934698604085519) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (8616103075630934513) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic_with_device (9213582497852420203) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic (4100272100480415076) -->
-    <skip />
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen_with_device" msgid="1474703115926205251">"ඔබ දැනට ඔබේ සම්පූර්ණ තිරය <xliff:g id="DEVICE_NAME">%1$s</xliff:g> වෙත විකාශය කරමින් සිටී"</string>
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen" msgid="8419219169553867625">"ඔබ දැනට ඔබේ සම්පූර්ණ තිරයම අවට උපාංගයකට විකාශය කරමින් සිටී"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app_with_device" msgid="2715934698604085519">"ඔබ දැනට <xliff:g id="DEVICE_NAME">%2$s</xliff:g> වෙත <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> විකාශය කරමින් සිටී"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="8616103075630934513">"ඔබ දැනට අවට උපාංගයකට <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> විකාශය කරමින් සිටී"</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic_with_device" msgid="9213582497852420203">"ඔබ දැනට <xliff:g id="DEVICE_NAME">%1$s</xliff:g> වෙත විකාශය කරයි"</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic" msgid="4100272100480415076">"ඔබ දැනට අවට උපාංගයකට විකාශය කරමින් සිටී"</string>
     <string name="cast_to_other_device_stop_dialog_button" msgid="6420183747435521834">"විකාශය නවතන්න"</string>
     <string name="close_dialog_button" msgid="4749497706540104133">"වසන්න"</string>
     <string name="issuerecord_title" msgid="286627115110121849">"ගැටලු රෙකෝඩරය"</string>
@@ -303,8 +290,7 @@
     <string name="start_dreams" msgid="9131802557946276718">"තිර සුරැකුම"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"ඊතර නෙට්"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"බාධා නොකරන්න"</string>
-    <!-- no translation found for quick_settings_modes_label (5407025818652750501) -->
-    <skip />
+    <string name="quick_settings_modes_label" msgid="5407025818652750501">"ප්‍රමුඛතා ප්‍රකාර"</string>
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"බ්ලූටූත්"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"යුගල කළ උපාංග නොතිබේ"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"උපාංගයක් සම්බන්ධ කිරීමට හෝ විසන්ධි කිරීමට තට්ටු කරන්න"</string>
@@ -402,6 +388,9 @@
     <string name="performance" msgid="6552785217174378320">"කාර්ය සාධනය"</string>
     <string name="user_interface" msgid="3712869377953950887">"පරිශීලක අතුරු මුහුණත"</string>
     <string name="thermal" msgid="6758074791325414831">"තාප"</string>
+    <string name="custom" msgid="3337456985275158299">"අභිරුචි"</string>
+    <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"අභිරුචි ලුහුබැඳීම් සැකසීම්"</string>
+    <string name="restore_default" msgid="5259420807486239755">"පෙරනිමිය ප්‍රතිසාධනය කරන්න"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"තනි අත් ප්‍රකාරය"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"ශ්‍රවණ උපාංග"</string>
     <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"ක්‍රියාකාරී"</string>
@@ -440,6 +429,17 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"සැකසීම් විවෘත කරන්න"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"වෙනත් උපාංගය"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"දළ විශ්ලේෂණය ටොගල කරන්න"</string>
+    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"ප්‍රමුඛතා ප්‍රකාර"</string>
+    <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_off" msgid="1736604456618147306">"ක්‍රියාවිරහිතයි"</string>
+    <!-- no translation found for zen_mode_set_up (7457957033034460064) -->
+    <skip />
+    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
+    <skip />
+    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
+    <skip />
     <string name="zen_priority_introduction" msgid="3159291973383796646">"එලාම සිහිකැඳවීම්, සිදුවීම්, සහ ඔබ සඳහන් කළ අමතන්නන් හැර, ශබ්ද සහ කම්පනවලින් ඔබට බාධා නොවනු ඇත. සංගීතය, වීඩියෝ, සහ ක්‍රීඩා ඇතුළු ඔබ වාදනය කිරීමට තෝරන ලද සියලු දේ ඔබට තවම ඇසෙනු ඇත."</string>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"එලාම හැර, ශබ්ද සහ කම්පනවලින් ඔබට බාධා නොවනු ඇත. සංගීතය, වීඩියෝ, සහ ක්‍රීඩා ඇතුළු ඔබ වාදනය කිරීමට තෝරන ලද සියලු දේ ඔබට තවම ඇසෙනු ඇත."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"අභිරුචිකරණය"</string>
@@ -478,6 +478,8 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • සෙමින් ආරෝපණය වෙමින් • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>කින් සම්පූර්ණ වේ"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ආරෝපණය වෙමින් • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>කින් සම්පූර්ණ වේ"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"අගුළු තිරයෙහි විජට්"</string>
+    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
+    <skip />
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"පොදු නිබන්ධනය ආරම්භ කිරීමට වමට ස්වයිප් කරන්න"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"අභිරුචිකරණය කරන්න"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"අස් කරන්න"</string>
@@ -504,10 +506,9 @@
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"විජට්ටුව තෝරන්න"</string>
     <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"විජට්ටුව ඉවත් කරන්න"</string>
     <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"තෝරන ලද විජට්ටුව තබන්න"</string>
-    <!-- no translation found for communal_widget_picker_title (1953369090475731663) -->
-    <skip />
-    <!-- no translation found for communal_widget_picker_description (490515450110487871) -->
-    <skip />
+    <string name="communal_widget_picker_title" msgid="1953369090475731663">"අගුළු තිර විජට්"</string>
+    <string name="communal_widget_picker_description" msgid="490515450110487871">"ඔබේ ටැබ්ලටය අගුළු දමා තිබුණත්, ඕනෑම කෙනෙකුට ඔබේ අගුළු තිරයෙහි විජට් බැලිය හැක."</string>
+    <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"විජට් නොතෝරන්න"</string>
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"අගුළු තිර විජට්"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"විජට් එකක් භාවිතයෙන් යෙදුමක් විවෘත කිරීමට, ඔබට ඒ ඔබ බව සත්‍යාපනය කිරීමට අවශ්‍ය වනු ඇත. එසේම, ඔබේ ටැබ්ලටය අගුළු දමා ඇති විට පවා ඕනෑම කෙනෙකුට ඒවා බැලිය හැකි බව මතක තබා ගන්න. සමහර විජට් ඔබේ අගුළු තිරය සඳහා අදහස් කර නොතිබිය හැකි අතර මෙහි එක් කිරීමට අනාරක්ෂිත විය හැක."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"තේරුණා"</string>
@@ -564,8 +565,8 @@
     <string name="media_projection_action_text" msgid="3634906766918186440">"දැන් අරඹන්න"</string>
     <string name="empty_shade_text" msgid="8935967157319717412">"දැනුම්දීම් නැත"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"නව දැනුම්දීම් නැත"</string>
-    <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"අනුවර්තන දැනුම්දීම් ක්‍රියාත්මකයි"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"ඔබේ උපාංගය දැන් ශබ්දය අඩු කරන අතර ඔබට කෙටි කාල පරාසයක් තුළ බොහෝ දැනුම්දීම් ලැබෙන විට තිරය මත උත්පතන විනාඩි දෙකක් දක්වා අඩු කරයි."</string>
+    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"දැනුම්දීම් සිසිල් කිරීම ක්‍රියාත්මකයි"</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"ඔබට එකවර දැනුම්දීම් වැඩි ප්‍රමාණයක් ලැබෙන විට ඔබේ උපාංග පරිමාව සහ ඇඟවීම් මිනිත්තු 2ක් දක්වා ස්වයංක්‍රීයව අඩු වේ."</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"ක්‍රියාවිරහිත කරන්න"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"පැරණි දැනුම්දීම් බැලීමට අගුළු හරින්න"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"මෙම උපාංගය ඔබගේ මාපියන්ගෙන් අයකු විසින් කළමනාකරණය කෙරේ"</string>
@@ -732,8 +733,7 @@
     <string name="notification_alert_title" msgid="3656229781017543655">"පෙරනිමි"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"ස්වයංක්‍රිය"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"හඬක් හෝ කම්පනයක් නැත"</string>
-    <!-- no translation found for notification_conversation_summary_low (3855696451919728790) -->
-    <skip />
+    <string name="notification_conversation_summary_low" msgid="3855696451919728790">"ශබ්දයක් හෝ කම්පනයක් නොමැති නමුත් තවමත් සංවාද කොටසේ දිස් වේ"</string>
     <string name="notification_channel_summary_default" msgid="777294388712200605">"උපාංග සැකසීම් මත පදනම්ව නාද වීමට හෝ කම්පනය විය හැක"</string>
     <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"උපාංග සැකසීම් මත පදනම්ව නාද වීමට හෝ කම්පනය විය හැක. <xliff:g id="APP_NAME">%1$s</xliff:g> වෙතින් සංවාද පෙරනිමියෙන් බුබුළු දමයි."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"මෙම දැනුම් දීම ශබ්දයක් හෝ කම්පනයක් ඇති කළ යුතු ද යන්න පද්ධතිය මගින් තීරණය කර තිබේද"</string>
@@ -790,8 +790,7 @@
     <string name="keyboard_key_page_up" msgid="173914303254199845">"Page Up යතුර"</string>
     <string name="keyboard_key_page_down" msgid="9035902490071829731">"Page Down යතුර"</string>
     <string name="keyboard_key_forward_del" msgid="5325501825762733459">"Delete යතුර"</string>
-    <!-- no translation found for keyboard_key_esc (6230365950511411322) -->
-    <skip />
+    <string name="keyboard_key_esc" msgid="6230365950511411322">"Esc"</string>
     <string name="keyboard_key_move_home" msgid="3496502501803911971">"Home යතුර"</string>
     <string name="keyboard_key_move_end" msgid="99190401463834854">"End යතුර"</string>
     <string name="keyboard_key_insert" msgid="4621692715704410493">"Insert යතුර"</string>
@@ -1233,7 +1232,6 @@
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"සත්‍යාපනය අවශ්‍යයි. සත්‍යාපනය කිරීමට ඇඟිලි සලකුණු සංවේදකය ස්පර්ශ කරන්න."</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"ක්‍රියාත්මක වන දුරකථන ඇමතුම"</string>
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"ජංගම දත්ත"</string>
-    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"සම්බන්ධයි"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"තාවකාලිකව සම්බන්ධ කළා"</string>
     <string name="mobile_data_poor_connection" msgid="819617772268371434">"දුර්වල සම්බන්ධතාව"</string>
@@ -1374,8 +1372,7 @@
     <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>
-    <!-- no translation found for shortcut_helper_category_current_app_shortcuts (4017840565974573628) -->
-    <skip />
+    <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_search_placeholder" msgid="5488547526269871819">"කෙටි මං සොයන්න"</string>
@@ -1388,13 +1385,27 @@
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"නිමයි"</string>
     <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"අනර්ඝ වැඩක්!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"ආපස්සට යන්න"</string>
-    <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"ආපසු යාමට, ස්පර්ශ පුවරුවේ ඕනෑම තැනක ඇඟිලි තුනක් භාවිතයෙන් වමට හෝ දකුණට ස්වයිප් කරන්න."</string>
+    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
+    <skip />
+    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
+    <skip />
     <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"ඇඟිලි තුනක් දකුණට සහ වමට චලනය වන බව පෙන්වන ස්පර්ශක පුවරුව"</string>
     <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"පසුපස අභිනය සඳහා සජීවිකරණය පෙන්වන උපාංග තිරය"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"යතුරු පුවරු පසු ආලෝකය"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$dන් %1$d වැනි මට්ටම"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"නිවෙස් පාලන"</string>
     <string name="home_controls_dream_description" msgid="4644150952104035789">"තිර සුරැකුමක් ලෙස ඔබේ නිවසේ පාලන වෙත ඉක්මනින් ප්‍රවේශ වන්න"</string>
-    <!-- no translation found for volume_undo_action (5815519725211877114) -->
-    <skip />
+    <string name="volume_undo_action" msgid="5815519725211877114">"පසුගමනය කරන්න"</string>
+    <string name="back_edu_toast_content" msgid="4530314597378982956">"ආපසු යාමට, ස්පර්ශ පුවරුව මත ඇඟිලි තුනකින් වමට හෝ දකුණට ස්වයිප් කරන්න"</string>
+    <string name="home_edu_toast_content" msgid="3381071147871955415">"මුල් පිටුවට යාමට, ස්පර්ශ පුවරුව මත ඇඟිලි තුනකින් ඉහළට ස්වයිප් කරන්න"</string>
+    <string name="overview_edu_toast_content" msgid="5797030644017804518">"මෑත යෙදුම් බැලීමට, ඉහළට ස්වයිප් කර ස්පර්ශ පුවරුව මත ඇඟිලි තුනකින් අල්ලාගෙන සිටින්න"</string>
+    <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"ඔබේ සියලුම යෙදුම් බැලීමට, ඔබේ යතුරුපුවරුවේ ක්‍රියාකාරී යතුර ඔබන්න"</string>
+    <string name="back_edu_notification_title" msgid="5624780717751357278">"ආපසු යාමට ඔබේ ස්පර්ශ පුවරුව භාවිත කරන්න"</string>
+    <string name="back_edu_notification_content" msgid="2497557451540954068">"ඇඟිලි තුනක් භාවිතයෙන් වමට හෝ දකුණට ස්වයිප් කරන්න. තව ඉංගිත දැන ගැනීමට තට්ටු කරන්න."</string>
+    <string name="home_edu_notification_title" msgid="6097902076909654045">"මුල් පිටුවට යාමට ඔබේ ස්පර්ශ පුවරුව භාවිත කරන්න"</string>
+    <string name="home_edu_notification_content" msgid="6631697734535766588">"ඇඟිලි තුනක් භාවිතයෙන් ඉහළට ස්වයිප් කරන්න. තව ඉංගිත දැන ගැනීමට තට්ටු කරන්න."</string>
+    <string name="overview_edu_notification_title" msgid="1265824157319562406">"මෑත යෙදුම් බැලීමට ඔබේ ස්පර්ශ පුවරුව භාවිත කරන්න"</string>
+    <string name="overview_edu_notification_content" msgid="3578204677648432500">"ඇඟිලි තුනක් භාවිතයෙන් ඉහළට ස්වයිප් කර අල්ලාගෙන සිටින්න. තව ඉංගිත දැන ගැනීමට තට්ටු කරන්න."</string>
+    <string name="all_apps_edu_notification_title" msgid="372262997265569063">"සියලුම යෙදුම් බැලීමට ඔබේ යතුරු පුවරුව භාවිත කරන්න"</string>
+    <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"ඕනෑම අවස්ථාවක ක්‍රියාකාරී යතුර ඔබන්න. තව ඉංගිත දැන ගැනීමට තට්ටු කරන්න."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-si/tiles_states_strings.xml b/packages/SystemUI/res/values-si/tiles_states_strings.xml
index 8d1f261..681f3d5 100644
--- a/packages/SystemUI/res/values-si/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-si/tiles_states_strings.xml
@@ -56,9 +56,6 @@
     <item msgid="5376619709702103243">"අක්‍රියයි"</item>
     <item msgid="4875147066469902392">"සක්‍රියයි"</item>
   </string-array>
-    <!-- no translation found for tile_states_modes:0 (7764936419245199023) -->
-    <!-- no translation found for tile_states_modes:1 (2004750556637773692) -->
-    <!-- no translation found for tile_states_modes:2 (8968530753931637871) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"නොමැත"</item>
     <item msgid="5044688398303285224">"අක්‍රියයි"</item>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index 2f99316..b607739 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -126,38 +126,25 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"Zobrazte klepnutím"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"Pri ukladaní nahrávky obrazovky sa vyskytla chyba"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Pri spustení nahrávania obrazovky sa vyskytla chyba"</string>
-    <!-- no translation found for screenrecord_stop_dialog_title (8716193661764511095) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message (6262768207331626817) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5995770227684523244) -->
-    <skip />
+    <string name="screenrecord_stop_dialog_title" msgid="8716193661764511095">"Chcete zastaviť nahrávanie?"</string>
+    <string name="screenrecord_stop_dialog_message" msgid="6262768207331626817">"Momentálne nahrávate celú obrazovku"</string>
+    <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Momentálne nahrávate aplikáciu <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Zastaviť nahrávanie"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Zdieľa sa obrazovka"</string>
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Chcete prestať zdieľať obrazovku?"</string>
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen_with_host_app (522823522115375414) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen (5090115386271179270) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_specific (5923772039347985172) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_generic (6681016774654578261) -->
-    <skip />
+    <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Momentálne zdieľate celú obrazovku s aplikáciou <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
+    <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Momentálne zdieľate celú obrazovku s aplikáciou"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Momentálne zdieľate aplikáciu <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Momentálne zdieľate aplikáciu"</string>
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Prestať zdieľať"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Prenáša sa obrazovka"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Chcete zastaviť prenos?"</string>
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen_with_device (1474703115926205251) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen (8419219169553867625) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app_with_device (2715934698604085519) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (8616103075630934513) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic_with_device (9213582497852420203) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic (4100272100480415076) -->
-    <skip />
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen_with_device" msgid="1474703115926205251">"Momentálne prenášate celú obrazovku do zariadenia <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen" msgid="8419219169553867625">"Momentálne prenášate celú obrazovku do zariadenia v okolí"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app_with_device" msgid="2715934698604085519">"Momentálne prenášate <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> do zariadenia <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="8616103075630934513">"Momentálne prenášate <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> do zariadenia v okolí"</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic_with_device" msgid="9213582497852420203">"Momentálne prenášate do zariadenia <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic" msgid="4100272100480415076">"Momentálne prenášate do zariadenia v okolí"</string>
     <string name="cast_to_other_device_stop_dialog_button" msgid="6420183747435521834">"Zastaviť prenos"</string>
     <string name="close_dialog_button" msgid="4749497706540104133">"Zavrieť"</string>
     <string name="issuerecord_title" msgid="286627115110121849">"Nástroj na zaznamenávanie problémov"</string>
@@ -303,8 +290,7 @@
     <string name="start_dreams" msgid="9131802557946276718">"Šetrič obrazovky"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"Režim bez vyrušení"</string>
-    <!-- no translation found for quick_settings_modes_label (5407025818652750501) -->
-    <skip />
+    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Režimy priority"</string>
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Nie sú k dispozícii žiadne spárované zariadenia"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Klepnutím pripojíte alebo odpojíte zariadenie"</string>
@@ -402,6 +388,9 @@
     <string name="performance" msgid="6552785217174378320">"Výkon"</string>
     <string name="user_interface" msgid="3712869377953950887">"Používateľské rozhranie"</string>
     <string name="thermal" msgid="6758074791325414831">"Termálne"</string>
+    <string name="custom" msgid="3337456985275158299">"Vlastné"</string>
+    <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Nastavenia vlastnej stopy"</string>
+    <string name="restore_default" msgid="5259420807486239755">"Obnoviť predvolené"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Režim jednej ruky"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Načúvacie zariadenia"</string>
     <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktívne"</string>
@@ -440,6 +429,17 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Otvoriť Nastavenia"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Iné zariadenie"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Prepnúť prehľad"</string>
+    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Režimy priority"</string>
+    <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Hotovo"</string>
+    <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Nastavenia"</string>
+    <string name="zen_mode_on" msgid="9085304934016242591">"Zapnuté"</string>
+    <string name="zen_mode_off" msgid="1736604456618147306">"Vypnuté"</string>
+    <!-- no translation found for zen_mode_set_up (7457957033034460064) -->
+    <skip />
+    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
+    <skip />
+    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
+    <skip />
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Nebudú vás vyrušovať zvuky ani vibrácie, iba budíky, pripomenutia, udalosti a volajúci, ktorých určíte. Budete naďalej počuť všetko, čo sa rozhodnete prehrať, ako napríklad hudbu, videá a hry."</string>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Nebudú vás vyrušovať zvuky ani vibrácie, iba budíky. Budete naďalej počuť všetko, čo sa rozhodnete prehrať, ako napríklad hudbu, videá a hry."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Prispôsobiť"</string>
@@ -478,6 +478,8 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Nabíja sa pomaly • Do úplného nabitia zostáva <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Nabíja sa • Do úplného nabitia zostáva <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Miniaplikácie na uzamknutej obrazovke"</string>
+    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
+    <skip />
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Potiahnutím doľava spustite komunitný návod"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Prispôsobiť"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Zavrieť"</string>
@@ -504,10 +506,9 @@
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"vybrať miniaplikáciu"</string>
     <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"odstrániť miniaplikáciu"</string>
     <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"prepnúť vybranú miniaplikáciu"</string>
-    <!-- no translation found for communal_widget_picker_title (1953369090475731663) -->
-    <skip />
-    <!-- no translation found for communal_widget_picker_description (490515450110487871) -->
-    <skip />
+    <string name="communal_widget_picker_title" msgid="1953369090475731663">"Miniaplikácie na uzamknutej obrazovke"</string>
+    <string name="communal_widget_picker_description" msgid="490515450110487871">"Miniaplikácie na uzamknutej obrazovke uvidia všetci, aj keď je tablet uzamknutý."</string>
+    <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"zrušiť výber miniaplikácie"</string>
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Miniaplikácie na uzamknutej obrazovke"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Ak chcete otvoriť aplikáciu pomocou miniaplikácie, budete musieť overiť svoju totožnosť. Pamätajte, že si miniaplikáciu môže pozrieť ktokoľvek, aj keď máte tablet uzamknutý. Niektoré miniaplikácie možno nie sú určené pre uzamknutú obrazovku a ich pridanie tu môže byť nebezpečné."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Dobre"</string>
@@ -564,8 +565,8 @@
     <string name="media_projection_action_text" msgid="3634906766918186440">"Spustiť"</string>
     <string name="empty_shade_text" msgid="8935967157319717412">"Žiadne upozornenia"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"Žiadne nové upozornenia"</string>
-    <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"Adaptívne upozornenia sú zap."</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"Zariadenie teraz zníži hlasitosť a zredukuje vyskakovacie okná na obrazovke na dve až štyri minúty, keď dostanete veľa upozornení v krátkom čase."</string>
+    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Stlmenie upozornení je zapnuté"</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Keď dostanete priveľa upozornení naraz, hlasitosť vášho zariadenia a počet upozornení sa automaticky znížia až na dve minúty."</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Vypnúť"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Odomknutím zobrazíte staršie upozornenia"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Toto zariadenie spravuje tvoj rodič"</string>
@@ -732,8 +733,7 @@
     <string name="notification_alert_title" msgid="3656229781017543655">"Predvolené"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Automaticky"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"Žiadny zvuk ani vibrácie"</string>
-    <!-- no translation found for notification_conversation_summary_low (3855696451919728790) -->
-    <skip />
+    <string name="notification_conversation_summary_low" msgid="3855696451919728790">"Žiadny zvuk ani vibrácie, ale stále sa zobrazuje v sekcii konverzácie"</string>
     <string name="notification_channel_summary_default" msgid="777294388712200605">"Môže zvoniť či vibrovať podľa nastavení v zariadení"</string>
     <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Môže zvoniť alebo vibrovať podľa nastavení v zariadení. Predvolene sa zobrazia konverzácie z bubliny aplikácie <xliff:g id="APP_NAME">%1$s</xliff:g>."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Nechajte systém určiť, či má toto upozornenie vydávať zvuk alebo vibrovať"</string>
@@ -790,8 +790,7 @@
     <string name="keyboard_key_page_up" msgid="173914303254199845">"Posunúť o stranu vyššie"</string>
     <string name="keyboard_key_page_down" msgid="9035902490071829731">"Posunúť o stranu nižšie"</string>
     <string name="keyboard_key_forward_del" msgid="5325501825762733459">"Odstrániť"</string>
-    <!-- no translation found for keyboard_key_esc (6230365950511411322) -->
-    <skip />
+    <string name="keyboard_key_esc" msgid="6230365950511411322">"Esc"</string>
     <string name="keyboard_key_move_home" msgid="3496502501803911971">"Domov"</string>
     <string name="keyboard_key_move_end" msgid="99190401463834854">"Ukončiť"</string>
     <string name="keyboard_key_insert" msgid="4621692715704410493">"Vložiť"</string>
@@ -1233,7 +1232,6 @@
     <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>
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobilné dáta"</string>
-    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Pripojené"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Dočasne pripojené"</string>
     <string name="mobile_data_poor_connection" msgid="819617772268371434">"Slabé pripojenie"</string>
@@ -1374,8 +1372,7 @@
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Rozdelená obrazovka"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Vstup"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Odkazy do aplikácií"</string>
-    <!-- no translation found for shortcut_helper_category_current_app_shortcuts (4017840565974573628) -->
-    <skip />
+    <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Aktuálna aplikácia"</string>
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Dostupnosť"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Klávesové skratky"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Vyhľadávacie odkazy"</string>
@@ -1388,13 +1385,27 @@
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Hotovo"</string>
     <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Skvelé!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Prejsť späť"</string>
-    <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"Ak chcete prejsť späť, potiahnite doľava alebo doprava troma prstami kdekoľvek na touchpade."</string>
+    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
+    <skip />
+    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
+    <skip />
     <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Tri prsty na touchpade pohybujúce sa doprava a doľava"</string>
     <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Obrazovka zariadenia, na ktorej je animácia gesta späť"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Podsvietenie klávesnice"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"%1$d. úroveň z %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Ovládanie domácnosti"</string>
     <string name="home_controls_dream_description" msgid="4644150952104035789">"Rýchly prístup k ovládaniu domácnosti z šetriča obrazovky"</string>
-    <!-- no translation found for volume_undo_action (5815519725211877114) -->
-    <skip />
+    <string name="volume_undo_action" msgid="5815519725211877114">"Vrátiť späť"</string>
+    <string name="back_edu_toast_content" msgid="4530314597378982956">"Ak chcete prejsť späť, potiahnite po touchpade troma prstami doľava alebo doprava"</string>
+    <string name="home_edu_toast_content" msgid="3381071147871955415">"Ak sa chcete vrátiť na plochu, potiahnite po touchpade troma prstami nahor."</string>
+    <string name="overview_edu_toast_content" msgid="5797030644017804518">"Ak si chcete zobraziť nedávne aplikácie, potiahnite po touchpade troma prstami nahor a pridržte ich."</string>
+    <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Ak si chcete zobraziť všetky aplikácie, stlačte na klávesnici akčný kláves"</string>
+    <string name="back_edu_notification_title" msgid="5624780717751357278">"Prechádzajte späť pomocou touchpadu"</string>
+    <string name="back_edu_notification_content" msgid="2497557451540954068">"Potiahnite troma prstami doľava alebo doprava. Viac o gestách sa dozviete klepnutím."</string>
+    <string name="home_edu_notification_title" msgid="6097902076909654045">"Vráťte sa na plochu pomocou touchpadu"</string>
+    <string name="home_edu_notification_content" msgid="6631697734535766588">"Potiahnite troma prstami nahor. Viac o gestách sa dozviete klepnutím."</string>
+    <string name="overview_edu_notification_title" msgid="1265824157319562406">"Zobrazte si nedávne aplikácie pomocou touchpadu"</string>
+    <string name="overview_edu_notification_content" msgid="3578204677648432500">"Potiahnite troma prstami nahor a pridržte ich. Viac o gestách sa dozviete klepnutím."</string>
+    <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Zobrazte si všetky aplikácie pomocou klávesnice"</string>
+    <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Akčný kláves môžete stlačiť kedykoľvek. Viac o gestách sa dozviete klepnutím."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-sk/tiles_states_strings.xml b/packages/SystemUI/res/values-sk/tiles_states_strings.xml
index 340af48..6b5af80 100644
--- a/packages/SystemUI/res/values-sk/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-sk/tiles_states_strings.xml
@@ -56,9 +56,6 @@
     <item msgid="5376619709702103243">"Vypnuté"</item>
     <item msgid="4875147066469902392">"Zapnuté"</item>
   </string-array>
-    <!-- no translation found for tile_states_modes:0 (7764936419245199023) -->
-    <!-- no translation found for tile_states_modes:1 (2004750556637773692) -->
-    <!-- no translation found for tile_states_modes:2 (8968530753931637871) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Nie je k dispozícii"</item>
     <item msgid="5044688398303285224">"Vypnuté"</item>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index ac78905..69f63b9 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -218,7 +218,7 @@
     <string name="biometric_re_enroll_notification_content" msgid="8685925877186288180">"To je potrebno, če želite izboljšati varnost in učinkovitost delovanja."</string>
     <string name="fingerprint_re_enroll_notification_title" msgid="4539432429683916604">"Vnovična nastavitev odklepanja s prstnim odtisom"</string>
     <string name="fingerprint_re_enroll_notification_name" msgid="630798657797645704">"Odklepanje s prstnim odtisom"</string>
-    <string name="fingerprint_re_enroll_dialog_title" msgid="3526033128113925780">"Nastavitev odklepanja s prstnim odtisom"</string>
+    <string name="fingerprint_re_enroll_dialog_title" msgid="3526033128113925780">"Nastavite odklepanje s prstnim odtisom"</string>
     <string name="fingerprint_re_enroll_dialog_content" msgid="4866561176695984879">"Če želite znova nastaviti odklepanje s prstnim odtisom, bodo trenutne slike in modeli prstnih odtisov izbrisani.\n\nPo izbrisu boste morali znova nastaviti odklepanje s prstnim odtisom, da boste telefon lahko odklenili s prstnim odtisom ali potrdili svojo identiteto."</string>
     <string name="fingerprint_re_enroll_dialog_content_singular" msgid="3083663339787381218">"Če želite znova nastaviti odklepanje s prstnim odtisom, bodo trenutne slike in model prstnih odtisov izbrisani.\n\nPo izbrisu boste morali znova nastaviti odklepanje s prstnim odtisom, da boste telefon lahko odklenili s prstnim odtisom ali potrdili svojo identiteto."</string>
     <string name="fingerprint_reenroll_failure_dialog_content" msgid="4733768492747300666">"Odklepanja s prstnim odtisom ni bilo mogoče nastaviti. Odprite nastavitve in poskusite znova."</string>
@@ -388,6 +388,9 @@
     <string name="performance" msgid="6552785217174378320">"Učinkovitost delovanja"</string>
     <string name="user_interface" msgid="3712869377953950887">"Uporabniški vmesnik"</string>
     <string name="thermal" msgid="6758074791325414831">"Toplotno"</string>
+    <string name="custom" msgid="3337456985275158299">"Po meri"</string>
+    <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Nastavitve sledi po meri"</string>
+    <string name="restore_default" msgid="5259420807486239755">"Obnovi privzeto"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Enoročni način"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Slušni pripomočki"</string>
     <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktivno"</string>
@@ -426,6 +429,17 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Odpri nastavitve"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Druga naprava"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Vklop/izklop pregleda"</string>
+    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Prednostni načini"</string>
+    <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Končano"</string>
+    <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Nastavitve"</string>
+    <string name="zen_mode_on" msgid="9085304934016242591">"Vklopljeno"</string>
+    <string name="zen_mode_off" msgid="1736604456618147306">"Izklopljeno"</string>
+    <!-- no translation found for zen_mode_set_up (7457957033034460064) -->
+    <skip />
+    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
+    <skip />
+    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
+    <skip />
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Ne bodo vas motili zvoki ali vibriranje, razen v primeru alarmov, opomnikov, dogodkov in klicateljev, ki jih določite. Še vedno pa boste slišali vse, kar se boste odločili predvajati, vključno z glasbo, videoposnetki in igrami."</string>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Ne bodo vas motili zvoki ali vibriranje, razen v primeru alarmov. Še vedno pa boste slišali vse, kar se boste odločili predvajati, vključno z glasbo, videoposnetki in igrami."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Prilagodi"</string>
@@ -464,6 +478,8 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Počasno polnjenje • Napolnjeno čez <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Polnjenje • Napolnjeno čez <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Pripomočki na zaklenjenem zaslonu"</string>
+    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
+    <skip />
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Povlecite levo, da zaženete vadnico za skupnost"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Prilagodi"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Opusti"</string>
@@ -492,6 +508,7 @@
     <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"postavitev izbranega pripomočka"</string>
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"Pripomočki na zaklenjenem zaslonu"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"Pripomočki na zaklenjenem zaslonu so vidni vsem, tudi če je tablica zaklenjena."</string>
+    <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"preklic izbire pripomočka"</string>
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Pripomočki na zaklenjenem zaslonu"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Če želite aplikacijo odpreti s pripomočkom, morate potrditi, da ste to vi. Upoštevajte tudi, da si jih lahko ogledajo vsi, tudi ko je tablični računalnik zaklenjen. Nekateri pripomočki morda niso predvideni za uporabo na zaklenjenem zaslonu, zato jih tukaj morda ni varno dodati."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Razumem"</string>
@@ -548,8 +565,8 @@
     <string name="media_projection_action_text" msgid="3634906766918186440">"Začni zdaj"</string>
     <string name="empty_shade_text" msgid="8935967157319717412">"Ni obvestil"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"Ni novih obvestil"</string>
-    <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"Prilagodljiva obvestila so vklopljena"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"Naprava za do dve minuti zmanjša glasnost in število pojavnih elementov na zaslonu, ko v kratkem času prejmete veliko obvestil."</string>
+    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Utišanje obvestil je vklopljeno"</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Ko prejmete preveč obvestil naenkrat, se glasnost naprave in opozoril samodejno zmanjša za največ 2 minuti."</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Izklopi"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Odklenite za ogled starejših obvestil"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"To napravo upravlja tvoj starš"</string>
@@ -716,8 +733,7 @@
     <string name="notification_alert_title" msgid="3656229781017543655">"Privzeto"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Samodejno"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"Brez zvočnega opozarjanja ali vibriranja."</string>
-    <!-- no translation found for notification_conversation_summary_low (3855696451919728790) -->
-    <skip />
+    <string name="notification_conversation_summary_low" msgid="3855696451919728790">"Brez zvoka ali vibriranja, vendar kljub temu prikazano v razdelku s pogovorom"</string>
     <string name="notification_channel_summary_default" msgid="777294388712200605">"Zvonjenje ali vibriranje je omogočeno na podlagi nastavitev naprave."</string>
     <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Zvonjenje ali vibriranje je omogočeno na podlagi nastavitev naprave. Pogovori v aplikaciji <xliff:g id="APP_NAME">%1$s</xliff:g> so privzeto prikazani v oblačkih."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Naj sistem določi, ali ob prejemu tega obvestila naprava predvaja zvok ali zavibrira"</string>
@@ -1216,7 +1232,6 @@
     <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>
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Prenos podatkov v mobilnem omrežju"</string>
-    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</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>
     <string name="mobile_data_poor_connection" msgid="819617772268371434">"Slaba povezava"</string>
@@ -1370,7 +1385,10 @@
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Končano"</string>
     <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Odlično!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Nazaj"</string>
-    <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"Za pomik nazaj povlecite levo ali desno s tremi prsti kjer koli na sledilni ploščici."</string>
+    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
+    <skip />
+    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
+    <skip />
     <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Sledilna ploščica s tremi prsti, ki se premikajo desno in levo"</string>
     <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Zaslon naprave z animacijo, ki prikazuje potezo za pomik nazaj"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Osvetlitev tipkovnice"</string>
@@ -1378,4 +1396,16 @@
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Kontrolniki za dom"</string>
     <string name="home_controls_dream_description" msgid="4644150952104035789">"Hiter dostop do kontrolnikov za dom na ohranjevalniku zaslona"</string>
     <string name="volume_undo_action" msgid="5815519725211877114">"Razveljavi"</string>
+    <string name="back_edu_toast_content" msgid="4530314597378982956">"Za pomik nazaj povlecite s tremi prsti levo ali desno po sledilni ploščici"</string>
+    <string name="home_edu_toast_content" msgid="3381071147871955415">"Za pomik na začetni zaslon povlecite s tremi prsti navzgor po sledilni ploščici"</string>
+    <string name="overview_edu_toast_content" msgid="5797030644017804518">"Za ogled nedavnih aplikacij povlecite s tremi prsti navzgor po sledilni ploščici in pridržite"</string>
+    <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Za ogled vseh aplikacij pritisnite tipko za dejanja na tipkovnici"</string>
+    <string name="back_edu_notification_title" msgid="5624780717751357278">"Uporaba sledilne ploščice za pomik nazaj"</string>
+    <string name="back_edu_notification_content" msgid="2497557451540954068">"S tremi prsti povlecite levo ali desno. Dotaknite se, če želite spoznati več potez."</string>
+    <string name="home_edu_notification_title" msgid="6097902076909654045">"Uporaba sledilne ploščice za pomik na začetni zaslon"</string>
+    <string name="home_edu_notification_content" msgid="6631697734535766588">"S tremi prsti povlecite navzgor. Dotaknite se, če želite spoznati več potez."</string>
+    <string name="overview_edu_notification_title" msgid="1265824157319562406">"Uporaba sledilne ploščice za ogled nedavnih aplikacij"</string>
+    <string name="overview_edu_notification_content" msgid="3578204677648432500">"S tremi prsti povlecite navzgor in pridržite. Dotaknite se, če želite spoznati več potez."</string>
+    <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Uporaba tipkovnice za prikaz vseh aplikacij"</string>
+    <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Kadar koli pritisnite tipko za dejanja. Dotaknite se, če želite spoznati več potez."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-sl/tiles_states_strings.xml b/packages/SystemUI/res/values-sl/tiles_states_strings.xml
index e57b87b..5f60ffd 100644
--- a/packages/SystemUI/res/values-sl/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-sl/tiles_states_strings.xml
@@ -56,11 +56,6 @@
     <item msgid="5376619709702103243">"Izklopljeno"</item>
     <item msgid="4875147066469902392">"Vklopljeno"</item>
   </string-array>
-  <string-array name="tile_states_modes">
-    <item msgid="7764936419245199023">"Ni na voljo"</item>
-    <item msgid="2004750556637773692">"Izklopljeno"</item>
-    <item msgid="8968530753931637871">"Vklopljeno"</item>
-  </string-array>
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Ni na voljo"</item>
     <item msgid="5044688398303285224">"Izklopljeno"</item>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index 926a73f..b26c7d8 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -126,38 +126,25 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"Trokit për të parë"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"Gabim gjatë ruajtjes së regjistrimit të ekranit"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Gabim gjatë nisjes së regjistrimit të ekranit"</string>
-    <!-- no translation found for screenrecord_stop_dialog_title (8716193661764511095) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message (6262768207331626817) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5995770227684523244) -->
-    <skip />
+    <string name="screenrecord_stop_dialog_title" msgid="8716193661764511095">"Të ndalohet regjistrimi?"</string>
+    <string name="screenrecord_stop_dialog_message" msgid="6262768207331626817">"Po regjistron aktualisht të gjithë ekranin"</string>
+    <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Po regjistron aktualisht \"<xliff:g id="APP_NAME">%1$s</xliff:g>\""</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Ndalo regjistrimin"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Ekrani po ndahet"</string>
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Të ndalohet ndarja e ekranit?"</string>
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen_with_host_app (522823522115375414) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen (5090115386271179270) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_specific (5923772039347985172) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_generic (6681016774654578261) -->
-    <skip />
+    <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Po ndan aktualisht të gjithë ekranin me \"<xliff:g id="HOST_APP_NAME">%1$s</xliff:g>\""</string>
+    <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Po ndan aktualisht të gjithë ekranin me një aplikacion"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Po ndan aktualisht \"<xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>\""</string>
+    <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Po ndan aktualisht një aplikacion"</string>
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Ndalo ndarjen"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Po transmeton ekranin"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Të ndalohet transmetimi?"</string>
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen_with_device (1474703115926205251) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen (8419219169553867625) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app_with_device (2715934698604085519) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (8616103075630934513) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic_with_device (9213582497852420203) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic (4100272100480415076) -->
-    <skip />
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen_with_device" msgid="1474703115926205251">"Po transmeton aktualisht të gjithë ekranin te \"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>\""</string>
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen" msgid="8419219169553867625">"Po transmeton aktualisht të gjithë ekranin te një pajisje në afërsi"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app_with_device" msgid="2715934698604085519">"Po transmeton aktualisht \"<xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>\" te \"<xliff:g id="DEVICE_NAME">%2$s</xliff:g>\""</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="8616103075630934513">"Po transmeton aktualisht \"<xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>\" te një pajisje në afërsi"</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic_with_device" msgid="9213582497852420203">"Po transmeton aktualisht te \"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>\""</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic" msgid="4100272100480415076">"Po transmeton aktualisht te një pajisje në afërsi"</string>
     <string name="cast_to_other_device_stop_dialog_button" msgid="6420183747435521834">"Ndalo transmetimin"</string>
     <string name="close_dialog_button" msgid="4749497706540104133">"Mbyll"</string>
     <string name="issuerecord_title" msgid="286627115110121849">"Regjistruesi i problemeve"</string>
@@ -303,8 +290,7 @@
     <string name="start_dreams" msgid="9131802557946276718">"Mbrojtësi i ekranit"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Eternet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"Mos shqetëso"</string>
-    <!-- no translation found for quick_settings_modes_label (5407025818652750501) -->
-    <skip />
+    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Modalitetet e përparësisë"</string>
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth-i"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Nuk ofrohet për përdorim asnjë pajisje e çiftuar"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Trokit për të lidhur ose shkëputur një pajisje"</string>
@@ -402,6 +388,9 @@
     <string name="performance" msgid="6552785217174378320">"Performanca"</string>
     <string name="user_interface" msgid="3712869377953950887">"Ndërfaqja e përdoruesit"</string>
     <string name="thermal" msgid="6758074791325414831">"Termike"</string>
+    <string name="custom" msgid="3337456985275158299">"I personalizuar"</string>
+    <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Cilësimet e gjurmimit të personalizuar"</string>
+    <string name="restore_default" msgid="5259420807486239755">"Restauro parazgjedhjen"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Modaliteti i përdorimit me një dorë"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Pajisje ndihmëse për dëgjimin"</string>
     <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktive"</string>
@@ -440,6 +429,17 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Hap \"Cilësimet\""</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Pajisje tjetër"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Kalo te përmbledhja"</string>
+    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Modalitetet e përparësisë"</string>
+    <string name="zen_modes_dialog_done" msgid="6654130880256438950">"U krye"</string>
+    <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Cilësimet"</string>
+    <string name="zen_mode_on" msgid="9085304934016242591">"Aktiv"</string>
+    <string name="zen_mode_off" msgid="1736604456618147306">"Joaktiv"</string>
+    <!-- no translation found for zen_mode_set_up (7457957033034460064) -->
+    <skip />
+    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
+    <skip />
+    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
+    <skip />
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Nuk do të shqetësohesh nga tingujt dhe dridhjet, përveç alarmeve, alarmeve rikujtuese, ngjarjeve dhe telefonuesve që specifikon. Do të vazhdosh të dëgjosh çdo gjë që zgjedh të luash duke përfshirë muzikën, videot dhe lojërat."</string>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Nuk do të shqetësohesh nga tingujt dhe dridhjet, përveç alarmeve. Do të vazhdosh të dëgjosh çdo gjë që zgjedh të luash duke përfshirë muzikën, videot dhe lojërat."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Personalizo"</string>
@@ -478,6 +478,8 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Po karikohet ngadalë • Plot për <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Po karikohet • Plot për <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Miniaplikacionet në ekranin e kyçjes"</string>
+    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
+    <skip />
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Rrëshqit shpejt majtas për të filluar udhëzuesin e përbashkët"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Personalizo"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Hiq"</string>
@@ -504,10 +506,9 @@
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"zgjidh miniaplikacionin"</string>
     <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"hiq miniaplikacionin"</string>
     <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"vendos miniaplikacionin e zgjedhur"</string>
-    <!-- no translation found for communal_widget_picker_title (1953369090475731663) -->
-    <skip />
-    <!-- no translation found for communal_widget_picker_description (490515450110487871) -->
-    <skip />
+    <string name="communal_widget_picker_title" msgid="1953369090475731663">"Kyç miniaplikacionet e ekranit"</string>
+    <string name="communal_widget_picker_description" msgid="490515450110487871">"Çdo person mund të shikojë miniaplikacionet në ekranin tënd të kyçjes, edhe nëse tableti është i kyçur."</string>
+    <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"anulo zgjedhjen e miniaplikacionit"</string>
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Kyç miniaplikacionet e ekranit"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Për të hapur një aplikacion duke përdorur një miniaplikacion, do të duhet të verifikosh që je ti. Ki parasysh gjithashtu që çdo person mund t\'i shikojë, edhe kur tableti yt është i kyçur. Disa miniaplikacione mund të mos jenë planifikuar për ekranin tënd të kyçjes dhe mund të mos jetë e sigurt t\'i shtosh këtu."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"E kuptova"</string>
@@ -564,8 +565,8 @@
     <string name="media_projection_action_text" msgid="3634906766918186440">"Fillo tani"</string>
     <string name="empty_shade_text" msgid="8935967157319717412">"Asnjë njoftim"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"Nuk ka njoftime të reja"</string>
-    <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"Njoftimet me përshtatje janë aktive"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"Pajisja jote tani ul volumin dhe zvogëlon numrin e dritareve kërcyese në ekran për deri në dy minuta kur ti merr shumë njoftime në një periudhë të shkurtër kohe."</string>
+    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Reduktimi i njoftimeve është aktiv"</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Volumi i pajisjes dhe sinjalizimet zvogëlohen automatikisht për deri në 2 minuta kur merr shumë njoftime në të njëjtën kohë."</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Çaktivizo"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Shkyç për të parë njoftimet e vjetra"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Kjo pajisje menaxhohet nga prindi yt"</string>
@@ -732,8 +733,7 @@
     <string name="notification_alert_title" msgid="3656229781017543655">"E parazgjedhur"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Automatike"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"Asnjë tingull ose dridhje"</string>
-    <!-- no translation found for notification_conversation_summary_low (3855696451919728790) -->
-    <skip />
+    <string name="notification_conversation_summary_low" msgid="3855696451919728790">"Nuk ka tinguj apo dridhje, por shfaqet përsëri në seksionin e bisedave"</string>
     <string name="notification_channel_summary_default" msgid="777294388712200605">"Mund të bjerë zilja ose të dridhet në bazë të cilësimeve të pajisjes"</string>
     <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Mund të bjerë zilja ose të dridhet në bazë të cilësimeve të pajisjes. Bisedat nga flluska e <xliff:g id="APP_NAME">%1$s</xliff:g> si parazgjedhje."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Kërkoji sistemit të përcaktojë nëse ky njoftim duhet të lëshojë tingull apo dridhje"</string>
@@ -790,8 +790,7 @@
     <string name="keyboard_key_page_up" msgid="173914303254199845">"Faqja lart"</string>
     <string name="keyboard_key_page_down" msgid="9035902490071829731">"Faqja poshtë"</string>
     <string name="keyboard_key_forward_del" msgid="5325501825762733459">"Fshi"</string>
-    <!-- no translation found for keyboard_key_esc (6230365950511411322) -->
-    <skip />
+    <string name="keyboard_key_esc" msgid="6230365950511411322">"Esc"</string>
     <string name="keyboard_key_move_home" msgid="3496502501803911971">"Kreu"</string>
     <string name="keyboard_key_move_end" msgid="99190401463834854">"Fundi"</string>
     <string name="keyboard_key_insert" msgid="4621692715704410493">"Fut"</string>
@@ -1233,7 +1232,6 @@
     <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>
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Të dhënat celulare"</string>
-    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</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>
     <string name="mobile_data_poor_connection" msgid="819617772268371434">"Lidhje e dobët"</string>
@@ -1374,8 +1372,7 @@
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Ekrani i ndarë"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Hyrja"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Shkurtoret e aplikacionit"</string>
-    <!-- no translation found for shortcut_helper_category_current_app_shortcuts (4017840565974573628) -->
-    <skip />
+    <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Aplikacioni aktual"</string>
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Qasshmëria"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Shkurtoret e tastierës"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Kërko për shkurtoret"</string>
@@ -1388,13 +1385,27 @@
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"U krye"</string>
     <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Punë e shkëlqyer!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Kthehu prapa"</string>
-    <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"Për t\'u kthyer prapa, rrëshqit shpejt majtas ose djathtas duke përdorur tre gishta kudo në bllokun me prekje."</string>
+    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
+    <skip />
+    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
+    <skip />
     <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Blloku me prekje që tregon tre gishta që lëvizin djathtas dhe majtas"</string>
     <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Ekrani i pajisjes që tregon një animacion për gjestin e kthimit prapa"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Drita e sfondit e tastierës"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Niveli: %1$d nga %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Kontrollet e shtëpisë"</string>
     <string name="home_controls_dream_description" msgid="4644150952104035789">"Qasu te kontrollet e shtëpisë si mbrojtës ekrani"</string>
-    <!-- no translation found for volume_undo_action (5815519725211877114) -->
-    <skip />
+    <string name="volume_undo_action" msgid="5815519725211877114">"Zhbëj"</string>
+    <string name="back_edu_toast_content" msgid="4530314597378982956">"Për t\'u kthyer prapa, rrëshqit shpejt majtas ose djathtas me tre gishta në bllokun me prekje"</string>
+    <string name="home_edu_toast_content" msgid="3381071147871955415">"Për të shkuar tek ekrani bazë, rrëshqit shpejt lart me tre gishta në bllokun me prekje"</string>
+    <string name="overview_edu_toast_content" msgid="5797030644017804518">"Për aplikacionet e fundit, rrëshqit shpejt lart dhe mbaj shtypur me tre gishta në bllokun me prekje"</string>
+    <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Për të shikuar të gjitha aplikacionet, shtyp tastin e veprimit në tastierë"</string>
+    <string name="back_edu_notification_title" msgid="5624780717751357278">"Përdor bllokun me prekje për t\'u kthyer prapa"</string>
+    <string name="back_edu_notification_content" msgid="2497557451540954068">"Rrëshqit shpejt majtas ose djathtas duke përdorur tre gishta. Trokit për të mësuar më shumë gjeste."</string>
+    <string name="home_edu_notification_title" msgid="6097902076909654045">"Përdor bllokun me prekje për të shkuar tek ekrani bazë"</string>
+    <string name="home_edu_notification_content" msgid="6631697734535766588">"Rrëshqit shpejt lart duke përdorur tre gishta. Trokit për të mësuar më shumë gjeste."</string>
+    <string name="overview_edu_notification_title" msgid="1265824157319562406">"Përdor bllokun me prekje për të shikuar aplikacionet e fundit"</string>
+    <string name="overview_edu_notification_content" msgid="3578204677648432500">"Rrëshqit shpejt lart dhe mbaj shtypur me tre gishta. Trokit për të mësuar më shumë gjeste."</string>
+    <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Përdor tastierën për të shikuar të gjitha aplikacionet"</string>
+    <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Shtyp tastin e veprimit në çdo kohë. Trokit për të mësuar më shumë gjeste."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-sq/tiles_states_strings.xml b/packages/SystemUI/res/values-sq/tiles_states_strings.xml
index 4f42c30..9b5032e 100644
--- a/packages/SystemUI/res/values-sq/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-sq/tiles_states_strings.xml
@@ -56,9 +56,6 @@
     <item msgid="5376619709702103243">"Joaktiv"</item>
     <item msgid="4875147066469902392">"Aktiv"</item>
   </string-array>
-    <!-- no translation found for tile_states_modes:0 (7764936419245199023) -->
-    <!-- no translation found for tile_states_modes:1 (2004750556637773692) -->
-    <!-- no translation found for tile_states_modes:2 (8968530753931637871) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Nuk ofrohet"</item>
     <item msgid="5044688398303285224">"Joaktiv"</item>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index e5b9445..7acfbf5 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -141,7 +141,7 @@
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Желите да зауставите пребацивање?"</string>
     <string name="cast_to_other_device_stop_dialog_message_entire_screen_with_device" msgid="1474703115926205251">"Тренутно пребацујете цео екран на: <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="cast_to_other_device_stop_dialog_message_entire_screen" msgid="8419219169553867625">"Тренутно пребацујете цео екран на уређај у близини"</string>
-    <string name="cast_to_other_device_stop_dialog_message_specific_app_with_device" msgid="2715934698604085519">"<xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> се тренутно пребацује на: <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app_with_device" msgid="2715934698604085519">"<xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> тренутно пребацујете на: <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
     <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="8616103075630934513">"<xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> се тренутно пребацује на уређај у близини"</string>
     <string name="cast_to_other_device_stop_dialog_message_generic_with_device" msgid="9213582497852420203">"Тренутно пребацујете на: <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="cast_to_other_device_stop_dialog_message_generic" msgid="4100272100480415076">"Тренутно пребацујете на уређај у близини"</string>
@@ -290,8 +290,7 @@
     <string name="start_dreams" msgid="9131802557946276718">"Чувар екрана"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Етернет"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"Не узнемиравај"</string>
-    <!-- no translation found for quick_settings_modes_label (5407025818652750501) -->
-    <skip />
+    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Приоритетни режими"</string>
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Није доступан ниједан упарени уређај"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Додирните да бисте повезали уређај или прекинули везу"</string>
@@ -389,6 +388,9 @@
     <string name="performance" msgid="6552785217174378320">"Перформансе"</string>
     <string name="user_interface" msgid="3712869377953950887">"Кориснички интерфејс"</string>
     <string name="thermal" msgid="6758074791325414831">"Термална камера"</string>
+    <string name="custom" msgid="3337456985275158299">"Прилагођено"</string>
+    <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Подешавања прилагођеног праћења"</string>
+    <string name="restore_default" msgid="5259420807486239755">"Врати подразумевано"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Режим једном руком"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Слушни апарати"</string>
     <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Активно"</string>
@@ -427,6 +429,17 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Отвори Подешавања"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Други уређај"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Укључи/искључи преглед"</string>
+    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Приоритетни режими"</string>
+    <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_off" msgid="1736604456618147306">"Искључено"</string>
+    <!-- no translation found for zen_mode_set_up (7457957033034460064) -->
+    <skip />
+    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
+    <skip />
+    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
+    <skip />
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Неће вас узнемиравати звукови и вибрације осим за аларме, подсетнике, догађаје и позиваоце које наведете. И даље ћете чути све што одаберете да пустите, укључујући музику, видео снимке и игре."</string>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Неће вас узнемиравати звукови и вибрације осим за аларме. И даље ћете чути све што одаберете да пустите, укључујући музику, видео снимке и игре."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Прилагоди"</string>
@@ -465,6 +478,8 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Споро се пуни • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> до краја пуњења"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Пуни се • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> до краја пуњења"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Виџети на закључаном екрану"</string>
+    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
+    <skip />
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Превуците улево да бисте започели заједнички водич"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Прилагодите"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Одбаци"</string>
@@ -492,7 +507,8 @@
     <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"уклоните виџет"</string>
     <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"поставите изабрани виџет"</string>
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"Виџети за закључани екран"</string>
-    <string name="communal_widget_picker_description" msgid="490515450110487871">"Сви могу да виде веџете на закључаном екрану, чак и када је таблет закључан."</string>
+    <string name="communal_widget_picker_description" msgid="490515450110487871">"Сви могу да виде виџете на закључаном екрану, чак и када је таблет закључан."</string>
+    <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"поништи избор виџета"</string>
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Виџети за закључани екран"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Да бисте отворили апликацију која користи виџет, треба да потврдите да сте то ви. Имајте у виду да свако може да га види, чак и када је таблет закључан. Неки виџети можда нису намењени за закључани екран и можда није безбедно да их тамо додате."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Важи"</string>
@@ -549,8 +565,8 @@
     <string name="media_projection_action_text" msgid="3634906766918186440">"Започни"</string>
     <string name="empty_shade_text" msgid="8935967157319717412">"Нема обавештења"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"Нема нових обавештења"</string>
-    <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"Прилаг. обавештења су укључена"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"Уређај сада смањује звук и број искачућих прозора на екрану до 2 минута кад примите много обавештења у кратком року."</string>
+    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Утишавање обавештења је укључено"</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Звук и број упозорења на уређају се аутоматски смањују на 2 минута када добијете превише обавештења."</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Искључи"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Откључајте за старија обавештења"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Овим уређајем управља родитељ"</string>
@@ -717,8 +733,7 @@
     <string name="notification_alert_title" msgid="3656229781017543655">"Подразумевано"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Аутоматска"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"Без звука и вибрирања"</string>
-    <!-- no translation found for notification_conversation_summary_low (3855696451919728790) -->
-    <skip />
+    <string name="notification_conversation_summary_low" msgid="3855696451919728790">"Без звука и вибрирања, али се још увек приказује у одељку за конверзације"</string>
     <string name="notification_channel_summary_default" msgid="777294388712200605">"Може да звони или вибрира у зависности од подешавања уређаја"</string>
     <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Може да звони или вибрира у зависности од подешавања уређаја. Конверзације из апликације <xliff:g id="APP_NAME">%1$s</xliff:g> подразумевано се приказују у облачићима."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Нека систем утврди да ли ово обавештење треба да емитује звук или да вибрира"</string>
@@ -1217,7 +1232,6 @@
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Потребна је потврда идентитета. Додирните сензор за отисак прста да бисте потврдили идентитет."</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Актуелни телефонски позив"</string>
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Мобилни подаци"</string>
-    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Повезано"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Привремено повезано"</string>
     <string name="mobile_data_poor_connection" msgid="819617772268371434">"Веза је лоша"</string>
@@ -1371,13 +1385,27 @@
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Готово"</string>
     <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Одлично!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Назад"</string>
-    <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"Да бисте се вратили, превуците улево или удесно са три прста било где на тачпеду."</string>
+    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
+    <skip />
+    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
+    <skip />
     <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Тачпед са приказом три прста који се померају удесно и улево"</string>
     <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Екран уређаја са приказом анимације покрета за назад"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Позадинско осветљење тастатуре"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"%1$d. ниво од %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Контроле за дом"</string>
     <string name="home_controls_dream_description" msgid="4644150952104035789">"Брз приступ контролама за дом као чувару екрана"</string>
-    <!-- no translation found for volume_undo_action (5815519725211877114) -->
-    <skip />
+    <string name="volume_undo_action" msgid="5815519725211877114">"Опозови"</string>
+    <string name="back_edu_toast_content" msgid="4530314597378982956">"Да бисте се вратили, превуците улево или удесно са три прста на тачпеду"</string>
+    <string name="home_edu_toast_content" msgid="3381071147871955415">"Да бисте отишли на почетни екран, превуците нагоре са три прста на тачпеду"</string>
+    <string name="overview_edu_toast_content" msgid="5797030644017804518">"Да бисте прегледали недавне апликације, превуците нагоре и задржите са три прста на тачпеду"</string>
+    <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Да бисте погледали све апликације, притисните тастер радњи на тастатури"</string>
+    <string name="back_edu_notification_title" msgid="5624780717751357278">"Користите тачпед да бисте се вратили"</string>
+    <string name="back_edu_notification_content" msgid="2497557451540954068">"Превуците улево или удесно са три прста. Додирните да бисте видели више покрета."</string>
+    <string name="home_edu_notification_title" msgid="6097902076909654045">"Користите тачпед да бисте отишли на почетни екран"</string>
+    <string name="home_edu_notification_content" msgid="6631697734535766588">"Превуците нагоре са три прста. Додирните да бисте видели више покрета."</string>
+    <string name="overview_edu_notification_title" msgid="1265824157319562406">"Користите тачпед да бисте прегледали недавне апликације"</string>
+    <string name="overview_edu_notification_content" msgid="3578204677648432500">"Превуците нагоре и задржите са три прста. Додирните да бисте видели више покрета."</string>
+    <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Користите тастатуру да бисте прегледали све апликације"</string>
+    <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Притисните тастер радњи у било ком тренутку. Додирните да бисте видели више покрета."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-sr/tiles_states_strings.xml b/packages/SystemUI/res/values-sr/tiles_states_strings.xml
index 904fb23..2acf1d2 100644
--- a/packages/SystemUI/res/values-sr/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-sr/tiles_states_strings.xml
@@ -56,9 +56,6 @@
     <item msgid="5376619709702103243">"Искључено"</item>
     <item msgid="4875147066469902392">"Укључено"</item>
   </string-array>
-    <!-- no translation found for tile_states_modes:0 (7764936419245199023) -->
-    <!-- no translation found for tile_states_modes:1 (2004750556637773692) -->
-    <!-- no translation found for tile_states_modes:2 (8968530753931637871) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Недоступно"</item>
     <item msgid="5044688398303285224">"Искључено"</item>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index 68063d6..b632dde 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -126,38 +126,25 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"Tryck för att visa"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"Det gick inte att spara skärminspelningen"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Det gick inte att starta skärminspelningen"</string>
-    <!-- no translation found for screenrecord_stop_dialog_title (8716193661764511095) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message (6262768207331626817) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5995770227684523244) -->
-    <skip />
+    <string name="screenrecord_stop_dialog_title" msgid="8716193661764511095">"Vill du stoppa inspelningen?"</string>
+    <string name="screenrecord_stop_dialog_message" msgid="6262768207331626817">"Du spelar för närvarande in hela din skärm"</string>
+    <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Du spelar för närvarande in <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Sluta spela in"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Skärmen delas"</string>
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Vill du sluta dela skärmen?"</string>
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen_with_host_app (522823522115375414) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen (5090115386271179270) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_specific (5923772039347985172) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_generic (6681016774654578261) -->
-    <skip />
+    <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Du delar för närvarande hela din skärm med <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
+    <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Du delar för närvarande hela din skärm med en app"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Du delar för närvarande <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Du delar för närvarande en app"</string>
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Sluta dela"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Skärmen castas"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Vill du sluta att casta?"</string>
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen_with_device (1474703115926205251) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen (8419219169553867625) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app_with_device (2715934698604085519) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (8616103075630934513) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic_with_device (9213582497852420203) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic (4100272100480415076) -->
-    <skip />
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen_with_device" msgid="1474703115926205251">"Du castar för närvarande hela din skärm till <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen" msgid="8419219169553867625">"Du castar för närvarande hela din skärm till en enhet i närheten"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app_with_device" msgid="2715934698604085519">"Du castar <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> till <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="8616103075630934513">"Du castar <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> till en enhet i närheten"</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic_with_device" msgid="9213582497852420203">"Du castar för närvarande till <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic" msgid="4100272100480415076">"Du castar för närvarande till en enhet i närheten"</string>
     <string name="cast_to_other_device_stop_dialog_button" msgid="6420183747435521834">"Sluta casta"</string>
     <string name="close_dialog_button" msgid="4749497706540104133">"Stäng"</string>
     <string name="issuerecord_title" msgid="286627115110121849">"Probleminspelare"</string>
@@ -303,8 +290,7 @@
     <string name="start_dreams" msgid="9131802557946276718">"Skärmsläckare"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"Stör ej"</string>
-    <!-- no translation found for quick_settings_modes_label (5407025818652750501) -->
-    <skip />
+    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Prioriterade lägen"</string>
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Det finns inga kopplade enheter tillgängliga"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Tryck för att ansluta eller koppla från en enhet"</string>
@@ -402,6 +388,9 @@
     <string name="performance" msgid="6552785217174378320">"Prestanda"</string>
     <string name="user_interface" msgid="3712869377953950887">"Användargränssnitt"</string>
     <string name="thermal" msgid="6758074791325414831">"Termisk"</string>
+    <string name="custom" msgid="3337456985275158299">"Anpassat"</string>
+    <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Anpassade spårningsinställningar"</string>
+    <string name="restore_default" msgid="5259420807486239755">"Återställ standardinställning"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Enhandsläge"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Hörhjälpmedel"</string>
     <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktiva"</string>
@@ -440,6 +429,17 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Öppna Inställningar"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Annan enhet"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Aktivera och inaktivera översikten"</string>
+    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Prioriterade lägen"</string>
+    <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Klar"</string>
+    <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Inställningar"</string>
+    <string name="zen_mode_on" msgid="9085304934016242591">"På"</string>
+    <string name="zen_mode_off" msgid="1736604456618147306">"Av"</string>
+    <!-- no translation found for zen_mode_set_up (7457957033034460064) -->
+    <skip />
+    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
+    <skip />
+    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
+    <skip />
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Du blir inte störd av ljud och vibrationer, förutom från alarm, påminnelser, händelser och specifika samtal. Ljudet är fortfarande på för sådant du väljer att spela upp, till exempel musik, videor och spel."</string>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Du blir inte störd av ljud och vibrationer, förutom från alarm. Ljudet är fortfarande på för sådant du väljer att spela upp, till exempel musik, videor och spel."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Anpassa"</string>
@@ -478,6 +478,8 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Laddas långsamt • Fulladdat om <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Laddas • Fulladdat om <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Widgetar på låsskärmen"</string>
+    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
+    <skip />
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Svep åt vänster för att börja med gruppguiden"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Anpassa"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Ignorera"</string>
@@ -504,10 +506,9 @@
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"välj widget"</string>
     <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"ta bort widget"</string>
     <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"placera vald widget"</string>
-    <!-- no translation found for communal_widget_picker_title (1953369090475731663) -->
-    <skip />
-    <!-- no translation found for communal_widget_picker_description (490515450110487871) -->
-    <skip />
+    <string name="communal_widget_picker_title" msgid="1953369090475731663">"Widgetar för låsskärm"</string>
+    <string name="communal_widget_picker_description" msgid="490515450110487871">"Vem som helst kan se widgetar på din låsskärm, även om surfplattan är låst."</string>
+    <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"avmarkera widget"</string>
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widgetar för låsskärm"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Du måste verifiera din identitet innan du öppnar en app med en widget. Tänk också på att alla kan se dem, även när surfplattan är låst. Vissa widgetar kanske inte är avsedda för låsskärmen och det kan vara osäkert att lägga till dem här."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"OK"</string>
@@ -564,8 +565,8 @@
     <string name="media_projection_action_text" msgid="3634906766918186440">"Starta nu"</string>
     <string name="empty_shade_text" msgid="8935967157319717412">"Inga aviseringar"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"Det finns inga nya aviseringar"</string>
-    <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"Anpassade aviseringar är på"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"Om du får många aviseringar tätt inpå varandra sänker nu enheten volymen och minskar antalet popuper i upp till två minuter."</string>
+    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Gradvis sänkning för aviseringar är på"</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Enheten sänker volymen och minimerar aviseringar i upp till två minuter när du får för många aviseringar samtidigt."</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Inaktivera"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Lås upp för att se äldre aviseringar"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Den här enheten hanteras av din förälder"</string>
@@ -732,8 +733,7 @@
     <string name="notification_alert_title" msgid="3656229781017543655">"Standard"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Automatiskt"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"Inga ljud eller vibrationer"</string>
-    <!-- no translation found for notification_conversation_summary_low (3855696451919728790) -->
-    <skip />
+    <string name="notification_conversation_summary_low" msgid="3855696451919728790">"Inga ljud eller vibrationer men visas fortfarande i avsnittet för konversationer"</string>
     <string name="notification_channel_summary_default" msgid="777294388712200605">"Kan ringa eller vibrera beroende på inställningarna på enheten"</string>
     <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Kan ringa eller vibrera beroende på inställningarna på enheten. Konversationer från <xliff:g id="APP_NAME">%1$s</xliff:g> visas i bubblor som standard."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Låt systemet avgöra om den här aviseringen ska låta eller vibrera"</string>
@@ -790,8 +790,7 @@
     <string name="keyboard_key_page_up" msgid="173914303254199845">"Sida upp"</string>
     <string name="keyboard_key_page_down" msgid="9035902490071829731">"Sida ned"</string>
     <string name="keyboard_key_forward_del" msgid="5325501825762733459">"Radera"</string>
-    <!-- no translation found for keyboard_key_esc (6230365950511411322) -->
-    <skip />
+    <string name="keyboard_key_esc" msgid="6230365950511411322">"Esc"</string>
     <string name="keyboard_key_move_home" msgid="3496502501803911971">"Start"</string>
     <string name="keyboard_key_move_end" msgid="99190401463834854">"Slut"</string>
     <string name="keyboard_key_insert" msgid="4621692715704410493">"Infoga"</string>
@@ -1233,7 +1232,6 @@
     <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>
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobildata"</string>
-    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Ansluten"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Tillfälligt ansluten"</string>
     <string name="mobile_data_poor_connection" msgid="819617772268371434">"Dålig anslutning"</string>
@@ -1374,8 +1372,7 @@
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Delad skärm"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Ingång"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Genvägar till appar"</string>
-    <!-- no translation found for shortcut_helper_category_current_app_shortcuts (4017840565974573628) -->
-    <skip />
+    <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Aktuell app"</string>
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Tillgänglighet"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Kortkommandon"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Sökgenvägar"</string>
@@ -1388,13 +1385,27 @@
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Klar"</string>
     <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Bra jobbat!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Tillbaka"</string>
-    <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"Gå tillbaka genom att svepa åt vänster eller höger med tre fingrar var som helst på styrplattan."</string>
+    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
+    <skip />
+    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
+    <skip />
     <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Tre fingrar rör sig åt höger och vänster på en styrplatta"</string>
     <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"En enhetsskärm visar en animation för rörelsen Tillbaka"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Bakgrundsbelysning för tangentbord"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Nivå %1$d av %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Hemstyrning"</string>
     <string name="home_controls_dream_description" msgid="4644150952104035789">"Kom snabbt åt hemstyrningen via skärmsläckaren"</string>
-    <!-- no translation found for volume_undo_action (5815519725211877114) -->
-    <skip />
+    <string name="volume_undo_action" msgid="5815519725211877114">"Ångra"</string>
+    <string name="back_edu_toast_content" msgid="4530314597378982956">"Svep åt höger eller vänster på styrplattan med tre fingrar för att gå tillbaka"</string>
+    <string name="home_edu_toast_content" msgid="3381071147871955415">"Svep uppåt på styrplattan med tre fingrar för att gå till startskärmen"</string>
+    <string name="overview_edu_toast_content" msgid="5797030644017804518">"Svep uppåt på styrplattan med tre fingrar och håll kvar för att se nyligen använda appar"</string>
+    <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Tryck på åtgärdstangenten på tangentbordet för att se alla appar"</string>
+    <string name="back_edu_notification_title" msgid="5624780717751357278">"Använd styrplattan för att gå tillbaka"</string>
+    <string name="back_edu_notification_content" msgid="2497557451540954068">"Svep åt vänster eller höger med tre fingrar. Tryck för att lära dig fler rörelser."</string>
+    <string name="home_edu_notification_title" msgid="6097902076909654045">"Använd styrplattan för att gå till startskärmen"</string>
+    <string name="home_edu_notification_content" msgid="6631697734535766588">"Svep uppåt med tre fingrar. Tryck för att lära dig fler rörelser."</string>
+    <string name="overview_edu_notification_title" msgid="1265824157319562406">"Använd styrplattan för att se nyligen använda appar"</string>
+    <string name="overview_edu_notification_content" msgid="3578204677648432500">"Svep uppåt med tre fingrar och håll kvar. Tryck för att lära dig fler rörelser."</string>
+    <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Använd tangentbordet för att se alla appar"</string>
+    <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Tryck på åtgärdstangenten när som helst. Tryck för att lära dig fler rörelser."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-sv/tiles_states_strings.xml b/packages/SystemUI/res/values-sv/tiles_states_strings.xml
index bd62fc1..cf49f8d 100644
--- a/packages/SystemUI/res/values-sv/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-sv/tiles_states_strings.xml
@@ -56,9 +56,6 @@
     <item msgid="5376619709702103243">"Av"</item>
     <item msgid="4875147066469902392">"På"</item>
   </string-array>
-    <!-- no translation found for tile_states_modes:0 (7764936419245199023) -->
-    <!-- no translation found for tile_states_modes:1 (2004750556637773692) -->
-    <!-- no translation found for tile_states_modes:2 (8968530753931637871) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Inte tillgängligt"</item>
     <item msgid="5044688398303285224">"Av"</item>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index 986e00e..d3e4e5d 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -126,38 +126,25 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"Gusa ili uangalie"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"Hitilafu imetokea wakati wa kuhifadhi rekodi ya skrini"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Hitilafu imetokea wakati wa kuanza kurekodi skrini"</string>
-    <!-- no translation found for screenrecord_stop_dialog_title (8716193661764511095) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message (6262768207331626817) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5995770227684523244) -->
-    <skip />
+    <string name="screenrecord_stop_dialog_title" msgid="8716193661764511095">"Ungependa kuacha kurekodi?"</string>
+    <string name="screenrecord_stop_dialog_message" msgid="6262768207331626817">"Kwa sasa unarekodi skrini yako nzima"</string>
+    <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Kwa sasa unarekodi <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Acha kurekodi"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Inaruhusu ufikiaji kwenye skrini"</string>
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Ungependa kuacha kuonyesha skrini?"</string>
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen_with_host_app (522823522115375414) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen (5090115386271179270) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_specific (5923772039347985172) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_generic (6681016774654578261) -->
-    <skip />
+    <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Kwa sasa unatuma maudhui yaliyo katika skrini yako nzima kwenye <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
+    <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Kwa sasa unatuma maudhui yaliyo katika skrini yako nzima kwenye programu"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Kwa sasa unatumia <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> pamoja na wengine"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Kwa sasa unatumia programu pamoja na wengine"</string>
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Acha kuonyesha skrini"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Inatuma skrini"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Ungependa kuacha kutuma?"</string>
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen_with_device (1474703115926205251) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen (8419219169553867625) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app_with_device (2715934698604085519) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (8616103075630934513) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic_with_device (9213582497852420203) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic (4100272100480415076) -->
-    <skip />
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen_with_device" msgid="1474703115926205251">"Kwa sasa unatuma maudhui yaliyo katika skrini yako nzima kwenye <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen" msgid="8419219169553867625">"Kwa sasa unatuma maudhui yaliyo katika skrini yako nzima kwenye kifaa kilicho karibu"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app_with_device" msgid="2715934698604085519">"Kwa sasa unatuma maudhui yaliyo katika <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> kwenye <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="8616103075630934513">"Kwa sasa unatuma maudhui yaliyo katika <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> kwenye kifaa kilicho karibu"</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic_with_device" msgid="9213582497852420203">"Kwa sasa unatuma maudhui kwenye <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic" msgid="4100272100480415076">"Kwa sasa unatuma maudhui kwenye kifaa kilicho karibu"</string>
     <string name="cast_to_other_device_stop_dialog_button" msgid="6420183747435521834">"Acha kutuma maudhui"</string>
     <string name="close_dialog_button" msgid="4749497706540104133">"Funga"</string>
     <string name="issuerecord_title" msgid="286627115110121849">"Kifaa cha Kurekodi Hitilafu"</string>
@@ -303,8 +290,7 @@
     <string name="start_dreams" msgid="9131802557946276718">"Taswira ya skrini"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"Usinisumbue"</string>
-    <!-- no translation found for quick_settings_modes_label (5407025818652750501) -->
-    <skip />
+    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Hali za kipaumbele"</string>
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Hakuna vifaa vilivyooanishwa vinavyopatikana"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Gusa ili uunganishe au utenganishe kifaa"</string>
@@ -402,6 +388,9 @@
     <string name="performance" msgid="6552785217174378320">"Utendaji"</string>
     <string name="user_interface" msgid="3712869377953950887">"Kiolesura"</string>
     <string name="thermal" msgid="6758074791325414831">"Joto"</string>
+    <string name="custom" msgid="3337456985275158299">"Maalum"</string>
+    <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Mipangilio Maalum ya Historia ya Shughuli"</string>
+    <string name="restore_default" msgid="5259420807486239755">"Rejesha Mipangilio Chaguomsingi"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Hali ya kutumia kwa mkono mmoja"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Vifaa vya kusikilizia"</string>
     <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Vimeunganishwa"</string>
@@ -440,6 +429,17 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Fungua Mipangilio"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Kifaa kingine"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Washa Muhtasari"</string>
+    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Hali za kipaumbele"</string>
+    <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Nimemaliza"</string>
+    <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Mipangilio"</string>
+    <string name="zen_mode_on" msgid="9085304934016242591">"Imewashwa"</string>
+    <string name="zen_mode_off" msgid="1736604456618147306">"Imezimwa"</string>
+    <!-- no translation found for zen_mode_set_up (7457957033034460064) -->
+    <skip />
+    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
+    <skip />
+    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
+    <skip />
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Hutasumbuliwa na sauti na mitetemo, isipokuwa kengele, vikumbusho, matukio na simu zinazopigwa na watu uliobainisha. Bado utasikia chochote utakachochagua kucheza, ikiwa ni pamoja na muziki, video na michezo."</string>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Hutasumbuliwa na sauti na mitetemo, isipokuwa kengele. Bado utasikia chochote utakachochagua kucheza, ikiwa ni pamoja na muziki, video na michezo."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Badilisha upendavyo"</string>
@@ -478,6 +478,8 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Inachaji polepole • Itajaa baada ya <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Inachaji • Itajaa baada ya <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Wijeti kwenye skrini iliyofungwa"</string>
+    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
+    <skip />
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Telezesha kidole kushoto ili uanze mafunzo ya pamoja"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Weka mapendeleo"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Funga"</string>
@@ -504,10 +506,9 @@
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"chagua wijeti"</string>
     <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"ondoa wijeti"</string>
     <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"weka wijeti uliyochagua"</string>
-    <!-- no translation found for communal_widget_picker_title (1953369090475731663) -->
-    <skip />
-    <!-- no translation found for communal_widget_picker_description (490515450110487871) -->
-    <skip />
+    <string name="communal_widget_picker_title" msgid="1953369090475731663">"Wijeti zinazoonekana kwenye skrini iliyofungwa"</string>
+    <string name="communal_widget_picker_description" msgid="490515450110487871">"Yeyote anaweza kuona wijeti kwenye skrini yako iliyofungwa, hata ikiwa umefunga kishikwambi chako."</string>
+    <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"acha kuchagua wijeti"</string>
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Wijeti zinazoonekana kwenye skrini iliyofungwa"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Utahitaji kuthibitisha kuwa ni wewe ili ufungue programu ukitumia wijeti. Pia, kumbuka kuwa mtu yeyote anaweza kuziona, hata kishikwambi chako kikiwa kimefungwa. Huenda baadhi ya wijeti hazikukusudiwa kutumika kwenye skrini yako iliyofungwa na huenda si salama kuziweka hapa."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Nimeelewa"</string>
@@ -564,8 +565,8 @@
     <string name="media_projection_action_text" msgid="3634906766918186440">"Anza sasa"</string>
     <string name="empty_shade_text" msgid="8935967157319717412">"Hakuna arifa"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"Hakuna arifa mpya"</string>
-    <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"Umewasha arifa zinazojirekebisha"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"Sasa kifaa chako kinapunguza sauti na idadi ya madirisha ibukizi kwenye skrini kwa hadi dakika mbili ukipokea arifa nyingi kwa muda mfupi."</string>
+    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Umewasha mipangilio ya kutuliza arifa"</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Arifa na sauti hupunguzwa kiotomatiki kwenye kifaa chako kwa hadi dakika 2 unapopokea arifa nyingi kwa wakati mmoja."</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Zima"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Fungua ili uone arifa za zamani"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Kifaa hiki kinadhibitiwa na mzazi wako"</string>
@@ -732,8 +733,7 @@
     <string name="notification_alert_title" msgid="3656229781017543655">"Chaguomsingi"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Otomatiki"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"Hakuna sauti wala mtetemo"</string>
-    <!-- no translation found for notification_conversation_summary_low (3855696451919728790) -->
-    <skip />
+    <string name="notification_conversation_summary_low" msgid="3855696451919728790">"Hakutakuwa na sauti wala mtetemo lakini bado itaonekana katika sehemu ya mazungumzo"</string>
     <string name="notification_channel_summary_default" msgid="777294388712200605">"Huenda ikalia au kutetema kulingana na mipangilio ya kifaa"</string>
     <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Huenda ikalia au kutetema kulingana na mipangilio ya kifaa. Mazungumzo kutoka kiputo cha <xliff:g id="APP_NAME">%1$s</xliff:g> kwa chaguomsingi."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Ruhusu mfumo ubainishe iwapo arifa hii inapaswa kutoa sauti au mtetemo"</string>
@@ -790,8 +790,7 @@
     <string name="keyboard_key_page_up" msgid="173914303254199845">"Page Up"</string>
     <string name="keyboard_key_page_down" msgid="9035902490071829731">"Page Down"</string>
     <string name="keyboard_key_forward_del" msgid="5325501825762733459">"Futa"</string>
-    <!-- no translation found for keyboard_key_esc (6230365950511411322) -->
-    <skip />
+    <string name="keyboard_key_esc" msgid="6230365950511411322">"Esc"</string>
     <string name="keyboard_key_move_home" msgid="3496502501803911971">"Mwanzo"</string>
     <string name="keyboard_key_move_end" msgid="99190401463834854">"Mwisho"</string>
     <string name="keyboard_key_insert" msgid="4621692715704410493">"Ingiza"</string>
@@ -1233,7 +1232,6 @@
     <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>
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Data ya mtandao wa simu"</string>
-    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Imeunganishwa"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Imeunganishwa kwa muda"</string>
     <string name="mobile_data_poor_connection" msgid="819617772268371434">"Muunganisho duni"</string>
@@ -1374,8 +1372,7 @@
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Gawa skrini"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Kifaa cha kuingiza data"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Njia za mikato za programu"</string>
-    <!-- no translation found for shortcut_helper_category_current_app_shortcuts (4017840565974573628) -->
-    <skip />
+    <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Programu Inayotumika Sasa"</string>
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Ufikivu"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Mikato ya kibodi"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Njia mkato za kutafutia"</string>
@@ -1388,13 +1385,27 @@
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Nimemaliza"</string>
     <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Kazi nzuri!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Rudi nyuma"</string>
-    <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"Ili kurudi nyuma, telezesha vidole vitatu kushoto au kulia mahali popote kwenye padi ya kugusa."</string>
+    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
+    <skip />
+    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
+    <skip />
     <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Padi ya kugusa inayoonyesha vidole vitatu vikisonga kulia na kushoto"</string>
     <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Skrini ya kifaa inayoonyesha uhuishaji wa mguso wa nyuma"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Mwanga chini ya kibodi"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Kiwango cha %1$d kati ya %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Dhibiti Vifaa Nyumbani"</string>
     <string name="home_controls_dream_description" msgid="4644150952104035789">"Fikia haraka vidhibiti vya vifaa nyumbani vikiwa taswira ya skrini"</string>
-    <!-- no translation found for volume_undo_action (5815519725211877114) -->
-    <skip />
+    <string name="volume_undo_action" msgid="5815519725211877114">"Tendua"</string>
+    <string name="back_edu_toast_content" msgid="4530314597378982956">"Telezesha vidole vitatu kutoka kushoto au kulia kwenye padi ya kugusa ili urudi nyuma"</string>
+    <string name="home_edu_toast_content" msgid="3381071147871955415">"Telezesha vidole vitatu juu kwenye padi ya kugusa ili urudi kwenye skrini ya kwanza"</string>
+    <string name="overview_edu_toast_content" msgid="5797030644017804518">"Telezesha vidole vitatu juu na ushikilie kwenye padi ya kugusa ili uangalie programu za hivi majuzi"</string>
+    <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Bonyeza kitufe cha vitendo kwenye kibodi yako ili uangalie programu zako zote"</string>
+    <string name="back_edu_notification_title" msgid="5624780717751357278">"Kutumia padi yako ya kugusa ili kurudi nyuma"</string>
+    <string name="back_edu_notification_content" msgid="2497557451540954068">"Telezesha vidole vitatu kulia au kushoto. Gusa ili upate maelezo kuhusu miguso zaidi."</string>
+    <string name="home_edu_notification_title" msgid="6097902076909654045">"Kutumia padi yako ya kugusa ili kurudi kwenye skrini ya kwanza"</string>
+    <string name="home_edu_notification_content" msgid="6631697734535766588">"Telezesha vidole vitatu juu. Gusa ili upate maelezo kuhusu miguso zaidi."</string>
+    <string name="overview_edu_notification_title" msgid="1265824157319562406">"Kutumia padi yako ya kugusa ili kuangalia programu za hivi majuzi"</string>
+    <string name="overview_edu_notification_content" msgid="3578204677648432500">"Telezesha vidole vitatu juu na ushikilie. Gusa ili upate maelezo kuhusu miguso zaidi."</string>
+    <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Kutumia kibodi yako kuangalia programu zote"</string>
+    <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Bonyeza kitufe cha vitendo wakati wowote. Gusa ili upate maelezo kuhusu miguso zaidi."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-sw/tiles_states_strings.xml b/packages/SystemUI/res/values-sw/tiles_states_strings.xml
index b291f0d..15de7f8 100644
--- a/packages/SystemUI/res/values-sw/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-sw/tiles_states_strings.xml
@@ -56,9 +56,6 @@
     <item msgid="5376619709702103243">"Kimezimwa"</item>
     <item msgid="4875147066469902392">"Kimewashwa"</item>
   </string-array>
-    <!-- no translation found for tile_states_modes:0 (7764936419245199023) -->
-    <!-- no translation found for tile_states_modes:1 (2004750556637773692) -->
-    <!-- no translation found for tile_states_modes:2 (8968530753931637871) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Hakipatikani"</item>
     <item msgid="5044688398303285224">"Kimezimwa"</item>
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index 315e198..6cef518 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -126,38 +126,25 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"பார்க்கத் தட்டவும்"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"ஸ்கிரீன் ரெக்கார்டிங்கைச் சேமிப்பதில் பிழை ஏற்பட்டது"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"ஸ்கிரீன் ரெக்கார்டிங்கைத் தொடங்குவதில் பிழை"</string>
-    <!-- no translation found for screenrecord_stop_dialog_title (8716193661764511095) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message (6262768207331626817) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5995770227684523244) -->
-    <skip />
+    <string name="screenrecord_stop_dialog_title" msgid="8716193661764511095">"ரெக்கார்டிங்கை நிறுத்தவா?"</string>
+    <string name="screenrecord_stop_dialog_message" msgid="6262768207331626817">"இப்போது உங்கள் முழுத்திரையையும் ரெக்கார்டு செய்கிறீர்கள்"</string>
+    <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"இப்போது நீங்கள் <xliff:g id="APP_NAME">%1$s</xliff:g> ஐ ரெக்கார்டு செய்கிறீர்கள்"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"ரெக்கார்டிங்கை நிறுத்து"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"திரையைப் பகிர்கிறது"</string>
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"திரையைப் பகிர்வதை நிறுத்தவா?"</string>
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen_with_host_app (522823522115375414) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen (5090115386271179270) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_specific (5923772039347985172) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_generic (6681016774654578261) -->
-    <skip />
+    <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"இப்போது உங்கள் முழுத்திரையையும் <xliff:g id="HOST_APP_NAME">%1$s</xliff:g> உடன் பகிர்கிறீர்கள்"</string>
+    <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"இப்போது உங்கள் முழுத்திரையையும் ஆப்ஸுடன் பகிர்கிறீர்கள்"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"இப்போது நீங்கள் <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> ஐப் பகிர்கிறீர்கள்"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"இப்போது நீங்கள் ஓர் ஆப்ஸைப் பகிர்கிறீர்கள்"</string>
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"பகிர்வதை நிறுத்து"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"திரையை அலைபரப்புகிறது"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"அலைபரப்பை நிறுத்தவா?"</string>
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen_with_device (1474703115926205251) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen (8419219169553867625) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app_with_device (2715934698604085519) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (8616103075630934513) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic_with_device (9213582497852420203) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic (4100272100480415076) -->
-    <skip />
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen_with_device" msgid="1474703115926205251">"இப்போது உங்கள் முழுத்திரையையும் <xliff:g id="DEVICE_NAME">%1$s</xliff:g>க்கு அலைபரப்புகிறீர்கள்"</string>
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen" msgid="8419219169553867625">"இப்போது உங்கள் முழுத்திரையையும் அருகிலுள்ள சாதனத்திற்கு அலைபரப்புகிறீர்கள்"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app_with_device" msgid="2715934698604085519">"இப்போது நீங்கள் <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> ஐ <xliff:g id="DEVICE_NAME">%2$s</xliff:g>க்கு அலைபரப்புகிறீர்கள்"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="8616103075630934513">"இப்போது நீங்கள் <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> ஐ அருகிலுள்ள சாதனத்திற்கு அலைபரப்புகிறீர்கள்"</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic_with_device" msgid="9213582497852420203">"இப்போது நீங்கள் <xliff:g id="DEVICE_NAME">%1$s</xliff:g>க்கு அலைபரப்புகிறீர்கள்"</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic" msgid="4100272100480415076">"இப்போது நீங்கள் அருகிலுள்ள சாதனத்திற்கு அலைபரப்புகிறீர்கள்"</string>
     <string name="cast_to_other_device_stop_dialog_button" msgid="6420183747435521834">"அலைபரப்புவதை நிறுத்து"</string>
     <string name="close_dialog_button" msgid="4749497706540104133">"மூடு"</string>
     <string name="issuerecord_title" msgid="286627115110121849">"சிக்கல் ரெக்கார்டர்"</string>
@@ -303,8 +290,7 @@
     <string name="start_dreams" msgid="9131802557946276718">"ஸ்கிரீன் சேவர்"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"ஈதர்நெட்"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"தொந்தரவு செய்ய வேண்டாம்"</string>
-    <!-- no translation found for quick_settings_modes_label (5407025818652750501) -->
-    <skip />
+    <string name="quick_settings_modes_label" msgid="5407025818652750501">"முன்னுரிமைப் பயன்முறைகள்"</string>
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"புளூடூத்"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"இணைக்கப்பட்ட சாதனங்கள் இல்லை"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"சாதனத்தை இணைக்க/துண்டிக்க தட்டவும்"</string>
@@ -402,6 +388,9 @@
     <string name="performance" msgid="6552785217174378320">"செயல்திறன்"</string>
     <string name="user_interface" msgid="3712869377953950887">"பயனர் இடைமுகம்"</string>
     <string name="thermal" msgid="6758074791325414831">"தெர்மல்"</string>
+    <string name="custom" msgid="3337456985275158299">"பிரத்தியேகம்"</string>
+    <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"பிரத்தியேக டிரேஸ் அமைப்புகள்"</string>
+    <string name="restore_default" msgid="5259420807486239755">"இயல்புக்கு மீட்டெடு"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"ஒற்றைக் கைப் பயன்முறை"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"செவித்துணைக் கருவிகள்"</string>
     <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"செயலில் உள்ளது"</string>
@@ -440,6 +429,17 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"அமைப்புகளைத் திற"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"பிற சாதனம்"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"மேலோட்டப் பார்வையை நிலைமாற்று"</string>
+    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"முன்னுரிமைப் பயன்முறைகள்"</string>
+    <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_off" msgid="1736604456618147306">"முடக்கப்பட்டுள்ளது"</string>
+    <!-- no translation found for zen_mode_set_up (7457957033034460064) -->
+    <skip />
+    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
+    <skip />
+    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
+    <skip />
     <string name="zen_priority_introduction" msgid="3159291973383796646">"அலாரங்கள், நினைவூட்டல்கள், நிகழ்வுகள் மற்றும் குறிப்பிட்ட அழைப்பாளர்களைத் தவிர்த்து, பிற ஒலிகள் மற்றும் அதிர்வுகளின் தொந்தரவு இருக்காது. எனினும், நீங்கள் எதையேனும் (இசை, வீடியோக்கள், கேம்ஸ் போன்றவை) ஒலிக்கும்படி தேர்ந்தெடுத்திருந்தால், அவை வழக்கம் போல் ஒலிக்கும்."</string>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"அலாரங்களைத் தவிர்த்து, பிற ஒலிகள் மற்றும் அதிர்வுகளின் தொந்தரவு இருக்காது. எனினும், நீங்கள் எதையேனும் (இசை, வீடியோக்கள், கேம்ஸ் போன்றவை) ஒலிக்கும்படி தேர்ந்தெடுத்திருந்தால், அவை வழக்கம் போல் ஒலிக்கும்."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"பிரத்தியேகமாக்கு"</string>
@@ -478,6 +478,8 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • மெதுவாக சார்ஜாகிறது • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> இல் முழுதும் சார்ஜாகும்"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • சார்ஜாகிறது • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> இல் முழுவதும் சார்ஜாகும்"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"பூட்டுத் திரையில் விட்ஜெட்கள்"</string>
+    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
+    <skip />
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"சமூகப் பயிற்சியைத் தொடங்க இடதுபுறம் ஸ்வைப் செய்யுங்கள்"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"பிரத்தியேகமாக்குங்கள்"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"மூடுக"</string>
@@ -504,10 +506,9 @@
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"விட்ஜெட்டைத் தேர்ந்தெடுக்கும்"</string>
     <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"விட்ஜெட்டை அகற்றும்"</string>
     <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"தேர்ந்தெடுத்த விட்ஜெட்டைக் காட்சிப்படுத்தும்"</string>
-    <!-- no translation found for communal_widget_picker_title (1953369090475731663) -->
-    <skip />
-    <!-- no translation found for communal_widget_picker_description (490515450110487871) -->
-    <skip />
+    <string name="communal_widget_picker_title" msgid="1953369090475731663">"பூட்டுத் திரை விட்ஜெட்கள்"</string>
+    <string name="communal_widget_picker_description" msgid="490515450110487871">"டேப்லெட் பூட்டப்பட்டிருந்தாலும் பூட்டுத் திரையில் விட்ஜெட்டை எவரும் பார்க்கலாம்."</string>
+    <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"விட்ஜெட்டைத் தேர்வுநீக்கும்"</string>
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"பூட்டுத் திரை விட்ஜெட்கள்"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"விட்ஜெட்டைப் பயன்படுத்தி ஆப்ஸைத் திறக்க, அது நீங்கள்தான் என்பதை உறுதிசெய்ய வேண்டும். அத்துடன், உங்கள் டேப்லெட் பூட்டப்பட்டிருந்தாலும்கூட அவற்றை யார் வேண்டுமானாலும் பார்க்கலாம் என்பதை நினைவில்கொள்ளுங்கள். சில விட்ஜெட்கள் உங்கள் பூட்டுத் திரைக்காக உருவாக்கப்பட்டவை அல்ல என்பதையும் அவற்றை இங்கே சேர்ப்பது பாதுகாப்பற்றதாக இருக்கக்கூடும் என்பதையும் நினைவில்கொள்ளுங்கள்."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"சரி"</string>
@@ -564,8 +565,8 @@
     <string name="media_projection_action_text" msgid="3634906766918186440">"இப்போது தொடங்கு"</string>
     <string name="empty_shade_text" msgid="8935967157319717412">"அறிவிப்புகள் இல்லை"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"புதிய அறிவிப்புகள் இல்லை"</string>
-    <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"சூழல்சார் அறிவிப்புகள் இயக்கப்பட்டது"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"குறைந்த நேரத்தில் அதிக அறிவிப்பைப் பெறும்போது இரண்டு நிமிடங்கள் வரை உங்கள் சாதனம் ஒலியையும், திரையில் காட்டப்படும் பாப்-அப்களின் எண்ணிக்கையையும் குறைக்கிறது."</string>
+    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"\'குறைந்த ஒலியளவில் அறிவிப்புகள்\' இயக்கப்பட்டுள்ளது"</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"ஒரே நேரம் பல அறிவிப்புகள் வரும்போது சாதன ஒலியளவும் விழிப்பூட்டலும் தானாக 2 நிமிடம் குறைக்கப்படும்."</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"முடக்கு"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"பழைய அறிவிப்பைப் பார்க்க அன்லாக் செய்க"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"இந்தச் சாதனம் உங்கள் பெற்றோரால் நிர்வகிக்கப்படுகிறது"</string>
@@ -732,8 +733,7 @@
     <string name="notification_alert_title" msgid="3656229781017543655">"இயல்புநிலை"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"தானியங்கு"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"ஒலி / அதிர்வு இல்லை"</string>
-    <!-- no translation found for notification_conversation_summary_low (3855696451919728790) -->
-    <skip />
+    <string name="notification_conversation_summary_low" msgid="3855696451919728790">"ஒலியோ அதிர்வோ இருக்காது, ஆனால் உரையாடல் பிரிவில் தொடர்ந்து காட்டப்படும்"</string>
     <string name="notification_channel_summary_default" msgid="777294388712200605">"சாதன அமைப்புகளைப் பொறுத்து ஒலிக்கக்கூடும் அல்லது அதிர்வடையக்கூடும்"</string>
     <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"சாதன அமைப்புகளைப் பொறுத்து ஒலிக்கக்கூடும் அல்லது அதிர்வடையக்கூடும். இயல்பாக, <xliff:g id="APP_NAME">%1$s</xliff:g> ஆப்ஸில் பெறப்படும் உரையாடல் அறிவிப்புகள் குமிழ்களாகத் தோன்றும்."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"இந்த அறிவிப்பு ஒலி எழுப்ப வேண்டுமா அதிர வேண்டுமா என்பதை சிஸ்டம் தீர்மானிக்கும்"</string>
@@ -790,8 +790,7 @@
     <string name="keyboard_key_page_up" msgid="173914303254199845">"பேஜ் அப்"</string>
     <string name="keyboard_key_page_down" msgid="9035902490071829731">"பேஜ் டவுன்"</string>
     <string name="keyboard_key_forward_del" msgid="5325501825762733459">"டெலிட்"</string>
-    <!-- no translation found for keyboard_key_esc (6230365950511411322) -->
-    <skip />
+    <string name="keyboard_key_esc" msgid="6230365950511411322">"Esc"</string>
     <string name="keyboard_key_move_home" msgid="3496502501803911971">"ஹோம்"</string>
     <string name="keyboard_key_move_end" msgid="99190401463834854">"என்ட்"</string>
     <string name="keyboard_key_insert" msgid="4621692715704410493">"இன்சர்ட்"</string>
@@ -1233,7 +1232,6 @@
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"அங்கீகாரம் தேவை. கைரேகை சென்சாரைத் தொட்டு அங்கீகரியுங்கள்."</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"செயலில் உள்ள மொபைல் அழைப்பு"</string>
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"மொபைல் டேட்டா"</string>
-    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"இணைக்கப்பட்டது"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"தற்காலிகமாக இணைக்கப்பட்டுள்ளது"</string>
     <string name="mobile_data_poor_connection" msgid="819617772268371434">"இணைப்பு மோசமாக உள்ளது"</string>
@@ -1374,8 +1372,7 @@
     <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>
-    <!-- no translation found for shortcut_helper_category_current_app_shortcuts (4017840565974573628) -->
-    <skip />
+    <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_search_placeholder" msgid="5488547526269871819">"தேடல் ஷார்ட்கட்கள்"</string>
@@ -1388,13 +1385,27 @@
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"முடிந்தது"</string>
     <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"அருமை!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"பின்செல்"</string>
-    <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"பின்செல்ல, உங்கள் டச்பேடில் எங்கு வேண்டுமானாலும் இடது அல்லது வலதுபுறமாக மூன்று விரல்களால் ஸ்வைப் செய்யவும்."</string>
+    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
+    <skip />
+    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
+    <skip />
     <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"மூன்று விரல்கள் வலது மற்றும் இடதுபுறம் நகர்வதை டச்பேட் காட்டுகிறது"</string>
     <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"பின்செல்லும் சைகைக்கான அனிமேஷனை சாதனத்தின் திரை காட்டுகிறது"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"கீபோர்டு பேக்லைட்"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"நிலை, %2$d இல் %1$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"ஹோம் கன்ட்ரோல்கள்"</string>
     <string name="home_controls_dream_description" msgid="4644150952104035789">"ஹோம் கன்ட்ரோல்களை ஸ்கிரீன் சேவராக அணுகலாம்"</string>
-    <!-- no translation found for volume_undo_action (5815519725211877114) -->
-    <skip />
+    <string name="volume_undo_action" msgid="5815519725211877114">"செயல்தவிர்"</string>
+    <string name="back_edu_toast_content" msgid="4530314597378982956">"பின்செல்ல, உங்கள் டச்பேடில் இடது அல்லது வலதுபுறமாக மூன்று விரல்களால் ஸ்வைப் செய்யவும்"</string>
+    <string name="home_edu_toast_content" msgid="3381071147871955415">"முகப்புக்குச் செல்ல, உங்கள் டச்பேடில் மூன்று விரல்களால் மேல்நோக்கி ஸ்வைப் செய்யவும்"</string>
+    <string name="overview_edu_toast_content" msgid="5797030644017804518">"சமீபத்திய ஆப்ஸைப் பார்க்க, டச்பேடில் மூன்று விரல்களால் மேல்நோக்கி ஸ்வைப் செய்து பிடிக்கவும்"</string>
+    <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"அனைத்து ஆப்ஸையும் பார்க்க, உங்கள் கீபோர்டில் உள்ள ஆக்ஷன் பட்டனை அழுத்தவும்"</string>
+    <string name="back_edu_notification_title" msgid="5624780717751357278">"பின்செல்ல, உங்கள் டச்பேடைப் பயன்படுத்துங்கள்"</string>
+    <string name="back_edu_notification_content" msgid="2497557451540954068">"மூன்று விரல்களால் இடது அல்லது வலதுபுறம் ஸ்வைப் செய்யவும். சைகைகள் குறித்து மேலும் அறிய தட்டவும்."</string>
+    <string name="home_edu_notification_title" msgid="6097902076909654045">"முகப்புக்குச் செல்ல, உங்கள் டச்பேடைப் பயன்படுத்துங்கள்"</string>
+    <string name="home_edu_notification_content" msgid="6631697734535766588">"மூன்று விரல்களால் மேல்நோக்கி ஸ்வைப் செய்யவும். சைகைகள் குறித்து மேலும் அறிய தட்டவும்."</string>
+    <string name="overview_edu_notification_title" msgid="1265824157319562406">"சமீபத்திய ஆப்ஸைப் பார்க்க, உங்கள் டச்பேடைப் பயன்படுத்துங்கள்"</string>
+    <string name="overview_edu_notification_content" msgid="3578204677648432500">"மூன்று விரல்களால் மேல்நோக்கி ஸ்வைப் செய்து பிடிக்கவும். சைகைகள் குறித்து மேலும் அறிய தட்டவும்."</string>
+    <string name="all_apps_edu_notification_title" msgid="372262997265569063">"அனைத்து ஆப்ஸையும் பார்க்க உங்கள் கீபோர்டைப் பயன்படுத்துங்கள்"</string>
+    <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"எப்போது வேண்டுமானாலும் ஆக்ஷன் பட்டனை அழுத்தலாம். சைகைகள் குறித்து மேலும் அறிய தட்டவும்."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ta/tiles_states_strings.xml b/packages/SystemUI/res/values-ta/tiles_states_strings.xml
index a180f49..a3b9538 100644
--- a/packages/SystemUI/res/values-ta/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ta/tiles_states_strings.xml
@@ -56,9 +56,6 @@
     <item msgid="5376619709702103243">"முடக்கப்பட்டுள்ளது"</item>
     <item msgid="4875147066469902392">"இயக்கப்பட்டுள்ளது"</item>
   </string-array>
-    <!-- no translation found for tile_states_modes:0 (7764936419245199023) -->
-    <!-- no translation found for tile_states_modes:1 (2004750556637773692) -->
-    <!-- no translation found for tile_states_modes:2 (8968530753931637871) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"கிடைக்கவில்லை"</item>
     <item msgid="5044688398303285224">"முடக்கப்பட்டுள்ளது"</item>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index 87e205b..e9cb6eb 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -290,8 +290,7 @@
     <string name="start_dreams" msgid="9131802557946276718">"స్క్రీన్ సేవర్"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"ఈథర్‌నెట్"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"అంతరాయం కలిగించవద్దు"</string>
-    <!-- no translation found for quick_settings_modes_label (5407025818652750501) -->
-    <skip />
+    <string name="quick_settings_modes_label" msgid="5407025818652750501">"ముఖ్యమైన ఫైల్స్ మోడ్స్"</string>
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"బ్లూటూత్"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"జత చేసిన పరికరాలు ఏవీ అందుబాటులో లేవు"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"పరికరాన్ని కనెక్ట్ చేయడానికి లేదా డిస్‌కనెక్ట్ చేయడానికి ట్యాప్ చేయండి"</string>
@@ -389,6 +388,9 @@
     <string name="performance" msgid="6552785217174378320">"పనితీరు"</string>
     <string name="user_interface" msgid="3712869377953950887">"యూజర్ ఇంటర్‌ఫేస్"</string>
     <string name="thermal" msgid="6758074791325414831">"థర్మల్"</string>
+    <string name="custom" msgid="3337456985275158299">"అనుకూలంగా మార్చుకోండి"</string>
+    <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"కస్టమ్ ట్రేస్ సెట్టింగ్‌లు"</string>
+    <string name="restore_default" msgid="5259420807486239755">"డిఫాల్ట్‌ను రీస్టోర్ చేయండి"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"వన్-హ్యాండెడ్ మోడ్"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"వినికిడి పరికరాలు"</string>
     <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"యాక్టివ్"</string>
@@ -427,6 +429,14 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"సెట్టింగ్‌లను తెరవండి"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"ఇతర పరికరం"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"స్థూలదృష్టిని టోగుల్ చేయి"</string>
+    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"ముఖ్యమైన ఫైళ్ల మోడ్స్"</string>
+    <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_off" msgid="1736604456618147306">"ఆఫ్‌లో ఉంది"</string>
+    <string name="zen_mode_set_up" msgid="7457957033034460064">"సెటప్ చేయండి"</string>
+    <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"సెట్టింగ్‌లలో మేనేజ్ చేయండి"</string>
+    <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{మోడ్స్ ఏవీ యాక్టివ్‌గా లేవు}=1{{mode} యాక్టివ్‌గా ఉంది}other{# మోడ్స్ యాక్టివ్‌గా ఉన్నాయి}}"</string>
     <string name="zen_priority_introduction" msgid="3159291973383796646">"మీరు పేర్కొనే అలారాలు, రిమైండర్‌లు, ఈవెంట్‌లు మరియు కాలర్‌ల నుండి మినహా మరే ఇతర ధ్వనులు మరియు వైబ్రేషన్‌లతో మీకు అంతరాయం కలగదు. మీరు ఇప్పటికీ సంగీతం, వీడియోలు మరియు గేమ్‌లతో సహా మీరు ప్లే చేయడానికి ఎంచుకున్నవి ఏవైనా వింటారు."</string>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"అలారాలు నుండి మినహా మరే ఇతర ధ్వనులు మరియు వైబ్రేషన్‌లతో మీకు అంతరాయం కలగదు. మీరు ఇప్పటికీ సంగీతం, వీడియోలు మరియు గేమ్‌లతో సహా మీరు ప్లే చేయడానికి ఎంచుకున్నవి ఏవైనా వింటారు."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"అనుకూలంగా మార్చండి"</string>
@@ -465,6 +475,8 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • నెమ్మదిగా ఛార్జ్ అవుతోంది • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>లో పూర్తి ఛార్జ్"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ఛార్జ్ అవుతోంది • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>లో పూర్తిగా ఛార్జ్ అవుతుంది"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"లాక్ స్క్రీన్‌లో విడ్జెట్‌లు"</string>
+    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
+    <skip />
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"కమ్యూనల్ ట్యుటోరియల్‌ను ప్రారంభించడానికి ఎడమ వైపునకు స్వైప్ చేయండి"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"అనుకూలంగా మార్చండి"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"విస్మరించండి"</string>
@@ -493,6 +505,7 @@
     <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"ఎంచుకున్న విడ్జెట్ కోసం ప్లేస్‌ను ఎంచుకోండి"</string>
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"లాక్ స్క్రీన్ విడ్జెట్‌లు"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"మీ టాబ్లెట్ లాక్ చేసి ఉన్నా, మీ లాక్ స్క్రీన్‌లో విడ్జెట్‌లను ఎవరైనా చూడవచ్చు."</string>
+    <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"విడ్జెట్ ఎంపిక రద్దు చేయండి"</string>
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"లాక్ స్క్రీన్ విడ్జెట్‌లు"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"విడ్జెట్‌ను ఉపయోగించి యాప్‌ను తెరవడానికి, ఇది మీరేనని వెరిఫై చేయాల్సి ఉంటుంది. అలాగే, మీ టాబ్లెట్ లాక్ చేసి ఉన్నప్పటికీ, ఎవరైనా వాటిని చూడగలరని గుర్తుంచుకోండి. కొన్ని విడ్జెట్‌లు మీ లాక్ స్క్రీన్‌కు తగినవి కాకపోవచ్చు, వాటిని ఇక్కడ జోడించడం సురక్షితం కాకపోవచ్చు."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"అర్థమైంది"</string>
@@ -549,8 +562,8 @@
     <string name="media_projection_action_text" msgid="3634906766918186440">"ఇప్పుడే ప్రారంభించండి"</string>
     <string name="empty_shade_text" msgid="8935967157319717412">"నోటిఫికేషన్‌లు లేవు"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"కొత్త నోటిఫికేషన్‌లు ఏవీ లేవు"</string>
-    <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"అనుకూల నోటిఫికేషన్‌లు ఆన్‌లో ఉన్నాయి"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"మీరు తక్కువ సమయంలో అనేక నోటిఫికేషన్‌లను స్వీకరించినప్పుడు మీ పరికరం ఇప్పుడు వాల్యూమ్‌ను తగ్గిస్తుంది, స్క్రీన్‌పై పాప్-అప్‌లను రెండు నిమిషాల వరకు తగ్గిస్తుంది."</string>
+    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"నోటిఫికేషన్ కూల్‌డౌన్ ఆన్ అయింది"</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"ఒకేసారి పలు నోటిఫికేషన్‌లు వస్తే, పరికర వాల్యూమ్, అలర్ట్‌లు ఆటోమేటిక్‌గా 2 నిమిషాలకు తగ్గించబడతాయి."</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"ఆఫ్ చేయండి"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"పాత నోటిఫికేషన్‌ల కోసం అన్‌లాక్ చేయండి"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"ఈ పరికరాన్ని మీ తల్లి/తండ్రి మేనేజ్ చేస్తున్నారు"</string>
@@ -717,8 +730,7 @@
     <string name="notification_alert_title" msgid="3656229781017543655">"ఆటోమేటిక్ సెట్టింగ్"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"ఆటోమేటిక్"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"సౌండ్ లేదా వైబ్రేషన్‌లు ఏవీ ఉండవు"</string>
-    <!-- no translation found for notification_conversation_summary_low (3855696451919728790) -->
-    <skip />
+    <string name="notification_conversation_summary_low" msgid="3855696451919728790">"ఎటువంటి సౌండ్ లేదా వైబ్రేషన్ లేకుండా సంభాషణ విభాగంలో నోటిఫికేషన్ కనిపిస్తుంది"</string>
     <string name="notification_channel_summary_default" msgid="777294388712200605">"పరికర సెట్టింగ్‌ల ఆధారంగా రింగ్ లేదా వైబ్రేట్ కావచ్చు"</string>
     <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"పరికర సెట్టింగ్‌ల ఆధారంగా రింగ్ లేదా వైబ్రేట్ కావచ్చు. <xliff:g id="APP_NAME">%1$s</xliff:g> నుండి సంభాషణలు ఆటోమేటిక్‌గా బబుల్‌లో కనిపిస్తాయి."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"ఈ నోటిఫికేషన్ వచ్చినప్పుడు శబ్దం చేయాలా లేదా వైబ్రేట్ చేయాలా అనేది నిర్ణయించడానికి సిస్టమ్‌కు అనుమతి ఇవ్వండి"</string>
@@ -1082,7 +1094,7 @@
     <string name="controls_dialog_ok" msgid="2770230012857881822">"జోడించండి"</string>
     <string name="controls_dialog_remove" msgid="3775288002711561936">"తీసివేయండి"</string>
     <string name="controls_dialog_message" msgid="342066938390663844">"<xliff:g id="APP">%s</xliff:g> ద్వారా సూచించబడింది"</string>
-    <string name="controls_tile_locked" msgid="731547768182831938">"పరికరంలాక్ చేయబడింది"</string>
+    <string name="controls_tile_locked" msgid="731547768182831938">"డివైజ్ లాక్ అయ్యింది"</string>
     <string name="controls_settings_show_controls_dialog_title" msgid="3357852503553809554">"లాక్ స్క్రీన్ నుండి పరికరాలను చూపించాలా, కంట్రోల్ చేయాలా?"</string>
     <string name="controls_settings_show_controls_dialog_message" msgid="7666211700524587969">"మీరు లాక్ స్క్రీన్‌కు మీ బాహ్య పరికరాల కోసం కంట్రోల్స్‌ను జోడించవచ్చు.\n\nమీ ఫోన్ లేదా టాబ్లెట్‌ను అన్‌లాక్ చేయకుండానే కొన్ని పరికరాలను కంట్రోల్ చేయడానికి మీ పరికర యాప్ మిమ్మల్ని అనుమతించవచ్చు.\n\nమీరు సెట్టింగ్‌లలో ఎప్పుడైనా మార్పులు చేయవచ్చు."</string>
     <string name="controls_settings_trivial_controls_dialog_title" msgid="7593188157655036677">"లాక్ స్క్రీన్ నుండి పరికరాలను కంట్రోల్ చేయాలా?"</string>
@@ -1217,7 +1229,6 @@
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"ప్రామాణీకరణ అవసరం. ప్రామాణీకరించడానికి వేలిముద్ర సెన్సార్‌ను తాకండి."</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"ఫోన్ కాల్ జరుగుతోంది"</string>
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"మొబైల్ డేటా"</string>
-    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"కనెక్ట్ చేయబడింది"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"తాత్కాలికంగా కనెక్ట్ చేయబడింది"</string>
     <string name="mobile_data_poor_connection" msgid="819617772268371434">"కనెక్షన్ బాగాలేదు"</string>
@@ -1371,13 +1382,27 @@
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"పూర్తయింది"</string>
     <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"విజయవంతమైంది!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"వెనుకకు"</string>
-    <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"వెనుకకు వెళ్లడానికి, టచ్‌ప్యాడ్‌లో ఎక్కడైనా మూడు వేళ్లను ఉపయోగించి ఎడమ లేదా కుడికి స్వైప్ చేయండి."</string>
+    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
+    <skip />
+    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
+    <skip />
     <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"మూడు వేళ్లు కుడి, ఎడమకు కదులుతున్నట్లు చూపే టచ్‌ప్యాడ్"</string>
     <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"వెనుక సంజ్ఞ కోసం యానిమేషన్‌ను చూపుతున్న పరికర స్క్రీన్"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"కీబోర్డ్ బ్యాక్‌లైట్"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$dలో %1$dవ స్థాయి"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"హోమ్ కంట్రోల్స్"</string>
     <string name="home_controls_dream_description" msgid="4644150952104035789">"హోమ్ కంట్రోల్స్‌ను స్క్రీన్ సేవర్‌గా చేసి వేగంగా యాక్సెస్ పొందండి"</string>
-    <!-- no translation found for volume_undo_action (5815519725211877114) -->
-    <skip />
+    <string name="volume_undo_action" msgid="5815519725211877114">"చర్య రద్దు చేయండి"</string>
+    <string name="back_edu_toast_content" msgid="4530314597378982956">"వెనుకకు వెళ్లడానికి, టచ్‌ప్యాడ్‌లో మూడు వేళ్లను ఉపయోగించి ఎడమ లేదా కుడి వైపునకు స్వైప్ చేయండి"</string>
+    <string name="home_edu_toast_content" msgid="3381071147871955415">"హోమ్‌కు వెళ్లడానికి, టచ్‌ప్యాడ్‌లో మీ మూడు వెళ్లతో పైకి స్వైప్ చేయండి"</string>
+    <string name="overview_edu_toast_content" msgid="5797030644017804518">"ఇటీవలి యాప్‌లను చూడటానికి, టచ్‌ప్యాడ్‌లో మూడు వేళ్లతో పైకి స్వైప్ చేసి, హోల్డ్ చేయండి"</string>
+    <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"మీ యాప్‌లన్నింటినీ చూడటానికి, మీ కీబోర్డ్‌లో యాక్షన్ కీని నొక్కండి"</string>
+    <string name="back_edu_notification_title" msgid="5624780717751357278">"వెనుకకు వెళ్లడానికి మీ టచ్‌ప్యాడ్‌ను ఉపయోగించండి"</string>
+    <string name="back_edu_notification_content" msgid="2497557451540954068">"మూడు వేళ్లతో ఎడమ / కుడి వైపునకు స్వైప్ చేయండి. మరిన్ని సంజ్ఞల గురించి తెలుసుకోవడానికి ట్యాప్ చేయండి."</string>
+    <string name="home_edu_notification_title" msgid="6097902076909654045">"హోమ్‌కు వెళ్లడానికి మీ టచ్‌ప్యాడ్‌ను ఉపయోగించండి"</string>
+    <string name="home_edu_notification_content" msgid="6631697734535766588">"మూడు వేళ్లను ఉపయోగించి పైకి స్వైప్ చేయండి. మరిన్ని సంజ్ఞల గురించి తెలుసుకోవడానికి ట్యాప్ చేయండి."</string>
+    <string name="overview_edu_notification_title" msgid="1265824157319562406">"ఇటీవలి యాప్‌లను చూడటానికి మీ టచ్‌ప్యాడ్‌ను ఉపయోగించండి"</string>
+    <string name="overview_edu_notification_content" msgid="3578204677648432500">"మూడు వేళ్లతో పైకి స్వైప్ చేసి, హోల్డ్ చేయండి. మరిన్ని సంజ్ఞల గురించి తెలుసుకోవడానికి ట్యాప్ చేయండి."</string>
+    <string name="all_apps_edu_notification_title" msgid="372262997265569063">"యాప్‌లన్నింటినీ చూడటానికి మీ కీబోర్డ్‌ను ఉపయోగించండి"</string>
+    <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"ఏ సమయంలోనైనా యాక్షన్ కీని నొక్కండి. మరిన్ని సంజ్ఞల గురించి తెలుసుకోవడానికి ట్యాప్ చేయండి."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-te/tiles_states_strings.xml b/packages/SystemUI/res/values-te/tiles_states_strings.xml
index 609a846cd..6584cdd 100644
--- a/packages/SystemUI/res/values-te/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-te/tiles_states_strings.xml
@@ -56,9 +56,6 @@
     <item msgid="5376619709702103243">"ఆఫ్‌లో ఉంది"</item>
     <item msgid="4875147066469902392">"ఆన్‌లో ఉంది"</item>
   </string-array>
-    <!-- no translation found for tile_states_modes:0 (7764936419245199023) -->
-    <!-- no translation found for tile_states_modes:1 (2004750556637773692) -->
-    <!-- no translation found for tile_states_modes:2 (8968530753931637871) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"అందుబాటులో లేదు"</item>
     <item msgid="5044688398303285224">"ఆఫ్‌లో ఉంది"</item>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index 6449dd8..0d131ac 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -126,7 +126,7 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"แตะเพื่อดู"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"เกิดข้อผิดพลาดในการบันทึกหน้าจอ"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"เกิดข้อผิดพลาดขณะเริ่มบันทึกหน้าจอ"</string>
-    <string name="screenrecord_stop_dialog_title" msgid="8716193661764511095">"หยุดบันทึกใช่ไหม"</string>
+    <string name="screenrecord_stop_dialog_title" msgid="8716193661764511095">"หยุดบันทึกไหม"</string>
     <string name="screenrecord_stop_dialog_message" msgid="6262768207331626817">"คุณกำลังบันทึกทั้งหน้าจอ"</string>
     <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"คุณกำลังบันทึก <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"หยุดบันทึก"</string>
@@ -138,7 +138,7 @@
     <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"คุณกำลังแชร์แอป"</string>
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"หยุดแชร์"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"กำลังแคสต์หน้าจอ"</string>
-    <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"หยุดการแคสต์ไหม"</string>
+    <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"หยุดแคสต์ไหม"</string>
     <string name="cast_to_other_device_stop_dialog_message_entire_screen_with_device" msgid="1474703115926205251">"คุณกำลังแคสต์ทั้งหน้าจอไปยัง <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="cast_to_other_device_stop_dialog_message_entire_screen" msgid="8419219169553867625">"คุณกำลังแคสต์ทั้งหน้าจอไปยังอุปกรณ์ที่อยู่ใกล้เคียง"</string>
     <string name="cast_to_other_device_stop_dialog_message_specific_app_with_device" msgid="2715934698604085519">"คุณกำลังแคสต์ <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> ไปยัง <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
@@ -388,6 +388,9 @@
     <string name="performance" msgid="6552785217174378320">"ประสิทธิภาพ"</string>
     <string name="user_interface" msgid="3712869377953950887">"อินเทอร์เฟซผู้ใช้"</string>
     <string name="thermal" msgid="6758074791325414831">"ความร้อน"</string>
+    <string name="custom" msgid="3337456985275158299">"กำหนดเอง"</string>
+    <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"การตั้งค่าการติดตามแบบกำหนดเอง"</string>
+    <string name="restore_default" msgid="5259420807486239755">"คืนค่าเริ่มต้น"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"โหมดมือเดียว"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"เครื่องช่วยฟัง"</string>
     <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"ใช้งานอยู่"</string>
@@ -426,6 +429,17 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"เปิดการตั้งค่า"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"อุปกรณ์อื่น"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"สลับภาพรวม"</string>
+    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"โหมดสำคัญ"</string>
+    <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_off" msgid="1736604456618147306">"ปิด"</string>
+    <!-- no translation found for zen_mode_set_up (7457957033034460064) -->
+    <skip />
+    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
+    <skip />
+    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
+    <skip />
     <string name="zen_priority_introduction" msgid="3159291973383796646">"คุณจะไม่ถูกรบกวนจากเสียงและการสั่น ยกเว้นเสียงนาฬิกาปลุก การช่วยเตือน กิจกรรม และผู้โทรที่ระบุไว้ คุณจะยังคงได้ยินสิ่งที่คุณเลือกเล่น เช่น เพลง วิดีโอ และเกม"</string>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"คุณจะไม่ถูกรบกวนจากเสียงและการสั่น ยกเว้นเสียงนาฬิกาปลุก คุณจะยังคงได้ยินสิ่งที่คุณเลือกเล่น เช่น เพลง วิดีโอ และเกม"</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"กำหนดค่า"</string>
@@ -464,6 +478,8 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • กำลังชาร์จอย่างช้าๆ • จะเต็มในอีก <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • กำลังชาร์จ • จะเต็มในอีก <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"วิดเจ็ตในหน้าจอล็อก"</string>
+    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
+    <skip />
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"ปัดไปทางซ้ายเพื่อเริ่มบทแนะนำส่วนกลาง"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"ปรับแต่ง"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"ปิด"</string>
@@ -492,6 +508,7 @@
     <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"จัดวางวิดเจ็ตที่เลือก"</string>
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"วิดเจ็ตในหน้าจอล็อก"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"ทุกคนจะดูวิดเจ็ตที่อยู่ในหน้าจอล็อกของคุณได้ แม้ว่าแท็บเล็ตจะล็อกอยู่ก็ตาม"</string>
+    <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"ยกเลิกการเลือกวิดเจ็ต"</string>
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"วิดเจ็ตในหน้าจอล็อก"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"หากต้องการเปิดแอปโดยใช้วิดเจ็ต คุณจะต้องยืนยันตัวตนของคุณ นอกจากนี้ โปรดทราบว่าผู้อื่นจะดูวิดเจ็ตเหล่านี้ได้แม้ว่าแท็บเล็ตจะล็อกอยู่ก็ตาม วิดเจ็ตบางอย่างอาจไม่ได้มีไว้สำหรับหน้าจอล็อกของคุณ และอาจไม่ปลอดภัยที่จะเพิ่มที่นี่"</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"รับทราบ"</string>
@@ -548,8 +565,8 @@
     <string name="media_projection_action_text" msgid="3634906766918186440">"เริ่มเลย"</string>
     <string name="empty_shade_text" msgid="8935967157319717412">"ไม่มีการแจ้งเตือน"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"ไม่มีการแจ้งเตือนใหม่"</string>
-    <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"การแจ้งเตือนแบบปรับอัตโนมัติเปิดอยู่"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"จากนี้ไปอุปกรณ์จะลดระดับเสียงและจำนวนป๊อปอัปบนหน้าจอสูงสุด 2 นาทีเมื่อคุณได้รับการแจ้งเตือนจำนวนมากในระยะเวลาสั้นๆ"</string>
+    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"การพักการแจ้งเตือนเปิดอยู่"</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"ระบบจะลดระดับเสียงและจำนวนการแจ้งเตือนของอุปกรณ์โดยอัตโนมัติสูงสุด 2 นาทีเมื่อคุณได้รับการแจ้งเตือนพร้อมกันมากเกินไป"</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"ปิด"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"ปลดล็อกเพื่อดูการแจ้งเตือนเก่า"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"อุปกรณ์นี้จัดการโดยผู้ปกครอง"</string>
@@ -716,8 +733,7 @@
     <string name="notification_alert_title" msgid="3656229781017543655">"ค่าเริ่มต้น"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"อัตโนมัติ"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"ไม่มีเสียงหรือการสั่น"</string>
-    <!-- no translation found for notification_conversation_summary_low (3855696451919728790) -->
-    <skip />
+    <string name="notification_conversation_summary_low" msgid="3855696451919728790">"ไม่มีเสียงหรือการสั่น แต่ยังคงปรากฏในส่วนการสนทนา"</string>
     <string name="notification_channel_summary_default" msgid="777294388712200605">"อาจส่งเสียงหรือสั่นโดยขึ้นอยู่กับการตั้งค่าอุปกรณ์"</string>
     <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"อาจส่งเสียงหรือสั่นโดยขึ้นอยู่กับการตั้งค่าอุปกรณ์ การสนทนาจาก <xliff:g id="APP_NAME">%1$s</xliff:g> จะแสดงเป็นบับเบิลโดยค่าเริ่มต้น"</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"ให้ระบบพิจารณาว่าจะให้การแจ้งเตือนนี้ส่งเสียงหรือสั่นหรือไม่"</string>
@@ -1216,7 +1232,6 @@
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"ต้องมีการตรวจสอบสิทธิ์ แตะเซ็นเซอร์ลายนิ้วมือเพื่อตรวจสอบสิทธิ์"</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"กำลังโทรอยู่"</string>
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"อินเทอร์เน็ตมือถือ"</string>
-    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"เชื่อมต่อแล้ว"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"เชื่อมต่อแล้วชั่วคราว"</string>
     <string name="mobile_data_poor_connection" msgid="819617772268371434">"การเชื่อมต่อไม่ดี"</string>
@@ -1357,8 +1372,7 @@
     <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>
-    <!-- no translation found for shortcut_helper_category_current_app_shortcuts (4017840565974573628) -->
-    <skip />
+    <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_search_placeholder" msgid="5488547526269871819">"ค้นหาแป้นพิมพ์ลัด"</string>
@@ -1371,7 +1385,10 @@
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"เสร็จสิ้น"</string>
     <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"เก่งมาก"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"ย้อนกลับ"</string>
-    <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"หากต้องการย้อนกลับ ให้ใช้ 3 นิ้วปัดไปทางซ้ายหรือขวาที่ใดก็ได้บนทัชแพด"</string>
+    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
+    <skip />
+    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
+    <skip />
     <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"ทัชแพดแสดงภาพ 3 นิ้วเลื่อนไปทางขวาและซ้าย"</string>
     <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"หน้าจออุปกรณ์แสดงภาพเคลื่อนไหวของท่าทางสัมผัสย้อนกลับ"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"ไฟแบ็กไลต์ของแป้นพิมพ์"</string>
@@ -1379,4 +1396,16 @@
     <string name="home_controls_dream_label" msgid="6567105701292324257">"ระบบควบคุมอุปกรณ์สมาร์ทโฮม"</string>
     <string name="home_controls_dream_description" msgid="4644150952104035789">"เข้าถึงระบบควบคุมอุปกรณ์สมาร์ทโฮมได้อย่างรวดเร็วผ่านภาพพักหน้าจอ"</string>
     <string name="volume_undo_action" msgid="5815519725211877114">"เลิกทำ"</string>
+    <string name="back_edu_toast_content" msgid="4530314597378982956">"หากต้องการย้อนกลับ ให้ใช้ 3 นิ้วปัดไปทางซ้ายหรือขวาบนทัชแพด"</string>
+    <string name="home_edu_toast_content" msgid="3381071147871955415">"หากต้องการไปที่หน้าแรก ให้ใช้ 3 นิ้วปัดขึ้นบนทัชแพด"</string>
+    <string name="overview_edu_toast_content" msgid="5797030644017804518">"หากต้องการดูแอปล่าสุด ให้ใช้ 3 นิ้วปัดขึ้นแล้วค้างไว้บนทัชแพด"</string>
+    <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"หากต้องการดูแอปทั้งหมด ให้กดปุ่มดำเนินการบนแป้นพิมพ์"</string>
+    <string name="back_edu_notification_title" msgid="5624780717751357278">"ใช้ทัชแพดเพื่อย้อนกลับ"</string>
+    <string name="back_edu_notification_content" msgid="2497557451540954068">"ใช้ 3 นิ้วปัดไปทางซ้ายหรือขวา แตะเพื่อดูข้อมูลเพิ่มเติมเกี่ยวกับท่าทางสัมผัสต่างๆ"</string>
+    <string name="home_edu_notification_title" msgid="6097902076909654045">"ใช้ทัชแพดเพื่อไปยังหน้าแรก"</string>
+    <string name="home_edu_notification_content" msgid="6631697734535766588">"ใช้ 3 นิ้วปัดขึ้น แตะเพื่อดูข้อมูลเพิ่มเติมเกี่ยวกับท่าทางสัมผัสต่างๆ"</string>
+    <string name="overview_edu_notification_title" msgid="1265824157319562406">"ใช้ทัชแพดเพื่อดูแอปล่าสุด"</string>
+    <string name="overview_edu_notification_content" msgid="3578204677648432500">"ใช้ 3 นิ้วปัดขึ้นแล้วค้างไว้ แตะเพื่อดูข้อมูลเพิ่มเติมเกี่ยวกับท่าทางสัมผัสต่างๆ"</string>
+    <string name="all_apps_edu_notification_title" msgid="372262997265569063">"ใช้แป้นพิมพ์เพื่อดูแอปทั้งหมด"</string>
+    <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"กดปุ่มดำเนินการได้ทุกเมื่อ แตะเพื่อดูข้อมูลเพิ่มเติมเกี่ยวกับท่าทางสัมผัสต่างๆ"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-th/tiles_states_strings.xml b/packages/SystemUI/res/values-th/tiles_states_strings.xml
index 20f66da..8b7187b 100644
--- a/packages/SystemUI/res/values-th/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-th/tiles_states_strings.xml
@@ -56,11 +56,6 @@
     <item msgid="5376619709702103243">"ปิด"</item>
     <item msgid="4875147066469902392">"เปิด"</item>
   </string-array>
-  <string-array name="tile_states_modes">
-    <item msgid="7764936419245199023">"ไม่พร้อมใช้งาน"</item>
-    <item msgid="2004750556637773692">"ปิด"</item>
-    <item msgid="8968530753931637871">"เปิด"</item>
-  </string-array>
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"ไม่พร้อมใช้งาน"</item>
     <item msgid="5044688398303285224">"ปิด"</item>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index e52ebcc..f836aca 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -388,6 +388,9 @@
     <string name="performance" msgid="6552785217174378320">"Performance"</string>
     <string name="user_interface" msgid="3712869377953950887">"User Interface"</string>
     <string name="thermal" msgid="6758074791325414831">"Thermal"</string>
+    <string name="custom" msgid="3337456985275158299">"Custom"</string>
+    <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Mga Setting ng Custom na Trace"</string>
+    <string name="restore_default" msgid="5259420807486239755">"I-restore ang Default"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"One-hand mode"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Mga hearing device"</string>
     <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktibo"</string>
@@ -426,6 +429,17 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Buksan ang Mga Setting"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Iba pang device"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"I-toggle ang Overview"</string>
+    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Mga priyoridad na mode"</string>
+    <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Tapos na"</string>
+    <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Mga Setting"</string>
+    <string name="zen_mode_on" msgid="9085304934016242591">"Naka-on"</string>
+    <string name="zen_mode_off" msgid="1736604456618147306">"Naka-off"</string>
+    <!-- no translation found for zen_mode_set_up (7457957033034460064) -->
+    <skip />
+    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
+    <skip />
+    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
+    <skip />
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Hindi ka maiistorbo ng mga tunog at pag-vibrate, maliban mula sa mga alarm, paalala, event, at tumatawag na tutukuyin mo. Maririnig mo pa rin ang kahit na anong piliin mong i-play kabilang ang mga musika, video, at laro."</string>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Hindi ka maiistorbo ng mga tunog at pag-vibrate, maliban sa mga alarm. Maririnig mo pa rin ang anumang pipiliin mong i-play kabilang ang mga musika, video, at laro."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"I-customize"</string>
@@ -464,6 +478,8 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Mabagal na nagcha-charge • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> na lang para mapuno"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Nagcha-charge • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> na lang para mapuno"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Mga widget sa lock screen"</string>
+    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
+    <skip />
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Mag-swipe pakaliwa para simulan ang communal na tutorial"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"I-customize"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"I-dismiss"</string>
@@ -492,6 +508,7 @@
     <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"ilagay ang napiling widget"</string>
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"Mga widget ng lock screen"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"Makikita ng sinuman ang mga widget sa lock screen, kahit naka-lock ang tablet."</string>
+    <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"i-unselect ang widget"</string>
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Mga widget ng lock screen"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Para magbukas ng app gamit ang isang widget, kakailanganin mong i-verify na ikaw iyan. Bukod pa rito, tandaang puwedeng tingnan ng kahit na sino ang mga ito, kahit na naka-lock ang iyong tablet. Posibleng hindi para sa iyong lock screen ang ilang widget at posibleng hindi ligtas ang mga ito na idagdag dito."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"OK"</string>
@@ -548,8 +565,8 @@
     <string name="media_projection_action_text" msgid="3634906766918186440">"Magsimula ngayon"</string>
     <string name="empty_shade_text" msgid="8935967157319717412">"Walang mga notification"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"Walang bagong notification"</string>
-    <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"On ang adaptive notifications"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"Hinihinaan ng device ang volume at binabawasan nito ang pop-ups nang hanggang 2 minuto kapag nakatanggap ng maraming notification sa maikling oras."</string>
+    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Naka-on ang cooldown sa notification"</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Babawasan ang volume at alerto nang hanggang 2 minuto kapag nakatanggap ng maraming notification."</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"I-off"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"I-unlock para makita ang mga mas lumang notification"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Pinapamahalaan ng magulang mo itong device"</string>
@@ -716,8 +733,7 @@
     <string name="notification_alert_title" msgid="3656229781017543655">"Default"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Awtomatiko"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"Walang tunog o pag-vibrate"</string>
-    <!-- no translation found for notification_conversation_summary_low (3855696451919728790) -->
-    <skip />
+    <string name="notification_conversation_summary_low" msgid="3855696451919728790">"Walang tunog o pag-vibrate, pero lumalabas pa rin sa seksyon ng pag-uusap"</string>
     <string name="notification_channel_summary_default" msgid="777294388712200605">"Puwedeng mag-ring o mag-vibrate batay sa mga setting ng device"</string>
     <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Puwedeng mag-ring o mag-vibrate batay sa mga setting ng device. Mga pag-uusap mula sa <xliff:g id="APP_NAME">%1$s</xliff:g> bubble bilang default."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Ipatukoy sa system kung dapat gumawa ng tunog o pag-vibrate ang notification na ito"</string>
@@ -1216,7 +1232,6 @@
     <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>
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobile data"</string>
-    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Nakakonekta"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Pansamantalang nakakonekta"</string>
     <string name="mobile_data_poor_connection" msgid="819617772268371434">"Mahina ang koneksyon"</string>
@@ -1357,8 +1372,7 @@
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Split screen"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Input"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Mga shortcut ng app"</string>
-    <!-- no translation found for shortcut_helper_category_current_app_shortcuts (4017840565974573628) -->
-    <skip />
+    <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Kasalukuyang App"</string>
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accessibility"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Mga keyboard shortcut"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Mga shortcut ng paghahanap"</string>
@@ -1371,7 +1385,10 @@
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Tapos na"</string>
     <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Magaling!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Bumalik"</string>
-    <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"Para bumalik, mag-swipe pakaliwa o pakanan gamit ang tatlong daliri kahit saan sa touchpad."</string>
+    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
+    <skip />
+    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
+    <skip />
     <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Touchpad na nagpapakita ng tatlong daliring gumagalaw pakanan at pakaliwa"</string>
     <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Screen ng device na nagpapakita ng animation para sa galaw sa pagbalik"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Backlight ng keyboard"</string>
@@ -1379,4 +1396,16 @@
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Mga Home Control"</string>
     <string name="home_controls_dream_description" msgid="4644150952104035789">"Mabilis i-access ang home control bilang screensaver"</string>
     <string name="volume_undo_action" msgid="5815519725211877114">"I-undo"</string>
+    <string name="back_edu_toast_content" msgid="4530314597378982956">"Para bumalik, mag-swipe pakaliwa o pakanan gamit ang tatlong daliri sa touchpad."</string>
+    <string name="home_edu_toast_content" msgid="3381071147871955415">"Para pumunta sa home, mag-swipe pataas gamit ang tatlong daliri sa touchpad"</string>
+    <string name="overview_edu_toast_content" msgid="5797030644017804518">"Para tingnan ang kamakailang app, mag-swipe pataas at i-hold gamit ang tatlong daliri sa touchpad"</string>
+    <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Para tingnan ang lahat ng iyong app, pindutin ang action key sa keyboard mo"</string>
+    <string name="back_edu_notification_title" msgid="5624780717751357278">"Gamitin ang iyong touchpad para bumalik"</string>
+    <string name="back_edu_notification_content" msgid="2497557451540954068">"Mag-swipe pakaliwa o pakanan gamit ang tatlong daliri. I-tap para matuto pa tungkol sa mga galaw."</string>
+    <string name="home_edu_notification_title" msgid="6097902076909654045">"Gamitin ang touchpad mo para pumunta sa home"</string>
+    <string name="home_edu_notification_content" msgid="6631697734535766588">"Mag-swipe pataas gamit ang tatlong daliri I-tap para matuto pa tungkol sa mga galaw."</string>
+    <string name="overview_edu_notification_title" msgid="1265824157319562406">"Gamitin ang iyong touchpad para tingnan ang mga kamakailang app"</string>
+    <string name="overview_edu_notification_content" msgid="3578204677648432500">"Mag-swipe pataas at i-hold gamit ang tatlong daliri. I-tap para matuto pa tungkol sa mga galaw."</string>
+    <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Gamitin ang iyong keyboard para tingnan ang lahat ng app"</string>
+    <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Pindutin ang action key kahit kailan. I-tap para matuto pa tungkol sa mga galaw."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-tl/tiles_states_strings.xml b/packages/SystemUI/res/values-tl/tiles_states_strings.xml
index 85c4cfa..fe2827f 100644
--- a/packages/SystemUI/res/values-tl/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-tl/tiles_states_strings.xml
@@ -56,11 +56,6 @@
     <item msgid="5376619709702103243">"Naka-off"</item>
     <item msgid="4875147066469902392">"Naka-on"</item>
   </string-array>
-  <string-array name="tile_states_modes">
-    <item msgid="7764936419245199023">"Hindi available"</item>
-    <item msgid="2004750556637773692">"Naka-off"</item>
-    <item msgid="8968530753931637871">"Naka-on"</item>
-  </string-array>
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Hindi available"</item>
     <item msgid="5044688398303285224">"Naka-off"</item>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index 2c429d8..b30df3d 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -126,38 +126,25 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"Görüntülemek için dokunun"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"Ekran kaydı saklanırken hata oluştu"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Ekran kaydı başlatılırken hata oluştu"</string>
-    <!-- no translation found for screenrecord_stop_dialog_title (8716193661764511095) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message (6262768207331626817) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5995770227684523244) -->
-    <skip />
+    <string name="screenrecord_stop_dialog_title" msgid="8716193661764511095">"Kayıt işlemi durdurulsun mu?"</string>
+    <string name="screenrecord_stop_dialog_message" msgid="6262768207331626817">"Şu anda ekranınızın tamamını kaydediyorsunuz"</string>
+    <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Şu anda <xliff:g id="APP_NAME">%1$s</xliff:g> içeriğini kaydediyorsunuz"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Kaydı durdur"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Ekran paylaşılıyor"</string>
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Ekran paylaşımı durdurulsun mu?"</string>
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen_with_host_app (522823522115375414) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen (5090115386271179270) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_specific (5923772039347985172) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_generic (6681016774654578261) -->
-    <skip />
+    <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Şu anda ekranınızın tamamını <xliff:g id="HOST_APP_NAME">%1$s</xliff:g> ile paylaşıyorsunuz"</string>
+    <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Şu anda ekranınızın tamamını bir uygulamayla paylaşıyorsunuz"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Şu anda <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> içeriğini paylaşıyorsunuz"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Şu anda bir uygulamayı paylaşıyorsunuz"</string>
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Paylaşımı durdur"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Ekran yayınlanıyor"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Yayın durdurulsun mu?"</string>
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen_with_device (1474703115926205251) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen (8419219169553867625) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app_with_device (2715934698604085519) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (8616103075630934513) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic_with_device (9213582497852420203) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic (4100272100480415076) -->
-    <skip />
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen_with_device" msgid="1474703115926205251">"Şu anda ekranınızın tamamını <xliff:g id="DEVICE_NAME">%1$s</xliff:g> cihazında yayınlıyorsunuz"</string>
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen" msgid="8419219169553867625">"Şu anda ekranınızın tamamını yakındaki bir cihazda yayınlıyorsunuz"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app_with_device" msgid="2715934698604085519">"Şu anda <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> içeriğini <xliff:g id="DEVICE_NAME">%2$s</xliff:g> cihazında yayınlıyorsunuz"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="8616103075630934513">"Şu anda <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> içeriğini yakındaki bir cihazda yayınlıyorsunuz"</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic_with_device" msgid="9213582497852420203">"Şu anda <xliff:g id="DEVICE_NAME">%1$s</xliff:g> cihazında yayınlıyorsunuz"</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic" msgid="4100272100480415076">"Şu anda yakındaki bir cihazda yayınlıyorsunuz"</string>
     <string name="cast_to_other_device_stop_dialog_button" msgid="6420183747435521834">"Yayını durdur"</string>
     <string name="close_dialog_button" msgid="4749497706540104133">"Kapat"</string>
     <string name="issuerecord_title" msgid="286627115110121849">"Sorun Kaydedici"</string>
@@ -303,8 +290,7 @@
     <string name="start_dreams" msgid="9131802557946276718">"Ekran koruyucu"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"Rahatsız Etmeyin"</string>
-    <!-- no translation found for quick_settings_modes_label (5407025818652750501) -->
-    <skip />
+    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Öncelik modları"</string>
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Kullanılabilir eşlenmiş cihaz yok"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Cihaz bağlamak veya cihazın bağlantısını kesmek için dokunun"</string>
@@ -402,6 +388,9 @@
     <string name="performance" msgid="6552785217174378320">"Performans"</string>
     <string name="user_interface" msgid="3712869377953950887">"Kullanıcı Arayüzü"</string>
     <string name="thermal" msgid="6758074791325414831">"Termal"</string>
+    <string name="custom" msgid="3337456985275158299">"Özel"</string>
+    <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Özel İz Ayarları"</string>
+    <string name="restore_default" msgid="5259420807486239755">"Varsayılanları Geri Yükle"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Tek el modu"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"İşitme cihazları"</string>
     <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Etkin"</string>
@@ -440,6 +429,17 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Ayarlar\'ı aç"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Diğer cihaz"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Genel bakışı aç/kapat"</string>
+    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Öncelik modları"</string>
+    <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Bitti"</string>
+    <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Ayarlar"</string>
+    <string name="zen_mode_on" msgid="9085304934016242591">"Açık"</string>
+    <string name="zen_mode_off" msgid="1736604456618147306">"Kapalı"</string>
+    <!-- no translation found for zen_mode_set_up (7457957033034460064) -->
+    <skip />
+    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
+    <skip />
+    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
+    <skip />
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Alarmlar, hatırlatıcılar, etkinlikler ve sizin seçtiğiniz kişilerden gelen çağrılar dışında hiçbir ses ve titreşimle rahatsız edilmeyeceksiniz. O sırada çaldığınız müzik, seyrettiğiniz video ya da oynadığınız oyunların sesini duymaya devam edeceksiniz."</string>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Alarmlar dışında hiçbir ses ve titreşimle rahatsız edilmeyeceksiniz. O sırada çaldığınız müzik, seyrettiğiniz video ya da oynadığınız oyunların sesini duymaya devam edeceksiniz."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Özelleştir"</string>
@@ -478,6 +478,8 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Yavaş şarj oluyor • Dolmasına <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> kaldı"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Şarj oluyor • Dolmasına <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> kaldı"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Kilit ekranındaki widget\'lar"</string>
+    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
+    <skip />
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Ortak eğitimi başlatmak için sola kaydırın"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Özelleştir"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Kapat"</string>
@@ -504,10 +506,9 @@
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"widget seçin"</string>
     <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"widget\'ı kaldır"</string>
     <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"seçilen widget\'ı yerleştir"</string>
-    <!-- no translation found for communal_widget_picker_title (1953369090475731663) -->
-    <skip />
-    <!-- no translation found for communal_widget_picker_description (490515450110487871) -->
-    <skip />
+    <string name="communal_widget_picker_title" msgid="1953369090475731663">"Kilit ekranı widget\'ları"</string>
+    <string name="communal_widget_picker_description" msgid="490515450110487871">"Kilit ekranınızdaki widget\'lar, tabletiniz kilitliyken bile herkes tarafından görüntülenebilir."</string>
+    <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"widget\'ın seçimini kaldırın"</string>
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Kilit ekranı widget\'ları"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Widget kullanarak bir uygulamayı açmak için kimliğinizi doğrulamanız gerekir. Ayrıca, tabletiniz kilitliyken bile widget\'ların herkes tarafından görüntülenebileceğini unutmayın. Bazı widget\'lar kilit ekranınız için tasarlanmamış olabileceğinden buraya eklenmeleri güvenli olmayabilir."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Anladım"</string>
@@ -564,8 +565,8 @@
     <string name="media_projection_action_text" msgid="3634906766918186440">"Şimdi başlat"</string>
     <string name="empty_shade_text" msgid="8935967157319717412">"Bildirim yok"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"Yeni bildirim yok"</string>
-    <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"Uyarlanabilir bildirimler açık"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"Cihazınız, kısa süre içinde çok sayıda bildirim aldığınızda artık iki dakika boyunca sesi kısar ve ekrandaki pop-up\'ları azaltır."</string>
+    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Bildirim şiddetini düşürme etkin"</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Aynı anda çok sayıda bildirim aldığınızda 2 dakika boyunca otomatik olarak cihazınızın sesi kısılır ve uyarıları azaltılır."</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Kapat"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Eski bildirimler için kilidi açın"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Bu cihaz ebeveyniniz tarafından yönetiliyor"</string>
@@ -732,8 +733,7 @@
     <string name="notification_alert_title" msgid="3656229781017543655">"Varsayılan"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Otomatik"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"Sessiz veya titreşim yok"</string>
-    <!-- no translation found for notification_conversation_summary_low (3855696451919728790) -->
-    <skip />
+    <string name="notification_conversation_summary_low" msgid="3855696451919728790">"Ses veya titreşim olmamasına rağmen görüşme bölümünde görünmeye devam ediyor"</string>
     <string name="notification_channel_summary_default" msgid="777294388712200605">"Cihaz ayarlarına bağlı olarak zili çalabilir veya titreyebilir"</string>
     <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Cihaz ayarlarına bağlı olarak zili çalabilir veya titreyebilir <xliff:g id="APP_NAME">%1$s</xliff:g> adlı uygulamadan görüşmeler varsayılan olarak baloncukla gösterilir."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Bu bildirimin ses çıkarması veya titreşmesi gerekip gerekmediğine sistem karar versin"</string>
@@ -790,8 +790,7 @@
     <string name="keyboard_key_page_up" msgid="173914303254199845">"Sayfa Yukarı"</string>
     <string name="keyboard_key_page_down" msgid="9035902490071829731">"Sayfa Aşağı"</string>
     <string name="keyboard_key_forward_del" msgid="5325501825762733459">"Delete"</string>
-    <!-- no translation found for keyboard_key_esc (6230365950511411322) -->
-    <skip />
+    <string name="keyboard_key_esc" msgid="6230365950511411322">"Esc"</string>
     <string name="keyboard_key_move_home" msgid="3496502501803911971">"Home"</string>
     <string name="keyboard_key_move_end" msgid="99190401463834854">"End"</string>
     <string name="keyboard_key_insert" msgid="4621692715704410493">"Insert"</string>
@@ -1233,7 +1232,6 @@
     <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>
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobil veri"</string>
-    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</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>
     <string name="mobile_data_poor_connection" msgid="819617772268371434">"Bağlantı zayıf"</string>
@@ -1374,8 +1372,7 @@
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Bölünmüş ekran"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Giriş"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Uygulama kısayolları"</string>
-    <!-- no translation found for shortcut_helper_category_current_app_shortcuts (4017840565974573628) -->
-    <skip />
+    <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Mevcut Uygulama"</string>
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Erişilebilirlik"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Klavye kısayolları"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Arama kısayolları"</string>
@@ -1388,13 +1385,27 @@
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Bitti"</string>
     <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Tebrikler!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Geri dön"</string>
-    <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"Geri dönmek için dokunmatik alanın herhangi bir yerinde üç parmağınızla sola veya sağa kaydırın."</string>
+    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
+    <skip />
+    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
+    <skip />
     <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Sağa ve sola hareket eden üç parmağın gösterildiği dokunmatik alan"</string>
     <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Geri hareketi animasyonunu gösteren cihaz ekranı"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Klavye aydınlatması"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Seviye %1$d / %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Ev Kontrolleri"</string>
     <string name="home_controls_dream_description" msgid="4644150952104035789">"Ekran koruyucu olarak ev kontrollerinize hızla erişin"</string>
-    <!-- no translation found for volume_undo_action (5815519725211877114) -->
-    <skip />
+    <string name="volume_undo_action" msgid="5815519725211877114">"Geri al"</string>
+    <string name="back_edu_toast_content" msgid="4530314597378982956">"Geri dönmek için dokunmatik alanda üç parmağınızla sola veya sağa kaydırın"</string>
+    <string name="home_edu_toast_content" msgid="3381071147871955415">"Ana sayfaya gitmek için dokunmatik alanda üç parmağınızla yukarı kaydırın"</string>
+    <string name="overview_edu_toast_content" msgid="5797030644017804518">"Son uygulamaları görüntülemek için dokunmatik alanda üç parmağınızla yukarı kaydırıp basılı tutun"</string>
+    <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Tüm uygulamalarınızı görüntülemek için klavyenizdeki eylem tuşuna basın"</string>
+    <string name="back_edu_notification_title" msgid="5624780717751357278">"Geri dönmek için dokunmatik alanınızı kullanın"</string>
+    <string name="back_edu_notification_content" msgid="2497557451540954068">"Üç parmağınızla sola veya sağa kaydırın. Daha fazla hareket öğrenmek için dokunun."</string>
+    <string name="home_edu_notification_title" msgid="6097902076909654045">"Ana sayfaya gitmek için dokunmatik alanınızı kullanın"</string>
+    <string name="home_edu_notification_content" msgid="6631697734535766588">"Üç parmağınızla yukarı kaydırın. Daha fazla hareket öğrenmek için dokunun."</string>
+    <string name="overview_edu_notification_title" msgid="1265824157319562406">"Son uygulamaları görüntülemek için dokunmatik alanınızı kullanın"</string>
+    <string name="overview_edu_notification_content" msgid="3578204677648432500">"Üç parmağınızla yukarı kaydırıp basılı tutun. Daha fazla hareket öğrenmek için dokunun."</string>
+    <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Tüm uygulamaları görüntülemek için klavyenizi kullanın"</string>
+    <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"İstediğiniz zaman eylem tuşuna basın. Daha fazla hareket öğrenmek için dokunun."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-tr/tiles_states_strings.xml b/packages/SystemUI/res/values-tr/tiles_states_strings.xml
index b26588b..1ed106f 100644
--- a/packages/SystemUI/res/values-tr/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-tr/tiles_states_strings.xml
@@ -56,9 +56,6 @@
     <item msgid="5376619709702103243">"Kapalı"</item>
     <item msgid="4875147066469902392">"Açık"</item>
   </string-array>
-    <!-- no translation found for tile_states_modes:0 (7764936419245199023) -->
-    <!-- no translation found for tile_states_modes:1 (2004750556637773692) -->
-    <!-- no translation found for tile_states_modes:2 (8968530753931637871) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Kullanılamıyor"</item>
     <item msgid="5044688398303285224">"Kapalı"</item>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index 3ebb787..7e72f7e 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -126,38 +126,25 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"Натисніть, щоб переглянути"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"Не вдалося зберегти запис відео з екрана"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Не вдалося почати запис екрана"</string>
-    <!-- no translation found for screenrecord_stop_dialog_title (8716193661764511095) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message (6262768207331626817) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5995770227684523244) -->
-    <skip />
+    <string name="screenrecord_stop_dialog_title" msgid="8716193661764511095">"Зупинити запис?"</string>
+    <string name="screenrecord_stop_dialog_message" msgid="6262768207331626817">"Ви зараз записуєте відео з усього екрана"</string>
+    <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Ви зараз записуєте відео з додатка <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Зупинити запис"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Показ екрана"</string>
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Зупинити показ екрана?"</string>
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen_with_host_app (522823522115375414) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen (5090115386271179270) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_specific (5923772039347985172) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_generic (6681016774654578261) -->
-    <skip />
+    <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Ви зараз ділитеся вмістом усього екрана з додатком <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
+    <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Ви зараз ділитеся вмістом усього екрана з додатком"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Ви зараз показуєте вікно додатка <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Ви зараз показуєте вікно додатка"</string>
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Зупинити показ"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Трансляція екрана"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Зупинити трансляцію?"</string>
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen_with_device (1474703115926205251) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen (8419219169553867625) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app_with_device (2715934698604085519) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (8616103075630934513) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic_with_device (9213582497852420203) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic (4100272100480415076) -->
-    <skip />
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen_with_device" msgid="1474703115926205251">"Ви зараз транслюєте весь екран на пристрій \"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>\""</string>
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen" msgid="8419219169553867625">"Ви зараз транслюєте весь екран на пристрій поблизу"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app_with_device" msgid="2715934698604085519">"Ви зараз транслюєте додаток <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> на пристрій \"<xliff:g id="DEVICE_NAME">%2$s</xliff:g>\""</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="8616103075630934513">"Ви зараз транслюєте додаток <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> на пристрій поблизу"</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic_with_device" msgid="9213582497852420203">"Ви зараз транслюєте контент на пристрій \"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>\""</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic" msgid="4100272100480415076">"Ви зараз транслюєте контент на пристрій поблизу"</string>
     <string name="cast_to_other_device_stop_dialog_button" msgid="6420183747435521834">"Припинити трансляцію"</string>
     <string name="close_dialog_button" msgid="4749497706540104133">"Закрити"</string>
     <string name="issuerecord_title" msgid="286627115110121849">"Засіб запису проблем"</string>
@@ -303,8 +290,7 @@
     <string name="start_dreams" msgid="9131802557946276718">"Заставка"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"Не турбувати"</string>
-    <!-- no translation found for quick_settings_modes_label (5407025818652750501) -->
-    <skip />
+    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Режими пріоритету"</string>
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Немає спарених пристроїв"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Натисніть, щоб під’єднати або від’єднати пристрій"</string>
@@ -402,6 +388,9 @@
     <string name="performance" msgid="6552785217174378320">"Продуктивність"</string>
     <string name="user_interface" msgid="3712869377953950887">"Інтерфейс користувача"</string>
     <string name="thermal" msgid="6758074791325414831">"Нагрівання"</string>
+    <string name="custom" msgid="3337456985275158299">"Власні"</string>
+    <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Власні налаштування трасування"</string>
+    <string name="restore_default" msgid="5259420807486239755">"Відновити налаштування за умовчанням"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Режим керування однією рукою"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Слухові апарати"</string>
     <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Під’єднано"</string>
@@ -440,6 +429,17 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Відкрити налаштування"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Інший пристрій"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Увімкнути або вимкнути огляд"</string>
+    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Режими пріоритету"</string>
+    <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_off" msgid="1736604456618147306">"Вимкнено"</string>
+    <!-- no translation found for zen_mode_set_up (7457957033034460064) -->
+    <skip />
+    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
+    <skip />
+    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
+    <skip />
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Ви отримуватиме звукові та вібросигнали лише для вибраних будильників, нагадувань, подій і абонентів. Однак ви чутимете все, що захочете відтворити, зокрема музику, відео й ігри."</string>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Ви отримуватиме звукові та вібросигнали лише для будильників. Однак ви чутимете все, що захочете відтворити, зокрема музику, відео й ігри."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Налаштувати"</string>
@@ -478,6 +478,8 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Повільне заряджання • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> до повного заряду"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Заряджання • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> до повного заряду"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Віджети на заблокованому екрані"</string>
+    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
+    <skip />
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Проведіть пальцем уліво, щоб відкрити спільний навчальний посібник"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Налаштувати"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Закрити"</string>
@@ -504,10 +506,9 @@
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"виберіть віджет"</string>
     <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"видалити віджет"</string>
     <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"розмістити вибраний віджет"</string>
-    <!-- no translation found for communal_widget_picker_title (1953369090475731663) -->
-    <skip />
-    <!-- no translation found for communal_widget_picker_description (490515450110487871) -->
-    <skip />
+    <string name="communal_widget_picker_title" msgid="1953369090475731663">"Віджети для заблокованого екрана"</string>
+    <string name="communal_widget_picker_description" msgid="490515450110487871">"Будь-хто бачитиме віджети навіть на заблокованому екрані планшета."</string>
+    <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"скасувати вибір віджета"</string>
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Віджети для заблокованого екрана"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Щоб відкрити додаток за допомогою віджета, вам потрібно буде підтвердити особу. Пам’ятайте також, що бачити віджети можуть усі, навіть коли планшет заблоковано. Можливо, деякі віджети не призначені для заблокованого екрана, і додавати їх на нього може бути небезпечно."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"OK"</string>
@@ -564,8 +565,8 @@
     <string name="media_projection_action_text" msgid="3634906766918186440">"Почати зараз"</string>
     <string name="empty_shade_text" msgid="8935967157319717412">"Сповіщень немає"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"Немає нових сповіщень"</string>
-    <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"Адаптивні сповіщення ввімкнено"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"Пристрій знижує гучність і зменшує кількість спливаючих вікон на екрані на період до двох хвилин, коли ви отримуєте багато сповіщень за короткий час."</string>
+    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Зниження гучності сповіщень увімкнено"</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Коли ви отримуєте забагато сповіщень за раз, пристрій автоматично знижує їх гучність і кількість на період до 2 хвилин."</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Вимкнути"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Розблокуйте, щоб переглянути старіші"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Цим пристроєм керує батько або мати"</string>
@@ -732,8 +733,7 @@
     <string name="notification_alert_title" msgid="3656229781017543655">"За умовчанням"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Автоматично"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"Без звуку чи вібрації"</string>
-    <!-- no translation found for notification_conversation_summary_low (3855696451919728790) -->
-    <skip />
+    <string name="notification_conversation_summary_low" msgid="3855696451919728790">"Без звуку чи вібрації, але сповіщення відображається в розділі розмов"</string>
     <string name="notification_channel_summary_default" msgid="777294388712200605">"Дзвінок або вібрація залежно від налаштувань пристрою"</string>
     <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Може дзвонити або вібрувати залежно від налаштувань пристрою. Показує спливаючі розмови з додатка <xliff:g id="APP_NAME">%1$s</xliff:g> за умовчанням."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Дозволити системі визначати, чи має сповіщення супроводжуватися звуком або вібрацією"</string>
@@ -790,8 +790,7 @@
     <string name="keyboard_key_page_up" msgid="173914303254199845">"Сторінка вгору"</string>
     <string name="keyboard_key_page_down" msgid="9035902490071829731">"Сторінка вниз"</string>
     <string name="keyboard_key_forward_del" msgid="5325501825762733459">"Delete"</string>
-    <!-- no translation found for keyboard_key_esc (6230365950511411322) -->
-    <skip />
+    <string name="keyboard_key_esc" msgid="6230365950511411322">"Esc"</string>
     <string name="keyboard_key_move_home" msgid="3496502501803911971">"Home"</string>
     <string name="keyboard_key_move_end" msgid="99190401463834854">"End"</string>
     <string name="keyboard_key_insert" msgid="4621692715704410493">"Insert"</string>
@@ -1233,7 +1232,6 @@
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Пройдіть автентифікацію. Для цього торкніться сканера відбитків пальців."</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Активний телефонний виклик"</string>
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Мобільний трафік"</string>
-    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Підключено"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Тимчасово з’єднано"</string>
     <string name="mobile_data_poor_connection" msgid="819617772268371434">"Погане з’єднання"</string>
@@ -1374,8 +1372,7 @@
     <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>
-    <!-- no translation found for shortcut_helper_category_current_app_shortcuts (4017840565974573628) -->
-    <skip />
+    <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_search_placeholder" msgid="5488547526269871819">"Комбінації клавіш для пошуку"</string>
@@ -1388,13 +1385,27 @@
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Готово"</string>
     <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Чудово!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Назад"</string>
-    <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"Щоб перейти назад, проведіть трьома пальцями вліво або вправо по сенсорній панелі."</string>
+    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
+    <skip />
+    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
+    <skip />
     <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Сенсорна панель із зображенням трьох пальців, що рухаються вправо й уліво"</string>
     <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Екран пристрою, на якому показано анімацію щодо жесту \"Назад\""</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Підсвічування клавіатури"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Рівень %1$d з %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Автоматизація дому"</string>
     <string name="home_controls_dream_description" msgid="4644150952104035789">"Швидкий доступ до керування домом через заставку"</string>
-    <!-- no translation found for volume_undo_action (5815519725211877114) -->
-    <skip />
+    <string name="volume_undo_action" msgid="5815519725211877114">"Відмінити"</string>
+    <string name="back_edu_toast_content" msgid="4530314597378982956">"Щоб перейти назад, проведіть трьома пальцями вліво або вправо по сенсорній панелі"</string>
+    <string name="home_edu_toast_content" msgid="3381071147871955415">"Щоб перейти на головний екран, проведіть трьома пальцями вгору по сенсорній панелі"</string>
+    <string name="overview_edu_toast_content" msgid="5797030644017804518">"Щоб переглянути останні додатки, проведіть трьома пальцями вгору по сенсорній панелі й утримуйте їх"</string>
+    <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Щоб переглянути всі додатки, натисніть клавішу дії на клавіатурі"</string>
+    <string name="back_edu_notification_title" msgid="5624780717751357278">"Щоб повернутися, використовуйте сенсорну панель"</string>
+    <string name="back_edu_notification_content" msgid="2497557451540954068">"Проведіть трьома пальцями вліво чи вправо. Натисніть, щоб дізнатися про інші жести."</string>
+    <string name="home_edu_notification_title" msgid="6097902076909654045">"Щоб перейти на головний екран, використовуйте сенсорну панель"</string>
+    <string name="home_edu_notification_content" msgid="6631697734535766588">"Проведіть трьома пальцями вгору. Натисніть, щоб дізнатися про інші жести."</string>
+    <string name="overview_edu_notification_title" msgid="1265824157319562406">"Щоб переглянути останні додатки, використовуйте сенсорну панель"</string>
+    <string name="overview_edu_notification_content" msgid="3578204677648432500">"Проведіть трьома пальцями вгору й утримуйте їх на екрані. Натисніть, щоб дізнатися про інші жести."</string>
+    <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Щоб переглянути всі додатки, використовуйте клавіатуру"</string>
+    <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Будь-коли натисніть клавішу дії. Натисніть, щоб дізнатися про інші жести."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-uk/tiles_states_strings.xml b/packages/SystemUI/res/values-uk/tiles_states_strings.xml
index 2d86916..61e62e4 100644
--- a/packages/SystemUI/res/values-uk/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-uk/tiles_states_strings.xml
@@ -56,9 +56,6 @@
     <item msgid="5376619709702103243">"Вимкнено"</item>
     <item msgid="4875147066469902392">"Увімкнено"</item>
   </string-array>
-    <!-- no translation found for tile_states_modes:0 (7764936419245199023) -->
-    <!-- no translation found for tile_states_modes:1 (2004750556637773692) -->
-    <!-- no translation found for tile_states_modes:2 (8968530753931637871) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Недоступно"</item>
     <item msgid="5044688398303285224">"Вимкнено"</item>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index fe8eb4d..94586b7 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -388,6 +388,9 @@
     <string name="performance" msgid="6552785217174378320">"کارکردگی"</string>
     <string name="user_interface" msgid="3712869377953950887">"یوزر انٹرفیس"</string>
     <string name="thermal" msgid="6758074791325414831">"تھرمل"</string>
+    <string name="custom" msgid="3337456985275158299">"حسب ضرورت"</string>
+    <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"حسب ضرورت ٹریس کی ترتیبات"</string>
+    <string name="restore_default" msgid="5259420807486239755">"ڈیفالٹ بحال کریں"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"ایک ہاتھ کی وضع"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"سماعت کے آلات"</string>
     <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"فعال"</string>
@@ -426,6 +429,17 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"ترتیبات کھولیں"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"دوسرا آلہ"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"مجموعی جائزہ ٹوگل کریں"</string>
+    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"ترجیحی وضع"</string>
+    <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_off" msgid="1736604456618147306">"آف ہے"</string>
+    <!-- no translation found for zen_mode_set_up (7457957033034460064) -->
+    <skip />
+    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
+    <skip />
+    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
+    <skip />
     <string name="zen_priority_introduction" msgid="3159291973383796646">"الارمز، یاددہانیوں، ایونٹس اور آپ کے متعین کردہ کالرز کے علاوہ، آپ آوازوں اور وائبریشنز سے ڈسٹرب نہیں ہوں گے۔ موسیقی، ویڈیوز اور گیمز سمیت آپ ابھی بھی ہر وہ چیز سنیں گے جسے چلانے کا آپ انتخاب کرتے ہیں۔"</string>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"الارمز کے علاوہ، آپ آوازوں اور وائبریشنز سے ڈسٹرب نہیں ہوں گے۔ موسیقی، ویڈیوز اور گیمز سمیت آپ ہر وہ چیز سنیں گے جسے چلانے کا آپ انتخاب کرتے ہیں۔"</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"حسب ضرورت بنائیں"</string>
@@ -464,6 +478,8 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • آہستہ چارج ہو رہا ہے • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> میں مکمل"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • چارج ہو رہا ہے • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> میں مکمل"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"مقفل اسکرین پر ویجیٹس"</string>
+    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
+    <skip />
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"کمیونل ٹیوٹوریل شروع کرنے کے لیے بائیں سوائپ کریں"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"حسب ضرورت بنائیں"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"برخاست کریں"</string>
@@ -492,6 +508,7 @@
     <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"منتخب ویجیٹ رکھیں"</string>
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"مقفل اسکرین کے ویجیٹس"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"کوئی بھی آپ کی مقفل اسکرین پر ویجیٹ دیکھ سکتا ہے اگرچہ آپ کا ٹیبلیٹ مقفل ہو۔"</string>
+    <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"ویجیٹ غیر منتخب کریں"</string>
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"مقفل اسکرین کے ویجیٹس"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"ویجیٹ کے ذریعے ایپ کھولنے کے لیے آپ کو تصدیق کرنی ہوگی کہ یہ آپ ہی ہیں۔ نیز، ذہن میں رکھیں کہ کوئی بھی انہیں دیکھ سکتا ہے، یہاں تک کہ جب آپ کا ٹیبلیٹ مقفل ہو۔ ہو سکتا ہے کچھ ویجٹس آپ کی لاک اسکرین کے لیے نہ بنائے گئے ہوں اور یہاں شامل کرنا غیر محفوظ ہو سکتا ہے۔"</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"سمجھ آ گئی"</string>
@@ -548,8 +565,8 @@
     <string name="media_projection_action_text" msgid="3634906766918186440">"ابھی شروع کریں"</string>
     <string name="empty_shade_text" msgid="8935967157319717412">"کوئی اطلاعات نہیں ہیں"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"کوئی نئی اطلاعات نہیں"</string>
-    <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"تغیر پذیر اطلاعات آن ہیں"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"کم وقت میں متعدد اطلاعات ملنے پر آپ کا آلہ اب دو منٹ تک سکرین پر والیوم اور پوپ اپس کم کر دیتا ہے۔"</string>
+    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"ںوٹیفیکیشن کول ڈاؤن آن ہے"</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"جب آپ کو ایک ساتھ بہت زیادہ اطلاعات موصول ہوتی ہیں تو آپ کے آلے کا والیوم اور الرٹس خودکار طور پر 2 منٹ تک کم ہو جاتے ہیں۔"</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"آف کریں"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"پرانی اطلاعات دیکھنے کیلئے غیر مقفل کریں"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"یہ آلہ آپ کے والدین کے زیر انتظام ہے"</string>
@@ -720,8 +737,7 @@
     <string name="notification_alert_title" msgid="3656229781017543655">"ڈیفالٹ"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"خودکار"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"کوئی آواز یا وائبریشن نہیں"</string>
-    <!-- no translation found for notification_conversation_summary_low (3855696451919728790) -->
-    <skip />
+    <string name="notification_conversation_summary_low" msgid="3855696451919728790">"کوئی آواز یا وائبریشن نہیں ہے لیکن پھر بھی گفتگو کے سیکشن میں ظاہر ہوتا ہے"</string>
     <string name="notification_channel_summary_default" msgid="777294388712200605">"آلے کی ترتیبات کی بنیاد پر وائبریٹ یا گھنٹی بج سکتی ہے"</string>
     <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"آلے کی ترتیبات بنیاد پر وائبریٹ یا گھنٹی بج سکتی ہے۔ بذریعہ ڈیفالٹ <xliff:g id="APP_NAME">%1$s</xliff:g> بلبلہ سے گفتگوئیں۔"</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"سسٹم کو اس بات کا تعین کرنے دیں کہ آیا اس اطلاع کی آواز ہو یا وائبریٹ ہونا چاہیے"</string>
@@ -1220,7 +1236,6 @@
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"توثیق مطلوب ہے۔ توثیق کرنے کے لیے فنگر پرنٹ سینسر کو ٹچ کریں۔"</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"جاری فون کال"</string>
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"موبائل ڈیٹا"</string>
-    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="NETWORKMODE">%2$s</xliff:g> / <xliff:g id="STATE">%1$s</xliff:g>"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"منسلک ہے"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"عارضی طور پر منسلک ہے"</string>
     <string name="mobile_data_poor_connection" msgid="819617772268371434">"کمزور کنکشن"</string>
@@ -1374,7 +1389,10 @@
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"ہو گیا"</string>
     <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"بہترین!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"واپس جائیں"</string>
-    <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"واپس جانے کے لیے، ٹچ پیڈ پر کہیں بھی تین انگلیوں کی مدد سے دائیں یا بائیں سوائپ کریں۔"</string>
+    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
+    <skip />
+    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
+    <skip />
     <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"ٹچ پیڈ دائیں اور بائیں حرکت کرتی ہوئی تین انگلیاں دکھا رہا ہے"</string>
     <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"آلہ کی اسکرین پیچھے جانے کے اشارے کے لیے اینیمیشن دکھا رہی ہے"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"کی بورڈ بیک لائٹ"</string>
@@ -1382,4 +1400,16 @@
     <string name="home_controls_dream_label" msgid="6567105701292324257">"ہوم کنٹرولز"</string>
     <string name="home_controls_dream_description" msgid="4644150952104035789">"اسکرین سیور کے بطور اپنے ہوم کنٹرولز تک فوری رسائی حاصل کریں"</string>
     <string name="volume_undo_action" msgid="5815519725211877114">"کالعدم کریں"</string>
+    <string name="back_edu_toast_content" msgid="4530314597378982956">"واپس جانے کے لیے، ٹچ پیڈ پر تین انگلیوں کی مدد سے بائیں یا دائیں سوائپ کریں"</string>
+    <string name="home_edu_toast_content" msgid="3381071147871955415">"ہوم پر جانے کے لیے، ٹچ پیڈ پر تین انگلیوں کی مدد سے اوپر سوائپ کریں"</string>
+    <string name="overview_edu_toast_content" msgid="5797030644017804518">"حالیہ ایپس دیکھنے کے لیے، ٹچ پیڈ پر تین انگلیوں سے اوپر سوائپ کریں اور دبائے رکھیں"</string>
+    <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"اپنی سبھی ایپس دیکھنے کے لیے، اپنے کی بورڈ پر ایکشن کلید دبائیں"</string>
+    <string name="back_edu_notification_title" msgid="5624780717751357278">"واپس جانے کے لیے اپنے ٹچ پیڈ کا استعمال کریں"</string>
+    <string name="back_edu_notification_content" msgid="2497557451540954068">"تین انگلیوں سے دائیں یا بائیں طرف سوائپ کریں۔ مزید اشارے جاننے کے لیے تھپتھپائیں۔"</string>
+    <string name="home_edu_notification_title" msgid="6097902076909654045">"ہوم پر جانے کے لیے اپنے ٹچ پیڈ کا استعمال کریں"</string>
+    <string name="home_edu_notification_content" msgid="6631697734535766588">"تین انگلیوں سے اوپر سوائپ کریں۔ مزید اشارے جاننے کے لیے تھپتھپائیں۔"</string>
+    <string name="overview_edu_notification_title" msgid="1265824157319562406">"حالیہ ایپس دیکھنے کے لیے اپنے ٹچ پیڈ کا استعمال کریں"</string>
+    <string name="overview_edu_notification_content" msgid="3578204677648432500">"تین انگلیوں سے اوپر کی طرف سوائپ کریں اور دبائے رکھیں۔ مزید اشارے جاننے کے لیے تھپتھپائیں۔"</string>
+    <string name="all_apps_edu_notification_title" msgid="372262997265569063">"سبھی ایپس دیکھنے کے لیے اپنے کی بورڈ کا استعمال کریں"</string>
+    <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"کسی بھی وقت ایکشن کلید دبائیں۔ مزید اشارے جاننے کے لیے تھپتھپائیں۔"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ur/tiles_states_strings.xml b/packages/SystemUI/res/values-ur/tiles_states_strings.xml
index e398b2f..ebbc30e 100644
--- a/packages/SystemUI/res/values-ur/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ur/tiles_states_strings.xml
@@ -56,11 +56,6 @@
     <item msgid="5376619709702103243">"آف ہے"</item>
     <item msgid="4875147066469902392">"آن ہے"</item>
   </string-array>
-  <string-array name="tile_states_modes">
-    <item msgid="7764936419245199023">"دستیاب نہیں ہے"</item>
-    <item msgid="2004750556637773692">"آف ہے"</item>
-    <item msgid="8968530753931637871">"آن ہے"</item>
-  </string-array>
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"دستیاب نہیں ہے"</item>
     <item msgid="5044688398303285224">"آف ہے"</item>
diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml
index 7e9466c..3f60776 100644
--- a/packages/SystemUI/res/values-uz/strings.xml
+++ b/packages/SystemUI/res/values-uz/strings.xml
@@ -290,8 +290,7 @@
     <string name="start_dreams" msgid="9131802557946276718">"Ekran lavhasi"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"Bezovta qilinmasin"</string>
-    <!-- no translation found for quick_settings_modes_label (5407025818652750501) -->
-    <skip />
+    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Muhim rejimlar"</string>
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Ulangan qurilmalar topilmadi"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Qurilma ulash yoki uzish uchun tegining"</string>
@@ -389,6 +388,9 @@
     <string name="performance" msgid="6552785217174378320">"Unumdorlik"</string>
     <string name="user_interface" msgid="3712869377953950887">"Foydalanuvchi interfeysi"</string>
     <string name="thermal" msgid="6758074791325414831">"Termal"</string>
+    <string name="custom" msgid="3337456985275158299">"Maxsus"</string>
+    <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Maxsus trassirovka sozlamalari"</string>
+    <string name="restore_default" msgid="5259420807486239755">"Asliga qaytarish"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Ixcham rejim"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Eshitish qurilmalari"</string>
     <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Faol"</string>
@@ -427,6 +429,17 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Sozlamalarni ochish"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Boshqa qurilma"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Umumiy nazar rejimini almashtirish"</string>
+    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Muhim rejimlar"</string>
+    <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Tayyor"</string>
+    <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Sozlamalar"</string>
+    <string name="zen_mode_on" msgid="9085304934016242591">"Yoniq"</string>
+    <string name="zen_mode_off" msgid="1736604456618147306">"Yoqilmagan"</string>
+    <!-- no translation found for zen_mode_set_up (7457957033034460064) -->
+    <skip />
+    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
+    <skip />
+    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
+    <skip />
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Turli ovoz va tebranishlar endi sizni bezovta qilmaydi. Biroq, signallar, eslatmalar, tadbirlar haqidagi bildirishnomalar va siz tanlagan abonentlardan kelgan chaqiruvlar bundan mustasno. Lekin, ijro etiladigan barcha narsalar, jumladan, musiqa, video va o‘yinlar ovozi eshitiladi."</string>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Turli ovoz va tebranishlar endi sizni bezovta qilmaydi. Biroq, signallar bundan mustasno. Lekin, ijro etiladigan barcha narsalar, jumladan, musiqa, video va o‘yinlar ovozi eshitiladi."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Sozlash"</string>
@@ -465,6 +478,8 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Sekin quvvat olmoqda • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> qoldi"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Quvvat olmoqda • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> qoldi"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Ekran qulfidagi vidjetlar"</string>
+    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
+    <skip />
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Qoʻllanma bilan tanishish uchun chapga suring"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Moslash"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Yopish"</string>
@@ -493,6 +508,7 @@
     <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"tanlangan vidjetni joylash"</string>
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"Ekran qulfi vidjetlari"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"Ekran quflidagi vidjetlar hammaga koʻrinadi, hatto planshet qulflanganda ham."</string>
+    <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"vidjetni bekor qilish"</string>
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Ekran qulfi vidjetlari"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Ilovani vidjet orqali ochish uchun shaxsingizni tasdiqlashingiz kerak. Shuningdek, planshet qulflanganda ham bu axborotlar hammaga koʻrinishini unutmang. Ayrim vidjetlar ekran qulfiga moslanmagan va ularni bu yerda chiqarish xavfli boʻlishi mumkin."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"OK"</string>
@@ -549,8 +565,8 @@
     <string name="media_projection_action_text" msgid="3634906766918186440">"Boshlash"</string>
     <string name="empty_shade_text" msgid="8935967157319717412">"Bildirishnomalar yo‘q"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"Yangi bildirishoma yoʻq"</string>
-    <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"Adaptiv bildirishnomalar yoniq"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"Qisqa vaqt oraligʻida koʻp bildirishnoma kelsa, qurilmadagi tovush balandligi hamda bildirgi oynalar soni ikki daqiqagacha kamaytiriladi."</string>
+    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Bildirishnomalarni sekinlatish yoniq"</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Bir vaqtda juda koʻp bildirishnoma olsangiz, qurilmangiz tovushi va ogohlantirishlar 2 daqiqagacha avtomatik pasaytiriladi."</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Faolsizlantirish"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Eskilarini koʻrish uchun qulfni yeching"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Bu qurilmani ota-onangiz boshqaradi"</string>
@@ -717,8 +733,7 @@
     <string name="notification_alert_title" msgid="3656229781017543655">"Standart"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Avtomatik"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"Tovush yoki tebranishsiz"</string>
-    <!-- no translation found for notification_conversation_summary_low (3855696451919728790) -->
-    <skip />
+    <string name="notification_conversation_summary_low" msgid="3855696451919728790">"Ovoz yoki tebranishsiz, ammo suhbatlar ruknida chiqaveradi"</string>
     <string name="notification_channel_summary_default" msgid="777294388712200605">"Qurilma sozlamalari asosida jiringlashi yoki tebranishi mumkin"</string>
     <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Qurilma sozlamalari asosida jiringlashi yoki tebranishi mumkin. <xliff:g id="APP_NAME">%1$s</xliff:g> suhbatlari standart holatda bulutcha shaklida chiqadi."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Bu bildirishnoma jiringlashi yoki tebranishini hal qilsin"</string>
@@ -1217,7 +1232,6 @@
     <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>
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobil internet"</string>
-    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Ulangan"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Vaqtincha ulangan"</string>
     <string name="mobile_data_poor_connection" msgid="819617772268371434">"Aloqa beqaror"</string>
@@ -1358,8 +1372,7 @@
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Ekranni ikkiga ajratish"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Kiritish"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Ilova yorliqlari"</string>
-    <!-- no translation found for shortcut_helper_category_current_app_shortcuts (4017840565974573628) -->
-    <skip />
+    <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Joriy ilova"</string>
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Qulayliklar"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Tezkor tugmalar"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Tezkor tugmalar qidiruvi"</string>
@@ -1372,13 +1385,27 @@
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Tayyor"</string>
     <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Barakalla!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Orqaga qaytish"</string>
-    <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"Ortga qaytish uchun sensorli panelda uchta barmoqni chapga yoki oʻngga suring."</string>
+    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
+    <skip />
+    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
+    <skip />
     <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Sensorli panelda uchta barmoq chapga va oʻngga harakatlanishi"</string>
     <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Qurilma ekranidagi ortga qaytish ishorasi animatsiyasi"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Klaviatura orqa yoritkichi"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Daraja: %1$d / %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Uy boshqaruvi"</string>
     <string name="home_controls_dream_description" msgid="4644150952104035789">"Uy boshqaruvi tugmalarini ekran lavhasida tezkor oching"</string>
-    <!-- no translation found for volume_undo_action (5815519725211877114) -->
-    <skip />
+    <string name="volume_undo_action" msgid="5815519725211877114">"Bekor qilish"</string>
+    <string name="back_edu_toast_content" msgid="4530314597378982956">"Orqaga qaytish uchun sensorli panelda uchta barmoq bilan chapga yoki oʻngga suring"</string>
+    <string name="home_edu_toast_content" msgid="3381071147871955415">"Bosh ekranga qaytish uchun sensorli panelda uchta barmoq bilan chapga yoki oʻngga suring"</string>
+    <string name="overview_edu_toast_content" msgid="5797030644017804518">"Oxirgi ilovalarni koʻrish uchun sensorli panelda uchta barmoq bilan tepaga surib, bosib turing"</string>
+    <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Barcha ishoralarni koʻrish uchun klaviaturadagi amal tugmasini bosing"</string>
+    <string name="back_edu_notification_title" msgid="5624780717751357278">"Sensorli panel orqali orqaga qaytish"</string>
+    <string name="back_edu_notification_content" msgid="2497557451540954068">"Uchta barmoq bilan chapga yoki oʻngga suring. Boshqa ishoralar bilan tanishish uchun bosing."</string>
+    <string name="home_edu_notification_title" msgid="6097902076909654045">"Sensorli panel orqali bosh ekranga qaytish"</string>
+    <string name="home_edu_notification_content" msgid="6631697734535766588">"Uchta barmoq bilan tepaga suring. Boshqa ishoralar bilan tanishish uchun bosing."</string>
+    <string name="overview_edu_notification_title" msgid="1265824157319562406">"Sensorli panel orqali oxirgi ilovalarni koʻrish"</string>
+    <string name="overview_edu_notification_content" msgid="3578204677648432500">"Uchta barmoq bilan tepaga surib, bosib turing. Boshqa ishoralar bilan tanishish uchun bosing."</string>
+    <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Klaviatura orqali barcha ilovalarni koʻrish"</string>
+    <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Amal tugmasini istalganda bosing. Boshqa ishoralar bilan tanishish uchun bosing."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-uz/tiles_states_strings.xml b/packages/SystemUI/res/values-uz/tiles_states_strings.xml
index da9c98f..2ae81123 100644
--- a/packages/SystemUI/res/values-uz/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-uz/tiles_states_strings.xml
@@ -56,9 +56,6 @@
     <item msgid="5376619709702103243">"Oʻchiq"</item>
     <item msgid="4875147066469902392">"Yoniq"</item>
   </string-array>
-    <!-- no translation found for tile_states_modes:0 (7764936419245199023) -->
-    <!-- no translation found for tile_states_modes:1 (2004750556637773692) -->
-    <!-- no translation found for tile_states_modes:2 (8968530753931637871) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Ishlamaydi"</item>
     <item msgid="5044688398303285224">"Oʻchiq"</item>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index 42909c9..dcf8051 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -126,38 +126,25 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"Nhấn để xem"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"Có lỗi xảy ra khi lưu video ghi màn hình"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Lỗi khi bắt đầu ghi màn hình"</string>
-    <!-- no translation found for screenrecord_stop_dialog_title (8716193661764511095) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message (6262768207331626817) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5995770227684523244) -->
-    <skip />
+    <string name="screenrecord_stop_dialog_title" msgid="8716193661764511095">"Dừng ghi?"</string>
+    <string name="screenrecord_stop_dialog_message" msgid="6262768207331626817">"Bạn đang ghi lại toàn bộ nội dung trên màn hình"</string>
+    <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Bạn đang ghi lại nội dung của <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Dừng ghi"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Đang chia sẻ màn hình"</string>
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Dừng chia sẻ màn hình?"</string>
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen_with_host_app (522823522115375414) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen (5090115386271179270) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_specific (5923772039347985172) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_generic (6681016774654578261) -->
-    <skip />
+    <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Bạn đang chia sẻ toàn bộ nội dung trên màn hình với <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
+    <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Bạn đang chia sẻ toàn bộ nội dung trên màn hình với một ứng dụng"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Bạn đang chia sẻ nội dung của <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Bạn đang chia sẻ một ứng dụng"</string>
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Dừng chia sẻ"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Đang truyền màn hình"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Dừng truyền?"</string>
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen_with_device (1474703115926205251) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen (8419219169553867625) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app_with_device (2715934698604085519) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (8616103075630934513) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic_with_device (9213582497852420203) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic (4100272100480415076) -->
-    <skip />
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen_with_device" msgid="1474703115926205251">"Bạn đang truyền toàn bộ nội dung trên màn hình đến <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen" msgid="8419219169553867625">"Bạn đang truyền toàn bộ nội dung trên màn hình đến một thiết bị ở gần"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app_with_device" msgid="2715934698604085519">"Bạn đang truyền nội dung của <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> đến <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="8616103075630934513">"Bạn đang truyền nội dung của <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> đến một thiết bị ở gần"</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic_with_device" msgid="9213582497852420203">"Bạn đang truyền đến <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic" msgid="4100272100480415076">"Bạn đang truyền đến một thiết bị ở gần"</string>
     <string name="cast_to_other_device_stop_dialog_button" msgid="6420183747435521834">"Dừng truyền"</string>
     <string name="close_dialog_button" msgid="4749497706540104133">"Đóng"</string>
     <string name="issuerecord_title" msgid="286627115110121849">"Trình ghi sự cố"</string>
@@ -303,8 +290,7 @@
     <string name="start_dreams" msgid="9131802557946276718">"Trình bảo vệ m.hình"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"Không làm phiền"</string>
-    <!-- no translation found for quick_settings_modes_label (5407025818652750501) -->
-    <skip />
+    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Chế độ ưu tiên"</string>
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Không có thiết bị nào được ghép nối"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Nhấn để kết nối/ngắt kết nối với một thiết bị"</string>
@@ -402,6 +388,9 @@
     <string name="performance" msgid="6552785217174378320">"Hiệu suất"</string>
     <string name="user_interface" msgid="3712869377953950887">"Giao diện người dùng"</string>
     <string name="thermal" msgid="6758074791325414831">"Nhiệt"</string>
+    <string name="custom" msgid="3337456985275158299">"Tuỳ chỉnh"</string>
+    <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Cài đặt dấu vết tuỳ chỉnh"</string>
+    <string name="restore_default" msgid="5259420807486239755">"Khôi phục giá trị mặc định"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Chế độ một tay"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Thiết bị trợ thính"</string>
     <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Đang hoạt động"</string>
@@ -440,6 +429,17 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Mở phần Cài đặt"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Thiết bị khác"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Bật/tắt chế độ xem Tổng quan"</string>
+    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Chế độ ưu tiên"</string>
+    <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Xong"</string>
+    <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Cài đặt"</string>
+    <string name="zen_mode_on" msgid="9085304934016242591">"Đang bật"</string>
+    <string name="zen_mode_off" msgid="1736604456618147306">"Đang tắt"</string>
+    <!-- no translation found for zen_mode_set_up (7457957033034460064) -->
+    <skip />
+    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
+    <skip />
+    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
+    <skip />
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Bạn sẽ không bị làm phiền bởi âm thanh và tiếng rung, ngoại trừ báo thức, lời nhắc, sự kiện và người gọi mà bạn chỉ định. Bạn sẽ vẫn nghe thấy mọi thứ bạn chọn phát, bao gồm nhạc, video và trò chơi."</string>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Bạn sẽ không bị làm phiền bởi âm thanh và tiếng rung, ngoại trừ báo thức. Bạn sẽ vẫn nghe thấy mọi thứ bạn chọn phát, bao gồm nhạc, video và trò chơi."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Tùy chỉnh"</string>
@@ -478,6 +478,8 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Đang sạc chậm • Sẽ đầy sau <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Đang sạc • Sẽ đầy sau <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Các tiện ích trên màn hình khoá"</string>
+    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
+    <skip />
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Vuốt sang trái để bắt đầu xem hướng dẫn chung"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Tuỳ chỉnh"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Đóng"</string>
@@ -504,10 +506,9 @@
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"chọn tiện ích"</string>
     <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"xoá tiện ích"</string>
     <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"đặt tiện ích đã chọn vào vị trí"</string>
-    <!-- no translation found for communal_widget_picker_title (1953369090475731663) -->
-    <skip />
-    <!-- no translation found for communal_widget_picker_description (490515450110487871) -->
-    <skip />
+    <string name="communal_widget_picker_title" msgid="1953369090475731663">"Tiện ích trên màn hình khoá"</string>
+    <string name="communal_widget_picker_description" msgid="490515450110487871">"Ai cũng thấy được các tiện ích trên màn hình khoá, dù bạn đã khoá máy tính bảng."</string>
+    <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"bỏ chọn tiện ích"</string>
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Tiện ích trên màn hình khoá"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Để dùng tiện ích mở một ứng dụng, bạn cần xác minh danh tính của mình. Ngoài ra, hãy lưu ý rằng bất kỳ ai cũng có thể xem các tiện ích này, ngay cả khi máy tính bảng của bạn được khoá. Một số tiện ích có thể không dành cho màn hình khoá và không an toàn khi thêm vào đây."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Tôi hiểu"</string>
@@ -564,8 +565,8 @@
     <string name="media_projection_action_text" msgid="3634906766918186440">"Bắt đầu ngay"</string>
     <string name="empty_shade_text" msgid="8935967157319717412">"Không có thông báo nào"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"Không có thông báo mới"</string>
-    <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"Thông báo thích ứng đang bật"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"Giờ đây, thiết bị sẽ giảm âm lượng và giảm số cửa sổ bật lên trên màn hình trong tối đa 2 phút khi bạn nhận được nhiều thông báo trong một khoảng thời gian ngắn."</string>
+    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Chế độ Giảm dần âm lượng thông báo đang bật"</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Khi bạn nhận quá nhiều thông báo cùng lúc, âm lượng và cảnh báo tự động giảm trong tối đa 2 phút."</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Tắt"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Mở khoá để xem thông báo cũ"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Thiết bị này do cha mẹ của bạn quản lý"</string>
@@ -732,8 +733,7 @@
     <string name="notification_alert_title" msgid="3656229781017543655">"Mặc định"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Tự động"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"Không phát âm thanh hoặc rung"</string>
-    <!-- no translation found for notification_conversation_summary_low (3855696451919728790) -->
-    <skip />
+    <string name="notification_conversation_summary_low" msgid="3855696451919728790">"Không có âm báo hoặc rung nhưng vẫn xuất hiện trong phần cuộc trò chuyện"</string>
     <string name="notification_channel_summary_default" msgid="777294388712200605">"Có thể đổ chuông hoặc rung tuỳ theo chế độ cài đặt trên thiết bị"</string>
     <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Có thể đổ chuông hoặc rung tuỳ theo chế độ cài đặt trên thiết bị. Theo mặc định, các cuộc trò chuyện từ <xliff:g id="APP_NAME">%1$s</xliff:g> sẽ hiển thị dưới dạng bong bóng."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Cho phép hệ thống quyết định xem thông báo này phát âm thanh hay rung"</string>
@@ -790,8 +790,7 @@
     <string name="keyboard_key_page_up" msgid="173914303254199845">"Page Up"</string>
     <string name="keyboard_key_page_down" msgid="9035902490071829731">"Page Down"</string>
     <string name="keyboard_key_forward_del" msgid="5325501825762733459">"Delete"</string>
-    <!-- no translation found for keyboard_key_esc (6230365950511411322) -->
-    <skip />
+    <string name="keyboard_key_esc" msgid="6230365950511411322">"Esc"</string>
     <string name="keyboard_key_move_home" msgid="3496502501803911971">"Home"</string>
     <string name="keyboard_key_move_end" msgid="99190401463834854">"Cuối"</string>
     <string name="keyboard_key_insert" msgid="4621692715704410493">"Insert"</string>
@@ -1233,7 +1232,6 @@
     <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>
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Dữ liệu di động"</string>
-    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</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>
     <string name="mobile_data_poor_connection" msgid="819617772268371434">"Kết nối kém"</string>
@@ -1374,8 +1372,7 @@
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Chia đôi màn hình"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Phương thức nhập"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Lối tắt ứng dụng"</string>
-    <!-- no translation found for shortcut_helper_category_current_app_shortcuts (4017840565974573628) -->
-    <skip />
+    <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Ứng dụng hiện tại"</string>
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Hỗ trợ tiếp cận"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Phím tắt"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Lối tắt tìm kiếm"</string>
@@ -1388,13 +1385,27 @@
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Xong"</string>
     <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Tuyệt vời!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Quay lại"</string>
-    <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"Để quay lại, hãy vuốt sang trái hoặc sang phải bằng 3 ngón tay ở vị trí bất kỳ trên bàn di chuột."</string>
+    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
+    <skip />
+    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
+    <skip />
     <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Minh hoạ thao tác di chuyển sang phải và sang trái bằng 3 ngón tay trên bàn di chuột"</string>
     <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Màn hình thiết bị hiện ảnh động minh hoạ cử chỉ quay lại"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Đèn nền bàn phím"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Độ sáng %1$d/%2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Điều khiển nhà"</string>
     <string name="home_controls_dream_description" msgid="4644150952104035789">"Điều khiển nhà nhanh bằng trình bảo vệ màn hình"</string>
-    <!-- no translation found for volume_undo_action (5815519725211877114) -->
-    <skip />
+    <string name="volume_undo_action" msgid="5815519725211877114">"Huỷ"</string>
+    <string name="back_edu_toast_content" msgid="4530314597378982956">"Để quay lại, hãy dùng 3 ngón tay vuốt sang trái hoặc phải trên bàn di chuột"</string>
+    <string name="home_edu_toast_content" msgid="3381071147871955415">"Để trở về màn hình chính, hãy dùng 3 ngón tay vuốt lên trên bàn di chuột"</string>
+    <string name="overview_edu_toast_content" msgid="5797030644017804518">"Để xem các ứng dụng gần đây, hãy dùng 3 ngón tay vuốt lên và giữ trên bàn di chuột"</string>
+    <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Để xem tất cả ứng dụng của bạn, hãy nhấn phím hành động trên bàn phím"</string>
+    <string name="back_edu_notification_title" msgid="5624780717751357278">"Dùng bàn di chuột để quay lại"</string>
+    <string name="back_edu_notification_content" msgid="2497557451540954068">"Dùng 3 ngón tay vuốt sang trái hoặc sang phải. Hãy nhấn để tìm hiểu các cử chỉ khác."</string>
+    <string name="home_edu_notification_title" msgid="6097902076909654045">"Dùng bàn di chuột để chuyển đến màn hình chính"</string>
+    <string name="home_edu_notification_content" msgid="6631697734535766588">"Dùng 3 ngón tay vuốt lên. Hãy nhấn để tìm hiểu các cử chỉ khác."</string>
+    <string name="overview_edu_notification_title" msgid="1265824157319562406">"Dùng bàn di chuột để xem các ứng dụng gần đây"</string>
+    <string name="overview_edu_notification_content" msgid="3578204677648432500">"Dùng 3 ngón tay vuốt lên và giữ. Hãy nhấn để tìm hiểu các cử chỉ khác."</string>
+    <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Sử dụng bàn phím để xem tất cả ứng dụng"</string>
+    <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Nhấn phím hành động bất cứ lúc nào. Hãy nhấn để tìm hiểu các cử chỉ khác."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-vi/tiles_states_strings.xml b/packages/SystemUI/res/values-vi/tiles_states_strings.xml
index efcc373..d9d8af1 100644
--- a/packages/SystemUI/res/values-vi/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-vi/tiles_states_strings.xml
@@ -56,9 +56,6 @@
     <item msgid="5376619709702103243">"Đang tắt"</item>
     <item msgid="4875147066469902392">"Đang bật"</item>
   </string-array>
-    <!-- no translation found for tile_states_modes:0 (7764936419245199023) -->
-    <!-- no translation found for tile_states_modes:1 (2004750556637773692) -->
-    <!-- no translation found for tile_states_modes:2 (8968530753931637871) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Không hoạt động"</item>
     <item msgid="5044688398303285224">"Đang tắt"</item>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index b21bd97..6162da8 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -126,38 +126,25 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"点按即可查看"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"保存屏幕录制内容时出错"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"启动屏幕录制时出错"</string>
-    <!-- no translation found for screenrecord_stop_dialog_title (8716193661764511095) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message (6262768207331626817) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5995770227684523244) -->
-    <skip />
+    <string name="screenrecord_stop_dialog_title" msgid="8716193661764511095">"要停止录制吗?"</string>
+    <string name="screenrecord_stop_dialog_message" msgid="6262768207331626817">"您正在录制整个屏幕"</string>
+    <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"您正在录制“<xliff:g id="APP_NAME">%1$s</xliff:g>”"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"停止录制"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"正在共享屏幕"</string>
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"要停止共享屏幕吗?"</string>
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen_with_host_app (522823522115375414) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen (5090115386271179270) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_specific (5923772039347985172) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_generic (6681016774654578261) -->
-    <skip />
+    <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"您正在与“<xliff:g id="HOST_APP_NAME">%1$s</xliff:g>”分享整个屏幕"</string>
+    <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"您正在与一个应用分享整个屏幕"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"您正在分享“<xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>”的画面"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"您正在分享一个应用的画面"</string>
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"停止共享"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"正在投屏"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"停止投屏吗?"</string>
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen_with_device (1474703115926205251) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen (8419219169553867625) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app_with_device (2715934698604085519) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (8616103075630934513) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic_with_device (9213582497852420203) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic (4100272100480415076) -->
-    <skip />
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen_with_device" msgid="1474703115926205251">"您正在将整个屏幕投放到“<xliff:g id="DEVICE_NAME">%1$s</xliff:g>”上"</string>
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen" msgid="8419219169553867625">"您正在将整个屏幕投放到附近的设备上"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app_with_device" msgid="2715934698604085519">"您正在将“<xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>”投放到“<xliff:g id="DEVICE_NAME">%2$s</xliff:g>”上"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="8616103075630934513">"您正在将“<xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>”投放到附近的设备上"</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic_with_device" msgid="9213582497852420203">"您正在向“<xliff:g id="DEVICE_NAME">%1$s</xliff:g>”上投放内容"</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic" msgid="4100272100480415076">"您正在向附近的设备投放内容"</string>
     <string name="cast_to_other_device_stop_dialog_button" msgid="6420183747435521834">"停止投屏"</string>
     <string name="close_dialog_button" msgid="4749497706540104133">"关闭"</string>
     <string name="issuerecord_title" msgid="286627115110121849">"问题录制器"</string>
@@ -303,8 +290,7 @@
     <string name="start_dreams" msgid="9131802557946276718">"屏保"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"有线网络"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"勿扰"</string>
-    <!-- no translation found for quick_settings_modes_label (5407025818652750501) -->
-    <skip />
+    <string name="quick_settings_modes_label" msgid="5407025818652750501">"优先模式"</string>
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"蓝牙"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"没有可用的配对设备"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"点按即可连接设备或断开设备连接"</string>
@@ -402,6 +388,9 @@
     <string name="performance" msgid="6552785217174378320">"性能"</string>
     <string name="user_interface" msgid="3712869377953950887">"界面"</string>
     <string name="thermal" msgid="6758074791325414831">"散热"</string>
+    <string name="custom" msgid="3337456985275158299">"自定义"</string>
+    <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"自定义跟踪记录设置"</string>
+    <string name="restore_default" msgid="5259420807486239755">"恢复默认设置"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"单手模式"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"助听装置"</string>
     <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"已连接"</string>
@@ -440,6 +429,17 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"打开“设置”"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"其他设备"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"切换概览"</string>
+    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"优先模式"</string>
+    <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_off" msgid="1736604456618147306">"已关闭"</string>
+    <!-- no translation found for zen_mode_set_up (7457957033034460064) -->
+    <skip />
+    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
+    <skip />
+    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
+    <skip />
     <string name="zen_priority_introduction" msgid="3159291973383796646">"您将不会受到声音和振动的打扰(闹钟、提醒、活动和所指定来电者的相关提示音除外)。您依然可以听到您选择播放的任何内容(包括音乐、视频和游戏)的相关音效。"</string>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"您将不会受到声音和振动的打扰(闹钟提示音除外)。您依然可以听到您选择播放的任何内容(包括音乐、视频和游戏)的相关音效。"</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"自定义"</string>
@@ -478,6 +478,8 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • 正在慢速充电 • 将于 <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>后充满"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • 正在充电 • 将于 <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>后充满"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"锁定屏幕上的微件"</string>
+    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
+    <skip />
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"向左滑动即可启动公共教程"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"自定义"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"关闭"</string>
@@ -504,10 +506,9 @@
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"选择微件"</string>
     <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"移除微件"</string>
     <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"放置所选微件"</string>
-    <!-- no translation found for communal_widget_picker_title (1953369090475731663) -->
-    <skip />
-    <!-- no translation found for communal_widget_picker_description (490515450110487871) -->
-    <skip />
+    <string name="communal_widget_picker_title" msgid="1953369090475731663">"锁屏微件"</string>
+    <string name="communal_widget_picker_description" msgid="490515450110487871">"任何人都可以查看锁屏上的微件,即使平板电脑已锁定。"</string>
+    <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"取消选中微件"</string>
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"锁定的屏幕中的微件"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"若要使用微件打开应用,您需要验证是您本人在操作。另外请注意,任何人都可以查看此类微件,即使您的平板电脑已锁定。有些微件可能不适合显示在锁定的屏幕中,因此添加到这里可能不安全。"</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"知道了"</string>
@@ -564,8 +565,8 @@
     <string name="media_projection_action_text" msgid="3634906766918186440">"立即开始"</string>
     <string name="empty_shade_text" msgid="8935967157319717412">"没有通知"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"没有新通知"</string>
-    <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"自适应通知功能已开启"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"现在,当在短时间内收到许多通知时,您的设备会调低音量并减少屏幕上的弹出式窗口,最多持续两分钟。"</string>
+    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"“通知音量渐降”设置已开启"</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"当您一次收到过多通知时,设备音量会自动降低,提醒次数也会自动减少,这种状况最长可持续 2 分钟。"</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"关闭"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"解锁即可查看旧通知"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"此设备由您的家长管理"</string>
@@ -732,8 +733,7 @@
     <string name="notification_alert_title" msgid="3656229781017543655">"默认"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"自动"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"不发出提示音,也不振动"</string>
-    <!-- no translation found for notification_conversation_summary_low (3855696451919728790) -->
-    <skip />
+    <string name="notification_conversation_summary_low" msgid="3855696451919728790">"不发出提示音或振动,但依然出现在对话部分"</string>
     <string name="notification_channel_summary_default" msgid="777294388712200605">"可能会响铃或振动,取决于设备设置"</string>
     <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"可能会响铃或振动,取决于设备设置。默认情况下,来自<xliff:g id="APP_NAME">%1$s</xliff:g>的对话会以消息气泡的形式显示。"</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"让系统决定是否应让设备在收到此通知时发出提示音或振动"</string>
@@ -790,8 +790,7 @@
     <string name="keyboard_key_page_up" msgid="173914303254199845">"向上翻页"</string>
     <string name="keyboard_key_page_down" msgid="9035902490071829731">"PgDn"</string>
     <string name="keyboard_key_forward_del" msgid="5325501825762733459">"删除"</string>
-    <!-- no translation found for keyboard_key_esc (6230365950511411322) -->
-    <skip />
+    <string name="keyboard_key_esc" msgid="6230365950511411322">"Esc"</string>
     <string name="keyboard_key_move_home" msgid="3496502501803911971">"Home"</string>
     <string name="keyboard_key_move_end" msgid="99190401463834854">"End"</string>
     <string name="keyboard_key_insert" msgid="4621692715704410493">"Insert"</string>
@@ -1233,7 +1232,6 @@
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"需要进行身份验证。请轻触指纹传感器以验证身份。"</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"正在进行通话"</string>
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"移动数据网络"</string>
-    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"已连接"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"已暂时连接"</string>
     <string name="mobile_data_poor_connection" msgid="819617772268371434">"连接状况不佳"</string>
@@ -1374,8 +1372,7 @@
     <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>
-    <!-- no translation found for shortcut_helper_category_current_app_shortcuts (4017840565974573628) -->
-    <skip />
+    <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_search_placeholder" msgid="5488547526269871819">"搜索快捷键"</string>
@@ -1388,13 +1385,27 @@
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"完成"</string>
     <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"太棒了!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"返回"</string>
-    <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"如要返回,请使用三根手指在触控板上的任意位置左滑或右滑。"</string>
+    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
+    <skip />
+    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
+    <skip />
     <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"触控板,其中显示了三根手指右移和左移"</string>
     <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"设备屏幕,其中显示了返回手势的动画"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"键盘背光"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"第 %1$d 级,共 %2$d 级"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"家居控制"</string>
     <string name="home_controls_dream_description" msgid="4644150952104035789">"通过屏保快速访问家居控制功能"</string>
-    <!-- no translation found for volume_undo_action (5815519725211877114) -->
-    <skip />
+    <string name="volume_undo_action" msgid="5815519725211877114">"撤消"</string>
+    <string name="back_edu_toast_content" msgid="4530314597378982956">"如要返回,请用三根手指在触控板上向左或向右滑动"</string>
+    <string name="home_edu_toast_content" msgid="3381071147871955415">"如要前往主屏幕,请用三根手指在触控板上向上滑动"</string>
+    <string name="overview_edu_toast_content" msgid="5797030644017804518">"如要查看最近用过的应用,请用三根手指在触控板上向上滑动并按住"</string>
+    <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"如要查看所有应用,请按下键盘上的快捷操作按键"</string>
+    <string name="back_edu_notification_title" msgid="5624780717751357278">"使用触控板返回"</string>
+    <string name="back_edu_notification_content" msgid="2497557451540954068">"用三根手指向左或向右滑动。点按即可了解更多手势。"</string>
+    <string name="home_edu_notification_title" msgid="6097902076909654045">"使用触控板前往主屏幕"</string>
+    <string name="home_edu_notification_content" msgid="6631697734535766588">"用三根手指向上滑动。点按即可了解更多手势。"</string>
+    <string name="overview_edu_notification_title" msgid="1265824157319562406">"使用触控板查看最近用过的应用"</string>
+    <string name="overview_edu_notification_content" msgid="3578204677648432500">"用三根手指向上滑动并按住。点按即可了解更多手势。"</string>
+    <string name="all_apps_edu_notification_title" msgid="372262997265569063">"使用键盘查看所有应用"</string>
+    <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"您可随时按下快捷操作按键。点按即可了解更多手势。"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-zh-rCN/tiles_states_strings.xml b/packages/SystemUI/res/values-zh-rCN/tiles_states_strings.xml
index a0d37b9..0446a1b 100644
--- a/packages/SystemUI/res/values-zh-rCN/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/tiles_states_strings.xml
@@ -56,9 +56,6 @@
     <item msgid="5376619709702103243">"已关闭"</item>
     <item msgid="4875147066469902392">"已开启"</item>
   </string-array>
-    <!-- no translation found for tile_states_modes:0 (7764936419245199023) -->
-    <!-- no translation found for tile_states_modes:1 (2004750556637773692) -->
-    <!-- no translation found for tile_states_modes:2 (8968530753931637871) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"不可用"</item>
     <item msgid="5044688398303285224">"已关闭"</item>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index daac54e..9c1049b 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -126,38 +126,25 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"輕按即可查看"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"儲存螢幕錄影時發生錯誤"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"開始錄影畫面時發生錯誤"</string>
-    <!-- no translation found for screenrecord_stop_dialog_title (8716193661764511095) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message (6262768207331626817) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5995770227684523244) -->
-    <skip />
+    <string name="screenrecord_stop_dialog_title" msgid="8716193661764511095">"要停止錄影嗎?"</string>
+    <string name="screenrecord_stop_dialog_message" msgid="6262768207331626817">"你正在錄影整個螢幕畫面"</string>
+    <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"你正在錄影「<xliff:g id="APP_NAME">%1$s</xliff:g>」"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"停止錄製"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"正在分享螢幕"</string>
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"要停止分享螢幕嗎?"</string>
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen_with_host_app (522823522115375414) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen (5090115386271179270) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_specific (5923772039347985172) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_generic (6681016774654578261) -->
-    <skip />
+    <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"你正與「<xliff:g id="HOST_APP_NAME">%1$s</xliff:g>」分享整個螢幕畫面"</string>
+    <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"你正與一個應用程式分享整個螢幕畫面"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"你正在分享「<xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>」"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"你正在分享一個應用程式"</string>
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"停止分享"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"正在投放螢幕"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"要停止投放嗎?"</string>
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen_with_device (1474703115926205251) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen (8419219169553867625) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app_with_device (2715934698604085519) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (8616103075630934513) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic_with_device (9213582497852420203) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic (4100272100480415076) -->
-    <skip />
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen_with_device" msgid="1474703115926205251">"你正在投放整個螢幕畫面至「<xliff:g id="DEVICE_NAME">%1$s</xliff:g>」"</string>
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen" msgid="8419219169553867625">"你正在投放整個螢幕畫面至一部附近的裝置"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app_with_device" msgid="2715934698604085519">"你正在投放「<xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>」至「<xliff:g id="DEVICE_NAME">%2$s</xliff:g>」"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="8616103075630934513">"你正在投放「<xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>」至一部附近的裝置"</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic_with_device" msgid="9213582497852420203">"你正在投放內容至「<xliff:g id="DEVICE_NAME">%1$s</xliff:g>」"</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic" msgid="4100272100480415076">"你正在投放內容至一部附近的裝置"</string>
     <string name="cast_to_other_device_stop_dialog_button" msgid="6420183747435521834">"停止投放"</string>
     <string name="close_dialog_button" msgid="4749497706540104133">"關閉"</string>
     <string name="issuerecord_title" msgid="286627115110121849">"問題記錄工具"</string>
@@ -303,8 +290,7 @@
     <string name="start_dreams" msgid="9131802557946276718">"螢幕保護程式"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"以太網"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"請勿騷擾"</string>
-    <!-- no translation found for quick_settings_modes_label (5407025818652750501) -->
-    <skip />
+    <string name="quick_settings_modes_label" msgid="5407025818652750501">"優先模式"</string>
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"藍牙"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"找不到配對的裝置"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"輕按即可連結或解除連結裝置"</string>
@@ -402,6 +388,9 @@
     <string name="performance" msgid="6552785217174378320">"效能"</string>
     <string name="user_interface" msgid="3712869377953950887">"使用者介面"</string>
     <string name="thermal" msgid="6758074791325414831">"熱能"</string>
+    <string name="custom" msgid="3337456985275158299">"自訂"</string>
+    <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"自訂 Trace 設定"</string>
+    <string name="restore_default" msgid="5259420807486239755">"還原預設值"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"單手模式"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"助聽器"</string>
     <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"已連線"</string>
@@ -440,6 +429,17 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"開啟「設定」"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"其他裝置"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"切換概覽"</string>
+    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"優先模式"</string>
+    <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_off" msgid="1736604456618147306">"關閉"</string>
+    <!-- no translation found for zen_mode_set_up (7457957033034460064) -->
+    <skip />
+    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
+    <skip />
+    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
+    <skip />
     <string name="zen_priority_introduction" msgid="3159291973383796646">"你不會受到聲音和震動騷擾 (鬧鐘、提醒、活動和你指定的來電者鈴聲除外)。當你選擇播放音樂、影片和遊戲等,仍可以聽到該內容的聲音。"</string>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"你不會受到聲音和震動騷擾 (鬧鐘除外)。當你選擇播放音樂、影片和遊戲等,仍可以聽到該內容的聲音。"</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"自訂"</string>
@@ -478,6 +478,8 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • 慢速充電中 • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>後充滿電"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • 充電中 • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>後充滿電"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"上鎖畫面上的小工具"</string>
+    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
+    <skip />
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"向左滑動即可開始共用教學課程"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"自訂"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"關閉"</string>
@@ -504,10 +506,9 @@
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"揀小工具"</string>
     <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"移除小工具"</string>
     <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"放置所選小工具"</string>
-    <!-- no translation found for communal_widget_picker_title (1953369090475731663) -->
-    <skip />
-    <!-- no translation found for communal_widget_picker_description (490515450110487871) -->
-    <skip />
+    <string name="communal_widget_picker_title" msgid="1953369090475731663">"上鎖畫面小工具"</string>
+    <string name="communal_widget_picker_description" msgid="490515450110487871">"任何人都可查看上鎖畫面的小工具,即使平板電腦已上鎖亦然。"</string>
+    <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"取消揀小工具"</string>
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"上鎖畫面小工具"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"如要使用小工具開啟應用程式,系統會要求你驗證身分。請注意,即使平板電腦已鎖定,所有人還是能查看小工具。部分小工具可能不適用於上鎖畫面,而且新增至這裡後可能會有安全疑慮。"</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"知道了"</string>
@@ -564,8 +565,8 @@
     <string name="media_projection_action_text" msgid="3634906766918186440">"立即開始"</string>
     <string name="empty_shade_text" msgid="8935967157319717412">"沒有通知"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"沒有新通知"</string>
-    <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"已啟用自動調節通知"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"現在每當你於短時間內收到大量通知,裝置便會調低音量並減少螢幕上的彈出式視窗最多兩分鐘。"</string>
+    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"調低通知強度功能已開啟"</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"如果同一時間收到太多通知,裝置會在最長 2 分鐘內調低音量,並減少警示。"</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"關閉"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"解鎖即可查看舊通知"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"此裝置由你的家長管理"</string>
@@ -732,8 +733,7 @@
     <string name="notification_alert_title" msgid="3656229781017543655">"預設"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"自動"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"無音效或震動"</string>
-    <!-- no translation found for notification_conversation_summary_low (3855696451919728790) -->
-    <skip />
+    <string name="notification_conversation_summary_low" msgid="3855696451919728790">"沒有音效或震動,但仍在對話部分顯示"</string>
     <string name="notification_channel_summary_default" msgid="777294388712200605">"可能會根據裝置設定發出鈴聲或震動"</string>
     <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"可能會根據裝置設定發出鈴聲或震動。根據預設,來自 <xliff:g id="APP_NAME">%1$s</xliff:g> 的對話會以對話氣泡顯示。"</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"由系統判斷是否要讓此通知發出音效或震動"</string>
@@ -790,8 +790,7 @@
     <string name="keyboard_key_page_up" msgid="173914303254199845">"上一頁"</string>
     <string name="keyboard_key_page_down" msgid="9035902490071829731">"下一頁"</string>
     <string name="keyboard_key_forward_del" msgid="5325501825762733459">"刪除"</string>
-    <!-- no translation found for keyboard_key_esc (6230365950511411322) -->
-    <skip />
+    <string name="keyboard_key_esc" msgid="6230365950511411322">"Esc 鍵"</string>
     <string name="keyboard_key_move_home" msgid="3496502501803911971">"Home"</string>
     <string name="keyboard_key_move_end" msgid="99190401463834854">"End"</string>
     <string name="keyboard_key_insert" msgid="4621692715704410493">"插入"</string>
@@ -1233,7 +1232,6 @@
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"需要驗證。掂一下指紋感應器就可以驗證。"</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"通話中"</string>
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"流動數據"</string>
-    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"已連線"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"已暫時連線"</string>
     <string name="mobile_data_poor_connection" msgid="819617772268371434">"連線速度欠佳"</string>
@@ -1374,8 +1372,7 @@
     <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>
-    <!-- no translation found for shortcut_helper_category_current_app_shortcuts (4017840565974573628) -->
-    <skip />
+    <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_search_placeholder" msgid="5488547526269871819">"搜尋快速鍵"</string>
@@ -1388,13 +1385,27 @@
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"完成"</string>
     <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"太好了!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"返回"</string>
-    <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"用三隻手指在觸控板上任何一處左右滑動即可返回。"</string>
+    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
+    <skip />
+    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
+    <skip />
     <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"觸控板上有三隻手指左右移動"</string>
     <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"裝置畫面顯示返回手勢動畫"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"鍵盤背光"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"第 %1$d 級,共 %2$d 級"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"智能家居"</string>
     <string name="home_controls_dream_description" msgid="4644150952104035789">"在螢幕保護程式畫面上控制智能家居"</string>
-    <!-- no translation found for volume_undo_action (5815519725211877114) -->
-    <skip />
+    <string name="volume_undo_action" msgid="5815519725211877114">"復原"</string>
+    <string name="back_edu_toast_content" msgid="4530314597378982956">"如要返回,請用三隻手指在觸控板上向左或向右滑動"</string>
+    <string name="home_edu_toast_content" msgid="3381071147871955415">"如要返回主畫面,請用三隻手指在觸控板上向上滑動"</string>
+    <string name="overview_edu_toast_content" msgid="5797030644017804518">"如要查看最近使用的應用程式,請用三隻手指在觸控板上向上滑動並按住"</string>
+    <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"如要查看所有應用程式,請在鍵盤上按下快捷操作鍵"</string>
+    <string name="back_edu_notification_title" msgid="5624780717751357278">"使用觸控板返回"</string>
+    <string name="back_edu_notification_content" msgid="2497557451540954068">"用三隻手指向左或向右滑動。輕按即可瞭解更多手勢。"</string>
+    <string name="home_edu_notification_title" msgid="6097902076909654045">"使用觸控板返回主畫面"</string>
+    <string name="home_edu_notification_content" msgid="6631697734535766588">"用三隻手指向上滑動。輕按即可瞭解更多手勢。"</string>
+    <string name="overview_edu_notification_title" msgid="1265824157319562406">"使用觸控板查看最近使用的應用程式"</string>
+    <string name="overview_edu_notification_content" msgid="3578204677648432500">"用三隻手指向上滑動並按住。輕按即可瞭解更多手勢。"</string>
+    <string name="all_apps_edu_notification_title" msgid="372262997265569063">"使用鍵盤查看所有應用程式"</string>
+    <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"隨時按下快捷操作鍵。輕按即可瞭解更多手勢。"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-zh-rHK/tiles_states_strings.xml b/packages/SystemUI/res/values-zh-rHK/tiles_states_strings.xml
index de07c6c..cca7ac4 100644
--- a/packages/SystemUI/res/values-zh-rHK/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/tiles_states_strings.xml
@@ -56,9 +56,6 @@
     <item msgid="5376619709702103243">"已關閉"</item>
     <item msgid="4875147066469902392">"已開啟"</item>
   </string-array>
-    <!-- no translation found for tile_states_modes:0 (7764936419245199023) -->
-    <!-- no translation found for tile_states_modes:1 (2004750556637773692) -->
-    <!-- no translation found for tile_states_modes:2 (8968530753931637871) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"無法使用"</item>
     <item msgid="5044688398303285224">"已關閉"</item>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index a29d47b..1ac8ec4 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -126,38 +126,25 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"輕觸即可查看"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"儲存螢幕錄影內容時發生錯誤"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"開始錄製螢幕畫面時發生錯誤"</string>
-    <!-- no translation found for screenrecord_stop_dialog_title (8716193661764511095) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message (6262768207331626817) -->
-    <skip />
-    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5995770227684523244) -->
-    <skip />
+    <string name="screenrecord_stop_dialog_title" msgid="8716193661764511095">"要停止錄製畫面嗎?"</string>
+    <string name="screenrecord_stop_dialog_message" msgid="6262768207331626817">"目前正在錄製整個螢幕的畫面"</string>
+    <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"目前正在錄製「<xliff:g id="APP_NAME">%1$s</xliff:g>」的畫面"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"停止錄音"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"正在分享畫面"</string>
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"要停止分享畫面嗎?"</string>
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen_with_host_app (522823522115375414) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_entire_screen (5090115386271179270) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_specific (5923772039347985172) -->
-    <skip />
-    <!-- no translation found for share_to_app_stop_dialog_message_single_app_generic (6681016774654578261) -->
-    <skip />
+    <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"目前正在與「<xliff:g id="HOST_APP_NAME">%1$s</xliff:g>」分享整個畫面"</string>
+    <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"目前正在與某個應用程式分享整個畫面"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"目前正在分享「<xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>」的畫面"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"目前正在分享應用程式畫面"</string>
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"停止分享"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"正在投放畫面"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"要停止投放嗎?"</string>
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen_with_device (1474703115926205251) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_entire_screen (8419219169553867625) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app_with_device (2715934698604085519) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (8616103075630934513) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic_with_device (9213582497852420203) -->
-    <skip />
-    <!-- no translation found for cast_to_other_device_stop_dialog_message_generic (4100272100480415076) -->
-    <skip />
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen_with_device" msgid="1474703115926205251">"目前正在將整個畫面投放到「<xliff:g id="DEVICE_NAME">%1$s</xliff:g>」"</string>
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen" msgid="8419219169553867625">"目前正在將整個畫面投放到鄰近裝置"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app_with_device" msgid="2715934698604085519">"目前正在將「<xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>」投放到「<xliff:g id="DEVICE_NAME">%2$s</xliff:g>」"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="8616103075630934513">"目前正在將「<xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>」投放到鄰近裝置"</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic_with_device" msgid="9213582497852420203">"目前正在將內容投放到「<xliff:g id="DEVICE_NAME">%1$s</xliff:g>」"</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic" msgid="4100272100480415076">"目前正在將內容投放到鄰近裝置"</string>
     <string name="cast_to_other_device_stop_dialog_button" msgid="6420183747435521834">"停止投放"</string>
     <string name="close_dialog_button" msgid="4749497706540104133">"關閉"</string>
     <string name="issuerecord_title" msgid="286627115110121849">"問題記錄工具"</string>
@@ -303,8 +290,7 @@
     <string name="start_dreams" msgid="9131802557946276718">"螢幕保護程式"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"乙太網路"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"零打擾"</string>
-    <!-- no translation found for quick_settings_modes_label (5407025818652750501) -->
-    <skip />
+    <string name="quick_settings_modes_label" msgid="5407025818652750501">"優先模式"</string>
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"藍牙"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"找不到配對的裝置"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"輕觸即可連結/取消連結裝置"</string>
@@ -402,6 +388,9 @@
     <string name="performance" msgid="6552785217174378320">"效能"</string>
     <string name="user_interface" msgid="3712869377953950887">"使用者介面"</string>
     <string name="thermal" msgid="6758074791325414831">"熱成像"</string>
+    <string name="custom" msgid="3337456985275158299">"自訂"</string>
+    <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"自訂追蹤記錄設定"</string>
+    <string name="restore_default" msgid="5259420807486239755">"還原為預設值"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"單手模式"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"助聽器"</string>
     <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"已連線"</string>
@@ -440,6 +429,17 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"開啟「設定」"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"其他裝置"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"切換總覽"</string>
+    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"優先模式"</string>
+    <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_off" msgid="1736604456618147306">"關閉"</string>
+    <!-- no translation found for zen_mode_set_up (7457957033034460064) -->
+    <skip />
+    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
+    <skip />
+    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
+    <skip />
     <string name="zen_priority_introduction" msgid="3159291973383796646">"裝置不會發出音效或震動造成干擾,但是會保留與鬧鐘、提醒、活動和指定來電者有關的設定。如果你選擇播放音樂、影片和遊戲等內容,還是可以聽見相關音訊。"</string>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"裝置不會發出音效或震動造成干擾,但是會保留鬧鐘響鈴。如果你選擇播放音樂、影片和遊戲等內容,還是可以聽見相關音訊。"</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"自訂"</string>
@@ -478,6 +478,8 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • 慢速充電中 • 將於 <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>後充飽"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • 充電中 • 將於 <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>後充飽"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"螢幕鎖定畫面上的小工具"</string>
+    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
+    <skip />
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"向左滑動即可啟動通用教學課程"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"自訂"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"關閉"</string>
@@ -504,10 +506,9 @@
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"選取小工具"</string>
     <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"移除小工具"</string>
     <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"放置所選小工具"</string>
-    <!-- no translation found for communal_widget_picker_title (1953369090475731663) -->
-    <skip />
-    <!-- no translation found for communal_widget_picker_description (490515450110487871) -->
-    <skip />
+    <string name="communal_widget_picker_title" msgid="1953369090475731663">"螢幕鎖定畫面小工具"</string>
+    <string name="communal_widget_picker_description" msgid="490515450110487871">"即使平板電腦已鎖定,所有人仍可查看螢幕鎖定畫面上的小工具。"</string>
+    <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"取消選取小工具"</string>
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"螢幕鎖定小工具"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"如要使用小工具開啟應用程式,系統會要求你驗證身分。請注意,即使平板電腦已鎖定,所有人還是能查看小工具。某些小工具可能不適用於螢幕鎖定畫面,而且新增到這裡可能有安全疑慮。"</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"我知道了"</string>
@@ -564,8 +565,8 @@
     <string name="media_projection_action_text" msgid="3634906766918186440">"立即開始"</string>
     <string name="empty_shade_text" msgid="8935967157319717412">"沒有通知"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"沒有新通知"</string>
-    <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"自動調整通知功能已開啟"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"如果在短時間內收到多則通知,裝置會在最長兩分鐘內調降音量,並減少在畫面上顯示彈出式視窗。"</string>
+    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"通知緩和設定已開啟"</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"如果一次收到過多通知,裝置就會自動降低音量並減少通知數量,持續時間最多 2 分鐘。"</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"關閉"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"解鎖即可查看舊通知"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"這個裝置是由你的家長管理"</string>
@@ -732,8 +733,7 @@
     <string name="notification_alert_title" msgid="3656229781017543655">"預設"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"自動"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"不震動或發出聲音"</string>
-    <!-- no translation found for notification_conversation_summary_low (3855696451919728790) -->
-    <skip />
+    <string name="notification_conversation_summary_low" msgid="3855696451919728790">"不震動或發出聲音,但持續顯示在對話區中"</string>
     <string name="notification_channel_summary_default" msgid="777294388712200605">"根據裝置的設定響鈴或震動"</string>
     <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"根據裝置的設定響鈴或震動。根據預設,來自「<xliff:g id="APP_NAME">%1$s</xliff:g>」的對話會以對話框形式顯示。"</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"由系統判斷要讓裝置在收到這則通知時震動還是發出音效"</string>
@@ -790,8 +790,7 @@
     <string name="keyboard_key_page_up" msgid="173914303254199845">"Page Up 鍵"</string>
     <string name="keyboard_key_page_down" msgid="9035902490071829731">"Page Down 鍵"</string>
     <string name="keyboard_key_forward_del" msgid="5325501825762733459">"Delete 鍵"</string>
-    <!-- no translation found for keyboard_key_esc (6230365950511411322) -->
-    <skip />
+    <string name="keyboard_key_esc" msgid="6230365950511411322">"Esc 鍵"</string>
     <string name="keyboard_key_move_home" msgid="3496502501803911971">"Home 鍵"</string>
     <string name="keyboard_key_move_end" msgid="99190401463834854">"End 鍵"</string>
     <string name="keyboard_key_insert" msgid="4621692715704410493">"Insert 鍵"</string>
@@ -1233,7 +1232,6 @@
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"需要驗證。輕觸指紋感應器即可進行驗證。"</string>
     <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"通話中"</string>
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"行動數據"</string>
-    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"已連線"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"已暫時建立連線"</string>
     <string name="mobile_data_poor_connection" msgid="819617772268371434">"連線品質不佳"</string>
@@ -1374,8 +1372,7 @@
     <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>
-    <!-- no translation found for shortcut_helper_category_current_app_shortcuts (4017840565974573628) -->
-    <skip />
+    <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_search_placeholder" msgid="5488547526269871819">"搜尋快速鍵"</string>
@@ -1388,13 +1385,27 @@
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"完成"</string>
     <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"太棒了!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"返回"</string>
-    <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"如要返回,請用三指在觸控板上向左或右滑動。"</string>
+    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
+    <skip />
+    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
+    <skip />
     <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"動畫顯示三指正在觸控板上向左右移動"</string>
     <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"裝置畫面顯示返回手勢的動畫"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"鍵盤背光"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"第 %1$d 級,共 %2$d 級"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"居家控制"</string>
     <string name="home_controls_dream_description" msgid="4644150952104035789">"在螢幕保護程式畫面上快速存取居家控制功能"</string>
-    <!-- no translation found for volume_undo_action (5815519725211877114) -->
-    <skip />
+    <string name="volume_undo_action" msgid="5815519725211877114">"復原"</string>
+    <string name="back_edu_toast_content" msgid="4530314597378982956">"如要返回,請在觸控板上用三指向左或向右滑動"</string>
+    <string name="home_edu_toast_content" msgid="3381071147871955415">"如要前往主畫面,請在觸控板上用三指向上滑動"</string>
+    <string name="overview_edu_toast_content" msgid="5797030644017804518">"如要查看最近使用的應用程式,請在觸控板上向上滑動並按住"</string>
+    <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"如要查看所有應用程式,請按下鍵盤上的快捷操作鍵"</string>
+    <string name="back_edu_notification_title" msgid="5624780717751357278">"使用觸控板返回"</string>
+    <string name="back_edu_notification_content" msgid="2497557451540954068">"用三指向左或向右滑動。輕觸即可進一步瞭解手勢。"</string>
+    <string name="home_edu_notification_title" msgid="6097902076909654045">"使用觸控板前往主畫面"</string>
+    <string name="home_edu_notification_content" msgid="6631697734535766588">"用三指向上滑動。輕觸即可進一步瞭解手勢。"</string>
+    <string name="overview_edu_notification_title" msgid="1265824157319562406">"使用觸控板查看最近使用的應用程式"</string>
+    <string name="overview_edu_notification_content" msgid="3578204677648432500">"用三指向上滑動並按住。輕觸即可進一步瞭解手勢。"</string>
+    <string name="all_apps_edu_notification_title" msgid="372262997265569063">"使用鍵盤查看所有應用程式"</string>
+    <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"你隨時可以按下快捷操作鍵。輕觸即可進一步瞭解手勢。"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-zh-rTW/tiles_states_strings.xml b/packages/SystemUI/res/values-zh-rTW/tiles_states_strings.xml
index cbbea41..4cc5804 100644
--- a/packages/SystemUI/res/values-zh-rTW/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/tiles_states_strings.xml
@@ -56,9 +56,6 @@
     <item msgid="5376619709702103243">"已關閉"</item>
     <item msgid="4875147066469902392">"已開啟"</item>
   </string-array>
-    <!-- no translation found for tile_states_modes:0 (7764936419245199023) -->
-    <!-- no translation found for tile_states_modes:1 (2004750556637773692) -->
-    <!-- no translation found for tile_states_modes:2 (8968530753931637871) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"無法使用"</item>
     <item msgid="5044688398303285224">"已關閉"</item>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index a23c69f..3a34c47 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -290,8 +290,7 @@
     <string name="start_dreams" msgid="9131802557946276718">"Isigciniskrini"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"I-Ethernet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"Ungaphazamisi"</string>
-    <!-- no translation found for quick_settings_modes_label (5407025818652750501) -->
-    <skip />
+    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Amamodi okubalulekile"</string>
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"I-Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Awekho amadivayisi abhanqiwe atholakalayo"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Thepha ukuze uxhumae noma ungaxhumi idivaysi"</string>
@@ -389,6 +388,9 @@
     <string name="performance" msgid="6552785217174378320">"Ukusebenza"</string>
     <string name="user_interface" msgid="3712869377953950887">"Okusetshenziswa Kubonwa"</string>
     <string name="thermal" msgid="6758074791325414831">"Ithermal"</string>
+    <string name="custom" msgid="3337456985275158299">"Okumuntu ngamunye"</string>
+    <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Amasethingi Okulandelela Okomuntu Ngamunye"</string>
+    <string name="restore_default" msgid="5259420807486239755">"Buyisela Okuzenzakalelayo"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Imodi yesandla esisodwa"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Izinsizakuzwa"</string>
     <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Kuyasebenza"</string>
@@ -427,6 +429,17 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Vula Amasethingi"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Enye idivayisi"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Guqula ukubuka konke"</string>
+    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Amamodi okubalulekile"</string>
+    <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Kwenziwe"</string>
+    <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Amasethingi"</string>
+    <string name="zen_mode_on" msgid="9085304934016242591">"Vuliwe"</string>
+    <string name="zen_mode_off" msgid="1736604456618147306">"Valiwe"</string>
+    <!-- no translation found for zen_mode_set_up (7457957033034460064) -->
+    <skip />
+    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
+    <skip />
+    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
+    <skip />
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Ngeke uphazanyiswe imisindo nokudlidliza, ngaphandle kusukela kuma-alamu, izikhumbuzi, imicimbi, nabafonayo obacacisayo. Usazozwa noma yini okhetha ukuyidlala okufaka umculo, amavidiyo, namageyimu."</string>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Ngeke uze uphazanyiswe imisindo nokudlidliza, ngaphandle kusukela kuma-alamu. Usazozwa noma yini okhetha ukuyidlala okufaka umculo, amavidiyo, namageyimu."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Enza ngendlela oyifisayo"</string>
@@ -465,6 +478,8 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Ishaja kancane • Izogcwala ngo-<xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Iyashaja • Izogcwala ngo-<xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Amawijethi ekukhiyeni isikrini"</string>
+    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
+    <skip />
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Swayiphela kwesokunxele ukuze uqale okokufundisa komphakathi"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Enza ngendlela oyifisayo"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Chitha"</string>
@@ -493,6 +508,7 @@
     <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"beka iwijethi ekhethiwe"</string>
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"Amawijethi wesikrini esikhiyiwe"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"Noma ubani angabuka amawijethi ngisho noma ithebulethi ikhiyiwe."</string>
+    <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"yeka ukukhetha iwijethi"</string>
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Amawijethi wesikrini esikhiyiwe"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Ukuze uvule i-app usebenzisa iwijethi, uzodinga ukuqinisekisa ukuthi nguwe. Futhi, khumbula ukuthi noma ubani angakwazi ukuzibuka, nanoma ithebhulethi yakho ikhiyiwe. Amanye amawijethi kungenzeka abengahloselwe ukukhiya isikrini sakho futhi kungenzeka awaphephile ukuthi angafakwa lapha."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Ngiyezwa"</string>
@@ -549,8 +565,8 @@
     <string name="media_projection_action_text" msgid="3634906766918186440">"Qala manje"</string>
     <string name="empty_shade_text" msgid="8935967157319717412">"Azikho izaziso"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"Azikho izaziso ezintsha"</string>
-    <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"Izaziso zokuzijwayeza zivuliwe"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"Idivayisi yakho manje yehlisa ivolumu futhi yehlisa okwesikhashana esikrinini kuze kufike emizuzwini emibili lapho uthola izaziso eziningi ngesikhathi esifushane."</string>
+    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Ukwehlisa umsindo wezaziso kuvuliwe"</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Ivolumu yedivayisi yakho kanye nezexwayiso kuncishiswa ngokuzenzakalelayo imizuzu efika kwemi-2 lapho uthola izaziso eziningi kakhulu ngesikhathi esisodwa."</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Vala"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Vula ukuze ubone izaziso ezindala"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Le divayisi iphethwe ngumzali wakho"</string>
@@ -717,8 +733,7 @@
     <string name="notification_alert_title" msgid="3656229781017543655">"Okuzenzekelayo"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Okuzenzekelayo"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"Awukho umsindo noma ukudlidliza"</string>
-    <!-- no translation found for notification_conversation_summary_low (3855696451919728790) -->
-    <skip />
+    <string name="notification_conversation_summary_low" msgid="3855696451919728790">"Awukho umsindo noma ukudlidliza kodwa kusavela esigabeni sengxoxo"</string>
     <string name="notification_channel_summary_default" msgid="777294388712200605">"Ingase ikhale noma idlidlize ngokusekelwe kumasethingi edivayisi"</string>
     <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Ingase ikhale noma idlidlize kuya ngamasethingi wedivayisi. Izingxoxo ezivela ku-<xliff:g id="APP_NAME">%1$s</xliff:g> ziba yibhamuza ngokuzenzakalela."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Vumela isistimu inqume uma lesi saziso kufanele senze umsindo noma sidlidlize"</string>
@@ -1217,7 +1232,6 @@
     <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>
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Idatha yeselula"</string>
-    <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Ixhunyiwe"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Ixhume okwesikhashana"</string>
     <string name="mobile_data_poor_connection" msgid="819617772268371434">"Uxhumo olungeluhle"</string>
@@ -1358,8 +1372,7 @@
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Hlukanisa isikrini"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Okokufaka"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Izinqamuleli Zohlelo lokusebenza"</string>
-    <!-- no translation found for shortcut_helper_category_current_app_shortcuts (4017840565974573628) -->
-    <skip />
+    <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"I-App yamanje"</string>
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Ukufinyeleleka"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Izinqamuleli zekhibhodi"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Sesha izinqamuleli"</string>
@@ -1372,13 +1385,27 @@
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Kwenziwe"</string>
     <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Umsebenzi omuhle!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Buyela emuva"</string>
-    <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"Ukuze ubuyele emuva, swayiphela kwesokunxele noma kwesokudla usebenzisa iminwe emithathu noma yikuphi ephedini yokuthinta."</string>
+    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
+    <skip />
+    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
+    <skip />
     <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Iphedi yokuthinta ebonisa iminwe emithathu iya kwesokudla nakwesokunxele"</string>
     <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Isikrini sedivayisi esibonisa opopayi bokuthinta kwasemuva"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Ilambu lekhibhodi"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Ileveli %1$d ka-%2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Izilawuli Zasekhaya"</string>
     <string name="home_controls_dream_description" msgid="4644150952104035789">"Finyelela ngokushesha izilawuli zakho zasekhaya njengesigcini-skrini"</string>
-    <!-- no translation found for volume_undo_action (5815519725211877114) -->
-    <skip />
+    <string name="volume_undo_action" msgid="5815519725211877114">"Hlehlisa"</string>
+    <string name="back_edu_toast_content" msgid="4530314597378982956">"Ukuze ubuyele emuva, swayiphela kwesokunxele noma kwesokudla ngeminwe emithathu ephedini yokuthinta"</string>
+    <string name="home_edu_toast_content" msgid="3381071147871955415">"Ukuze uye ekhaya, swayiphela phezulu ngeminwe emithathu ephedini yokuthinta"</string>
+    <string name="overview_edu_toast_content" msgid="5797030644017804518">"Ukuze ubuke ama-app akamuva, swayiphela phezulu bese ubambe ngeminwe emithathu ephedini yokuthinta"</string>
+    <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Ukuze ubuke wonke ama-app wakho, cindezela inkinobho yokufinyelela kukhibhodi yakho"</string>
+    <string name="back_edu_notification_title" msgid="5624780717751357278">"Sebenzisa iphedi yokuthinta ukuze ubuyele emuva"</string>
+    <string name="back_edu_notification_content" msgid="2497557451540954068">"Swayiphela kwesokunxele noma kwesokudla usebenzisa iminwe emithathu. Thepha ukuze ufunde kabanzi ngokunyakazisa umzimba."</string>
+    <string name="home_edu_notification_title" msgid="6097902076909654045">"Sebenzisa iphedi yokuthinta ukuya ekhaya"</string>
+    <string name="home_edu_notification_content" msgid="6631697734535766588">"Swayiphela phezulu usebenzisa iminwe emithathu. Thepha ukuze ufunde kabanzi ngokunyakazisa umzimba."</string>
+    <string name="overview_edu_notification_title" msgid="1265824157319562406">"Sebenzisa iphedi yokuthinta ukuze ubuke ama-app akamuva"</string>
+    <string name="overview_edu_notification_content" msgid="3578204677648432500">"Swayiphela phezulu bese uyabamba usebenzisa iminwe emithathu. Thepha ukuze ufunde kabanzi ngokunyakazisa umzimba."</string>
+    <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Sebenzisa ikhibhodi yakho ukubuka wonke ama-app"</string>
+    <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Cindezela inkinobho yokufinyelela noma kunini. Thepha ukuze ufunde kabanzi ngokunyakazisa umzimba."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-zu/tiles_states_strings.xml b/packages/SystemUI/res/values-zu/tiles_states_strings.xml
index 8d6b843..a795ee8 100644
--- a/packages/SystemUI/res/values-zu/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-zu/tiles_states_strings.xml
@@ -56,9 +56,6 @@
     <item msgid="5376619709702103243">"Valiwe"</item>
     <item msgid="4875147066469902392">"Vuliwe"</item>
   </string-array>
-    <!-- no translation found for tile_states_modes:0 (7764936419245199023) -->
-    <!-- no translation found for tile_states_modes:1 (2004750556637773692) -->
-    <!-- no translation found for tile_states_modes:2 (8968530753931637871) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Akutholakali"</item>
     <item msgid="5044688398303285224">"Valiwe"</item>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 30f23bf..f0c8894 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -104,7 +104,7 @@
 
     <!-- Tiles native to System UI. Order should match "quick_settings_tiles_default" -->
     <string name="quick_settings_tiles_stock" translatable="false">
-        internet,bt,flashlight,dnd,modes,alarm,airplane,controls,wallet,rotation,battery,cast,screenrecord,mictoggle,cameratoggle,location,hotspot,inversion,saver,dark,work,night,reverse,reduce_brightness,qr_code_scanner,onehanded,color_correction,dream,font_scaling,record_issue,hearing_devices
+        internet,bt,flashlight,dnd,alarm,airplane,controls,wallet,rotation,battery,cast,screenrecord,mictoggle,cameratoggle,location,hotspot,inversion,saver,dark,work,night,reverse,reduce_brightness,qr_code_scanner,onehanded,color_correction,dream,font_scaling,record_issue,hearing_devices
     </string>
 
     <!-- The tiles to display in QuickSettings -->
@@ -1050,4 +1050,7 @@
 
     <!-- Only applicable for dual shade - Allow Notifications/QS shade to anchor to the bottom. -->
     <bool name="config_dualShadeAlignedToBottom">false</bool>
+
+    <!-- List of packages for which we want to use activity info (instead of application info) for biometric prompt logo. Empty for AOSP. [DO NOT TRANSLATE] -->
+    <string-array name="config_useActivityLogoForBiometricPrompt" translatable="false"/>
 </resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index eda7bb0..e5750d2 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1729,10 +1729,18 @@
     <dimen name="wallet_button_vertical_padding">8dp</dimen>
 
     <!-- Ongoing activity chip -->
+    <!-- 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. -->
+    <dimen name="ongoing_activity_chip_side_padding_for_embedded_padding_icon">6dp</dimen>
+    <!-- The icon size, used with the default phone icon. -->
     <dimen name="ongoing_activity_chip_icon_size">16dp</dimen>
-    <!-- The padding between the icon and the text. -->
+    <!-- The icon size, used with an icon that has embedded padding. (If the icon has embedded padding, we need to make the whole icon larger so the icon itself doesn't look small.) -->
+    <dimen name="ongoing_activity_chip_embedded_padding_icon_size">22dp</dimen>
+    <!-- The padding between the icon and the text. Only used if the default phone icon is used. -->
     <dimen name="ongoing_activity_chip_icon_text_padding">4dp</dimen>
+    <!-- The end padding for the timer text view. Only used if an embedded padding icon is used. -->
+    <dimen name="ongoing_activity_chip_text_end_padding_for_embedded_padding_icon">6dp</dimen>
     <dimen name="ongoing_activity_chip_corner_radius">28dp</dimen>
 
     <!-- Status bar user chip -->
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index 212dae2..e4f900d 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -132,6 +132,7 @@
 
     <!-- Status bar -->
     <item type="id" name="status_bar_dot" />
+    <item type="id" name="ongoing_activity_chip_custom_icon" />
 
     <!-- Default display cutout on the physical top of screen -->
     <item type="id" name="display_cutout" />
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index acc12d7..fe49f3a 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -432,8 +432,6 @@
 
     <!-- Content description for the app logo icon on biometric prompt. [CHAR LIMIT=NONE] -->
     <string name="biometric_dialog_logo">App logo</string>
-    <!-- List of packages for which we want to show overridden logo. For example, an app overrides its launcher logo, if it's in this array, biometric dialog shows the overridden logo; otherwise biometric dialog still shows the default application info icon. [CHAR LIMIT=NONE] -->
-    <string-array name="biometric_dialog_package_names_for_logo_with_overrides" />
     <!-- Message shown when a biometric is authenticated, asking the user to confirm authentication [CHAR LIMIT=30] -->
     <string name="biometric_dialog_confirm">Confirm</string>
     <!-- Button name on BiometricPrompt shown when a biometric is detected but not authenticated. Tapping the button resumes authentication [CHAR LIMIT=30] -->
@@ -948,6 +946,10 @@
     <string name="performance">Performance</string>
     <string name="user_interface">User Interface</string>
     <string name="thermal">Thermal</string>
+    <string name="custom">Custom</string>
+
+    <string name="custom_trace_settings_dialog_title">Custom Trace Settings</string>
+    <string name="restore_default">Restore Default</string>
 
     <!-- QuickSettings: Label for the toggle that controls whether One-handed mode is enabled. [CHAR LIMIT=NONE] -->
     <string name="quick_settings_onehanded_label">One-handed mode</string>
@@ -1099,6 +1101,20 @@
     <!-- Priority modes: label for an inactive mode [CHAR LIMIT=35] -->
     <string name="zen_mode_off">Off</string>
 
+    <!-- Priority modes: label for a mode that needs to be set up [CHAR LIMIT=35] -->
+    <string name="zen_mode_set_up">Set up</string>
+
+    <!-- Priority modes: label for a mode that cannot be manually turned on [CHAR LIMIT=35] -->
+    <string name="zen_mode_no_manual_invocation">Manage in settings</string>
+
+    <string name="zen_mode_active_modes">
+        {count, plural,
+            =0 {No active modes}
+            =1 {{mode} is active}
+            other {# modes are active}
+        }
+    </string>
+
     <!-- Zen mode: Priority only introduction message on first use -->
     <string name="zen_priority_introduction">You won\'t be disturbed by sounds and vibrations, except from alarms, reminders, events, and callers you specify. You\'ll still hear anything you choose to play including music, videos, and games.</string>
 
@@ -1207,6 +1223,9 @@
     <!-- Label for accessibility action that shows widgets on lock screen on click. [CHAR LIMIT=NONE] -->
     <string name="accessibility_action_open_communal_hub">Widgets on lock screen</string>
 
+    <!-- Label for an accessibility announcement when a widget has been added to the lock screen. [CHAR LIMIT=NONE] -->
+    <string name="accessibility_announcement_communal_widget_added"><xliff:g id="widget_name" example="Calendar month view">%1$s</xliff:g> widget added to lock screen</string>
+
     <!-- Indicator on keyguard to start the communal tutorial. [CHAR LIMIT=100] -->
     <string name="communal_tutorial_indicator_text">Swipe left to start the communal tutorial</string>
 
@@ -1360,11 +1379,15 @@
 
     <!-- Casting that launched by SysUI (i.e. when there is no app name) -->
     <!-- System casting media projection permission dialog title. [CHAR LIMIT=100] -->
-    <string name="media_projection_entry_cast_permission_dialog_title">Start casting?</string>
+    <string name="media_projection_entry_cast_permission_dialog_title">Cast your screen?</string>
+    <!-- System casting media projection permission option for capturing just a single app [CHAR LIMIT=50] -->
+    <string name="media_projection_entry_cast_permission_dialog_option_text_single_app">Cast one app</string>
+    <!-- System casting media projection permission option for capturing the whole screen [CHAR LIMIT=50] -->
+    <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen">Cast entire screen</string>
     <!-- System casting media projection permission warning for capturing the whole screen when SysUI casting requests it. [CHAR LIMIT=350] -->
-    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen">When you’re casting, Android has access to anything visible on your screen or played on your device. So be careful with things like passwords, payment details, messages, photos, and audio and video.</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen">When you’re casting your entire screen, anything on your screen is visible. So be careful with things like passwords, payment details, messages, photos, and audio and video.</string>
     <!-- System casting media projection permission warning for capturing a single app when SysUI casting requests it. [CHAR LIMIT=350] -->
-    <string name="media_projection_entry_cast_permission_dialog_warning_single_app">When you’re casting an app, Android has access to anything shown or played on that app. So be careful with things like passwords, payment details, messages, photos, and audio and video.</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_single_app">When you’re casting an app, anything shown or played in that app is visible. So be careful with things like passwords, payment details, messages, photos, and audio and video.</string>
     <!-- System casting media projection permission button to continue for SysUI casting. [CHAR LIMIT=60] -->
     <string name="media_projection_entry_cast_permission_dialog_continue">Start casting</string>
 
@@ -3204,9 +3227,6 @@
 
     <!-- Provider Model: Default title of the mobile network in the mobile layout. [CHAR LIMIT=50] -->
     <string name="mobile_data_settings_title">Mobile data</string>
-    <!-- Provider Model: Summary text separator for preferences including a short description
-         (eg. "Connected / 5G"). [CHAR LIMIT=50] -->
-    <string name="preference_summary_default_combination"><xliff:g id="state" example="Connected">%1$s</xliff:g> / <xliff:g id="networkMode" example="LTE">%2$s</xliff:g></string>
     <!-- Provider Model:
          Summary indicating that a SIM has an active mobile data connection [CHAR LIMIT=50] -->
     <string name="mobile_data_connection_active">Connected</string>
@@ -3663,7 +3683,10 @@
     <!-- Touchpad back gesture action name in tutorial [CHAR LIMIT=NONE] -->
     <string name="touchpad_back_gesture_action_title">Go back</string>
     <!-- Touchpad back gesture guidance in gestures tutorial [CHAR LIMIT=NONE] -->
-    <string name="touchpad_back_gesture_guidance">To go back, swipe left or right using three fingers anywhere on the touchpad.</string>
+    <string name="touchpad_back_gesture_guidance">To go back, swipe left or right using three fingers anywhere on the touchpad.\n\nYou can also use the keyboard shortcut
+Action + ESC for this.</string>
+    <!-- Text shown to the user after they complete back gesture tutorial [CHAR LIMIT=NONE] -->
+    <string name="touchpad_back_gesture_finished">You completed the go back gesture.</string>
     <string name="touchpad_back_gesture_animation_content_description">Touchpad showing three fingers moving right and left</string>
     <string name="touchpad_back_gesture_screen_animation_content_description">Device screen showing animation for back gesture</string>
 
@@ -3704,4 +3727,16 @@
     <string name="all_apps_edu_notification_title">Use your keyboard to view all apps</string>
     <!-- Education notification text for All Apps [CHAR_LIMIT=100] -->
     <string name="all_apps_edu_notification_content">Press the action key at any time. Tap to learn more gestures.</string>
+
+    <!-- Title for Extra Dim dialog [CHAR LIMIT=NONE] -->
+    <string name="accessibility_deprecate_extra_dim_dialog_title">Extra dim is now part of the brightness bar</string>
+    <!-- Content description for Extra Dim dialog. This helps users understand that we could make screen much dimmer by lowering the brightness through the brightness bar in a dark environment. [CHAR LIMIT=NONE] -->
+    <string name="accessibility_deprecate_extra_dim_dialog_description">
+        You can now make the screen extra dim by lowering the brightness level even further from the top of your screen.\n\nThis works best when you\'re in a dark environment.
+    </string>
+    <!-- Label for button removing Extra Dim shortcuts [CHAR LIMIT=NONE] -->
+    <string name="accessibility_deprecate_extra_dim_dialog_button">Remove extra dim shortcut</string>
+    <!-- Toast message for notifying users to use regular brightness bar to lower the brightness. [CHAR LIMIT=NONE] -->
+    <string name="accessibility_deprecate_extra_dim_dialog_toast">
+        Extra dim shortcut removed. To lower your brightness, use the regular brightness bar.</string>
 </resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 36912ac..7fa7088 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -192,11 +192,10 @@
 
     <style name="TextAppearance.AuthCredential.LogoDescription" parent="TextAppearance.Material3.LabelLarge" >
         <item name="android:fontFamily">@*android:string/config_bodyFontFamilyMedium</item>
-        <item name="android:ellipsize">marquee</item>
         <item name="android:gravity">@integer/biometric_dialog_text_gravity</item>
-        <item name="android:marqueeRepeatLimit">1</item>
-        <item name="android:singleLine">true</item>
+        <item name="android:maxLines">1</item>
         <item name="android:textColor">?androidprv:attr/materialColorOnSurfaceVariant</item>
+        <item name="android:ellipsize">end</item>
     </style>
 
     <style name="TextAppearance.AuthCredential.Title" parent="TextAppearance.Material3.HeadlineSmall" >
@@ -1386,9 +1385,13 @@
         <item name="android:textColor">?androidprv:attr/materialColorOnSurfaceVariant</item>
     </style>
 
-    <style name="TextAppearance.InternetDialog.Active"/>
+    <style name="TextAppearance.InternetDialog.Active">
+        <item name="android:textColor">?androidprv:attr/materialColorOnPrimaryContainer</item>
+    </style>
 
-    <style name="TextAppearance.InternetDialog.Secondary.Active"/>
+    <style name="TextAppearance.InternetDialog.Secondary.Active">
+        <item name="android:textColor">?androidprv:attr/materialColorOnPrimaryContainer</item>
+    </style>
 
     <style name="FgsManagerDialogTitle">
         <item name="android:fontFamily">@*android:string/config_bodyFontFamily</item>
@@ -1424,17 +1427,32 @@
         <item name="android:orientation">horizontal</item>
         <item name="android:focusable">true</item>
         <item name="android:clickable">true</item>
+        <item name="android:textColor">?androidprv:attr/materialColorOnSurface</item>
+    </style>
+
+    <style name="BluetoothTileDialog.Device.Active">
+        <item name="android:textColor">?androidprv:attr/materialColorOnPrimaryContainer</item>
     </style>
 
     <style name="BluetoothTileDialog.DeviceName">
         <item name="android:textSize">14sp</item>
         <item name="android:textAppearance">@style/TextAppearance.Dialog.Title</item>
+        <item name="android:textColor">?androidprv:attr/materialColorOnSurface</item>
     </style>
 
     <style name="BluetoothTileDialog.DeviceSummary">
         <item name="android:ellipsize">end</item>
         <item name="android:maxLines">2</item>
         <item name="android:textAppearance">@style/TextAppearance.Dialog.Body.Message</item>
+        <item name="android:textColor">?androidprv:attr/materialColorOnSurfaceVariant</item>
+    </style>
+
+    <style name="BluetoothTileDialog.DeviceName.Active">
+        <item name="android:textColor">?androidprv:attr/materialColorOnPrimaryContainer</item>
+    </style>
+
+    <style name="BluetoothTileDialog.DeviceSummary.Active">
+        <item name="android:textColor">?androidprv:attr/materialColorOnPrimaryContainer</item>
     </style>
 
     <style name="BroadcastDialog">
diff --git a/packages/SystemUI/res/values/tiles_states_strings.xml b/packages/SystemUI/res/values/tiles_states_strings.xml
index c702927..ad09b46 100644
--- a/packages/SystemUI/res/values/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values/tiles_states_strings.xml
@@ -85,16 +85,6 @@
         <item>On</item>
     </string-array>
 
-    <!-- State names for modes (Priority modes) tile: unavailable, off, on.
-         This subtitle is shown when the tile is in that particular state but does not set its own
-         subtitle, so some of these may never appear on screen. They should still be translated as
-         if they could appear. [CHAR LIMIT=32] -->
-    <string-array name="tile_states_modes">
-        <item>Unavailable</item>
-        <item>Off</item>
-        <item>On</item>
-    </string-array>
-
     <!-- State names for flashlight tile: unavailable, off, on.
          This subtitle is shown when the tile is in that particular state but does not set its own
          subtitle, so some of these may never appear on screen. They should still be translated as
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
index 0f11717..1342dd0 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
@@ -287,7 +287,7 @@
     /**
      * Helper used to receive device state info from {@link DeviceStateManager}.
      */
-    static class DeviceStateHelper implements DeviceStateManager.DeviceStateCallback {
+    public static class DeviceStateHelper implements DeviceStateManager.DeviceStateCallback {
 
         @Nullable
         private final DisplayAddress.Physical mRearDisplayPhysicalAddress;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index 4217820..bf905db 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -647,7 +647,12 @@
     @Override
     public void onFinishInflate() {
         super.onFinishInflate();
+        updateSecurityViewFlipper();
+    }
+
+    protected void updateSecurityViewFlipper() {
         mSecurityViewFlipper = findViewById(R.id.view_flipper);
+        setupViewMode();
     }
 
     @Override
@@ -1004,10 +1009,10 @@
 
             if (mUserSwitcherViewGroup == null) {
                 inflateUserSwitcher();
+                setupUserSwitcher();
+                mUserSwitcherController.addUserSwitchCallback(mUserSwitchCallback);
             }
             updateSecurityViewLocation();
-            setupUserSwitcher();
-            mUserSwitcherController.addUserSwitchCallback(mUserSwitchCallback);
         }
 
         @Override
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index 428cd0e..afd42cb 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -164,11 +164,6 @@
                     }
                     mCurrentUser = mSelectedUserInteractor.getSelectedUserId();
                     showPrimarySecurityScreen(false);
-                    if (mCurrentSecurityMode != SimPin
-                            && mCurrentSecurityMode != SimPuk) {
-                        reinflateViewFlipper((l) -> {
-                        });
-                    }
                 }
             };
 
@@ -375,8 +370,7 @@
 
                 @Override
                 public void onDensityOrFontScaleChanged() {
-                    KeyguardSecurityContainerController.this
-                            .onDensityOrFontScaleOrOrientationChanged();
+                    mView.onDensityOrFontScaleChanged();
                 }
 
                 @Override
@@ -724,7 +718,10 @@
     @Override
     public void onResume(int reason) {
         if (DEBUG) Log.d(TAG, "screen on, instance " + Integer.toHexString(hashCode()));
+        mView.clearFocus();
+        mView.clearAccessibilityFocus();
         mView.requestFocus();
+        mView.requestAccessibilityFocus();
         if (mCurrentSecurityMode != SecurityMode.None) {
             int state = SysUiStatsLog.KEYGUARD_BOUNCER_STATE_CHANGED__STATE__SHOWN;
             if (mView.isSidedSecurityMode()) {
@@ -1224,11 +1221,6 @@
         mView.reloadColors();
     }
 
-    /** Handles density or font scale changes. */
-    private void onDensityOrFontScaleOrOrientationChanged() {
-        reinflateViewFlipper(controller -> mView.onDensityOrFontScaleChanged());
-    }
-
     /**
      * Reinflate the view flipper child view.
      */
@@ -1236,7 +1228,10 @@
             KeyguardSecurityViewFlipperController.OnViewInflatedCallback onViewInflatedListener) {
         mSecurityViewFlipperController.clearViews();
         mSecurityViewFlipperController.asynchronouslyInflateView(mCurrentSecurityMode,
-                mKeyguardSecurityCallback, onViewInflatedListener);
+                mKeyguardSecurityCallback, (controller) -> {
+                mView.updateSecurityViewFlipper();
+                onViewInflatedListener.onViewInflated(controller);
+            });
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
index d848602..073f33fe 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
@@ -33,7 +33,6 @@
 import com.android.systemui.shade.TouchLogger;
 import com.android.systemui.statusbar.CrossFadeHelper;
 
-import java.io.PrintWriter;
 import java.util.Set;
 
 /**
@@ -117,18 +116,6 @@
         return TouchLogger.logDispatchTouch(TAG, ev, super.dispatchTouchEvent(ev));
     }
 
-    public void dump(PrintWriter pw, String[] args) {
-        pw.println("KeyguardStatusView:");
-        pw.println("  mDarkAmount: " + mDarkAmount);
-        pw.println("  visibility: " + getVisibility());
-        if (mClockView != null) {
-            mClockView.dump(pw, args);
-        }
-        if (mKeyguardSlice != null) {
-            mKeyguardSlice.dump(pw, args);
-        }
-    }
-
     @Override
     public ViewPropertyAnimator animate() {
         if (Build.IS_DEBUGGABLE) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
index 603a47e..63a4af9 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
@@ -48,9 +48,7 @@
 import com.android.internal.jank.InteractionJankMonitor;
 import com.android.keyguard.KeyguardClockSwitch.ClockSize;
 import com.android.keyguard.logging.KeyguardLogger;
-import com.android.systemui.Dumpable;
 import com.android.systemui.animation.ViewHierarchyAnimator;
-import com.android.systemui.dump.DumpManager;
 import com.android.systemui.keyguard.MigrateClocksToBlueprint;
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
 import com.android.systemui.plugins.clocks.ClockController;
@@ -70,15 +68,12 @@
 import kotlin.coroutines.CoroutineContext;
 import kotlin.coroutines.EmptyCoroutineContext;
 
-import java.io.PrintWriter;
-
 import javax.inject.Inject;
 
 /**
  * Injectable controller for {@link KeyguardStatusView}.
  */
-public class KeyguardStatusViewController extends ViewController<KeyguardStatusView> implements
-        Dumpable {
+public class KeyguardStatusViewController extends ViewController<KeyguardStatusView> {
     private static final boolean DEBUG = KeyguardConstants.DEBUG;
     @VisibleForTesting static final String TAG = "KeyguardStatusViewController";
     private static final long STATUS_AREA_HEIGHT_ANIMATION_MILLIS = 133;
@@ -108,7 +103,6 @@
 
     private Boolean mSplitShadeEnabled = false;
     private Boolean mStatusViewCentered = true;
-    private DumpManager mDumpManager;
 
     private final TransitionListenerAdapter mKeyguardStatusAlignmentTransitionListener =
             new TransitionListenerAdapter() {
@@ -176,7 +170,6 @@
             KeyguardLogger logger,
             InteractionJankMonitor interactionJankMonitor,
             KeyguardInteractor keyguardInteractor,
-            DumpManager dumpManager,
             PowerInteractor powerInteractor) {
         super(keyguardStatusView);
         mKeyguardSliceViewController = keyguardSliceViewController;
@@ -188,7 +181,6 @@
                 dozeParameters, screenOffAnimationController, /* animateYPos= */ true,
                 logger.getBuffer());
         mInteractionJankMonitor = interactionJankMonitor;
-        mDumpManager = dumpManager;
         mKeyguardInteractor = keyguardInteractor;
         mPowerInteractor = powerInteractor;
     }
@@ -222,7 +214,6 @@
                     });
         }
 
-        mDumpManager.registerDumpable(getInstanceName(), this);
         if (MigrateClocksToBlueprint.isEnabled()) {
             startCoroutines(EmptyCoroutineContext.INSTANCE);
             mView.setVisibility(View.GONE);
@@ -276,13 +267,6 @@
     }
 
     /**
-     * Called in notificationPanelViewController to avoid leak
-     */
-    public void onDestroy() {
-        mDumpManager.unregisterDumpable(getInstanceName());
-    }
-
-    /**
      * Updates views on doze time tick.
      */
     public void dozeTimeTick() {
@@ -604,11 +588,6 @@
         return mKeyguardClockSwitchController.getClock();
     }
 
-    @Override
-    public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
-        mView.dump(pw, args);
-    }
-
     String getInstanceName() {
         return TAG + "#" + hashCode();
     }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 7f5839d4..0da252d 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -2086,6 +2086,7 @@
 
     private void handleUserUnlocked(int userId) {
         Assert.isMainThread();
+        mLogger.logUserUnlocked(userId);
         mUserIsUnlocked.put(userId, true);
         mNeedsSlowUnlockTransition = resolveNeedsSlowUnlockTransition();
         for (int i = 0; i < mCallbacks.size(); i++) {
@@ -2098,12 +2099,15 @@
 
     private void handleUserStopped(int userId) {
         Assert.isMainThread();
-        mUserIsUnlocked.put(userId, mUserManager.isUserUnlocked(userId));
+        boolean isUnlocked = mUserManager.isUserUnlocked(userId);
+        mLogger.logUserStopped(userId, isUnlocked);
+        mUserIsUnlocked.put(userId, isUnlocked);
     }
 
     @VisibleForTesting
     void handleUserRemoved(int userId) {
         Assert.isMainThread();
+        mLogger.logUserRemoved(userId);
         mUserIsUnlocked.delete(userId);
         mUserTrustIsUsuallyManaged.delete(userId);
     }
@@ -2444,7 +2448,9 @@
 
         mTaskStackChangeListeners.registerTaskStackListener(mTaskStackListener);
         int user = mSelectedUserInteractor.getSelectedUserId(true);
-        mUserIsUnlocked.put(user, mUserManager.isUserUnlocked(user));
+        boolean isUserUnlocked = mUserManager.isUserUnlocked(user);
+        mLogger.logUserUnlockedInitialState(user, isUserUnlocked);
+        mUserIsUnlocked.put(user, isUserUnlocked);
         mLogoutEnabled = mDevicePolicyManager.isLogoutEnabled();
         updateSecondaryLockscreenRequirement(user);
         List<UserInfo> allUsers = mUserManager.getUsers();
@@ -4059,6 +4065,9 @@
         pw.println("    strongAuthFlags=" + Integer.toHexString(strongAuthFlags));
         pw.println("ActiveUnlockRunning="
                 + mTrustManager.isActiveUnlockRunning(mSelectedUserInteractor.getSelectedUserId()));
+        pw.println("userUnlockedCache[userid=" + userId + "]=" + isUserUnlocked(userId));
+        pw.println("actualUserUnlocked[userid=" + userId + "]="
+                + mUserManager.isUserUnlocked(userId));
         new DumpsysTableLogger(
                 "KeyguardActiveUnlockTriggers",
                 KeyguardActiveUnlockModel.TABLE_HEADERS,
diff --git a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardQuickAffordancesLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardQuickAffordancesLogger.kt
new file mode 100644
index 0000000..c11cf55
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardQuickAffordancesLogger.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.keyguard.logging
+
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.core.LogLevel
+import com.android.systemui.log.dagger.KeyguardQuickAffordancesLog
+import javax.inject.Inject
+
+class KeyguardQuickAffordancesLogger
+@Inject
+constructor(
+    @KeyguardQuickAffordancesLog val buffer: LogBuffer,
+) {
+    fun logQuickAffordanceTapped(configKey: String?) {
+        val (slotId, affordanceId) = configKey?.decode() ?: ("" to "")
+        buffer.log(
+            TAG,
+            LogLevel.DEBUG,
+            {
+                str1 = affordanceId
+                str2 = slotId
+            },
+            { "QuickAffordance tapped with id: $str1, in slot: $str2" }
+        )
+    }
+
+    fun logQuickAffordanceTriggered(slotId: String, affordanceId: String) {
+        buffer.log(
+            TAG,
+            LogLevel.DEBUG,
+            {
+                str1 = affordanceId
+                str2 = slotId
+            },
+            { "QuickAffordance triggered with id: $str1, in slot: $str2" }
+        )
+    }
+
+    fun logQuickAffordanceSelected(slotId: String, affordanceId: String) {
+        buffer.log(
+            TAG,
+            LogLevel.DEBUG,
+            {
+                str1 = affordanceId
+                str2 = slotId
+            },
+            { "QuickAffordance selected with id: $str1, in slot: $str2" }
+        )
+    }
+
+    private fun String.decode(): Pair<String, String> {
+        val splitUp = this.split(DELIMITER)
+        return Pair(splitUp[0], splitUp[1])
+    }
+
+    companion object {
+        private const val TAG = "KeyguardQuickAffordancesLogger"
+        private const val DELIMITER = "::"
+    }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
index 1f4e732..0b58f06 100644
--- a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
+++ b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
@@ -391,6 +391,7 @@
             { "handleTimeFormatUpdate timeFormat=$str1" }
         )
     }
+
     fun logUdfpsPointerDown(sensorId: Int) {
         logBuffer.log(TAG, DEBUG, { int1 = sensorId }, { "onUdfpsPointerDown, sensorId: $int1" })
     }
@@ -639,12 +640,45 @@
             { "fingerprint acquire message: $int1" }
         )
     }
+
     fun logForceIsDismissibleKeyguard(keepUnlocked: Boolean) {
         logBuffer.log(
-                TAG,
-                DEBUG,
-                { bool1 = keepUnlocked },
-                { "keepUnlockedOnFold changed to: $bool1" }
+            TAG,
+            DEBUG,
+            { bool1 = keepUnlocked },
+            { "keepUnlockedOnFold changed to: $bool1" }
+        )
+    }
+
+    fun logUserUnlocked(userId: Int) {
+        logBuffer.log(TAG, DEBUG, { int1 = userId }, { "userUnlocked userId: $int1" })
+    }
+
+    fun logUserStopped(userId: Int, isUnlocked: Boolean) {
+        logBuffer.log(
+            TAG,
+            DEBUG,
+            {
+                int1 = userId
+                bool1 = isUnlocked
+            },
+            { "userStopped userId: $int1 isUnlocked: $bool1" }
+        )
+    }
+
+    fun logUserRemoved(userId: Int) {
+        logBuffer.log(TAG, DEBUG, { int1 = userId }, { "userRemoved userId: $int1" })
+    }
+
+    fun logUserUnlockedInitialState(userId: Int, isUnlocked: Boolean) {
+        logBuffer.log(
+            TAG,
+            DEBUG,
+            {
+                int1 = userId
+                bool1 = isUnlocked
+            },
+            { "userUnlockedInitialState userId: $int1 isUnlocked: $bool1" }
         )
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
index 56de5a3..8ae11ab 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
@@ -24,7 +24,6 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.content.pm.ApplicationInfo;
 import android.content.res.Configuration;
 import android.os.Bundle;
 import android.os.Process;
@@ -81,7 +80,9 @@
 
     public SystemUIApplication() {
         super();
-        Trace.registerWithPerfetto();
+        if (!isSubprocess()) {
+            Trace.registerWithPerfetto();
+        }
         Log.v(TAG, "SystemUIApplication constructed.");
         // SysUI may be building without protolog preprocessing in some cases
         ProtoLog.REQUIRE_PROTOLOGTOOL = false;
@@ -182,9 +183,7 @@
         } else {
             // We don't need to startServices for sub-process that is doing some tasks.
             // (screenshots, sweetsweetdesserts or tuner ..)
-            String processName = ActivityThread.currentProcessName();
-            ApplicationInfo info = getApplicationInfo();
-            if (processName != null && processName.startsWith(info.processName + ":")) {
+            if (isSubprocess()) {
                 return;
             }
             // For a secondary user, boot-completed will never be called because it has already
@@ -195,6 +194,12 @@
         }
     }
 
+    /** Returns whether this is a subprocess (e.g. com.android.systemui:screenshot) */
+    private boolean isSubprocess() {
+        String processName = ActivityThread.currentProcessName();
+        return processName != null && processName.contains(":");
+    }
+
     /**
      * Makes sure that all the CoreStartables are running. If they are already running, this is a
      * no-op. This is needed to conditionally start all the services, as we only need to have it in
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/FullscreenMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/FullscreenMagnificationController.java
index 3c0ac9a..394f8dd 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/FullscreenMagnificationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/FullscreenMagnificationController.java
@@ -30,6 +30,8 @@
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
 import android.graphics.Region;
+import android.graphics.drawable.GradientDrawable;
+import android.hardware.display.DisplayManager;
 import android.os.Handler;
 import android.util.Log;
 import android.view.AttachedSurfaceControl;
@@ -49,6 +51,8 @@
 import androidx.annotation.UiThread;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.policy.ScreenDecorationsUtils;
+import com.android.systemui.Flags;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.res.R;
 import com.android.systemui.util.leak.RotationUtils;
@@ -70,6 +74,7 @@
     private SurfaceControl.Transaction mTransaction;
     private View mFullscreenBorder = null;
     private int mBorderOffset;
+    private int mBorderStoke;
     private final int mDisplayId;
     private static final Region sEmptyRegion = new Region();
     private ValueAnimator mShowHideBorderAnimator;
@@ -86,16 +91,20 @@
         }
     };
     private final long mLongAnimationTimeMs;
+    private final DisplayManager mDisplayManager;
+    private final DisplayManager.DisplayListener mDisplayListener;
+    private String mCurrentDisplayUniqueId;
 
     public FullscreenMagnificationController(
             @UiContext Context context,
             @Main Handler handler,
             @Main Executor executor,
+            DisplayManager displayManager,
             AccessibilityManager accessibilityManager,
             WindowManager windowManager,
             IWindowManager iWindowManager,
             Supplier<SurfaceControlViewHost> scvhSupplier) {
-        this(context, handler, executor, accessibilityManager,
+        this(context, handler, executor, displayManager, accessibilityManager,
                 windowManager, iWindowManager, scvhSupplier,
                 new SurfaceControl.Transaction(), null);
     }
@@ -105,6 +114,7 @@
             @UiContext Context context,
             @Main Handler handler,
             @Main Executor executor,
+            DisplayManager displayManager,
             AccessibilityManager accessibilityManager,
             WindowManager windowManager,
             IWindowManager iWindowManager,
@@ -120,10 +130,7 @@
         mWindowBounds = mWindowManager.getCurrentWindowMetrics().getBounds();
         mTransaction = transaction;
         mScvhSupplier = scvhSupplier;
-        mBorderOffset = mContext.getResources().getDimensionPixelSize(
-                R.dimen.magnifier_border_width_fullscreen_with_offset)
-                - mContext.getResources().getDimensionPixelSize(
-                R.dimen.magnifier_border_width_fullscreen);
+        updateDimensions();
         mDisplayId = mContext.getDisplayId();
         mConfiguration = new Configuration(context.getResources().getConfiguration());
         mLongAnimationTimeMs = mContext.getResources().getInteger(
@@ -140,6 +147,31 @@
                 }
             }
         });
+        mCurrentDisplayUniqueId = mContext.getDisplayNoVerify().getUniqueId();
+        mDisplayManager = displayManager;
+        mDisplayListener = new DisplayManager.DisplayListener() {
+            @Override
+            public void onDisplayAdded(int displayId) {
+                // Do nothing
+            }
+
+            @Override
+            public void onDisplayRemoved(int displayId) {
+                // Do nothing
+            }
+
+            @Override
+            public void onDisplayChanged(int displayId) {
+                final String uniqueId = mContext.getDisplayNoVerify().getUniqueId();
+                if (uniqueId.equals(mCurrentDisplayUniqueId)) {
+                    // Same unique ID means the physical display doesn't change. Early return.
+                    return;
+                }
+
+                mCurrentDisplayUniqueId = uniqueId;
+                applyCornerRadiusToBorder();
+            }
+        };
     }
 
     private ValueAnimator createNullTargetObjectAnimator() {
@@ -180,10 +212,15 @@
         }
         mContext.unregisterComponentCallbacks(this);
 
+
         mShowHideBorderAnimator.reverse();
     }
 
     private void cleanUpBorder() {
+        if (Flags.updateCornerRadiusOnDisplayChanged()) {
+            mDisplayManager.unregisterDisplayListener(mDisplayListener);
+        }
+
         if (mSurfaceControlViewHost != null) {
             mSurfaceControlViewHost.release();
             mSurfaceControlViewHost = null;
@@ -226,6 +263,9 @@
             } catch (Exception e) {
                 Log.w(TAG, "Failed to register rotation watcher", e);
             }
+            if (Flags.updateCornerRadiusOnDisplayChanged()) {
+                mHandler.post(this::applyCornerRadiusToBorder);
+            }
         }
 
         mTransaction
@@ -247,6 +287,9 @@
 
         mAccessibilityManager.attachAccessibilityOverlayToDisplay(
                 mDisplayId, mBorderSurfaceControl);
+        if (Flags.updateCornerRadiusOnDisplayChanged()) {
+            mDisplayManager.registerDisplayListener(mDisplayListener, mHandler);
+        }
 
         applyTouchableRegion();
     }
@@ -304,6 +347,11 @@
             final int newWidth = mWindowBounds.width() + 2 * mBorderOffset;
             final int newHeight = mWindowBounds.height() + 2 * mBorderOffset;
             mSurfaceControlViewHost.relayout(newWidth, newHeight);
+            if (Flags.updateCornerRadiusOnDisplayChanged()) {
+                // Recenter the border
+                mTransaction.setPosition(
+                        mBorderSurfaceControl, -mBorderOffset, -mBorderOffset).apply();
+            }
         }
 
         // Rotating from Landscape to ReverseLandscape will not trigger the config changes in
@@ -352,6 +400,22 @@
                 R.dimen.magnifier_border_width_fullscreen_with_offset)
                 - mContext.getResources().getDimensionPixelSize(
                         R.dimen.magnifier_border_width_fullscreen);
+        mBorderStoke = mContext.getResources().getDimensionPixelSize(
+                R.dimen.magnifier_border_width_fullscreen_with_offset);
+    }
+
+    private void applyCornerRadiusToBorder() {
+        if (!isActivated()) {
+            return;
+        }
+
+        float cornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(mContext);
+        GradientDrawable backgroundDrawable = (GradientDrawable) mFullscreenBorder.getBackground();
+        backgroundDrawable.setStroke(
+                mBorderStoke,
+                mContext.getResources().getColor(
+                        R.color.magnification_border_color, mContext.getTheme()));
+        backgroundDrawable.setCornerRadius(cornerRadius);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationImpl.java b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationImpl.java
index e9c9bc7..93c4630 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationImpl.java
@@ -149,6 +149,7 @@
         private final Context mContext;
         private final Handler mHandler;
         private final Executor mExecutor;
+        private final DisplayManager mDisplayManager;
         private final IWindowManager mIWindowManager;
 
         FullscreenMagnificationControllerSupplier(Context context,
@@ -159,6 +160,7 @@
             mContext = context;
             mHandler = handler;
             mExecutor = executor;
+            mDisplayManager = displayManager;
             mIWindowManager = iWindowManager;
         }
 
@@ -173,6 +175,7 @@
                     windowContext,
                     mHandler,
                     mExecutor,
+                    mDisplayManager,
                     windowContext.getSystemService(AccessibilityManager.class),
                     windowContext.getSystemService(WindowManager.class),
                     mIWindowManager,
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/extradim/ExtraDimDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/accessibility/extradim/ExtraDimDialogDelegate.kt
new file mode 100644
index 0000000..fcb1206
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/extradim/ExtraDimDialogDelegate.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.accessibility.extradim
+
+import android.content.Context
+import android.content.DialogInterface
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.accessibility.AccessibilityManager
+import android.widget.Toast
+import com.android.internal.accessibility.AccessibilityShortcutController
+import com.android.internal.accessibility.common.ShortcutConstants
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.res.R
+import com.android.systemui.settings.UserTracker
+import com.android.systemui.statusbar.phone.SystemUIDialog
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+
+/** Dialog for removing Extra Dim shortcuts. */
+class ExtraDimDialogDelegate
+@Inject
+constructor(
+    private val context: Context,
+    @Application private val applicationScope: CoroutineScope,
+    @Background private val backgroundDispatcher: CoroutineDispatcher,
+    private val systemUIDialogFactory: SystemUIDialog.Factory,
+    private val accessibilityManager: AccessibilityManager,
+    private val userTracker: UserTracker,
+) : SystemUIDialog.Delegate {
+
+    private val onClickListener: DialogInterface.OnClickListener =
+        DialogInterface.OnClickListener { dialog, _ ->
+            applicationScope.launch {
+                dialog.dismiss()
+                onRemoveExtraDimShortcutButtonClicked()
+                Toast.makeText(
+                        context,
+                        context.getText(R.string.accessibility_deprecate_extra_dim_dialog_toast),
+                        Toast.LENGTH_LONG
+                    )
+                    .show()
+            }
+        }
+
+    override fun beforeCreate(dialog: SystemUIDialog, savedInstanceState: Bundle?) {
+        dialog.setTitle(R.string.accessibility_deprecate_extra_dim_dialog_title)
+        dialog.setView(
+            LayoutInflater.from(dialog.context)
+                .inflate(R.layout.accessibility_deprecate_extra_dim_dialog, null)
+        )
+        dialog.setPositiveButton(
+            R.string.accessibility_deprecate_extra_dim_dialog_button,
+            onClickListener
+        )
+    }
+
+    override fun createDialog(): SystemUIDialog {
+        val dialog = systemUIDialogFactory.create(this)
+        dialog.setCanceledOnTouchOutside(false)
+        return dialog
+    }
+
+    private suspend fun onRemoveExtraDimShortcutButtonClicked() =
+        withContext(backgroundDispatcher) {
+            accessibilityManager.enableShortcutsForTargets(
+                /* enable= */ false,
+                ShortcutConstants.UserShortcutType.ALL,
+                setOf(
+                    AccessibilityShortcutController.REDUCE_BRIGHT_COLORS_COMPONENT_NAME
+                        .flattenToString()
+                ),
+                userTracker.userId
+            )
+        }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/extradim/ExtraDimDialogManager.kt b/packages/SystemUI/src/com/android/systemui/accessibility/extradim/ExtraDimDialogManager.kt
new file mode 100644
index 0000000..e1297d3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/extradim/ExtraDimDialogManager.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.accessibility.extradim
+
+import androidx.annotation.VisibleForTesting
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.statusbar.phone.SystemUIDialog
+import javax.inject.Inject
+import javax.inject.Provider
+
+/** Managing the Extra Dim Dialog behaviors. */
+@SysUISingleton
+class ExtraDimDialogManager
+@Inject
+constructor(
+    private val extraDimDialogDelegateProvider: Provider<ExtraDimDialogDelegate>,
+    private val mActivityStarter: ActivityStarter
+) {
+    private var dialog: SystemUIDialog? = null
+
+    @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
+    fun dismissKeyguardIfNeededAndShowDialog() {
+        mActivityStarter.executeRunnableDismissingKeyguard(
+            { showRemoveExtraDimShortcutsDialog() },
+            /* cancelAction= */ null,
+            /* dismissShade= */ false,
+            /* afterKeyguardGone= */ true,
+            /* deferred= */ false
+        )
+    }
+
+    /** Show the dialog for removing all Extra Dim shortcuts. */
+    private fun showRemoveExtraDimShortcutsDialog() {
+        dialog?.dismiss()
+        dialog = extraDimDialogDelegateProvider.get().createDialog()
+        dialog!!.show()
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/extradim/ExtraDimDialogReceiver.kt b/packages/SystemUI/src/com/android/systemui/accessibility/extradim/ExtraDimDialogReceiver.kt
new file mode 100644
index 0000000..405993a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/extradim/ExtraDimDialogReceiver.kt
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.accessibility.extradim
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import com.android.server.display.feature.flags.Flags
+import javax.inject.Inject
+
+/**
+ * BroadcastReceiver for handling [ExtraDimDialogDelegate] intent.
+ *
+ * This is not exported. Need to call from framework and use SYSTEM user to send the intent.
+ */
+class ExtraDimDialogReceiver
+@Inject
+constructor(
+    private val extraDimDialogManager: ExtraDimDialogManager,
+) : BroadcastReceiver() {
+
+    override fun onReceive(context: Context, intent: Intent) {
+        if (
+            !Flags.evenDimmer() ||
+                !context
+                    .getResources()
+                    .getBoolean(com.android.internal.R.bool.config_evenDimmerEnabled)
+        ) {
+            return
+        }
+
+        if (ACTION == intent.action) {
+            extraDimDialogManager.dismissKeyguardIfNeededAndShowDialog()
+        }
+    }
+
+    companion object {
+        const val ACTION = "com.android.systemui.action.LAUNCH_REMOVE_EXTRA_DIM_DIALOG"
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java
index f041f4d..083f1db 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java
@@ -52,6 +52,7 @@
 import androidx.recyclerview.widget.LinearLayoutManager;
 import androidx.recyclerview.widget.RecyclerView;
 
+import com.android.internal.logging.UiEventLogger;
 import com.android.settingslib.bluetooth.BluetoothCallback;
 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
 import com.android.settingslib.bluetooth.HapClientProfile;
@@ -104,6 +105,7 @@
     private final AudioManager mAudioManager;
     private final LocalBluetoothProfileManager mProfileManager;
     private final HapClientProfile mHapClientProfile;
+    private final UiEventLogger mUiEventLogger;
     private HearingDevicesListAdapter mDeviceListAdapter;
     private HearingDevicesPresetsController mPresetsController;
     private Context mApplicationContext;
@@ -163,7 +165,8 @@
             DialogTransitionAnimator dialogTransitionAnimator,
             @Nullable LocalBluetoothManager localBluetoothManager,
             @Main Handler handler,
-            AudioManager audioManager) {
+            AudioManager audioManager,
+            UiEventLogger uiEventLogger) {
         mApplicationContext = applicationContext;
         mShowPairNewDevice = showPairNewDevice;
         mSystemUIDialogFactory = systemUIDialogFactory;
@@ -174,6 +177,7 @@
         mAudioManager = audioManager;
         mProfileManager = localBluetoothManager.getProfileManager();
         mHapClientProfile = mProfileManager.getHapClientProfile();
+        mUiEventLogger = uiEventLogger;
     }
 
     @Override
@@ -187,6 +191,7 @@
 
     @Override
     public void onDeviceItemGearClicked(@NonNull DeviceItem deviceItem, @NonNull View view) {
+        mUiEventLogger.log(HearingDevicesUiEvent.HEARING_DEVICES_GEAR_CLICK);
         dismissDialogIfExists();
         Intent intent = new Intent(ACTION_BLUETOOTH_DEVICE_DETAILS);
         Bundle bundle = new Bundle();
@@ -198,13 +203,21 @@
     }
 
     @Override
-    public void onDeviceItemOnClicked(@NonNull DeviceItem deviceItem, @NonNull View view) {
+    public void onDeviceItemClicked(@NonNull DeviceItem deviceItem, @NonNull View view) {
         CachedBluetoothDevice cachedBluetoothDevice = deviceItem.getCachedBluetoothDevice();
         switch (deviceItem.getType()) {
-            case ACTIVE_MEDIA_BLUETOOTH_DEVICE, CONNECTED_BLUETOOTH_DEVICE ->
-                    cachedBluetoothDevice.disconnect();
-            case AVAILABLE_MEDIA_BLUETOOTH_DEVICE -> cachedBluetoothDevice.setActive();
-            case SAVED_BLUETOOTH_DEVICE -> cachedBluetoothDevice.connect();
+            case ACTIVE_MEDIA_BLUETOOTH_DEVICE, CONNECTED_BLUETOOTH_DEVICE -> {
+                mUiEventLogger.log(HearingDevicesUiEvent.HEARING_DEVICES_DISCONNECT);
+                cachedBluetoothDevice.disconnect();
+            }
+            case AVAILABLE_MEDIA_BLUETOOTH_DEVICE -> {
+                mUiEventLogger.log(HearingDevicesUiEvent.HEARING_DEVICES_SET_ACTIVE);
+                cachedBluetoothDevice.setActive();
+            }
+            case SAVED_BLUETOOTH_DEVICE -> {
+                mUiEventLogger.log(HearingDevicesUiEvent.HEARING_DEVICES_CONNECT);
+                cachedBluetoothDevice.connect();
+            }
         }
     }
 
@@ -262,6 +275,7 @@
         if (mLocalBluetoothManager == null) {
             return;
         }
+        mUiEventLogger.log(HearingDevicesUiEvent.HEARING_DEVICES_DIALOG_SHOW);
         mPairButton = dialog.requireViewById(R.id.pair_new_device_button);
         mDeviceList = dialog.requireViewById(R.id.device_list);
         mPresetSpinner = dialog.requireViewById(R.id.preset_spinner);
@@ -341,12 +355,17 @@
             }
         });
 
+        // Refresh the spinner and setSelection(index, false) before setOnItemSelectedListener() to
+        // avoid extra onItemSelected() get called when first register the listener.
+        final List<BluetoothHapPresetInfo> presetInfos = mPresetsController.getAllPresetInfo();
+        final int activePresetIndex = mPresetsController.getActivePresetIndex();
+        refreshPresetInfoAdapter(presetInfos, activePresetIndex);
         mPresetSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
             @Override
             public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
+                mUiEventLogger.log(HearingDevicesUiEvent.HEARING_DEVICES_PRESET_SELECT);
                 mPresetsController.selectPreset(
                         mPresetsController.getAllPresetInfo().get(position).getIndex());
-                mPresetSpinner.setSelection(position);
             }
 
             @Override
@@ -354,9 +373,6 @@
                 // Do nothing
             }
         });
-        final List<BluetoothHapPresetInfo> presetInfos = mPresetsController.getAllPresetInfo();
-        final int activePresetIndex = mPresetsController.getActivePresetIndex();
-        refreshPresetInfoAdapter(presetInfos, activePresetIndex);
         mPresetSpinner.setVisibility(
                 (activeHearingDevice != null && activeHearingDevice.isConnectedHapClientDevice()
                         && !mPresetInfoAdapter.isEmpty()) ? VISIBLE : GONE);
@@ -365,6 +381,7 @@
     private void setupPairNewDeviceButton(SystemUIDialog dialog, @Visibility int visibility) {
         if (visibility == VISIBLE) {
             mPairButton.setOnClickListener(v -> {
+                mUiEventLogger.log(HearingDevicesUiEvent.HEARING_DEVICES_PAIR);
                 dismissDialogIfExists();
                 final Intent intent = new Intent(Settings.ACTION_HEARING_DEVICE_PAIRING_SETTINGS);
                 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
@@ -413,7 +430,7 @@
             final int size = mPresetInfoAdapter.getCount();
             for (int position = 0; position < size; position++) {
                 if (presetInfos.get(position).getIndex() == activePresetIndex) {
-                    mPresetSpinner.setSelection(position);
+                    mPresetSpinner.setSelection(position, /* animate= */ false);
                 }
             }
         }
@@ -464,12 +481,15 @@
         text.setText(item.getToolName());
         Intent intent = item.getToolIntent();
         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
-        view.setOnClickListener(
-                v -> {
-                    dismissDialogIfExists();
-                    mActivityStarter.postStartActivityDismissingKeyguard(intent, /* delay= */ 0,
-                            mDialogTransitionAnimator.createActivityTransitionController(view));
-                });
+        view.setOnClickListener(v -> {
+            final String name = intent.getComponent() != null
+                    ? intent.getComponent().flattenToString()
+                    : intent.getPackage() + "/" + intent.getAction();
+            mUiEventLogger.log(HearingDevicesUiEvent.HEARING_DEVICES_RELATED_TOOL_CLICK, 0, name);
+            dismissDialogIfExists();
+            mActivityStarter.postStartActivityDismissingKeyguard(intent, /* delay= */ 0,
+                    mDialogTransitionAnimator.createActivityTransitionController(view));
+        });
         return view;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesListAdapter.java b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesListAdapter.java
index 737805b..b46b8fe 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesListAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesListAdapter.java
@@ -96,7 +96,7 @@
          * @param deviceItem bluetooth device item
          * @param view       the view that was clicked
          */
-        void onDeviceItemOnClicked(@NonNull DeviceItem deviceItem, @NonNull View view);
+        void onDeviceItemClicked(@NonNull DeviceItem deviceItem, @NonNull View view);
     }
 
     private static class DeviceItemViewHolder extends RecyclerView.ViewHolder {
@@ -119,7 +119,7 @@
 
         public void bindView(DeviceItem item, HearingDeviceItemCallback callback) {
             mContainer.setEnabled(item.isEnabled());
-            mContainer.setOnClickListener(view -> callback.onDeviceItemOnClicked(item, view));
+            mContainer.setOnClickListener(view -> callback.onDeviceItemClicked(item, view));
             Integer backgroundResId = item.getBackground();
             if (backgroundResId != null) {
                 mContainer.setBackground(mContext.getDrawable(item.getBackground()));
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesUiEvent.java b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesUiEvent.java
new file mode 100644
index 0000000..3fbe56e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesUiEvent.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.accessibility.hearingaid;
+
+import com.android.internal.logging.UiEvent;
+import com.android.internal.logging.UiEventLogger;
+
+public enum HearingDevicesUiEvent implements UiEventLogger.UiEventEnum {
+
+    @UiEvent(doc = "Hearing devices dialog is shown")
+    HEARING_DEVICES_DIALOG_SHOW(1848),
+    @UiEvent(doc = "Pair new device")
+    HEARING_DEVICES_PAIR(1849),
+    @UiEvent(doc = "Connect to the device")
+    HEARING_DEVICES_CONNECT(1850),
+    @UiEvent(doc = "Disconnect from the device")
+    HEARING_DEVICES_DISCONNECT(1851),
+    @UiEvent(doc = "Set the device as active device")
+    HEARING_DEVICES_SET_ACTIVE(1852),
+    @UiEvent(doc = "Click on the device gear to enter device detail page")
+    HEARING_DEVICES_GEAR_CLICK(1853),
+    @UiEvent(doc = "Select a preset from preset spinner")
+    HEARING_DEVICES_PRESET_SELECT(1854),
+    @UiEvent(doc = "Click on related tool")
+    HEARING_DEVICES_RELATED_TOOL_CLICK(1856);
+
+    private final int mId;
+
+    HearingDevicesUiEvent(int id) {
+        mId = id;
+    }
+
+    @Override
+    public int getId() {
+        return mId;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/activatable/Activatable.kt b/packages/SystemUI/src/com/android/systemui/activatable/Activatable.kt
new file mode 100644
index 0000000..dc2d931
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/activatable/Activatable.kt
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.activatable
+
+/** Defines interface for classes that can be activated to do coroutine work. */
+interface Activatable {
+
+    /**
+     * Activates this object.
+     *
+     * Serves as an entrypoint to kick off coroutine work that the object requires in order to keep
+     * its state fresh and/or perform side-effects.
+     *
+     * The method suspends and doesn't return until all work required by the object is finished. In
+     * most cases, it's expected for the work to remain ongoing forever so this method will forever
+     * suspend its caller until the coroutine that called it is canceled.
+     *
+     * Implementations could follow this pattern:
+     * ```kotlin
+     * override suspend fun activate() {
+     *     coroutineScope {
+     *         launch { ... }
+     *         launch { ... }
+     *         launch { ... }
+     *     }
+     * }
+     * ```
+     *
+     * **Must be invoked** by the owner of the object when the object is to become active.
+     * Similarly, the work must be canceled by the owner when the objects is to be deactivated.
+     *
+     * One way to have a parent call this would be by using a `LaunchedEffect` in Compose:
+     * ```kotlin
+     * @Composable
+     * fun MyUi(activatable: Activatable) {
+     *     LaunchedEffect(activatable) {
+     *         activatable.activate()
+     *     }
+     * }
+     * ```
+     */
+    suspend fun activate()
+}
diff --git a/packages/SystemUI/src/com/android/systemui/ambient/touch/BouncerSwipeTouchHandler.java b/packages/SystemUI/src/com/android/systemui/ambient/touch/BouncerSwipeTouchHandler.java
deleted file mode 100644
index 636bc5b..0000000
--- a/packages/SystemUI/src/com/android/systemui/ambient/touch/BouncerSwipeTouchHandler.java
+++ /dev/null
@@ -1,395 +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.ambient.touch;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
-import android.graphics.Rect;
-import android.graphics.Region;
-import android.util.Log;
-import android.view.GestureDetector;
-import android.view.InputEvent;
-import android.view.MotionEvent;
-import android.view.VelocityTracker;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.VisibleForTesting;
-
-import com.android.internal.logging.UiEvent;
-import com.android.internal.logging.UiEventLogger;
-import com.android.internal.widget.LockPatternUtils;
-import com.android.systemui.Flags;
-import com.android.systemui.ambient.touch.dagger.BouncerSwipeModule;
-import com.android.systemui.ambient.touch.scrim.ScrimController;
-import com.android.systemui.ambient.touch.scrim.ScrimManager;
-import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants;
-import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.settings.UserTracker;
-import com.android.systemui.shade.ShadeExpansionChangeEvent;
-import com.android.systemui.statusbar.NotificationShadeWindowController;
-import com.android.systemui.statusbar.phone.CentralSurfaces;
-import com.android.wm.shell.animation.FlingAnimationUtils;
-
-import java.util.Optional;
-
-import javax.inject.Inject;
-import javax.inject.Named;
-
-/**
- * Monitor for tracking touches on the DreamOverlay to bring up the bouncer.
- */
-public class BouncerSwipeTouchHandler implements TouchHandler {
-    /**
-     * An interface for creating ValueAnimators.
-     */
-    public interface ValueAnimatorCreator {
-        /**
-         * Creates {@link ValueAnimator}.
-         */
-        ValueAnimator create(float start, float finish);
-    }
-
-    /**
-     * An interface for obtaining VelocityTrackers.
-     */
-    public interface VelocityTrackerFactory {
-        /**
-         * Obtains {@link VelocityTracker}.
-         */
-        VelocityTracker obtain();
-    }
-
-    public static final float FLING_PERCENTAGE_THRESHOLD = 0.5f;
-
-    private static final String TAG = "BouncerSwipeTouchHandler";
-    private final NotificationShadeWindowController mNotificationShadeWindowController;
-    private final LockPatternUtils mLockPatternUtils;
-    private final UserTracker mUserTracker;
-    private final float mBouncerZoneScreenPercentage;
-    private final float mMinBouncerZoneScreenPercentage;
-
-    private final ScrimManager mScrimManager;
-    private ScrimController mCurrentScrimController;
-    private float mCurrentExpansion;
-    private final Optional<CentralSurfaces> mCentralSurfaces;
-
-    private VelocityTracker mVelocityTracker;
-
-    private final FlingAnimationUtils mFlingAnimationUtils;
-    private final FlingAnimationUtils mFlingAnimationUtilsClosing;
-
-    private Boolean mCapture;
-    private Boolean mExpanded;
-
-    private TouchSession mTouchSession;
-
-    private final ValueAnimatorCreator mValueAnimatorCreator;
-
-    private final VelocityTrackerFactory mVelocityTrackerFactory;
-
-    private final UiEventLogger mUiEventLogger;
-
-    private final ActivityStarter mActivityStarter;
-
-    private final ScrimManager.Callback mScrimManagerCallback = new ScrimManager.Callback() {
-        @Override
-        public void onScrimControllerChanged(ScrimController controller) {
-            if (mCurrentScrimController != null) {
-                mCurrentScrimController.reset();
-            }
-
-            mCurrentScrimController = controller;
-        }
-    };
-
-    private final GestureDetector.OnGestureListener mOnGestureListener =
-            new GestureDetector.SimpleOnGestureListener() {
-                @Override
-                public boolean onScroll(MotionEvent e1, @NonNull MotionEvent e2, float distanceX,
-                        float distanceY) {
-                    if (mCapture == null) {
-                        if (Flags.dreamOverlayBouncerSwipeDirectionFiltering()) {
-                            mCapture = Math.abs(distanceY) > Math.abs(distanceX)
-                                    && distanceY > 0;
-                        } else {
-                            // If the user scrolling favors a vertical direction, begin capturing
-                            // scrolls.
-                            mCapture = Math.abs(distanceY) > Math.abs(distanceX);
-                        }
-                        if (mCapture) {
-                            // reset expanding
-                            mExpanded = false;
-                            // Since the user is dragging the bouncer up, set scrimmed to false.
-                            mCurrentScrimController.show();
-                        }
-                    }
-
-                    if (!mCapture) {
-                        return false;
-                    }
-
-                    // Don't set expansion for downward scroll.
-                    if (e1.getY() < e2.getY()) {
-                        return true;
-                    }
-
-                    if (!mCentralSurfaces.isPresent()) {
-                        return true;
-                    }
-
-                    // If scrolling up and keyguard is not locked, dismiss both keyguard and the
-                    // dream since there's no bouncer to show.
-                    if (e1.getY() > e2.getY()
-                            && !mLockPatternUtils.isSecure(mUserTracker.getUserId())) {
-                        mActivityStarter.executeRunnableDismissingKeyguard(
-                                () -> mCentralSurfaces.get().awakenDreams(),
-                                /* cancelAction= */ null,
-                                /* dismissShade= */ true,
-                                /* afterKeyguardGone= */ true,
-                                /* deferred= */ false);
-                        return true;
-                    }
-
-                    // For consistency, we adopt the expansion definition found in the
-                    // PanelViewController. In this case, expansion refers to the view above the
-                    // bouncer. As that view's expansion shrinks, the bouncer appears. The bouncer
-                    // is fully hidden at full expansion (1) and fully visible when fully collapsed
-                    // (0).
-                    final float screenTravelPercentage = Math.abs(e1.getY() - e2.getY())
-                            / mTouchSession.getBounds().height();
-                    setPanelExpansion(1 - screenTravelPercentage);
-                    return true;
-                }
-            };
-
-    private void setPanelExpansion(float expansion) {
-        mCurrentExpansion = expansion;
-        ShadeExpansionChangeEvent event =
-                new ShadeExpansionChangeEvent(
-                        /* fraction= */ mCurrentExpansion,
-                        /* expanded= */ mExpanded,
-                        /* tracking= */ true);
-        mCurrentScrimController.expand(event);
-    }
-
-
-    @VisibleForTesting
-    public enum DreamEvent implements UiEventLogger.UiEventEnum {
-        @UiEvent(doc = "The screensaver has been swiped up.")
-        DREAM_SWIPED(988),
-
-        @UiEvent(doc = "The bouncer has become fully visible over dream.")
-        DREAM_BOUNCER_FULLY_VISIBLE(1056);
-
-        private final int mId;
-
-        DreamEvent(int id) {
-            mId = id;
-        }
-
-        @Override
-        public int getId() {
-            return mId;
-        }
-    }
-
-    @Inject
-    public BouncerSwipeTouchHandler(
-            ScrimManager scrimManager,
-            Optional<CentralSurfaces> centralSurfaces,
-            NotificationShadeWindowController notificationShadeWindowController,
-            ValueAnimatorCreator valueAnimatorCreator,
-            VelocityTrackerFactory velocityTrackerFactory,
-            LockPatternUtils lockPatternUtils,
-            UserTracker userTracker,
-            @Named(BouncerSwipeModule.SWIPE_TO_BOUNCER_FLING_ANIMATION_UTILS_OPENING)
-            FlingAnimationUtils flingAnimationUtils,
-            @Named(BouncerSwipeModule.SWIPE_TO_BOUNCER_FLING_ANIMATION_UTILS_CLOSING)
-            FlingAnimationUtils flingAnimationUtilsClosing,
-            @Named(BouncerSwipeModule.SWIPE_TO_BOUNCER_START_REGION) float swipeRegionPercentage,
-            @Named(BouncerSwipeModule.MIN_BOUNCER_ZONE_SCREEN_PERCENTAGE) float minRegionPercentage,
-            UiEventLogger uiEventLogger,
-            ActivityStarter activityStarter) {
-        mCentralSurfaces = centralSurfaces;
-        mScrimManager = scrimManager;
-        mNotificationShadeWindowController = notificationShadeWindowController;
-        mLockPatternUtils = lockPatternUtils;
-        mUserTracker = userTracker;
-        mBouncerZoneScreenPercentage = swipeRegionPercentage;
-        mMinBouncerZoneScreenPercentage = minRegionPercentage;
-        mFlingAnimationUtils = flingAnimationUtils;
-        mFlingAnimationUtilsClosing = flingAnimationUtilsClosing;
-        mValueAnimatorCreator = valueAnimatorCreator;
-        mVelocityTrackerFactory = velocityTrackerFactory;
-        mUiEventLogger = uiEventLogger;
-        mActivityStarter = activityStarter;
-    }
-
-    @Override
-    public void getTouchInitiationRegion(Rect bounds, Region region, Rect exclusionRect) {
-        final int width = bounds.width();
-        final int height = bounds.height();
-        final int minAllowableBottom = Math.round(height * (1 - mMinBouncerZoneScreenPercentage));
-
-        final Rect normalRegion = new Rect(0,
-                Math.round(height * (1 - mBouncerZoneScreenPercentage)),
-                width, height);
-
-        if (exclusionRect != null) {
-            int lowestBottom = Math.min(Math.max(0, exclusionRect.bottom), minAllowableBottom);
-            normalRegion.top = Math.max(normalRegion.top, lowestBottom);
-        }
-        region.union(normalRegion);
-    }
-
-
-    @Override
-    public void onSessionStart(TouchSession session) {
-        mVelocityTracker = mVelocityTrackerFactory.obtain();
-        mTouchSession = session;
-        mVelocityTracker.clear();
-
-        if (!Flags.communalBouncerDoNotModifyPluginOpen()) {
-            mNotificationShadeWindowController.setForcePluginOpen(true, this);
-        }
-
-        mScrimManager.addCallback(mScrimManagerCallback);
-        mCurrentScrimController = mScrimManager.getCurrentController();
-
-        session.registerCallback(() -> {
-            if (mVelocityTracker != null) {
-                mVelocityTracker.recycle();
-                mVelocityTracker = null;
-            }
-            mScrimManager.removeCallback(mScrimManagerCallback);
-            mCapture = null;
-            mTouchSession = null;
-
-            if (!Flags.communalBouncerDoNotModifyPluginOpen()) {
-                mNotificationShadeWindowController.setForcePluginOpen(false, this);
-            }
-        });
-
-        session.registerGestureListener(mOnGestureListener);
-        session.registerInputListener(ev -> onMotionEvent(ev));
-
-    }
-
-    private void onMotionEvent(InputEvent event) {
-        if (!(event instanceof MotionEvent)) {
-            Log.e(TAG, "non MotionEvent received:" + event);
-            return;
-        }
-
-        final MotionEvent motionEvent = (MotionEvent) event;
-
-        switch (motionEvent.getAction()) {
-            case MotionEvent.ACTION_CANCEL:
-            case MotionEvent.ACTION_UP:
-                mTouchSession.pop();
-                // If we are not capturing any input, there is no need to consider animating to
-                // finish transition.
-                if (mCapture == null || !mCapture) {
-                    break;
-                }
-
-                // We must capture the resulting velocities as resetMonitor() will clear these
-                // values.
-                mVelocityTracker.computeCurrentVelocity(1000);
-                final float verticalVelocity = mVelocityTracker.getYVelocity();
-                final float horizontalVelocity = mVelocityTracker.getXVelocity();
-
-                final float velocityVector =
-                        (float) Math.hypot(horizontalVelocity, verticalVelocity);
-
-                mExpanded = !flingRevealsOverlay(verticalVelocity, velocityVector);
-                final float expansion = mExpanded
-                        ? KeyguardBouncerConstants.EXPANSION_VISIBLE
-                        : KeyguardBouncerConstants.EXPANSION_HIDDEN;
-
-                // Log the swiping up to show Bouncer event.
-                if (expansion == KeyguardBouncerConstants.EXPANSION_VISIBLE) {
-                    mUiEventLogger.log(DreamEvent.DREAM_SWIPED);
-                }
-
-                flingToExpansion(verticalVelocity, expansion);
-                break;
-            default:
-                mVelocityTracker.addMovement(motionEvent);
-                break;
-        }
-    }
-
-    private ValueAnimator createExpansionAnimator(float targetExpansion) {
-        final ValueAnimator animator =
-                mValueAnimatorCreator.create(mCurrentExpansion, targetExpansion);
-        animator.addUpdateListener(
-                animation -> {
-                    float expansionFraction = (float) animation.getAnimatedValue();
-                    setPanelExpansion(expansionFraction);
-                });
-        if (targetExpansion == KeyguardBouncerConstants.EXPANSION_VISIBLE) {
-            animator.addListener(
-                    new AnimatorListenerAdapter() {
-                        @Override
-                        public void onAnimationEnd(Animator animation) {
-                            mUiEventLogger.log(DreamEvent.DREAM_BOUNCER_FULLY_VISIBLE);
-                        }
-                    });
-        }
-        return animator;
-    }
-
-    protected boolean flingRevealsOverlay(float velocity, float velocityVector) {
-        // Fully expand the space above the bouncer, if the user has expanded the bouncer less
-        // than halfway or final velocity was positive, indicating a downward direction.
-        if (Math.abs(velocityVector) < mFlingAnimationUtils.getMinVelocityPxPerSecond()) {
-            return mCurrentExpansion > FLING_PERCENTAGE_THRESHOLD;
-        } else {
-            return velocity > 0;
-        }
-    }
-
-    protected void flingToExpansion(float velocity, float expansion) {
-        if (!mCentralSurfaces.isPresent()) {
-            return;
-        }
-
-        // Don't set expansion if the user doesn't have a pin/password set.
-        if (!mLockPatternUtils.isSecure(mUserTracker.getUserId())) {
-            return;
-        }
-
-        // The animation utils deal in pixel units, rather than expansion height.
-        final float viewHeight = mTouchSession.getBounds().height();
-        final float currentHeight = viewHeight * mCurrentExpansion;
-        final float targetHeight = viewHeight * expansion;
-        final ValueAnimator animator = createExpansionAnimator(expansion);
-        if (expansion == KeyguardBouncerConstants.EXPANSION_HIDDEN) {
-            // Hides the bouncer, i.e., fully expands the space above the bouncer.
-            mFlingAnimationUtilsClosing.apply(animator, currentHeight, targetHeight, velocity,
-                    viewHeight);
-        } else {
-            // Shows the bouncer, i.e., fully collapses the space above the bouncer.
-            mFlingAnimationUtils.apply(
-                    animator, currentHeight, targetHeight, velocity, viewHeight);
-        }
-
-        animator.start();
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/ambient/touch/BouncerSwipeTouchHandler.kt b/packages/SystemUI/src/com/android/systemui/ambient/touch/BouncerSwipeTouchHandler.kt
new file mode 100644
index 0000000..d5790a4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/ambient/touch/BouncerSwipeTouchHandler.kt
@@ -0,0 +1,371 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.ambient.touch
+
+import android.animation.Animator
+import android.animation.AnimatorListenerAdapter
+import android.animation.ValueAnimator
+import android.graphics.Rect
+import android.graphics.Region
+import android.util.Log
+import android.view.GestureDetector
+import android.view.GestureDetector.SimpleOnGestureListener
+import android.view.InputEvent
+import android.view.MotionEvent
+import android.view.VelocityTracker
+import androidx.annotation.VisibleForTesting
+import com.android.internal.logging.UiEvent
+import com.android.internal.logging.UiEventLogger
+import com.android.internal.widget.LockPatternUtils
+import com.android.systemui.Flags
+import com.android.systemui.ambient.touch.TouchHandler.TouchSession
+import com.android.systemui.ambient.touch.dagger.BouncerSwipeModule
+import com.android.systemui.ambient.touch.scrim.ScrimController
+import com.android.systemui.ambient.touch.scrim.ScrimManager
+import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants
+import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.settings.UserTracker
+import com.android.systemui.shade.ShadeExpansionChangeEvent
+import com.android.systemui.statusbar.NotificationShadeWindowController
+import com.android.systemui.statusbar.phone.CentralSurfaces
+import com.android.wm.shell.animation.FlingAnimationUtils
+import java.util.Optional
+import javax.inject.Inject
+import javax.inject.Named
+import kotlin.math.abs
+import kotlin.math.hypot
+import kotlin.math.max
+import kotlin.math.min
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+
+/** Monitor for tracking touches on the DreamOverlay to bring up the bouncer. */
+class BouncerSwipeTouchHandler
+@Inject
+constructor(
+    scope: CoroutineScope,
+    private val scrimManager: ScrimManager,
+    private val centralSurfaces: Optional<CentralSurfaces>,
+    private val notificationShadeWindowController: NotificationShadeWindowController,
+    private val valueAnimatorCreator: ValueAnimatorCreator,
+    private val velocityTrackerFactory: VelocityTrackerFactory,
+    private val lockPatternUtils: LockPatternUtils,
+    private val userTracker: UserTracker,
+    private val communalViewModel: CommunalViewModel,
+    @param:Named(BouncerSwipeModule.SWIPE_TO_BOUNCER_FLING_ANIMATION_UTILS_OPENING)
+    private val flingAnimationUtils: FlingAnimationUtils,
+    @param:Named(BouncerSwipeModule.SWIPE_TO_BOUNCER_FLING_ANIMATION_UTILS_CLOSING)
+    private val flingAnimationUtilsClosing: FlingAnimationUtils,
+    @param:Named(BouncerSwipeModule.SWIPE_TO_BOUNCER_START_REGION)
+    private val bouncerZoneScreenPercentage: Float,
+    @param:Named(BouncerSwipeModule.MIN_BOUNCER_ZONE_SCREEN_PERCENTAGE)
+    private val minBouncerZoneScreenPercentage: Float,
+    private val uiEventLogger: UiEventLogger,
+    private val activityStarter: ActivityStarter
+) : TouchHandler {
+    /** An interface for creating ValueAnimators. */
+    interface ValueAnimatorCreator {
+        /** Creates [ValueAnimator]. */
+        fun create(start: Float, finish: Float): ValueAnimator
+    }
+
+    /** An interface for obtaining VelocityTrackers. */
+    interface VelocityTrackerFactory {
+        /** Obtains [VelocityTracker]. */
+        fun obtain(): VelocityTracker?
+    }
+
+    private var currentScrimController: ScrimController? = null
+    private var currentExpansion = 0f
+    private var velocityTracker: VelocityTracker? = null
+    private var capture: Boolean? = null
+    private var expanded: Boolean = false
+    private var touchSession: TouchSession? = null
+    private val scrimManagerCallback =
+        ScrimManager.Callback { controller ->
+            currentScrimController?.reset()
+
+            currentScrimController = controller
+        }
+
+    /** Determines whether the touch handler should process touches in fullscreen swiping mode */
+    private var touchAvailable = false
+
+    private val onGestureListener: GestureDetector.OnGestureListener =
+        object : SimpleOnGestureListener() {
+            override fun onScroll(
+                e1: MotionEvent?,
+                e2: MotionEvent,
+                distanceX: Float,
+                distanceY: Float
+            ): Boolean {
+                if (capture == null) {
+                    capture =
+                        if (Flags.dreamOverlayBouncerSwipeDirectionFiltering()) {
+                            (abs(distanceY.toDouble()) > abs(distanceX.toDouble()) &&
+                                distanceY > 0) &&
+                                if (Flags.hubmodeFullscreenVerticalSwipe()) touchAvailable else true
+                        } else {
+                            // If the user scrolling favors a vertical direction, begin capturing
+                            // scrolls.
+                            abs(distanceY.toDouble()) > abs(distanceX.toDouble())
+                        }
+                    if (capture == true) {
+                        // reset expanding
+                        expanded = false
+                        // Since the user is dragging the bouncer up, set scrimmed to false.
+                        currentScrimController?.show()
+                    }
+                }
+                if (capture != true) {
+                    return false
+                }
+
+                if (!centralSurfaces.isPresent) {
+                    return true
+                }
+
+                e1?.apply outer@{
+                    // Don't set expansion for downward scroll.
+                    if (y < e2.y) {
+                        return true
+                    }
+
+                    // If scrolling up and keyguard is not locked, dismiss both keyguard and the
+                    // dream since there's no bouncer to show.
+                    if (y > e2.y && !lockPatternUtils.isSecure(userTracker.userId)) {
+                        activityStarter.executeRunnableDismissingKeyguard(
+                            { centralSurfaces.get().awakenDreams() },
+                            /* cancelAction= */ null,
+                            /* dismissShade= */ true,
+                            /* afterKeyguardGone= */ true,
+                            /* deferred= */ false
+                        )
+                        return true
+                    }
+
+                    // For consistency, we adopt the expansion definition found in the
+                    // PanelViewController. In this case, expansion refers to the view above the
+                    // bouncer. As that view's expansion shrinks, the bouncer appears. The bouncer
+                    // is fully hidden at full expansion (1) and fully visible when fully collapsed
+                    // (0).
+                    touchSession?.apply {
+                        val screenTravelPercentage =
+                            (abs((this@outer.y - e2.y).toDouble()) / getBounds().height()).toFloat()
+                        setPanelExpansion(1 - screenTravelPercentage)
+                    }
+                }
+
+                return true
+            }
+        }
+
+    init {
+        if (Flags.hubmodeFullscreenVerticalSwipe()) {
+            scope.launch {
+                communalViewModel.glanceableTouchAvailable.collect {
+                    onGlanceableTouchAvailable(it)
+                }
+            }
+        }
+    }
+
+    @VisibleForTesting
+    fun onGlanceableTouchAvailable(available: Boolean) {
+        touchAvailable = available
+    }
+
+    private fun setPanelExpansion(expansion: Float) {
+        currentExpansion = expansion
+        val event =
+            ShadeExpansionChangeEvent(
+                /* fraction= */ currentExpansion,
+                /* expanded= */ expanded,
+                /* tracking= */ true
+            )
+        currentScrimController?.expand(event)
+    }
+
+    @VisibleForTesting
+    enum class DreamEvent(private val mId: Int) : UiEventLogger.UiEventEnum {
+        @UiEvent(doc = "The screensaver has been swiped up.") DREAM_SWIPED(988),
+        @UiEvent(doc = "The bouncer has become fully visible over dream.")
+        DREAM_BOUNCER_FULLY_VISIBLE(1056);
+
+        override fun getId(): Int {
+            return mId
+        }
+    }
+
+    override fun getTouchInitiationRegion(bounds: Rect, region: Region, exclusionRect: Rect?) {
+        val width = bounds.width()
+        val height = bounds.height()
+        val minAllowableBottom = Math.round(height * (1 - minBouncerZoneScreenPercentage))
+        val normalRegion =
+            Rect(0, Math.round(height * (1 - bouncerZoneScreenPercentage)), width, height)
+
+        if (Flags.hubmodeFullscreenVerticalSwipe()) {
+            region.op(bounds, Region.Op.UNION)
+            exclusionRect?.apply { region.op(this, Region.Op.DIFFERENCE) }
+        }
+
+        if (exclusionRect != null) {
+            val lowestBottom =
+                min(max(0.0, exclusionRect.bottom.toDouble()), minAllowableBottom.toDouble())
+                    .toInt()
+            normalRegion.top = max(normalRegion.top.toDouble(), lowestBottom.toDouble()).toInt()
+        }
+        region.union(normalRegion)
+    }
+
+    override fun onSessionStart(session: TouchSession) {
+        velocityTracker = velocityTrackerFactory.obtain()
+        touchSession = session
+        velocityTracker?.apply { clear() }
+        if (!Flags.communalBouncerDoNotModifyPluginOpen()) {
+            notificationShadeWindowController.setForcePluginOpen(true, this)
+        }
+        scrimManager.addCallback(scrimManagerCallback)
+        currentScrimController = scrimManager.currentController
+        session.registerCallback {
+            velocityTracker?.apply { recycle() }
+            velocityTracker = null
+
+            scrimManager.removeCallback(scrimManagerCallback)
+            capture = null
+            touchSession = null
+            if (!Flags.communalBouncerDoNotModifyPluginOpen()) {
+                notificationShadeWindowController.setForcePluginOpen(false, this)
+            }
+        }
+        session.registerGestureListener(onGestureListener)
+        session.registerInputListener { ev: InputEvent -> onMotionEvent(ev) }
+    }
+
+    private fun onMotionEvent(event: InputEvent) {
+        if (event !is MotionEvent) {
+            Log.e(TAG, "non MotionEvent received:$event")
+            return
+        }
+        val motionEvent = event
+        when (motionEvent.action) {
+            MotionEvent.ACTION_CANCEL,
+            MotionEvent.ACTION_UP -> {
+                if (Flags.hubmodeFullscreenVerticalSwipe() && capture == true) {
+                    communalViewModel.onResetTouchState()
+                }
+                touchSession?.apply { pop() }
+                // If we are not capturing any input, there is no need to consider animating to
+                // finish transition.
+                if (capture == null || !capture!!) {
+                    return
+                }
+
+                // We must capture the resulting velocities as resetMonitor() will clear these
+                // values.
+                velocityTracker!!.computeCurrentVelocity(1000)
+                val verticalVelocity = velocityTracker!!.yVelocity
+                val horizontalVelocity = velocityTracker!!.xVelocity
+                val velocityVector =
+                    hypot(horizontalVelocity.toDouble(), verticalVelocity.toDouble()).toFloat()
+                expanded = !flingRevealsOverlay(verticalVelocity, velocityVector)
+                val expansion =
+                    if (expanded!!) KeyguardBouncerConstants.EXPANSION_VISIBLE
+                    else KeyguardBouncerConstants.EXPANSION_HIDDEN
+
+                // Log the swiping up to show Bouncer event.
+                if (expansion == KeyguardBouncerConstants.EXPANSION_VISIBLE) {
+                    uiEventLogger.log(DreamEvent.DREAM_SWIPED)
+                }
+                flingToExpansion(verticalVelocity, expansion)
+            }
+            else -> velocityTracker!!.addMovement(motionEvent)
+        }
+    }
+
+    private fun createExpansionAnimator(targetExpansion: Float): ValueAnimator {
+        val animator = valueAnimatorCreator.create(currentExpansion, targetExpansion)
+        animator.addUpdateListener { animation: ValueAnimator ->
+            val expansionFraction = animation.animatedValue as Float
+            setPanelExpansion(expansionFraction)
+        }
+        if (targetExpansion == KeyguardBouncerConstants.EXPANSION_VISIBLE) {
+            animator.addListener(
+                object : AnimatorListenerAdapter() {
+                    override fun onAnimationEnd(animation: Animator) {
+                        uiEventLogger.log(DreamEvent.DREAM_BOUNCER_FULLY_VISIBLE)
+                    }
+                }
+            )
+        }
+        return animator
+    }
+
+    protected fun flingRevealsOverlay(velocity: Float, velocityVector: Float): Boolean {
+        // Fully expand the space above the bouncer, if the user has expanded the bouncer less
+        // than halfway or final velocity was positive, indicating a downward direction.
+        return if (abs(velocityVector.toDouble()) < flingAnimationUtils.minVelocityPxPerSecond) {
+            currentExpansion > FLING_PERCENTAGE_THRESHOLD
+        } else {
+            velocity > 0
+        }
+    }
+
+    protected fun flingToExpansion(velocity: Float, expansion: Float) {
+        if (!centralSurfaces.isPresent) {
+            return
+        }
+
+        // Don't set expansion if the user doesn't have a pin/password set.
+        if (!lockPatternUtils.isSecure(userTracker.userId)) {
+            return
+        }
+
+        touchSession?.apply {
+            // The animation utils deal in pixel units, rather than expansion height.
+            val viewHeight = getBounds().height().toFloat()
+            val currentHeight = viewHeight * currentExpansion
+            val targetHeight = viewHeight * expansion
+            val animator = createExpansionAnimator(expansion)
+            if (expansion == KeyguardBouncerConstants.EXPANSION_HIDDEN) {
+                // Hides the bouncer, i.e., fully expands the space above the bouncer.
+                flingAnimationUtilsClosing.apply(
+                    animator,
+                    currentHeight,
+                    targetHeight,
+                    velocity,
+                    viewHeight
+                )
+            } else {
+                // Shows the bouncer, i.e., fully collapses the space above the bouncer.
+                flingAnimationUtils.apply(
+                    animator,
+                    currentHeight,
+                    targetHeight,
+                    velocity,
+                    viewHeight
+                )
+            }
+            animator.start()
+        }
+    }
+
+    companion object {
+        const val FLING_PERCENTAGE_THRESHOLD = 0.5f
+        private const val TAG = "BouncerSwipeTouchHandler"
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/ambient/touch/ShadeTouchHandler.java b/packages/SystemUI/src/com/android/systemui/ambient/touch/ShadeTouchHandler.java
deleted file mode 100644
index baca959..0000000
--- a/packages/SystemUI/src/com/android/systemui/ambient/touch/ShadeTouchHandler.java
+++ /dev/null
@@ -1,132 +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.ambient.touch;
-
-import static com.android.systemui.ambient.touch.dagger.ShadeModule.NOTIFICATION_SHADE_GESTURE_INITIATION_HEIGHT;
-
-import android.app.DreamManager;
-import android.graphics.Rect;
-import android.graphics.Region;
-import android.view.GestureDetector;
-import android.view.MotionEvent;
-
-import androidx.annotation.NonNull;
-
-import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor;
-import com.android.systemui.shade.ShadeViewController;
-import com.android.systemui.statusbar.phone.CentralSurfaces;
-
-import java.util.Optional;
-
-import javax.inject.Inject;
-import javax.inject.Named;
-
-/**
- * {@link ShadeTouchHandler} is responsible for handling swipe down gestures over dream
- * to bring down the shade.
- */
-public class ShadeTouchHandler implements TouchHandler {
-    private final Optional<CentralSurfaces> mSurfaces;
-    private final ShadeViewController mShadeViewController;
-    private final DreamManager mDreamManager;
-    private final int mInitiationHeight;
-    private final CommunalSettingsInteractor
-            mCommunalSettingsInteractor;
-
-    /**
-     * Tracks whether or not we are capturing a given touch. Will be null before and after a touch.
-     */
-    private Boolean mCapture;
-
-    @Inject
-    ShadeTouchHandler(Optional<CentralSurfaces> centralSurfaces,
-            ShadeViewController shadeViewController,
-            DreamManager dreamManager,
-            CommunalSettingsInteractor communalSettingsInteractor,
-            @Named(NOTIFICATION_SHADE_GESTURE_INITIATION_HEIGHT) int initiationHeight) {
-        mSurfaces = centralSurfaces;
-        mShadeViewController = shadeViewController;
-        mDreamManager = dreamManager;
-        mCommunalSettingsInteractor = communalSettingsInteractor;
-        mInitiationHeight = initiationHeight;
-    }
-
-    @Override
-    public void onSessionStart(TouchSession session) {
-        if (mSurfaces.isEmpty()) {
-            session.pop();
-            return;
-        }
-
-        session.registerCallback(() -> mCapture = null);
-
-        session.registerInputListener(ev -> {
-            if (ev instanceof MotionEvent) {
-                if (mCapture != null && mCapture) {
-                    sendTouchEvent((MotionEvent) ev);
-                }
-                if (((MotionEvent) ev).getAction() == MotionEvent.ACTION_UP
-                        || ((MotionEvent) ev).getAction() == MotionEvent.ACTION_CANCEL) {
-                    session.pop();
-                }
-            }
-        });
-
-        session.registerGestureListener(new GestureDetector.SimpleOnGestureListener() {
-            @Override
-            public boolean onScroll(MotionEvent e1, @NonNull MotionEvent e2, float distanceX,
-                    float distanceY) {
-                if (mCapture == null) {
-                    // Only capture swipes that are going downwards.
-                    mCapture = Math.abs(distanceY) > Math.abs(distanceX) && distanceY < 0;
-                    if (mCapture) {
-                        // Send the initial touches over, as the input listener has already
-                        // processed these touches.
-                        sendTouchEvent(e1);
-                        sendTouchEvent(e2);
-                    }
-                }
-                return mCapture;
-            }
-
-            @Override
-            public boolean onFling(MotionEvent e1, @NonNull MotionEvent e2, float velocityX,
-                    float velocityY) {
-                return mCapture;
-            }
-        });
-    }
-
-    private void sendTouchEvent(MotionEvent event) {
-        if (mCommunalSettingsInteractor.isCommunalFlagEnabled() && !mDreamManager.isDreaming()) {
-            // Send touches to central surfaces only when on the glanceable hub while not dreaming.
-            // While sending touches where while dreaming will open the shade, the shade
-            // while closing if opened then closed in the same gesture.
-            mSurfaces.get().handleExternalShadeWindowTouch(event);
-        } else {
-            // Send touches to the shade view when dreaming.
-            mShadeViewController.handleExternalTouch(event);
-        }
-    }
-
-    @Override
-    public void getTouchInitiationRegion(Rect bounds, Region region, Rect exclusionRect) {
-        final Rect outBounds = new Rect(bounds);
-        outBounds.inset(0, 0, 0, outBounds.height() - mInitiationHeight);
-        region.op(outBounds, Region.Op.UNION);
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/ambient/touch/ShadeTouchHandler.kt b/packages/SystemUI/src/com/android/systemui/ambient/touch/ShadeTouchHandler.kt
new file mode 100644
index 0000000..06b41de
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/ambient/touch/ShadeTouchHandler.kt
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.ambient.touch
+
+import android.app.DreamManager
+import android.graphics.Rect
+import android.graphics.Region
+import android.view.GestureDetector.SimpleOnGestureListener
+import android.view.InputEvent
+import android.view.MotionEvent
+import androidx.annotation.VisibleForTesting
+import com.android.systemui.Flags
+import com.android.systemui.ambient.touch.TouchHandler.TouchSession
+import com.android.systemui.ambient.touch.dagger.ShadeModule
+import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
+import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
+import com.android.systemui.shade.ShadeViewController
+import com.android.systemui.statusbar.phone.CentralSurfaces
+import java.util.Optional
+import javax.inject.Inject
+import javax.inject.Named
+import kotlin.math.abs
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+
+/**
+ * [ShadeTouchHandler] is responsible for handling swipe down gestures over dream to bring down the
+ * shade.
+ */
+class ShadeTouchHandler
+@Inject
+constructor(
+    scope: CoroutineScope,
+    private val surfaces: Optional<CentralSurfaces>,
+    private val shadeViewController: ShadeViewController,
+    private val dreamManager: DreamManager,
+    private val communalViewModel: CommunalViewModel,
+    private val communalSettingsInteractor: CommunalSettingsInteractor,
+    @param:Named(ShadeModule.NOTIFICATION_SHADE_GESTURE_INITIATION_HEIGHT)
+    private val initiationHeight: Int
+) : TouchHandler {
+    /**
+     * Tracks whether or not we are capturing a given touch. Will be null before and after a touch.
+     */
+    private var capture: Boolean? = null
+
+    /** Determines whether the touch handler should process touches in fullscreen swiping mode */
+    private var touchAvailable = false
+
+    init {
+        if (Flags.hubmodeFullscreenVerticalSwipe()) {
+            scope.launch {
+                communalViewModel.glanceableTouchAvailable.collect {
+                    onGlanceableTouchAvailable(it)
+                }
+            }
+        }
+    }
+
+    @VisibleForTesting
+    fun onGlanceableTouchAvailable(available: Boolean) {
+        touchAvailable = available
+    }
+
+    override fun onSessionStart(session: TouchSession) {
+        if (surfaces.isEmpty) {
+            session.pop()
+            return
+        }
+        session.registerCallback { capture = null }
+        session.registerInputListener { ev: InputEvent? ->
+            if (ev is MotionEvent) {
+                if (capture == true) {
+                    sendTouchEvent(ev)
+                }
+                if (ev.action == MotionEvent.ACTION_UP || ev.action == MotionEvent.ACTION_CANCEL) {
+                    if (capture == true) {
+                        communalViewModel.onResetTouchState()
+                    }
+                    session.pop()
+                }
+            }
+        }
+        session.registerGestureListener(
+            object : SimpleOnGestureListener() {
+                override fun onScroll(
+                    e1: MotionEvent?,
+                    e2: MotionEvent,
+                    distanceX: Float,
+                    distanceY: Float
+                ): Boolean {
+                    if (capture == null) {
+                        // Only capture swipes that are going downwards.
+                        capture =
+                            abs(distanceY.toDouble()) > abs(distanceX.toDouble()) &&
+                                distanceY < 0 &&
+                                if (Flags.hubmodeFullscreenVerticalSwipe()) touchAvailable else true
+                        if (capture == true) {
+                            // Send the initial touches over, as the input listener has already
+                            // processed these touches.
+                            e1?.apply { sendTouchEvent(this) }
+                            sendTouchEvent(e2)
+                        }
+                    }
+                    return capture == true
+                }
+
+                override fun onFling(
+                    e1: MotionEvent?,
+                    e2: MotionEvent,
+                    velocityX: Float,
+                    velocityY: Float
+                ): Boolean {
+                    return capture == true
+                }
+            }
+        )
+    }
+
+    private fun sendTouchEvent(event: MotionEvent) {
+        if (communalSettingsInteractor.isCommunalFlagEnabled() && !dreamManager.isDreaming) {
+            // Send touches to central surfaces only when on the glanceable hub while not dreaming.
+            // While sending touches where while dreaming will open the shade, the shade
+            // while closing if opened then closed in the same gesture.
+            surfaces.get().handleExternalShadeWindowTouch(event)
+        } else {
+            // Send touches to the shade view when dreaming.
+            shadeViewController.handleExternalTouch(event)
+        }
+    }
+
+    override fun getTouchInitiationRegion(bounds: Rect, region: Region, exclusionRect: Rect?) {
+        // If fullscreen swipe, use entire space minus exclusion region
+        if (Flags.hubmodeFullscreenVerticalSwipe()) {
+            region.op(bounds, Region.Op.UNION)
+
+            exclusionRect?.apply { region.op(this, Region.Op.DIFFERENCE) }
+        }
+
+        val outBounds = Rect(bounds)
+        outBounds.inset(0, 0, 0, outBounds.height() - initiationHeight)
+        region.op(outBounds, Region.Op.UNION)
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/ambient/touch/dagger/AmbientTouchModule.kt b/packages/SystemUI/src/com/android/systemui/ambient/touch/dagger/AmbientTouchModule.kt
index a4924d1..ae21e56 100644
--- a/packages/SystemUI/src/com/android/systemui/ambient/touch/dagger/AmbientTouchModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/ambient/touch/dagger/AmbientTouchModule.kt
@@ -17,12 +17,14 @@
 
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.LifecycleOwner
+import androidx.lifecycle.coroutineScope
 import com.android.systemui.ambient.dagger.AmbientModule
 import com.android.systemui.ambient.touch.TouchHandler
 import dagger.Module
 import dagger.Provides
 import dagger.multibindings.ElementsIntoSet
 import javax.inject.Named
+import kotlinx.coroutines.CoroutineScope
 
 @Module
 interface AmbientTouchModule {
@@ -33,6 +35,12 @@
             return lifecycleOwner.lifecycle
         }
 
+        @JvmStatic
+        @Provides
+        fun providesLifecycleScope(lifecycle: Lifecycle): CoroutineScope {
+            return lifecycle.coroutineScope
+        }
+
         @Provides
         @ElementsIntoSet
         fun providesDreamTouchHandlers(
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
index 430ff07..9521be1 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
@@ -65,9 +65,6 @@
 import android.window.OnBackInvokedDispatcher;
 
 import androidx.constraintlayout.widget.ConstraintLayout;
-import androidx.core.view.AccessibilityDelegateCompat;
-import androidx.core.view.ViewCompat;
-import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
 
 import com.android.app.animation.Interpolators;
 import com.android.internal.annotations.VisibleForTesting;
@@ -385,19 +382,6 @@
         mBiometricScrollView = mLayout.findViewById(R.id.biometric_scrollview);
         addView(mLayout);
         mBackgroundView = mLayout.findViewById(R.id.background);
-        ViewCompat.setAccessibilityDelegate(mBackgroundView, new AccessibilityDelegateCompat() {
-            @Override
-            public void onInitializeAccessibilityNodeInfo(View host,
-                    AccessibilityNodeInfoCompat info) {
-                super.onInitializeAccessibilityNodeInfo(host, info);
-                info.addAction(
-                        new AccessibilityNodeInfoCompat.AccessibilityActionCompat(
-                                AccessibilityNodeInfoCompat.ACTION_CLICK,
-                                mContext.getString(R.string.biometric_dialog_cancel_authentication)
-                        )
-                );
-            }
-        });
 
         mPanelView = mLayout.findViewById(R.id.panel);
         if (!constraintBp()) {
@@ -428,7 +412,6 @@
         });
 
         setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
-        setFocusableInTouchMode(true);
         requestFocus();
     }
 
@@ -480,7 +463,8 @@
         }
     }
 
-    private void onBackInvoked() {
+    @VisibleForTesting
+    public void onBackInvoked() {
         sendEarlyUserCanceled();
         animateAway(AuthDialogCallback.DISMISSED_USER_CANCELED);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index 3dd3758..5ffb9ab2 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -56,13 +56,13 @@
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
 import android.view.View;
-import android.view.WindowManager;
 import android.view.accessibility.AccessibilityManager;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.OptIn;
 
+import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.InstanceId;
@@ -147,7 +147,7 @@
     private final Execution mExecution;
     private final FingerprintManager mFingerprintManager;
     @NonNull private final LayoutInflater mInflater;
-    private final WindowManager mWindowManager;
+    private final ViewCaptureAwareWindowManager mWindowManager;
     private final DelayableExecutor mFgExecutor;
     @NonNull private final Executor mBiometricExecutor;
     @NonNull private final StatusBarStateController mStatusBarStateController;
@@ -693,7 +693,7 @@
             @NonNull Execution execution,
             @NonNull LayoutInflater inflater,
             @Nullable FingerprintManager fingerprintManager,
-            @NonNull WindowManager windowManager,
+            @NonNull ViewCaptureAwareWindowManager viewCaptureAwareWindowManager,
             @NonNull StatusBarStateController statusBarStateController,
             @Main DelayableExecutor fgExecutor,
             @NonNull StatusBarKeyguardViewManager statusBarKeyguardViewManager,
@@ -741,7 +741,7 @@
         // The fingerprint manager is queried for UDFPS before this class is constructed, so the
         // fingerprint manager should never be null.
         mFingerprintManager = checkNotNull(fingerprintManager);
-        mWindowManager = windowManager;
+        mWindowManager = viewCaptureAwareWindowManager;
         mFgExecutor = fgExecutor;
         mStatusBarStateController = statusBarStateController;
         mKeyguardStateController = keyguardStateController;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
index e03d160..1bac0bc 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
@@ -44,6 +44,7 @@
 import android.view.accessibility.AccessibilityManager.TouchExplorationStateChangeListener
 import androidx.annotation.LayoutRes
 import androidx.annotation.VisibleForTesting
+import com.android.app.viewcapture.ViewCaptureAwareWindowManager
 import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.systemui.animation.ActivityTransitionAnimator
 import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor
@@ -94,7 +95,7 @@
 constructor(
     private val context: Context,
     private val inflater: LayoutInflater,
-    private val windowManager: WindowManager,
+    private val windowManager: ViewCaptureAwareWindowManager,
     private val accessibilityManager: AccessibilityManager,
     private val statusBarStateController: StatusBarStateController,
     private val statusBarKeyguardViewManager: StatusBarKeyguardViewManager,
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
index 430887d..3ef5572 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
@@ -24,7 +24,6 @@
 import android.hardware.biometrics.BiometricPrompt
 import android.hardware.biometrics.Flags
 import android.hardware.face.FaceManager
-import android.text.method.ScrollingMovementMethod
 import android.util.Log
 import android.view.HapticFeedbackConstants
 import android.view.MotionEvent
@@ -35,6 +34,10 @@
 import android.widget.ImageView
 import android.widget.LinearLayout
 import android.widget.TextView
+import androidx.core.view.AccessibilityDelegateCompat
+import androidx.core.view.ViewCompat
+import androidx.core.view.accessibility.AccessibilityNodeInfoCompat
+import androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat
 import androidx.lifecycle.DefaultLifecycleObserver
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.LifecycleOwner
@@ -64,6 +67,7 @@
 import kotlinx.coroutines.launch
 
 private const val TAG = "BiometricViewBinder"
+private const val MAX_LOGO_DESCRIPTION_CHARACTER_NUMBER = 30
 
 /** Top-most view binder for BiometricPrompt views. */
 object BiometricViewBinder {
@@ -124,7 +128,6 @@
             !accessibilityManager.isEnabled || !accessibilityManager.isTouchExplorationEnabled
         subtitleView.isSelected =
             !accessibilityManager.isEnabled || !accessibilityManager.isTouchExplorationEnabled
-        descriptionView.movementMethod = ScrollingMovementMethod()
 
         val iconOverlayView = view.requireViewById<LottieAnimationView>(R.id.biometric_icon_overlay)
         val iconView = view.requireViewById<LottieAnimationView>(R.id.biometric_icon)
@@ -147,6 +150,25 @@
         val confirmationButton = view.requireViewById<Button>(R.id.button_confirm)
         val retryButton = view.requireViewById<Button>(R.id.button_try_again)
 
+        // Handles custom "Cancel Authentication" talkback action
+        val cancelDelegate: AccessibilityDelegateCompat =
+            object : AccessibilityDelegateCompat() {
+                override fun onInitializeAccessibilityNodeInfo(
+                    host: View,
+                    info: AccessibilityNodeInfoCompat
+                ) {
+                    super.onInitializeAccessibilityNodeInfo(host, info)
+                    info.addAction(
+                        AccessibilityActionCompat(
+                            AccessibilityNodeInfoCompat.ACTION_CLICK,
+                            view.context.getString(R.string.biometric_dialog_cancel_authentication)
+                        )
+                    )
+                }
+            }
+        ViewCompat.setAccessibilityDelegate(backgroundView, cancelDelegate)
+        ViewCompat.setAccessibilityDelegate(cancelButton, cancelDelegate)
+
         // TODO(b/330788871): temporary workaround for the unsafe callbacks & legacy controllers
         val adapter =
             Spaghetti(
@@ -174,8 +196,12 @@
                 }
             }
 
-            logoView.setImageDrawable(viewModel.logo.first())
-            logoDescriptionView.text = viewModel.logoDescription.first()
+            val logoInfo = viewModel.logoInfo.first()
+            logoView.setImageDrawable(logoInfo.first)
+            // The ellipsize effect on xml happens only when the TextView does not have any free
+            // space on the screen to show the text. So we need to manually truncate.
+            logoDescriptionView.text =
+                logoInfo.second?.ellipsize(MAX_LOGO_DESCRIPTION_CHARACTER_NUMBER)
             titleView.text = viewModel.title.first()
             subtitleView.text = viewModel.subtitle.first()
             descriptionView.text = viewModel.description.first()
@@ -384,8 +410,12 @@
                             backgroundView.importantForAccessibility =
                                 IMPORTANT_FOR_ACCESSIBILITY_NO
 
-                            // Allow icon to be used as confirmation button with a11y enabled
-                            if (accessibilityManager.isTouchExplorationEnabled) {
+                            // Allow icon to be used as confirmation button with udfps and a11y
+                            // enabled
+                            if (
+                                accessibilityManager.isTouchExplorationEnabled &&
+                                    modalities.hasUdfps
+                            ) {
                                 iconOverlayView.setOnClickListener {
                                     viewModel.confirmAuthenticated()
                                 }
@@ -692,6 +722,9 @@
         else -> ""
     }
 
+private fun String.ellipsize(cutOffLength: Int) =
+    if (length <= cutOffLength) this else replaceRange(cutOffLength, length, "...")
+
 private fun Boolean.asVisibleOrGone(): Int = if (this) View.VISIBLE else View.GONE
 
 private fun Boolean.asVisibleOrHidden(): Int = if (this) View.VISIBLE else View.INVISIBLE
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptIconViewModel.kt
index 6c83dac..c089143 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptIconViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptIconViewModel.kt
@@ -469,7 +469,7 @@
         if (isPendingConfirmation) {
             when (sensorType) {
                 FingerprintSensorType.POWER_BUTTON -> -1
-                else -> R.string.fingerprint_dialog_authenticated_confirmation
+                else -> R.string.biometric_dialog_confirm
             }
         } else if (isAuthenticating || isAuthenticated) {
             when (sensorType) {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
index 3904ee1..d1ac681 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
@@ -36,7 +36,6 @@
 import android.view.HapticFeedbackConstants
 import android.view.MotionEvent
 import com.android.launcher3.icons.IconProvider
-import com.android.systemui.Flags.bpTalkback
 import com.android.systemui.Flags.constraintBp
 import com.android.systemui.biometrics.UdfpsUtils
 import com.android.systemui.biometrics.Utils
@@ -471,26 +470,13 @@
             }
         }
 
-    /** Logo for the prompt. */
-    val logo: Flow<Drawable?> =
+    /** (logoIcon, logoDescription) for the prompt. */
+    val logoInfo: Flow<Pair<Drawable?, String>> =
         promptSelectorInteractor.prompt
             .map {
                 when {
-                    !(customBiometricPrompt() && constraintBp()) || it == null -> null
-                    it.logoBitmap != null -> BitmapDrawable(context.resources, it.logoBitmap)
-                    else -> context.getUserBadgedIcon(it, iconProvider, activityTaskManager)
-                }
-            }
-            .distinctUntilChanged()
-
-    /** Logo description for the prompt. */
-    val logoDescription: Flow<String> =
-        promptSelectorInteractor.prompt
-            .map {
-                when {
-                    !(customBiometricPrompt() && constraintBp()) || it == null -> ""
-                    !it.logoDescription.isNullOrEmpty() -> it.logoDescription
-                    else -> context.getUserBadgedLabel(it, activityTaskManager)
+                    !(customBiometricPrompt() && constraintBp()) || it == null -> Pair(null, "")
+                    else -> context.getUserBadgedLogoInfo(it, iconProvider, activityTaskManager)
                 }
             }
             .distinctUntilChanged()
@@ -915,7 +901,11 @@
         event: MotionEvent,
         touchExplorationEnabled: Boolean,
     ): Boolean {
-        if (bpTalkback() && modalities.first().hasUdfps && touchExplorationEnabled) {
+        if (
+            modalities.first().hasUdfps &&
+            touchExplorationEnabled &&
+            !isAuthenticated.first().isAuthenticated
+        ) {
             // TODO(b/315184924): Remove uses of UdfpsUtils
             val scaledTouch =
                 udfpsUtils.getTouchInNativeCoordinates(
@@ -982,43 +972,60 @@
     }
 }
 
-private fun Context.getUserBadgedIcon(
+/**
+ * The order of getting logo icon/description is:
+ * 1. If the app sets customized icon/description, use the passed-in value
+ * 2. If shouldShowLogoWithOverrides(), use activityInfo to get icon/description
+ * 3. Otherwise, use applicationInfo to get icon/description
+ */
+private fun Context.getUserBadgedLogoInfo(
     prompt: BiometricPromptRequest.Biometric,
     iconProvider: IconProvider,
     activityTaskManager: ActivityTaskManager
-): Drawable? {
-    var icon: Drawable? = null
-    val componentName = prompt.getComponentNameForLogo(activityTaskManager)
-    if (componentName != null && shouldShowLogoWithOverrides(componentName)) {
-        val activityInfo = getActivityInfo(componentName)
-        icon = if (activityInfo == null) null else iconProvider.getIcon(activityInfo)
+): Pair<Drawable?, String> {
+    var icon: Drawable? =
+        if (prompt.logoBitmap != null) BitmapDrawable(resources, prompt.logoBitmap) else null
+    var label = prompt.logoDescription ?: ""
+    if (icon != null && label.isNotEmpty()) {
+        return Pair(icon, label)
     }
-    if (icon == null) {
-        val appInfo = prompt.getApplicationInfoForLogo(this, componentName)
-        if (appInfo == null) {
-            Log.w(PromptViewModel.TAG, "Cannot find app logo for package $opPackageName")
-            return null
-        } else {
-            icon = packageManager.getApplicationIcon(appInfo)
+
+    // Use activityInfo if shouldUseActivityLogo() is true
+    val componentName = prompt.getComponentNameForLogo(activityTaskManager)
+    if (componentName != null && shouldUseActivityLogo(componentName)) {
+        val activityInfo = getActivityInfo(componentName)
+        if (activityInfo != null) {
+            if (icon == null) {
+                icon = iconProvider.getIcon(activityInfo)
+            }
+            if (label.isEmpty()) {
+                label = activityInfo.loadLabel(packageManager).toString()
+            }
         }
     }
-    return packageManager.getUserBadgedIcon(icon, UserHandle.of(prompt.userInfo.userId))
-}
-
-private fun Context.getUserBadgedLabel(
-    prompt: BiometricPromptRequest.Biometric,
-    activityTaskManager: ActivityTaskManager
-): String {
-    val componentName = prompt.getComponentNameForLogo(activityTaskManager)
-    val appInfo = prompt.getApplicationInfoForLogo(this, componentName)
-    return if (appInfo == null || packageManager.getApplicationLabel(appInfo).isNullOrEmpty()) {
-        Log.w(PromptViewModel.TAG, "Cannot find app logo for package $opPackageName")
-        ""
-    } else {
-        packageManager
-            .getUserBadgedLabel(packageManager.getApplicationLabel(appInfo), UserHandle.of(userId))
-            .toString()
+    if (icon != null && label.isNotEmpty()) {
+        return Pair(icon, label)
     }
+
+    // Use applicationInfo for other cases
+    val appInfo = prompt.getApplicationInfo(this, componentName)
+    if (appInfo == null) {
+        Log.w(PromptViewModel.TAG, "Cannot find app logo for package $opPackageName")
+    } else {
+        if (icon == null) {
+            icon = packageManager.getApplicationIcon(appInfo)
+        }
+        if (label.isEmpty()) {
+            label =
+                packageManager
+                    .getUserBadgedLabel(
+                        packageManager.getApplicationLabel(appInfo),
+                        UserHandle.of(userId)
+                    )
+                    .toString()
+        }
+    }
+    return Pair(icon, label)
 }
 
 private fun BiometricPromptRequest.Biometric.getComponentNameForLogo(
@@ -1036,7 +1043,7 @@
     }
 }
 
-private fun BiometricPromptRequest.Biometric.getApplicationInfoForLogo(
+private fun BiometricPromptRequest.Biometric.getApplicationInfo(
     context: Context,
     componentNameForLogo: ComponentName?
 ): ApplicationInfo? {
@@ -1053,14 +1060,22 @@
         Log.w(PromptViewModel.TAG, "Cannot find application info for $opPackageName")
         null
     } else {
-        context.getApplicationInfo(packageName)
+        try {
+            context.packageManager.getApplicationInfo(
+                packageName,
+                PackageManager.MATCH_DISABLED_COMPONENTS or PackageManager.MATCH_ANY_USER
+            )
+        } catch (e: PackageManager.NameNotFoundException) {
+            Log.w(PromptViewModel.TAG, "Cannot find application info for $opPackageName", e)
+            null
+        }
     }
 }
 
-private fun Context.shouldShowLogoWithOverrides(componentName: ComponentName): Boolean {
-    return resources
-        .getStringArray(R.array.biometric_dialog_package_names_for_logo_with_overrides)
-        .find { componentName.packageName.contentEquals(it) } != null
+private fun Context.shouldUseActivityLogo(componentName: ComponentName): Boolean {
+    return resources.getStringArray(R.array.config_useActivityLogoForBiometricPrompt).find {
+        componentName.packageName.contentEquals(it)
+    } != null
 }
 
 private fun Context.getActivityInfo(componentName: ComponentName): ActivityInfo? =
@@ -1071,17 +1086,6 @@
         null
     }
 
-private fun Context.getApplicationInfo(packageName: String): ApplicationInfo? =
-    try {
-        packageManager.getApplicationInfo(
-            packageName,
-            PackageManager.MATCH_DISABLED_COMPONENTS or PackageManager.MATCH_ANY_USER
-        )
-    } catch (e: PackageManager.NameNotFoundException) {
-        Log.w(PromptViewModel.TAG, "Cannot find application info for $opPackageName", e)
-        null
-    }
-
 /** How the fingerprint sensor was started for the prompt. */
 enum class FingerprintStartMode {
     /** Fingerprint sensor has not started. */
diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegate.kt
index 911145b..f5b9a05 100644
--- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegate.kt
+++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegate.kt
@@ -37,6 +37,7 @@
 import androidx.recyclerview.widget.DiffUtil
 import androidx.recyclerview.widget.LinearLayoutManager
 import androidx.recyclerview.widget.RecyclerView
+import com.android.internal.R as InternalR
 import com.android.internal.logging.UiEventLogger
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.res.R
@@ -367,6 +368,7 @@
             private val nameView = view.requireViewById<TextView>(R.id.bluetooth_device_name)
             private val summaryView = view.requireViewById<TextView>(R.id.bluetooth_device_summary)
             private val iconView = view.requireViewById<ImageView>(R.id.bluetooth_device_icon)
+            private val iconGear = view.requireViewById<ImageView>(R.id.gear_icon_image)
             private val gearView = view.requireViewById<View>(R.id.gear_icon)
 
             internal fun bind(
@@ -380,6 +382,36 @@
                         mutableDeviceItemClick.tryEmit(item)
                         uiEventLogger.log(BluetoothTileDialogUiEvent.DEVICE_CLICKED)
                     }
+
+                    // updating icon colors
+                    val tintColor =
+                        com.android.settingslib.Utils.getColorAttr(
+                                context,
+                                if (item.isActive) InternalR.attr.materialColorOnPrimaryContainer
+                                else InternalR.attr.materialColorOnSurface
+                            )
+                            .defaultColor
+
+                    // update icons
+                    iconView.apply {
+                        item.iconWithDescription?.let {
+                            setImageDrawable(it.first.apply { mutate()?.setTint(tintColor) })
+                            contentDescription = it.second
+                        }
+                    }
+
+                    iconGear.apply { drawable?.let { it.mutate()?.setTint(tintColor) } }
+
+                    // update text styles
+                    nameView.setTextAppearance(
+                        if (item.isActive) R.style.BluetoothTileDialog_DeviceName_Active
+                        else R.style.BluetoothTileDialog_DeviceName
+                    )
+                    summaryView.setTextAppearance(
+                        if (item.isActive) R.style.BluetoothTileDialog_DeviceSummary_Active
+                        else R.style.BluetoothTileDialog_DeviceSummary
+                    )
+
                     accessibilityDelegate =
                         object : AccessibilityDelegate() {
                             override fun onInitializeAccessibilityNodeInfo(
@@ -398,12 +430,7 @@
                 }
                 nameView.text = item.deviceName
                 summaryView.text = item.connectionSummary
-                iconView.apply {
-                    item.iconWithDescription?.let {
-                        setImageDrawable(it.first)
-                        contentDescription = it.second
-                    }
-                }
+
                 gearView.setOnClickListener {
                     deviceItemOnClickCallback.onDeviceItemGearClicked(item, it)
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItem.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItem.kt
index 0ea98d1..a78130f 100644
--- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItem.kt
+++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItem.kt
@@ -52,4 +52,5 @@
     val background: Int? = null,
     var isEnabled: Boolean = true,
     var actionAccessibilityLabel: String = "",
+    var isActive: Boolean = false
 )
diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemFactory.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemFactory.kt
index 51b2280..d7893db 100644
--- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemFactory.kt
@@ -55,7 +55,8 @@
             type: DeviceItemType,
             connectionSummary: String,
             background: Int,
-            actionAccessibilityLabel: String
+            actionAccessibilityLabel: String,
+            isActive: Boolean
         ): DeviceItem {
             return DeviceItem(
                 type = type,
@@ -68,7 +69,8 @@
                     },
                 background = background,
                 isEnabled = !cachedDevice.isBusy,
-                actionAccessibilityLabel = actionAccessibilityLabel
+                actionAccessibilityLabel = actionAccessibilityLabel,
+                isActive = isActive
             )
         }
     }
@@ -91,7 +93,8 @@
             DeviceItemType.ACTIVE_MEDIA_BLUETOOTH_DEVICE,
             cachedDevice.connectionSummary ?: "",
             backgroundOn,
-            context.getString(actionAccessibilityLabelDisconnect)
+            context.getString(actionAccessibilityLabelDisconnect),
+            isActive = true
         )
     }
 }
@@ -116,7 +119,8 @@
             cachedDevice.connectionSummary.takeUnless { it.isNullOrEmpty() }
                 ?: context.getString(audioSharing),
             if (cachedDevice.isBusy) backgroundOffBusy else backgroundOn,
-            ""
+            "",
+            isActive = !cachedDevice.isBusy
         )
     }
 }
@@ -150,7 +154,8 @@
             cachedDevice.connectionSummary.takeUnless { it.isNullOrEmpty() }
                 ?: context.getString(connected),
             if (cachedDevice.isBusy) backgroundOffBusy else backgroundOff,
-            context.getString(actionAccessibilityLabelActivate)
+            context.getString(actionAccessibilityLabelActivate),
+            isActive = false
         )
     }
 }
@@ -188,7 +193,8 @@
             cachedDevice.connectionSummary.takeUnless { it.isNullOrEmpty() }
                 ?: context.getString(connected),
             if (cachedDevice.isBusy) backgroundOffBusy else backgroundOff,
-            context.getString(actionAccessibilityLabelDisconnect)
+            context.getString(actionAccessibilityLabelDisconnect),
+            isActive = false
         )
     }
 }
@@ -216,7 +222,8 @@
             cachedDevice.connectionSummary.takeUnless { it.isNullOrEmpty() }
                 ?: context.getString(saved),
             if (cachedDevice.isBusy) backgroundOffBusy else backgroundOff,
-            context.getString(actionAccessibilityLabelActivate)
+            context.getString(actionAccessibilityLabelActivate),
+            isActive = false
         )
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractor.kt
index 4984fc6..373671d0 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractor.kt
@@ -21,7 +21,7 @@
 import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepository
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFingerprintAuthInteractor
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryBiometricsAllowedInteractor
 import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
 import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
@@ -60,7 +60,8 @@
     private val biometricSettingsRepository: BiometricSettingsRepository,
     private val systemClock: SystemClock,
     private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
-    private val deviceEntryFingerprintAuthInteractor: Lazy<DeviceEntryFingerprintAuthInteractor>,
+    private val deviceEntryBiometricsAllowedInteractor:
+        Lazy<DeviceEntryBiometricsAllowedInteractor>,
     private val keyguardInteractor: Lazy<KeyguardInteractor>,
     keyguardTransitionInteractor: Lazy<KeyguardTransitionInteractor>,
     sceneInteractor: Lazy<SceneInteractor>,
@@ -114,7 +115,7 @@
                                 flowOf(false)
                             } else {
                                 combine(
-                                    deviceEntryFingerprintAuthInteractor
+                                    deviceEntryBiometricsAllowedInteractor
                                         .get()
                                         .isFingerprintAuthCurrentlyAllowed,
                                     keyguardInteractor.get().isKeyguardDismissible,
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractor.kt
index 35015e7..8e12646 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractor.kt
@@ -33,6 +33,7 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.deviceentry.data.repository.DeviceEntryFaceAuthRepository
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryBiometricsAllowedInteractor
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFingerprintAuthInteractor
 import com.android.systemui.flags.SystemPropertiesHelper
 import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
@@ -76,10 +77,11 @@
     private val deviceEntryFingerprintAuthInteractor: DeviceEntryFingerprintAuthInteractor,
     faceAuthRepository: DeviceEntryFaceAuthRepository,
     private val securityModel: KeyguardSecurityModel,
+    deviceEntryBiometricsAllowedInteractor: DeviceEntryBiometricsAllowedInteractor,
 ) {
 
     private val isFingerprintAuthCurrentlyAllowedOnBouncer =
-        deviceEntryFingerprintAuthInteractor.isFingerprintCurrentlyAllowedOnBouncer.stateIn(
+        deviceEntryBiometricsAllowedInteractor.isFingerprintCurrentlyAllowedOnBouncer.stateIn(
             applicationScope,
             SharingStarted.Eagerly,
             false
@@ -87,6 +89,7 @@
 
     private val currentSecurityMode
         get() = securityModel.getSecurityMode(currentUserId)
+
     private val currentUserId
         get() = userRepository.getSelectedUserInfo().id
 
@@ -349,6 +352,7 @@
 
 interface CountDownTimerCallback {
     fun onFinish()
+
     fun onTick(millisUntilFinished: Long)
 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/log/BouncerLoggerStartable.kt b/packages/SystemUI/src/com/android/systemui/bouncer/log/BouncerLoggerStartable.kt
index 29c4f2e..87ec254 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/log/BouncerLoggerStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/log/BouncerLoggerStartable.kt
@@ -20,6 +20,7 @@
 import com.android.systemui.CoreStartable
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryBiometricSettingsInteractor
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryBiometricsAllowedInteractor
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFingerprintAuthInteractor
 import com.android.systemui.log.BouncerLogger
@@ -39,6 +40,7 @@
     private val faceAuthInteractor: DeviceEntryFaceAuthInteractor,
     private val fingerprintAuthInteractor: DeviceEntryFingerprintAuthInteractor,
     private val bouncerLogger: BouncerLogger,
+    private val deviceEntryBiometricsAllowedInteractor: DeviceEntryBiometricsAllowedInteractor,
 ) : CoreStartable {
     override fun start() {
         if (!Build.isDebuggable()) {
@@ -69,13 +71,13 @@
             }
         }
         applicationScope.launch {
-            fingerprintAuthInteractor.isFingerprintCurrentlyAllowedOnBouncer.collectLatest {
-                newValue ->
-                bouncerLogger.interestedStateChanged(
-                    "fingerprintCurrentlyAllowedOnBouncer",
-                    newValue
-                )
-            }
+            deviceEntryBiometricsAllowedInteractor.isFingerprintCurrentlyAllowedOnBouncer
+                .collectLatest { newValue ->
+                    bouncerLogger.interestedStateChanged(
+                        "fingerprintCurrentlyAllowedOnBouncer",
+                        newValue
+                    )
+                }
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerMessageViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerMessageViewModel.kt
index 810b6d1..31479f1 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerMessageViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerMessageViewModel.kt
@@ -30,8 +30,8 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.deviceentry.domain.interactor.BiometricMessageInteractor
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryBiometricsAllowedInteractor
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor
-import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFingerprintAuthInteractor
 import com.android.systemui.deviceentry.domain.interactor.DeviceUnlockedInteractor
 import com.android.systemui.deviceentry.shared.model.DeviceEntryRestrictionReason
 import com.android.systemui.deviceentry.shared.model.FaceFailureMessage
@@ -76,7 +76,7 @@
     private val biometricMessageInteractor: BiometricMessageInteractor,
     private val faceAuthInteractor: DeviceEntryFaceAuthInteractor,
     private val deviceUnlockedInteractor: DeviceUnlockedInteractor,
-    private val fingerprintInteractor: DeviceEntryFingerprintAuthInteractor,
+    private val deviceEntryBiometricsAllowedInteractor: DeviceEntryBiometricsAllowedInteractor,
     flags: ComposeBouncerFlags,
 ) {
     /**
@@ -121,7 +121,8 @@
                         combine(
                             deviceUnlockedInteractor.deviceEntryRestrictionReason,
                             lockoutMessage,
-                            fingerprintInteractor.isFingerprintCurrentlyAllowedOnBouncer,
+                            deviceEntryBiometricsAllowedInteractor
+                                .isFingerprintCurrentlyAllowedOnBouncer,
                             resetToDefault,
                         ) { deviceEntryRestrictedReason, lockoutMsg, isFpAllowedInBouncer, _ ->
                             lockoutMsg
@@ -168,7 +169,7 @@
             biometricMessageInteractor.faceMessage
                 .sample(
                     authenticationInteractor.authenticationMethod,
-                    fingerprintInteractor.isFingerprintCurrentlyAllowedOnBouncer,
+                    deviceEntryBiometricsAllowedInteractor.isFingerprintCurrentlyAllowedOnBouncer,
                 )
                 .collectLatest { (faceMessage, authMethod, fingerprintAllowedOnBouncer) ->
                     val isFaceAuthStrong = faceAuthInteractor.isFaceAuthStrong()
@@ -223,7 +224,7 @@
             biometricMessageInteractor.fingerprintMessage
                 .sample(
                     authenticationInteractor.authenticationMethod,
-                    fingerprintInteractor.isFingerprintCurrentlyAllowedOnBouncer
+                    deviceEntryBiometricsAllowedInteractor.isFingerprintCurrentlyAllowedOnBouncer
                 )
                 .collectLatest { (fingerprintMessage, authMethod, isFingerprintAllowed) ->
                     val defaultPrimaryMessage =
@@ -261,7 +262,7 @@
             bouncerInteractor.onIncorrectBouncerInput
                 .sample(
                     authenticationInteractor.authenticationMethod,
-                    fingerprintInteractor.isFingerprintCurrentlyAllowedOnBouncer
+                    deviceEntryBiometricsAllowedInteractor.isFingerprintCurrentlyAllowedOnBouncer
                 )
                 .collectLatest { (_, authMethod, isFingerprintAllowed) ->
                     message.emit(
@@ -414,7 +415,7 @@
         biometricMessageInteractor: BiometricMessageInteractor,
         faceAuthInteractor: DeviceEntryFaceAuthInteractor,
         deviceUnlockedInteractor: DeviceUnlockedInteractor,
-        fingerprintInteractor: DeviceEntryFingerprintAuthInteractor,
+        deviceEntryBiometricsAllowedInteractor: DeviceEntryBiometricsAllowedInteractor,
         flags: ComposeBouncerFlags,
         userSwitcherViewModel: UserSwitcherViewModel,
     ): BouncerMessageViewModel {
@@ -428,7 +429,7 @@
             biometricMessageInteractor = biometricMessageInteractor,
             faceAuthInteractor = faceAuthInteractor,
             deviceUnlockedInteractor = deviceUnlockedInteractor,
-            fingerprintInteractor = fingerprintInteractor,
+            deviceEntryBiometricsAllowedInteractor = deviceEntryBiometricsAllowedInteractor,
             flags = flags,
             selectedUser = userSwitcherViewModel.selectedUser,
         )
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt
index 40a141d..e2089bb 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt
@@ -24,7 +24,6 @@
 import androidx.compose.ui.input.key.type
 import androidx.core.graphics.drawable.toBitmap
 import com.android.compose.animation.scene.Back
-import com.android.compose.animation.scene.SceneKey
 import com.android.compose.animation.scene.Swipe
 import com.android.compose.animation.scene.SwipeDirection
 import com.android.compose.animation.scene.UserAction
@@ -43,7 +42,6 @@
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.inputmethod.domain.interactor.InputMethodInteractor
-import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.user.domain.interactor.SelectedUserInteractor
 import com.android.systemui.user.ui.viewmodel.UserActionViewModel
 import com.android.systemui.user.ui.viewmodel.UserSwitcherViewModel
@@ -67,7 +65,9 @@
 /** Holds UI state and handles user input on bouncer UIs. */
 class BouncerViewModel(
     @Application private val applicationContext: Context,
-    @Application private val applicationScope: CoroutineScope,
+    @Deprecated("TODO(b/354270224): remove this. Injecting CoroutineScope to view-models is banned")
+    @Application
+    private val applicationScope: CoroutineScope,
     @Main private val mainDispatcher: CoroutineDispatcher,
     private val bouncerInteractor: BouncerInteractor,
     private val inputMethodInteractor: InputMethodInteractor,
@@ -91,14 +91,13 @@
                 initialValue = null,
             )
 
-    val destinationScenes: StateFlow<Map<UserAction, UserActionResult>> =
-        bouncerInteractor.dismissDestination
-            .map(::destinationSceneMap)
-            .stateIn(
-                applicationScope,
-                SharingStarted.WhileSubscribed(),
-                initialValue = destinationSceneMap(Scenes.Lockscreen),
+    val destinationScenes: Flow<Map<UserAction, UserActionResult>> =
+        bouncerInteractor.dismissDestination.map { prevScene ->
+            mapOf(
+                Back to UserActionResult(prevScene),
+                Swipe(SwipeDirection.Down) to UserActionResult(prevScene),
             )
+        }
 
     val message: BouncerMessageViewModel = bouncerMessageViewModel
 
@@ -328,8 +327,7 @@
                 { message },
                 failedAttempts,
                 remainingAttempts,
-            )
-                ?: message
+            ) ?: message
         } else {
             message
         }
@@ -346,8 +344,7 @@
                     .KEYGUARD_DIALOG_FAILED_ATTEMPTS_ERASING_PROFILE,
                 { message },
                 failedAttempts,
-            )
-                ?: message
+            ) ?: message
         } else {
             message
         }
@@ -375,12 +372,6 @@
         }
     }
 
-    private fun destinationSceneMap(prevScene: SceneKey) =
-        mapOf(
-            Back to UserActionResult(prevScene),
-            Swipe(SwipeDirection.Down) to UserActionResult(prevScene),
-        )
-
     /**
      * Notifies that a key event has occurred.
      *
@@ -390,8 +381,7 @@
         return (authMethodViewModel.value as? PinBouncerViewModel)?.onKeyEvent(
             keyEvent.type,
             keyEvent.nativeKeyEvent.keyCode
-        )
-            ?: false
+        ) ?: false
     }
 
     data class DialogViewModel(
diff --git a/packages/SystemUI/src/com/android/systemui/camera/CameraGestureHelper.kt b/packages/SystemUI/src/com/android/systemui/camera/CameraGestureHelper.kt
index ecbd3f9..6757edb 100644
--- a/packages/SystemUI/src/com/android/systemui/camera/CameraGestureHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/camera/CameraGestureHelper.kt
@@ -19,6 +19,7 @@
 import android.app.ActivityManager
 import android.app.ActivityOptions
 import android.app.IActivityTaskManager
+import android.app.admin.DevicePolicyManager
 import android.content.ContentResolver
 import android.content.Context
 import android.content.Intent
@@ -32,8 +33,8 @@
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.shared.system.ActivityManagerKt.isInForeground
+import com.android.systemui.statusbar.NotificationLockscreenUserManager
 import com.android.systemui.statusbar.StatusBarState
-import com.android.systemui.statusbar.phone.CentralSurfaces
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
 import com.android.systemui.statusbar.policy.KeyguardStateController
 import com.android.systemui.user.domain.interactor.SelectedUserInteractor
@@ -45,9 +46,10 @@
  * the camera).
  */
 @SysUISingleton
-class CameraGestureHelper @Inject constructor(
+class CameraGestureHelper
+@Inject
+constructor(
     private val context: Context,
-    private val centralSurfaces: CentralSurfaces,
     private val keyguardStateController: KeyguardStateController,
     private val statusBarKeyguardViewManager: StatusBarKeyguardViewManager,
     private val packageManager: PackageManager,
@@ -59,24 +61,25 @@
     private val contentResolver: ContentResolver,
     @Main private val uiExecutor: Executor,
     private val selectedUserInteractor: SelectedUserInteractor,
+    private val devicePolicyManager: DevicePolicyManager,
+    private val lockscreenUserManager: NotificationLockscreenUserManager,
 ) {
-    /**
-     * Whether the camera application can be launched for the camera launch gesture.
-     */
+    /** Whether the camera application can be launched for the camera launch gesture. */
     fun canCameraGestureBeLaunched(statusBarState: Int): Boolean {
-        if (!centralSurfaces.isCameraAllowedByAdmin) {
+        if (!isCameraAllowedByAdmin()) {
             return false
         }
 
-        val resolveInfo: ResolveInfo? = packageManager.resolveActivityAsUser(
-            getStartCameraIntent(selectedUserInteractor.getSelectedUserId()),
-            PackageManager.MATCH_DEFAULT_ONLY,
-            selectedUserInteractor.getSelectedUserId()
-        )
+        val resolveInfo: ResolveInfo? =
+            packageManager.resolveActivityAsUser(
+                getStartCameraIntent(selectedUserInteractor.getSelectedUserId()),
+                PackageManager.MATCH_DEFAULT_ONLY,
+                selectedUserInteractor.getSelectedUserId()
+            )
         val resolvedPackage = resolveInfo?.activityInfo?.packageName
         return (resolvedPackage != null &&
-                (statusBarState != StatusBarState.SHADE ||
-                        !activityManager.isInForeground(resolvedPackage)))
+            (statusBarState != StatusBarState.SHADE ||
+                !activityManager.isInForeground(resolvedPackage)))
     }
 
     /**
@@ -87,9 +90,11 @@
     fun launchCamera(source: Int) {
         val intent: Intent = getStartCameraIntent(selectedUserInteractor.getSelectedUserId())
         intent.putExtra(CameraIntents.EXTRA_LAUNCH_SOURCE, source)
-        val wouldLaunchResolverActivity = activityIntentHelper.wouldLaunchResolverActivity(
-            intent, selectedUserInteractor.getSelectedUserId()
-        )
+        val wouldLaunchResolverActivity =
+            activityIntentHelper.wouldLaunchResolverActivity(
+                intent,
+                selectedUserInteractor.getSelectedUserId()
+            )
         if (CameraIntents.isSecureCameraIntent(intent) && !wouldLaunchResolverActivity) {
             uiExecutor.execute {
                 // Normally an activity will set its requested rotation animation on its window.
@@ -101,7 +106,7 @@
                 val activityOptions = ActivityOptions.makeBasic()
                 activityOptions.setDisallowEnterPictureInPictureWhileLaunching(true)
                 activityOptions.rotationAnimationHint =
-                        WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS
+                    WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS
                 try {
                     activityTaskManager.startActivityAsUser(
                         null,
@@ -118,11 +123,7 @@
                         selectedUserInteractor.getSelectedUserId(true),
                     )
                 } catch (e: RemoteException) {
-                    Log.w(
-                        "CameraGestureHelper",
-                        "Unable to start camera activity",
-                        e
-                    )
+                    Log.w("CameraGestureHelper", "Unable to start camera activity", e)
                 }
             }
         } else {
@@ -131,9 +132,6 @@
             activityStarter.startActivity(intent, false /* dismissShade */)
         }
 
-        // Call this to make sure that the keyguard returns if the app that is being launched
-        // crashes after a timeout.
-        centralSurfaces.startLaunchTransitionTimeout()
         // Call this to make sure the keyguard is ready to be dismissed once the next intent is
         // handled by the OS (in our case it is the activity we started right above)
         statusBarKeyguardViewManager.readyForKeyguardDone()
@@ -152,4 +150,17 @@
             cameraIntents.getInsecureCameraIntent(userId)
         }
     }
+
+    private fun isCameraAllowedByAdmin(): Boolean {
+        if (devicePolicyManager.getCameraDisabled(null, lockscreenUserManager.getCurrentUserId())) {
+            return false
+        } else if (keyguardStateController.isShowing() && statusBarKeyguardViewManager.isSecure()) {
+            // Check if the admin has disabled the camera specifically for the keyguard
+            return (devicePolicyManager.getKeyguardDisabledFeatures(
+                null,
+                lockscreenUserManager.getCurrentUserId()
+            ) and DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA) == 0
+        }
+        return true
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
index bd0e729..04c6fa9 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
@@ -301,8 +301,8 @@
                     mClipboardLogger.logUnguarded(CLIPBOARD_OVERLAY_SHOWN_EXPANDED);
                     setExpandedView(this::animateIn);
                 }
-                mView.announceForAccessibility(
-                        getAccessibilityAnnouncement(mClipboardModel.getType()));
+                mWindow.withWindowAttached(() -> mView.announceForAccessibility(
+                        getAccessibilityAnnouncement(mClipboardModel.getType())));
             } else if (!mIsMinimized) {
                 setExpandedView(() -> {
                 });
@@ -320,8 +320,8 @@
                     setExpandedView();
                 }
                 animateIn();
-                mView.announceForAccessibility(
-                        getAccessibilityAnnouncement(mClipboardModel.getType()));
+                mWindow.withWindowAttached(() -> mView.announceForAccessibility(
+                        getAccessibilityAnnouncement(mClipboardModel.getType())));
             } else if (!mIsMinimized) {
                 setExpandedView();
             }
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayWindow.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayWindow.java
index 9dac9b3..0dc6fda 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayWindow.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayWindow.java
@@ -18,10 +18,8 @@
 
 import android.annotation.MainThread;
 import android.annotation.NonNull;
-import android.app.ICompatCameraControlCallback;
 import android.content.Context;
 import android.content.res.Configuration;
-import android.util.Log;
 import android.view.View;
 import android.view.ViewRootImpl;
 import android.view.ViewTreeObserver;
@@ -104,12 +102,6 @@
         }
     }
 
-    @Override // ViewRootImpl.ActivityConfigCallback
-    public void requestCompatCameraControl(boolean showControl, boolean transformationApplied,
-            ICompatCameraControlCallback callback) {
-        Log.w(TAG, "unexpected requestCompatCameraControl call");
-    }
-
     void remove() {
         final View decorView = peekDecorView();
         if (decorView != null && decorView.isAttachedToWindow()) {
diff --git a/packages/SystemUI/src/com/android/systemui/common/ui/binder/IconViewBinder.kt b/packages/SystemUI/src/com/android/systemui/common/ui/binder/IconViewBinder.kt
index 64dedea..108e22b 100644
--- a/packages/SystemUI/src/com/android/systemui/common/ui/binder/IconViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/common/ui/binder/IconViewBinder.kt
@@ -16,7 +16,6 @@
 
 package com.android.systemui.common.ui.binder
 
-import android.view.View
 import android.widget.ImageView
 import com.android.systemui.common.shared.model.Icon
 
@@ -31,13 +30,4 @@
             is Icon.Resource -> view.setImageResource(icon.res)
         }
     }
-
-    fun bindNullable(icon: Icon?, view: ImageView) {
-        if (icon != null) {
-            view.visibility = View.VISIBLE
-            bind(icon, view)
-        } else {
-            view.visibility = View.GONE
-        }
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/common/ui/domain/interactor/ConfigurationInteractor.kt b/packages/SystemUI/src/com/android/systemui/common/ui/domain/interactor/ConfigurationInteractor.kt
index e0e1971..adb1ee2 100644
--- a/packages/SystemUI/src/com/android/systemui/common/ui/domain/interactor/ConfigurationInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/common/ui/domain/interactor/ConfigurationInteractor.kt
@@ -25,6 +25,7 @@
 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.map
 import kotlinx.coroutines.flow.mapLatest
@@ -74,6 +75,13 @@
         return onAnyConfigurationChange.mapLatest { repository.getDimensionPixelSize(resourceId) }
     }
 
+    /** Emits the dimensional pixel size of the given resource, inverting it for RTL if necessary */
+    fun directionalDimensionPixelSize(originLayoutDirection: Int, resourceId: Int): Flow<Int> {
+        return dimensionPixelSize(resourceId).combine(layoutDirection) { size, direction ->
+            if (originLayoutDirection == direction) size else -size
+        }
+    }
+
     /** Given a set of [resourceId]s, emit Map<ResourceId, DimensionPixelSize> on config change */
     fun dimensionPixelSize(resourceIds: Set<Int>): Flow<Map<Int, Int>> {
         return onAnyConfigurationChange.mapLatest {
diff --git a/packages/SystemUI/src/com/android/systemui/communal/CommunalMetricsStartable.kt b/packages/SystemUI/src/com/android/systemui/communal/CommunalMetricsStartable.kt
new file mode 100644
index 0000000..c1cef67
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/CommunalMetricsStartable.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.communal
+
+import android.app.StatsManager
+import android.util.StatsEvent
+import com.android.systemui.CoreStartable
+import com.android.systemui.communal.domain.interactor.CommunalInteractor
+import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
+import com.android.systemui.communal.shared.log.CommunalMetricsLogger
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.shared.system.SysUiStatsLog
+import java.util.concurrent.Executor
+import javax.inject.Inject
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.runBlocking
+
+@SysUISingleton
+class CommunalMetricsStartable
+@Inject
+constructor(
+    @Background private val bgExecutor: Executor,
+    private val communalSettingsInteractor: CommunalSettingsInteractor,
+    private val communalInteractor: CommunalInteractor,
+    private val statsManager: StatsManager,
+    private val metricsLogger: CommunalMetricsLogger,
+) : CoreStartable, StatsManager.StatsPullAtomCallback {
+    override fun start() {
+        if (!communalSettingsInteractor.isCommunalFlagEnabled()) {
+            return
+        }
+
+        statsManager.setPullAtomCallback(
+            /* atomTag = */ SysUiStatsLog.COMMUNAL_HUB_SNAPSHOT,
+            /* metadata = */ null,
+            /* executor = */ bgExecutor,
+            /* callback = */ this,
+        )
+    }
+
+    override fun onPullAtom(atomTag: Int, statsEvents: MutableList<StatsEvent>): Int {
+        if (atomTag != SysUiStatsLog.COMMUNAL_HUB_SNAPSHOT) {
+            return StatsManager.PULL_SKIP
+        }
+
+        metricsLogger.logWidgetsSnapshot(
+            statsEvents,
+            componentNames =
+                runBlocking {
+                    communalInteractor.widgetContent.first().map {
+                        it.componentName.flattenToString()
+                    }
+                },
+        )
+        return StatsManager.PULL_SUCCESS
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt b/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt
index e9b2385..b7c02ea 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt
@@ -20,11 +20,13 @@
 import com.android.compose.animation.scene.SceneKey
 import com.android.compose.animation.scene.TransitionKey
 import com.android.systemui.CoreStartable
+import com.android.systemui.Flags.communalSceneKtfRefactor
 import com.android.systemui.communal.domain.interactor.CommunalInteractor
 import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
 import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
 import com.android.systemui.communal.shared.model.CommunalScenes
 import com.android.systemui.communal.shared.model.CommunalTransitionKeys
+import com.android.systemui.communal.shared.model.EditModeState
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
@@ -96,20 +98,25 @@
             return
         }
 
-        // Handle automatically switching based on keyguard state.
-        keyguardTransitionInteractor.startedKeyguardTransitionStep
-            .mapLatest(::determineSceneAfterTransition)
-            .filterNotNull()
-            .onEach { (nextScene, nextTransition) ->
-                if (!communalSceneInteractor.isLaunchingWidget.value) {
+        if (!communalSceneKtfRefactor()) {
+            // Handle automatically switching based on keyguard state.
+            keyguardTransitionInteractor.startedKeyguardTransitionStep
+                .mapLatest(::determineSceneAfterTransition)
+                .filterNotNull()
+                .onEach { (nextScene, nextTransition) ->
                     // When launching a widget, we don't want to animate the scene change or the
                     // Communal Hub will reveal the wallpaper even though it shouldn't. Instead we
                     // snap to the new scene as part of the launch animation, once the activity
                     // launch is done, so we don't change scene here.
-                    communalSceneInteractor.changeScene(nextScene, nextTransition)
+                    val delaySceneTransition =
+                        communalSceneInteractor.editModeState.value == EditModeState.STARTING ||
+                            communalSceneInteractor.isLaunchingWidget.value
+                    if (!delaySceneTransition) {
+                        communalSceneInteractor.changeScene(nextScene, nextTransition)
+                    }
                 }
-            }
-            .launchIn(applicationScope)
+                .launchIn(applicationScope)
+        }
 
         // TODO(b/322787129): re-enable once custom animations are in place
         // Handle automatically switching to communal when docked.
diff --git a/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt b/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt
index a445335..ba2b7bf 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt
@@ -28,6 +28,8 @@
 import com.android.systemui.communal.data.repository.CommunalTutorialRepositoryModule
 import com.android.systemui.communal.data.repository.CommunalWidgetRepositoryModule
 import com.android.systemui.communal.domain.interactor.CommunalSceneTransitionInteractor
+import com.android.systemui.communal.shared.log.CommunalMetricsLogger
+import com.android.systemui.communal.shared.log.CommunalStatsLogProxyImpl
 import com.android.systemui.communal.shared.model.CommunalScenes
 import com.android.systemui.communal.util.CommunalColors
 import com.android.systemui.communal.util.CommunalColorsImpl
@@ -44,6 +46,7 @@
 import dagger.Provides
 import dagger.multibindings.ClassKey
 import dagger.multibindings.IntoMap
+import javax.inject.Named
 import kotlinx.coroutines.CoroutineScope
 
 @Module(
@@ -74,6 +77,11 @@
     @Binds fun bindCommunalColors(impl: CommunalColorsImpl): CommunalColors
 
     @Binds
+    fun bindCommunalStatsLogProxy(
+        impl: CommunalStatsLogProxyImpl
+    ): CommunalMetricsLogger.StatsLogProxy
+
+    @Binds
     @IntoMap
     @ClassKey(CommunalSceneTransitionInteractor::class)
     abstract fun bindCommunalSceneTransitionInteractor(
@@ -81,6 +89,8 @@
     ): CoreStartable
 
     companion object {
+        const val LOGGABLE_PREFIXES = "loggable_prefixes"
+
         @Provides
         @Communal
         @SysUISingleton
@@ -107,5 +117,14 @@
         ): CommunalBackupUtils {
             return CommunalBackupUtils(context)
         }
+
+        /** The prefixes of widgets packages names that are considered loggable. */
+        @Provides
+        @Named(LOGGABLE_PREFIXES)
+        fun provideLoggablePrefixes(@Application context: Context): List<String> {
+            return context.resources
+                .getStringArray(com.android.internal.R.array.config_loggable_dream_prefixes)
+                .toList()
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalStartableModule.kt b/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalStartableModule.kt
index 74a2cd3..6cbf540 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalStartableModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalStartableModule.kt
@@ -19,6 +19,7 @@
 import com.android.systemui.CoreStartable
 import com.android.systemui.communal.CommunalBackupRestoreStartable
 import com.android.systemui.communal.CommunalDreamStartable
+import com.android.systemui.communal.CommunalMetricsStartable
 import com.android.systemui.communal.CommunalOngoingContentStartable
 import com.android.systemui.communal.CommunalSceneStartable
 import com.android.systemui.communal.log.CommunalLoggerStartable
@@ -59,4 +60,9 @@
     @IntoMap
     @ClassKey(CommunalOngoingContentStartable::class)
     fun bindCommunalOngoingContentStartable(impl: CommunalOngoingContentStartable): CoreStartable
+
+    @Binds
+    @IntoMap
+    @ClassKey(CommunalMetricsStartable::class)
+    fun bindCommunalMetricsStartable(impl: CommunalMetricsStartable): CoreStartable
 }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSmartspaceRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSmartspaceRepository.kt
index e1d9bef..86241a5 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSmartspaceRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSmartspaceRepository.kt
@@ -19,6 +19,7 @@
 import android.app.smartspace.SmartspaceTarget
 import android.os.Parcelable
 import androidx.annotation.VisibleForTesting
+import com.android.systemui.Flags.communalTimerFlickerFix
 import com.android.systemui.communal.data.model.CommunalSmartspaceTimer
 import com.android.systemui.communal.smartspace.CommunalSmartspaceController
 import com.android.systemui.dagger.SysUISingleton
@@ -80,7 +81,8 @@
                     // The view layer should have the instance based smartspaceTargetId instead of
                     // stable id, so that when a new instance of the timer is created, for example,
                     // when it is paused, the view should re-render its remote views.
-                    smartspaceTargetId = target.smartspaceTargetId,
+                    smartspaceTargetId =
+                        if (communalTimerFlickerFix()) stableId else target.smartspaceTargetId,
                     createdTimestampMillis = targetCreationTimes[stableId]!!,
                     remoteViews = target.remoteViews!!,
                 )
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt
index e65e5e5..ad0bfc7 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt
@@ -398,16 +398,13 @@
             )
         }
 
-        val session =
-            installSessions.firstOrNull {
-                it.packageName ==
-                    ComponentName.unflattenFromString(entry.componentName)?.packageName
-            }
-        return if (session != null) {
+        val componentName = ComponentName.unflattenFromString(entry.componentName)
+        val session = installSessions.firstOrNull { it.packageName == componentName?.packageName }
+        return if (componentName != null && session != null) {
             CommunalWidgetContentModel.Pending(
                 appWidgetId = entry.appWidgetId,
                 priority = entry.priority,
-                packageName = session.packageName,
+                componentName = componentName,
                 icon = session.icon,
                 user = session.user,
             )
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
index dbddc23..6aaaf3d 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
@@ -433,6 +433,7 @@
                     is CommunalWidgetContentModel.Available -> {
                         WidgetContent.Widget(
                             appWidgetId = widget.appWidgetId,
+                            priority = widget.priority,
                             providerInfo = widget.providerInfo,
                             appWidgetHost = appWidgetHost,
                             inQuietMode = isQuietModeEnabled(widget.providerInfo.profile)
@@ -441,7 +442,8 @@
                     is CommunalWidgetContentModel.Pending -> {
                         WidgetContent.PendingWidget(
                             appWidgetId = widget.appWidgetId,
-                            packageName = widget.packageName,
+                            priority = widget.priority,
+                            componentName = widget.componentName,
                             icon = widget.icon,
                         )
                     }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractor.kt
index aa9cbd0..e45a695 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractor.kt
@@ -28,6 +28,7 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.util.kotlin.BooleanFlowOperators.allOf
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -52,7 +53,7 @@
     @Application private val applicationScope: CoroutineScope,
     private val communalSceneRepository: CommunalSceneRepository,
 ) {
-    val _isLaunchingWidget = MutableStateFlow(false)
+    private val _isLaunchingWidget = MutableStateFlow(false)
 
     /** Whether a widget launch is currently in progress. */
     val isLaunchingWidget: StateFlow<Boolean> = _isLaunchingWidget.asStateFlow()
@@ -184,6 +185,10 @@
                 initialValue = false,
             )
 
+    /** This flow will be true when idle on the hub and not transitioning to edit mode. */
+    val isIdleOnCommunalNotEditMode: Flow<Boolean> =
+        allOf(isIdleOnCommunal, editModeState.map { it == null })
+
     /**
      * Flow that emits a boolean if any portion of the communal UI is visible at all.
      *
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneTransitionInteractor.kt
index 8351566..c780aac 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneTransitionInteractor.kt
@@ -85,16 +85,16 @@
      */
     private val nextKeyguardStateInternal =
         combine(
-            keyguardInteractor.isDreaming,
+            keyguardInteractor.isAbleToDream,
             keyguardInteractor.isKeyguardOccluded,
             keyguardInteractor.isKeyguardGoingAway,
         ) { dreaming, occluded, keyguardGoingAway ->
             if (keyguardGoingAway) {
                 KeyguardState.GONE
+            } else if (occluded && !dreaming) {
+                KeyguardState.OCCLUDED
             } else if (dreaming) {
                 KeyguardState.DREAMING
-            } else if (occluded) {
-                KeyguardState.OCCLUDED
             } else {
                 KeyguardState.LOCKSCREEN
             }
@@ -162,17 +162,20 @@
             // We may receive an Idle event without a corresponding Transition
             // event, such as when snapping to a scene without an animation.
             val targetState =
-                if (idle.currentScene == CommunalScenes.Blank) {
+                if (idle.currentScene == CommunalScenes.Communal) {
+                    KeyguardState.GLANCEABLE_HUB
+                } else if (currentToState == KeyguardState.GLANCEABLE_HUB) {
                     nextKeyguardState.value
                 } else {
-                    KeyguardState.GLANCEABLE_HUB
+                    // Do nothing as we are no longer in the hub state.
+                    return
                 }
             transitionKtfTo(targetState)
             repository.nextLockscreenTargetState.value = null
         }
     }
 
-    private fun finishCurrentTransition() {
+    private suspend fun finishCurrentTransition() {
         internalTransitionInteractor.updateTransition(
             currentTransitionId!!,
             1f,
@@ -188,7 +191,7 @@
                 from = internalTransitionInteractor.currentTransitionInfoInternal.value.to,
                 to = state,
                 animator = null,
-                modeOnCanceled = TransitionModeOnCanceled.REVERSE
+                modeOnCanceled = TransitionModeOnCanceled.REVERSE,
             )
         currentTransitionId = internalTransitionInteractor.startTransition(newTransition)
         internalTransitionInteractor.updateTransition(
@@ -282,7 +285,7 @@
         currentTransitionId = internalTransitionInteractor.startTransition(transitionInfo)
     }
 
-    private fun updateProgress(progress: Float) {
+    private suspend fun updateProgress(progress: Float) {
         if (currentTransitionId == null) return
         internalTransitionInteractor.updateTransition(
             currentTransitionId!!,
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/model/CommunalContentModel.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/model/CommunalContentModel.kt
index 122240d..73c6ce3 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/model/CommunalContentModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/model/CommunalContentModel.kt
@@ -18,6 +18,7 @@
 
 import android.appwidget.AppWidgetProviderInfo
 import android.appwidget.AppWidgetProviderInfo.WIDGET_FEATURE_RECONFIGURABLE
+import android.content.ComponentName
 import android.content.pm.ApplicationInfo
 import android.graphics.Bitmap
 import android.widget.RemoteViews
@@ -46,14 +47,18 @@
 
     sealed interface WidgetContent : CommunalContentModel {
         val appWidgetId: Int
+        val priority: Int
+        val componentName: ComponentName
 
         data class Widget(
             override val appWidgetId: Int,
+            override val priority: Int,
             val providerInfo: AppWidgetProviderInfo,
             val appWidgetHost: CommunalAppWidgetHost,
             val inQuietMode: Boolean,
         ) : WidgetContent {
             override val key = KEY.widget(appWidgetId)
+            override val componentName: ComponentName = providerInfo.provider
             // Widget size is always half.
             override val size = CommunalContentSize.HALF
 
@@ -66,9 +71,11 @@
 
         data class DisabledWidget(
             override val appWidgetId: Int,
+            override val priority: Int,
             val providerInfo: AppWidgetProviderInfo
         ) : WidgetContent {
             override val key = KEY.disabledWidget(appWidgetId)
+            override val componentName: ComponentName = providerInfo.provider
             // Widget size is always half.
             override val size = CommunalContentSize.HALF
 
@@ -78,7 +85,8 @@
 
         data class PendingWidget(
             override val appWidgetId: Int,
-            val packageName: String,
+            override val priority: Int,
+            override val componentName: ComponentName,
             val icon: Bitmap? = null,
         ) : WidgetContent {
             override val key = KEY.pendingWidget(appWidgetId)
diff --git a/packages/SystemUI/src/com/android/systemui/communal/log/CommunalLoggerStartable.kt b/packages/SystemUI/src/com/android/systemui/communal/log/CommunalLoggerStartable.kt
index 81feb44..2352841f 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/log/CommunalLoggerStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/log/CommunalLoggerStartable.kt
@@ -19,14 +19,16 @@
 import com.android.compose.animation.scene.ObservableTransitionState
 import com.android.internal.logging.UiEventLogger
 import com.android.systemui.CoreStartable
-import com.android.systemui.communal.domain.interactor.CommunalInteractor
+import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
 import com.android.systemui.communal.shared.log.CommunalUiEvent
 import com.android.systemui.communal.shared.model.CommunalScenes
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
 import com.android.systemui.util.kotlin.pairwise
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.drop
 import kotlinx.coroutines.flow.filterNotNull
@@ -40,12 +42,13 @@
 @Inject
 constructor(
     @Background private val backgroundScope: CoroutineScope,
-    private val communalInteractor: CommunalInteractor,
+    private val communalSceneInteractor: CommunalSceneInteractor,
+    private val keyguardInteractor: KeyguardInteractor,
     private val uiEventLogger: UiEventLogger,
 ) : CoreStartable {
 
     override fun start() {
-        communalInteractor.transitionState
+        communalSceneInteractor.transitionState
             .map { state ->
                 when {
                     state.isOnCommunal() -> CommunalUiEvent.COMMUNAL_HUB_SHOWN
@@ -60,22 +63,46 @@
             .onEach { uiEvent -> uiEventLogger.log(uiEvent) }
             .launchIn(backgroundScope)
 
-        communalInteractor.transitionState
+        communalSceneInteractor.transitionState
             .pairwise()
-            .map { (old, new) ->
+            .combine(keyguardInteractor.isDreamingWithOverlay) { (old, new), isDreaming ->
                 when {
                     new.isOnCommunal() && old.isSwipingToCommunal() ->
-                        CommunalUiEvent.COMMUNAL_HUB_SWIPE_TO_ENTER_FINISH
+                        if (isDreaming) {
+                            CommunalUiEvent.DREAM_TO_COMMUNAL_HUB_SWIPE_FINISH
+                        } else {
+                            CommunalUiEvent.COMMUNAL_HUB_SWIPE_TO_ENTER_FINISH
+                        }
                     new.isOnCommunal() && old.isSwipingFromCommunal() ->
-                        CommunalUiEvent.COMMUNAL_HUB_SWIPE_TO_EXIT_CANCEL
+                        if (isDreaming) {
+                            CommunalUiEvent.COMMUNAL_HUB_TO_DREAM_SWIPE_CANCEL
+                        } else {
+                            CommunalUiEvent.COMMUNAL_HUB_SWIPE_TO_EXIT_CANCEL
+                        }
                     new.isNotOnCommunal() && old.isSwipingFromCommunal() ->
-                        CommunalUiEvent.COMMUNAL_HUB_SWIPE_TO_EXIT_FINISH
+                        if (isDreaming) {
+                            CommunalUiEvent.COMMUNAL_HUB_TO_DREAM_SWIPE_FINISH
+                        } else {
+                            CommunalUiEvent.COMMUNAL_HUB_SWIPE_TO_EXIT_FINISH
+                        }
                     new.isNotOnCommunal() && old.isSwipingToCommunal() ->
-                        CommunalUiEvent.COMMUNAL_HUB_SWIPE_TO_ENTER_CANCEL
+                        if (isDreaming) {
+                            CommunalUiEvent.DREAM_TO_COMMUNAL_HUB_SWIPE_CANCEL
+                        } else {
+                            CommunalUiEvent.COMMUNAL_HUB_SWIPE_TO_ENTER_CANCEL
+                        }
                     new.isSwipingToCommunal() && old.isNotOnCommunal() ->
-                        CommunalUiEvent.COMMUNAL_HUB_SWIPE_TO_ENTER_START
+                        if (isDreaming) {
+                            CommunalUiEvent.DREAM_TO_COMMUNAL_HUB_SWIPE_START
+                        } else {
+                            CommunalUiEvent.COMMUNAL_HUB_SWIPE_TO_ENTER_START
+                        }
                     new.isSwipingFromCommunal() && old.isOnCommunal() ->
-                        CommunalUiEvent.COMMUNAL_HUB_SWIPE_TO_EXIT_START
+                        if (isDreaming) {
+                            CommunalUiEvent.COMMUNAL_HUB_TO_DREAM_SWIPE_START
+                        } else {
+                            CommunalUiEvent.COMMUNAL_HUB_SWIPE_TO_EXIT_START
+                        }
                     else -> null
                 }
             }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/shared/log/CommunalMetricsLogger.kt b/packages/SystemUI/src/com/android/systemui/communal/shared/log/CommunalMetricsLogger.kt
new file mode 100644
index 0000000..9ce8cf7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/shared/log/CommunalMetricsLogger.kt
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.shared.log
+
+import android.util.StatsEvent
+import com.android.systemui.communal.dagger.CommunalModule.Companion.LOGGABLE_PREFIXES
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.shared.system.SysUiStatsLog
+import javax.inject.Inject
+import javax.inject.Named
+
+@SysUISingleton
+class CommunalMetricsLogger
+@Inject
+constructor(
+    @Named(LOGGABLE_PREFIXES) private val loggablePrefixes: List<String>,
+    private val statsLogProxy: StatsLogProxy,
+) {
+    /** Logs an add widget event for metrics. No-op if widget is not loggable. */
+    fun logAddWidget(componentName: String, rank: Int) {
+        if (!componentName.isLoggable()) {
+            return
+        }
+
+        statsLogProxy.writeCommunalHubWidgetEventReported(
+            SysUiStatsLog.COMMUNAL_HUB_WIDGET_EVENT_REPORTED__ACTION__ADD,
+            componentName,
+            rank,
+        )
+    }
+
+    /** Logs a remove widget event for metrics. No-op if widget is not loggable. */
+    fun logRemoveWidget(componentName: String, rank: Int) {
+        if (!componentName.isLoggable()) {
+            return
+        }
+
+        statsLogProxy.writeCommunalHubWidgetEventReported(
+            SysUiStatsLog.COMMUNAL_HUB_WIDGET_EVENT_REPORTED__ACTION__REMOVE,
+            componentName,
+            rank,
+        )
+    }
+
+    /** Logs a tap widget event for metrics. No-op if widget is not loggable. */
+    fun logTapWidget(componentName: String, rank: Int) {
+        if (!componentName.isLoggable()) {
+            return
+        }
+
+        statsLogProxy.writeCommunalHubWidgetEventReported(
+            SysUiStatsLog.COMMUNAL_HUB_WIDGET_EVENT_REPORTED__ACTION__TAP,
+            componentName,
+            rank,
+        )
+    }
+
+    /** Logs loggable widgets and the total widget count as a [StatsEvent]. */
+    fun logWidgetsSnapshot(
+        statsEvents: MutableList<StatsEvent>,
+        componentNames: List<String>,
+    ) {
+        val loggableComponentNames = componentNames.filter { it.isLoggable() }.toTypedArray()
+        statsEvents.add(
+            statsLogProxy.buildCommunalHubSnapshotStatsEvent(
+                componentNames = loggableComponentNames,
+                widgetCount = componentNames.size,
+            )
+        )
+    }
+
+    /** Whether the component name matches any of the loggable prefixes. */
+    private fun String.isLoggable(): Boolean {
+        return loggablePrefixes.any { loggablePrefix -> startsWith(loggablePrefix) }
+    }
+
+    /** Proxy of [SysUiStatsLog] for testing purpose. */
+    interface StatsLogProxy {
+        /** Logs a [SysUiStatsLog.COMMUNAL_HUB_WIDGET_EVENT_REPORTED] stats event. */
+        fun writeCommunalHubWidgetEventReported(
+            action: Int,
+            componentName: String,
+            rank: Int,
+        )
+
+        /** Builds a [SysUiStatsLog.COMMUNAL_HUB_SNAPSHOT] stats event. */
+        fun buildCommunalHubSnapshotStatsEvent(
+            componentNames: Array<String>,
+            widgetCount: Int,
+        ): StatsEvent
+    }
+}
+
+/** Redirects calls to [SysUiStatsLog]. */
+@SysUISingleton
+class CommunalStatsLogProxyImpl @Inject constructor() : CommunalMetricsLogger.StatsLogProxy {
+    override fun writeCommunalHubWidgetEventReported(
+        action: Int,
+        componentName: String,
+        rank: Int,
+    ) {
+        SysUiStatsLog.write(
+            SysUiStatsLog.COMMUNAL_HUB_WIDGET_EVENT_REPORTED,
+            action,
+            componentName,
+            rank,
+        )
+    }
+
+    override fun buildCommunalHubSnapshotStatsEvent(
+        componentNames: Array<String>,
+        widgetCount: Int,
+    ): StatsEvent {
+        return SysUiStatsLog.buildStatsEvent(
+            SysUiStatsLog.COMMUNAL_HUB_SNAPSHOT,
+            componentNames,
+            widgetCount,
+        )
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/shared/log/CommunalUiEvent.kt b/packages/SystemUI/src/com/android/systemui/communal/shared/log/CommunalUiEvent.kt
index b64c195..4711d88 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/shared/log/CommunalUiEvent.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/shared/log/CommunalUiEvent.kt
@@ -53,7 +53,21 @@
     @UiEvent(doc = "User performs a swipe up gesture from bottom to enter bouncer")
     COMMUNAL_HUB_SWIPE_UP_TO_BOUNCER(1573),
     @UiEvent(doc = "User performs a swipe down gesture from top to enter shade")
-    COMMUNAL_HUB_SWIPE_DOWN_TO_SHADE(1574);
+    COMMUNAL_HUB_SWIPE_DOWN_TO_SHADE(1574),
+    @UiEvent(doc = "User starts the swipe gesture to enter the Communal Hub from Dream")
+    DREAM_TO_COMMUNAL_HUB_SWIPE_START(1860),
+    @UiEvent(doc = "User finishes the swipe gesture to enter the Communal Hub from Dream")
+    DREAM_TO_COMMUNAL_HUB_SWIPE_FINISH(1861),
+    @UiEvent(doc = "User cancels the swipe gesture to enter the Communal Hub from Dream")
+    DREAM_TO_COMMUNAL_HUB_SWIPE_CANCEL(1862),
+    @UiEvent(doc = "User starts the swipe gesture to exit the Communal Hub to go to Dream")
+    COMMUNAL_HUB_TO_DREAM_SWIPE_START(1863),
+    @UiEvent(doc = "User finishes the swipe gesture to exit the Communal Hub to go to Dream")
+    COMMUNAL_HUB_TO_DREAM_SWIPE_FINISH(1864),
+    @UiEvent(doc = "User cancels the swipe gesture to exit the Communal Hub to go to Dream")
+    COMMUNAL_HUB_TO_DREAM_SWIPE_CANCEL(1865),
+    @UiEvent(doc = "A transition from Dream to Communal Hub starts due to dream awakening")
+    DREAM_TO_COMMUNAL_HUB_DREAM_AWAKE_START(1866);
 
     override fun getId(): Int {
         return id
diff --git a/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalWidgetContentModel.kt b/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalWidgetContentModel.kt
index 53aecc1..7cddb72 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalWidgetContentModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalWidgetContentModel.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.communal.shared.model
 
 import android.appwidget.AppWidgetProviderInfo
+import android.content.ComponentName
 import android.graphics.Bitmap
 import android.os.UserHandle
 
@@ -36,7 +37,7 @@
     data class Pending(
         override val appWidgetId: Int,
         override val priority: Int,
-        val packageName: String,
+        val componentName: ComponentName,
         val icon: Bitmap?,
         val user: UserHandle,
     ) : CommunalWidgetContentModel
diff --git a/packages/SystemUI/src/com/android/systemui/communal/smartspace/SmartspaceInteractionHandler.kt b/packages/SystemUI/src/com/android/systemui/communal/smartspace/SmartspaceInteractionHandler.kt
index 4e3d3ff..c4edcac 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/smartspace/SmartspaceInteractionHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/smartspace/SmartspaceInteractionHandler.kt
@@ -25,6 +25,9 @@
 import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
 import com.android.systemui.communal.util.InteractionHandlerDelegate
 import com.android.systemui.communal.widgets.SmartspaceAppWidgetHostView
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.core.Logger
+import com.android.systemui.log.dagger.CommunalLog
 import com.android.systemui.plugins.ActivityStarter
 import javax.inject.Inject
 
@@ -34,12 +37,19 @@
 constructor(
     private val activityStarter: ActivityStarter,
     communalSceneInteractor: CommunalSceneInteractor,
+    @CommunalLog val logBuffer: LogBuffer,
 ) : RemoteViews.InteractionHandler {
+
+    private companion object {
+        const val TAG = "SmartspaceInteractionHandler"
+    }
+
     private val delegate =
         InteractionHandlerDelegate(
             communalSceneInteractor,
             findViewToAnimate = { view -> view is SmartspaceAppWidgetHostView },
             intentStarter = this::startIntent,
+            logger = Logger(logBuffer, TAG),
         )
 
     override fun onInteraction(
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
index 01ed2b7..d1a5a4b 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.communal.ui.viewmodel
 
+import android.appwidget.AppWidgetProviderInfo
 import android.content.ComponentName
 import android.os.UserHandle
 import android.view.View
@@ -29,9 +30,12 @@
 import com.android.systemui.communal.widgets.WidgetConfigurator
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.media.controls.ui.view.MediaHost
+import com.android.systemui.util.kotlin.BooleanFlowOperators.anyOf
+import com.android.systemui.util.kotlin.BooleanFlowOperators.not
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
 import kotlinx.coroutines.flow.flowOf
 
 /** The base view model for the communal hub. */
@@ -57,6 +61,26 @@
     val selectedKey: StateFlow<String?>
         get() = _selectedKey
 
+    private val _isTouchConsumed: MutableStateFlow<Boolean> = MutableStateFlow(false)
+
+    /** Whether an element inside the lazy grid is actively consuming touches */
+    val isTouchConsumed: Flow<Boolean> = _isTouchConsumed.asStateFlow()
+
+    private val _isNestedScrolling: MutableStateFlow<Boolean> = MutableStateFlow(false)
+
+    /** Whether the lazy grid is reporting scrolling within itself */
+    val isNestedScrolling: Flow<Boolean> = _isNestedScrolling.asStateFlow()
+
+    /**
+     * Whether touch is available to be consumed by a touch handler. Touch is available during
+     * nested scrolling as lazy grid reports this for all scroll directions that it detects. In the
+     * case that there is consumed scrolling on a nested element, such as an AndroidView, no nested
+     * scrolling will be reported. It is up to the flow consumer to determine whether the nested
+     * scroll can be applied. In the communal case, this would be identifying the scroll as
+     * vertical, which the lazy horizontal grid does not handle.
+     */
+    val glanceableTouchAvailable: Flow<Boolean> = anyOf(not(isTouchConsumed), isNestedScrolling)
+
     /** Accessibility delegate to be set on CommunalAppWidgetHostView. */
     open val widgetAccessibilityDelegate: View.AccessibilityDelegate? = null
 
@@ -99,18 +123,6 @@
         communalSceneInteractor.setTransitionState(transitionState)
     }
 
-    /**
-     * Called when a widget is added via drag and drop from the widget picker into the communal hub.
-     */
-    open fun onAddWidget(
-        componentName: ComponentName,
-        user: UserHandle,
-        priority: Int,
-        configurator: WidgetConfigurator? = null
-    ) {
-        communalInteractor.addWidget(componentName, user, priority, configurator)
-    }
-
     open fun onOpenEnableWidgetDialog() {}
 
     open fun onOpenEnableWorkProfileDialog() {}
@@ -136,8 +148,26 @@
     /** Called as the UI request to dismiss the any displaying popup */
     open fun onHidePopup() {}
 
+    /** Called as the UI requests adding a widget. */
+    open fun onAddWidget(
+        componentName: ComponentName,
+        user: UserHandle,
+        priority: Int,
+        configurator: WidgetConfigurator? = null,
+    ) {}
+
     /** Called as the UI requests deleting a widget. */
-    open fun onDeleteWidget(id: Int) {}
+    open fun onDeleteWidget(
+        id: Int,
+        componentName: ComponentName,
+        priority: Int,
+    ) {}
+
+    /** Called as the UI detects a tap event on the widget. */
+    open fun onTapWidget(
+        componentName: ComponentName,
+        priority: Int,
+    ) {}
 
     /**
      * Called as the UI requests reordering widgets.
@@ -168,6 +198,9 @@
     /** Called as the user request to show the customize widget button. */
     open fun onLongClick() {}
 
+    /** Called as the UI determines that a new widget has been added to the grid. */
+    open fun onNewWidgetAdded(provider: AppWidgetProviderInfo) {}
+
     /** Called when the grid scroll position has been updated. */
     open fun onScrollPositionUpdated(firstVisibleItemIndex: Int, firstVisibleItemScroll: Int) {
         currentScrollIndex = firstVisibleItemIndex
@@ -194,4 +227,28 @@
     fun setSelectedKey(key: String?) {
         _selectedKey.value = key
     }
+
+    /** Invoked once touches inside the lazy grid are consumed */
+    fun onHubTouchConsumed() {
+        if (_isTouchConsumed.value) {
+            return
+        }
+
+        _isTouchConsumed.value = true
+    }
+
+    /** Invoked when nested scrolling begins on the lazy grid */
+    fun onNestedScrolling() {
+        if (_isNestedScrolling.value) {
+            return
+        }
+
+        _isNestedScrolling.value = true
+    }
+
+    /** Resets nested scroll and touch consumption state */
+    fun onResetTouchState() {
+        _isTouchConsumed.value = false
+        _isNestedScrolling.value = false
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt
index b1e5135..1a86c71 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt
@@ -18,22 +18,28 @@
 
 import android.appwidget.AppWidgetManager
 import android.appwidget.AppWidgetProviderInfo
+import android.content.ComponentName
+import android.content.Context
 import android.content.Intent
 import android.content.pm.PackageManager
 import android.content.res.Resources
+import android.os.UserHandle
 import android.util.Log
+import android.view.accessibility.AccessibilityEvent
+import android.view.accessibility.AccessibilityManager
 import androidx.activity.result.ActivityResultLauncher
 import com.android.internal.logging.UiEventLogger
-import com.android.systemui.Flags.enableWidgetPickerSizeFilter
 import com.android.systemui.communal.data.model.CommunalWidgetCategories
 import com.android.systemui.communal.domain.interactor.CommunalInteractor
-import com.android.systemui.communal.domain.interactor.CommunalPrefsInteractor
 import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
 import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
 import com.android.systemui.communal.domain.model.CommunalContentModel
+import com.android.systemui.communal.shared.log.CommunalMetricsLogger
 import com.android.systemui.communal.shared.log.CommunalUiEvent
 import com.android.systemui.communal.shared.model.EditModeState
+import com.android.systemui.communal.widgets.WidgetConfigurator
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
 import com.android.systemui.keyguard.shared.model.KeyguardState
@@ -71,7 +77,10 @@
     private val uiEventLogger: UiEventLogger,
     @CommunalLog logBuffer: LogBuffer,
     @Background private val backgroundDispatcher: CoroutineDispatcher,
-    private val communalPrefsInteractor: CommunalPrefsInteractor,
+    private val metricsLogger: CommunalMetricsLogger,
+    @Application private val context: Context,
+    private val accessibilityManager: AccessibilityManager,
+    private val packageManager: PackageManager,
 ) : BaseCommunalViewModel(communalSceneInteractor, communalInteractor, mediaHost) {
 
     private val logger = Logger(logBuffer, "CommunalEditModeViewModel")
@@ -113,7 +122,24 @@
     override val reorderingWidgets: StateFlow<Boolean>
         get() = _reorderingWidgets
 
-    override fun onDeleteWidget(id: Int) = communalInteractor.deleteWidget(id)
+    override fun onAddWidget(
+        componentName: ComponentName,
+        user: UserHandle,
+        priority: Int,
+        configurator: WidgetConfigurator?
+    ) {
+        communalInteractor.addWidget(componentName, user, priority, configurator)
+        metricsLogger.logAddWidget(componentName.flattenToString(), priority)
+    }
+
+    override fun onDeleteWidget(
+        id: Int,
+        componentName: ComponentName,
+        priority: Int,
+    ) {
+        communalInteractor.deleteWidget(id)
+        metricsLogger.logRemoveWidget(componentName.flattenToString(), priority)
+    }
 
     override fun onReorderWidgets(widgetIdToPriorityMap: Map<Int, Int>) =
         communalInteractor.updateWidgetOrder(widgetIdToPriorityMap)
@@ -135,6 +161,25 @@
         uiEventLogger.log(CommunalUiEvent.COMMUNAL_HUB_REORDER_WIDGET_CANCEL)
     }
 
+    override fun onNewWidgetAdded(provider: AppWidgetProviderInfo) {
+        if (!accessibilityManager.isEnabled) {
+            return
+        }
+
+        // Send an accessibility announcement for the newly added widget
+        val widgetLabel = provider.loadLabel(packageManager)
+        val announcementText =
+            context.getString(
+                R.string.accessibility_announcement_communal_widget_added,
+                widgetLabel
+            )
+        accessibilityManager.sendAccessibilityEvent(
+            AccessibilityEvent(AccessibilityEvent.TYPE_ANNOUNCEMENT).apply {
+                contentDescription = announcementText
+            }
+        )
+    }
+
     val isIdleOnCommunal: StateFlow<Boolean> = communalInteractor.isIdleOnCommunal
 
     /** Launch the widget picker activity using the given {@link ActivityResultLauncher}. */
@@ -176,16 +221,14 @@
 
         return Intent(Intent.ACTION_PICK).apply {
             setPackage(packageName)
-            if (enableWidgetPickerSizeFilter()) {
-                putExtra(
-                    EXTRA_DESIRED_WIDGET_WIDTH,
-                    resources.getDimensionPixelSize(R.dimen.communal_widget_picker_desired_width)
-                )
-                putExtra(
-                    EXTRA_DESIRED_WIDGET_HEIGHT,
-                    resources.getDimensionPixelSize(R.dimen.communal_widget_picker_desired_height)
-                )
-            }
+            putExtra(
+                EXTRA_DESIRED_WIDGET_WIDTH,
+                resources.getDimensionPixelSize(R.dimen.communal_widget_picker_desired_width)
+            )
+            putExtra(
+                EXTRA_DESIRED_WIDGET_HEIGHT,
+                resources.getDimensionPixelSize(R.dimen.communal_widget_picker_desired_height)
+            )
             putExtra(
                 AppWidgetManager.EXTRA_CATEGORY_FILTER,
                 CommunalWidgetCategories.defaultCategories
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalTransitionViewModel.kt
index e1408a0..bbd8596 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalTransitionViewModel.kt
@@ -19,6 +19,7 @@
 import android.graphics.Color
 import com.android.systemui.communal.domain.interactor.CommunalInteractor
 import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
+import com.android.systemui.communal.shared.model.CommunalScenes
 import com.android.systemui.communal.util.CommunalColors
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
@@ -58,9 +59,17 @@
     dreamToGlanceableHubTransitionViewModel: DreamingToGlanceableHubTransitionViewModel,
     glanceableHubToDreamTransitionViewModel: GlanceableHubToDreamingTransitionViewModel,
     communalInteractor: CommunalInteractor,
-    communalSceneInteractor: CommunalSceneInteractor,
+    private val communalSceneInteractor: CommunalSceneInteractor,
     keyguardTransitionInteractor: KeyguardTransitionInteractor
 ) {
+    /**
+     * Snaps to [CommunalScenes.Communal], showing the glanceable hub immediately without any
+     * transition.
+     */
+    fun snapToCommunal() {
+        communalSceneInteractor.snapToScene(CommunalScenes.Communal)
+    }
+
     // Show UMO on glanceable hub immediately on transition into glanceable hub
     private val showUmoFromOccludedToGlanceableHub: Flow<Boolean> =
         keyguardTransitionInteractor
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
index 1e087f7..3fc8b09 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.communal.ui.viewmodel
 
+import android.content.ComponentName
 import android.content.res.Resources
 import android.os.Bundle
 import android.view.View
@@ -25,6 +26,7 @@
 import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
 import com.android.systemui.communal.domain.interactor.CommunalTutorialInteractor
 import com.android.systemui.communal.domain.model.CommunalContentModel
+import com.android.systemui.communal.shared.log.CommunalMetricsLogger
 import com.android.systemui.communal.shared.model.CommunalBackgroundType
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
@@ -92,6 +94,7 @@
     private val shadeInteractor: ShadeInteractor,
     @Named(MediaModule.COMMUNAL_HUB) mediaHost: MediaHost,
     @CommunalLog logBuffer: LogBuffer,
+    private val metricsLogger: CommunalMetricsLogger,
 ) : BaseCommunalViewModel(communalSceneInteractor, communalInteractor, mediaHost) {
 
     private val _isMediaHostVisible =
@@ -260,6 +263,10 @@
         }
     }
 
+    override fun onTapWidget(componentName: ComponentName, priority: Int) {
+        metricsLogger.logTapWidget(componentName.flattenToString(), priority)
+    }
+
     fun onClick() {
         keyguardIndicationController.showActionToUnlock()
     }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/util/DensityUtils.kt b/packages/SystemUI/src/com/android/systemui/communal/util/DensityUtils.kt
new file mode 100644
index 0000000..57be7b5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/util/DensityUtils.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.util
+
+import android.view.Display
+import android.view.WindowManagerGlobal
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.dp
+
+/**
+ * [DensityUtils] helps convert dp defined values to be consistent regardless of the set density.
+ */
+class DensityUtils {
+    companion object {
+        val Int.adjustedDp: Dp
+            get() = this.dp * scalingAdjustment
+
+        private val windowManagerService = WindowManagerGlobal.getWindowManagerService()
+        val scalingAdjustment
+            get() =
+                windowManagerService?.let { wm ->
+                    wm.getInitialDisplayDensity(Display.DEFAULT_DISPLAY).toFloat() /
+                        wm.getBaseDisplayDensity(Display.DEFAULT_DISPLAY)
+                } ?: 1F
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/util/InteractionHandlerDelegate.kt b/packages/SystemUI/src/com/android/systemui/communal/util/InteractionHandlerDelegate.kt
index 51a5fcd..d2029d5 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/util/InteractionHandlerDelegate.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/util/InteractionHandlerDelegate.kt
@@ -26,12 +26,14 @@
 import com.android.systemui.animation.ActivityTransitionAnimator
 import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
 import com.android.systemui.communal.widgets.CommunalTransitionAnimatorController
+import com.android.systemui.log.core.Logger
 
 /** A delegate that can be used to launch activities from [RemoteViews] */
 class InteractionHandlerDelegate(
     private val communalSceneInteractor: CommunalSceneInteractor,
     private val findViewToAnimate: (View) -> Boolean,
     private val intentStarter: IntentStarter,
+    private val logger: Logger,
 ) : RemoteViews.InteractionHandler {
 
     /** Responsible for starting the pending intent for launching activities. */
@@ -49,6 +51,10 @@
         pendingIntent: PendingIntent,
         response: RemoteViews.RemoteResponse
     ): Boolean {
+        logger.i({ "Starting $str1 ($str2)" }) {
+            str1 = pendingIntent.toLoggingString()
+            str2 = pendingIntent.creatorPackage
+        }
         val launchOptions = response.getLaunchOptions(view)
         return when {
             pendingIntent.isActivity -> {
@@ -82,3 +88,12 @@
         return null
     }
 }
+
+private fun PendingIntent.toLoggingString() =
+    when {
+        isActivity -> "activity"
+        isBroadcast -> "broadcast"
+        isForegroundService -> "fgService"
+        isService -> "service"
+        else -> "unknown"
+    }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHost.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHost.kt
index 058ca4d..10a565f 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHost.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHost.kt
@@ -36,7 +36,7 @@
     context: Context,
     private val backgroundScope: CoroutineScope,
     hostId: Int,
-    interactionHandler: RemoteViews.InteractionHandler,
+    private val interactionHandler: RemoteViews.InteractionHandler,
     looper: Looper,
     logBuffer: LogBuffer,
 ) : AppWidgetHost(context, hostId, interactionHandler, looper) {
@@ -55,7 +55,7 @@
         appWidgetId: Int,
         appWidget: AppWidgetProviderInfo?
     ): AppWidgetHostView {
-        return CommunalAppWidgetHostView(context)
+        return CommunalAppWidgetHostView(context, interactionHandler)
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostView.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostView.kt
index 2559137..d549734 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostView.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostView.kt
@@ -17,17 +17,25 @@
 package com.android.systemui.communal.widgets
 
 import android.appwidget.AppWidgetHostView
+import android.appwidget.AppWidgetManager
 import android.appwidget.AppWidgetProviderInfo
 import android.content.Context
+import android.content.pm.LauncherActivityInfo
+import android.content.pm.LauncherApps
 import android.graphics.Outline
 import android.graphics.Rect
 import android.view.View
 import android.view.ViewOutlineProvider
+import android.widget.RemoteViews
+import android.widget.RemoteViews.RemoteResponse
 import com.android.systemui.animation.LaunchableView
 import com.android.systemui.animation.LaunchableViewDelegate
 
 /** AppWidgetHostView that displays in communal hub with support for rounded corners. */
-class CommunalAppWidgetHostView(context: Context) : AppWidgetHostView(context), LaunchableView {
+class CommunalAppWidgetHostView(
+    context: Context,
+    private val interactionHandler: RemoteViews.InteractionHandler,
+) : AppWidgetHostView(context, interactionHandler), LaunchableView {
     private val launchableViewDelegate =
         LaunchableViewDelegate(
             this,
@@ -92,4 +100,26 @@
         launchableViewDelegate.setShouldBlockVisibilityChanges(block)
 
     override fun setVisibility(visibility: Int) = launchableViewDelegate.setVisibility(visibility)
+
+    override fun onDefaultViewClicked(view: View) {
+        AppWidgetManager.getInstance(context)?.noteAppWidgetTapped(appWidgetId)
+        if (appWidgetInfo == null) {
+            return
+        }
+        val launcherApps = context.getSystemService(LauncherApps::class.java)
+        val activityInfo: LauncherActivityInfo =
+            launcherApps
+                .getActivityList(appWidgetInfo.provider.packageName, appWidgetInfo.profile)
+                ?.getOrNull(0) ?: return
+
+        val intent =
+            launcherApps.getMainActivityLaunchIntent(
+                activityInfo.componentName,
+                null,
+                activityInfo.user
+            )
+        if (intent != null) {
+            interactionHandler.onInteraction(view, intent, RemoteResponse.fromPendingIntent(intent))
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt
index 08fe42e..03ef17b 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt
@@ -41,6 +41,7 @@
 import com.android.systemui.communal.ui.compose.CommunalHub
 import com.android.systemui.communal.ui.viewmodel.CommunalEditModeViewModel
 import com.android.systemui.communal.util.WidgetPickerIntentUtils.getWidgetExtraFromIntent
+import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.log.LogBuffer
 import com.android.systemui.log.core.Logger
 import com.android.systemui.log.dagger.CommunalLog
@@ -127,7 +128,7 @@
                 Box(
                     modifier =
                         Modifier.fillMaxSize()
-                            .background(LocalAndroidColorScheme.current.onSecondaryFixed),
+                            .background(LocalAndroidColorScheme.current.surfaceDim),
                 ) {
                     CommunalHub(
                         viewModel = communalViewModel,
@@ -146,7 +147,8 @@
             communalViewModel.canShowEditMode.collect {
                 communalViewModel.changeScene(
                     CommunalScenes.Blank,
-                    CommunalTransitionKeys.ToEditMode
+                    CommunalTransitionKeys.ToEditMode,
+                    KeyguardState.GONE,
                 )
                 // wait till transitioned to Blank scene, then animate in communal content in
                 // edit mode
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/RoundedCornerEnforcement.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/RoundedCornerEnforcement.kt
index abda44b..87aa5e2 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/widgets/RoundedCornerEnforcement.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/RoundedCornerEnforcement.kt
@@ -24,6 +24,7 @@
 import android.view.View
 import android.view.ViewGroup
 import androidx.core.os.BuildCompat.isAtLeastS
+import com.android.systemui.communal.util.DensityUtils
 import com.android.systemui.res.R
 import kotlin.math.min
 
@@ -82,7 +83,8 @@
     /** Get the radius of the rounded rectangle defined in the host's resource. */
     private fun getOwnedEnforcedRadius(context: Context): Float {
         val res: Resources = context.resources
-        return res.getDimension(R.dimen.communal_enforced_rounded_corner_max_radius)
+        return res.getDimension(R.dimen.communal_enforced_rounded_corner_max_radius) *
+            DensityUtils.scalingAdjustment
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetInteractionHandler.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetInteractionHandler.kt
index 519903e..0eeb506 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetInteractionHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetInteractionHandler.kt
@@ -25,6 +25,9 @@
 import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
 import com.android.systemui.communal.util.InteractionHandlerDelegate
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.core.Logger
+import com.android.systemui.log.dagger.CommunalLog
 import com.android.systemui.plugins.ActivityStarter
 import javax.inject.Inject
 
@@ -33,14 +36,20 @@
 @Inject
 constructor(
     private val activityStarter: ActivityStarter,
-    private val communalSceneInteractor: CommunalSceneInteractor
+    communalSceneInteractor: CommunalSceneInteractor,
+    @CommunalLog val logBuffer: LogBuffer,
 ) : RemoteViews.InteractionHandler {
 
+    private companion object {
+        const val TAG = "WidgetInteractionHandler"
+    }
+
     private val delegate =
         InteractionHandlerDelegate(
             communalSceneInteractor,
             findViewToAnimate = { view -> view is CommunalAppWidgetHostView },
             intentStarter = this::startIntent,
+            logger = Logger(logBuffer, TAG),
         )
 
     override fun onInteraction(
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
index 2fbb75e..c2e1e33 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
@@ -29,7 +29,6 @@
 import com.android.systemui.sensorprivacy.SensorUseStartedActivity;
 import com.android.systemui.settings.brightness.BrightnessDialog;
 import com.android.systemui.telephony.ui.activity.SwitchToManagedProfileForCallActivity;
-import com.android.systemui.touchpad.tutorial.ui.view.TouchpadTutorialActivity;
 import com.android.systemui.tuner.TunerActivity;
 import com.android.systemui.usb.UsbAccessoryUriActivity;
 import com.android.systemui.usb.UsbConfirmActivity;
@@ -157,10 +156,4 @@
     @ClassKey(SwitchToManagedProfileForCallActivity.class)
     public abstract Activity bindSwitchToManagedProfileForCallActivity(
             SwitchToManagedProfileForCallActivity activity);
-
-    /** Inject into TouchpadTutorialActivity. */
-    @Binds
-    @IntoMap
-    @ClassKey(TouchpadTutorialActivity.class)
-    public abstract Activity bindTouchpadTutorialActivity(TouchpadTutorialActivity activity);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DefaultBroadcastReceiverBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DefaultBroadcastReceiverBinder.java
index 7ced932..5a0eb72 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DefaultBroadcastReceiverBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DefaultBroadcastReceiverBinder.java
@@ -19,6 +19,7 @@
 import android.content.BroadcastReceiver;
 
 import com.android.systemui.GuestResetOrExitSessionReceiver;
+import com.android.systemui.accessibility.extradim.ExtraDimDialogReceiver;
 import com.android.systemui.accessibility.hearingaid.HearingDevicesDialogReceiver;
 import com.android.systemui.media.dialog.MediaOutputDialogReceiver;
 import com.android.systemui.people.widget.PeopleSpaceWidgetPinnedReceiver;
@@ -88,4 +89,13 @@
     @ClassKey(HearingDevicesDialogReceiver.class)
     public abstract BroadcastReceiver bindHearingDevicesDialogReceiver(
             HearingDevicesDialogReceiver broadcastReceiver);
+
+    /**
+     *
+     */
+    @Binds
+    @IntoMap
+    @ClassKey(ExtraDimDialogReceiver.class)
+    public abstract BroadcastReceiver bindExtraDimDialogReceiver(
+            ExtraDimDialogReceiver broadcastReceiver);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
index 9ae63a1..3273111 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
@@ -77,6 +77,7 @@
 import com.android.systemui.statusbar.policy.SensorPrivacyController;
 import com.android.systemui.statusbar.policy.SensorPrivacyControllerImpl;
 import com.android.systemui.toast.ToastModule;
+import com.android.systemui.touchpad.tutorial.TouchpadKeyboardTutorialModule;
 import com.android.systemui.unfold.SysUIUnfoldStartableModule;
 import com.android.systemui.unfold.UnfoldTransitionModule;
 import com.android.systemui.util.kotlin.SysUICoroutinesModule;
@@ -141,6 +142,7 @@
         SysUIUnfoldStartableModule.class,
         UnfoldTransitionModule.Startables.class,
         ToastModule.class,
+        TouchpadKeyboardTutorialModule.class,
         VolumeModule.class,
         WallpaperModule.class,
         ShortcutHelperModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
index 88601da..4286646 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
@@ -30,6 +30,7 @@
 import com.android.systemui.dreams.DreamMonitor
 import com.android.systemui.dreams.homecontrols.HomeControlsDreamStartable
 import com.android.systemui.globalactions.GlobalActionsComponent
+import com.android.systemui.inputdevice.oobe.KeyboardTouchpadOobeTutorialCoreStartable
 import com.android.systemui.keyboard.KeyboardUI
 import com.android.systemui.keyboard.PhysicalKeyboardCoreStartable
 import com.android.systemui.keyguard.KeyguardViewConfigurator
@@ -257,6 +258,13 @@
 
     @Binds
     @IntoMap
+    @ClassKey(KeyboardTouchpadOobeTutorialCoreStartable::class)
+    abstract fun bindOobeSchedulerCoreStartable(
+        listener: KeyboardTouchpadOobeTutorialCoreStartable
+    ): CoreStartable
+
+    @Binds
+    @IntoMap
     @ClassKey(PhysicalKeyboardCoreStartable::class)
     abstract fun bindKeyboardCoreStartable(listener: PhysicalKeyboardCoreStartable): CoreStartable
 
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 1771f4d..609aa39 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -80,6 +80,7 @@
 import com.android.systemui.model.SysUiState;
 import com.android.systemui.motiontool.MotionToolModule;
 import com.android.systemui.navigationbar.NavigationBarComponent;
+import com.android.systemui.navigationbar.gestural.dagger.GestureModule;
 import com.android.systemui.notetask.NoteTaskModule;
 import com.android.systemui.people.PeopleModule;
 import com.android.systemui.plugins.BcSmartspaceConfigPlugin;
@@ -102,6 +103,7 @@
 import com.android.systemui.screenshot.dagger.ScreenshotModule;
 import com.android.systemui.security.data.repository.SecurityRepositoryModule;
 import com.android.systemui.settings.DisplayTracker;
+import com.android.systemui.settings.UserTracker;
 import com.android.systemui.shade.ShadeController;
 import com.android.systemui.shade.transition.LargeScreenShadeInterpolator;
 import com.android.systemui.shade.transition.LargeScreenShadeInterpolatorImpl;
@@ -143,6 +145,7 @@
 import com.android.systemui.statusbar.window.StatusBarWindowModule;
 import com.android.systemui.telephony.data.repository.TelephonyRepositoryModule;
 import com.android.systemui.temporarydisplay.dagger.TemporaryDisplayModule;
+import com.android.systemui.touchpad.TouchpadModule;
 import com.android.systemui.tuner.dagger.TunerModule;
 import com.android.systemui.user.UserModule;
 import com.android.systemui.user.domain.UserDomainLayerModule;
@@ -152,6 +155,7 @@
 import com.android.systemui.util.kotlin.SysUICoroutinesModule;
 import com.android.systemui.util.reference.ReferenceModule;
 import com.android.systemui.util.sensors.SensorModule;
+import com.android.systemui.util.settings.SettingsProxy;
 import com.android.systemui.util.settings.SettingsUtilModule;
 import com.android.systemui.util.time.SystemClock;
 import com.android.systemui.util.time.SystemClockImpl;
@@ -215,6 +219,7 @@
         FlagsModule.class,
         FlagDependenciesModule.class,
         FooterActionsModule.class,
+        GestureModule.class,
         InputMethodModule.class,
         KeyEventRepositoryModule.class,
         KeyboardModule.class,
@@ -257,6 +262,7 @@
         CommonSystemUIUnfoldModule.class,
         TelephonyRepositoryModule.class,
         TemporaryDisplayModule.class,
+        TouchpadModule.class,
         TunerModule.class,
         UserDomainLayerModule.class,
         UserModule.class,
@@ -264,15 +270,15 @@
         NoteTaskModule.class,
         WalletModule.class,
         ContextualEducationModule.class
-        },
+},
         subcomponents = {
-            ComplicationComponent.class,
-            DozeComponent.class,
-            ExpandableNotificationRowComponent.class,
-            KeyguardBouncerComponent.class,
-            NavigationBarComponent.class,
-            NotificationRowComponent.class,
-            WindowRootViewComponent.class,
+                ComplicationComponent.class,
+                DozeComponent.class,
+                ExpandableNotificationRowComponent.class,
+                KeyguardBouncerComponent.class,
+                NavigationBarComponent.class,
+                NotificationRowComponent.class,
+                WindowRootViewComponent.class,
         })
 public abstract class SystemUIModule {
 
@@ -439,4 +445,9 @@
 
     @Binds
     abstract SceneDataSource bindSceneDataSource(SceneDataSourceDelegator delegator);
+
+    @Provides
+    static SettingsProxy.CurrentUserIdProvider provideCurrentUserId(UserTracker userTracker) {
+        return userTracker::getUserId;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricsAllowedInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricsAllowedInteractor.kt
new file mode 100644
index 0000000..79b176c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricsAllowedInteractor.kt
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.deviceentry.domain.interactor
+
+import com.android.systemui.biometrics.data.repository.FacePropertyRepository
+import com.android.systemui.biometrics.shared.model.SensorStrength
+import com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.map
+
+/**
+ * Individual biometrics (ie: fingerprint or face) may not be allowed to be used based on the
+ * lockout states of biometrics of the same or higher sensor strength.
+ *
+ * This class coordinates the lockout states of each individual biometric based on the lockout
+ * states of other biometrics.
+ */
+@OptIn(ExperimentalCoroutinesApi::class)
+@SysUISingleton
+class DeviceEntryBiometricsAllowedInteractor
+@Inject
+constructor(
+    deviceEntryFingerprintAuthInteractor: DeviceEntryFingerprintAuthInteractor,
+    deviceEntryFaceAuthInteractor: DeviceEntryFaceAuthInteractor,
+    biometricSettingsInteractor: DeviceEntryBiometricSettingsInteractor,
+    facePropertyRepository: FacePropertyRepository,
+) {
+
+    private val isStrongFaceAuth: Flow<Boolean> =
+        facePropertyRepository.sensorInfo.map { it?.strength == SensorStrength.STRONG }
+
+    private val isStrongFaceAuthLockedOut: Flow<Boolean> =
+        combine(isStrongFaceAuth, deviceEntryFaceAuthInteractor.isLockedOut) {
+            isStrongFaceAuth,
+            isFaceAuthLockedOut ->
+            isStrongFaceAuth && isFaceAuthLockedOut
+        }
+
+    /**
+     * Whether fingerprint authentication is currently allowed for the user. This is true if the
+     * user has fingerprint auth enabled, enrolled, it is not disabled by any security timeouts by
+     * [com.android.systemui.keyguard.shared.model.AuthenticationFlags], not locked out due to too
+     * many incorrect attempts, and other biometrics at a higher or equal strenght are not locking
+     * fingerprint out.
+     */
+    val isFingerprintAuthCurrentlyAllowed: Flow<Boolean> =
+        combine(
+            deviceEntryFingerprintAuthInteractor.isLockedOut,
+            biometricSettingsInteractor.fingerprintAuthCurrentlyAllowed,
+            isStrongFaceAuthLockedOut,
+        ) { fpLockedOut, fpAllowedBySettings, strongAuthFaceAuthLockedOut ->
+            !fpLockedOut && fpAllowedBySettings && !strongAuthFaceAuthLockedOut
+        }
+
+    /** Whether fingerprint authentication is currently allowed while on the bouncer. */
+    val isFingerprintCurrentlyAllowedOnBouncer =
+        deviceEntryFingerprintAuthInteractor.isSensorUnderDisplay.flatMapLatest { sensorBelowDisplay
+            ->
+            if (sensorBelowDisplay) {
+                flowOf(false)
+            } else {
+                isFingerprintAuthCurrentlyAllowed
+            }
+        }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFingerprintAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFingerprintAuthInteractor.kt
index a5eafa9..969f53f 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFingerprintAuthInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFingerprintAuthInteractor.kt
@@ -29,10 +29,7 @@
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.filterIsInstance
-import kotlinx.coroutines.flow.flatMapLatest
-import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.map
 
 @OptIn(ExperimentalCoroutinesApi::class)
@@ -70,29 +67,9 @@
         repository.authenticationStatus.filterIsInstance<SuccessFingerprintAuthenticationStatus>()
 
     /**
-     * Whether fingerprint authentication is currently allowed for the user. This is true if the
-     * user has fingerprint auth enabled, enrolled, it is not disabled by any security timeouts by
-     * [com.android.systemui.keyguard.shared.model.AuthenticationFlags] and not locked out due to
-     * too many incorrect attempts.
-     */
-    val isFingerprintAuthCurrentlyAllowed: Flow<Boolean> =
-        combine(isLockedOut, biometricSettingsInteractor.fingerprintAuthCurrentlyAllowed, ::Pair)
-            .map { (lockedOut, currentlyAllowed) -> !lockedOut && currentlyAllowed }
-
-    /**
      * Whether the fingerprint sensor is present under the display as opposed to being on the power
      * button or behind/rear of the phone.
      */
     val isSensorUnderDisplay =
         fingerprintPropertyRepository.sensorType.map(FingerprintSensorType::isUdfps)
-
-    /** Whether fingerprint authentication is currently allowed while on the bouncer. */
-    val isFingerprintCurrentlyAllowedOnBouncer =
-        isSensorUnderDisplay.flatMapLatest { sensorBelowDisplay ->
-            if (sensorBelowDisplay) {
-                flowOf(false)
-            } else {
-                isFingerprintAuthCurrentlyAllowed
-            }
-        }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/doze/AlwaysOnDisplayPolicy.java b/packages/SystemUI/src/com/android/systemui/doze/AlwaysOnDisplayPolicy.java
index e182d0b..f80e0be 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/AlwaysOnDisplayPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/AlwaysOnDisplayPolicy.java
@@ -57,21 +57,29 @@
 
 
     /**
-     * Integer used to dim the screen while dozing.
+     * Integer in the scale [1, 255] used to dim the screen while dozing.
      *
      * @see R.integer.config_screenBrightnessDoze
      */
     public int defaultDozeBrightness;
 
     /**
-     * Integer used to dim the screen just before the screen turns off.
+     * Integer in the scale [1, 255] used to dim the screen just before the screen turns off.
      *
      * @see R.integer.config_screenBrightnessDim
      */
     public int dimBrightness;
 
     /**
-     * Integer array to map ambient brightness type to real screen brightness.
+     * Float in the scale [0, 1] used to dim the screen just before the screen turns off.
+     *
+     * @see R.integer.config_screenBrightnessDimFloat
+     */
+    public float dimBrightnessFloat;
+
+    /**
+     * Integer array to map ambient brightness type to real screen brightness in the integer scale
+     * [1, 255].
      *
      * @see Settings.Global#ALWAYS_ON_DISPLAY_CONSTANTS
      * @see #KEY_SCREEN_BRIGHTNESS_ARRAY
@@ -189,6 +197,8 @@
                         com.android.internal.R.integer.config_screenBrightnessDoze);
                 dimBrightness = resources.getInteger(
                         com.android.internal.R.integer.config_screenBrightnessDim);
+                dimBrightnessFloat = resources.getFloat(
+                        com.android.internal.R.dimen.config_screenBrightnessDimFloat);
                 screenBrightnessArray = mParser.getIntArray(KEY_SCREEN_BRIGHTNESS_ARRAY,
                         resources.getIntArray(
                                 R.array.config_doze_brightness_sensor_to_brightness));
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeBrightnessHostForwarder.java b/packages/SystemUI/src/com/android/systemui/doze/DozeBrightnessHostForwarder.java
index cf0dcad..0b33614 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeBrightnessHostForwarder.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeBrightnessHostForwarder.java
@@ -36,4 +36,10 @@
         super.setDozeScreenBrightness(brightness);
         mHost.setDozeScreenBrightness(brightness);
     }
+
+    @Override
+    public void setDozeScreenBrightnessFloat(float brightness) {
+        super.setDozeScreenBrightnessFloat(brightness);
+        mHost.setDozeScreenBrightnessFloat(brightness);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
index 17b455d..2e7a459 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
@@ -71,11 +71,17 @@
 
     /**
      * Sets the actual display brightness.
-     * @param value from 0 to 255.
+     * @param value from 1 to 255.
      */
     void setDozeScreenBrightness(int value);
 
     /**
+     * Sets the actual display brightness.
+     * @param value from {@link PowerManager#BRIGHTNESS_MIN} to {@link PowerManager#BRIGHTNESS_MAX}.
+     */
+    void setDozeScreenBrightnessFloat(float value);
+
+    /**
      * Fade out screen before switching off the display power mode.
      * @param onDisplayOffCallback Executed when the display is black.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
index 9a9e698..5bfcc97 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
@@ -401,13 +401,22 @@
 
     /**
      * Appends new AOD screen brightness to logs
-     * @param brightness display brightness setting
+     * @param brightness display brightness setting between 1 and 255
      */
     public void traceDozeScreenBrightness(int brightness) {
         mLogger.logDozeScreenBrightness(brightness);
     }
 
     /**
+     * Appends new AOD screen brightness to logs
+     * @param brightness display brightness setting between {@link PowerManager#BRIGHTNESS_MIN} and
+     *                   {@link PowerManager#BRIGHTNESS_MAX}
+     */
+    public void traceDozeScreenBrightnessFloat(float brightness) {
+        mLogger.logDozeScreenBrightnessFloat(brightness);
+    }
+
+    /**
     * Appends new AOD dimming scrim opacity to logs
     * @param scrimOpacity
      */
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt b/packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt
index 9d6693e..a31dbec 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt
@@ -309,7 +309,15 @@
         buffer.log(TAG, INFO, {
             int1 = brightness
         }, {
-            "Doze screen brightness set, brightness=$int1"
+            "Doze screen brightness set (int), brightness=$int1"
+        })
+    }
+
+    fun logDozeScreenBrightnessFloat(brightness: Float) {
+        buffer.log(TAG, INFO, {
+            double1 = brightness.toDouble()
+        }, {
+            "Doze screen brightness set (float), brightness=$double1"
         })
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
index 7f0b16b..8198ef4 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
@@ -507,9 +507,13 @@
         /** Request waking up. */
         void requestWakeUp(@DozeLog.Reason int reason);
 
-        /** Set screen brightness */
+        /** Set screen brightness between 1 and 255 */
         void setDozeScreenBrightness(int brightness);
 
+        /** Set screen brightness between {@link PowerManager#BRIGHTNESS_MIN} and
+         * {@link PowerManager#BRIGHTNESS_MAX} */
+        void setDozeScreenBrightnessFloat(float brightness);
+
         class Delegate implements Service {
             private final Service mDelegate;
             private final Executor mBgExecutor;
@@ -540,6 +544,13 @@
                     mDelegate.setDozeScreenBrightness(brightness);
                 });
             }
+
+            @Override
+            public void setDozeScreenBrightnessFloat(float brightness) {
+                mBgExecutor.execute(() -> {
+                    mDelegate.setDozeScreenBrightnessFloat(brightness);
+                });
+            }
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
index 323ed98..89fce4a 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
@@ -20,6 +20,8 @@
 
 import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_GOING_TO_SLEEP;
 
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -27,6 +29,7 @@
 import android.hardware.SensorEvent;
 import android.hardware.SensorEventListener;
 import android.hardware.SensorManager;
+import android.hardware.display.DisplayManager;
 import android.os.Handler;
 import android.os.PowerManager;
 import android.os.SystemProperties;
@@ -34,6 +37,7 @@
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.util.IndentingPrintWriter;
+import android.view.Display;
 
 import com.android.internal.R;
 import com.android.systemui.doze.dagger.BrightnessSensor;
@@ -46,6 +50,7 @@
 import com.android.systemui.util.settings.SystemSettings;
 
 import java.io.PrintWriter;
+import java.util.Arrays;
 import java.util.Objects;
 import java.util.Optional;
 
@@ -74,6 +79,7 @@
     private final DozeHost mDozeHost;
     private final Handler mHandler;
     private final SensorManager mSensorManager;
+    private final DisplayManager mDisplayManager;
     private final Optional<Sensor>[] mLightSensorOptional; // light sensors to use per posture
     private final WakefulnessLifecycle mWakefulnessLifecycle;
     private final DozeParameters mDozeParameters;
@@ -81,13 +87,17 @@
     private final DozeLog mDozeLog;
     private final SystemSettings mSystemSettings;
     private final int[] mSensorToBrightness;
+    @Nullable
+    private final float[] mSensorToBrightnessFloat;
     private final int[] mSensorToScrimOpacity;
     private final int mScreenBrightnessDim;
+    private final float mScreenBrightnessDimFloat;
 
     @DevicePostureController.DevicePostureInt
     private int mDevicePosture;
     private boolean mRegistered;
     private int mDefaultDozeBrightness;
+    private float mDefaultDozeBrightnessFloat;
     private boolean mPaused = false;
     private boolean mScreenOff = false;
     private int mLastSensorValue = -1;
@@ -102,6 +112,7 @@
     private int mDebugBrightnessBucket = -1;
 
     @Inject
+    @SuppressLint("AndroidFrameworkRequiresPermission")
     public DozeScreenBrightness(
             Context context,
             @WrappedService DozeMachine.Service service,
@@ -113,10 +124,12 @@
             DozeParameters dozeParameters,
             DevicePostureController devicePostureController,
             DozeLog dozeLog,
-            SystemSettings systemSettings) {
+            SystemSettings systemSettings,
+            DisplayManager displayManager) {
         mContext = context;
         mDozeService = service;
         mSensorManager = sensorManager;
+        mDisplayManager = displayManager;
         mLightSensorOptional = lightSensorOptional;
         mDevicePostureController = devicePostureController;
         mDevicePosture = mDevicePostureController.getDevicePosture();
@@ -131,8 +144,13 @@
                 R.dimen.config_screenBrightnessMinimumDimAmountFloat);
 
         mDefaultDozeBrightness = alwaysOnDisplayPolicy.defaultDozeBrightness;
+        mDefaultDozeBrightnessFloat =
+                mDisplayManager.getDefaultDozeBrightness(mContext.getDisplayId());
         mScreenBrightnessDim = alwaysOnDisplayPolicy.dimBrightness;
+        mScreenBrightnessDimFloat = alwaysOnDisplayPolicy.dimBrightnessFloat;
         mSensorToBrightness = alwaysOnDisplayPolicy.screenBrightnessArray;
+        mSensorToBrightnessFloat =
+                mDisplayManager.getDozeBrightnessSensorValueToBrightness(mContext.getDisplayId());
         mSensorToScrimOpacity = alwaysOnDisplayPolicy.dimmingScrimArray;
 
         mDevicePostureController.addCallback(mDevicePostureCallback);
@@ -193,11 +211,22 @@
         if (force || mRegistered || mDebugBrightnessBucket != -1) {
             int sensorValue = mDebugBrightnessBucket == -1
                     ? mLastSensorValue : mDebugBrightnessBucket;
-            int brightness = computeBrightness(sensorValue);
-            boolean brightnessReady = brightness > 0;
-            if (brightnessReady) {
-                mDozeService.setDozeScreenBrightness(
-                        clampToDimBrightnessForScreenOff(clampToUserSetting(brightness)));
+            boolean brightnessReady;
+            if (shouldUseFloatBrightness()) {
+                float brightness = computeBrightnessFloat(sensorValue);
+                brightnessReady = brightness >= 0;
+                if (brightnessReady) {
+                    mDozeService.setDozeScreenBrightnessFloat(
+                            clampToDimBrightnessForScreenOffFloat(
+                                    clampToUserSettingFloat(brightness)));
+                }
+            } else {
+                int brightness = computeBrightness(sensorValue);
+                brightnessReady = brightness > 0;
+                if (brightnessReady) {
+                    mDozeService.setDozeScreenBrightness(
+                            clampToDimBrightnessForScreenOff(clampToUserSetting(brightness)));
+                }
             }
 
             int scrimOpacity = -1;
@@ -249,17 +278,30 @@
         return mSensorToBrightness[sensorValue];
     }
 
+    private float computeBrightnessFloat(int sensorValue) {
+        if (sensorValue < 0 || sensorValue >= mSensorToBrightnessFloat.length) {
+            return -1;
+        }
+        return mSensorToBrightnessFloat[sensorValue];
+    }
+
     @Override
     public void onAccuracyChanged(Sensor sensor, int accuracy) {
     }
 
     private void resetBrightnessToDefault() {
-        mDozeService.setDozeScreenBrightness(
-                clampToDimBrightnessForScreenOff(
-                        clampToUserSetting(mDefaultDozeBrightness)));
+        if (shouldUseFloatBrightness()) {
+            mDozeService.setDozeScreenBrightnessFloat(
+                    clampToDimBrightnessForScreenOffFloat(
+                            clampToUserSettingOrAutoBrightnessFloat(mDefaultDozeBrightnessFloat)));
+        } else {
+            mDozeService.setDozeScreenBrightness(
+                    clampToDimBrightnessForScreenOff(
+                            clampToUserSettingOrAutoBrightness(mDefaultDozeBrightness)));
+        }
         mDozeHost.setAodDimmingScrim(0f);
     }
-    //TODO: brightnessfloat change usages to float.
+
     private int clampToUserSetting(int brightness) {
         int screenBrightnessModeSetting = mSystemSettings.getIntForUser(
                 Settings.System.SCREEN_BRIGHTNESS_MODE,
@@ -268,10 +310,45 @@
             return brightness;
         }
 
-        int userSetting = mSystemSettings.getIntForUser(
+        return Math.min(brightness, getScreenBrightness());
+    }
+
+    @SuppressLint("AndroidFrameworkRequiresPermission")
+    private float clampToUserSettingFloat(float brightness) {
+        int screenBrightnessModeSetting = mSystemSettings.getIntForUser(
+                Settings.System.SCREEN_BRIGHTNESS_MODE,
+                Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL, UserHandle.USER_CURRENT);
+        if (screenBrightnessModeSetting == Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC) {
+            return brightness;
+        }
+
+        return Math.min(brightness, getScreenBrightnessFloat());
+    }
+
+    private int clampToUserSettingOrAutoBrightness(int brightness) {
+        return Math.min(brightness, getScreenBrightness());
+    }
+
+    private float clampToUserSettingOrAutoBrightnessFloat(float brightness) {
+        return Math.min(brightness, getScreenBrightnessFloat());
+    }
+
+    /**
+     * Gets the current screen brightness that may have been set by manually by the user
+     * or by autobrightness.
+     */
+    private int getScreenBrightness() {
+        return mSystemSettings.getIntForUser(
                 Settings.System.SCREEN_BRIGHTNESS, Integer.MAX_VALUE,
                 UserHandle.USER_CURRENT);
-        return Math.min(brightness, userSetting);
+    }
+
+    /**
+     * Gets the current screen brightness that may have been set by manually by the user
+     * or by autobrightness.
+     */
+    private float getScreenBrightnessFloat() {
+        return mDisplayManager.getBrightness(Display.DEFAULT_DISPLAY);
     }
 
     /**
@@ -301,6 +378,31 @@
         }
     }
 
+    /**
+     * Clamp the brightness to the dim brightness value used by PowerManagerService just before the
+     * device times out and goes to sleep, if we are sleeping from a timeout. This ensures that we
+     * don't raise the brightness back to the user setting before or during the screen off
+     * animation.
+     */
+    private float clampToDimBrightnessForScreenOffFloat(float brightness) {
+        final boolean screenTurningOff =
+                (mDozeParameters.shouldClampToDimBrightness()
+                        || mWakefulnessLifecycle.getWakefulness() == WAKEFULNESS_GOING_TO_SLEEP)
+                && mState == DozeMachine.State.INITIALIZED;
+        if (screenTurningOff
+                && mWakefulnessLifecycle.getLastSleepReason() == GO_TO_SLEEP_REASON_TIMEOUT) {
+            return Math.max(
+                    PowerManager.BRIGHTNESS_MIN,
+                    // Use the lower of either the dim brightness, or the current brightness reduced
+                    // by the minimum dim amount. This is the same logic used in
+                    // DisplayPowerController#updatePowerState to apply a minimum dim amount.
+                    Math.min(brightness - mScreenBrightnessMinimumDimAmountFloat,
+                            mScreenBrightnessDimFloat));
+        } else {
+            return brightness;
+        }
+    }
+
     private void setLightSensorEnabled(boolean enabled) {
         if (enabled && !mRegistered && isLightSensorPresent()) {
             // Wait until we get an event from the sensor until indicating ready.
@@ -342,6 +444,20 @@
         idpw.increaseIndent();
         idpw.println("registered=" + mRegistered);
         idpw.println("posture=" + DevicePostureController.devicePostureToString(mDevicePosture));
+        idpw.println("sensorToBrightness=" + Arrays.toString(mSensorToBrightness));
+        idpw.println("sensorToBrightnessFloat=" + Arrays.toString(mSensorToBrightnessFloat));
+        idpw.println("sensorToScrimOpacity=" + Arrays.toString(mSensorToScrimOpacity));
+        idpw.println("screenBrightnessDim=" + mScreenBrightnessDim);
+        idpw.println("screenBrightnessDimFloat=" + mScreenBrightnessDimFloat);
+        idpw.println("mDefaultDozeBrightness=" + mDefaultDozeBrightness);
+        idpw.println("mDefaultDozeBrightnessFloat=" + mDefaultDozeBrightnessFloat);
+        idpw.println("mLastSensorValue=" + mLastSensorValue);
+        idpw.println("shouldUseFloatBrightness()=" + shouldUseFloatBrightness());
+    }
+
+    private boolean shouldUseFloatBrightness() {
+        return com.android.server.display.feature.flags.Flags.dozeBrightnessFloat()
+                && mSensorToBrightnessFloat != null;
     }
 
     private final DevicePostureController.Callback mDevicePostureCallback =
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
index 83fa001..931066d 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
@@ -27,7 +27,9 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.ActivityInfo;
 import android.graphics.drawable.ColorDrawable;
+import android.service.dreams.DreamActivity;
 import android.util.Log;
 import android.view.View;
 import android.view.ViewGroup;
@@ -56,12 +58,14 @@
 import com.android.systemui.ambient.touch.dagger.AmbientTouchComponent;
 import com.android.systemui.ambient.touch.scrim.ScrimManager;
 import com.android.systemui.communal.domain.interactor.CommunalInteractor;
+import com.android.systemui.communal.shared.log.CommunalUiEvent;
 import com.android.systemui.communal.shared.model.CommunalScenes;
 import com.android.systemui.complication.Complication;
 import com.android.systemui.complication.dagger.ComplicationComponent;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dreams.dagger.DreamOverlayComponent;
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
+import com.android.systemui.navigationbar.gestural.domain.GestureInteractor;
 import com.android.systemui.shade.ShadeExpansionChangeEvent;
 import com.android.systemui.touch.TouchInsetManager;
 import com.android.systemui.util.concurrency.DelayableExecutor;
@@ -134,6 +138,8 @@
 
     private final DreamOverlayComponent mDreamOverlayComponent;
 
+    private ComponentName mCurrentBlockedGestureDreamActivityComponent;
+
     /**
      * This {@link LifecycleRegistry} controls when dream overlay functionality, like touch
      * handling, should be active. It will automatically be paused when the dream overlay is hidden
@@ -221,6 +227,8 @@
 
     private final DreamOverlayStateController mStateController;
 
+    private final GestureInteractor mGestureInteractor;
+
     @VisibleForTesting
     public enum DreamOverlayEvent implements UiEventLogger.UiEventEnum {
         @UiEvent(doc = "The dream overlay has entered start.")
@@ -264,6 +272,7 @@
             ComponentName homeControlPanelDreamComponent,
             DreamOverlayCallbackController dreamOverlayCallbackController,
             KeyguardInteractor keyguardInteractor,
+            GestureInteractor gestureInteractor,
             @Named(DREAM_OVERLAY_WINDOW_TITLE) String windowTitle) {
         super(executor);
         mContext = context;
@@ -280,6 +289,7 @@
         mWindowTitle = windowTitle;
         mCommunalInteractor = communalInteractor;
         mSystemDialogsCloser = systemDialogsCloser;
+        mGestureInteractor = gestureInteractor;
 
         final ViewModelStore viewModelStore = new ViewModelStore();
         final Complication.Host host =
@@ -390,6 +400,7 @@
         mStarted = true;
 
         updateRedirectWakeup();
+        updateBlockedGestureDreamActivityComponent();
     }
 
     private void updateRedirectWakeup() {
@@ -400,6 +411,18 @@
         redirectWake(mCommunalAvailable && !glanceableHubAllowKeyguardWhenDreaming());
     }
 
+    private void updateBlockedGestureDreamActivityComponent() {
+        // TODO(b/343815446): We should not be crafting this ActivityInfo ourselves. It should be
+        // in a common place, Such as DreamActivity itself.
+        final ActivityInfo info = new ActivityInfo();
+        info.name = DreamActivity.class.getName();
+        info.packageName = getDreamComponent().getPackageName();
+        mCurrentBlockedGestureDreamActivityComponent = info.getComponentName();
+
+        mGestureInteractor.addGestureBlockedActivity(mCurrentBlockedGestureDreamActivityComponent,
+                GestureInteractor.Scope.Global);
+    }
+
     @Override
     public void onEndDream() {
         resetCurrentDreamOverlayLocked();
@@ -407,6 +430,7 @@
 
     @Override
     public void onWakeRequested() {
+        mUiEventLogger.log(CommunalUiEvent.DREAM_TO_COMMUNAL_HUB_DREAM_AWAKE_START);
         mCommunalInteractor.changeScene(CommunalScenes.Communal, null);
     }
 
@@ -470,6 +494,7 @@
      *                     into the dream window.
      */
     private boolean addOverlayWindowLocked(WindowManager.LayoutParams layoutParams) {
+
         mWindow = new PhoneWindow(mContext);
         // Default to SystemUI name for TalkBack.
         mWindow.setTitle(mWindowTitle);
@@ -552,6 +577,14 @@
         }
 
         mWindow = null;
+
+        // Always unregister the any set DreamActivity from being blocked from gestures.
+        if (mCurrentBlockedGestureDreamActivityComponent != null) {
+            mGestureInteractor.removeGestureBlockedActivity(
+                    mCurrentBlockedGestureDreamActivityComponent, GestureInteractor.Scope.Global);
+            mCurrentBlockedGestureDreamActivityComponent = null;
+        }
+
         mStarted = false;
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/CommunalTouchHandler.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/CommunalTouchHandler.java
index 04fda33..ee7b6f5 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/touch/CommunalTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/CommunalTouchHandler.java
@@ -20,6 +20,7 @@
 
 import android.graphics.Rect;
 import android.graphics.Region;
+import android.util.LayoutDirection;
 import android.view.GestureDetector;
 import android.view.MotionEvent;
 
@@ -27,6 +28,7 @@
 import androidx.lifecycle.Lifecycle;
 
 import com.android.systemui.ambient.touch.TouchHandler;
+import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor;
 import com.android.systemui.communal.domain.interactor.CommunalInteractor;
 import com.android.systemui.dreams.touch.dagger.CommunalTouchModule;
 import com.android.systemui.statusbar.phone.CentralSurfaces;
@@ -43,30 +45,42 @@
     private final Optional<CentralSurfaces> mCentralSurfaces;
     private final Lifecycle mLifecycle;
     private final CommunalInteractor mCommunalInteractor;
+
+    private final ConfigurationInteractor mConfigurationInteractor;
     private Boolean mIsEnabled = false;
 
+    private int mLayoutDirection = LayoutDirection.LTR;
+
     @VisibleForTesting
-    final Consumer<Boolean> mIsCommunalAvailableCallback =
-            isAvailable -> {
-                setIsEnabled(isAvailable);
-            };
+    final Consumer<Boolean> mIsCommunalAvailableCallback = isAvailable -> setIsEnabled(isAvailable);
+
+    @VisibleForTesting
+    final Consumer<Integer> mLayoutDirectionCallback = direction -> mLayoutDirection = direction;
 
     @Inject
     public CommunalTouchHandler(
             Optional<CentralSurfaces> centralSurfaces,
             @Named(CommunalTouchModule.COMMUNAL_GESTURE_INITIATION_WIDTH) int initiationWidth,
             CommunalInteractor communalInteractor,
+            ConfigurationInteractor configurationInteractor,
             Lifecycle lifecycle) {
         mInitiationWidth = initiationWidth;
         mCentralSurfaces = centralSurfaces;
         mLifecycle = lifecycle;
         mCommunalInteractor = communalInteractor;
+        mConfigurationInteractor = configurationInteractor;
 
         collectFlow(
                 mLifecycle,
                 mCommunalInteractor.isCommunalAvailable(),
                 mIsCommunalAvailableCallback
         );
+
+        collectFlow(
+                mLifecycle,
+                mConfigurationInteractor.getLayoutDirection(),
+                mLayoutDirectionCallback
+        );
     }
 
     @Override
@@ -90,7 +104,15 @@
     @Override
     public void getTouchInitiationRegion(Rect bounds, Region region, Rect exclusionRect) {
         final Rect outBounds = new Rect(bounds);
-        outBounds.inset(outBounds.width() - mInitiationWidth, 0, 0, 0);
+        final int inset = outBounds.width() - mInitiationWidth;
+
+        // Touch initiation area is defined in terms of LTR. The insets must be flipped for RTL
+        if (mLayoutDirection == LayoutDirection.LTR) {
+            outBounds.inset(inset, 0, 0, 0);
+        } else {
+            outBounds.inset(0, 0, inset, 0);
+        }
+
         region.op(outBounds, Region.Op.UNION);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/education/dagger/ContextualEducationModule.kt b/packages/SystemUI/src/com/android/systemui/education/dagger/ContextualEducationModule.kt
index 0e2e2e6..532b123 100644
--- a/packages/SystemUI/src/com/android/systemui/education/dagger/ContextualEducationModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/education/dagger/ContextualEducationModule.kt
@@ -19,12 +19,13 @@
 import com.android.systemui.CoreStartable
 import com.android.systemui.Flags
 import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.contextualeducation.GestureType
 import com.android.systemui.education.data.repository.ContextualEducationRepository
 import com.android.systemui.education.data.repository.ContextualEducationRepositoryImpl
 import com.android.systemui.education.domain.interactor.ContextualEducationInteractor
+import com.android.systemui.education.domain.interactor.KeyboardTouchpadEduInteractor
 import com.android.systemui.education.domain.interactor.KeyboardTouchpadEduStatsInteractor
 import com.android.systemui.education.domain.interactor.KeyboardTouchpadEduStatsInteractorImpl
-import com.android.systemui.shared.education.GestureType
 import dagger.Binds
 import dagger.Lazy
 import dagger.Module
@@ -73,7 +74,7 @@
                 implLazy.get()
             } else {
                 // No-op implementation when the flag is disabled.
-                return NoOpCoreStartable
+                return NoOpContextualEducationInteractor
             }
         }
 
@@ -88,6 +89,18 @@
                 return NoOpKeyboardTouchpadEduStatsInteractor
             }
         }
+
+        @Provides
+        fun provideKeyboardTouchpadEduInteractor(
+            implLazy: Lazy<KeyboardTouchpadEduInteractor>
+        ): CoreStartable {
+            return if (Flags.keyboardTouchpadContextualEducation()) {
+                implLazy.get()
+            } else {
+                // No-op implementation when the flag is disabled.
+                return NoOpKeyboardTouchpadEduInteractor
+            }
+        }
     }
 
     private object NoOpKeyboardTouchpadEduStatsInteractor : KeyboardTouchpadEduStatsInteractor {
@@ -96,7 +109,11 @@
         override fun updateShortcutTriggerTime(gestureType: GestureType) {}
     }
 
-    private object NoOpCoreStartable : CoreStartable {
+    private object NoOpContextualEducationInteractor : CoreStartable {
+        override fun start() {}
+    }
+
+    private object NoOpKeyboardTouchpadEduInteractor : CoreStartable {
         override fun start() {}
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/education/data/repository/ContextualEducationRepository.kt b/packages/SystemUI/src/com/android/systemui/education/data/repository/ContextualEducationRepository.kt
index 248b7a5..52ccba4 100644
--- a/packages/SystemUI/src/com/android/systemui/education/data/repository/ContextualEducationRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/education/data/repository/ContextualEducationRepository.kt
@@ -17,9 +17,9 @@
 package com.android.systemui.education.data.repository
 
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.contextualeducation.GestureType
 import com.android.systemui.education.dagger.ContextualEducationModule.EduClock
 import com.android.systemui.education.data.model.GestureEduModel
-import com.android.systemui.shared.education.GestureType
 import java.time.Clock
 import javax.inject.Inject
 import kotlinx.coroutines.flow.Flow
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 b7fc773..4b37b29 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
@@ -27,9 +27,9 @@
 import androidx.datastore.preferences.preferencesDataStoreFile
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.contextualeducation.GestureType
 import com.android.systemui.education.dagger.ContextualEducationModule.EduDataStoreScope
 import com.android.systemui.education.data.model.GestureEduModel
-import com.android.systemui.shared.education.GestureType
 import java.time.Instant
 import javax.inject.Inject
 import javax.inject.Provider
diff --git a/packages/SystemUI/src/com/android/systemui/education/domain/interactor/ContextualEducationInteractor.kt b/packages/SystemUI/src/com/android/systemui/education/domain/interactor/ContextualEducationInteractor.kt
index e2aa911..bee289d 100644
--- a/packages/SystemUI/src/com/android/systemui/education/domain/interactor/ContextualEducationInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/education/domain/interactor/ContextualEducationInteractor.kt
@@ -19,12 +19,18 @@
 import com.android.systemui.CoreStartable
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.contextualeducation.GestureType
+import com.android.systemui.contextualeducation.GestureType.BACK
+import com.android.systemui.education.data.model.GestureEduModel
 import com.android.systemui.education.data.repository.ContextualEducationRepository
-import com.android.systemui.shared.education.GestureType
 import com.android.systemui.user.domain.interactor.SelectedUserInteractor
 import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.collectLatest
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flowOn
 import kotlinx.coroutines.launch
 
 /**
@@ -36,16 +42,28 @@
 @Inject
 constructor(
     @Background private val backgroundScope: CoroutineScope,
+    @Background private val backgroundDispatcher: CoroutineDispatcher,
     private val selectedUserInteractor: SelectedUserInteractor,
     private val repository: ContextualEducationRepository,
 ) : CoreStartable {
 
+    val backGestureModelFlow = readEduModelsOnSignalCountChanged(BACK)
+
     override fun start() {
         backgroundScope.launch {
             selectedUserInteractor.selectedUser.collectLatest { repository.setUser(it) }
         }
     }
 
+    private fun readEduModelsOnSignalCountChanged(gestureType: GestureType): Flow<GestureEduModel> {
+        return repository
+            .readGestureEduModelFlow(gestureType)
+            .distinctUntilChanged(
+                areEquivalent = { old, new -> old.signalCount == new.signalCount }
+            )
+            .flowOn(backgroundDispatcher)
+    }
+
     suspend fun incrementSignalCount(gestureType: GestureType) =
         repository.incrementSignalCount(gestureType)
 
diff --git a/packages/SystemUI/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractor.kt b/packages/SystemUI/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractor.kt
new file mode 100644
index 0000000..9016c73
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractor.kt
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.education.domain.interactor
+
+import com.android.systemui.CoreStartable
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.contextualeducation.GestureType.BACK
+import com.android.systemui.education.data.model.GestureEduModel
+import com.android.systemui.education.shared.model.EducationInfo
+import com.android.systemui.education.shared.model.EducationUiType
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.mapNotNull
+import kotlinx.coroutines.launch
+
+/** Allow listening to new contextual education triggered */
+@SysUISingleton
+class KeyboardTouchpadEduInteractor
+@Inject
+constructor(
+    @Background private val backgroundScope: CoroutineScope,
+    private val contextualEducationInteractor: ContextualEducationInteractor
+) : CoreStartable {
+
+    companion object {
+        const val MAX_SIGNAL_COUNT: Int = 2
+    }
+
+    private val _educationTriggered = MutableStateFlow<EducationInfo?>(null)
+    val educationTriggered = _educationTriggered.asStateFlow()
+
+    override fun start() {
+        backgroundScope.launch {
+            contextualEducationInteractor.backGestureModelFlow
+                .mapNotNull { getEduType(it) }
+                .collect { _educationTriggered.value = EducationInfo(BACK, it) }
+        }
+    }
+
+    private fun getEduType(model: GestureEduModel): EducationUiType? {
+        if (isEducationNeeded(model)) {
+            return EducationUiType.Toast
+        } else {
+            return null
+        }
+    }
+
+    private fun isEducationNeeded(model: GestureEduModel): Boolean {
+        // Todo: b/354884305 - add complete education logic to show education in correct scenarios
+        val shortcutWasTriggered = model.lastShortcutTriggeredTime == null
+        val signalCountReached = model.signalCount >= MAX_SIGNAL_COUNT
+
+        return shortcutWasTriggered && signalCountReached
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduStatsInteractor.kt b/packages/SystemUI/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduStatsInteractor.kt
index 643e571..3223433 100644
--- a/packages/SystemUI/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduStatsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduStatsInteractor.kt
@@ -18,7 +18,7 @@
 
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.shared.education.GestureType
+import com.android.systemui.contextualeducation.GestureType
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.launch
diff --git a/packages/SystemUI/src/com/android/systemui/education/shared/model/EducationInfo.kt b/packages/SystemUI/src/com/android/systemui/education/shared/model/EducationInfo.kt
new file mode 100644
index 0000000..d92fb9b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/education/shared/model/EducationInfo.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.education.shared.model
+
+import com.android.systemui.contextualeducation.GestureType
+
+/**
+ * Model for education triggered. [gestureType] indicates what gesture it is trying to educate about
+ * and [educationUiType] is how we educate user in the UI
+ */
+data class EducationInfo(val gestureType: GestureType, val educationUiType: EducationUiType)
+
+enum class EducationUiType {
+    Toast,
+    Notification,
+}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsClassicDebug.java b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsClassicDebug.java
index 7d11d32..303f916 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsClassicDebug.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsClassicDebug.java
@@ -17,6 +17,7 @@
 package com.android.systemui.flags;
 
 import static com.android.systemui.Flags.exampleFlag;
+import static com.android.systemui.Flags.classicFlagsMultiUser;
 import static com.android.systemui.Flags.sysuiTeamfood;
 import static com.android.systemui.flags.FlagManager.ACTION_GET_FLAGS;
 import static com.android.systemui.flags.FlagManager.ACTION_SET_FLAG;
@@ -41,6 +42,7 @@
 
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.settings.UserContextProvider;
 import com.android.systemui.util.settings.GlobalSettings;
 
 import java.io.PrintWriter;
@@ -125,9 +127,14 @@
             @Main Resources resources,
             ServerFlagReader serverFlagReader,
             @Named(ALL_FLAGS) Map<String, Flag<?>> allFlags,
-            Restarter restarter) {
+            Restarter restarter,
+            UserContextProvider userContextProvider) {
         mFlagManager = flagManager;
-        mContext = context;
+        if (classicFlagsMultiUser()) {
+            mContext = userContextProvider.createCurrentUserContext(context);
+        } else {
+            mContext = context;
+        }
         mGlobalSettings = globalSettings;
         mResources = resources;
         mSystemProperties = systemProperties;
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt b/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt
index 0e06117..59de203 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt
@@ -23,7 +23,13 @@
 import com.android.server.notification.Flags.politeNotifications
 import com.android.server.notification.Flags.vibrateWhileUnlocked
 import com.android.systemui.Flags.FLAG_COMMUNAL_HUB
+import com.android.systemui.Flags.FLAG_STATUS_BAR_CALL_CHIP_NOTIFICATION_ICON
+import com.android.systemui.Flags.FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS
+import com.android.systemui.Flags.FLAG_STATUS_BAR_USE_REPOS_FOR_CALL_CHIP
 import com.android.systemui.Flags.communalHub
+import com.android.systemui.Flags.statusBarCallChipNotificationIcon
+import com.android.systemui.Flags.statusBarScreenSharingChips
+import com.android.systemui.Flags.statusBarUseReposForCallChip
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.KeyguardBottomAreaRefactor
 import com.android.systemui.keyguard.MigrateClocksToBlueprint
@@ -38,6 +44,7 @@
 import com.android.systemui.statusbar.notification.shared.NotificationAvalancheSuppression
 import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor
 import com.android.systemui.statusbar.notification.shared.NotificationMinimalismPrototype
+import com.android.systemui.statusbar.notification.shared.NotificationThrottleHun
 import com.android.systemui.statusbar.notification.shared.NotificationsHeadsUpRefactor
 import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor
 import com.android.systemui.statusbar.notification.shared.PriorityPeopleSection
@@ -51,6 +58,7 @@
         // Internal notification backend dependencies
         crossAppPoliteNotifications dependsOn politeNotifications
         vibrateWhileUnlockedToken dependsOn politeNotifications
+        modesUi dependsOn modesApi
 
         // Internal notification frontend dependencies
         NotificationsLiveDataStoreRefactor.token dependsOn NotificationIconContainerRefactor.token
@@ -58,6 +66,7 @@
         NotificationAvalancheSuppression.token dependsOn VisualInterruptionRefactor.token
         PriorityPeopleSection.token dependsOn SortBySectionTimeFlag.token
         NotificationMinimalismPrototype.token dependsOn NotificationsHeadsUpRefactor.token
+        NotificationsHeadsUpRefactor.token dependsOn NotificationThrottleHun.token
 
         // SceneContainer dependencies
         SceneContainerFlag.getFlagDependencies().forEach { (alpha, beta) -> alpha dependsOn beta }
@@ -74,6 +83,10 @@
 
         // QS Fragment using Compose dependencies
         QSComposeFragment.token dependsOn NewQsUI.token
+
+        // Status bar chip dependencies
+        statusBarCallChipNotificationIconToken dependsOn statusBarUseReposForCallChipToken
+        statusBarCallChipNotificationIconToken dependsOn statusBarScreenSharingChipsToken
     }
 
     private inline val politeNotifications
@@ -85,6 +98,25 @@
     private inline val vibrateWhileUnlockedToken: FlagToken
         get() = FlagToken(FLAG_VIBRATE_WHILE_UNLOCKED, vibrateWhileUnlocked())
 
+    private inline val modesUi
+        get() = FlagToken(android.app.Flags.FLAG_MODES_UI, android.app.Flags.modesUi())
+
+    private inline val modesApi
+        get() = FlagToken(android.app.Flags.FLAG_MODES_API, android.app.Flags.modesApi())
+
     private inline val communalHub
         get() = FlagToken(FLAG_COMMUNAL_HUB, communalHub())
+
+    private inline val statusBarCallChipNotificationIconToken
+        get() =
+            FlagToken(
+                FLAG_STATUS_BAR_CALL_CHIP_NOTIFICATION_ICON,
+                statusBarCallChipNotificationIcon()
+            )
+
+    private inline val statusBarScreenSharingChipsToken
+        get() = FlagToken(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, statusBarScreenSharingChips())
+
+    private inline val statusBarUseReposForCallChipToken
+        get() = FlagToken(FLAG_STATUS_BAR_USE_REPOS_FOR_CALL_CHIP, statusBarUseReposForCallChip())
 }
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index d0beb7a..8990505 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -292,15 +292,6 @@
     val WM_ENABLE_SHELL_TRANSITIONS =
         sysPropBooleanFlag("persist.wm.debug.shell_transit", default = true)
 
-    // TODO(b/254513207): Tracking Bug
-    @Keep
-    @JvmField
-    val WM_ENABLE_PARTIAL_SCREEN_SHARING =
-        releasedFlag(
-            name = "enable_record_task_content",
-            namespace = DeviceConfig.NAMESPACE_WINDOW_MANAGER,
-        )
-
     // TODO(b/256873975): Tracking Bug
     @JvmField
     @Keep
diff --git a/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffect.kt b/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffect.kt
index c44eb47..4652b2a 100644
--- a/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffect.kt
+++ b/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffect.kt
@@ -16,11 +16,19 @@
 
 package com.android.systemui.haptics.qs
 
+import android.content.ComponentName
 import android.os.VibrationEffect
 import android.service.quicksettings.Tile
+import android.view.View
 import androidx.annotation.VisibleForTesting
+import com.android.systemui.animation.ActivityTransitionAnimator
+import com.android.systemui.animation.DelegateTransitionAnimatorController
+import com.android.systemui.animation.DialogCuj
+import com.android.systemui.animation.DialogTransitionAnimator
 import com.android.systemui.animation.Expandable
-import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.core.LogLevel
+import com.android.systemui.log.dagger.QSLog
 import com.android.systemui.plugins.qs.QSTile
 import com.android.systemui.statusbar.VibratorHelper
 import com.android.systemui.statusbar.policy.KeyguardStateController
@@ -42,7 +50,7 @@
 constructor(
     private val vibratorHelper: VibratorHelper?,
     private val keyguardStateController: KeyguardStateController,
-    private val falsingManager: FalsingManager,
+    @QSLog private val logBuffer: LogBuffer,
 ) {
 
     var effectDuration = 0
@@ -58,6 +66,7 @@
     /** The [QSTile] and [Expandable] used to perform a long-click and click actions */
     var qsTile: QSTile? = null
     var expandable: Expandable? = null
+        private set
 
     /** Haptic effects */
     private val durations =
@@ -96,6 +105,7 @@
     }
 
     fun handleActionDown() {
+        logEvent(qsTile?.tileSpec, state, "action down received")
         when (state) {
             State.IDLE -> {
                 setState(State.TIMEOUT_WAIT)
@@ -107,6 +117,7 @@
     }
 
     fun handleActionUp() {
+        logEvent(qsTile?.tileSpec, state, "action up received")
         if (state == State.RUNNING_FORWARD) {
             setState(State.RUNNING_BACKWARDS_FROM_UP)
             callback?.onReverseAnimator()
@@ -125,12 +136,16 @@
     }
 
     fun handleAnimationStart() {
-        vibrate(longPressHint)
-        setState(State.RUNNING_FORWARD)
+        logEvent(qsTile?.tileSpec, state, "animation started")
+        if (state == State.TIMEOUT_WAIT) {
+            vibrate(longPressHint)
+            setState(State.RUNNING_FORWARD)
+        }
     }
 
     /** This function is called both when an animator completes or gets cancelled */
     fun handleAnimationComplete() {
+        logEvent(qsTile?.tileSpec, state, "animation completed")
         when (state) {
             State.RUNNING_FORWARD -> {
                 vibrate(snapEffect)
@@ -140,14 +155,19 @@
                     callback?.onResetProperties()
                     setState(State.IDLE)
                 }
+                logEvent(qsTile?.tileSpec, state, "long click action triggered")
                 qsTile?.longClick(expandable)
             }
             State.RUNNING_BACKWARDS_FROM_UP -> {
                 callback?.onEffectFinishedReversing()
                 setState(getStateForClick())
+                logEvent(qsTile?.tileSpec, state, "click action triggered")
                 qsTile?.click(expandable)
             }
-            State.RUNNING_BACKWARDS_FROM_CANCEL -> setState(State.IDLE)
+            State.RUNNING_BACKWARDS_FROM_CANCEL -> {
+                callback?.onEffectFinishedReversing()
+                setState(State.IDLE)
+            }
             else -> {}
         }
     }
@@ -169,6 +189,7 @@
         if (keyguardStateController.isPrimaryBouncerShowing || !isStateClickable) return false
 
         setState(getStateForClick())
+        logEvent(qsTile?.tileSpec, state, "click action triggered")
         qsTile?.click(expandable)
         return true
     }
@@ -183,11 +204,8 @@
     @VisibleForTesting
     fun getStateForClick(): State {
         val isTileUnavailable = qsTile?.state?.state == Tile.STATE_UNAVAILABLE
-        val isFalseTapWhileLocked =
-            !keyguardStateController.isUnlocked &&
-                falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)
         val handlesLongClick = qsTile?.state?.handlesLongClick == true
-        return if (isTileUnavailable || isFalseTapWhileLocked || !handlesLongClick) {
+        return if (isTileUnavailable || !handlesLongClick || keyguardStateController.isShowing) {
             // The click event will not perform an action that resets the state. Therefore, this is
             // the last opportunity to reset the state back to IDLE.
             State.IDLE
@@ -222,13 +240,72 @@
 
     fun resetState() = setState(State.IDLE)
 
+    fun createExpandableFromView(view: View) {
+        expandable =
+            object : Expandable {
+                override fun activityTransitionController(
+                    launchCujType: Int?,
+                    cookie: ActivityTransitionAnimator.TransitionCookie?,
+                    component: ComponentName?,
+                    returnCujType: Int?,
+                ): ActivityTransitionAnimator.Controller? {
+                    val delegatedController =
+                        ActivityTransitionAnimator.Controller.fromView(
+                            view,
+                            launchCujType,
+                            cookie,
+                            component,
+                            returnCujType,
+                        )
+                    return delegatedController?.let { createTransitionControllerDelegate(it) }
+                }
+
+                override fun dialogTransitionController(
+                    cuj: DialogCuj?,
+                ): DialogTransitionAnimator.Controller? =
+                    DialogTransitionAnimator.Controller.fromView(view, cuj)
+            }
+    }
+
+    @VisibleForTesting
+    fun createTransitionControllerDelegate(
+        controller: ActivityTransitionAnimator.Controller
+    ): DelegateTransitionAnimatorController {
+        val delegated =
+            object : DelegateTransitionAnimatorController(controller) {
+                override fun onTransitionAnimationCancelled(newKeyguardOccludedState: Boolean?) {
+                    if (state == State.LONG_CLICKED) {
+                        setState(State.RUNNING_BACKWARDS_FROM_CANCEL)
+                        callback?.onReverseAnimator(false)
+                    }
+                    delegate.onTransitionAnimationCancelled(newKeyguardOccludedState)
+                }
+            }
+        return delegated
+    }
+
+    private fun logEvent(tileSpec: String?, state: State, event: String) {
+        if (!DEBUG) return
+        logBuffer.log(
+            TAG,
+            LogLevel.DEBUG,
+            {
+                str1 = tileSpec
+                str2 = event
+                str3 = state.name
+            },
+            { "[long-press effect on $str1 tile] $str2 on state: $str3" }
+        )
+    }
+
     enum class State {
         IDLE, /* The effect is idle waiting for touch input */
         TIMEOUT_WAIT, /* The effect is waiting for a tap timeout period */
         RUNNING_FORWARD, /* The effect is running normally */
         /* The effect was interrupted by an ACTION_UP and is now running backwards */
         RUNNING_BACKWARDS_FROM_UP,
-        /* The effect was interrupted by an ACTION_CANCEL and is now running backwards */
+        /* The effect was cancelled by an ACTION_CANCEL or a shade collapse and is now running
+        backwards */
         RUNNING_BACKWARDS_FROM_CANCEL,
         CLICKED, /* The effect has ended with a click */
         LONG_CLICKED, /* The effect has ended with a long-click */
@@ -247,9 +324,14 @@
         fun onStartAnimator()
 
         /** Reverse the effect animator */
-        fun onReverseAnimator()
+        fun onReverseAnimator(playHaptics: Boolean = true)
 
         /** Cancel the effect animator */
         fun onCancelAnimator()
     }
+
+    companion object {
+        private const val TAG = "QSLongPressEffect"
+        private const val DEBUG = true
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/data/repository/InputDeviceRepository.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/data/repository/InputDeviceRepository.kt
index 3b161b6..5a008bd 100644
--- a/packages/SystemUI/src/com/android/systemui/inputdevice/data/repository/InputDeviceRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/inputdevice/data/repository/InputDeviceRepository.kt
@@ -45,7 +45,7 @@
 
     data class DeviceAdded(val deviceId: Int) : DeviceChange
 
-    data object DeviceRemoved : DeviceChange
+    data class DeviceRemoved(val deviceId: Int) : DeviceChange
 
     data object FreshStart : DeviceChange
 
@@ -72,7 +72,7 @@
 
                         override fun onInputDeviceRemoved(deviceId: Int) {
                             connectedDevices = connectedDevices - deviceId
-                            sendWithLogging(connectedDevices to DeviceRemoved)
+                            sendWithLogging(connectedDevices to DeviceRemoved(deviceId))
                         }
                     }
                 sendWithLogging(connectedDevices to FreshStart)
diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/oobe/KeyboardTouchpadOobeTutorialCoreStartable.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/oobe/KeyboardTouchpadOobeTutorialCoreStartable.kt
new file mode 100644
index 0000000..701d3da
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/inputdevice/oobe/KeyboardTouchpadOobeTutorialCoreStartable.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.inputdevice.oobe
+
+import com.android.systemui.CoreStartable
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.inputdevice.oobe.domain.interactor.OobeSchedulerInteractor
+import com.android.systemui.shared.Flags.newTouchpadGesturesTutorial
+import dagger.Lazy
+import javax.inject.Inject
+
+/** A [CoreStartable] to launch a scheduler for keyboard and touchpad OOBE education */
+@SysUISingleton
+class KeyboardTouchpadOobeTutorialCoreStartable
+@Inject
+constructor(private val oobeSchedulerInteractor: Lazy<OobeSchedulerInteractor>) : CoreStartable {
+    override fun start() {
+        if (newTouchpadGesturesTutorial()) {
+            oobeSchedulerInteractor.get().start()
+        }
+    }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/PartitionedGridLayoutKosmos.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/oobe/data/model/OobeSchedulerInfo.kt
similarity index 61%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/PartitionedGridLayoutKosmos.kt
copy to packages/SystemUI/src/com/android/systemui/inputdevice/oobe/data/model/OobeSchedulerInfo.kt
index 37c9552..e5aedc0 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/PartitionedGridLayoutKosmos.kt
+++ b/packages/SystemUI/src/com/android/systemui/inputdevice/oobe/data/model/OobeSchedulerInfo.kt
@@ -14,11 +14,14 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.panels.domain.interactor
+package com.android.systemui.inputdevice.oobe.data.model
 
-import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.qs.panels.ui.compose.PartitionedGridLayout
-import com.android.systemui.qs.panels.ui.viewmodel.partitionedGridViewModel
+data class OobeSchedulerInfo(
+    val keyboard: DeviceSchedulerInfo = DeviceSchedulerInfo(),
+    val touchpad: DeviceSchedulerInfo = DeviceSchedulerInfo()
+)
 
-val Kosmos.partitionedGridLayout by
-    Kosmos.Fixture { PartitionedGridLayout(partitionedGridViewModel) }
+data class DeviceSchedulerInfo(var isLaunched: Boolean = false, var connectionTime: Long? = null) {
+    val wasEverConnected: Boolean
+        get() = connectionTime != null
+}
diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/oobe/domain/interactor/OobeSchedulerInteractor.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/oobe/domain/interactor/OobeSchedulerInteractor.kt
new file mode 100644
index 0000000..b014c08
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/inputdevice/oobe/domain/interactor/OobeSchedulerInteractor.kt
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.inputdevice.oobe.domain.interactor
+
+import android.content.Context
+import android.content.Intent
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.inputdevice.oobe.data.model.DeviceSchedulerInfo
+import com.android.systemui.inputdevice.oobe.data.model.OobeSchedulerInfo
+import com.android.systemui.keyboard.data.repository.KeyboardRepository
+import com.android.systemui.touchpad.data.repository.TouchpadRepository
+import java.time.Duration
+import java.time.Instant
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.launch
+
+/**
+ * When the first time a keyboard or touchpad id connected, wait for [LAUNCH_DELAY], then launch the
+ * tutorial as soon as there's a connected device
+ */
+@SysUISingleton
+class OobeSchedulerInteractor
+@Inject
+constructor(
+    @Application private val context: Context,
+    @Application private val applicationScope: CoroutineScope,
+    private val keyboardRepository: KeyboardRepository,
+    private val touchpadRepository: TouchpadRepository
+) {
+    private val info = OobeSchedulerInfo()
+
+    fun start() {
+        if (!info.keyboard.isLaunched) {
+            applicationScope.launch {
+                schedule(keyboardRepository.isAnyKeyboardConnected, info.keyboard)
+            }
+        }
+        if (!info.touchpad.isLaunched) {
+            applicationScope.launch {
+                schedule(touchpadRepository.isAnyTouchpadConnected, info.touchpad)
+            }
+        }
+    }
+
+    private suspend fun schedule(isAnyDeviceConnected: Flow<Boolean>, info: DeviceSchedulerInfo) {
+        if (!info.wasEverConnected) {
+            waitForDeviceConnection(isAnyDeviceConnected)
+            info.connectionTime = Instant.now().toEpochMilli()
+        }
+        delay(remainingTimeMillis(info.connectionTime!!))
+        waitForDeviceConnection(isAnyDeviceConnected)
+        info.isLaunched = true
+        launchOobe()
+    }
+
+    private suspend fun waitForDeviceConnection(isAnyDeviceConnected: Flow<Boolean>): Boolean {
+        return isAnyDeviceConnected.filter { it }.first()
+    }
+
+    private fun launchOobe() {
+        val intent = Intent(TUTORIAL_ACTION)
+        intent.addCategory(Intent.CATEGORY_DEFAULT)
+        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+        context.startActivity(intent)
+    }
+
+    private fun remainingTimeMillis(start: Long): Long {
+        val elapsed = Instant.now().toEpochMilli() - start
+        return LAUNCH_DELAY - elapsed
+    }
+
+    companion object {
+        const val TAG = "OobeSchedulerInteractor"
+        const val TUTORIAL_ACTION = "com.android.systemui.action.TOUCHPAD_TUTORIAL"
+        private val LAUNCH_DELAY = Duration.ofHours(72).toMillis()
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/inputmethod/data/model/InputMethodModel.kt b/packages/SystemUI/src/com/android/systemui/inputmethod/data/model/InputMethodModel.kt
index bdc18b3..0e19d87 100644
--- a/packages/SystemUI/src/com/android/systemui/inputmethod/data/model/InputMethodModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/inputmethod/data/model/InputMethodModel.kt
@@ -22,6 +22,8 @@
  * @see android.view.inputmethod.InputMethodInfo
  */
 data class InputMethodModel(
+    /** A unique ID for the user associated with this input method. */
+    val userId: Int,
     /** A unique ID for this input method. */
     val imeId: String,
     /** The subtypes of this IME (may be empty). */
diff --git a/packages/SystemUI/src/com/android/systemui/inputmethod/data/repository/InputMethodRepository.kt b/packages/SystemUI/src/com/android/systemui/inputmethod/data/repository/InputMethodRepository.kt
index 5f316c4..c6fdc32 100644
--- a/packages/SystemUI/src/com/android/systemui/inputmethod/data/repository/InputMethodRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/inputmethod/data/repository/InputMethodRepository.kt
@@ -18,7 +18,6 @@
 
 import android.annotation.SuppressLint
 import android.os.UserHandle
-import android.view.inputmethod.InputMethodInfo
 import android.view.inputmethod.InputMethodManager
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
@@ -34,18 +33,27 @@
 
 /** Provides access to input-method related application state in the bouncer. */
 interface InputMethodRepository {
+
     /**
      * Creates and returns a new `Flow` of installed input methods that are enabled for the
      * specified user.
      *
+     * @param user The user to query.
      * @param fetchSubtypes Whether to fetch the IME Subtypes as well (requires an additional IPC
      *   call for each IME, avoid if not needed).
      * @see InputMethodManager.getEnabledInputMethodListAsUser
      */
-    suspend fun enabledInputMethods(userId: Int, fetchSubtypes: Boolean): Flow<InputMethodModel>
+    suspend fun enabledInputMethods(
+        user: UserHandle,
+        fetchSubtypes: Boolean,
+    ): Flow<InputMethodModel>
 
-    /** Returns enabled subtypes for the currently selected input method. */
-    suspend fun selectedInputMethodSubtypes(): List<InputMethodModel.Subtype>
+    /**
+     * Returns enabled subtypes for the currently selected input method.
+     *
+     * @param user The user to query.
+     */
+    suspend fun selectedInputMethodSubtypes(user: UserHandle): List<InputMethodModel.Subtype>
 
     /**
      * Shows the system's input method picker dialog.
@@ -67,20 +75,22 @@
 ) : InputMethodRepository {
 
     override suspend fun enabledInputMethods(
-        userId: Int,
+        user: UserHandle,
         fetchSubtypes: Boolean
     ): Flow<InputMethodModel> {
         return withContext(backgroundDispatcher) {
-                inputMethodManager.getEnabledInputMethodListAsUser(UserHandle.of(userId))
+                inputMethodManager.getEnabledInputMethodListAsUser(user)
             }
             .asFlow()
             .map { inputMethodInfo ->
                 InputMethodModel(
+                    userId = user.identifier,
                     imeId = inputMethodInfo.id,
                     subtypes =
                         if (fetchSubtypes) {
                             enabledInputMethodSubtypes(
-                                inputMethodInfo,
+                                user = user,
+                                imeId = inputMethodInfo.id,
                                 allowsImplicitlyEnabledSubtypes = true
                             )
                         } else {
@@ -90,11 +100,19 @@
             }
     }
 
-    override suspend fun selectedInputMethodSubtypes(): List<InputMethodModel.Subtype> {
-        return enabledInputMethodSubtypes(
-            inputMethodInfo = null, // Fetch subtypes for the currently-selected IME.
-            allowsImplicitlyEnabledSubtypes = false
-        )
+    override suspend fun selectedInputMethodSubtypes(
+        user: UserHandle,
+    ): List<InputMethodModel.Subtype> {
+        val selectedIme = inputMethodManager.getCurrentInputMethodInfoAsUser(user)
+        return if (selectedIme == null) {
+            emptyList()
+        } else {
+            enabledInputMethodSubtypes(
+                user = user,
+                imeId = selectedIme.id,
+                allowsImplicitlyEnabledSubtypes = false
+            )
+        }
     }
 
     @SuppressLint("MissingPermission")
@@ -107,21 +125,23 @@
     /**
      * Returns a list of enabled input method subtypes for the specified input method info.
      *
-     * @param inputMethodInfo The [InputMethodInfo] whose subtypes list will be returned. If `null`,
-     *   returns enabled subtypes for the currently selected [InputMethodInfo].
+     * @param user The user to query.
+     * @param imeId The ID of the input method whose subtypes list will be returned.
      * @param allowsImplicitlyEnabledSubtypes Whether to allow to return the implicitly enabled
      *   subtypes. If an input method info doesn't have enabled subtypes, the framework will
      *   implicitly enable subtypes according to the current system language.
-     * @see InputMethodManager.getEnabledInputMethodSubtypeList
+     * @see InputMethodManager.getEnabledInputMethodSubtypeListAsUser
      */
     private suspend fun enabledInputMethodSubtypes(
-        inputMethodInfo: InputMethodInfo?,
+        user: UserHandle,
+        imeId: String,
         allowsImplicitlyEnabledSubtypes: Boolean
     ): List<InputMethodModel.Subtype> {
         return withContext(backgroundDispatcher) {
-                inputMethodManager.getEnabledInputMethodSubtypeList(
-                    inputMethodInfo,
-                    allowsImplicitlyEnabledSubtypes
+                inputMethodManager.getEnabledInputMethodSubtypeListAsUser(
+                    imeId,
+                    allowsImplicitlyEnabledSubtypes,
+                    user
                 )
             }
             .map {
diff --git a/packages/SystemUI/src/com/android/systemui/inputmethod/domain/interactor/InputMethodInteractor.kt b/packages/SystemUI/src/com/android/systemui/inputmethod/domain/interactor/InputMethodInteractor.kt
index c54aa7f..d3ef178 100644
--- a/packages/SystemUI/src/com/android/systemui/inputmethod/domain/interactor/InputMethodInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/inputmethod/domain/interactor/InputMethodInteractor.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.inputmethod.domain.interactor
 
+import android.os.UserHandle
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.inputmethod.data.repository.InputMethodRepository
 import javax.inject.Inject
@@ -36,14 +37,16 @@
      * Method adapted from `com.android.inputmethod.latin.Utils`.
      */
     suspend fun hasMultipleEnabledImesOrSubtypes(userId: Int): Boolean {
+        val user = UserHandle.of(userId)
         // Count IMEs that either have no subtypes, or have at least one non-auxiliary subtype.
         val matchingInputMethods =
             repository
-                .enabledInputMethods(userId, fetchSubtypes = true)
+                .enabledInputMethods(user, fetchSubtypes = true)
                 .filter { ime -> ime.subtypes.isEmpty() || ime.subtypes.any { !it.isAuxiliary } }
                 .take(2) // Short-circuit if we find at least 2 matching IMEs.
 
-        return matchingInputMethods.count() > 1 || repository.selectedInputMethodSubtypes().size > 1
+        return matchingInputMethods.count() > 1 ||
+            repository.selectedInputMethodSubtypes(user).size > 1
     }
 
     /** Shows the system's input method picker dialog. */
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/data/repository/KeyboardRepository.kt b/packages/SystemUI/src/com/android/systemui/keyboard/data/repository/KeyboardRepository.kt
index 817849c..b654307 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/data/repository/KeyboardRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/data/repository/KeyboardRepository.kt
@@ -41,6 +41,7 @@
 import kotlinx.coroutines.flow.asFlow
 import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.emptyFlow
+import kotlinx.coroutines.flow.filter
 import kotlinx.coroutines.flow.flatMapConcat
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.flowOn
@@ -78,9 +79,15 @@
 ) : KeyboardRepository {
 
     private val keyboardsChange: Flow<Pair<Collection<Int>, DeviceChange>> =
-        inputDeviceRepository.deviceChange.map { (ids, change) ->
-            ids.filter { id -> isPhysicalFullKeyboard(id) } to change
-        }
+        inputDeviceRepository.deviceChange
+            .map { (ids, change) -> ids.filter { id -> isPhysicalFullKeyboard(id) } to change }
+            .filter { (_, change) ->
+                when (change) {
+                    FreshStart -> true
+                    is DeviceAdded -> isPhysicalFullKeyboard(change.deviceId)
+                    is DeviceRemoved -> isPhysicalFullKeyboard(change.deviceId)
+                }
+            }
 
     @FlowPreview
     override val newlyConnectedKeyboard: Flow<Keyboard> =
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/docking/binder/KeyboardDockingIndicationViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyboard/docking/binder/KeyboardDockingIndicationViewBinder.kt
index f649be2..b859cdc 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/docking/binder/KeyboardDockingIndicationViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/docking/binder/KeyboardDockingIndicationViewBinder.kt
@@ -20,6 +20,7 @@
 import android.graphics.Paint
 import android.graphics.PixelFormat
 import android.view.WindowManager
+import com.android.app.viewcapture.ViewCaptureAwareWindowManager
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.keyboard.docking.ui.KeyboardDockingIndicationView
@@ -37,7 +38,7 @@
     context: Context,
     @Application private val applicationScope: CoroutineScope,
     private val viewModel: KeyboardDockingIndicationViewModel,
-    private val windowManager: WindowManager
+    private val windowManager: ViewCaptureAwareWindowManager,
 ) {
 
     private val windowLayoutParams =
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperCategoriesRepository.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperCategoriesRepository.kt
index 495e8f3..85bd0b0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperCategoriesRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperCategoriesRepository.kt
@@ -71,6 +71,35 @@
     stateRepository: ShortcutHelperStateRepository
 ) {
 
+    private val sources =
+        listOf(
+            InternalGroupsSource(
+                source = systemShortcutsSource,
+                isTrusted = true,
+                typeProvider = { System }
+            ),
+            InternalGroupsSource(
+                source = multitaskingShortcutsSource,
+                isTrusted = true,
+                typeProvider = { MultiTasking }
+            ),
+            InternalGroupsSource(
+                source = appCategoriesShortcutsSource,
+                isTrusted = true,
+                typeProvider = { AppCategories }
+            ),
+            InternalGroupsSource(
+                source = inputShortcutsSource,
+                isTrusted = false,
+                typeProvider = { InputMethodEditor }
+            ),
+            InternalGroupsSource(
+                source = currentAppShortcutsSource,
+                isTrusted = false,
+                typeProvider = { groups -> getCurrentAppShortcutCategoryType(groups) }
+            ),
+        )
+
     private val activeInputDevice =
         stateRepository.state.map {
             if (it is Active) {
@@ -82,17 +111,20 @@
 
     val categories: Flow<List<ShortcutCategory>> =
         activeInputDevice
-            .map {
-                if (it == null) {
+            .map { inputDevice ->
+                if (inputDevice == null) {
                     return@map emptyList()
                 }
-                return@map listOfNotNull(
-                    fetchSystemShortcuts(it),
-                    fetchMultiTaskingShortcuts(it),
-                    fetchAppCategoriesShortcuts(it),
-                    fetchImeShortcuts(it),
-                    fetchCurrentAppShortcuts(it),
-                )
+                val groupsFromAllSources = sources.map { it.source.shortcutGroups(inputDevice.id) }
+                val supportedKeyCodes = fetchSupportedKeyCodes(inputDevice.id, groupsFromAllSources)
+                return@map sources.mapIndexedNotNull { index, internalGroupsSource ->
+                    fetchShortcutCategory(
+                        internalGroupsSource,
+                        groupsFromAllSources[index],
+                        inputDevice,
+                        supportedKeyCodes,
+                    )
+                }
             }
             .stateIn(
                 scope = backgroundScope,
@@ -100,49 +132,22 @@
                 initialValue = emptyList(),
             )
 
-    private suspend fun fetchSystemShortcuts(inputDevice: InputDevice) =
-        toShortcutCategory(
-            inputDevice.keyCharacterMap,
-            System,
-            systemShortcutsSource.shortcutGroups(inputDevice.id),
-            keepIcons = true,
-        )
-
-    private suspend fun fetchMultiTaskingShortcuts(inputDevice: InputDevice) =
-        toShortcutCategory(
-            inputDevice.keyCharacterMap,
-            MultiTasking,
-            multitaskingShortcutsSource.shortcutGroups(inputDevice.id),
-            keepIcons = true,
-        )
-
-    private suspend fun fetchAppCategoriesShortcuts(inputDevice: InputDevice) =
-        toShortcutCategory(
-            inputDevice.keyCharacterMap,
-            AppCategories,
-            appCategoriesShortcutsSource.shortcutGroups(inputDevice.id),
-            keepIcons = true,
-        )
-
-    private suspend fun fetchImeShortcuts(inputDevice: InputDevice) =
-        toShortcutCategory(
-            inputDevice.keyCharacterMap,
-            InputMethodEditor,
-            inputShortcutsSource.shortcutGroups(inputDevice.id),
-            keepIcons = false,
-        )
-
-    private suspend fun fetchCurrentAppShortcuts(inputDevice: InputDevice): ShortcutCategory? {
-        val shortcutGroups = currentAppShortcutsSource.shortcutGroups(inputDevice.id)
-        val categoryType = getCurrentAppShortcutCategoryType(shortcutGroups)
-        return if (categoryType == null) {
+    private fun fetchShortcutCategory(
+        internalGroupsSource: InternalGroupsSource,
+        groups: List<KeyboardShortcutGroup>,
+        inputDevice: InputDevice,
+        supportedKeyCodes: Set<Int>,
+    ): ShortcutCategory? {
+        val type = internalGroupsSource.typeProvider(groups)
+        return if (type == null) {
             null
         } else {
             toShortcutCategory(
                 inputDevice.keyCharacterMap,
-                categoryType,
-                shortcutGroups,
-                keepIcons = false
+                type,
+                groups,
+                internalGroupsSource.isTrusted,
+                supportedKeyCodes,
             )
         }
     }
@@ -162,13 +167,19 @@
         type: ShortcutCategoryType,
         shortcutGroups: List<KeyboardShortcutGroup>,
         keepIcons: Boolean,
+        supportedKeyCodes: Set<Int>,
     ): ShortcutCategory? {
         val subCategories =
             shortcutGroups
                 .map { shortcutGroup ->
                     ShortcutSubCategory(
                         shortcutGroup.label.toString(),
-                        toShortcuts(keyCharacterMap, shortcutGroup.items, keepIcons)
+                        toShortcuts(
+                            keyCharacterMap,
+                            shortcutGroup.items,
+                            keepIcons,
+                            supportedKeyCodes,
+                        )
                     )
                 }
                 .filter { it.shortcuts.isNotEmpty() }
@@ -184,7 +195,15 @@
         keyCharacterMap: KeyCharacterMap,
         infoList: List<KeyboardShortcutInfo>,
         keepIcons: Boolean,
-    ) = infoList.mapNotNull { toShortcut(keyCharacterMap, it, keepIcons) }
+        supportedKeyCodes: Set<Int>,
+    ) =
+        infoList
+            .filter {
+                // Allow KEYCODE_UNKNOWN (0) because shortcuts can have just modifiers and no
+                // keycode, or they could have a baseCharacter instead of a keycode.
+                it.keycode == KeyEvent.KEYCODE_UNKNOWN || supportedKeyCodes.contains(it.keycode)
+            }
+            .mapNotNull { toShortcut(keyCharacterMap, it, keepIcons) }
 
     private fun toShortcut(
         keyCharacterMap: KeyCharacterMap,
@@ -268,6 +287,29 @@
         return null
     }
 
+    private suspend fun fetchSupportedKeyCodes(
+        deviceId: Int,
+        groupsFromAllSources: List<List<KeyboardShortcutGroup>>
+    ): Set<Int> =
+        withContext(backgroundDispatcher) {
+            val allUsedKeyCodes =
+                groupsFromAllSources
+                    .flatMap { groups -> groups.flatMap { group -> group.items } }
+                    .map { info -> info.keycode }
+                    .distinct()
+            val keyCodesSupported =
+                inputManager.deviceHasKeys(deviceId, allUsedKeyCodes.toIntArray())
+            return@withContext allUsedKeyCodes
+                .filterIndexed { index, _ -> keyCodesSupported[index] }
+                .toSet()
+        }
+
+    private class InternalGroupsSource(
+        val source: KeyboardShortcutGroupsSource,
+        val isTrusted: Boolean,
+        val typeProvider: (groups: List<KeyboardShortcutGroup>) -> ShortcutCategoryType?,
+    )
+
     companion object {
         private const val TAG = "SHCategoriesRepo"
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperKeys.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperKeys.kt
index cbe6fc7..8db16fa 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperKeys.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperKeys.kt
@@ -98,6 +98,7 @@
 import android.view.KeyEvent.KEYCODE_PAGE_DOWN
 import android.view.KeyEvent.KEYCODE_PAGE_UP
 import android.view.KeyEvent.KEYCODE_PERIOD
+import android.view.KeyEvent.KEYCODE_RECENT_APPS
 import android.view.KeyEvent.KEYCODE_SCROLL_LOCK
 import android.view.KeyEvent.KEYCODE_SHIFT_LEFT
 import android.view.KeyEvent.KEYCODE_SHIFT_RIGHT
@@ -118,6 +119,9 @@
     val keyIcons =
         mapOf(
             META_META_ON to R.drawable.ic_ksh_key_meta,
+            KEYCODE_BACK to R.drawable.ic_arrow_back_2,
+            KEYCODE_HOME to R.drawable.ic_radio_button_unchecked,
+            KEYCODE_RECENT_APPS to R.drawable.ic_check_box_outline_blank,
         )
 
     val specialKeyLabels =
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/source/SystemShortcutsSource.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/source/SystemShortcutsSource.kt
index e55e339..7c0c75e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/source/SystemShortcutsSource.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/source/SystemShortcutsSource.kt
@@ -18,14 +18,17 @@
 
 import android.content.res.Resources
 import android.view.KeyEvent.KEYCODE_A
+import android.view.KeyEvent.KEYCODE_BACK
 import android.view.KeyEvent.KEYCODE_DEL
 import android.view.KeyEvent.KEYCODE_DPAD_LEFT
 import android.view.KeyEvent.KEYCODE_ENTER
 import android.view.KeyEvent.KEYCODE_ESCAPE
 import android.view.KeyEvent.KEYCODE_H
+import android.view.KeyEvent.KEYCODE_HOME
 import android.view.KeyEvent.KEYCODE_I
 import android.view.KeyEvent.KEYCODE_L
 import android.view.KeyEvent.KEYCODE_N
+import android.view.KeyEvent.KEYCODE_RECENT_APPS
 import android.view.KeyEvent.KEYCODE_S
 import android.view.KeyEvent.KEYCODE_SLASH
 import android.view.KeyEvent.KEYCODE_TAB
@@ -60,24 +63,36 @@
                 command(META_META_ON)
             },
             // Access home screen:
+            //  - Home button
             //  - Meta + H
             //  - Meta + Enter
             shortcutInfo(resources.getString(R.string.group_system_access_home_screen)) {
+                command(modifiers = 0, KEYCODE_HOME)
+            },
+            shortcutInfo(resources.getString(R.string.group_system_access_home_screen)) {
                 command(META_META_ON, KEYCODE_H)
             },
             shortcutInfo(resources.getString(R.string.group_system_access_home_screen)) {
                 command(META_META_ON, KEYCODE_ENTER)
             },
             // Overview of open apps:
+            //  - Recent apps button
             //  - Meta + Tab
             shortcutInfo(resources.getString(R.string.group_system_overview_open_apps)) {
+                command(modifiers = 0, KEYCODE_RECENT_APPS)
+            },
+            shortcutInfo(resources.getString(R.string.group_system_overview_open_apps)) {
                 command(META_META_ON, KEYCODE_TAB)
             },
             // Back: go back to previous state (back button)
+            //  - Back button
             //  - Meta + Escape OR
             //  - Meta + Backspace OR
             //  - Meta + Left arrow
             shortcutInfo(resources.getString(R.string.group_system_go_back)) {
+                command(modifiers = 0, KEYCODE_BACK)
+            },
+            shortcutInfo(resources.getString(R.string.group_system_go_back)) {
                 command(META_META_ON, KEYCODE_ESCAPE)
             },
             shortcutInfo(resources.getString(R.string.group_system_go_back)) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutCategory.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutCategory.kt
index 4eabefc..c89ef15 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutCategory.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutCategory.kt
@@ -31,7 +31,12 @@
 data class ShortcutCategory(
     val type: ShortcutCategoryType,
     val subCategories: List<ShortcutSubCategory>
-)
+) {
+    constructor(
+        type: ShortcutCategoryType,
+        vararg subCategories: ShortcutSubCategory
+    ) : this(type, subCategories.asList())
+}
 
 class ShortcutCategoryBuilder(val type: ShortcutCategoryType) {
     private val subCategories = mutableListOf<ShortcutSubCategory>()
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 869f00c..67aedde 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt
@@ -24,6 +24,9 @@
 import androidx.compose.animation.core.animateFloatAsState
 import androidx.compose.foundation.Image
 import androidx.compose.foundation.background
+import androidx.compose.foundation.focusable
+import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.interaction.collectIsFocusedAsState
 import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.BoxScope
@@ -77,11 +80,16 @@
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.drawWithContent
 import androidx.compose.ui.focus.FocusRequester
 import androidx.compose.ui.focus.focusRequester
+import androidx.compose.ui.geometry.CornerRadius
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.geometry.Rect
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.RectangleShape
 import androidx.compose.ui.graphics.Shape
+import androidx.compose.ui.graphics.drawscope.Stroke
 import androidx.compose.ui.graphics.graphicsLayer
 import androidx.compose.ui.input.nestedscroll.nestedScroll
 import androidx.compose.ui.platform.LocalContext
@@ -91,11 +99,16 @@
 import androidx.compose.ui.semantics.Role
 import androidx.compose.ui.semantics.role
 import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.text.SpanStyle
+import androidx.compose.ui.text.buildAnnotatedString
+import androidx.compose.ui.text.withStyle
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.sp
+import androidx.compose.ui.util.fastFirstOrNull
 import androidx.compose.ui.util.fastForEach
 import androidx.compose.ui.util.fastForEachIndexed
+import androidx.compose.ui.zIndex
 import com.android.compose.ui.graphics.painter.rememberDrawablePainter
 import com.android.compose.windowsizeclass.LocalWindowSizeClass
 import com.android.systemui.keyboard.shortcut.shared.model.Shortcut
@@ -112,6 +125,7 @@
 
 @Composable
 fun ShortcutHelper(
+    onSearchQueryChanged: (String) -> Unit,
     onKeyboardSettingsClicked: () -> Unit,
     modifier: Modifier = Modifier,
     shortcutsUiState: ShortcutsUiState,
@@ -119,39 +133,69 @@
 ) {
     when (shortcutsUiState) {
         is ShortcutsUiState.Active -> {
-            if (useSinglePane()) {
-                ShortcutHelperSinglePane(
-                    modifier,
-                    shortcutsUiState.shortcutCategories,
-                    shortcutsUiState.defaultSelectedCategory,
-                    onKeyboardSettingsClicked
-                )
-            } else {
-                ShortcutHelperTwoPane(
-                    modifier,
-                    shortcutsUiState.shortcutCategories,
-                    shortcutsUiState.defaultSelectedCategory,
-                    onKeyboardSettingsClicked
-                )
-            }
+            ActiveShortcutHelper(
+                shortcutsUiState,
+                useSinglePane,
+                onSearchQueryChanged,
+                modifier,
+                onKeyboardSettingsClicked
+            )
         }
-        is ShortcutsUiState.Inactive -> {
+        else -> {
             // No-op for now.
         }
     }
 }
 
 @Composable
+private fun ActiveShortcutHelper(
+    shortcutsUiState: ShortcutsUiState.Active,
+    useSinglePane: @Composable () -> Boolean,
+    onSearchQueryChanged: (String) -> Unit,
+    modifier: Modifier,
+    onKeyboardSettingsClicked: () -> Unit
+) {
+    var selectedCategoryType by
+        remember(shortcutsUiState.defaultSelectedCategory) {
+            mutableStateOf(shortcutsUiState.defaultSelectedCategory)
+        }
+    if (useSinglePane()) {
+        ShortcutHelperSinglePane(
+            shortcutsUiState.searchQuery,
+            onSearchQueryChanged,
+            shortcutsUiState.shortcutCategories,
+            selectedCategoryType,
+            onCategorySelected = { selectedCategoryType = it },
+            onKeyboardSettingsClicked,
+            modifier,
+        )
+    } else {
+        ShortcutHelperTwoPane(
+            shortcutsUiState.searchQuery,
+            onSearchQueryChanged,
+            modifier,
+            shortcutsUiState.shortcutCategories,
+            selectedCategoryType,
+            onCategorySelected = { selectedCategoryType = it },
+            onKeyboardSettingsClicked
+        )
+    }
+}
+
+@Composable
 private fun shouldUseSinglePane() =
     LocalWindowSizeClass.current.widthSizeClass == WindowWidthSizeClass.Compact ||
         LocalWindowSizeClass.current.heightSizeClass == WindowHeightSizeClass.Compact
 
 @Composable
 private fun ShortcutHelperSinglePane(
-    modifier: Modifier = Modifier,
+    searchQuery: String,
+    onSearchQueryChanged: (String) -> Unit,
     categories: List<ShortcutCategory>,
-    defaultSelectedCategory: ShortcutCategoryType,
+    selectedCategoryType: ShortcutCategoryType?,
+    onCategorySelected: (ShortcutCategoryType?) -> Unit,
     onKeyboardSettingsClicked: () -> Unit,
+    modifier: Modifier = Modifier,
 ) {
     Column(
         modifier =
@@ -162,9 +206,9 @@
     ) {
         TitleBar()
         Spacer(modifier = Modifier.height(6.dp))
-        ShortcutsSearchBar()
+        ShortcutsSearchBar(onSearchQueryChanged)
         Spacer(modifier = Modifier.height(16.dp))
-        CategoriesPanelSinglePane(categories, defaultSelectedCategory)
+        CategoriesPanelSinglePane(searchQuery, categories, selectedCategoryType, onCategorySelected)
         Spacer(modifier = Modifier.weight(1f))
         KeyboardSettings(onClick = onKeyboardSettingsClicked)
     }
@@ -172,16 +216,18 @@
 
 @Composable
 private fun CategoriesPanelSinglePane(
+    searchQuery: String,
     categories: List<ShortcutCategory>,
-    defaultSelectedCategory: ShortcutCategoryType,
+    selectedCategoryType: ShortcutCategoryType?,
+    onCategorySelected: (ShortcutCategoryType?) -> Unit,
 ) {
-    val selectedCategory = categories.firstOrNull { it.type == defaultSelectedCategory }
-    var expandedCategory by remember { mutableStateOf(selectedCategory) }
     Column(verticalArrangement = Arrangement.spacedBy(2.dp)) {
         categories.fastForEachIndexed { index, category ->
-            val isExpanded = expandedCategory == category
+            val isExpanded = selectedCategoryType == category.type
             val itemShape =
-                if (index == 0) {
+                if (categories.size == 1) {
+                    ShortcutHelper.Shapes.singlePaneSingleCategory
+                } else if (index == 0) {
                     ShortcutHelper.Shapes.singlePaneFirstCategory
                 } else if (index == categories.lastIndex) {
                     ShortcutHelper.Shapes.singlePaneLastCategory
@@ -189,15 +235,17 @@
                     ShortcutHelper.Shapes.singlePaneCategory
                 }
             CategoryItemSinglePane(
+                searchQuery = searchQuery,
                 category = category,
                 isExpanded = isExpanded,
                 onClick = {
-                    expandedCategory =
+                    onCategorySelected(
                         if (isExpanded) {
                             null
                         } else {
-                            category
+                            category.type
                         }
+                    )
                 },
                 shape = itemShape,
             )
@@ -207,6 +255,7 @@
 
 @Composable
 private fun CategoryItemSinglePane(
+    searchQuery: String,
     category: ShortcutCategory,
     isExpanded: Boolean,
     onClick: () -> Unit,
@@ -222,13 +271,15 @@
                 verticalAlignment = Alignment.CenterVertically,
                 modifier = Modifier.fillMaxWidth().heightIn(min = 88.dp).padding(horizontal = 16.dp)
             ) {
-                ShortcutCategoryIcon(category.icon)
+                ShortcutCategoryIcon(modifier = Modifier.size(24.dp), source = category.icon)
                 Spacer(modifier = Modifier.width(16.dp))
                 Text(category.label(LocalContext.current))
                 Spacer(modifier = Modifier.weight(1f))
                 RotatingExpandCollapseIcon(isExpanded)
             }
-            AnimatedVisibility(visible = isExpanded) { ShortcutCategoryDetailsSinglePane(category) }
+            AnimatedVisibility(visible = isExpanded) {
+                ShortcutCategoryDetailsSinglePane(searchQuery, category)
+            }
         }
     }
 }
@@ -253,8 +304,8 @@
 @Composable
 fun ShortcutCategoryIcon(
     source: IconSource,
-    contentDescription: String? = null,
     modifier: Modifier = Modifier,
+    contentDescription: String? = null,
     tint: Color = LocalContentColor.current
 ) {
     if (source.imageVector != null) {
@@ -326,86 +377,84 @@
 }
 
 @Composable
-private fun ShortcutCategoryDetailsSinglePane(category: ShortcutCategory) {
+private fun ShortcutCategoryDetailsSinglePane(searchQuery: String, category: ShortcutCategory) {
     Column(Modifier.padding(horizontal = 16.dp)) {
         category.subCategories.fastForEach { subCategory ->
-            ShortcutSubCategorySinglePane(subCategory)
+            ShortcutSubCategorySinglePane(searchQuery, subCategory)
         }
     }
 }
 
 @Composable
-private fun ShortcutSubCategorySinglePane(subCategory: ShortcutSubCategory) {
+private fun ShortcutSubCategorySinglePane(searchQuery: String, subCategory: ShortcutSubCategory) {
     // This @Composable is expected to be in a Column.
     SubCategoryTitle(subCategory.label)
     subCategory.shortcuts.fastForEachIndexed { index, shortcut ->
         if (index > 0) {
             HorizontalDivider()
         }
-        ShortcutSinglePane(shortcut)
-    }
-}
-
-@Composable
-private fun ShortcutSinglePane(shortcut: Shortcut) {
-    Column(Modifier.padding(vertical = 24.dp)) {
-        ShortcutDescriptionText(shortcut = shortcut)
-        Spacer(modifier = Modifier.height(12.dp))
-        ShortcutKeyCombinations(shortcut = shortcut)
+        ShortcutView(Modifier.padding(vertical = 24.dp), searchQuery, shortcut)
     }
 }
 
 @Composable
 private fun ShortcutHelperTwoPane(
+    searchQuery: String,
+    onSearchQueryChanged: (String) -> Unit,
     modifier: Modifier = Modifier,
     categories: List<ShortcutCategory>,
-    defaultSelectedCategory: ShortcutCategoryType,
+    selectedCategoryType: ShortcutCategoryType?,
+    onCategorySelected: (ShortcutCategoryType?) -> Unit,
     onKeyboardSettingsClicked: () -> Unit,
 ) {
-    var selectedCategoryType by remember { mutableStateOf(defaultSelectedCategory) }
-    val selectedCategory = categories.first { it.type == selectedCategoryType }
+    val selectedCategory = categories.fastFirstOrNull { it.type == selectedCategoryType }
     Column(modifier = modifier.fillMaxSize().padding(start = 24.dp, end = 24.dp, top = 26.dp)) {
         TitleBar()
         Spacer(modifier = Modifier.height(12.dp))
         Row(Modifier.fillMaxWidth()) {
             StartSidePanel(
-                modifier = Modifier.fillMaxWidth(fraction = 0.32f),
+                onSearchQueryChanged = onSearchQueryChanged,
+                modifier = Modifier.width(200.dp),
                 categories = categories,
-                selectedCategory = selectedCategoryType,
-                onCategoryClicked = { selectedCategoryType = it.type },
                 onKeyboardSettingsClicked = onKeyboardSettingsClicked,
+                selectedCategory = selectedCategoryType,
+                onCategoryClicked = { onCategorySelected(it.type) }
             )
             Spacer(modifier = Modifier.width(24.dp))
-            EndSidePanel(Modifier.fillMaxSize(), selectedCategory)
+            EndSidePanel(searchQuery, Modifier.fillMaxSize().padding(top = 8.dp), selectedCategory)
         }
     }
 }
 
 @Composable
-private fun EndSidePanel(modifier: Modifier, category: ShortcutCategory) {
+private fun EndSidePanel(searchQuery: String, modifier: Modifier, category: ShortcutCategory?) {
+    if (category == null) {
+        // TODO(b/353953351) - Show a "no results" UI?
+        return
+    }
     LazyColumn(modifier.nestedScroll(rememberNestedScrollInteropConnection())) {
         items(items = category.subCategories, key = { item -> item.label }) {
-            SubCategoryContainerDualPane(it)
+            SubCategoryContainerDualPane(searchQuery, it)
             Spacer(modifier = Modifier.height(8.dp))
         }
     }
 }
 
 @Composable
-private fun SubCategoryContainerDualPane(subCategory: ShortcutSubCategory) {
+private fun SubCategoryContainerDualPane(searchQuery: String, subCategory: ShortcutSubCategory) {
     Surface(
         modifier = Modifier.fillMaxWidth(),
         shape = RoundedCornerShape(28.dp),
         color = MaterialTheme.colorScheme.surfaceBright
     ) {
-        Column(Modifier.padding(horizontal = 32.dp, vertical = 24.dp)) {
+        Column(Modifier.padding(24.dp)) {
             SubCategoryTitle(subCategory.label)
-            Spacer(Modifier.height(24.dp))
+            Spacer(Modifier.height(8.dp))
             subCategory.shortcuts.fastForEachIndexed { index, shortcut ->
                 if (index > 0) {
                     HorizontalDivider()
                 }
-                ShortcutViewDualPane(shortcut)
+                ShortcutView(Modifier.padding(vertical = 16.dp), searchQuery, shortcut)
             }
         }
     }
@@ -421,20 +470,32 @@
 }
 
 @Composable
-private fun ShortcutViewDualPane(shortcut: Shortcut) {
-    Row(Modifier.padding(vertical = 16.dp)) {
+private fun ShortcutView(modifier: Modifier, searchQuery: String, shortcut: Shortcut) {
+    val interactionSource = remember { MutableInteractionSource() }
+    val isFocused by interactionSource.collectIsFocusedAsState()
+    Row(
+        modifier
+            .focusable(interactionSource = interactionSource)
+            .outlineFocusModifier(
+                isFocused = isFocused,
+                focusColor = MaterialTheme.colorScheme.secondary,
+                padding = 8.dp,
+                cornerRadius = 16.dp
+            )
+    ) {
         Row(
-            modifier = Modifier.width(160.dp).align(Alignment.CenterVertically),
+            modifier = Modifier.width(128.dp).align(Alignment.CenterVertically),
             horizontalArrangement = Arrangement.spacedBy(16.dp),
             verticalAlignment = Alignment.CenterVertically,
         ) {
             if (shortcut.icon != null) {
                 ShortcutIcon(
                     shortcut.icon,
-                    modifier = Modifier.size(36.dp),
+                    modifier = Modifier.size(24.dp),
                 )
             }
             ShortcutDescriptionText(
+                searchQuery = searchQuery,
                 shortcut = shortcut,
             )
         }
@@ -470,7 +531,11 @@
     modifier: Modifier = Modifier,
     shortcut: Shortcut,
 ) {
-    FlowRow(modifier = modifier, verticalArrangement = Arrangement.spacedBy(8.dp)) {
+    FlowRow(
+        modifier = modifier,
+        verticalArrangement = Arrangement.spacedBy(8.dp),
+        horizontalArrangement = Arrangement.End
+    ) {
         shortcut.commands.forEachIndexed { index, command ->
             if (index > 0) {
                 ShortcutOrSeparator(spacing = 16.dp)
@@ -544,28 +609,54 @@
 
 @Composable
 private fun ShortcutDescriptionText(
+    searchQuery: String,
     shortcut: Shortcut,
     modifier: Modifier = Modifier,
 ) {
     Text(
         modifier = modifier,
-        text = shortcut.label,
+        text = textWithHighlightedSearchQuery(shortcut.label, searchQuery),
         style = MaterialTheme.typography.bodyMedium,
         color = MaterialTheme.colorScheme.onSurface,
     )
 }
 
 @Composable
+private fun textWithHighlightedSearchQuery(text: String, searchValue: String) =
+    buildAnnotatedString {
+        val searchIndex = text.lowercase().indexOf(searchValue.trim().lowercase())
+        val postSearchIndex = searchIndex + searchValue.trim().length
+
+        if (searchIndex > 0) {
+            val preSearchText = text.substring(0, searchIndex)
+            append(preSearchText)
+        }
+        if (searchIndex >= 0) {
+            val searchText = text.substring(searchIndex, postSearchIndex)
+            withStyle(style = SpanStyle(background = MaterialTheme.colorScheme.primaryContainer)) {
+                append(searchText)
+            }
+            if (postSearchIndex < text.length) {
+                val postSearchText = text.substring(postSearchIndex)
+                append(postSearchText)
+            }
+        } else {
+            append(text)
+        }
+    }
+
+@Composable
 private fun StartSidePanel(
+    onSearchQueryChanged: (String) -> Unit,
     modifier: Modifier,
     categories: List<ShortcutCategory>,
     onKeyboardSettingsClicked: () -> Unit,
-    selectedCategory: ShortcutCategoryType,
+    selectedCategory: ShortcutCategoryType?,
     onCategoryClicked: (ShortcutCategory) -> Unit,
 ) {
     Column(modifier) {
-        ShortcutsSearchBar()
-        Spacer(modifier = Modifier.heightIn(16.dp))
+        ShortcutsSearchBar(onSearchQueryChanged)
+        Spacer(modifier = Modifier.heightIn(8.dp))
         CategoriesPanelTwoPane(categories, selectedCategory, onCategoryClicked)
         Spacer(modifier = Modifier.weight(1f))
         KeyboardSettings(onKeyboardSettingsClicked)
@@ -575,7 +666,7 @@
 @Composable
 private fun CategoriesPanelTwoPane(
     categories: List<ShortcutCategory>,
-    selectedCategory: ShortcutCategoryType,
+    selectedCategory: ShortcutCategoryType?,
     onCategoryClicked: (ShortcutCategory) -> Unit
 ) {
     Column {
@@ -599,10 +690,23 @@
     colors: NavigationDrawerItemColors =
         NavigationDrawerItemDefaults.colors(unselectedContainerColor = Color.Transparent),
 ) {
+    val interactionSource = remember { MutableInteractionSource() }
+    val isFocused by interactionSource.collectIsFocusedAsState()
+
     Surface(
         selected = selected,
         onClick = onClick,
-        modifier = Modifier.semantics { role = Role.Tab }.heightIn(min = 72.dp).fillMaxWidth(),
+        modifier =
+            Modifier.semantics { role = Role.Tab }
+                .heightIn(min = 64.dp)
+                .fillMaxWidth()
+                .focusable(interactionSource = interactionSource)
+                .outlineFocusModifier(
+                    isFocused = isFocused,
+                    focusColor = MaterialTheme.colorScheme.secondary,
+                    padding = 2.dp,
+                    cornerRadius = 33.dp
+                ),
         shape = RoundedCornerShape(28.dp),
         color = colors.containerColor(selected).value,
     ) {
@@ -626,6 +730,39 @@
     }
 }
 
+private fun Modifier.outlineFocusModifier(
+    isFocused: Boolean,
+    focusColor: Color,
+    padding: Dp,
+    cornerRadius: Dp
+): Modifier {
+    if (isFocused) {
+        return this.drawWithContent {
+                val focusOutline =
+                    Rect(Offset.Zero, size).let {
+                        if (padding > 0.dp) {
+                            it.inflate(padding.toPx())
+                        } else {
+                            it.deflate(padding.unaryMinus().toPx())
+                        }
+                    }
+                drawContent()
+                drawRoundRect(
+                    color = focusColor,
+                    style = Stroke(width = 3.dp.toPx()),
+                    topLeft = focusOutline.topLeft,
+                    size = focusOutline.size,
+                    cornerRadius = CornerRadius(cornerRadius.toPx())
+                )
+            }
+            // Increasing Z-Index so focus outline is drawn on top of "selected" category
+            // background.
+            .zIndex(1f)
+    } else {
+        return this
+    }
+}
+
 @Composable
 @OptIn(ExperimentalMaterial3Api::class)
 private fun TitleBar() {
@@ -643,17 +780,23 @@
 
 @Composable
 @OptIn(ExperimentalMaterial3Api::class)
-private fun ShortcutsSearchBar() {
-    var query by remember { mutableStateOf("") }
+private fun ShortcutsSearchBar(onQueryChange: (String) -> Unit) {
+    // Using an "internal query" to make sure the SearchBar is immediately updated, otherwise
+    // the cursor moves to the wrong position sometimes, when waiting for the query to come back
+    // from the ViewModel.
+    var queryInternal by remember { mutableStateOf("") }
     val focusRequester = remember { FocusRequester() }
     LaunchedEffect(Unit) { focusRequester.requestFocus() }
     SearchBar(
         modifier = Modifier.fillMaxWidth().focusRequester(focusRequester),
         colors = SearchBarDefaults.colors(containerColor = MaterialTheme.colorScheme.surfaceBright),
-        query = query,
+        query = queryInternal,
         active = false,
         onActiveChange = {},
-        onQueryChange = { query = it },
+        onQueryChange = {
+            queryInternal = it
+            onQueryChange(it)
+        },
         onSearch = {},
         leadingIcon = { Icon(Icons.Default.Search, contentDescription = null) },
         placeholder = { Text(text = stringResource(R.string.shortcut_helper_search_placeholder)) },
@@ -663,14 +806,27 @@
 
 @Composable
 private fun KeyboardSettings(onClick: () -> Unit) {
+    val interactionSource = remember { MutableInteractionSource() }
+    val isFocused by interactionSource.collectIsFocusedAsState()
+
     Surface(
         onClick = onClick,
         shape = RoundedCornerShape(24.dp),
         color = Color.Transparent,
-        modifier = Modifier.semantics { role = Role.Button }.fillMaxWidth()
+        modifier =
+            Modifier.semantics { role = Role.Button }
+                .fillMaxWidth()
+                .focusable(interactionSource = interactionSource)
     ) {
         Row(
-            modifier = Modifier.padding(horizontal = 24.dp, vertical = 16.dp),
+            modifier =
+                Modifier.padding(horizontal = 12.dp, vertical = 16.dp)
+                    .outlineFocusModifier(
+                        isFocused = isFocused,
+                        focusColor = MaterialTheme.colorScheme.secondary,
+                        padding = 8.dp,
+                        cornerRadius = 28.dp
+                    ),
             verticalAlignment = Alignment.CenterVertically
         ) {
             Text(
@@ -682,7 +838,8 @@
             Icon(
                 imageVector = Icons.AutoMirrored.Default.OpenInNew,
                 contentDescription = null,
-                tint = MaterialTheme.colorScheme.onSurfaceVariant
+                tint = MaterialTheme.colorScheme.onSurfaceVariant,
+                modifier = Modifier.size(24.dp)
             )
         }
     }
@@ -701,6 +858,10 @@
                 bottomStart = Dimensions.SinglePaneCategoryCornerRadius,
                 bottomEnd = Dimensions.SinglePaneCategoryCornerRadius
             )
+        val singlePaneSingleCategory =
+            RoundedCornerShape(
+                size = Dimensions.SinglePaneCategoryCornerRadius,
+            )
         val singlePaneCategory = RectangleShape
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/model/ShortcutsUiState.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/model/ShortcutsUiState.kt
index daafc3f..d2122b3 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/model/ShortcutsUiState.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/model/ShortcutsUiState.kt
@@ -22,8 +22,9 @@
 sealed interface ShortcutsUiState {
 
     data class Active(
+        val searchQuery: String,
         val shortcutCategories: List<ShortcutCategory>,
-        val defaultSelectedCategory: ShortcutCategoryType,
+        val defaultSelectedCategory: ShortcutCategoryType?,
     ) : ShortcutsUiState
 
     data object Inactive : ShortcutsUiState
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/view/ShortcutHelperActivity.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/view/ShortcutHelperActivity.kt
index d0e3ab4..2039743 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/view/ShortcutHelperActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/view/ShortcutHelperActivity.kt
@@ -26,8 +26,10 @@
 import androidx.activity.BackEventCompat
 import androidx.activity.ComponentActivity
 import androidx.activity.OnBackPressedCallback
+import androidx.compose.runtime.CompositionLocalProvider
 import androidx.compose.runtime.getValue
 import androidx.compose.ui.platform.ComposeView
+import androidx.compose.ui.platform.LocalContext
 import androidx.core.view.updatePadding
 import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import androidx.lifecycle.flowWithLifecycle
@@ -36,6 +38,7 @@
 import com.android.systemui.keyboard.shortcut.ui.composable.ShortcutHelper
 import com.android.systemui.keyboard.shortcut.ui.viewmodel.ShortcutHelperViewModel
 import com.android.systemui.res.R
+import com.android.systemui.settings.UserTracker
 import com.google.android.material.bottomsheet.BottomSheetBehavior
 import com.google.android.material.bottomsheet.BottomSheetBehavior.BottomSheetCallback
 import com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_HIDDEN
@@ -49,6 +52,7 @@
 class ShortcutHelperActivity
 @Inject
 constructor(
+    private val userTracker: UserTracker,
     private val viewModel: ShortcutHelperViewModel,
 ) : ComponentActivity() {
 
@@ -79,12 +83,16 @@
     private fun setUpComposeView() {
         requireViewById<ComposeView>(R.id.shortcut_helper_compose_container).apply {
             setContent {
-                PlatformTheme {
-                    val shortcutsUiState by viewModel.shortcutsUiState.collectAsStateWithLifecycle()
-                    ShortcutHelper(
-                        shortcutsUiState = shortcutsUiState,
-                        onKeyboardSettingsClicked = ::onKeyboardSettingsClicked,
-                    )
+                CompositionLocalProvider(LocalContext provides userTracker.userContext) {
+                    PlatformTheme {
+                        val shortcutsUiState by
+                            viewModel.shortcutsUiState.collectAsStateWithLifecycle()
+                        ShortcutHelper(
+                            shortcutsUiState = shortcutsUiState,
+                            onKeyboardSettingsClicked = ::onKeyboardSettingsClicked,
+                            onSearchQueryChanged = { viewModel.onSearchQueryChanged(it) },
+                        )
+                    }
                 }
             }
         }
@@ -92,7 +100,10 @@
 
     private fun onKeyboardSettingsClicked() {
         try {
-            startActivity(Intent(Settings.ACTION_HARD_KEYBOARD_SETTINGS))
+            startActivityAsUser(
+                Intent(Settings.ACTION_HARD_KEYBOARD_SETTINGS),
+                userTracker.userHandle
+            )
         } catch (e: ActivityNotFoundException) {
             // From the Settings docs: In some cases, a matching Activity may not exist, so ensure
             // you safeguard against this.
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 25574ea..19b46e3 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
@@ -16,31 +16,42 @@
 
 package com.android.systemui.keyboard.shortcut.ui.viewmodel
 
+import android.app.role.RoleManager
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.keyboard.shortcut.domain.interactor.ShortcutHelperCategoriesInteractor
 import com.android.systemui.keyboard.shortcut.domain.interactor.ShortcutHelperStateInteractor
+import com.android.systemui.keyboard.shortcut.shared.model.Shortcut
 import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategory
 import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType
 import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.CurrentApp
+import com.android.systemui.keyboard.shortcut.shared.model.ShortcutSubCategory
 import com.android.systemui.keyboard.shortcut.ui.model.ShortcutsUiState
+import com.android.systemui.settings.UserTracker
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.flowOn
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.withContext
 
 class ShortcutHelperViewModel
 @Inject
 constructor(
+    private val roleManager: RoleManager,
+    private val userTracker: UserTracker,
     @Background private val backgroundScope: CoroutineScope,
     @Background private val backgroundDispatcher: CoroutineDispatcher,
     private val stateInteractor: ShortcutHelperStateInteractor,
     categoriesInteractor: ShortcutHelperCategoriesInteractor,
 ) {
 
+    private val searchQuery = MutableStateFlow("")
+
     val shouldShow =
         categoriesInteractor.shortcutCategories
             .map { it.isNotEmpty() }
@@ -48,14 +59,15 @@
             .flowOn(backgroundDispatcher)
 
     val shortcutsUiState =
-        categoriesInteractor.shortcutCategories
-            .map {
-                if (it.isEmpty()) {
+        combine(searchQuery, categoriesInteractor.shortcutCategories) { query, categories ->
+                if (categories.isEmpty()) {
                     ShortcutsUiState.Inactive
                 } else {
+                    val filteredCategories = filterCategoriesBySearchQuery(query, categories)
                     ShortcutsUiState.Active(
-                        shortcutCategories = it,
-                        defaultSelectedCategory = getDefaultSelectedCategory(it),
+                        searchQuery = query,
+                        shortcutCategories = filteredCategories,
+                        defaultSelectedCategory = getDefaultSelectedCategory(filteredCategories),
                     )
                 }
             }
@@ -65,13 +77,58 @@
                 initialValue = ShortcutsUiState.Inactive
             )
 
-    private fun getDefaultSelectedCategory(
+    private suspend fun getDefaultSelectedCategory(
         categories: List<ShortcutCategory>
-    ): ShortcutCategoryType {
-        val currentAppShortcuts = categories.firstOrNull { it.type is CurrentApp }
-        return currentAppShortcuts?.type ?: categories.first().type
+    ): ShortcutCategoryType? {
+        val currentAppShortcuts =
+            categories.firstOrNull { it.type is CurrentApp && !isAppLauncher(it.type.packageName) }
+        return currentAppShortcuts?.type ?: categories.firstOrNull()?.type
     }
 
+    private suspend fun isAppLauncher(packageName: String): Boolean {
+        return withContext(backgroundDispatcher) {
+            roleManager
+                .getRoleHoldersAsUser(RoleManager.ROLE_HOME, userTracker.userHandle)
+                .firstOrNull() == packageName
+        }
+    }
+
+    private fun filterCategoriesBySearchQuery(
+        query: String,
+        categories: List<ShortcutCategory>
+    ): List<ShortcutCategory> {
+        val lowerCaseTrimmedQuery = query.trim().lowercase()
+        if (lowerCaseTrimmedQuery.isEmpty()) {
+            return categories
+        }
+        return categories
+            .map { category ->
+                category.copy(
+                    subCategories =
+                        filterSubCategoriesBySearchQuery(
+                            subCategories = category.subCategories,
+                            query = lowerCaseTrimmedQuery,
+                        )
+                )
+            }
+            .filter { it.subCategories.isNotEmpty() }
+    }
+
+    private fun filterSubCategoriesBySearchQuery(
+        subCategories: List<ShortcutSubCategory>,
+        query: String
+    ) =
+        subCategories
+            .map { subCategory ->
+                subCategory.copy(
+                    shortcuts = filterShortcutsBySearchQuery(subCategory.shortcuts, query)
+                )
+            }
+            .filter { it.shortcuts.isNotEmpty() }
+
+    private fun filterShortcutsBySearchQuery(shortcuts: List<Shortcut>, query: String) =
+        shortcuts.filter { shortcut -> shortcut.label.trim().lowercase().contains(query) }
+
     fun onViewClosed() {
         stateInteractor.onViewClosed()
     }
@@ -79,4 +136,8 @@
     fun onViewOpened() {
         stateInteractor.onViewOpened()
     }
+
+    fun onSearchQueryChanged(query: String) {
+        searchQuery.value = query
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/DismissCallbackRegistry.java b/packages/SystemUI/src/com/android/systemui/keyguard/DismissCallbackRegistry.java
index d1bbc33..0d01ee1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/DismissCallbackRegistry.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/DismissCallbackRegistry.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.keyguard;
 
+import android.util.Log;
 import com.android.internal.policy.IKeyguardDismissCallback;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.UiBackground;
@@ -33,6 +34,7 @@
 
     private final ArrayList<DismissCallbackWrapper> mDismissCallbacks = new ArrayList<>();
     private final Executor mUiBgExecutor;
+    private final static String TAG = "DismissCallbackRegistry";
 
     @Inject
     public DismissCallbackRegistry(@UiBackground Executor uiBgExecutor) {
@@ -40,10 +42,12 @@
     }
 
     public void addCallback(IKeyguardDismissCallback callback) {
+        Log.d(TAG, "Adding callback: " + callback);
         mDismissCallbacks.add(new DismissCallbackWrapper(callback));
     }
 
     public void notifyDismissCancelled() {
+        Log.d(TAG, "notifyDismissCancelled(" + mDismissCallbacks.size() + ")");
         for (int i = mDismissCallbacks.size() - 1; i >= 0; i--) {
             DismissCallbackWrapper callback = mDismissCallbacks.get(i);
             mUiBgExecutor.execute(callback::notifyDismissCancelled);
@@ -52,6 +56,7 @@
     }
 
     public void notifyDismissSucceeded() {
+        Log.d(TAG, "notifyDismissSucceeded(" + mDismissCallbacks.size() + ")");
         for (int i = mDismissCallbacks.size() - 1; i >= 0; i--) {
             DismissCallbackWrapper callback = mDismissCallbacks.get(i);
             mUiBgExecutor.execute(callback::notifyDismissSucceeded);
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
index d1a8463..2f41c0b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
@@ -62,21 +62,21 @@
 const val TAG = "KeyguardUnlock"
 
 /**
- * Starting scale factor for the app/launcher surface behind the keyguard, when it's animating
- * in during keyguard exit.
+ * Starting scale factor for the app/launcher surface behind the keyguard, when it's animating in
+ * during keyguard exit.
  */
 const val SURFACE_BEHIND_START_SCALE_FACTOR = 0.95f
 
 /**
- * How much to translate the surface behind the keyguard at the beginning of the exit animation,
- * in terms of percentage of the surface's height.
+ * How much to translate the surface behind the keyguard at the beginning of the exit animation, in
+ * terms of percentage of the surface's height.
  */
 const val SURFACE_BEHIND_START_TRANSLATION_Y = 0.05f
 
 /**
- * Y coordinate of the pivot point for the scale effect on the surface behind the keyguard. This
- * is expressed as percentage of the surface's height, so 0.66f means the surface will scale up
- * from the point at (width / 2, height * 0.66).
+ * Y coordinate of the pivot point for the scale effect on the surface behind the keyguard. This is
+ * expressed as percentage of the surface's height, so 0.66f means the surface will scale up from
+ * the point at (width / 2, height * 0.66).
  */
 const val SURFACE_BEHIND_SCALE_PIVOT_Y = 0.66f
 
@@ -155,19 +155,20 @@
  * [notifyStartSurfaceBehindRemoteAnimation] by [KeyguardViewMediator].
  */
 @SysUISingleton
-class KeyguardUnlockAnimationController @Inject constructor(
-        private val windowManager: WindowManager,
-        @Main private val resources: Resources,
-        private val keyguardStateController: KeyguardStateController,
-        private val
-    keyguardViewMediator: Lazy<KeyguardViewMediator>,
-        private val keyguardViewController: KeyguardViewController,
-        private val featureFlags: FeatureFlags,
-        private val biometricUnlockControllerLazy: Lazy<BiometricUnlockController>,
-        private val statusBarStateController: SysuiStatusBarStateController,
-        private val notificationShadeWindowController: NotificationShadeWindowController,
-        private val powerManager: PowerManager,
-        private val wallpaperManager: WallpaperManager,
+open class KeyguardUnlockAnimationController
+@Inject
+constructor(
+    private val windowManager: WindowManager,
+    @Main private val resources: Resources,
+    private val keyguardStateController: KeyguardStateController,
+    private val keyguardViewMediator: Lazy<KeyguardViewMediator>,
+    private val keyguardViewController: KeyguardViewController,
+    private val featureFlags: FeatureFlags,
+    private val biometricUnlockControllerLazy: Lazy<BiometricUnlockController>,
+    private val statusBarStateController: SysuiStatusBarStateController,
+    private val notificationShadeWindowController: NotificationShadeWindowController,
+    private val powerManager: PowerManager,
+    private val wallpaperManager: WallpaperManager,
 ) : KeyguardStateController.Callback, ISysuiUnlockAnimationController.Stub() {
 
     interface KeyguardUnlockAnimationListener {
@@ -221,8 +222,8 @@
     var playingCannedUnlockAnimation = false
 
     /**
-     * Whether we reached the swipe gesture threshold to dismiss keyguard, or restore it, once
-     * and should ignore any future changes to the dismiss amount before the animation finishes.
+     * Whether we reached the swipe gesture threshold to dismiss keyguard, or restore it, once and
+     * should ignore any future changes to the dismiss amount before the animation finishes.
      */
     var dismissAmountThresholdsReached = false
 
@@ -235,9 +236,7 @@
      */
     private var launcherUnlockController: ILauncherUnlockAnimationController? = null
 
-    /**
-     * Fully qualified class name of the launcher activity
-     */
+    /** Fully qualified class name of the launcher activity */
     private var launcherActivityClass: String? = null
 
     private val listeners = ArrayList<KeyguardUnlockAnimationListener>()
@@ -248,8 +247,8 @@
      * transition, but that's okay!
      */
     override fun setLauncherUnlockController(
-            activityClass: String,
-            callback: ILauncherUnlockAnimationController?
+        activityClass: String,
+        callback: ILauncherUnlockAnimationController?
     ) {
         launcherActivityClass = activityClass
         launcherUnlockController = callback
@@ -274,8 +273,7 @@
      * If we're unlocking via biometrics, PIN entry, or from clicking a notification, a canned
      * animation is started in [playCannedUnlockAnimation].
      */
-    @VisibleForTesting
-    var surfaceTransactionApplier: SyncRtSurfaceTransactionApplier? = null
+    @VisibleForTesting var surfaceTransactionApplier: SyncRtSurfaceTransactionApplier? = null
     private var surfaceBehindRemoteAnimationTargets: Array<RemoteAnimationTarget>? = null
     private var openingWallpaperTargets: Array<RemoteAnimationTarget>? = null
     private var closingWallpaperTargets: Array<RemoteAnimationTarget>? = null
@@ -291,8 +289,7 @@
      */
     private var surfaceBehindAlpha = 1f
 
-    @VisibleForTesting
-    var surfaceBehindAlphaAnimator = ValueAnimator.ofFloat(0f, 1f)
+    @VisibleForTesting var surfaceBehindAlphaAnimator = ValueAnimator.ofFloat(0f, 1f)
 
     var wallpaperCannedUnlockAnimator = ValueAnimator.ofFloat(0f, 1f)
 
@@ -310,8 +307,7 @@
      * Animator that animates in the surface behind the keyguard. This is used to play a canned
      * animation on the surface, if we're not doing a swipe gesture.
      */
-    @VisibleForTesting
-    val surfaceBehindEntryAnimator = ValueAnimator.ofFloat(0f, 1f)
+    @VisibleForTesting val surfaceBehindEntryAnimator = ValueAnimator.ofFloat(0f, 1f)
 
     /** Rounded corner radius to apply to the surface behind the keyguard. */
     private var roundedCornerRadius = 0f
@@ -322,8 +318,7 @@
      * window like any other app. This can be true while [willUnlockWithSmartspaceTransition] is
      * false, if the smartspace is not available or was not ready in time.
      */
-    @VisibleForTesting
-    var willUnlockWithInWindowLauncherAnimations: Boolean = false
+    @VisibleForTesting var willUnlockWithInWindowLauncherAnimations: Boolean = false
 
     /**
      * Whether we called [ILauncherUnlockAnimationController.prepareForUnlock], but have not yet
@@ -353,49 +348,64 @@
                 surfaceBehindAlpha = valueAnimator.animatedValue as Float
                 updateSurfaceBehindAppearAmount()
             }
-            addListener(object : AnimatorListenerAdapter() {
-                override fun onAnimationEnd(animation: Animator) {
-                    // If we animated the surface alpha to 0f, it means we cancelled a swipe to
-                    // dismiss. In this case, we should ask the KeyguardViewMediator to end the
-                    // remote animation to hide the surface behind the keyguard, but should *not*
-                    // call onKeyguardExitRemoteAnimationFinished since that will hide the keyguard
-                    // and unlock the device as well as hiding the surface.
-                    if (surfaceBehindAlpha == 0f) {
-                        Log.d(TAG, "surfaceBehindAlphaAnimator#onAnimationEnd")
-                        surfaceBehindRemoteAnimationTargets = null
-                        openingWallpaperTargets = null
-                        closingWallpaperTargets = null
-                        keyguardViewMediator.get().finishSurfaceBehindRemoteAnimation(
-                            false /* cancelled */)
-                    } else {
-                        Log.d(TAG, "skip finishSurfaceBehindRemoteAnimation" +
-                                " surfaceBehindAlpha=$surfaceBehindAlpha")
+            addListener(
+                object : AnimatorListenerAdapter() {
+                    override fun onAnimationEnd(animation: Animator) {
+                        // If we animated the surface alpha to 0f, it means we cancelled a swipe to
+                        // dismiss. In this case, we should ask the KeyguardViewMediator to end the
+                        // remote animation to hide the surface behind the keyguard, but should
+                        // *not* call onKeyguardExitRemoteAnimationFinished since that will hide the
+                        // keyguard and unlock the device as well as hiding the surface.
+                        if (surfaceBehindAlpha == 0f) {
+                            Log.d(TAG, "surfaceBehindAlphaAnimator#onAnimationEnd")
+                            surfaceBehindRemoteAnimationTargets = null
+                            openingWallpaperTargets = null
+                            closingWallpaperTargets = null
+                            keyguardViewMediator
+                                .get()
+                                .finishSurfaceBehindRemoteAnimation(false /* cancelled */)
+                        } else {
+                            Log.d(
+                                TAG,
+                                "skip finishSurfaceBehindRemoteAnimation" +
+                                    " surfaceBehindAlpha=$surfaceBehindAlpha"
+                            )
+                        }
                     }
                 }
-            })
+            )
         }
 
         with(wallpaperCannedUnlockAnimator) {
-            duration = if (fasterUnlockTransition()) UNLOCK_ANIMATION_DURATION_MS
-                    else LAUNCHER_ICONS_ANIMATION_DURATION_MS
-            interpolator = if (fasterUnlockTransition()) Interpolators.LINEAR
-                    else Interpolators.ALPHA_OUT
+            duration =
+                if (fasterUnlockTransition()) UNLOCK_ANIMATION_DURATION_MS
+                else LAUNCHER_ICONS_ANIMATION_DURATION_MS
+            interpolator =
+                if (fasterUnlockTransition()) Interpolators.LINEAR else Interpolators.ALPHA_OUT
             addUpdateListener { valueAnimator: ValueAnimator ->
                 setWallpaperAppearAmount(
-                        valueAnimator.animatedValue as Float, openingWallpaperTargets)
+                    valueAnimator.animatedValue as Float,
+                    openingWallpaperTargets
+                )
             }
-            addListener(object : AnimatorListenerAdapter() {
-                override fun onAnimationStart(animation: Animator) {
-                    super.onAnimationStart(animation)
-                    Trace.asyncTraceBegin(Trace.TRACE_TAG_APP, "WallpaperAlphaAnimation", 0)
+            addListener(
+                object : AnimatorListenerAdapter() {
+                    override fun onAnimationStart(animation: Animator) {
+                        super.onAnimationStart(animation)
+                        Trace.asyncTraceBegin(Trace.TRACE_TAG_APP, "WallpaperAlphaAnimation", 0)
+                    }
+
+                    override fun onAnimationEnd(animation: Animator) {
+                        Log.d(TAG, "wallpaperCannedUnlockAnimator#onAnimationEnd")
+                        keyguardViewMediator
+                            .get()
+                            .exitKeyguardAndFinishSurfaceBehindRemoteAnimation(
+                                false /* cancelled */
+                            )
+                        Trace.asyncTraceEnd(Trace.TRACE_TAG_APP, "WallpaperAlphaAnimation", 0)
+                    }
                 }
-                override fun onAnimationEnd(animation: Animator) {
-                    Log.d(TAG, "wallpaperCannedUnlockAnimator#onAnimationEnd")
-                    keyguardViewMediator.get().exitKeyguardAndFinishSurfaceBehindRemoteAnimation(
-                        false /* cancelled */)
-                    Trace.asyncTraceEnd(Trace.TRACE_TAG_APP, "WallpaperAlphaAnimation", 0)
-                }
-            })
+            )
         }
 
         if (fasterUnlockTransition()) {
@@ -405,7 +415,9 @@
                 interpolator = Interpolators.LINEAR
                 addUpdateListener { valueAnimator: ValueAnimator ->
                     setWallpaperAppearAmount(
-                            valueAnimator.animatedValue as Float, closingWallpaperTargets)
+                        valueAnimator.animatedValue as Float,
+                        closingWallpaperTargets
+                    )
                 }
             }
         }
@@ -418,15 +430,19 @@
                 surfaceBehindAlpha = valueAnimator.animatedValue as Float
                 setSurfaceBehindAppearAmount(valueAnimator.animatedValue as Float)
             }
-            addListener(object : AnimatorListenerAdapter() {
-                override fun onAnimationEnd(animation: Animator) {
-                    Log.d(TAG, "surfaceBehindEntryAnimator#onAnimationEnd")
-                    playingCannedUnlockAnimation = false
-                    keyguardViewMediator.get().exitKeyguardAndFinishSurfaceBehindRemoteAnimation(
-                        false /* cancelled */
-                    )
+            addListener(
+                object : AnimatorListenerAdapter() {
+                    override fun onAnimationEnd(animation: Animator) {
+                        Log.d(TAG, "surfaceBehindEntryAnimator#onAnimationEnd")
+                        playingCannedUnlockAnimation = false
+                        keyguardViewMediator
+                            .get()
+                            .exitKeyguardAndFinishSurfaceBehindRemoteAnimation(
+                                false /* cancelled */
+                            )
+                    }
                 }
-            })
+            )
         }
 
         // Listen for changes in the dismiss amount.
@@ -436,9 +452,7 @@
             resources.getDimensionPixelSize(R.dimen.rounded_corner_radius).toFloat()
     }
 
-    /**
-     * Add a listener to be notified of various stages of the unlock animation.
-     */
+    /** Add a listener to be notified of various stages of the unlock animation. */
     fun addKeyguardUnlockAnimationListener(listener: KeyguardUnlockAnimationListener) {
         listeners.add(listener)
     }
@@ -454,11 +468,11 @@
     fun canPerformInWindowLauncherAnimations(): Boolean {
         // TODO(b/278086361): Refactor in-window animations.
         return !KeyguardWmStateRefactor.isEnabled &&
-                isSupportedLauncherUnderneath() &&
-                // If the launcher is underneath, but we're about to launch an activity, don't do
-                // the animations since they won't be visible.
-                !notificationShadeWindowController.isLaunchingActivity &&
-                launcherUnlockController != null
+            isSupportedLauncherUnderneath() &&
+            // If the launcher is underneath, but we're about to launch an activity, don't do
+            // the animations since they won't be visible.
+            !notificationShadeWindowController.isLaunchingActivity &&
+            launcherUnlockController != null
     }
 
     /**
@@ -469,8 +483,11 @@
     private fun logInWindowAnimationConditions() {
         Log.wtf(TAG, "canPerformInWindowLauncherAnimations expected all of these to be true: ")
         Log.wtf(TAG, "  isNexusLauncherUnderneath: ${isSupportedLauncherUnderneath()}")
-        Log.wtf(TAG, "  !notificationShadeWindowController.isLaunchingActivity: " +
-                "${!notificationShadeWindowController.isLaunchingActivity}")
+        Log.wtf(
+            TAG,
+            "  !notificationShadeWindowController.isLaunchingActivity: " +
+                "${!notificationShadeWindowController.isLaunchingActivity}"
+        )
         Log.wtf(TAG, "  launcherUnlockController != null: ${launcherUnlockController != null}")
         Log.wtf(TAG, "  !isFoldable(context): ${!isFoldable(resources)}")
     }
@@ -480,8 +497,10 @@
      * changed.
      */
     override fun onKeyguardGoingAwayChanged() {
-        if (keyguardStateController.isKeyguardGoingAway &&
-                !statusBarStateController.leaveOpenOnKeyguardHide()) {
+        if (
+            keyguardStateController.isKeyguardGoingAway &&
+                !statusBarStateController.leaveOpenOnKeyguardHide()
+        ) {
             prepareForInWindowLauncherAnimations()
         }
 
@@ -489,16 +508,22 @@
         // make sure that we've left the launcher at 100% unlocked. This is a fail-safe to prevent
         // against "tiny launcher" and similar states where the launcher is left in the prepared to
         // animate state.
-        if (!keyguardStateController.isKeyguardGoingAway &&
-                willUnlockWithInWindowLauncherAnimations) {
+        if (
+            !keyguardStateController.isKeyguardGoingAway && willUnlockWithInWindowLauncherAnimations
+        ) {
             try {
-                launcherUnlockController?.setUnlockAmount(1f,
-                        biometricUnlockControllerLazy.get().isWakeAndUnlock /* forceIfAnimating */)
+                launcherUnlockController?.setUnlockAmount(
+                    1f,
+                    biometricUnlockControllerLazy.get().isWakeAndUnlock /* forceIfAnimating */
+                )
             } catch (e: DeadObjectException) {
-                Log.e(TAG, "launcherUnlockAnimationController was dead, but non-null in " +
+                Log.e(
+                    TAG,
+                    "launcherUnlockAnimationController was dead, but non-null in " +
                         "onKeyguardGoingAwayChanged(). Catching exception as this should mean " +
                         "Launcher is in the process of being destroyed, but the IPC to System UI " +
-                        "telling us hasn't arrived yet.")
+                        "telling us hasn't arrived yet."
+                )
             }
         }
     }
@@ -525,22 +550,26 @@
         // Grab the bounds of our lockscreen smartspace and send them to launcher so they can
         // position their smartspace there initially, then animate it to its resting position.
         if (willUnlockWithSmartspaceTransition) {
-            lockscreenSmartspaceBounds = Rect().apply {
-                lockscreenSmartspace!!.getBoundsOnScreen(this)
+            lockscreenSmartspaceBounds =
+                Rect().apply {
+                    lockscreenSmartspace!!.getBoundsOnScreen(this)
 
-                // The smartspace container on the lockscreen has left and top padding to align it
-                // with other lockscreen content. This padding is inside the bounds on screen, so
-                // add it to those bounds so that the padding-less launcher smartspace is properly
-                // aligned.
-                offset(lockscreenSmartspace!!.paddingLeft, lockscreenSmartspace!!.paddingTop)
+                    // The smartspace container on the lockscreen has left and top padding to align
+                    // it with other lockscreen content. This padding is inside the bounds on
+                    // screen, so add it to those bounds so that the padding-less launcher
+                    // smartspace is properly aligned.
+                    offset(lockscreenSmartspace!!.paddingLeft, lockscreenSmartspace!!.paddingTop)
 
-                // Also offset by the current card's top padding, if it has any. This allows us to
-                // align the tops of the lockscreen/launcher smartspace cards. Some cards, such as
-                // the three-line date/weather/alarm card, only have three lines on lockscreen but
-                // two on launcher.
-                offset(0, (lockscreenSmartspace
-                        as? BcSmartspaceDataPlugin.SmartspaceView)?.currentCardTopPadding ?: 0)
-            }
+                    // Also offset by the current card's top padding, if it has any. This allows us
+                    // to align the tops of the lockscreen/launcher smartspace cards. Some cards,
+                    // such as the three-line date/weather/alarm card, only have three lines on
+                    // lockscreen but two on launcher.
+                    offset(
+                        0,
+                        (lockscreenSmartspace as? BcSmartspaceDataPlugin.SmartspaceView)
+                            ?.currentCardTopPadding ?: 0
+                    )
+                }
         }
 
         // Currently selected lockscreen smartspace page, or -1 if it's not available.
@@ -583,8 +612,8 @@
         requestedShowSurfaceBehindKeyguard: Boolean
     ) {
         if (surfaceTransactionApplier == null) {
-            surfaceTransactionApplier = SyncRtSurfaceTransactionApplier(
-                    keyguardViewController.viewRootImpl.view)
+            surfaceTransactionApplier =
+                SyncRtSurfaceTransactionApplier(keyguardViewController.viewRootImpl.view)
         }
 
         surfaceBehindRemoteAnimationTargets = targets
@@ -603,8 +632,10 @@
             // surface behind the keyguard to finish unlocking.
             if (keyguardStateController.isFlingingToDismissKeyguard) {
                 playCannedUnlockAnimation()
-            } else if (keyguardStateController.isDismissingFromSwipe &&
-                    willUnlockWithInWindowLauncherAnimations) {
+            } else if (
+                keyguardStateController.isDismissingFromSwipe &&
+                    willUnlockWithInWindowLauncherAnimations
+            ) {
                 // If we're swiping to unlock to the Launcher, and can play in-window animations,
                 // make the launcher surface fully visible and play the in-window unlock animation
                 // on the launcher icons. System UI will remain locked, using the swipe-to-unlock
@@ -615,19 +646,23 @@
 
                 try {
                     launcherUnlockController?.playUnlockAnimation(
-                            true,
-                            unlockAnimationDurationMs() + cannedUnlockStartDelayMs(),
-                            0 /* startDelay */)
+                        true,
+                        unlockAnimationDurationMs() + cannedUnlockStartDelayMs(),
+                        0 /* startDelay */
+                    )
                 } catch (e: DeadObjectException) {
                     // Hello! If you are here investigating a bug where Launcher is blank (no icons)
                     // then the below assumption about Launcher's destruction was incorrect. This
                     // would mean prepareToUnlock was called (blanking Launcher in preparation for
                     // the beginning of the unlock animation), but then somehow we were unable to
                     // call playUnlockAnimation to animate the icons back in.
-                    Log.e(TAG, "launcherUnlockAnimationController was dead, but non-null. " +
+                    Log.e(
+                        TAG,
+                        "launcherUnlockAnimationController was dead, but non-null. " +
                             "Catching exception as this should mean Launcher is in the process " +
                             "of being destroyed, but the IPC to System UI telling us hasn't " +
-                            "arrived yet.")
+                            "arrived yet."
+                    )
                 }
 
                 launcherPreparedForUnlock = false
@@ -643,15 +678,18 @@
         }
 
         // Notify if waking from AOD only
-        val isWakeAndUnlockNotFromDream = biometricUnlockControllerLazy.get().isWakeAndUnlock &&
-            biometricUnlockControllerLazy.get().mode != MODE_WAKE_AND_UNLOCK_FROM_DREAM
+        val isWakeAndUnlockNotFromDream =
+            biometricUnlockControllerLazy.get().isWakeAndUnlock &&
+                biometricUnlockControllerLazy.get().mode != MODE_WAKE_AND_UNLOCK_FROM_DREAM
 
         listeners.forEach {
             it.onUnlockAnimationStarted(
                 playingCannedUnlockAnimation /* playingCannedAnimation */,
                 isWakeAndUnlockNotFromDream /* isWakeAndUnlockNotFromDream */,
                 cannedUnlockStartDelayMs() /* unlockStartDelay */,
-                LAUNCHER_ICONS_ANIMATION_DURATION_MS /* unlockAnimationDuration */) }
+                LAUNCHER_ICONS_ANIMATION_DURATION_MS /* unlockAnimationDuration */
+            )
+        }
 
         // Finish the keyguard remote animation if the dismiss amount has crossed the threshold.
         // Check it here in case there is no more change to the dismiss amount after the last change
@@ -685,8 +723,9 @@
             biometricUnlockControllerLazy.get().isWakeAndUnlock -> {
                 Log.d(TAG, "playCannedUnlockAnimation, isWakeAndUnlock")
                 setSurfaceBehindAppearAmount(1f)
-                keyguardViewMediator.get().exitKeyguardAndFinishSurfaceBehindRemoteAnimation(
-                    false /* cancelled */)
+                keyguardViewMediator
+                    .get()
+                    .exitKeyguardAndFinishSurfaceBehindRemoteAnimation(false /* cancelled */)
             }
 
             // Otherwise, we're doing a normal full-window unlock. Start this animator, which will
@@ -698,8 +737,11 @@
         }
 
         if (launcherPreparedForUnlock && !willUnlockWithInWindowLauncherAnimations) {
-            Log.wtf(TAG, "Launcher is prepared for unlock, so we should have started the " +
-                    "in-window animation, however we apparently did not.")
+            Log.wtf(
+                TAG,
+                "Launcher is prepared for unlock, so we should have started the " +
+                    "in-window animation, however we apparently did not."
+            )
             logInWindowAnimationConditions()
         }
     }
@@ -708,7 +750,6 @@
      * Unlock to the launcher, using in-window animations, and the smartspace shared element
      * transition if possible.
      */
-
     @VisibleForTesting
     fun unlockToLauncherWithInWindowAnimations() {
         surfaceBehindAlpha = 1f
@@ -717,26 +758,32 @@
         try {
             // Begin the animation, waiting for the shade to animate out.
             launcherUnlockController?.playUnlockAnimation(
-                    true /* unlocked */,
-                    LAUNCHER_ICONS_ANIMATION_DURATION_MS /* duration */,
-                    cannedUnlockStartDelayMs() /* startDelay */)
+                true /* unlocked */,
+                LAUNCHER_ICONS_ANIMATION_DURATION_MS /* duration */,
+                cannedUnlockStartDelayMs() /* startDelay */
+            )
         } catch (e: DeadObjectException) {
             // Hello! If you are here investigating a bug where Launcher is blank (no icons)
             // then the below assumption about Launcher's destruction was incorrect. This
             // would mean prepareToUnlock was called (blanking Launcher in preparation for
             // the beginning of the unlock animation), but then somehow we were unable to
             // call playUnlockAnimation to animate the icons back in.
-            Log.e(TAG, "launcherUnlockAnimationController was dead, but non-null. " +
+            Log.e(
+                TAG,
+                "launcherUnlockAnimationController was dead, but non-null. " +
                     "Catching exception as this should mean Launcher is in the process " +
                     "of being destroyed, but the IPC to System UI telling us hasn't " +
-                    "arrived yet.")
+                    "arrived yet."
+            )
         }
 
         launcherPreparedForUnlock = false
 
         // Now that the Launcher surface (with its smartspace positioned identically to ours) is
         // visible, hide our smartspace.
-        if (lockscreenSmartspace?.visibility == View.VISIBLE) {
+        if (
+            shouldPerformSmartspaceTransition() && lockscreenSmartspace?.visibility == View.VISIBLE
+        ) {
             lockscreenSmartspace?.visibility = View.INVISIBLE
         }
 
@@ -747,22 +794,31 @@
             fadeOutWallpaper()
         }
 
-        handler.postDelayed({
-            if (keyguardViewMediator.get().isShowingAndNotOccluded &&
-                !keyguardStateController.isKeyguardGoingAway) {
-                    Log.e(TAG, "Finish keyguard exit animation delayed Runnable ran, but we are " +
-                            "showing and not going away.")
-                return@postDelayed
-            }
+        handler.postDelayed(
+            {
+                if (
+                    keyguardViewMediator.get().isShowingAndNotOccluded &&
+                        !keyguardStateController.isKeyguardGoingAway
+                ) {
+                    Log.e(
+                        TAG,
+                        "Finish keyguard exit animation delayed Runnable ran, but we are " +
+                            "showing and not going away."
+                    )
+                    return@postDelayed
+                }
 
-            if (openingWallpaperTargets?.isNotEmpty() == true) {
-                fadeInWallpaper()
-                hideKeyguardViewAfterRemoteAnimation()
-            } else {
-                keyguardViewMediator.get().exitKeyguardAndFinishSurfaceBehindRemoteAnimation(
-                    false /* cancelled */)
-            }
-        }, cannedUnlockStartDelayMs())
+                if (openingWallpaperTargets?.isNotEmpty() == true) {
+                    fadeInWallpaper()
+                    hideKeyguardViewAfterRemoteAnimation()
+                } else {
+                    keyguardViewMediator
+                        .get()
+                        .exitKeyguardAndFinishSurfaceBehindRemoteAnimation(false /* cancelled */)
+                }
+            },
+            cannedUnlockStartDelayMs()
+        )
     }
 
     /**
@@ -784,12 +840,14 @@
         // interaction tight.
         if (keyguardStateController.isFlingingToDismissKeyguard) {
             setSurfaceBehindAppearAmount(keyguardStateController.dismissAmount)
-        } else if (keyguardStateController.isDismissingFromSwipe ||
-                keyguardStateController.isSnappingKeyguardBackAfterSwipe) {
+        } else if (
+            keyguardStateController.isDismissingFromSwipe ||
+                keyguardStateController.isSnappingKeyguardBackAfterSwipe
+        ) {
             val totalSwipeDistanceToDismiss =
-                    (DISMISS_AMOUNT_EXIT_KEYGUARD_THRESHOLD - DISMISS_AMOUNT_SHOW_SURFACE_THRESHOLD)
+                (DISMISS_AMOUNT_EXIT_KEYGUARD_THRESHOLD - DISMISS_AMOUNT_SHOW_SURFACE_THRESHOLD)
             val swipedDistanceSoFar: Float =
-                    keyguardStateController.dismissAmount - DISMISS_AMOUNT_SHOW_SURFACE_THRESHOLD
+                keyguardStateController.dismissAmount - DISMISS_AMOUNT_SHOW_SURFACE_THRESHOLD
             val progress = swipedDistanceSoFar / totalSwipeDistanceToDismiss
             setSurfaceBehindAppearAmount(progress)
         }
@@ -801,10 +859,13 @@
 
             // If the surface is visible or it's about to be, start updating its appearance to
             // reflect the new dismiss amount.
-            if ((keyguardViewMediator.get().requestedShowSurfaceBehindKeyguard() ||
-                    keyguardViewMediator.get()
+            if (
+                (keyguardViewMediator.get().requestedShowSurfaceBehindKeyguard() ||
+                    keyguardViewMediator
+                        .get()
                         .isAnimatingBetweenKeyguardAndSurfaceBehindOrWillBe) &&
-                    !playingCannedUnlockAnimation) {
+                    !playingCannedUnlockAnimation
+            ) {
                 updateSurfaceBehindAppearAmount()
             }
         }
@@ -838,11 +899,15 @@
 
         val dismissAmount = keyguardStateController.dismissAmount
 
-        if (dismissAmount >= DISMISS_AMOUNT_SHOW_SURFACE_THRESHOLD &&
-            !keyguardViewMediator.get().requestedShowSurfaceBehindKeyguard()) {
+        if (
+            dismissAmount >= DISMISS_AMOUNT_SHOW_SURFACE_THRESHOLD &&
+                !keyguardViewMediator.get().requestedShowSurfaceBehindKeyguard()
+        ) {
             keyguardViewMediator.get().showSurfaceBehindKeyguard()
-        } else if (dismissAmount < DISMISS_AMOUNT_SHOW_SURFACE_THRESHOLD &&
-                keyguardViewMediator.get().requestedShowSurfaceBehindKeyguard()) {
+        } else if (
+            dismissAmount < DISMISS_AMOUNT_SHOW_SURFACE_THRESHOLD &&
+                keyguardViewMediator.get().requestedShowSurfaceBehindKeyguard()
+        ) {
             // We're no longer past the threshold but we are showing the surface. Animate it
             // out.
             keyguardViewMediator.get().hideSurfaceBehindKeyguard()
@@ -868,22 +933,27 @@
         }
 
         // no-op if animation is not requested yet.
-        if (!keyguardViewMediator.get().requestedShowSurfaceBehindKeyguard() ||
-                !keyguardViewMediator.get().isAnimatingBetweenKeyguardAndSurfaceBehindOrWillBe) {
+        if (
+            !keyguardViewMediator.get().requestedShowSurfaceBehindKeyguard() ||
+                !keyguardViewMediator.get().isAnimatingBetweenKeyguardAndSurfaceBehindOrWillBe
+        ) {
             return
         }
 
         val dismissAmount = keyguardStateController.dismissAmount
-        if (dismissAmount >= 1f ||
+        if (
+            dismissAmount >= 1f ||
                 (keyguardStateController.isDismissingFromSwipe &&
-                        // Don't hide if we're flinging during a swipe, since we need to finish
-                        // animating it out. This will be called again after the fling ends.
-                        !keyguardStateController.isFlingingToDismissKeyguardDuringSwipeGesture &&
-                        dismissAmount >= DISMISS_AMOUNT_EXIT_KEYGUARD_THRESHOLD)) {
+                    // Don't hide if we're flinging during a swipe, since we need to finish
+                    // animating it out. This will be called again after the fling ends.
+                    !keyguardStateController.isFlingingToDismissKeyguardDuringSwipeGesture &&
+                    dismissAmount >= DISMISS_AMOUNT_EXIT_KEYGUARD_THRESHOLD)
+        ) {
             setSurfaceBehindAppearAmount(1f)
             dismissAmountThresholdsReached = true
-            keyguardViewMediator.get().exitKeyguardAndFinishSurfaceBehindRemoteAnimation(
-                    false /* cancelled */)
+            keyguardViewMediator
+                .get()
+                .exitKeyguardAndFinishSurfaceBehindRemoteAnimation(false /* cancelled */)
         }
     }
 
@@ -894,51 +964,56 @@
      * wallpapers, this transitions between the two wallpapers
      */
     fun setSurfaceBehindAppearAmount(amount: Float, wallpapers: Boolean = true) {
-        val animationAlpha = when {
-            // If we're snapping the keyguard back, immediately begin fading it out.
-            keyguardStateController.isSnappingKeyguardBackAfterSwipe -> amount
-            // If the screen has turned back off, the unlock animation is going to be cancelled,
-            // so set the surface alpha to 0f so it's no longer visible.
-            !powerManager.isInteractive -> 0f
-            else -> surfaceBehindAlpha
-        }
+        val animationAlpha =
+            when {
+                // If we're snapping the keyguard back, immediately begin fading it out.
+                keyguardStateController.isSnappingKeyguardBackAfterSwipe -> amount
+                // If the screen has turned back off, the unlock animation is going to be cancelled,
+                // so set the surface alpha to 0f so it's no longer visible.
+                !powerManager.isInteractive -> 0f
+                else -> surfaceBehindAlpha
+            }
 
         surfaceBehindRemoteAnimationTargets?.forEach { surfaceBehindRemoteAnimationTarget ->
             if (!KeyguardWmStateRefactor.isEnabled) {
                 val surfaceHeight: Int =
-                        surfaceBehindRemoteAnimationTarget.screenSpaceBounds.height()
+                    surfaceBehindRemoteAnimationTarget.screenSpaceBounds.height()
 
-                var scaleFactor = (SURFACE_BEHIND_START_SCALE_FACTOR +
-                        (1f - SURFACE_BEHIND_START_SCALE_FACTOR) *
-                        MathUtils.clamp(amount, 0f, 1f))
+                var scaleFactor =
+                    (SURFACE_BEHIND_START_SCALE_FACTOR +
+                        (1f - SURFACE_BEHIND_START_SCALE_FACTOR) * MathUtils.clamp(amount, 0f, 1f))
 
                 // If we're dismissing via swipe to the Launcher, we'll play in-window scale
                 // animations, so don't also scale the window.
-                if (keyguardStateController.isDismissingFromSwipe &&
-                        willUnlockWithInWindowLauncherAnimations) {
+                if (
+                    keyguardStateController.isDismissingFromSwipe &&
+                        willUnlockWithInWindowLauncherAnimations
+                ) {
                     scaleFactor = 1f
                 }
 
                 // Translate up from the bottom.
                 surfaceBehindMatrix.setTranslate(
-                        surfaceBehindRemoteAnimationTarget.screenSpaceBounds.left.toFloat(),
-                        surfaceBehindRemoteAnimationTarget.screenSpaceBounds.top.toFloat() +
-                                surfaceHeight * SURFACE_BEHIND_START_TRANSLATION_Y * (1f - amount)
+                    surfaceBehindRemoteAnimationTarget.screenSpaceBounds.left.toFloat(),
+                    surfaceBehindRemoteAnimationTarget.screenSpaceBounds.top.toFloat() +
+                        surfaceHeight * SURFACE_BEHIND_START_TRANSLATION_Y * (1f - amount)
                 )
 
                 // Scale up from a point at the center-bottom of the surface.
                 surfaceBehindMatrix.postScale(
-                        scaleFactor,
-                        scaleFactor,
-                        keyguardViewController.viewRootImpl.width / 2f,
-                        surfaceHeight * SURFACE_BEHIND_SCALE_PIVOT_Y
+                    scaleFactor,
+                    scaleFactor,
+                    keyguardViewController.viewRootImpl.width / 2f,
+                    surfaceHeight * SURFACE_BEHIND_SCALE_PIVOT_Y
                 )
 
                 // SyncRtSurfaceTransactionApplier cannot apply transaction when the target view is
                 // unable to draw
                 val sc: SurfaceControl? = surfaceBehindRemoteAnimationTarget.leash
-                if (keyguardViewController.viewRootImpl.view?.visibility != View.VISIBLE &&
-                        sc?.isValid == true) {
+                if (
+                    keyguardViewController.viewRootImpl.view?.visibility != View.VISIBLE &&
+                        sc?.isValid == true
+                ) {
                     with(SurfaceControl.Transaction()) {
                         setMatrix(sc, surfaceBehindMatrix, tmpFloat)
                         setCornerRadius(sc, roundedCornerRadius)
@@ -947,12 +1022,13 @@
                     }
                 } else {
                     applyParamsToSurface(
-                            SyncRtSurfaceTransactionApplier.SurfaceParams.Builder(
-                                    surfaceBehindRemoteAnimationTarget.leash)
-                                    .withMatrix(surfaceBehindMatrix)
-                                    .withCornerRadius(roundedCornerRadius)
-                                    .withAlpha(animationAlpha)
-                                    .build()
+                        SyncRtSurfaceTransactionApplier.SurfaceParams.Builder(
+                                surfaceBehindRemoteAnimationTarget.leash
+                            )
+                            .withMatrix(surfaceBehindMatrix)
+                            .withCornerRadius(roundedCornerRadius)
+                            .withAlpha(animationAlpha)
+                            .build()
                     )
                 }
             }
@@ -969,8 +1045,8 @@
 
                 val fadeOutStart = LOCK_WALLPAPER_FADE_OUT_START_DELAY / total
                 val fadeOutEnd = fadeOutStart + LOCK_WALLPAPER_FADE_OUT_DURATION / total
-                val fadeOutAmount = ((amount - fadeOutStart) / (fadeOutEnd - fadeOutStart))
-                        .coerceIn(0f, 1f)
+                val fadeOutAmount =
+                    ((amount - fadeOutStart) / (fadeOutEnd - fadeOutStart)).coerceIn(0f, 1f)
 
                 setWallpaperAppearAmount(fadeInAmount, openingWallpaperTargets)
                 setWallpaperAppearAmount(1 - fadeOutAmount, closingWallpaperTargets)
@@ -984,18 +1060,19 @@
             // SyncRtSurfaceTransactionApplier cannot apply transaction when the target view is
             // unable to draw
             val sc: SurfaceControl? = wallpaper.leash
-            if (keyguardViewController.viewRootImpl.view?.visibility != View.VISIBLE &&
-                    sc?.isValid == true) {
+            if (
+                keyguardViewController.viewRootImpl.view?.visibility != View.VISIBLE &&
+                    sc?.isValid == true
+            ) {
                 with(SurfaceControl.Transaction()) {
                     setAlpha(sc, animationAlpha)
                     apply()
                 }
             } else {
                 applyParamsToSurface(
-                        SyncRtSurfaceTransactionApplier.SurfaceParams.Builder(
-                                wallpaper.leash)
-                                .withAlpha(animationAlpha)
-                                .build()
+                    SyncRtSurfaceTransactionApplier.SurfaceParams.Builder(wallpaper.leash)
+                        .withAlpha(animationAlpha)
+                        .build()
                 )
             }
         }
@@ -1019,9 +1096,9 @@
         }
 
         if (!showKeyguard) {
-            // Make sure we made the surface behind fully visible, just in case. It should already be
-            // fully visible. The exit animation is finished, and we should not hold the leash anymore,
-            // so forcing it to 1f.
+            // Make sure we made the surface behind fully visible, just in case. It should already
+            // be fully visible. The exit animation is finished, and we should not hold the leash
+            // anymore, so forcing it to 1f.
             surfaceBehindAlpha = 1f
             setSurfaceBehindAppearAmount(1f)
 
@@ -1061,13 +1138,16 @@
 
             if (!KeyguardWmStateRefactor.isEnabled) {
                 keyguardViewController.hide(
-                        surfaceBehindRemoteAnimationStartTime,
-                        0 /* fadeOutDuration */
+                    surfaceBehindRemoteAnimationStartTime,
+                    0 /* fadeOutDuration */
                 )
             }
         } else {
-            Log.i(TAG, "#hideKeyguardViewAfterRemoteAnimation called when keyguard view is not " +
-                    "showing. Ignoring...")
+            Log.i(
+                TAG,
+                "#hideKeyguardViewAfterRemoteAnimation called when keyguard view is not " +
+                    "showing. Ignoring..."
+            )
         }
     }
 
@@ -1099,7 +1179,8 @@
         surfaceBehindAlphaAnimator.reverse()
     }
 
-    private fun shouldPerformSmartspaceTransition(): Boolean {
+    /** Note: declared open for ease of testing */
+    open fun shouldPerformSmartspaceTransition(): Boolean {
         // Feature is disabled, so we don't want to.
         if (!featureFlags.isEnabled(Flags.SMARTSPACE_SHARED_ELEMENT_TRANSITION_ENABLED)) {
             return false
@@ -1107,9 +1188,11 @@
 
         // If our controllers are null, or we haven't received a smartspace state from Launcher yet,
         // we will not be doing any smartspace transitions today.
-        if (launcherUnlockController == null ||
-            lockscreenSmartspace == null ||
-            launcherSmartspaceState == null) {
+        if (
+            launcherUnlockController == null ||
+                lockscreenSmartspace == null ||
+                launcherSmartspaceState == null
+        ) {
             return false
         }
 
@@ -1135,8 +1218,10 @@
         // element transition is if we're doing a biometric unlock. Otherwise, it means the bouncer
         // is showing, and you can't see the lockscreen smartspace, so a shared element transition
         // would not make sense.
-        if (!keyguardStateController.canDismissLockScreen() &&
-            !biometricUnlockControllerLazy.get().isBiometricUnlock) {
+        if (
+            !keyguardStateController.canDismissLockScreen() &&
+                !biometricUnlockControllerLazy.get().isBiometricUnlock
+        ) {
             return false
         }
 
@@ -1175,9 +1260,7 @@
         return willUnlockWithSmartspaceTransition
     }
 
-    /**
-     * Whether the RemoteAnimation on the app/launcher surface behind the keyguard is 'running'.
-     */
+    /** Whether the RemoteAnimation on the app/launcher surface behind the keyguard is 'running'. */
     fun isAnimatingBetweenKeyguardAndSurfaceBehind(): Boolean {
         return keyguardViewMediator.get().isAnimatingBetweenKeyguardAndSurfaceBehind
     }
@@ -1196,39 +1279,38 @@
      * in-window/shared element transitions!
      */
     fun isSupportedLauncherUnderneath(): Boolean {
-        return launcherActivityClass?.let { ActivityManagerWrapper.getInstance()
-                .runningTask?.topActivity?.className?.equals(it) }
-                ?: false
+        return launcherActivityClass?.let {
+            ActivityManagerWrapper.getInstance().runningTask?.topActivity?.className?.equals(it)
+        } ?: false
     }
 
     /**
-     * Temporary method for b/298186160
-     * TODO (b/298186160) replace references with the constant itself when flag is removed
+     * Temporary method for b/298186160 TODO (b/298186160) replace references with the constant
+     * itself when flag is removed
      */
     private fun cannedUnlockStartDelayMs(): Long {
         return if (fasterUnlockTransition()) CANNED_UNLOCK_START_DELAY
-                else LEGACY_CANNED_UNLOCK_START_DELAY
+        else LEGACY_CANNED_UNLOCK_START_DELAY
     }
 
     /**
-     * Temporary method for b/298186160
-     * TODO (b/298186160) replace references with the constant itself when flag is removed
+     * Temporary method for b/298186160 TODO (b/298186160) replace references with the constant
+     * itself when flag is removed
      */
     private fun unlockAnimationDurationMs(): Long {
         return if (fasterUnlockTransition()) UNLOCK_ANIMATION_DURATION_MS
-                else LEGACY_UNLOCK_ANIMATION_DURATION_MS
+        else LEGACY_UNLOCK_ANIMATION_DURATION_MS
     }
 
     /**
-     * Temporary method for b/298186160
-     * TODO (b/298186160) replace references with the constant itself when flag is removed
+     * Temporary method for b/298186160 TODO (b/298186160) replace references with the constant
+     * itself when flag is removed
      */
     private fun surfaceBehindFadeOutStartDelayMs(): Long {
         return if (fasterUnlockTransition()) UNLOCK_ANIMATION_SURFACE_BEHIND_START_DELAY_MS
-                else LEGACY_UNLOCK_ANIMATION_SURFACE_BEHIND_START_DELAY_MS
+        else LEGACY_UNLOCK_ANIMATION_SURFACE_BEHIND_START_DELAY_MS
     }
 
-
     companion object {
 
         fun isFoldable(resources: Resources): Boolean {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index e46a7cb..0b3d0f7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -1278,6 +1278,8 @@
                             initAlphaForAnimationTargets(wallpapers);
                             if (isDream) {
                                 mDreamViewModel.get().startTransitionFromDream();
+                            } else {
+                                mCommunalTransitionViewModel.get().snapToCommunal();
                             }
                             mUnoccludeFinishedCallback = finishedCallback;
                             return;
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 ae751db..edf17c1 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
@@ -34,6 +34,7 @@
 import com.android.systemui.keyguard.shared.model.BiometricUnlockMode
 import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
 import com.android.systemui.keyguard.shared.model.BiometricUnlockSource
+import com.android.systemui.keyguard.shared.model.CameraLaunchSourceModel
 import com.android.systemui.keyguard.shared.model.DismissAction
 import com.android.systemui.keyguard.shared.model.DozeStateModel
 import com.android.systemui.keyguard.shared.model.DozeTransitionModel
@@ -237,6 +238,9 @@
     /** Observable updated when keyguardDone should be called either now or soon. */
     val keyguardDone: Flow<KeyguardDone>
 
+    /** Last camera launch detection event */
+    val onCameraLaunchDetected: MutableStateFlow<CameraLaunchSourceModel>
+
     /**
      * Emits after the keyguard is done animating away.
      *
@@ -380,6 +384,8 @@
     private val _keyguardAlpha = MutableStateFlow(1f)
     override val keyguardAlpha = _keyguardAlpha.asStateFlow()
 
+    override val onCameraLaunchDetected = MutableStateFlow(CameraLaunchSourceModel())
+
     override val panelAlpha: MutableStateFlow<Float> = MutableStateFlow(1f)
 
     private val _clockShouldBeCentered = MutableStateFlow(true)
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 aaeeb39..de60c11 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
@@ -105,7 +105,7 @@
      * When the transition is over, TransitionState.FINISHED must be passed into the [state]
      * parameter.
      */
-    fun updateTransition(
+    suspend fun updateTransition(
         transitionId: UUID,
         @FloatRange(from = 0.0, to = 1.0) value: Float,
         state: TransitionState
@@ -173,9 +173,9 @@
         Log.d(TAG, "(Internal) Setting current transition info: $info")
 
         // There is no fairness guarantee with 'withContext', which means that transitions could
-        // be processed out of order. Use a Mutex to guarantee ordering.
+        // be processed out of order. Use a Mutex to guarantee ordering. [updateTransition]
+        // requires the same lock
         _currentTransitionMutex.lock()
-
         // Only used in a test environment
         if (forceDelayForRaceConditionTest) {
             delay(50L)
@@ -184,7 +184,6 @@
         // Animators must be started on the main thread.
         return withContext("$TAG#startTransition", mainDispatcher) {
             _currentTransitionMutex.unlock()
-
             if (lastStep.from == info.from && lastStep.to == info.to) {
                 Log.i(TAG, "Duplicate call to start the transition, rejecting: $info")
                 return@withContext null
@@ -206,7 +205,7 @@
 
             // Cancel any existing manual transitions
             updateTransitionId?.let { uuid ->
-                updateTransition(uuid, lastStep.value, TransitionState.CANCELED)
+                updateTransitionInternal(uuid, lastStep.value, TransitionState.CANCELED)
             }
 
             info.animator?.let { animator ->
@@ -264,7 +263,23 @@
         }
     }
 
-    override fun updateTransition(
+    override suspend fun updateTransition(
+        transitionId: UUID,
+        @FloatRange(from = 0.0, to = 1.0) value: Float,
+        state: TransitionState
+    ) {
+        // There is no fairness guarantee with 'withContext', which means that transitions could
+        // be processed out of order. Use a Mutex to guarantee ordering. [startTransition]
+        // requires the same lock
+        _currentTransitionMutex.lock()
+        withContext("$TAG#updateTransition", mainDispatcher) {
+            _currentTransitionMutex.unlock()
+
+            updateTransitionInternal(transitionId, value, state)
+        }
+    }
+
+    private suspend fun updateTransitionInternal(
         transitionId: UUID,
         @FloatRange(from = 0.0, to = 1.0) value: Float,
         state: TransitionState
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt
index b44a8cf..ae830ee 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt
@@ -26,7 +26,6 @@
 import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
 import com.android.systemui.keyguard.shared.model.Edge
 import com.android.systemui.keyguard.shared.model.KeyguardState
-import com.android.systemui.keyguard.shared.model.TransitionStep
 import com.android.systemui.power.domain.interactor.PowerInteractor
 import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import com.android.systemui.scene.shared.model.Scenes
@@ -91,12 +90,12 @@
                 edgeWithoutSceneContainer =
                     Edge.create(from = KeyguardState.ALTERNATE_BOUNCER, to = KeyguardState.GONE)
             )
-            .map<TransitionStep, Boolean?> {
+            .map {
                 // The alt bouncer is pretty fast to hide, so start the surface behind animation
                 // around 30%.
                 it.value > 0.3f
             }
-            .onStart {
+            .onStart<Boolean?> {
                 // Default to null ("don't care, use a reasonable default").
                 emit(null)
             }
@@ -145,6 +144,7 @@
                             }
                         } else {
                             if (isIdleOnCommunal) {
+                                if (SceneContainerFlag.isEnabled) return@collect
                                 KeyguardState.GLANCEABLE_HUB
                             } else if (isOccluded) {
                                 KeyguardState.OCCLUDED
@@ -158,7 +158,6 @@
     }
 
     private fun listenForAlternateBouncerToGone() {
-        // TODO(b/336576536): Check if adaptation for scene framework is needed
         if (SceneContainerFlag.isEnabled) return
         if (KeyguardWmStateRefactor.isEnabled) {
             // Handled via #dismissAlternateBouncer.
@@ -170,9 +169,9 @@
                     keyguardInteractor.isKeyguardGoingAway.filter { it }.map {}, // map to Unit
                     keyguardInteractor.isKeyguardOccluded.flatMapLatest { keyguardOccluded ->
                         if (keyguardOccluded) {
-                            primaryBouncerInteractor.keyguardAuthenticatedBiometricsHandled.drop(
-                                1
-                            ) // drop the initial state
+                            primaryBouncerInteractor.keyguardAuthenticatedBiometricsHandled
+                                // drop the initial state
+                                .drop(1)
                         } else {
                             emptyFlow()
                         }
@@ -184,7 +183,6 @@
     }
 
     private fun listenForAlternateBouncerToPrimaryBouncer() {
-        // TODO(b/336576536): Check if adaptation for scene framework is needed
         if (SceneContainerFlag.isEnabled) return
         scope.launch {
             keyguardInteractor.primaryBouncerShowing
@@ -200,7 +198,12 @@
             interpolator = Interpolators.LINEAR
             duration =
                 when (toState) {
+                    KeyguardState.AOD -> TO_AOD_DURATION
+                    KeyguardState.DOZING -> TO_DOZING_DURATION
                     KeyguardState.GONE -> TO_GONE_DURATION
+                    KeyguardState.LOCKSCREEN -> TO_LOCKSCREEN_DURATION
+                    KeyguardState.OCCLUDED -> TO_OCCLUDED_DURATION
+                    KeyguardState.PRIMARY_BOUNCER -> TO_PRIMARY_BOUNCER_DURATION
                     else -> TRANSITION_DURATION_MS
                 }.inWholeMilliseconds
         }
@@ -213,10 +216,11 @@
     companion object {
         const val TAG = "FromAlternateBouncerTransitionInteractor"
         val TRANSITION_DURATION_MS = 300.milliseconds
-        val TO_GONE_DURATION = 500.milliseconds
         val TO_AOD_DURATION = TRANSITION_DURATION_MS
-        val TO_PRIMARY_BOUNCER_DURATION = TRANSITION_DURATION_MS
         val TO_DOZING_DURATION = TRANSITION_DURATION_MS
+        val TO_GONE_DURATION = 500.milliseconds
+        val TO_LOCKSCREEN_DURATION = 300.milliseconds
         val TO_OCCLUDED_DURATION = TRANSITION_DURATION_MS
+        val TO_PRIMARY_BOUNCER_DURATION = TRANSITION_DURATION_MS
     }
 }
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 59ec87a..2a8bb47 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
@@ -119,7 +119,9 @@
                                 // needed. Also, don't react to wake and unlock events, as we'll be
                                 // receiving a call to #dismissAod() shortly when the authentication
                                 // completes.
-                                !maybeStartTransitionToOccludedOrInsecureCamera() &&
+                                !maybeStartTransitionToOccludedOrInsecureCamera { state, reason ->
+                                    startTransitionTo(state, ownerReason = reason)
+                                } &&
                                     !isWakeAndUnlock(biometricUnlockState.mode) &&
                                     !primaryBouncerShowing
                             } else {
@@ -218,7 +220,10 @@
             interpolator = Interpolators.LINEAR
             duration =
                 when (toState) {
+                    KeyguardState.GONE -> TO_GONE_DURATION
                     KeyguardState.LOCKSCREEN -> TO_LOCKSCREEN_DURATION
+                    KeyguardState.OCCLUDED -> TO_OCCLUDED_DURATION
+                    KeyguardState.PRIMARY_BOUNCER -> TO_PRIMARY_BOUNCER_DURATION
                     else -> DEFAULT_DURATION
                 }.inWholeMilliseconds
         }
@@ -227,9 +232,9 @@
     companion object {
         private const val TAG = "FromAodTransitionInteractor"
         private val DEFAULT_DURATION = 500.milliseconds
-        val TO_LOCKSCREEN_DURATION = 500.milliseconds
         val TO_GONE_DURATION = DEFAULT_DURATION
-        val TO_OCCLUDED_DURATION = DEFAULT_DURATION
+        val TO_LOCKSCREEN_DURATION = 500.milliseconds
+        val TO_OCCLUDED_DURATION = 550.milliseconds
         val TO_PRIMARY_BOUNCER_DURATION = DEFAULT_DURATION
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt
index 8f50b03..61446c1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt
@@ -20,8 +20,11 @@
 import android.annotation.SuppressLint
 import android.app.DreamManager
 import com.android.app.animation.Interpolators
+import com.android.systemui.Flags.communalSceneKtfRefactor
 import com.android.systemui.communal.domain.interactor.CommunalInteractor
 import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
+import com.android.systemui.communal.shared.model.CommunalScenes
+import com.android.systemui.communal.shared.model.CommunalTransitionKeys
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
@@ -166,7 +169,7 @@
                         }
                     } else if (occluded) {
                         startTransitionTo(KeyguardState.OCCLUDED)
-                    } else if (isIdleOnCommunal) {
+                    } else if (isIdleOnCommunal && !communalSceneKtfRefactor()) {
                         if (SceneContainerFlag.isEnabled) {
                             // TODO(b/336576536): Check if adaptation for scene framework is needed
                         } else {
@@ -183,7 +186,7 @@
                         if (SceneContainerFlag.isEnabled) {
                             // TODO(b/336576536): Check if adaptation for scene framework is needed
                         } else {
-                            startTransitionTo(KeyguardState.GLANCEABLE_HUB)
+                            transitionToGlanceableHub()
                         }
                     } else {
                         startTransitionTo(KeyguardState.LOCKSCREEN)
@@ -218,7 +221,9 @@
                         canWakeDirectlyToGone,
                         primaryBouncerShowing) ->
                     if (
-                        !maybeStartTransitionToOccludedOrInsecureCamera() &&
+                        !maybeStartTransitionToOccludedOrInsecureCamera { state, reason ->
+                            startTransitionTo(state, ownerReason = reason)
+                        } &&
                             // Handled by dismissFromDozing().
                             !isWakeAndUnlock(biometricUnlockState.mode)
                     ) {
@@ -242,7 +247,7 @@
                                     ownerReason = "waking from dozing"
                                 )
                             }
-                        } else if (isIdleOnCommunal) {
+                        } else if (isIdleOnCommunal && !communalSceneKtfRefactor()) {
                             if (SceneContainerFlag.isEnabled) {
                                 // TODO(b/336576536): Check if adaptation for scene framework is
                                 // needed
@@ -264,10 +269,7 @@
                                 // TODO(b/336576536): Check if adaptation for scene framework is
                                 // needed
                             } else {
-                                startTransitionTo(
-                                    KeyguardState.GLANCEABLE_HUB,
-                                    ownerReason = "waking from dozing"
-                                )
+                                transitionToGlanceableHub()
                             }
                         } else {
                             startTransitionTo(
@@ -280,6 +282,18 @@
         }
     }
 
+    private suspend fun transitionToGlanceableHub() {
+        if (communalSceneKtfRefactor()) {
+            communalSceneInteractor.changeScene(
+                CommunalScenes.Communal,
+                // Immediately show the hub when transitioning from dozing to hub.
+                CommunalTransitionKeys.Immediately,
+            )
+        } else {
+            startTransitionTo(KeyguardState.GLANCEABLE_HUB)
+        }
+    }
+
     /** Dismisses keyguard from the DOZING state. */
     fun dismissFromDozing() {
         scope.launch { startTransitionTo(KeyguardState.GONE) }
@@ -288,16 +302,25 @@
     override fun getDefaultAnimatorForTransitionsToState(toState: KeyguardState): ValueAnimator {
         return ValueAnimator().apply {
             interpolator = Interpolators.LINEAR
-            duration = DEFAULT_DURATION.inWholeMilliseconds
+            duration =
+                when (toState) {
+                    KeyguardState.GONE -> TO_GONE_DURATION
+                    KeyguardState.GLANCEABLE_HUB -> TO_GLANCEABLE_HUB_DURATION
+                    KeyguardState.LOCKSCREEN -> TO_LOCKSCREEN_DURATION
+                    KeyguardState.OCCLUDED -> TO_OCCLUDED_DURATION
+                    KeyguardState.PRIMARY_BOUNCER -> TO_PRIMARY_BOUNCER_DURATION
+                    else -> DEFAULT_DURATION
+                }.inWholeMilliseconds
         }
     }
 
     companion object {
         const val TAG = "FromDozingTransitionInteractor"
         private val DEFAULT_DURATION = 500.milliseconds
-        val TO_LOCKSCREEN_DURATION = DEFAULT_DURATION
-        val TO_GONE_DURATION = DEFAULT_DURATION
-        val TO_PRIMARY_BOUNCER_DURATION = DEFAULT_DURATION
         val TO_GLANCEABLE_HUB_DURATION = DEFAULT_DURATION
+        val TO_GONE_DURATION = DEFAULT_DURATION
+        val TO_LOCKSCREEN_DURATION = DEFAULT_DURATION
+        val TO_OCCLUDED_DURATION = 550.milliseconds
+        val TO_PRIMARY_BOUNCER_DURATION = DEFAULT_DURATION
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingLockscreenHostedTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingLockscreenHostedTransitionInteractor.kt
index 117dbcf..f3bd0e9 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingLockscreenHostedTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingLockscreenHostedTransitionInteractor.kt
@@ -61,6 +61,7 @@
     ) {
 
     override fun start() {
+        if (SceneContainerFlag.isEnabled) return
         listenForDreamingLockscreenHostedToLockscreen()
         listenForDreamingLockscreenHostedToGone()
         listenForDreamingLockscreenHostedToDozing()
@@ -96,8 +97,6 @@
     }
 
     private fun listenForDreamingLockscreenHostedToPrimaryBouncer() {
-        // TODO(b/336576536): Check if adaptation for scene framework is needed
-        if (SceneContainerFlag.isEnabled) return
         scope.launch {
             keyguardInteractor.primaryBouncerShowing
                 .filterRelevantKeyguardStateAnd { isBouncerShowing -> isBouncerShowing }
@@ -106,8 +105,6 @@
     }
 
     private fun listenForDreamingLockscreenHostedToGone() {
-        // TODO(b/336576536): Check if adaptation for scene framework is needed
-        if (SceneContainerFlag.isEnabled) return
         scope.launch {
             keyguardInteractor.biometricUnlockState
                 .filterRelevantKeyguardStateAnd { biometricUnlockState ->
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt
index 453401d..3775d19 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt
@@ -19,6 +19,7 @@
 import android.animation.ValueAnimator
 import com.android.app.animation.Interpolators
 import com.android.app.tracing.coroutines.launch
+import com.android.systemui.Flags.communalSceneKtfRefactor
 import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
@@ -81,7 +82,9 @@
         listenForDreamingToLockscreenOrGone()
         listenForDreamingToAodOrDozing()
         listenForTransitionToCamera(scope, keyguardInteractor)
-        listenForDreamingToGlanceableHub()
+        if (!communalSceneKtfRefactor()) {
+            listenForDreamingToGlanceableHub()
+        }
         listenForDreamingToPrimaryBouncer()
     }
 
@@ -97,7 +100,6 @@
 
     private fun listenForDreamingToGlanceableHub() {
         if (!communalSettingsInteractor.isCommunalFlagEnabled()) return
-        // TODO(b/336576536): Check if adaptation for scene framework is needed
         if (SceneContainerFlag.isEnabled) return
         scope.launch("$TAG#listenForDreamingToGlanceableHub", mainDispatcher) {
             glanceableHubTransitions.listenForGlanceableHubTransition(
@@ -192,7 +194,7 @@
 
     private fun listenForDreamingToGoneWhenDismissable() {
         if (SceneContainerFlag.isEnabled) {
-            return // TODO(b/336576536): Check if adaptation for scene framework is needed
+            return
         }
 
         if (KeyguardWmStateRefactor.isEnabled) {
@@ -214,7 +216,7 @@
     }
 
     private fun listenForDreamingToGoneFromBiometricUnlock() {
-        // TODO(b/336576536): Check if adaptation for scene framework is needed
+        // TODO(b/353542570): Adaptation for scene framework is needed
         if (SceneContainerFlag.isEnabled) return
         scope.launch {
             keyguardInteractor.biometricUnlockState
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt
index 1a7012a..befcc9e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt
@@ -19,7 +19,12 @@
 import android.animation.ValueAnimator
 import com.android.app.animation.Interpolators
 import com.android.app.tracing.coroutines.launch
+import com.android.systemui.Flags.communalSceneKtfRefactor
+import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
 import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
+import com.android.systemui.communal.shared.model.CommunalScenes
+import com.android.systemui.communal.shared.model.CommunalTransitionKeys
+import com.android.systemui.communal.shared.model.EditModeState
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
@@ -30,7 +35,9 @@
 import com.android.systemui.power.domain.interactor.PowerInteractor
 import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import com.android.systemui.util.kotlin.BooleanFlowOperators.allOf
+import com.android.systemui.util.kotlin.BooleanFlowOperators.noneOf
 import com.android.systemui.util.kotlin.BooleanFlowOperators.not
+import com.android.systemui.util.kotlin.sample
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.milliseconds
 import kotlin.time.Duration.Companion.seconds
@@ -50,6 +57,7 @@
     private val glanceableHubTransitions: GlanceableHubTransitions,
     private val communalSettingsInteractor: CommunalSettingsInteractor,
     keyguardInteractor: KeyguardInteractor,
+    private val communalSceneInteractor: CommunalSceneInteractor,
     override val transitionRepository: KeyguardTransitionRepository,
     override val internalTransitionInteractor: InternalKeyguardTransitionInteractor,
     transitionInteractor: KeyguardTransitionInteractor,
@@ -67,12 +75,13 @@
     ) {
 
     override fun start() {
-        // TODO(b/336576536): Check if adaptation for scene framework is needed
         if (SceneContainerFlag.isEnabled) return
         if (!communalSettingsInteractor.isCommunalFlagEnabled()) {
             return
         }
-        listenForHubToLockscreenOrDreaming()
+        if (!communalSceneKtfRefactor()) {
+            listenForHubToLockscreenOrDreaming()
+        }
         listenForHubToDozing()
         listenForHubToPrimaryBouncer()
         listenForHubToAlternateBouncer()
@@ -87,6 +96,8 @@
                 when (toState) {
                     KeyguardState.LOCKSCREEN -> TO_LOCKSCREEN_DURATION
                     KeyguardState.OCCLUDED -> TO_OCCLUDED_DURATION
+                    KeyguardState.ALTERNATE_BOUNCER -> TO_BOUNCER_DURATION
+                    KeyguardState.PRIMARY_BOUNCER -> TO_BOUNCER_DURATION
                     else -> DEFAULT_DURATION
                 }.inWholeMilliseconds
         }
@@ -120,7 +131,10 @@
         scope.launch("$TAG#listenForHubToPrimaryBouncer") {
             keyguardInteractor.primaryBouncerShowing
                 .filterRelevantKeyguardStateAnd { primaryBouncerShowing -> primaryBouncerShowing }
-                .collect { startTransitionTo(KeyguardState.PRIMARY_BOUNCER) }
+                .collect {
+                    // Bouncer shows on top of the hub, so do not change scenes here.
+                    startTransitionTo(KeyguardState.PRIMARY_BOUNCER)
+                }
         }
     }
 
@@ -130,7 +144,10 @@
                 .filterRelevantKeyguardStateAnd { alternateBouncerShowing ->
                     alternateBouncerShowing
                 }
-                .collect { pair -> startTransitionTo(KeyguardState.ALTERNATE_BOUNCER) }
+                .collect { pair ->
+                    // Bouncer shows on top of the hub, so do not change scenes here.
+                    startTransitionTo(KeyguardState.ALTERNATE_BOUNCER)
+                }
         }
     }
 
@@ -139,10 +156,18 @@
             powerInteractor.isAsleep
                 .filterRelevantKeyguardStateAnd { isAsleep -> isAsleep }
                 .collect {
-                    startTransitionTo(
-                        toState = KeyguardState.DOZING,
-                        modeOnCanceled = TransitionModeOnCanceled.LAST_VALUE,
-                    )
+                    if (communalSceneKtfRefactor()) {
+                        communalSceneInteractor.changeScene(
+                            newScene = CommunalScenes.Blank,
+                            transitionKey = CommunalTransitionKeys.Immediately,
+                            keyguardState = KeyguardState.DOZING,
+                        )
+                    } else {
+                        startTransitionTo(
+                            toState = KeyguardState.DOZING,
+                            modeOnCanceled = TransitionModeOnCanceled.LAST_VALUE,
+                        )
+                    }
                 }
         }
     }
@@ -152,7 +177,44 @@
             scope.launch {
                 keyguardOcclusionInteractor.isShowWhenLockedActivityOnTop
                     .filterRelevantKeyguardStateAnd { onTop -> onTop }
-                    .collect { maybeStartTransitionToOccludedOrInsecureCamera() }
+                    .collect {
+                        maybeStartTransitionToOccludedOrInsecureCamera { state, reason ->
+                            if (communalSceneKtfRefactor()) {
+                                communalSceneInteractor.changeScene(
+                                    newScene = CommunalScenes.Blank,
+                                    transitionKey = CommunalTransitionKeys.SimpleFade,
+                                    keyguardState = state,
+                                )
+                                null
+                            } else {
+                                startTransitionTo(state, ownerReason = reason)
+                            }
+                        }
+                    }
+            }
+        } else if (communalSceneKtfRefactor()) {
+            scope.launch {
+                allOf(
+                        keyguardInteractor.isKeyguardOccluded,
+                        noneOf(
+                            // Dream is a special-case of occluded, so filter out the dreaming
+                            // case here.
+                            keyguardInteractor.isDreaming,
+                            // When launching activities from widgets on the hub, we have a
+                            // custom occlusion animation.
+                            communalSceneInteractor.isLaunchingWidget,
+                        ),
+                    )
+                    .filterRelevantKeyguardStateAnd { isOccludedAndNotDreamingNorLaunchingWidget ->
+                        isOccludedAndNotDreamingNorLaunchingWidget
+                    }
+                    .collect { _ ->
+                        communalSceneInteractor.changeScene(
+                            newScene = CommunalScenes.Blank,
+                            transitionKey = CommunalTransitionKeys.SimpleFade,
+                            keyguardState = KeyguardState.OCCLUDED,
+                        )
+                    }
             }
         } else {
             scope.launch {
@@ -160,9 +222,7 @@
                     .filterRelevantKeyguardStateAnd { isOccludedAndNotDreaming ->
                         isOccludedAndNotDreaming
                     }
-                    .collect { isOccludedAndNotDreaming ->
-                        startTransitionTo(KeyguardState.OCCLUDED)
-                    }
+                    .collect { _ -> startTransitionTo(KeyguardState.OCCLUDED) }
             }
         }
     }
@@ -170,17 +230,57 @@
     private fun listenForHubToGone() {
         // TODO(b/336576536): Check if adaptation for scene framework is needed
         if (SceneContainerFlag.isEnabled) return
-        scope.launch {
-            keyguardInteractor.isKeyguardGoingAway
-                .filterRelevantKeyguardStateAnd { isKeyguardGoingAway -> isKeyguardGoingAway }
-                .collect { startTransitionTo(KeyguardState.GONE) }
+        if (communalSceneKtfRefactor()) {
+            scope.launch {
+                allOf(
+                        keyguardInteractor.isKeyguardGoingAway,
+                        // TODO(b/327225415): Handle edit mode opening here to avoid going to GONE
+                        // state until after edit mode is ready to be shown.
+                        noneOf(
+                            // When launching activities from widgets on the hub, we wait to change
+                            // scenes until the activity launch is complete.
+                            communalSceneInteractor.isLaunchingWidget,
+                        ),
+                    )
+                    .filterRelevantKeyguardStateAnd { isKeyguardGoingAway -> isKeyguardGoingAway }
+                    .sample(communalSceneInteractor.editModeState, ::Pair)
+                    .collect { (_, editModeState) ->
+                        if (
+                            editModeState == EditModeState.STARTING ||
+                                editModeState == EditModeState.SHOWING
+                        ) {
+                            // Don't change scenes here as that is handled by the edit activity.
+                            startTransitionTo(KeyguardState.GONE)
+                        } else {
+                            communalSceneInteractor.changeScene(
+                                newScene = CommunalScenes.Blank,
+                                transitionKey = CommunalTransitionKeys.SimpleFade,
+                                keyguardState = KeyguardState.GONE
+                            )
+                        }
+                    }
+            }
+        } else {
+            scope.launch {
+                keyguardInteractor.isKeyguardGoingAway
+                    .filterRelevantKeyguardStateAnd { isKeyguardGoingAway -> isKeyguardGoingAway }
+                    .collect { startTransitionTo(KeyguardState.GONE) }
+            }
         }
     }
 
     companion object {
         const val TAG = "FromGlanceableHubTransitionInteractor"
-        val DEFAULT_DURATION = 1.seconds
-        val TO_LOCKSCREEN_DURATION = DEFAULT_DURATION
+
+        /**
+         * DEFAULT_DURATION controls the timing for all animations other than those with overrides
+         * in [getDefaultAnimatorForTransitionsToState].
+         *
+         * Set at 400ms for parity with [FromLockscreenTransitionInteractor]
+         */
+        val DEFAULT_DURATION = 400.milliseconds
+        val TO_LOCKSCREEN_DURATION = 1.seconds
+        val TO_BOUNCER_DURATION = 400.milliseconds
         val TO_OCCLUDED_DURATION = 450.milliseconds
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
index b084824..8f4110c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
@@ -19,7 +19,7 @@
 import android.animation.ValueAnimator
 import com.android.app.animation.Interpolators
 import com.android.app.tracing.coroutines.launch
-import com.android.systemui.communal.domain.interactor.CommunalInteractor
+import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
@@ -52,7 +52,7 @@
     @Main mainDispatcher: CoroutineDispatcher,
     keyguardInteractor: KeyguardInteractor,
     powerInteractor: PowerInteractor,
-    private val communalInteractor: CommunalInteractor,
+    private val communalSceneInteractor: CommunalSceneInteractor,
     keyguardOcclusionInteractor: KeyguardOcclusionInteractor,
     private val biometricSettingsRepository: BiometricSettingsRepository,
     private val keyguardRepository: KeyguardRepository,
@@ -69,7 +69,7 @@
     ) {
 
     override fun start() {
-        // TODO(b/336576536): Check if adaptation for scene framework is needed
+        // KeyguardState.GONE does not exist with SceneContainerFlag enabled
         if (SceneContainerFlag.isEnabled) return
         listenForGoneToAodOrDozing()
         listenForGoneToDreaming()
@@ -88,7 +88,7 @@
                 biometricSettingsRepository.isCurrentUserInLockdown
                     .distinctUntilChanged()
                     .filterRelevantKeyguardStateAnd { inLockdown -> inLockdown }
-                    .sample(communalInteractor.isIdleOnCommunal, ::Pair)
+                    .sample(communalSceneInteractor.isIdleOnCommunalNotEditMode, ::Pair)
                     .collect { (_, isIdleOnCommunal) ->
                         val to =
                             if (isIdleOnCommunal) {
@@ -100,27 +100,25 @@
                     }
             }
 
-            if (!SceneContainerFlag.isEnabled) {
-                scope.launch {
-                    keyguardRepository.isKeyguardEnabled
-                        .filterRelevantKeyguardStateAnd { enabled -> enabled }
-                        .sample(keyguardEnabledInteractor.showKeyguardWhenReenabled)
-                        .filter { reshow -> reshow }
-                        .collect {
-                            startTransitionTo(
-                                KeyguardState.LOCKSCREEN,
-                                ownerReason =
-                                    "Keyguard was re-enabled, and we weren't GONE when it " +
-                                        "was originally disabled"
-                            )
-                        }
-                }
+            scope.launch {
+                keyguardRepository.isKeyguardEnabled
+                    .filterRelevantKeyguardStateAnd { enabled -> enabled }
+                    .sample(keyguardEnabledInteractor.showKeyguardWhenReenabled)
+                    .filter { reshow -> reshow }
+                    .collect {
+                        startTransitionTo(
+                            KeyguardState.LOCKSCREEN,
+                            ownerReason =
+                                "Keyguard was re-enabled, and we weren't GONE when it " +
+                                    "was originally disabled"
+                        )
+                    }
             }
         } else {
             scope.launch("$TAG#listenForGoneToLockscreenOrHub") {
                 keyguardInteractor.isKeyguardShowing
                     .filterRelevantKeyguardStateAnd { isKeyguardShowing -> isKeyguardShowing }
-                    .sample(communalInteractor.isIdleOnCommunal, ::Pair)
+                    .sample(communalSceneInteractor.isIdleOnCommunalNotEditMode, ::Pair)
                     .collect { (_, isIdleOnCommunal) ->
                         val to =
                             if (isIdleOnCommunal) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
index 5c7adf0..51d92f0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
@@ -20,6 +20,7 @@
 import android.util.MathUtils
 import com.android.app.animation.Interpolators
 import com.android.app.tracing.coroutines.launch
+import com.android.systemui.Flags.communalSceneKtfRefactor
 import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
@@ -90,7 +91,9 @@
         listenForLockscreenToPrimaryBouncerDragging()
         listenForLockscreenToAlternateBouncer()
         listenForLockscreenTransitionToCamera()
-        listenForLockscreenToGlanceableHub()
+        if (!communalSceneKtfRefactor()) {
+            listenForLockscreenToGlanceableHub()
+        }
     }
 
     /**
@@ -348,7 +351,6 @@
      * keyguard transition.
      */
     private fun listenForLockscreenToGlanceableHub() {
-        // TODO(b/336576536): Check if adaptation for scene framework is needed
         if (SceneContainerFlag.isEnabled) return
         if (!communalSettingsInteractor.isCommunalFlagEnabled()) {
             return
@@ -391,7 +393,7 @@
         val TO_DOZING_DURATION = 500.milliseconds
         val TO_DREAMING_DURATION = 933.milliseconds
         val TO_DREAMING_HOSTED_DURATION = 933.milliseconds
-        val TO_OCCLUDED_DURATION = 450.milliseconds
+        val TO_OCCLUDED_DURATION = 550.milliseconds
         val TO_AOD_DURATION = 500.milliseconds
         val TO_AOD_FOLD_DURATION = 1100.milliseconds
         val TO_PRIMARY_BOUNCER_DURATION = DEFAULT_DURATION
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt
index f3ca9df..710b710a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt
@@ -18,8 +18,12 @@
 
 import android.animation.ValueAnimator
 import com.android.app.animation.Interpolators
+import com.android.systemui.Flags.communalSceneKtfRefactor
 import com.android.systemui.Flags.restartDreamOnUnocclude
 import com.android.systemui.communal.domain.interactor.CommunalInteractor
+import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
+import com.android.systemui.communal.shared.model.CommunalScenes
+import com.android.systemui.communal.shared.model.CommunalTransitionKeys
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
@@ -49,6 +53,7 @@
     keyguardInteractor: KeyguardInteractor,
     powerInteractor: PowerInteractor,
     private val communalInteractor: CommunalInteractor,
+    private val communalSceneInteractor: CommunalSceneInteractor,
     keyguardOcclusionInteractor: KeyguardOcclusionInteractor,
 ) :
     TransitionInteractor(
@@ -138,17 +143,21 @@
         if (restartDreamOnUnocclude() && dreamFromOccluded) {
             startTransitionTo(KeyguardState.DREAMING)
         } else if (isIdleOnCommunal || showCommunalFromOccluded) {
-            // TODO(b/336576536): Check if adaptation for scene framework is needed
             if (SceneContainerFlag.isEnabled) return
-            startTransitionTo(KeyguardState.GLANCEABLE_HUB)
+            if (communalSceneKtfRefactor()) {
+                communalSceneInteractor.changeScene(
+                    CommunalScenes.Communal,
+                    CommunalTransitionKeys.SimpleFade
+                )
+            } else {
+                startTransitionTo(KeyguardState.GLANCEABLE_HUB)
+            }
         } else {
             startTransitionTo(KeyguardState.LOCKSCREEN)
         }
     }
 
     private fun listenForOccludedToGone() {
-        // TODO(b/336576536): Check if adaptation for scene framework is needed
-        if (SceneContainerFlag.isEnabled) return
         if (KeyguardWmStateRefactor.isEnabled) {
             // We don't think OCCLUDED to GONE is possible. You should always have to go via a
             // *_BOUNCER state to end up GONE. Launching an activity over a dismissable keyguard
@@ -156,7 +165,8 @@
             // If we're wrong - sorry, add it back here.
             return
         }
-
+        // TODO(b/353545202): Adaptation for scene framework is needed
+        if (SceneContainerFlag.isEnabled) return
         scope.launch {
             keyguardInteractor.isKeyguardOccluded
                 .sample(keyguardInteractor.isKeyguardShowing, ::Pair)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
index 2429088..2823b93 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
@@ -18,7 +18,9 @@
 
 import android.animation.ValueAnimator
 import com.android.keyguard.KeyguardSecurityModel
-import com.android.systemui.communal.domain.interactor.CommunalInteractor
+import com.android.systemui.Flags.communalSceneKtfRefactor
+import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
+import com.android.systemui.communal.shared.model.CommunalScenes
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
@@ -55,7 +57,7 @@
     @Background bgDispatcher: CoroutineDispatcher,
     @Main mainDispatcher: CoroutineDispatcher,
     keyguardInteractor: KeyguardInteractor,
-    private val communalInteractor: CommunalInteractor,
+    private val communalSceneInteractor: CommunalSceneInteractor,
     private val keyguardSecurityModel: KeyguardSecurityModel,
     private val selectedUserInteractor: SelectedUserInteractor,
     powerInteractor: PowerInteractor,
@@ -94,11 +96,13 @@
             .distinctUntilChanged()
 
     fun dismissPrimaryBouncer() {
-        scope.launch { startTransitionTo(KeyguardState.GONE) }
+        scope.launch {
+            startTransitionTo(KeyguardState.GONE)
+            closeHubImmediatelyIfNeeded()
+        }
     }
 
     private fun listenForPrimaryBouncerToLockscreenHubOrOccluded() {
-        // TODO(b/336576536): Check if adaptation for scene framework is needed
         if (SceneContainerFlag.isEnabled) return
         if (KeyguardWmStateRefactor.isEnabled) {
             scope.launch {
@@ -106,17 +110,20 @@
                     .sample(
                         powerInteractor.isAwake,
                         keyguardInteractor.isActiveDreamLockscreenHosted,
-                        communalInteractor.isIdleOnCommunal
+                        communalSceneInteractor.isIdleOnCommunal
                     )
-                    .filterRelevantKeyguardState()
-                    .collect {
-                        (isBouncerShowing, isAwake, isActiveDreamLockscreenHosted, isIdleOnCommunal)
-                        ->
+                    .filterRelevantKeyguardStateAnd { (isBouncerShowing, _, _, _) ->
+                        // TODO(b/307976454) - See if we need to listen for SHOW_WHEN_LOCKED
+                        // activities showing up over the bouncer. Camera launch can't show up over
+                        // bouncer since the first power press hides bouncer. Do occluding
+                        // activities auto hide bouncer? Not sure.
+                        !isBouncerShowing
+                    }
+                    .collect { (_, isAwake, isActiveDreamLockscreenHosted, isIdleOnCommunal) ->
                         if (
-                            !maybeStartTransitionToOccludedOrInsecureCamera() &&
-                                !isBouncerShowing &&
-                                isAwake &&
-                                !isActiveDreamLockscreenHosted
+                            !maybeStartTransitionToOccludedOrInsecureCamera { state, reason ->
+                                startTransitionTo(state, ownerReason = reason)
+                            } && isAwake && !isActiveDreamLockscreenHosted
                         ) {
                             val toState =
                                 if (isIdleOnCommunal) {
@@ -136,7 +143,7 @@
                         keyguardInteractor.isKeyguardOccluded,
                         keyguardInteractor.isDreaming,
                         keyguardInteractor.isActiveDreamLockscreenHosted,
-                        communalInteractor.isIdleOnCommunal,
+                        communalSceneInteractor.isIdleOnCommunal,
                     )
                     .filterRelevantKeyguardStateAnd {
                         (isBouncerShowing, isAwake, _, _, isActiveDreamLockscreenHosted, _) ->
@@ -159,14 +166,25 @@
         }
     }
 
+    private fun closeHubImmediatelyIfNeeded() {
+        // If the hub is showing, and we are not animating a widget launch nor transitioning to
+        // edit mode, then close the hub immediately.
+        if (
+            communalSceneKtfRefactor() &&
+                communalSceneInteractor.isIdleOnCommunal.value &&
+                !communalSceneInteractor.isLaunchingWidget.value &&
+                communalSceneInteractor.editModeState.value == null
+        ) {
+            communalSceneInteractor.snapToScene(CommunalScenes.Blank)
+        }
+    }
+
     private fun listenForPrimaryBouncerToAsleep() {
-        // TODO(b/336576536): Check if adaptation for scene framework is needed
         if (SceneContainerFlag.isEnabled) return
         scope.launch { listenForSleepTransition() }
     }
 
     private fun listenForPrimaryBouncerToDreamingLockscreenHosted() {
-        // TODO(b/336576536): Check if adaptation for scene framework is needed
         if (SceneContainerFlag.isEnabled) return
         scope.launch {
             keyguardInteractor.primaryBouncerShowing
@@ -180,7 +198,6 @@
     }
 
     private fun listenForPrimaryBouncerToGone() {
-        // TODO(b/336576536): Check if adaptation for scene framework is needed
         if (SceneContainerFlag.isEnabled) return
         if (KeyguardWmStateRefactor.isEnabled) {
             // This is handled in KeyguardSecurityContainerController and
@@ -213,6 +230,7 @@
                             },
                         modeOnCanceled = TransitionModeOnCanceled.RESET,
                     )
+                    closeHubImmediatelyIfNeeded()
                 }
         }
     }
@@ -227,6 +245,7 @@
                     KeyguardState.GONE -> TO_GONE_DURATION
                     KeyguardState.LOCKSCREEN -> TO_LOCKSCREEN_DURATION
                     KeyguardState.GLANCEABLE_HUB -> TO_GLANCEABLE_HUB_DURATION
+                    KeyguardState.OCCLUDED -> TO_OCCLUDED_DURATION
                     else -> DEFAULT_DURATION
                 }.inWholeMilliseconds
         }
@@ -239,7 +258,8 @@
         val TO_GONE_DURATION = 500.milliseconds
         val TO_GONE_SHORT_DURATION = 200.milliseconds
         val TO_LOCKSCREEN_DURATION = 450.milliseconds
+        val TO_OCCLUDED_DURATION = 550.milliseconds
         val TO_GLANCEABLE_HUB_DURATION = DEFAULT_DURATION
-        val TO_GONE_SURFACE_BEHIND_VISIBLE_THRESHOLD = 0.5f
+        val TO_GONE_SURFACE_BEHIND_VISIBLE_THRESHOLD = 0.1f
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/GlanceableHubTransitions.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/GlanceableHubTransitions.kt
index af1ce2b..f9ab1bb 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/GlanceableHubTransitions.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/GlanceableHubTransitions.kt
@@ -50,7 +50,6 @@
         fromState: KeyguardState,
         toState: KeyguardState,
     ) {
-        // TODO(b/336576536): Check if adaptation for scene framework is needed
         if (SceneContainerFlag.isEnabled) return
         val toScene =
             if (fromState == KeyguardState.GLANCEABLE_HUB) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/InternalKeyguardTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/InternalKeyguardTransitionInteractor.kt
index a51421c..2cc6afa 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/InternalKeyguardTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/InternalKeyguardTransitionInteractor.kt
@@ -63,7 +63,7 @@
 
     suspend fun startTransition(info: TransitionInfo) = repository.startTransition(info)
 
-    fun updateTransition(
+    suspend fun updateTransition(
         transitionId: UUID,
         @FloatRange(from = 0.0, to = 1.0) value: Float,
         state: TransitionState
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractor.kt
index f4d8265..1c445a7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractor.kt
@@ -19,12 +19,17 @@
 
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
 import com.android.systemui.keyguard.data.repository.KeyguardRepository
 import com.android.systemui.keyguard.shared.model.DismissAction
 import com.android.systemui.keyguard.shared.model.KeyguardDone
 import com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCER
 import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
 import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
+import com.android.systemui.scene.domain.interactor.SceneInteractor
+import com.android.systemui.scene.domain.resolver.NotifShadeSceneFamilyResolver
+import com.android.systemui.scene.domain.resolver.QuickSettingsSceneFamilyResolver
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.util.kotlin.Utils.Companion.sampleFilter
 import com.android.systemui.util.kotlin.sample
@@ -35,6 +40,7 @@
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.filter
 import kotlinx.coroutines.flow.filterNot
 import kotlinx.coroutines.flow.map
@@ -51,6 +57,10 @@
     transitionInteractor: KeyguardTransitionInteractor,
     val dismissInteractor: KeyguardDismissInteractor,
     @Application private val applicationScope: CoroutineScope,
+    sceneInteractor: SceneInteractor,
+    deviceEntryInteractor: DeviceEntryInteractor,
+    quickSettingsSceneFamilyResolver: QuickSettingsSceneFamilyResolver,
+    notifShadeSceneFamilyResolver: NotifShadeSceneFamilyResolver,
 ) {
     val dismissAction: Flow<DismissAction> = repository.dismissAction
 
@@ -80,9 +90,24 @@
             .filter { it }
             .map {}
 
+    /**
+     * True if the any variation of the notification shade or quick settings is showing AND the
+     * device is unlocked. Else, false.
+     */
+    private val isOnShadeWhileUnlocked: Flow<Boolean> =
+        combine(
+                sceneInteractor.currentScene,
+                deviceEntryInteractor.isUnlocked,
+            ) { scene, isUnlocked ->
+                isUnlocked &&
+                    (quickSettingsSceneFamilyResolver.includesScene(scene) ||
+                        notifShadeSceneFamilyResolver.includesScene(scene))
+            }
+            .distinctUntilChanged()
     val executeDismissAction: Flow<() -> KeyguardDone> =
         merge(
                 finishedTransitionToGone,
+                isOnShadeWhileUnlocked.filter { it }.map {},
                 dismissInteractor.dismissKeyguardRequestWithImmediateDismissAction
             )
             .sample(dismissAction)
@@ -99,9 +124,10 @@
                     scene = Scenes.Bouncer,
                     stateWithoutSceneContainer = PRIMARY_BOUNCER
                 ),
-                transitionInteractor.isFinishedIn(state = ALTERNATE_BOUNCER)
-            ) { isOnGone, isOnBouncer, isOnAltBouncer ->
-                !isOnGone && !isOnBouncer && !isOnAltBouncer
+                transitionInteractor.isFinishedIn(state = ALTERNATE_BOUNCER),
+                isOnShadeWhileUnlocked,
+            ) { isOnGone, isOnBouncer, isOnAltBouncer, isOnShadeWhileUnlocked ->
+                !isOnGone && !isOnBouncer && !isOnAltBouncer && !isOnShadeWhileUnlocked
             }
             .filter { it }
             .sampleFilter(dismissAction) { it !is DismissAction.None }
@@ -112,6 +138,7 @@
     }
 
     fun runAfterKeyguardGone(runnable: Runnable) {
+        if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return
         setDismissAction(
             DismissAction.RunAfterKeyguardGone(
                 dismissAction = { runnable.run() },
@@ -123,15 +150,18 @@
     }
 
     fun setDismissAction(dismissAction: DismissAction) {
+        if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return
         repository.dismissAction.value.onCancelAction.run()
         repository.setDismissAction(dismissAction)
     }
 
     fun handleDismissAction() {
+        if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return
         repository.setDismissAction(DismissAction.None)
     }
 
     suspend fun setKeyguardDone(keyguardDoneTiming: KeyguardDone) {
+        if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return
         dismissInteractor.setKeyguardDone(keyguardDoneTiming)
     }
 }
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 046e79c..42490c4 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
@@ -23,10 +23,7 @@
 import android.graphics.Point
 import android.util.MathUtils
 import com.android.app.animation.Interpolators
-import com.android.app.tracing.FlowTracing.tracedAwaitClose
-import com.android.app.tracing.FlowTracing.tracedConflatedCallbackFlow
 import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepository
-import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
 import com.android.systemui.common.shared.model.NotificationContainerBounds
 import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
 import com.android.systemui.dagger.SysUISingleton
@@ -35,9 +32,11 @@
 import com.android.systemui.keyguard.data.repository.KeyguardRepository
 import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
 import com.android.systemui.keyguard.shared.model.CameraLaunchSourceModel
+import com.android.systemui.keyguard.shared.model.CameraLaunchType
 import com.android.systemui.keyguard.shared.model.DozeStateModel
 import com.android.systemui.keyguard.shared.model.DozeStateModel.Companion.isDozeOff
 import com.android.systemui.keyguard.shared.model.DozeTransitionModel
+import com.android.systemui.keyguard.shared.model.Edge
 import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
 import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
 import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
@@ -48,11 +47,8 @@
 import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.shade.data.repository.ShadeRepository
-import com.android.systemui.statusbar.CommandQueue
-import com.android.systemui.statusbar.notification.NotificationUtils.interpolate
 import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor
 import com.android.systemui.util.kotlin.Utils.Companion.sample as sampleCombine
-import com.android.systemui.util.kotlin.pairwise
 import com.android.systemui.util.kotlin.sample
 import javax.inject.Inject
 import javax.inject.Provider
@@ -69,9 +65,7 @@
 import kotlinx.coroutines.flow.debounce
 import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.filter
-import kotlinx.coroutines.flow.first
 import kotlinx.coroutines.flow.flatMapLatest
-import kotlinx.coroutines.flow.flow
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.merge
@@ -87,7 +81,6 @@
 @Inject
 constructor(
     private val repository: KeyguardRepository,
-    private val commandQueue: CommandQueue,
     powerInteractor: PowerInteractor,
     bouncerRepository: KeyguardBouncerRepository,
     configurationInteractor: ConfigurationInteractor,
@@ -102,55 +95,34 @@
     // TODO(b/296118689): move to a repository
     private val _notificationPlaceholderBounds = MutableStateFlow(NotificationContainerBounds())
 
-    // When going to AOD, we interpolate bounds when receiving the new bounds
-    // When going back to LS, we'll apply new bounds directly
-    private val _nonSplitShadeNotifciationPlaceholderBounds =
-        _notificationPlaceholderBounds.pairwise().flatMapLatest { (oldBounds, newBounds) ->
-            val lastChangeStep = keyguardTransitionInteractor.transitionState.first()
-            if (lastChangeStep.to == AOD) {
-                keyguardTransitionInteractor.transitionState.map { step ->
-                    val startingProgress = lastChangeStep.value
-                    val progress = step.value
-                    if (step.to == AOD && progress >= startingProgress && startingProgress < 1f) {
-                        val adjustedProgress =
-                            ((progress - startingProgress) / (1F - startingProgress)).coerceIn(
-                                0F,
-                                1F
-                            )
-                        val top = interpolate(oldBounds.top, newBounds.top, adjustedProgress)
-                        val bottom =
-                            interpolate(
-                                oldBounds.bottom,
-                                newBounds.bottom,
-                                adjustedProgress.coerceIn(0F, 1F)
-                            )
-                        NotificationContainerBounds(top = top, bottom = bottom)
-                    } else {
-                        newBounds
-                    }
-                }
-            } else {
-                flow { emit(newBounds) }
-            }
-        }
-
     /** Bounds of the notification container. */
     val notificationContainerBounds: StateFlow<NotificationContainerBounds> by lazy {
         SceneContainerFlag.assertInLegacyMode()
-        combine(
+        combineTransform(
                 _notificationPlaceholderBounds,
-                _nonSplitShadeNotifciationPlaceholderBounds,
                 sharedNotificationContainerInteractor.get().configurationBasedDimensions,
-            ) { bounds, nonSplitShadeBounds: NotificationContainerBounds, cfg ->
+                keyguardTransitionInteractor.isInTransition(
+                    edge = Edge.create(from = LOCKSCREEN, to = AOD)
+                ),
+            ) { bounds, cfg, isTransitioningToAod ->
+                if (isTransitioningToAod) {
+                    // Keep bounds stable during this transition, to prevent cases like smartspace
+                    // popping in and adjusting the bounds. A prime example would be media playing,
+                    // which then updates smartspace on transition to AOD
+                    return@combineTransform
+                }
+
                 // We offset the placeholder bounds by the configured top margin to account for
                 // legacy placement behavior within notifications for splitshade.
-                if (MigrateClocksToBlueprint.isEnabled) {
-                    if (cfg.useSplitShade) {
-                        bounds.copy(bottom = bounds.bottom - cfg.keyguardSplitShadeTopMargin)
-                    } else {
-                        nonSplitShadeBounds
-                    }
-                } else bounds
+                emit(
+                    if (MigrateClocksToBlueprint.isEnabled) {
+                        if (cfg.useSplitShade) {
+                            bounds.copy(bottom = bounds.bottom - cfg.keyguardSplitShadeTopMargin)
+                        } else {
+                            bounds
+                        }
+                    } else bounds
+                )
             }
             .stateIn(
                 scope = applicationScope,
@@ -198,22 +170,7 @@
 
     /** Event for when the camera gesture is detected */
     val onCameraLaunchDetected: Flow<CameraLaunchSourceModel> =
-        tracedConflatedCallbackFlow("KeyguardInteractor#onCameraLaunchDetected") {
-            val callback =
-                object : CommandQueue.Callbacks {
-                    override fun onCameraLaunchGestureDetected(source: Int) {
-                        trySendWithFailureLogging(
-                            cameraLaunchSourceIntToModel(source),
-                            TAG,
-                            "updated onCameraLaunchGestureDetected"
-                        )
-                    }
-                }
-
-            commandQueue.addCallback(callback)
-
-            tracedAwaitClose("onCameraLaunchDetected") { commandQueue.removeCallback(callback) }
-        }
+        repository.onCameraLaunchDetected.filter { it.type != CameraLaunchType.IGNORE }
 
     /**
      * Dozing and dreaming have overlapping events. If the doze state remains in FINISH, it means
@@ -310,7 +267,7 @@
                 when {
                     isKeyguardVisible -> false
                     isPrimaryBouncerShowing -> false
-                    else -> cameraLaunchEvent == CameraLaunchSourceModel.POWER_DOUBLE_TAP
+                    else -> cameraLaunchEvent.type == CameraLaunchType.POWER_DOUBLE_TAP
                 }
             }
             .onStart { emit(false) }
@@ -440,16 +397,15 @@
         return repository.isKeyguardShowing()
     }
 
-    private fun cameraLaunchSourceIntToModel(value: Int): CameraLaunchSourceModel {
+    private fun cameraLaunchSourceIntToType(value: Int): CameraLaunchType {
         return when (value) {
-            StatusBarManager.CAMERA_LAUNCH_SOURCE_WIGGLE -> CameraLaunchSourceModel.WIGGLE
+            StatusBarManager.CAMERA_LAUNCH_SOURCE_WIGGLE -> CameraLaunchType.WIGGLE
             StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP ->
-                CameraLaunchSourceModel.POWER_DOUBLE_TAP
-            StatusBarManager.CAMERA_LAUNCH_SOURCE_LIFT_TRIGGER ->
-                CameraLaunchSourceModel.LIFT_TRIGGER
+                CameraLaunchType.POWER_DOUBLE_TAP
+            StatusBarManager.CAMERA_LAUNCH_SOURCE_LIFT_TRIGGER -> CameraLaunchType.LIFT_TRIGGER
             StatusBarManager.CAMERA_LAUNCH_SOURCE_QUICK_AFFORDANCE ->
-                CameraLaunchSourceModel.QUICK_AFFORDANCE
-            else -> throw IllegalArgumentException("Invalid CameraLaunchSourceModel value: $value")
+                CameraLaunchType.QUICK_AFFORDANCE
+            else -> throw IllegalArgumentException("Invalid CameraLaunchType value: $value")
         }
     }
 
@@ -508,6 +464,11 @@
         fromLockscreenTransitionInteractor.get().dismissKeyguard()
     }
 
+    fun onCameraLaunchDetected(source: Int) {
+        repository.onCameraLaunchDetected.value =
+            CameraLaunchSourceModel(type = cameraLaunchSourceIntToType(source))
+    }
+
     companion object {
         private const val TAG = "KeyguardInteractor"
     }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
index ccce3bf..31236a4 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
@@ -25,6 +25,7 @@
 import com.android.app.tracing.coroutines.withContext
 import com.android.compose.animation.scene.ObservableTransitionState
 import com.android.internal.widget.LockPatternUtils
+import com.android.keyguard.logging.KeyguardQuickAffordancesLogger
 import com.android.systemui.animation.DialogTransitionAnimator
 import com.android.systemui.animation.Expandable
 import com.android.systemui.dagger.SysUISingleton
@@ -80,7 +81,8 @@
     private val featureFlags: FeatureFlags,
     private val repository: Lazy<KeyguardQuickAffordanceRepository>,
     private val launchAnimator: DialogTransitionAnimator,
-    private val logger: KeyguardQuickAffordancesMetricsLogger,
+    private val logger: KeyguardQuickAffordancesLogger,
+    private val metricsLogger: KeyguardQuickAffordancesMetricsLogger,
     private val devicePolicyManager: DevicePolicyManager,
     private val dockManager: DockManager,
     private val biometricSettingsRepository: BiometricSettingsRepository,
@@ -171,7 +173,8 @@
             Log.e(TAG, "Affordance config with key of \"$configKey\" not found!")
             return
         }
-        logger.logOnShortcutTriggered(slotId, configKey)
+        logger.logQuickAffordanceTriggered(decodedSlotId, decodedConfigKey)
+        metricsLogger.logOnShortcutTriggered(slotId, configKey)
 
         when (val result = config.onTriggered(expandable)) {
             is KeyguardQuickAffordanceConfig.OnTriggeredResult.StartActivity ->
@@ -223,7 +226,8 @@
                 affordanceIds = selections,
             )
 
-        logger.logOnShortcutSelected(slotId, affordanceId)
+        logger.logQuickAffordanceSelected(slotId, affordanceId)
+        metricsLogger.logOnShortcutSelected(slotId, affordanceId)
         return true
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionBootInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionBootInteractor.kt
index 805dbb0..2ebd9e8 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionBootInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionBootInteractor.kt
@@ -30,6 +30,7 @@
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.first
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.launch
@@ -52,11 +53,12 @@
      * then we'll seed the repository with a transition from OFF -> GONE.
      */
     @OptIn(ExperimentalCoroutinesApi::class)
-    private val showLockscreenOnBoot =
+    private val showLockscreenOnBoot: Flow<Boolean> by lazy {
         deviceProvisioningInteractor.isDeviceProvisioned.map { provisioned ->
             (provisioned || deviceEntryInteractor.isAuthenticationRequired()) &&
                 deviceEntryInteractor.isLockscreenEnabled()
         }
+    }
 
     override fun start() {
         scope.launch {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
index f9bfaff..afbe357 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
@@ -47,6 +47,7 @@
 import kotlinx.coroutines.flow.SharedFlow
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.buffer
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.filter
@@ -55,6 +56,7 @@
 import kotlinx.coroutines.flow.onStart
 import kotlinx.coroutines.flow.shareIn
 import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.flow.transform
 import kotlinx.coroutines.launch
 
 /** Encapsulates business-logic related to the keyguard transitions. */
@@ -150,6 +152,12 @@
                         startedStep.to != prevStep.from
                 ) {
                     getTransitionValueFlow(prevStep.from).emit(0f)
+                } else if (prevStep.transitionState == TransitionState.RUNNING) {
+                    Log.e(
+                        TAG,
+                        "STARTED step ($startedStep) was preceded by a RUNNING step " +
+                            "($prevStep), which should never happen. Things could go badly here."
+                    )
                 }
             }
         }
@@ -252,15 +260,12 @@
     val startedKeyguardTransitionStep: Flow<TransitionStep> =
         repository.transitions.filter { step -> step.transitionState == TransitionState.STARTED }
 
-    /** The last [TransitionStep] with a [TransitionState] of FINISHED */
-    val finishedKeyguardTransitionStep: Flow<TransitionStep> =
-        repository.transitions.filter { step -> step.transitionState == TransitionState.FINISHED }
-
     /** The destination state of the last [TransitionState.STARTED] transition. */
     @SuppressLint("SharedFlowCreation")
     val startedKeyguardState: SharedFlow<KeyguardState> =
         startedKeyguardTransitionStep
             .map { step -> step.to }
+            .buffer(2, BufferOverflow.DROP_OLDEST)
             .shareIn(scope, SharingStarted.Eagerly, replay = 1)
 
     /** The from state of the last [TransitionState.STARTED] transition. */
@@ -269,6 +274,7 @@
     val startedKeyguardFromState: SharedFlow<KeyguardState> =
         startedKeyguardTransitionStep
             .map { step -> step.from }
+            .buffer(2, BufferOverflow.DROP_OLDEST)
             .shareIn(scope, SharingStarted.Eagerly, replay = 1)
 
     /** Which keyguard state to use when the device goes to sleep. */
@@ -310,8 +316,13 @@
      */
     @SuppressLint("SharedFlowCreation")
     val finishedKeyguardState: SharedFlow<KeyguardState> =
-        finishedKeyguardTransitionStep
-            .map { step -> step.to }
+        repository.transitions
+            .transform { step ->
+                if (step.transitionState == TransitionState.FINISHED) {
+                    emit(step.to)
+                }
+            }
+            .buffer(2, BufferOverflow.DROP_OLDEST)
             .shareIn(scope, SharingStarted.Eagerly, replay = 1)
 
     /**
@@ -398,7 +409,6 @@
      * including KeyguardSecurityContainerController and WindowManager.
      */
     fun startDismissKeyguardTransition(reason: String = "") {
-        // TODO(b/336576536): Check if adaptation for scene framework is needed
         if (SceneContainerFlag.isEnabled) return
         Log.d(TAG, "#startDismissKeyguardTransition(reason=$reason)")
         when (val startedState = repository.currentTransitionInfoInternal.value.to) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt
index 89c7178..d06ee64 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt
@@ -122,9 +122,14 @@
      * SHOW_WHEN_LOCKED activity, or back to [KeyguardState.GONE], for some power button launch
      * gesture cases. If so, start the transition.
      *
+     * @param startTransition A callback which is triggered to start the transition to the desired
+     *   KeyguardState. Allows caller to hook into the transition start if needed.
+     *
      * Returns true if a transition was started, false otherwise.
      */
-    suspend fun maybeStartTransitionToOccludedOrInsecureCamera(): Boolean {
+    suspend fun maybeStartTransitionToOccludedOrInsecureCamera(
+        startTransition: suspend (state: KeyguardState, reason: String) -> UUID?
+    ): Boolean {
         // The refactor is required for the occlusion interactor to work.
         KeyguardWmStateRefactor.isUnexpectedlyInLegacyMode()
 
@@ -136,10 +141,7 @@
             if (!maybeHandleInsecurePowerGesture()) {
                 // Otherwise, the double tap gesture occurred while not GONE and not dismissable,
                 // which means we will launch the secure camera, which OCCLUDES the keyguard.
-                startTransitionTo(
-                    KeyguardState.OCCLUDED,
-                    ownerReason = "Power button gesture on lockscreen"
-                )
+                startTransition(KeyguardState.OCCLUDED, "Power button gesture on lockscreen")
             }
 
             return true
@@ -147,10 +149,7 @@
             // A SHOW_WHEN_LOCKED activity is on top of the task stack. Transition to OCCLUDED so
             // it's visible.
             // TODO(b/307976454) - Centralize transition to DREAMING here.
-            startTransitionTo(
-                KeyguardState.OCCLUDED,
-                ownerReason = "SHOW_WHEN_LOCKED activity on top"
-            )
+            startTransition(KeyguardState.OCCLUDED, "SHOW_WHEN_LOCKED activity on top")
 
             return true
         } else {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractor.kt
index 3248114..b850095 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractor.kt
@@ -124,7 +124,7 @@
         }
     }
 
-    private fun finishCurrentTransition() {
+    private suspend fun finishCurrentTransition() {
         internalTransitionInteractor.updateTransition(currentTransitionId!!, 1f, FINISHED)
         resetTransitionData()
     }
@@ -223,7 +223,7 @@
         currentTransitionId = internalTransitionInteractor.startTransition(transitionInfo)
     }
 
-    private fun updateProgress(progress: Float) {
+    private suspend fun updateProgress(progress: Float) {
         if (currentTransitionId == null) return
         internalTransitionInteractor.updateTransition(
             currentTransitionId!!,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/CameraLaunchSourceModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/CameraLaunchSourceModel.kt
index 19baf77..c017651 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/CameraLaunchSourceModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/CameraLaunchSourceModel.kt
@@ -15,14 +15,8 @@
  */
 package com.android.systemui.keyguard.shared.model
 
-/** Camera launch sources */
-enum class CameraLaunchSourceModel {
-    /** Device is wiggled */
-    WIGGLE,
-    /** Power button has been double tapped */
-    POWER_DOUBLE_TAP,
-    /** Device has been lifted */
-    LIFT_TRIGGER,
-    /** Quick affordance button has been pressed */
-    QUICK_AFFORDANCE,
-}
+/** Camera launch source, with type and time detected */
+data class CameraLaunchSourceModel(
+    val type: CameraLaunchType = CameraLaunchType.IGNORE,
+    val detectedTime: Long = System.currentTimeMillis(),
+)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/CameraLaunchType.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/CameraLaunchType.kt
new file mode 100644
index 0000000..984abbb
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/CameraLaunchType.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+package com.android.systemui.keyguard.shared.model
+
+/** Camera launch sources */
+enum class CameraLaunchType {
+    /** Models no value */
+    IGNORE,
+    /** Device is wiggled */
+    WIGGLE,
+    /** Power button has been double tapped */
+    POWER_DOUBLE_TAP,
+    /** Device has been lifted */
+    LIFT_TRIGGER,
+    /** Quick affordance button has been pressed */
+    QUICK_AFFORDANCE,
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlow.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlow.kt
index 23aa21c..0cb8dd4 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlow.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlow.kt
@@ -177,7 +177,12 @@
          * Immediately (after 1ms) emits the given value for every step of the KeyguardTransition.
          */
         fun immediatelyTransitionTo(value: Float): Flow<Float> {
-            return sharedFlow(duration = 1.milliseconds, onStep = { value }, onFinish = { value })
+            return sharedFlow(
+                duration = 1.milliseconds,
+                onStep = { value },
+                onCancel = { value },
+                onFinish = { value }
+            )
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt
index db33acb..a250b22 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt
@@ -37,6 +37,7 @@
 import com.android.systemui.deviceentry.ui.binder.UdfpsAccessibilityOverlayBinder
 import com.android.systemui.deviceentry.ui.view.UdfpsAccessibilityOverlay
 import com.android.systemui.deviceentry.ui.viewmodel.AlternateBouncerUdfpsAccessibilityOverlayViewModel
+import com.android.systemui.keyguard.DismissCallbackRegistry
 import com.android.systemui.keyguard.ui.view.DeviceEntryIconView
 import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerDependencies
 import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerUdfpsIconViewModel
@@ -66,6 +67,7 @@
     private val alternateBouncerDependencies: Lazy<AlternateBouncerDependencies>,
     private val windowManager: Lazy<WindowManager>,
     private val layoutInflater: Lazy<LayoutInflater>,
+    private val dismissCallbackRegistry: DismissCallbackRegistry,
 ) : CoreStartable {
     private val layoutParams: WindowManager.LayoutParams
         get() =
@@ -162,6 +164,7 @@
 
             fun onBackRequested() {
                 alternateBouncerDependencies.get().viewModel.hideAlternateBouncer()
+                dismissCallbackRegistry.notifyDismissCancelled()
             }
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt
index 1b9788f..4d6577c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt
@@ -74,15 +74,26 @@
         val bgView = view.bgView
         longPressHandlingView.listener =
             object : LongPressHandlingView.Listener {
-                override fun onLongPressDetected(view: View, x: Int, y: Int, isA11yAction: Boolean) {
-                    if (!isA11yAction && falsingManager.isFalseLongTap(FalsingManager.LOW_PENALTY)) {
+                override fun onLongPressDetected(
+                    view: View,
+                    x: Int,
+                    y: Int,
+                    isA11yAction: Boolean
+                ) {
+                    if (
+                        !isA11yAction && falsingManager.isFalseLongTap(FalsingManager.LOW_PENALTY)
+                    ) {
                         return
                     }
                     vibratorHelper.performHapticFeedback(
                         view,
                         HapticFeedbackConstants.CONFIRM,
                     )
-                    applicationScope.launch { viewModel.onUserInteraction() }
+                    applicationScope.launch {
+                        view.clearFocus()
+                        view.clearAccessibilityFocus()
+                        viewModel.onUserInteraction()
+                    }
                 }
             }
 
@@ -95,6 +106,7 @@
                     launch("$TAG#viewModel.isVisible") {
                         viewModel.isVisible.collect { isVisible ->
                             longPressHandlingView.isInvisible = !isVisible
+                            view.isClickable = isVisible
                         }
                     }
                     launch("$TAG#viewModel.isLongPressEnabled") {
@@ -131,7 +143,11 @@
                                         view,
                                         HapticFeedbackConstants.CONFIRM,
                                     )
-                                    applicationScope.launch { viewModel.onUserInteraction() }
+                                    applicationScope.launch {
+                                        view.clearFocus()
+                                        view.clearAccessibilityFocus()
+                                        viewModel.onUserInteraction()
+                                    }
                                 }
                             } else {
                                 view.setOnClickListener(null)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardDismissActionBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardDismissActionBinder.kt
index b293027..74a7262 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardDismissActionBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardDismissActionBinder.kt
@@ -23,6 +23,7 @@
 import com.android.systemui.log.core.LogLevel
 import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import com.android.systemui.util.kotlin.sample
+import dagger.Lazy
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -34,7 +35,7 @@
 class KeyguardDismissActionBinder
 @Inject
 constructor(
-    private val interactor: KeyguardDismissActionInteractor,
+    private val interactorLazy: Lazy<KeyguardDismissActionInteractor>,
     @Application private val scope: CoroutineScope,
     private val keyguardLogger: KeyguardLogger,
 ) : CoreStartable {
@@ -44,6 +45,7 @@
             return
         }
 
+        val interactor = interactorLazy.get()
         scope.launch {
             interactor.executeDismissAction.collect {
                 log("executeDismissAction")
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardLongPressViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardLongPressViewBinder.kt
index b387855..830ef3b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardLongPressViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardLongPressViewBinder.kt
@@ -46,6 +46,7 @@
         onSingleTap: () -> Unit,
         falsingManager: FalsingManager,
     ) {
+        view.contentDescription = view.resources.getString(R.string.accessibility_desc_lock_screen)
         view.accessibilityHintLongPressAction =
             AccessibilityNodeInfo.AccessibilityAction(
                 AccessibilityNodeInfoCompat.ACTION_LONG_CLICK,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt
index b9a79dc..162a0d2 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt
@@ -30,6 +30,7 @@
 import androidx.core.view.updateLayoutParams
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.repeatOnLifecycle
+import com.android.keyguard.logging.KeyguardQuickAffordancesLogger
 import com.android.settingslib.Utils
 import com.android.systemui.animation.Expandable
 import com.android.systemui.animation.view.LaunchableImageView
@@ -74,6 +75,7 @@
         alpha: Flow<Float>,
         falsingManager: FalsingManager?,
         vibratorHelper: VibratorHelper?,
+        logger: KeyguardQuickAffordancesLogger,
         messageDisplayer: (Int) -> Unit,
     ): Binding {
         val button = view as ImageView
@@ -89,6 +91,7 @@
                                 falsingManager = falsingManager,
                                 messageDisplayer = messageDisplayer,
                                 vibratorHelper = vibratorHelper,
+                                logger = logger,
                             )
                         }
                     }
@@ -131,6 +134,7 @@
         falsingManager: FalsingManager?,
         messageDisplayer: (Int) -> Unit,
         vibratorHelper: VibratorHelper?,
+        logger: KeyguardQuickAffordancesLogger,
     ) {
         if (!viewModel.isVisible) {
             view.isInvisible = true
@@ -228,6 +232,7 @@
                     shakeAnimator.start()
 
                     vibratorHelper?.vibrate(KeyguardBottomAreaVibrations.Shake)
+                    logger.logQuickAffordanceTapped(viewModel.configKey)
                 }
                 view.onLongClickListener =
                     OnLongClickListener(falsingManager, viewModel, vibratorHelper, onTouchListener)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
index f96f053..91b66c3 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
@@ -207,14 +207,12 @@
                         launch {
                             viewModel.burnInLayerVisibility.collect { visibility ->
                                 childViews[burnInLayerId]?.visibility = visibility
-                                childViews[aodNotificationIconContainerId]?.visibility = visibility
                             }
                         }
 
                         launch {
                             viewModel.burnInLayerAlpha.collect { alpha ->
                                 childViews[statusViewId]?.alpha = alpha
-                                childViews[aodNotificationIconContainerId]?.alpha = alpha
                             }
                         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
index bc5b7b9..6faca1e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
@@ -50,6 +50,7 @@
 import com.android.internal.policy.SystemBarUtils
 import com.android.keyguard.ClockEventController
 import com.android.keyguard.KeyguardClockSwitch
+import com.android.keyguard.logging.KeyguardQuickAffordancesLogger
 import com.android.systemui.animation.view.LaunchableImageView
 import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor
 import com.android.systemui.broadcast.BroadcastDispatcher
@@ -147,6 +148,7 @@
     private val defaultShortcutsSection: DefaultShortcutsSection,
     private val keyguardClockInteractor: KeyguardClockInteractor,
     private val keyguardClockViewModel: KeyguardClockViewModel,
+    private val quickAffordancesLogger: KeyguardQuickAffordancesLogger,
 ) {
     val hostToken: IBinder? = bundle.getBinder(KEY_HOST_TOKEN)
     private val width: Int = bundle.getInt(KEY_VIEW_WIDTH)
@@ -462,6 +464,7 @@
                     alpha = flowOf(1f),
                     falsingManager = falsingManager,
                     vibratorHelper = vibratorHelper,
+                    logger = quickAffordancesLogger,
                 ) { message ->
                     indicationController.showTransientIndication(message)
                 }
@@ -476,6 +479,7 @@
                     alpha = flowOf(1f),
                     falsingManager = falsingManager,
                     vibratorHelper = vibratorHelper,
+                    logger = quickAffordancesLogger,
                 ) { message ->
                     indicationController.showTransientIndication(message)
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/DeviceEntryIconTransitionModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/DeviceEntryIconTransitionModule.kt
index dc7a649..0032c2f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/DeviceEntryIconTransitionModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/DeviceEntryIconTransitionModule.kt
@@ -18,6 +18,7 @@
 import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerToAodTransitionViewModel
 import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerToDozingTransitionViewModel
 import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerToGoneTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerToLockscreenTransitionViewModel
 import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerToOccludedTransitionViewModel
 import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerToPrimaryBouncerTransitionViewModel
 import com.android.systemui.keyguard.ui.viewmodel.AodToGoneTransitionViewModel
@@ -80,6 +81,12 @@
 
     @Binds
     @IntoSet
+    abstract fun alternateBouncerToLockscreen(
+        impl: AlternateBouncerToLockscreenTransitionViewModel
+    ): DeviceEntryIconTransition
+
+    @Binds
+    @IntoSet
     abstract fun alternateBouncerToOccluded(
         impl: AlternateBouncerToOccludedTransitionViewModel
     ): DeviceEntryIconTransition
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/DeviceEntryIconView.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/DeviceEntryIconView.kt
index 1c63235..3e6d5da 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/DeviceEntryIconView.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/DeviceEntryIconView.kt
@@ -247,6 +247,7 @@
         lp.height = ViewGroup.LayoutParams.MATCH_PARENT
         lp.width = ViewGroup.LayoutParams.MATCH_PARENT
         bgView.layoutParams = lp
+        bgView.alpha = 0f
     }
 
     fun getIconState(icon: IconType, aod: Boolean): IntArray {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AlignShortcutsToUdfpsSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AlignShortcutsToUdfpsSection.kt
index 2e96638..1ba830b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AlignShortcutsToUdfpsSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AlignShortcutsToUdfpsSection.kt
@@ -25,6 +25,7 @@
 import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
 import androidx.constraintlayout.widget.ConstraintSet.RIGHT
 import androidx.constraintlayout.widget.ConstraintSet.TOP
+import com.android.keyguard.logging.KeyguardQuickAffordancesLogger
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
 import com.android.systemui.keyguard.KeyguardBottomAreaRefactor
@@ -47,6 +48,7 @@
     private val falsingManager: FalsingManager,
     private val indicationController: KeyguardIndicationController,
     private val vibratorHelper: VibratorHelper,
+    private val shortcutsLogger: KeyguardQuickAffordancesLogger,
 ) : BaseShortcutSection() {
     override fun addViews(constraintLayout: ConstraintLayout) {
         if (KeyguardBottomAreaRefactor.isEnabled) {
@@ -64,6 +66,7 @@
                     keyguardQuickAffordancesCombinedViewModel.transitionAlpha,
                     falsingManager,
                     vibratorHelper,
+                    shortcutsLogger,
                 ) {
                     indicationController.showTransientIndication(it)
                 }
@@ -74,6 +77,7 @@
                     keyguardQuickAffordancesCombinedViewModel.transitionAlpha,
                     falsingManager,
                     vibratorHelper,
+                    shortcutsLogger,
                 ) {
                     indicationController.showTransientIndication(it)
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultShortcutsSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultShortcutsSection.kt
index 9146c60..64c46db 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultShortcutsSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultShortcutsSection.kt
@@ -26,6 +26,7 @@
 import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
 import androidx.constraintlayout.widget.ConstraintSet.RIGHT
 import androidx.constraintlayout.widget.ConstraintSet.VISIBILITY_MODE_IGNORE
+import com.android.keyguard.logging.KeyguardQuickAffordancesLogger
 import com.android.systemui.animation.view.LaunchableImageView
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.keyguard.KeyguardBottomAreaRefactor
@@ -52,6 +53,7 @@
     private val indicationController: KeyguardIndicationController,
     private val vibratorHelper: VibratorHelper,
     private val keyguardBlueprintInteractor: Lazy<KeyguardBlueprintInteractor>,
+    private val shortcutsLogger: KeyguardQuickAffordancesLogger,
 ) : BaseShortcutSection() {
 
     // Amount to increase the bottom margin by to avoid colliding with inset
@@ -86,6 +88,7 @@
                     keyguardQuickAffordancesCombinedViewModel.transitionAlpha,
                     falsingManager,
                     vibratorHelper,
+                    shortcutsLogger,
                 ) {
                     indicationController.showTransientIndication(it)
                 }
@@ -96,6 +99,7 @@
                     keyguardQuickAffordancesCombinedViewModel.transitionAlpha,
                     falsingManager,
                     vibratorHelper,
+                    shortcutsLogger,
                 ) {
                     indicationController.showTransientIndication(it)
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToAodTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToAodTransitionViewModel.kt
index 9da11ce..c590f07 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToAodTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToAodTransitionViewModel.kt
@@ -51,6 +51,7 @@
         transitionAnimation.sharedFlow(
             duration = FromAlternateBouncerTransitionInteractor.TO_AOD_DURATION,
             onStep = { 1 - it },
+            onCancel = { 0f },
             onFinish = { 0f },
         )
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToLockscreenTransitionViewModel.kt
new file mode 100644
index 0000000..b04521c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToLockscreenTransitionViewModel.kt
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.ui.viewmodel
+
+import android.util.MathUtils
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.domain.interactor.FromAlternateBouncerTransitionInteractor
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCER
+import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
+import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import javax.inject.Inject
+import kotlin.time.Duration.Companion.milliseconds
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+
+/**
+ * Breaks down ALTERNATE_BOUNCER->LOCKSCREEN transition into discrete steps for corresponding views
+ * to consume.
+ */
+@ExperimentalCoroutinesApi
+@SysUISingleton
+class AlternateBouncerToLockscreenTransitionViewModel
+@Inject
+constructor(
+    animationFlow: KeyguardTransitionAnimationFlow,
+) : DeviceEntryIconTransition {
+    private val transitionAnimation =
+        animationFlow.setup(
+            duration = FromAlternateBouncerTransitionInteractor.TO_LOCKSCREEN_DURATION,
+            edge = Edge.create(from = ALTERNATE_BOUNCER, to = LOCKSCREEN),
+        )
+
+    fun lockscreenAlpha(viewState: ViewStateAccessor): Flow<Float> {
+        var startAlpha = 1f
+        return transitionAnimation.sharedFlow(
+            duration = 250.milliseconds,
+            onStart = { startAlpha = viewState.alpha() },
+            onStep = { MathUtils.lerp(startAlpha, 1f, it) },
+        )
+    }
+
+    val deviceEntryBackgroundViewAlpha: Flow<Float> =
+        transitionAnimation.immediatelyTransitionTo(1f)
+    override val deviceEntryParentViewAlpha: Flow<Float> =
+        transitionAnimation.immediatelyTransitionTo(1f)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModel.kt
index b267ecb..6b22c0f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModel.kt
@@ -104,12 +104,10 @@
         transitionAnimation.sharedFlow(
             duration = 250.milliseconds,
             onStep = { it },
+            onCancel = { 1f },
             onFinish = { 1f },
         )
 
     override val deviceEntryParentViewAlpha: Flow<Float> =
-        transitionAnimation.sharedFlow(
-            duration = 500.milliseconds,
-            onStep = { 1f },
-        )
+        transitionAnimation.immediatelyTransitionTo(1f)
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToAodTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToAodTransitionViewModel.kt
index 1ee0368..7562392 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToAodTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToAodTransitionViewModel.kt
@@ -63,6 +63,7 @@
                 transitionAnimation.sharedFlow(
                     duration = FromDreamingTransitionInteractor.TO_AOD_DURATION,
                     onStep = { it },
+                    onCancel = { 1f },
                     onFinish = { 1f },
                 )
             } else {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToGlanceableHubTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToGlanceableHubTransitionViewModel.kt
index 00aa102..11ed52a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToGlanceableHubTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToGlanceableHubTransitionViewModel.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.keyguard.ui.viewmodel
 
+import android.util.LayoutDirection
 import com.android.app.animation.Interpolators.EMPHASIZED
 import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
 import com.android.systemui.dagger.SysUISingleton
@@ -54,7 +55,10 @@
 
     val dreamOverlayTranslationX: Flow<Float> =
         configurationInteractor
-            .dimensionPixelSize(R.dimen.dreaming_to_hub_transition_dream_overlay_translation_x)
+            .directionalDimensionPixelSize(
+                LayoutDirection.LTR,
+                R.dimen.dreaming_to_hub_transition_dream_overlay_translation_x
+            )
             .flatMapLatest { translatePx ->
                 transitionAnimation.sharedFlow(
                     duration = TO_GLANCEABLE_HUB_DURATION,
@@ -91,7 +95,7 @@
             startTime = 167.milliseconds,
             duration = 167.milliseconds,
             onStep = { it },
-            onCancel = { 0f },
+            onCancel = { 1f },
             onFinish = { 1f },
         )
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt
index 82381eb..b5ec7a6 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt
@@ -95,5 +95,11 @@
         )
 
     val deviceEntryBackgroundViewAlpha = transitionAnimation.immediatelyTransitionTo(1f)
-    override val deviceEntryParentViewAlpha = lockscreenAlpha
+    override val deviceEntryParentViewAlpha =
+        transitionAnimation.sharedFlow(
+            startTime = 233.milliseconds,
+            duration = 250.milliseconds,
+            onCancel = { 1f },
+            onStep = { it },
+        )
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToDreamingTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToDreamingTransitionViewModel.kt
index d594488..f69f996 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToDreamingTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToDreamingTransitionViewModel.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.keyguard.ui.viewmodel
 
+import android.util.LayoutDirection
 import com.android.app.animation.Interpolators
 import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
 import com.android.systemui.dagger.SysUISingleton
@@ -63,7 +64,10 @@
 
     val dreamOverlayTranslationX: Flow<Float> =
         configurationInteractor
-            .dimensionPixelSize(R.dimen.hub_to_dreaming_transition_dream_overlay_translation_x)
+            .directionalDimensionPixelSize(
+                LayoutDirection.LTR,
+                R.dimen.hub_to_dreaming_transition_dream_overlay_translation_x
+            )
             .flatMapLatest { translatePx: Int ->
                 transitionAnimation.sharedFlow(
                     duration = FROM_GLANCEABLE_HUB_DURATION,
@@ -89,7 +93,7 @@
         transitionAnimation.sharedFlow(
             duration = 167.milliseconds,
             onStep = { 1 - it },
-            onCancel = { 1f },
+            onCancel = { 0f },
             onFinish = { 0f },
         )
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToLockscreenTransitionViewModel.kt
index 046b95f..67b009e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToLockscreenTransitionViewModel.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.keyguard.ui.viewmodel
 
+import android.util.LayoutDirection
 import com.android.app.animation.Interpolators.EMPHASIZED
 import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
 import com.android.systemui.dagger.SysUISingleton
@@ -72,7 +73,10 @@
 
     val keyguardTranslationX: Flow<StateToValue> =
         configurationInteractor
-            .dimensionPixelSize(R.dimen.hub_to_lockscreen_transition_lockscreen_translation_x)
+            .directionalDimensionPixelSize(
+                LayoutDirection.LTR,
+                R.dimen.hub_to_lockscreen_transition_lockscreen_translation_x
+            )
             .flatMapLatest { translatePx: Int ->
                 transitionAnimation.sharedFlowWithState(
                     duration = TO_LOCKSCREEN_DURATION,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModel.kt
index 2bc8e51..43872b7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModel.kt
@@ -122,6 +122,7 @@
                     startTime = 1100.milliseconds,
                     duration = 200.milliseconds,
                     onStep = { it },
+                    onCancel = { 1f },
                     onFinish = { 1f },
                 )
             } else {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToGlanceableHubTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToGlanceableHubTransitionViewModel.kt
index 6a3573a..6d95ade 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToGlanceableHubTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToGlanceableHubTransitionViewModel.kt
@@ -43,7 +43,7 @@
         transitionAnimation.sharedFlow(
             duration = 167.milliseconds,
             onStep = { it },
-            onCancel = { 0f },
+            onCancel = { 1f },
             onFinish = { 1f },
         )
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
index 350ceb4..38a2b1b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
@@ -85,6 +85,8 @@
     private val notificationsKeyguardInteractor: NotificationsKeyguardInteractor,
     private val alternateBouncerToGoneTransitionViewModel:
         AlternateBouncerToGoneTransitionViewModel,
+    private val alternateBouncerToLockscreenTransitionViewModel:
+        AlternateBouncerToLockscreenTransitionViewModel,
     private val aodToGoneTransitionViewModel: AodToGoneTransitionViewModel,
     private val aodToLockscreenTransitionViewModel: AodToLockscreenTransitionViewModel,
     private val aodToOccludedTransitionViewModel: AodToOccludedTransitionViewModel,
@@ -238,6 +240,7 @@
                         alphaOnShadeExpansion,
                         keyguardInteractor.dismissAlpha,
                         alternateBouncerToGoneTransitionViewModel.lockscreenAlpha(viewState),
+                        alternateBouncerToLockscreenTransitionViewModel.lockscreenAlpha(viewState),
                         aodToGoneTransitionViewModel.lockscreenAlpha(viewState),
                         aodToLockscreenTransitionViewModel.lockscreenAlpha(viewState),
                         aodToOccludedTransitionViewModel.lockscreenAlpha(viewState),
@@ -317,19 +320,24 @@
     val isNotifIconContainerVisible: StateFlow<AnimatedValue<Boolean>> =
         combine(
                 goneToAodTransitionRunning,
+                keyguardTransitionInteractor
+                    .transitionValue(LOCKSCREEN)
+                    .map { it > 0f }
+                    .onStart { emit(false) },
                 keyguardTransitionInteractor.finishedKeyguardState.map {
                     KeyguardState.lockscreenVisibleInState(it)
                 },
                 deviceEntryInteractor.isBypassEnabled,
                 areNotifsFullyHiddenAnimated(),
                 isPulseExpandingAnimated(),
-            ) {
-                goneToAodTransitionRunning: Boolean,
-                onKeyguard: Boolean,
-                isBypassEnabled: Boolean,
-                notifsFullyHidden: AnimatedValue<Boolean>,
-                pulseExpanding: AnimatedValue<Boolean>,
-                ->
+            ) { flows ->
+                val goneToAodTransitionRunning = flows[0] as Boolean
+                val isOnLockscreen = flows[1] as Boolean
+                val onKeyguard = flows[2] as Boolean
+                val isBypassEnabled = flows[3] as Boolean
+                val notifsFullyHidden = flows[4] as AnimatedValue<Boolean>
+                val pulseExpanding = flows[5] as AnimatedValue<Boolean>
+
                 when {
                     // Hide the AOD icons if we're not in the KEYGUARD state unless the screen off
                     // animation is playing, in which case we want them to be visible if we're
@@ -348,6 +356,8 @@
                                 isBypassEnabled -> true
                                 // If we are pulsing (and not bypassing), then we are hidden
                                 isPulseExpanding -> false
+                                // Besides bypass above, they should not be visible on lockscreen
+                                isOnLockscreen -> false
                                 // If notifs are fully gone, then we're visible
                                 areNotifsFullyHidden -> true
                                 // Otherwise, we're hidden
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt
index 4bfefda..3fffeff 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt
@@ -17,7 +17,7 @@
 package com.android.systemui.keyguard.ui.viewmodel
 
 import android.content.res.Resources
-import com.android.compose.animation.scene.SceneKey
+import com.android.compose.animation.scene.ContentKey
 import com.android.internal.annotations.VisibleForTesting
 import com.android.systemui.biometrics.AuthController
 import com.android.systemui.dagger.SysUISingleton
@@ -90,12 +90,12 @@
 
     /**
      * Returns a flow that indicates whether lockscreen notifications should be rendered in the
-     * given [sceneKey].
+     * given [contentKey].
      */
-    fun areNotificationsVisible(sceneKey: SceneKey): Flow<Boolean> {
+    fun areNotificationsVisible(contentKey: ContentKey): Flow<Boolean> {
         // `Scenes.NotificationsShade` renders its own separate notifications stack, so when it's
         // open we avoid rendering the lockscreen notifications stack.
-        if (sceneKey == Scenes.NotificationsShade) {
+        if (contentKey == Scenes.NotificationsShade) {
             return flowOf(false)
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt
index 630dcca..15892e9 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt
@@ -25,7 +25,6 @@
 import com.android.compose.animation.scene.UserActionResult
 import com.android.systemui.communal.domain.interactor.CommunalInteractor
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
 import com.android.systemui.scene.shared.model.SceneFamilies
 import com.android.systemui.scene.shared.model.Scenes
@@ -35,93 +34,70 @@
 import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationsPlaceholderViewModel
 import com.android.systemui.util.kotlin.filterValuesNotNull
 import javax.inject.Inject
-import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.SharingStarted
-import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.flowOf
-import kotlinx.coroutines.flow.stateIn
 
 /** Models UI state and handles user input for the lockscreen scene. */
 @SysUISingleton
 class LockscreenSceneViewModel
 @Inject
 constructor(
-    @Application applicationScope: CoroutineScope,
-    deviceEntryInteractor: DeviceEntryInteractor,
-    communalInteractor: CommunalInteractor,
-    shadeInteractor: ShadeInteractor,
+    private val deviceEntryInteractor: DeviceEntryInteractor,
+    private val communalInteractor: CommunalInteractor,
+    private val shadeInteractor: ShadeInteractor,
     val touchHandling: KeyguardTouchHandlingViewModel,
     val notifications: NotificationsPlaceholderViewModel,
 ) {
-    val destinationScenes: StateFlow<Map<UserAction, UserActionResult>> =
-        shadeInteractor.isShadeTouchable
-            .flatMapLatest { isShadeTouchable ->
-                if (!isShadeTouchable) {
-                    flowOf(emptyMap())
-                } else {
-                    combine(
-                        deviceEntryInteractor.isUnlocked,
-                        communalInteractor.isCommunalAvailable,
-                        shadeInteractor.shadeMode,
-                    ) { isDeviceUnlocked, isCommunalAvailable, shadeMode ->
-                        destinationScenes(
-                            isDeviceUnlocked = isDeviceUnlocked,
-                            isCommunalAvailable = isCommunalAvailable,
-                            shadeMode = shadeMode,
+    val destinationScenes: Flow<Map<UserAction, UserActionResult>> =
+        shadeInteractor.isShadeTouchable.flatMapLatest { isShadeTouchable ->
+            if (!isShadeTouchable) {
+                flowOf(emptyMap())
+            } else {
+                combine(
+                    deviceEntryInteractor.isUnlocked,
+                    communalInteractor.isCommunalAvailable,
+                    shadeInteractor.shadeMode,
+                ) { isDeviceUnlocked, isCommunalAvailable, shadeMode ->
+                    val notifShadeSceneKey =
+                        UserActionResult(
+                            toScene = SceneFamilies.NotifShade,
+                            transitionKey = ToSplitShade.takeIf { shadeMode is ShadeMode.Split },
                         )
-                    }
+
+                    mapOf(
+                            Swipe.Left to
+                                UserActionResult(Scenes.Communal).takeIf { isCommunalAvailable },
+                            Swipe.Up to if (isDeviceUnlocked) Scenes.Gone else Scenes.Bouncer,
+
+                            // Swiping down from the top edge goes to QS (or shade if in split shade
+                            // mode).
+                            swipeDownFromTop(pointerCount = 1) to
+                                if (shadeMode is ShadeMode.Single) {
+                                    UserActionResult(Scenes.QuickSettings)
+                                } else {
+                                    notifShadeSceneKey
+                                },
+
+                            // TODO(b/338577208): Remove once we add Dual Shade invocation zones.
+                            swipeDownFromTop(pointerCount = 2) to
+                                UserActionResult(
+                                    toScene = SceneFamilies.QuickSettings,
+                                    transitionKey =
+                                        ToSplitShade.takeIf { shadeMode is ShadeMode.Split }
+                                ),
+
+                            // Swiping down, not from the edge, always navigates to the notif shade
+                            // scene.
+                            swipeDown(pointerCount = 1) to notifShadeSceneKey,
+                            swipeDown(pointerCount = 2) to notifShadeSceneKey,
+                        )
+                        .filterValuesNotNull()
                 }
             }
-            .stateIn(
-                scope = applicationScope,
-                started = SharingStarted.WhileSubscribed(),
-                initialValue =
-                    destinationScenes(
-                        isDeviceUnlocked = deviceEntryInteractor.isUnlocked.value,
-                        isCommunalAvailable = false,
-                        shadeMode = shadeInteractor.shadeMode.value,
-                    ),
-            )
-
-    private fun destinationScenes(
-        isDeviceUnlocked: Boolean,
-        isCommunalAvailable: Boolean,
-        shadeMode: ShadeMode,
-    ): Map<UserAction, UserActionResult> {
-        val notifShadeSceneKey =
-            UserActionResult(
-                toScene = SceneFamilies.NotifShade,
-                transitionKey = ToSplitShade.takeIf { shadeMode is ShadeMode.Split },
-            )
-
-        return mapOf(
-                Swipe.Left to UserActionResult(Scenes.Communal).takeIf { isCommunalAvailable },
-                Swipe.Up to if (isDeviceUnlocked) Scenes.Gone else Scenes.Bouncer,
-
-                // Swiping down from the top edge goes to QS (or shade if in split shade mode).
-                swipeDownFromTop(pointerCount = 1) to
-                    if (shadeMode is ShadeMode.Single) {
-                        UserActionResult(Scenes.QuickSettings)
-                    } else {
-                        notifShadeSceneKey
-                    },
-
-                // TODO(b/338577208): Remove once we add Dual Shade invocation zones.
-                swipeDownFromTop(pointerCount = 2) to
-                    UserActionResult(
-                        toScene = SceneFamilies.QuickSettings,
-                        transitionKey = ToSplitShade.takeIf { shadeMode is ShadeMode.Split }
-                    ),
-
-                // Swiping down, not from the edge, always navigates to the notif shade scene.
-                swipeDown(pointerCount = 1) to notifShadeSceneKey,
-                swipeDown(pointerCount = 2) to notifShadeSceneKey,
-            )
-            .filterValuesNotNull()
-    }
+        }
 
     private fun swipeDownFromTop(pointerCount: Int): Swipe {
         return Swipe(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModel.kt
index 5408428..2f21ebc 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModel.kt
@@ -70,6 +70,7 @@
                 transitionAnimation.sharedFlow(
                     duration = 300.milliseconds,
                     onStep = { 1 - it },
+                    onCancel = { 0f },
                     onFinish = { 0f },
                 ),
         )
@@ -153,6 +154,7 @@
                     transitionAnimation.sharedFlow(
                             duration = 300.milliseconds,
                             onStep = { it },
+                            onCancel = { 1f },
                             onFinish = { 1f },
                         ),
                     flowWhenShadeIsNotExpanded = transitionAnimation.immediatelyTransitionTo(1f),
@@ -164,6 +166,7 @@
                     transitionAnimation.sharedFlow(
                             duration = 200.milliseconds,
                             onStep = { 1f - it },
+                            onCancel = { 0f },
                             onFinish = { 0f },
                         ),
                 )
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModel.kt
index 579abeb..bbb55cc 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModel.kt
@@ -74,7 +74,12 @@
 
     override val deviceEntryParentViewAlpha: Flow<Float> =
         shadeDependentFlows.transitionFlow(
-            flowWhenShadeIsNotExpanded = lockscreenAlpha,
+            flowWhenShadeIsNotExpanded =
+                transitionAnimation.sharedFlow(
+                    duration = 250.milliseconds,
+                    onStep = { 1f - it },
+                    onCancel = { 0f },
+                ),
             flowWhenShadeIsExpanded = transitionAnimation.immediatelyTransitionTo(0f),
         )
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGlanceableHubTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGlanceableHubTransitionViewModel.kt
index c7273b7..378374e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGlanceableHubTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGlanceableHubTransitionViewModel.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.keyguard.ui.viewmodel
 
+import android.util.LayoutDirection
 import com.android.app.animation.Interpolators.EMPHASIZED
 import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
 import com.android.systemui.dagger.SysUISingleton
@@ -71,7 +72,10 @@
 
     val keyguardTranslationX: Flow<StateToValue> =
         configurationInteractor
-            .dimensionPixelSize(R.dimen.lockscreen_to_hub_transition_lockscreen_translation_x)
+            .directionalDimensionPixelSize(
+                LayoutDirection.LTR,
+                R.dimen.lockscreen_to_hub_transition_lockscreen_translation_x
+            )
             .flatMapLatest { translatePx: Int ->
                 transitionAnimation.sharedFlowWithState(
                     duration = FromLockscreenTransitionInteractor.TO_GLANCEABLE_HUB_DURATION,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModel.kt
index fcf8c14f..8d9ccef 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModel.kt
@@ -83,7 +83,12 @@
 
     override val deviceEntryParentViewAlpha: Flow<Float> =
         shadeDependentFlows.transitionFlow(
-            flowWhenShadeIsNotExpanded = lockscreenAlpha,
+            flowWhenShadeIsNotExpanded =
+                transitionAnimation.sharedFlow(
+                    duration = 250.milliseconds,
+                    onStep = { 1f - it },
+                    onCancel = { 0f },
+                ),
             flowWhenShadeIsExpanded = transitionAnimation.immediatelyTransitionTo(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 23c44b0a..e64c614 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
@@ -65,6 +65,7 @@
                 transitionAnimation.sharedFlow(
                     duration = 250.milliseconds,
                     onStep = { 1f - it },
+                    onCancel = { 0f },
                     onFinish = { 0f }
                 ),
             flowWhenShadeIsExpanded = transitionAnimation.immediatelyTransitionTo(0f)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt
index 36c7d5b..737bd7a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt
@@ -110,5 +110,11 @@
     val deviceEntryBackgroundViewAlpha: Flow<Float> =
         transitionAnimation.immediatelyTransitionTo(1f)
 
-    override val deviceEntryParentViewAlpha: Flow<Float> = lockscreenAlpha
+    override val deviceEntryParentViewAlpha: Flow<Float> =
+        transitionAnimation.sharedFlow(
+            startTime = 233.milliseconds,
+            duration = 250.milliseconds,
+            onStep = { it },
+            onCancel = { 1f },
+        )
 }
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 009f85d..501feca 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
@@ -70,6 +70,7 @@
                 transitionAnimation.sharedFlow(
                     duration = 300.milliseconds,
                     onStep = { it },
+                    onCancel = { 1f },
                     onFinish = { 1f },
                 )
             } else {
diff --git a/packages/SystemUI/src/com/android/systemui/lifecycle/RepeatWhenAttached.kt b/packages/SystemUI/src/com/android/systemui/lifecycle/RepeatWhenAttached.kt
index bf80e18..661da6d 100644
--- a/packages/SystemUI/src/com/android/systemui/lifecycle/RepeatWhenAttached.kt
+++ b/packages/SystemUI/src/com/android/systemui/lifecycle/RepeatWhenAttached.kt
@@ -30,10 +30,21 @@
 import com.android.systemui.Flags.coroutineTracing
 import com.android.systemui.util.Assert
 import com.android.systemui.util.Compile
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
+import com.android.systemui.utils.coroutines.flow.flatMapLatestConflated
 import kotlin.coroutines.CoroutineContext
 import kotlin.coroutines.EmptyCoroutineContext
+import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.DisposableHandle
+import kotlinx.coroutines.awaitCancellation
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.collectLatest
+import kotlinx.coroutines.flow.emptyFlow
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onStart
 
 /**
  * Runs the given [block] every time the [View] becomes attached (or immediately after calling this
@@ -216,6 +227,102 @@
 }
 
 /**
+ * Runs the given [block] every time the [View] becomes attached (or immediately after calling this
+ * function, if the view was already attached), automatically canceling the work when the view
+ * becomes detached.
+ *
+ * Only use from the main thread.
+ *
+ * The [block] may be run multiple times, running once per every time the view is attached.
+ */
+@MainThread
+suspend fun View.repeatWhenAttachedToWindow(block: suspend CoroutineScope.() -> Unit): Nothing {
+    Assert.isMainThread()
+    isAttached.collectLatest { if (it) coroutineScope { block() } }
+    awaitCancellation() // satisfies return type of Nothing
+}
+
+/**
+ * Runs the given [block] every time the [Window] this [View] is attached to becomes visible (or
+ * immediately after calling this function, if the window is already visible), automatically
+ * canceling the work when the window becomes invisible.
+ *
+ * Only use from the main thread.
+ *
+ * The [block] may be run multiple times, running once per every time the window becomes visible.
+ */
+@MainThread
+suspend fun View.repeatWhenWindowIsVisible(block: suspend CoroutineScope.() -> Unit): Nothing {
+    Assert.isMainThread()
+    isWindowVisible.collectLatest { if (it) coroutineScope { block() } }
+    awaitCancellation() // satisfies return type of Nothing
+}
+
+/**
+ * Runs the given [block] every time the [Window] this [View] is attached to has focus (or
+ * immediately after calling this function, if the window is already focused), automatically
+ * canceling the work when the window loses focus.
+ *
+ * Only use from the main thread.
+ *
+ * The [block] may be run multiple times, running once per every time the window is focused.
+ */
+@MainThread
+suspend fun View.repeatWhenWindowHasFocus(block: suspend CoroutineScope.() -> Unit): Nothing {
+    Assert.isMainThread()
+    isWindowFocused.collectLatest { if (it) coroutineScope { block() } }
+    awaitCancellation() // satisfies return type of Nothing
+}
+
+private val View.isAttached
+    get() = conflatedCallbackFlow {
+        val onAttachListener =
+            object : View.OnAttachStateChangeListener {
+                override fun onViewAttachedToWindow(v: View) {
+                    Assert.isMainThread()
+                    trySend(true)
+                }
+
+                override fun onViewDetachedFromWindow(v: View) {
+                    trySend(false)
+                }
+            }
+        addOnAttachStateChangeListener(onAttachListener)
+        trySend(isAttachedToWindow)
+        awaitClose { removeOnAttachStateChangeListener(onAttachListener) }
+    }
+
+private val View.currentViewTreeObserver: Flow<ViewTreeObserver?>
+    get() = isAttached.map { if (it) viewTreeObserver else null }
+
+private val View.isWindowVisible
+    get() =
+        currentViewTreeObserver.flatMapLatestConflated { vto ->
+            vto?.isWindowVisible?.onStart { emit(windowVisibility == View.VISIBLE) } ?: emptyFlow()
+        }
+
+private val View.isWindowFocused
+    get() =
+        currentViewTreeObserver.flatMapLatestConflated { vto ->
+            vto?.isWindowFocused?.onStart { emit(hasWindowFocus()) } ?: emptyFlow()
+        }
+
+private val ViewTreeObserver.isWindowFocused
+    get() = conflatedCallbackFlow {
+        val listener = ViewTreeObserver.OnWindowFocusChangeListener { trySend(it) }
+        addOnWindowFocusChangeListener(listener)
+        awaitClose { removeOnWindowFocusChangeListener(listener) }
+    }
+
+private val ViewTreeObserver.isWindowVisible
+    get() = conflatedCallbackFlow {
+        val listener =
+            ViewTreeObserver.OnWindowVisibilityChangeListener { v -> trySend(v == View.VISIBLE) }
+        addOnWindowVisibilityChangeListener(listener)
+        awaitClose { removeOnWindowVisibilityChangeListener(listener) }
+    }
+
+/**
  * Even though there is only has one usage of `Dispatchers.Main` in this file, we cache it in a
  * top-level property so that we do not unnecessarily create new `CoroutineContext` objects for
  * tracing on each call to [repeatWhenAttached]. It is okay to reuse a single instance of the
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/PartitionedGridLayoutKosmos.kt b/packages/SystemUI/src/com/android/systemui/log/dagger/KeyguardQuickAffordancesLog.kt
similarity index 63%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/PartitionedGridLayoutKosmos.kt
copy to packages/SystemUI/src/com/android/systemui/log/dagger/KeyguardQuickAffordancesLog.kt
index 37c9552..e9cf7e2 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/PartitionedGridLayoutKosmos.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/KeyguardQuickAffordancesLog.kt
@@ -14,11 +14,12 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.panels.domain.interactor
+package com.android.systemui.log.dagger
 
-import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.qs.panels.ui.compose.PartitionedGridLayout
-import com.android.systemui.qs.panels.ui.viewmodel.partitionedGridViewModel
+import javax.inject.Qualifier
 
-val Kosmos.partitionedGridLayout by
-    Kosmos.Fixture { PartitionedGridLayout(partitionedGridViewModel) }
+/** A [com.android.systemui.log.LogBuffer] for keyguard quick affordances related stuff. */
+@Qualifier
+@MustBeDocumented
+@Retention(AnnotationRetention.RUNTIME)
+annotation class KeyguardQuickAffordancesLog
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
index b2ba0e1..40bb8e1 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
@@ -568,6 +568,16 @@
     }
 
     /**
+     * Provides a {@link LogBuffer} for keyguard quick affordances-related logs.
+     */
+    @Provides
+    @SysUISingleton
+    @KeyguardQuickAffordancesLog
+    public static LogBuffer provideKeyguardQuickAffordancesLogBuffer(LogBufferFactory factory) {
+        return factory.create("KeyguardQuickAffordancesLog", 25);
+    }
+
+    /**
      * Provides a {@link LogBuffer} for keyguard transition animation logs.
      */
     @Provides
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java
index 69a157f..addb014 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java
@@ -19,6 +19,7 @@
 import static android.provider.Settings.ACTION_MEDIA_CONTROLS_SETTINGS;
 
 import static com.android.settingslib.flags.Flags.legacyLeAudioSharing;
+import static com.android.systemui.Flags.communalHub;
 import static com.android.systemui.Flags.mediaLockscreenLaunchAnimation;
 import static com.android.systemui.media.controls.shared.model.SmartspaceMediaDataKt.NUM_REQUIRED_RECOMMENDATIONS;
 
@@ -90,6 +91,8 @@
 import com.android.systemui.animation.GhostedViewTransitionAnimatorController;
 import com.android.systemui.bluetooth.BroadcastDialogController;
 import com.android.systemui.broadcast.BroadcastSender;
+import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor;
+import com.android.systemui.communal.widgets.CommunalTransitionAnimatorController;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.media.controls.domain.pipeline.MediaDataManager;
@@ -202,10 +205,12 @@
     );
 
     // Time in millis for playing turbulence noise that is played after a touch ripple.
-    @VisibleForTesting static final long TURBULENCE_NOISE_PLAY_DURATION = 7500L;
+    @VisibleForTesting
+    static final long TURBULENCE_NOISE_PLAY_DURATION = 7500L;
 
     private final SeekBarViewModel mSeekBarViewModel;
     private final MediaFlags mMediaFlags;
+    private final CommunalSceneInteractor mCommunalSceneInteractor;
     private SeekBarObserver mSeekBarObserver;
     protected final Executor mBackgroundExecutor;
     private final DelayableExecutor mMainExecutor;
@@ -293,8 +298,9 @@
      * Initialize a new control panel
      *
      * @param backgroundExecutor background executor, used for processing artwork
-     * @param mainExecutor main thread executor, used if we receive callbacks on the background
-     *                     thread that then trigger UI changes.
+     * @param mainExecutor       main thread executor, used if we receive callbacks on the
+     *                           background
+     *                           thread that then trigger UI changes.
      * @param activityStarter    activity starter
      */
     @Inject
@@ -314,6 +320,7 @@
             MediaUiEventLogger logger,
             KeyguardStateController keyguardStateController,
             ActivityIntentHelper activityIntentHelper,
+            CommunalSceneInteractor communalSceneInteractor,
             NotificationLockscreenUserManager lockscreenUserManager,
             BroadcastDialogController broadcastDialogController,
             GlobalSettings globalSettings,
@@ -337,6 +344,7 @@
         mLockscreenUserManager = lockscreenUserManager;
         mBroadcastDialogController = broadcastDialogController;
         mMediaFlags = mediaFlags;
+        mCommunalSceneInteractor = communalSceneInteractor;
 
         mSeekBarViewModel.setLogSeek(() -> {
             if (mPackageName != null && mInstanceId != null) {
@@ -375,6 +383,7 @@
 
     /**
      * Get the recommendation view holder used to display Smartspace media recs.
+     *
      * @return the recommendation view holder
      */
     @Nullable
@@ -693,7 +702,7 @@
             // TODO(b/233698402): Use the package name instead of app label to avoid the
             // unexpected result.
             mIsCurrentBroadcastedApp = device != null
-                && TextUtils.equals(device.getName(),
+                    && TextUtils.equals(device.getName(),
                     mContext.getString(R.string.broadcasting_description_is_broadcasting));
             useDisabledAlpha = !mIsCurrentBroadcastedApp;
             // Always be enabled if the broadcast button is shown
@@ -764,7 +773,7 @@
                             PendingIntent deviceIntent = device.getIntent();
                             boolean showOverLockscreen = mKeyguardStateController.isShowing()
                                     && mActivityIntentHelper.wouldPendingShowOverLockscreen(
-                                        deviceIntent, mLockscreenUserManager.getCurrentUserId());
+                                    deviceIntent, mLockscreenUserManager.getCurrentUserId());
                             if (deviceIntent.isActivity()) {
                                 if (!showOverLockscreen) {
                                     mActivityStarter.postStartActivityDismissingKeyguard(
@@ -825,24 +834,26 @@
         ConstraintSet expandedSet = mMediaViewController.getExpandedLayout();
         ConstraintSet collapsedSet = mMediaViewController.getCollapsedLayout();
         return mMetadataAnimationHandler.setNext(
-            new Triple(data.getSong(), data.getArtist(), data.isExplicit()),
-            () -> {
-                titleText.setText(data.getSong());
-                artistText.setText(data.getArtist());
-                setVisibleAndAlpha(expandedSet, R.id.media_explicit_indicator, data.isExplicit());
-                setVisibleAndAlpha(collapsedSet, R.id.media_explicit_indicator, data.isExplicit());
+                new Triple(data.getSong(), data.getArtist(), data.isExplicit()),
+                () -> {
+                    titleText.setText(data.getSong());
+                    artistText.setText(data.getArtist());
+                    setVisibleAndAlpha(expandedSet, R.id.media_explicit_indicator,
+                            data.isExplicit());
+                    setVisibleAndAlpha(collapsedSet, R.id.media_explicit_indicator,
+                            data.isExplicit());
 
-                // refreshState is required here to resize the text views (and prevent ellipsis)
-                mMediaViewController.refreshState();
-                return Unit.INSTANCE;
-            },
-            () -> {
-                // After finishing the enter animation, we refresh state. This could pop if
-                // something is incorrectly bound, but needs to be run if other elements were
-                // updated while the enter animation was running
-                mMediaViewController.refreshState();
-                return Unit.INSTANCE;
-            });
+                    // refreshState is required here to resize the text views (and prevent ellipsis)
+                    mMediaViewController.refreshState();
+                    return Unit.INSTANCE;
+                },
+                () -> {
+                    // After finishing the enter animation, we refresh state. This could pop if
+                    // something is incorrectly bound, but needs to be run if other elements were
+                    // updated while the enter animation was running
+                    mMediaViewController.refreshState();
+                    return Unit.INSTANCE;
+                });
     }
 
     // We may want to look into unifying this with bindRecommendationContentDescription if/when we
@@ -1105,7 +1116,7 @@
 
     private LayerDrawable setupGradientColorOnDrawable(Drawable albumArt, GradientDrawable gradient,
             ColorScheme mutableColorScheme, float startAlpha, float endAlpha) {
-        gradient.setColors(new int[] {
+        gradient.setColors(new int[]{
                 ColorUtilKt.getColorWithAlpha(
                         MediaColorSchemesKt.backgroundStartFromScheme(mutableColorScheme),
                         startAlpha),
@@ -1113,7 +1124,7 @@
                         MediaColorSchemesKt.backgroundEndFromScheme(mutableColorScheme),
                         endAlpha),
         });
-        return new LayerDrawable(new Drawable[] { albumArt, gradient });
+        return new LayerDrawable(new Drawable[]{albumArt, gradient});
     }
 
     private void scaleTransitionDrawableLayer(TransitionDrawable transitionDrawable, int layer,
@@ -1143,7 +1154,7 @@
         ConstraintSet collapsedSet = mMediaViewController.getCollapsedLayout();
         if (semanticActions != null) {
             // Hide all the generic buttons
-            for (ImageButton b: genericButtons) {
+            for (ImageButton b : genericButtons) {
                 setVisibleAndAlpha(collapsedSet, b.getId(), false);
                 setVisibleAndAlpha(expandedSet, b.getId(), false);
             }
@@ -1346,6 +1357,7 @@
                 /* shouldInverseNoiseLuminosity= */ false
         );
     }
+
     private void clearButton(final ImageButton button) {
         button.setImageDrawable(null);
         button.setContentDescription(null);
@@ -1421,19 +1433,33 @@
 
         // TODO(b/174236650): Make sure that the carousel indicator also fades out.
         // TODO(b/174236650): Instrument the animation to measure jank.
-        return new GhostedViewTransitionAnimatorController(player,
-                InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_MEDIA_PLAYER) {
-            @Override
-            protected float getCurrentTopCornerRadius() {
-                return mContext.getResources().getDimension(R.dimen.notification_corner_radius);
-            }
+        final ActivityTransitionAnimator.Controller controller =
+                new GhostedViewTransitionAnimatorController(player,
+                        InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_MEDIA_PLAYER) {
+                    @Override
+                    protected float getCurrentTopCornerRadius() {
+                        return mContext.getResources().getDimension(
+                                R.dimen.notification_corner_radius);
+                    }
 
-            @Override
-            protected float getCurrentBottomCornerRadius() {
-                // TODO(b/184121838): Make IlluminationDrawable support top and bottom radius.
-                return getCurrentTopCornerRadius();
-            }
-        };
+                    @Override
+                    protected float getCurrentBottomCornerRadius() {
+                        // TODO(b/184121838): Make IlluminationDrawable support top and bottom
+                        //  radius.
+                        return getCurrentTopCornerRadius();
+                    }
+                };
+
+        // When on the hub, wrap in the communal animation controller to ensure we exit the hub
+        // at the proper stage of the animation.
+        if (communalHub()
+                && mMediaViewController.getCurrentEndLocation()
+                == MediaHierarchyManager.LOCATION_COMMUNAL_HUB) {
+            mCommunalSceneInteractor.setIsLaunchingWidget(true);
+            return new CommunalTransitionAnimatorController(controller,
+                    mCommunalSceneInteractor);
+        }
+        return controller;
     }
 
     /** Bind this recommendation view based on the given data. */
@@ -1934,6 +1960,7 @@
 
     /**
      * Get the surface given the current end location for MediaViewController
+     *
      * @return surface used for Smartspace logging
      */
     protected int getSurfaceForSmartspaceLogging() {
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionUtils.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionUtils.kt
new file mode 100644
index 0000000..723ff5a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionUtils.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.mediaprojection
+
+import android.content.pm.PackageManager
+import com.android.systemui.util.Utils
+
+/** Various utility methods related to media projection. */
+object MediaProjectionUtils {
+    /**
+     * Returns true iff projecting to the given [packageName] means that we're casting media to a
+     * *different* device (as opposed to sharing media to some application on *this* device).
+     */
+    fun packageHasCastingCapabilities(
+        packageManager: PackageManager,
+        packageName: String
+    ): Boolean {
+        // The [isHeadlessRemoteDisplayProvider] check approximates whether a projection is to a
+        // different device or the same device, because headless remote display packages are the
+        // only kinds of packages that do cast-to-other-device. This isn't exactly perfect,
+        // because it means that any projection by those headless remote display packages will be
+        // marked as going to a different device, even if that isn't always true. See b/321078669.
+        return Utils.isHeadlessRemoteDisplayProvider(packageManager, packageName)
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
index 6c53374..3c83db3 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
@@ -41,7 +41,6 @@
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
-import android.graphics.Typeface;
 import android.media.projection.IMediaProjection;
 import android.media.projection.MediaProjectionConfig;
 import android.media.projection.MediaProjectionManager;
@@ -50,10 +49,8 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.text.BidiFormatter;
-import android.text.SpannableString;
 import android.text.TextPaint;
 import android.text.TextUtils;
-import android.text.style.StyleSpan;
 import android.util.Log;
 import android.view.Window;
 
@@ -61,6 +58,7 @@
 import com.android.systemui.flags.Flags;
 import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger;
 import com.android.systemui.mediaprojection.MediaProjectionServiceHelper;
+import com.android.systemui.mediaprojection.MediaProjectionUtils;
 import com.android.systemui.mediaprojection.SessionCreationSource;
 import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorActivity;
 import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDevicePolicyResolver;
@@ -68,12 +66,13 @@
 import com.android.systemui.res.R;
 import com.android.systemui.statusbar.phone.AlertDialogWithDelegate;
 import com.android.systemui.statusbar.phone.SystemUIDialog;
-import com.android.systemui.util.Utils;
 
-import dagger.Lazy;
+import java.util.function.Consumer;
 
 import javax.inject.Inject;
 
+import dagger.Lazy;
+
 public class MediaProjectionPermissionActivity extends Activity
         implements DialogInterface.OnClickListener {
     private static final String TAG = "MediaProjectionPermissionActivity";
@@ -187,70 +186,24 @@
             }
         }
 
-        CharSequence dialogText = null;
-        CharSequence dialogTitle = null;
-
         final String appName = extractAppName(aInfo, packageManager);
         final boolean hasCastingCapabilities =
-                Utils.isHeadlessRemoteDisplayProvider(packageManager, mPackageName);
-
-        if (hasCastingCapabilities) {
-            dialogText = getString(R.string.media_projection_sys_service_dialog_warning);
-            dialogTitle = getString(R.string.media_projection_sys_service_dialog_title);
-        } else {
-            String actionText = getString(R.string.media_projection_dialog_warning, appName);
-            SpannableString message = new SpannableString(actionText);
-
-            int appNameIndex = actionText.indexOf(appName);
-            if (appNameIndex >= 0) {
-                message.setSpan(new StyleSpan(Typeface.BOLD),
-                        appNameIndex, appNameIndex + appName.length(), 0);
-            }
-            dialogText = message;
-            dialogTitle = getString(R.string.media_projection_dialog_title, appName);
-        }
+                MediaProjectionUtils.INSTANCE.packageHasCastingCapabilities(
+                        packageManager, mPackageName);
 
         // Using application context for the dialog, instead of the activity context, so we get
         // the correct screen width when in split screen.
         Context dialogContext = getApplicationContext();
-        if (isPartialScreenSharingEnabled()) {
-            final boolean overrideDisableSingleAppOption =
-                    CompatChanges.isChangeEnabled(
-                            OVERRIDE_DISABLE_MEDIA_PROJECTION_SINGLE_APP_OPTION,
-                            mPackageName, getHostUserHandle());
-            MediaProjectionPermissionDialogDelegate delegate =
-                    new MediaProjectionPermissionDialogDelegate(
-                            dialogContext,
-                            getMediaProjectionConfig(),
-                            dialog -> {
-                                ScreenShareOption selectedOption =
-                                        dialog.getSelectedScreenShareOption();
-                                grantMediaProjectionPermission(selectedOption.getMode());
-                            },
-                            () -> finish(RECORD_CANCEL, /* projection= */ null),
-                            hasCastingCapabilities,
-                            appName,
-                            overrideDisableSingleAppOption,
-                            mUid,
-                            mMediaProjectionMetricsLogger);
-            mDialog =
-                    new AlertDialogWithDelegate(
-                            dialogContext, R.style.Theme_SystemUI_Dialog, delegate);
-        } else {
-            AlertDialog.Builder dialogBuilder =
-                    new AlertDialog.Builder(dialogContext, R.style.Theme_SystemUI_Dialog)
-                            .setTitle(dialogTitle)
-                            .setIcon(R.drawable.ic_media_projection_permission)
-                            .setMessage(dialogText)
-                            .setPositiveButton(R.string.media_projection_action_text, this)
-                            .setNeutralButton(android.R.string.cancel, this);
-            mDialog = dialogBuilder.create();
-        }
+        BaseMediaProjectionPermissionDialogDelegate<AlertDialog> delegate =
+                createPermissionDialogDelegate(appName, hasCastingCapabilities, dialogContext);
+        mDialog =
+                new AlertDialogWithDelegate(
+                        dialogContext, R.style.Theme_SystemUI_Dialog, delegate);
 
         if (savedInstanceState == null) {
             mMediaProjectionMetricsLogger.notifyProjectionInitiated(
                     mUid,
-                    appName == null
+                    hasCastingCapabilities
                             ? SessionCreationSource.CAST
                             : SessionCreationSource.APP);
         }
@@ -304,6 +257,44 @@
         return appName;
     }
 
+    private BaseMediaProjectionPermissionDialogDelegate<AlertDialog> createPermissionDialogDelegate(
+            String appName,
+            boolean hasCastingCapabilities,
+            Context dialogContext) {
+        final boolean overrideDisableSingleAppOption =
+                CompatChanges.isChangeEnabled(
+                        OVERRIDE_DISABLE_MEDIA_PROJECTION_SINGLE_APP_OPTION,
+                        mPackageName, getHostUserHandle());
+        MediaProjectionConfig mediaProjectionConfig = getMediaProjectionConfig();
+        Consumer<BaseMediaProjectionPermissionDialogDelegate<AlertDialog>> onStartRecordingClicked =
+                dialog -> {
+                    ScreenShareOption selectedOption = dialog.getSelectedScreenShareOption();
+                    grantMediaProjectionPermission(selectedOption.getMode());
+                };
+        Runnable onCancelClicked = () -> finish(RECORD_CANCEL, /* projection= */ null);
+        if (hasCastingCapabilities) {
+            return new SystemCastPermissionDialogDelegate(
+                    dialogContext,
+                    mediaProjectionConfig,
+                    onStartRecordingClicked,
+                    onCancelClicked,
+                    appName,
+                    overrideDisableSingleAppOption,
+                    mUid,
+                    mMediaProjectionMetricsLogger);
+        } else {
+            return new ShareToAppPermissionDialogDelegate(
+                    dialogContext,
+                    mediaProjectionConfig,
+                    onStartRecordingClicked,
+                    onCancelClicked,
+                    appName,
+                    overrideDisableSingleAppOption,
+                    mUid,
+                    mMediaProjectionMetricsLogger);
+        }
+    }
+
     @Override
     protected void onDestroy() {
         super.onDestroy();
@@ -366,7 +357,7 @@
                 setResult(RESULT_OK, intent);
                 finish(RECORD_CONTENT_DISPLAY, projection);
             }
-            if (isPartialScreenSharingEnabled() && screenShareMode == SINGLE_APP) {
+            if (screenShareMode == SINGLE_APP) {
                 IMediaProjection projection = MediaProjectionServiceHelper.createOrReuseProjection(
                         mUid, mPackageName, mReviewGrantedConsentRequired);
                 final Intent intent = new Intent(this,
@@ -437,8 +428,4 @@
         return intent.getParcelableExtra(
                 MediaProjectionManager.EXTRA_MEDIA_PROJECTION_CONFIG);
     }
-
-    private boolean isPartialScreenSharingEnabled() {
-        return mFeatureFlags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING);
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionDialogDelegate.kt
deleted file mode 100644
index 6d1a458..0000000
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionDialogDelegate.kt
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.systemui.mediaprojection.permission
-
-import android.app.AlertDialog
-import android.content.Context
-import android.media.projection.MediaProjectionConfig
-import android.os.Bundle
-import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger
-import com.android.systemui.res.R
-import java.util.function.Consumer
-
-/** Dialog to select screen recording options */
-class MediaProjectionPermissionDialogDelegate(
-    context: Context,
-    mediaProjectionConfig: MediaProjectionConfig?,
-    private val onStartRecordingClicked: Consumer<MediaProjectionPermissionDialogDelegate>,
-    private val onCancelClicked: Runnable,
-    private val hasCastingCapabilities: Boolean,
-    appName: String,
-    forceShowPartialScreenshare: Boolean,
-    hostUid: Int,
-    mediaProjectionMetricsLogger: MediaProjectionMetricsLogger,
-) :
-    BaseMediaProjectionPermissionDialogDelegate<AlertDialog>(
-        createOptionList(
-            context,
-            appName,
-            hasCastingCapabilities,
-            mediaProjectionConfig,
-            forceShowPartialScreenshare
-        ),
-        appName,
-        hostUid,
-        mediaProjectionMetricsLogger
-    ) {
-    override fun onCreate(dialog: AlertDialog, savedInstanceState: Bundle?) {
-        super.onCreate(dialog, savedInstanceState)
-        // TODO(b/270018943): Handle the case of System sharing (not recording nor casting)
-        if (hasCastingCapabilities) {
-            setDialogTitle(R.string.media_projection_entry_cast_permission_dialog_title)
-            setStartButtonText(R.string.media_projection_entry_cast_permission_dialog_continue)
-        } else {
-            setDialogTitle(R.string.media_projection_entry_app_permission_dialog_title)
-            setStartButtonText(R.string.media_projection_entry_app_permission_dialog_continue)
-        }
-        setStartButtonOnClickListener {
-            // Note that it is important to run this callback before dismissing, so that the
-            // callback can disable the dialog exit animation if it wants to.
-            onStartRecordingClicked.accept(this)
-            dialog.dismiss()
-        }
-        setCancelButtonOnClickListener {
-            onCancelClicked.run()
-            dialog.dismiss()
-        }
-    }
-
-    companion object {
-        private fun createOptionList(
-            context: Context,
-            appName: String,
-            hasCastingCapabilities: Boolean,
-            mediaProjectionConfig: MediaProjectionConfig?,
-            overrideDisableSingleAppOption: Boolean = false,
-        ): List<ScreenShareOption> {
-            val singleAppWarningText =
-                if (hasCastingCapabilities) {
-                    R.string.media_projection_entry_cast_permission_dialog_warning_single_app
-                } else {
-                    R.string.media_projection_entry_app_permission_dialog_warning_single_app
-                }
-            val entireScreenWarningText =
-                if (hasCastingCapabilities) {
-                    R.string.media_projection_entry_cast_permission_dialog_warning_entire_screen
-                } else {
-                    R.string.media_projection_entry_app_permission_dialog_warning_entire_screen
-                }
-
-            // The single app option should only be disabled if the client has setup a
-            // MediaProjection with MediaProjectionConfig#createConfigForDefaultDisplay AND
-            // it hasn't been overridden by the OVERRIDE_DISABLE_SINGLE_APP_OPTION per-app override.
-            val singleAppOptionDisabled =
-                !overrideDisableSingleAppOption &&
-                    mediaProjectionConfig?.regionToCapture ==
-                        MediaProjectionConfig.CAPTURE_REGION_FIXED_DISPLAY
-
-            val singleAppDisabledText =
-                if (singleAppOptionDisabled) {
-                    context.getString(
-                        R.string.media_projection_entry_app_permission_dialog_single_app_disabled,
-                        appName
-                    )
-                } else {
-                    null
-                }
-            val options =
-                listOf(
-                    ScreenShareOption(
-                        mode = SINGLE_APP,
-                        spinnerText = R.string.screen_share_permission_dialog_option_single_app,
-                        warningText = singleAppWarningText,
-                        spinnerDisabledText = singleAppDisabledText,
-                    ),
-                    ScreenShareOption(
-                        mode = ENTIRE_SCREEN,
-                        spinnerText = R.string.screen_share_permission_dialog_option_entire_screen,
-                        warningText = entireScreenWarningText
-                    )
-                )
-            return if (singleAppOptionDisabled) {
-                // Make sure "Entire screen" is the first option when "Single App" is disabled.
-                options.reversed()
-            } else {
-                options
-            }
-        }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionUtils.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionUtils.kt
new file mode 100644
index 0000000..88cbc38
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionUtils.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.mediaprojection.permission
+
+import android.content.Context
+import android.media.projection.MediaProjectionConfig
+import com.android.systemui.res.R
+
+/** Various utility methods related to media projection permissions. */
+object MediaProjectionPermissionUtils {
+    fun getSingleAppDisabledText(
+        context: Context,
+        appName: String,
+        mediaProjectionConfig: MediaProjectionConfig?,
+        overrideDisableSingleAppOption: Boolean,
+    ): String? {
+        // The single app option should only be disabled if the client has setup a
+        // MediaProjection with MediaProjectionConfig#createConfigForDefaultDisplay AND
+        // it hasn't been overridden by the OVERRIDE_DISABLE_SINGLE_APP_OPTION per-app override.
+        val singleAppOptionDisabled =
+            !overrideDisableSingleAppOption &&
+                mediaProjectionConfig?.regionToCapture ==
+                    MediaProjectionConfig.CAPTURE_REGION_FIXED_DISPLAY
+        return if (singleAppOptionDisabled) {
+            context.getString(
+                R.string.media_projection_entry_app_permission_dialog_single_app_disabled,
+                appName,
+            )
+        } else {
+            null
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/ShareToAppPermissionDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/ShareToAppPermissionDialogDelegate.kt
new file mode 100644
index 0000000..5a2d88c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/ShareToAppPermissionDialogDelegate.kt
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.mediaprojection.permission
+
+import android.app.AlertDialog
+import android.content.Context
+import android.media.projection.MediaProjectionConfig
+import android.os.Bundle
+import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger
+import com.android.systemui.res.R
+import java.util.function.Consumer
+
+/**
+ * Dialog to select screen recording options for sharing the screen to another app on the same
+ * device.
+ */
+class ShareToAppPermissionDialogDelegate(
+    context: Context,
+    mediaProjectionConfig: MediaProjectionConfig?,
+    private val onStartRecordingClicked:
+        Consumer<BaseMediaProjectionPermissionDialogDelegate<AlertDialog>>,
+    private val onCancelClicked: Runnable,
+    appName: String,
+    forceShowPartialScreenshare: Boolean,
+    hostUid: Int,
+    mediaProjectionMetricsLogger: MediaProjectionMetricsLogger,
+) :
+    BaseMediaProjectionPermissionDialogDelegate<AlertDialog>(
+        createOptionList(
+            context,
+            appName,
+            mediaProjectionConfig,
+            overrideDisableSingleAppOption = forceShowPartialScreenshare,
+        ),
+        appName,
+        hostUid,
+        mediaProjectionMetricsLogger,
+    ) {
+    override fun onCreate(dialog: AlertDialog, savedInstanceState: Bundle?) {
+        super.onCreate(dialog, savedInstanceState)
+        // TODO(b/270018943): Handle the case of System sharing (not recording nor casting)
+        setDialogTitle(R.string.media_projection_entry_app_permission_dialog_title)
+        setStartButtonText(R.string.media_projection_entry_app_permission_dialog_continue)
+        setStartButtonOnClickListener {
+            // Note that it is important to run this callback before dismissing, so that the
+            // callback can disable the dialog exit animation if it wants to.
+            onStartRecordingClicked.accept(this)
+            dialog.dismiss()
+        }
+        setCancelButtonOnClickListener {
+            onCancelClicked.run()
+            dialog.dismiss()
+        }
+    }
+
+    companion object {
+        private fun createOptionList(
+            context: Context,
+            appName: String,
+            mediaProjectionConfig: MediaProjectionConfig?,
+            overrideDisableSingleAppOption: Boolean,
+        ): List<ScreenShareOption> {
+            val singleAppDisabledText =
+                MediaProjectionPermissionUtils.getSingleAppDisabledText(
+                    context,
+                    appName,
+                    mediaProjectionConfig,
+                    overrideDisableSingleAppOption,
+                )
+            val options =
+                listOf(
+                    ScreenShareOption(
+                        mode = SINGLE_APP,
+                        spinnerText = R.string.screen_share_permission_dialog_option_single_app,
+                        warningText =
+                            R.string
+                                .media_projection_entry_app_permission_dialog_warning_single_app,
+                        spinnerDisabledText = singleAppDisabledText,
+                    ),
+                    ScreenShareOption(
+                        mode = ENTIRE_SCREEN,
+                        spinnerText = R.string.screen_share_permission_dialog_option_entire_screen,
+                        warningText =
+                            R.string
+                                .media_projection_entry_app_permission_dialog_warning_entire_screen,
+                    )
+                )
+            return if (singleAppDisabledText != null) {
+                // Make sure "Entire screen" is the first option when "Single App" is disabled.
+                options.reversed()
+            } else {
+                options
+            }
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/SystemCastPermissionDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/SystemCastPermissionDialogDelegate.kt
new file mode 100644
index 0000000..1ac3ccd
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/SystemCastPermissionDialogDelegate.kt
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.mediaprojection.permission
+
+import android.app.AlertDialog
+import android.content.Context
+import android.media.projection.MediaProjectionConfig
+import android.os.Bundle
+import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger
+import com.android.systemui.mediaprojection.permission.MediaProjectionPermissionUtils.getSingleAppDisabledText
+import com.android.systemui.res.R
+import java.util.function.Consumer
+
+/** Dialog to select screen recording options for casting the screen to a different device. */
+class SystemCastPermissionDialogDelegate(
+    context: Context,
+    mediaProjectionConfig: MediaProjectionConfig?,
+    private val onStartRecordingClicked:
+        Consumer<BaseMediaProjectionPermissionDialogDelegate<AlertDialog>>,
+    private val onCancelClicked: Runnable,
+    appName: String,
+    forceShowPartialScreenshare: Boolean,
+    hostUid: Int,
+    mediaProjectionMetricsLogger: MediaProjectionMetricsLogger,
+) :
+    BaseMediaProjectionPermissionDialogDelegate<AlertDialog>(
+        createOptionList(
+            context,
+            appName,
+            mediaProjectionConfig,
+            overrideDisableSingleAppOption = forceShowPartialScreenshare,
+        ),
+        appName,
+        hostUid,
+        mediaProjectionMetricsLogger,
+        dialogIconDrawable = R.drawable.ic_cast_connected,
+    ) {
+    override fun onCreate(dialog: AlertDialog, savedInstanceState: Bundle?) {
+        super.onCreate(dialog, savedInstanceState)
+        // TODO(b/270018943): Handle the case of System sharing (not recording nor casting)
+        setDialogTitle(R.string.media_projection_entry_cast_permission_dialog_title)
+        setStartButtonText(R.string.media_projection_entry_cast_permission_dialog_continue)
+        setStartButtonOnClickListener {
+            // Note that it is important to run this callback before dismissing, so that the
+            // callback can disable the dialog exit animation if it wants to.
+            onStartRecordingClicked.accept(this)
+            dialog.dismiss()
+        }
+        setCancelButtonOnClickListener {
+            onCancelClicked.run()
+            dialog.dismiss()
+        }
+    }
+
+    companion object {
+        private fun createOptionList(
+            context: Context,
+            appName: String,
+            mediaProjectionConfig: MediaProjectionConfig?,
+            overrideDisableSingleAppOption: Boolean,
+        ): List<ScreenShareOption> {
+            val singleAppDisabledText =
+                getSingleAppDisabledText(
+                    context,
+                    appName,
+                    mediaProjectionConfig,
+                    overrideDisableSingleAppOption
+                )
+            val options =
+                listOf(
+                    ScreenShareOption(
+                        mode = SINGLE_APP,
+                        spinnerText =
+                            R.string
+                                .media_projection_entry_cast_permission_dialog_option_text_single_app,
+                        warningText =
+                            R.string
+                                .media_projection_entry_cast_permission_dialog_warning_single_app,
+                        spinnerDisabledText = singleAppDisabledText,
+                    ),
+                    ScreenShareOption(
+                        mode = ENTIRE_SCREEN,
+                        spinnerText =
+                            R.string
+                                .media_projection_entry_cast_permission_dialog_option_text_entire_screen,
+                        warningText =
+                            R.string
+                                .media_projection_entry_cast_permission_dialog_warning_entire_screen,
+                    )
+                )
+            return if (singleAppDisabledText != null) {
+                // Make sure "Entire screen" is the first option when "Single App" is disabled.
+                options.reversed()
+            } else {
+                options
+            }
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java
index 1dbdec9..8e46fe4 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java
@@ -406,9 +406,8 @@
             @Override
             public void onViewAttachedToWindow(View v) {
                 if (result != null) {
-                    navBar.setImeWindowStatus(display.getDisplayId(), result.mImeToken,
-                            result.mImeWindowVis, result.mImeBackDisposition,
-                            result.mShowImeSwitcher);
+                    navBar.setImeWindowStatus(display.getDisplayId(), result.mImeWindowVis,
+                            result.mImeBackDisposition, result.mShowImeSwitcher);
                 }
             }
 
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
index d022c1c..15b1e4d 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
@@ -42,7 +42,6 @@
 import android.graphics.Rect;
 import android.hardware.display.DisplayManager;
 import android.inputmethodservice.InputMethodService;
-import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.Trace;
 import android.util.Log;
@@ -425,7 +424,7 @@
     }
 
     @Override
-    public void setImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition,
+    public void setImeWindowStatus(int displayId, int vis, int backDisposition,
             boolean showImeSwitcher) {
         boolean imeShown = mNavBarHelper.isImeShown(vis);
         if (!imeShown) {
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
index 947336d..0fe4d36 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
@@ -74,8 +74,10 @@
 import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
 import com.android.internal.policy.GestureNavigationSettingsObserver;
 import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.contextualeducation.GestureType;
 import com.android.systemui.model.SysUiState;
 import com.android.systemui.navigationbar.NavigationModeController;
+import com.android.systemui.navigationbar.gestural.domain.GestureInteractor;
 import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.NavigationEdgeBackPlugin;
 import com.android.systemui.plugins.PluginListener;
@@ -95,15 +97,14 @@
 import com.android.systemui.statusbar.phone.LightBarController;
 import com.android.systemui.util.concurrency.BackPanelUiThread;
 import com.android.systemui.util.concurrency.UiThreadContext;
+import com.android.systemui.util.kotlin.JavaAdapter;
 import com.android.wm.shell.back.BackAnimation;
 import com.android.wm.shell.desktopmode.DesktopMode;
 import com.android.wm.shell.pip.Pip;
 
 import java.io.PrintWriter;
 import java.util.ArrayDeque;
-import java.util.ArrayList;
 import java.util.Date;
-import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 import java.util.Optional;
@@ -157,12 +158,7 @@
     private TaskStackChangeListener mTaskStackListener = new TaskStackChangeListener() {
         @Override
         public void onTaskStackChanged() {
-            if (edgebackGestureHandlerGetRunningTasksBackground()) {
-                mBackgroundExecutor.execute(() -> mGestureBlockingActivityRunning.set(
-                        isGestureBlockingActivityRunning()));
-            } else {
-                mGestureBlockingActivityRunning.set(isGestureBlockingActivityRunning());
-            }
+            updateRunningActivityGesturesBlocked();
         }
         @Override
         public void onTaskCreated(int taskId, ComponentName componentName) {
@@ -209,8 +205,6 @@
     private final Optional<DesktopMode> mDesktopModeOptional;
     private final FalsingManager mFalsingManager;
     private final Configuration mLastReportedConfig = new Configuration();
-    // Activities which should not trigger Back gesture.
-    private final List<ComponentName> mGestureBlockingActivities = new ArrayList<>();
 
     private final Point mDisplaySize = new Point();
     private final int mDisplayId;
@@ -227,6 +221,10 @@
             mBackGestureTfClassifierProviderProvider;
     private final Provider<LightBarController> mLightBarControllerProvider;
 
+    private final GestureInteractor mGestureInteractor;
+
+    private final JavaAdapter mJavaAdapter;
+
     // The left side edge width where touch down is allowed
     private int mEdgeWidthLeft;
     // The right side edge width where touch down is allowed
@@ -426,7 +424,9 @@
             FalsingManager falsingManager,
             Provider<BackGestureTfClassifierProvider> backGestureTfClassifierProviderProvider,
             Provider<LightBarController> lightBarControllerProvider,
-            NotificationShadeWindowController notificationShadeWindowController) {
+            NotificationShadeWindowController notificationShadeWindowController,
+            GestureInteractor gestureInteractor,
+            JavaAdapter javaAdapter) {
         mContext = context;
         mDisplayId = context.getDisplayId();
         mUiThreadContext = uiThreadContext;
@@ -446,7 +446,13 @@
         mFalsingManager = falsingManager;
         mBackGestureTfClassifierProviderProvider = backGestureTfClassifierProviderProvider;
         mLightBarControllerProvider = lightBarControllerProvider;
+        mGestureInteractor = gestureInteractor;
+        mJavaAdapter = javaAdapter;
         mLastReportedConfig.setTo(mContext.getResources().getConfiguration());
+
+        mJavaAdapter.alwaysCollectFlow(mGestureInteractor.getGestureBlockedActivities(),
+                componentNames -> updateRunningActivityGesturesBlocked());
+
         ComponentName recentsComponentName = ComponentName.unflattenFromString(
                 context.getString(com.android.internal.R.string.config_recentsComponentName));
         if (recentsComponentName != null) {
@@ -466,8 +472,9 @@
                 } else {
                     String[] gestureBlockingActivities = resources.getStringArray(resId);
                     for (String gestureBlockingActivity : gestureBlockingActivities) {
-                        mGestureBlockingActivities.add(
-                                ComponentName.unflattenFromString(gestureBlockingActivity));
+                        mGestureInteractor.addGestureBlockedActivity(
+                                ComponentName.unflattenFromString(gestureBlockingActivity),
+                                GestureInteractor.Scope.Local);
                     }
                 }
             } catch (NameNotFoundException e) {
@@ -561,6 +568,15 @@
         }
     }
 
+    private void updateRunningActivityGesturesBlocked() {
+        if (edgebackGestureHandlerGetRunningTasksBackground()) {
+            mBackgroundExecutor.execute(() -> mGestureBlockingActivityRunning.set(
+                    isGestureBlockingActivityRunning()));
+        } else {
+            mGestureBlockingActivityRunning.set(isGestureBlockingActivityRunning());
+        }
+    }
+
     /**
      * Called when the nav/task bar is attached.
      */
@@ -1042,6 +1058,8 @@
                 mEdgeBackPlugin.setIsLeftPanel(mIsOnLeftEdge);
                 mEdgeBackPlugin.onMotionEvent(ev);
                 dispatchToBackAnimation(ev);
+                mOverviewProxyService.updateContextualEduStats(mIsTrackpadThreeFingerSwipe,
+                        GestureType.BACK);
             }
             if (mLogGesture || mIsTrackpadThreeFingerSwipe) {
                 mDownPoint.set(ev.getX(), ev.getY());
@@ -1293,7 +1311,8 @@
         } else {
             mPackageName = "_UNKNOWN";
         }
-        return topActivity != null && mGestureBlockingActivities.contains(topActivity);
+
+        return topActivity != null && mGestureInteractor.areGesturesBlocked(topActivity);
     }
 
     public void setBackAnimation(BackAnimation backAnimation) {
@@ -1342,6 +1361,10 @@
         private final Provider<LightBarController> mLightBarControllerProvider;
         private final NotificationShadeWindowController mNotificationShadeWindowController;
 
+        private final GestureInteractor mGestureInteractor;
+
+        private final JavaAdapter mJavaAdapter;
+
         @Inject
         public Factory(OverviewProxyService overviewProxyService,
                         SysUiState sysUiState,
@@ -1361,8 +1384,10 @@
                         FalsingManager falsingManager,
                         Provider<BackGestureTfClassifierProvider>
                                 backGestureTfClassifierProviderProvider,
-                Provider<LightBarController> lightBarControllerProvider,
-                NotificationShadeWindowController notificationShadeWindowController) {
+                        Provider<LightBarController> lightBarControllerProvider,
+                        NotificationShadeWindowController notificationShadeWindowController,
+                        GestureInteractor gestureInteractor,
+                        JavaAdapter javaAdapter) {
             mOverviewProxyService = overviewProxyService;
             mSysUiState = sysUiState;
             mPluginManager = pluginManager;
@@ -1382,6 +1407,8 @@
             mBackGestureTfClassifierProviderProvider = backGestureTfClassifierProviderProvider;
             mLightBarControllerProvider = lightBarControllerProvider;
             mNotificationShadeWindowController = notificationShadeWindowController;
+            mGestureInteractor = gestureInteractor;
+            mJavaAdapter = javaAdapter;
         }
 
         /** Construct a {@link EdgeBackGestureHandler}. */
@@ -1407,7 +1434,9 @@
                             mFalsingManager,
                             mBackGestureTfClassifierProviderProvider,
                             mLightBarControllerProvider,
-                            mNotificationShadeWindowController));
+                            mNotificationShadeWindowController,
+                            mGestureInteractor,
+                            mJavaAdapter));
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/dagger/GestureModule.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/dagger/GestureModule.kt
new file mode 100644
index 0000000..72a84f5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/dagger/GestureModule.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.navigationbar.gestural.dagger
+
+import com.android.systemui.navigationbar.gestural.data.respository.GestureRepository
+import com.android.systemui.navigationbar.gestural.data.respository.GestureRepositoryImpl
+import dagger.Binds
+import dagger.Module
+
+/** {@link Module} for gesture related dependencies */
+@Module
+interface GestureModule {
+    /**  */
+    @Binds fun gestureRespoitory(impl: GestureRepositoryImpl): GestureRepository
+}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/data/respository/GestureRepository.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/data/respository/GestureRepository.kt
new file mode 100644
index 0000000..8f35343
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/data/respository/GestureRepository.kt
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.navigationbar.gestural.data.respository
+
+import android.content.ComponentName
+import android.util.ArraySet
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Main
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.withContext
+
+/** A repository for storing gesture related information */
+interface GestureRepository {
+    /** A {@link StateFlow} tracking activities currently blocked from gestures. */
+    val gestureBlockedActivities: StateFlow<Set<ComponentName>>
+
+    /** Adds an activity to be blocked from gestures. */
+    suspend fun addGestureBlockedActivity(activity: ComponentName)
+
+    /** Removes an activity from being blocked from gestures. */
+    suspend fun removeGestureBlockedActivity(activity: ComponentName)
+}
+
+@SysUISingleton
+class GestureRepositoryImpl
+@Inject
+constructor(@Main private val mainDispatcher: CoroutineDispatcher) : GestureRepository {
+    private val _gestureBlockedActivities = MutableStateFlow<Set<ComponentName>>(ArraySet())
+
+    override val gestureBlockedActivities: StateFlow<Set<ComponentName>>
+        get() = _gestureBlockedActivities
+
+    override suspend fun addGestureBlockedActivity(activity: ComponentName) =
+        withContext(mainDispatcher) {
+            _gestureBlockedActivities.emit(
+                _gestureBlockedActivities.value.toMutableSet().apply { add(activity) }
+            )
+        }
+
+    override suspend fun removeGestureBlockedActivity(activity: ComponentName) =
+        withContext(mainDispatcher) {
+            _gestureBlockedActivities.emit(
+                _gestureBlockedActivities.value.toMutableSet().apply { remove(activity) }
+            )
+        }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/domain/GestureInteractor.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/domain/GestureInteractor.kt
new file mode 100644
index 0000000..6dc5939
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/domain/GestureInteractor.kt
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.navigationbar.gestural.domain
+
+import android.content.ComponentName
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.navigationbar.gestural.data.respository.GestureRepository
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.launch
+
+/**
+ * {@link GestureInteractor} helps interact with gesture-related logic, including accessing the
+ * underlying {@link GestureRepository}.
+ */
+class GestureInteractor
+@Inject
+constructor(
+    private val gestureRepository: GestureRepository,
+    @Application private val scope: CoroutineScope
+) {
+    enum class Scope {
+        Local,
+        Global
+    }
+
+    private val _localGestureBlockedActivities = MutableStateFlow<Set<ComponentName>>(setOf())
+    /** A {@link StateFlow} for listening to changes in Activities where gestures are blocked */
+    val gestureBlockedActivities: StateFlow<Set<ComponentName>>
+        get() =
+            combine(
+                    gestureRepository.gestureBlockedActivities,
+                    _localGestureBlockedActivities.asStateFlow()
+                ) { global, local ->
+                    global + local
+                }
+                .stateIn(scope, SharingStarted.WhileSubscribed(), setOf())
+
+    /**
+     * Adds an {@link Activity} to be blocked based on component when the topmost, focused {@link
+     * Activity}.
+     */
+    fun addGestureBlockedActivity(activity: ComponentName, gestureScope: Scope) {
+        scope.launch {
+            when (gestureScope) {
+                Scope.Local -> {
+                    _localGestureBlockedActivities.emit(
+                        _localGestureBlockedActivities.value.toMutableSet().apply { add(activity) }
+                    )
+                }
+                Scope.Global -> {
+                    gestureRepository.addGestureBlockedActivity(activity)
+                }
+            }
+        }
+    }
+
+    /** Removes an {@link Activity} from being blocked from gestures. */
+    fun removeGestureBlockedActivity(activity: ComponentName, gestureScope: Scope) {
+        scope.launch {
+            when (gestureScope) {
+                Scope.Local -> {
+                    _localGestureBlockedActivities.emit(
+                        _localGestureBlockedActivities.value.toMutableSet().apply {
+                            remove(activity)
+                        }
+                    )
+                }
+                Scope.Global -> {
+                    gestureRepository.removeGestureBlockedActivity(activity)
+                }
+            }
+        }
+    }
+
+    /**
+     * Checks whether the specified {@link Activity} {@link ComponentName} is being blocked from
+     * gestures.
+     */
+    fun areGesturesBlocked(activity: ComponentName): Boolean {
+        return gestureBlockedActivities.value.contains(activity)
+    }
+}
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 afdfa59..7b248eb 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBar.java
@@ -71,7 +71,6 @@
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Handler;
-import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.Trace;
 import android.provider.DeviceConfig;
@@ -1095,7 +1094,7 @@
     // ----- CommandQueue Callbacks -----
 
     @Override
-    public void setImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition,
+    public void setImeWindowStatus(int displayId, int vis, int backDisposition,
             boolean showImeSwitcher) {
         if (displayId != mDisplayId) {
             return;
diff --git a/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeSceneViewModel.kt b/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeSceneViewModel.kt
index 1f74716..fa8e13a 100644
--- a/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeSceneViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeSceneViewModel.kt
@@ -25,9 +25,8 @@
 import com.android.systemui.shade.domain.interactor.ShadeInteractor
 import com.android.systemui.shade.shared.model.ShadeAlignment
 import javax.inject.Inject
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flowOf
 
 /** Models UI state and handles user input for the Notifications Shade scene. */
 @SysUISingleton
@@ -36,16 +35,15 @@
 constructor(
     shadeInteractor: ShadeInteractor,
 ) {
-    val destinationScenes: StateFlow<Map<UserAction, UserActionResult>> =
-        MutableStateFlow(
-                mapOf(
-                    if (shadeInteractor.shadeAlignment == ShadeAlignment.Top) {
-                        Swipe.Up
-                    } else {
-                        Swipe.Down
-                    } to SceneFamilies.Home,
-                    Back to SceneFamilies.Home,
-                )
+    val destinationScenes: Flow<Map<UserAction, UserActionResult>> =
+        flowOf(
+            mapOf(
+                if (shadeInteractor.shadeAlignment == ShadeAlignment.Top) {
+                    Swipe.Up
+                } else {
+                    Swipe.Down
+                } to SceneFamilies.Home,
+                Back to SceneFamilies.Home,
             )
-            .asStateFlow()
+        )
 }
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 9380d44..8d48c1d 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
@@ -18,6 +18,7 @@
 package com.android.systemui.power.domain.interactor
 
 import android.os.PowerManager
+import com.android.systemui.camera.CameraGestureHelper
 import com.android.systemui.classifier.FalsingCollector
 import com.android.systemui.classifier.FalsingCollectorActual
 import com.android.systemui.dagger.SysUISingleton
@@ -28,6 +29,7 @@
 import com.android.systemui.power.shared.model.WakefulnessState
 import com.android.systemui.statusbar.phone.ScreenOffAnimationController
 import javax.inject.Inject
+import javax.inject.Provider
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.map
@@ -41,6 +43,7 @@
     @FalsingCollectorActual private val falsingCollector: FalsingCollector,
     private val screenOffAnimationController: ScreenOffAnimationController,
     private val statusBarStateController: StatusBarStateController,
+    private val cameraGestureHelper: Provider<CameraGestureHelper>,
 ) {
     /** Whether the screen is on or off. */
     val isInteractive: Flow<Boolean> = repository.isInteractive
@@ -206,7 +209,13 @@
     }
 
     fun onCameraLaunchGestureDetected() {
-        repository.updateWakefulness(powerButtonLaunchGestureTriggered = true)
+        if (
+            cameraGestureHelper
+                .get()
+                .canCameraGestureBeLaunched(statusBarStateController.getState())
+        ) {
+            repository.updateWakefulness(powerButtonLaunchGestureTriggered = true)
+        }
     }
 
     companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/power/shared/model/WakefulnessModel.kt b/packages/SystemUI/src/com/android/systemui/power/shared/model/WakefulnessModel.kt
index 5432793d..0f49c94 100644
--- a/packages/SystemUI/src/com/android/systemui/power/shared/model/WakefulnessModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/power/shared/model/WakefulnessModel.kt
@@ -19,7 +19,7 @@
      * all use cases. If you need more granular information about a waking/sleeping transition, use
      * the [KeyguardTransitionInteractor].
      */
-    internal val internalWakefulnessState: WakefulnessState = WakefulnessState.AWAKE,
+    val internalWakefulnessState: WakefulnessState = WakefulnessState.AWAKE,
     val lastWakeReason: WakeSleepReason = WakeSleepReason.OTHER,
     val lastSleepReason: WakeSleepReason = WakeSleepReason.OTHER,
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragmentLegacy.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragmentLegacy.java
index 38d7290..37002ca 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragmentLegacy.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragmentLegacy.java
@@ -17,6 +17,7 @@
 package com.android.systemui.qs;
 
 import android.content.res.Configuration;
+import android.graphics.Rect;
 import android.os.Bundle;
 import android.os.Trace;
 import android.view.ContextThemeWrapper;
@@ -30,6 +31,7 @@
 import com.android.systemui.plugins.qs.QS;
 import com.android.systemui.plugins.qs.QSContainerController;
 import com.android.systemui.qs.dagger.QSFragmentComponent;
+import com.android.systemui.qs.flags.QSComposeFragment;
 import com.android.systemui.res.R;
 import com.android.systemui.settings.brightness.MirrorController;
 import com.android.systemui.util.LifecycleFragment;
@@ -103,6 +105,7 @@
 
     @Override
     public View getHeader() {
+        QSComposeFragment.assertInLegacyMode();
         if (mQsImpl != null) {
             return mQsImpl.getHeader();
         } else {
@@ -111,6 +114,51 @@
     }
 
     @Override
+    public int getHeaderTop() {
+        if (mQsImpl != null) {
+            return mQsImpl.getHeaderTop();
+        } else {
+            return 0;
+        }
+    }
+
+    @Override
+    public int getHeaderBottom() {
+        if (mQsImpl != null) {
+            return mQsImpl.getHeaderBottom();
+        } else {
+            return 0;
+        }
+    }
+
+    @Override
+    public int getHeaderLeft() {
+        if (mQsImpl != null) {
+            return mQsImpl.getHeaderLeft();
+        } else {
+            return 0;
+        }
+    }
+
+    @Override
+    public void getHeaderBoundsOnScreen(Rect outBounds) {
+        if (mQsImpl != null) {
+            mQsImpl.getHeaderBoundsOnScreen(outBounds);
+        } else {
+            outBounds.setEmpty();
+        }
+    }
+
+    @Override
+    public boolean isHeaderShown() {
+        if (mQsImpl != null) {
+            return mQsImpl.isHeaderShown();
+        } else {
+            return false;
+        }
+    }
+
+    @Override
     public void setHasNotifications(boolean hasNotifications) {
         if (mQsImpl != null) {
             mQsImpl.setHasNotifications(hasNotifications);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java
index 8c0d122..a6fd35a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java
@@ -54,6 +54,7 @@
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.qs.customize.QSCustomizerController;
 import com.android.systemui.qs.dagger.QSComponent;
+import com.android.systemui.qs.flags.QSComposeFragment;
 import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel;
 import com.android.systemui.qs.logging.QSLogger;
 import com.android.systemui.res.R;
@@ -355,10 +356,36 @@
 
     @Override
     public View getHeader() {
+        QSComposeFragment.assertInLegacyMode();
         return mHeader;
     }
 
     @Override
+    public int getHeaderTop() {
+        return mHeader.getTop();
+    }
+
+    @Override
+    public int getHeaderBottom() {
+        return mHeader.getBottom();
+    }
+
+    @Override
+    public int getHeaderLeft() {
+        return mHeader.getLeft();
+    }
+
+    @Override
+    public void getHeaderBoundsOnScreen(Rect outBounds) {
+        mHeader.getBoundsOnScreen(outBounds);
+    }
+
+    @Override
+    public boolean isHeaderShown() {
+        return mHeader.isShown();
+    }
+
+    @Override
     public void setHasNotifications(boolean hasNotifications) {
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/dagger/PanelsModule.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/dagger/PanelsModule.kt
index 78f4b4b..072d322 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/dagger/PanelsModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/dagger/PanelsModule.kt
@@ -30,14 +30,10 @@
 import com.android.systemui.qs.panels.shared.model.InfiniteGridLayoutType
 import com.android.systemui.qs.panels.shared.model.PaginatedGridLayoutType
 import com.android.systemui.qs.panels.shared.model.PanelsLog
-import com.android.systemui.qs.panels.shared.model.PartitionedGridLayoutType
-import com.android.systemui.qs.panels.shared.model.StretchedGridLayoutType
 import com.android.systemui.qs.panels.ui.compose.GridLayout
 import com.android.systemui.qs.panels.ui.compose.InfiniteGridLayout
 import com.android.systemui.qs.panels.ui.compose.PaginatableGridLayout
 import com.android.systemui.qs.panels.ui.compose.PaginatedGridLayout
-import com.android.systemui.qs.panels.ui.compose.PartitionedGridLayout
-import com.android.systemui.qs.panels.ui.compose.StretchedGridLayout
 import com.android.systemui.qs.panels.ui.viewmodel.FixedColumnsSizeViewModel
 import com.android.systemui.qs.panels.ui.viewmodel.FixedColumnsSizeViewModelImpl
 import com.android.systemui.qs.panels.ui.viewmodel.IconLabelVisibilityViewModel
@@ -102,22 +98,6 @@
 
         @Provides
         @IntoSet
-        fun provideStretchedGridLayout(
-            gridLayout: StretchedGridLayout
-        ): Pair<GridLayoutType, GridLayout> {
-            return Pair(StretchedGridLayoutType, gridLayout)
-        }
-
-        @Provides
-        @IntoSet
-        fun providePartitionedGridLayout(
-            gridLayout: PartitionedGridLayout
-        ): Pair<GridLayoutType, GridLayout> {
-            return Pair(PartitionedGridLayoutType, gridLayout)
-        }
-
-        @Provides
-        @IntoSet
         fun providePaginatedGridLayout(
             gridLayout: PaginatedGridLayout
         ): Pair<GridLayoutType, GridLayout> {
@@ -148,22 +128,6 @@
 
         @Provides
         @IntoSet
-        fun provideStretchedGridConsistencyInteractor(
-            consistencyInteractor: NoopGridConsistencyInteractor
-        ): Pair<GridLayoutType, GridTypeConsistencyInteractor> {
-            return Pair(StretchedGridLayoutType, consistencyInteractor)
-        }
-
-        @Provides
-        @IntoSet
-        fun providePartitionedGridConsistencyInteractor(
-            consistencyInteractor: NoopGridConsistencyInteractor
-        ): Pair<GridLayoutType, GridTypeConsistencyInteractor> {
-            return Pair(PartitionedGridLayoutType, consistencyInteractor)
-        }
-
-        @Provides
-        @IntoSet
         fun providePaginatedGridConsistencyInteractor(
             @PaginatedBaseLayoutType consistencyInteractor: GridTypeConsistencyInteractor,
         ): Pair<GridLayoutType, GridTypeConsistencyInteractor> {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/shared/model/GridLayoutType.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/shared/model/GridLayoutType.kt
index b1942fe..323f39b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/shared/model/GridLayoutType.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/shared/model/GridLayoutType.kt
@@ -26,14 +26,5 @@
 /** Grid type representing a scrollable vertical grid. */
 data object InfiniteGridLayoutType : GridLayoutType
 
-/**
- * Grid type representing a scrollable vertical grid where tiles will stretch to fill in empty
- * spaces.
- */
-data object StretchedGridLayoutType : GridLayoutType
-
-/** Grid type grouping large tiles on top and icon tiles at the bottom. */
-data object PartitionedGridLayoutType : GridLayoutType
-
 /** Grid type for a paginated list of tiles. It will delegate to some other layout type. */
 data object PaginatedGridLayoutType : GridLayoutType
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/DragAndDropState.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/DragAndDropState.kt
index 782fb2a..71deeb6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/DragAndDropState.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/DragAndDropState.kt
@@ -14,12 +14,9 @@
  * limitations under the License.
  */
 
-@file:OptIn(ExperimentalFoundationApi::class)
-
 package com.android.systemui.qs.panels.ui.compose
 
 import android.content.ClipData
-import androidx.compose.foundation.ExperimentalFoundationApi
 import androidx.compose.foundation.draganddrop.dragAndDropSource
 import androidx.compose.foundation.draganddrop.dragAndDropTarget
 import androidx.compose.foundation.gestures.detectTapGestures
@@ -32,11 +29,12 @@
 import androidx.compose.ui.draganddrop.DragAndDropTarget
 import androidx.compose.ui.draganddrop.DragAndDropTransferData
 import androidx.compose.ui.draganddrop.mimeTypes
+import com.android.systemui.qs.panels.ui.viewmodel.EditTileViewModel
 import com.android.systemui.qs.pipeline.shared.TileSpec
 
 @Composable
 fun rememberDragAndDropState(listState: EditTileListState): DragAndDropState {
-    val sourceSpec: MutableState<TileSpec?> = remember { mutableStateOf(null) }
+    val sourceSpec: MutableState<EditTileViewModel?> = remember { mutableStateOf(null) }
     return remember(listState) { DragAndDropState(sourceSpec, listState) }
 }
 
@@ -45,7 +43,7 @@
  * drop events.
  */
 class DragAndDropState(
-    val sourceSpec: MutableState<TileSpec?>,
+    val sourceSpec: MutableState<EditTileViewModel?>,
     private val listState: EditTileListState
 ) {
     val dragInProgress: Boolean
@@ -53,15 +51,15 @@
 
     /** Returns index of the dragged tile if it's present in the list. Returns -1 if not. */
     fun currentPosition(): Int {
-        return sourceSpec.value?.let { listState.indexOf(it) } ?: -1
+        return sourceSpec.value?.let { listState.indexOf(it.tileSpec) } ?: -1
     }
 
     fun isMoving(tileSpec: TileSpec): Boolean {
-        return sourceSpec.value?.let { it == tileSpec } ?: false
+        return sourceSpec.value?.let { it.tileSpec == tileSpec } ?: false
     }
 
-    fun onStarted(spec: TileSpec) {
-        sourceSpec.value = spec
+    fun onStarted(tile: EditTileViewModel) {
+        sourceSpec.value = tile
     }
 
     fun onMoved(targetSpec: TileSpec) {
@@ -71,7 +69,7 @@
     fun movedOutOfBounds() {
         // Removing the tiles from the current tile grid if it moves out of bounds. This clears
         // the spacer and makes it apparent that dropping the tile at that point would remove it.
-        sourceSpec.value?.let { listState.removeFromCurrent(it) }
+        sourceSpec.value?.let { listState.remove(it.tileSpec) }
     }
 
     fun onDrop() {
@@ -100,7 +98,7 @@
             object : DragAndDropTarget {
                 override fun onDrop(event: DragAndDropEvent): Boolean {
                     return dragAndDropState.sourceSpec.value?.let {
-                        onDrop(it, dragAndDropState.currentPosition())
+                        onDrop(it.tileSpec, dragAndDropState.currentPosition())
                         dragAndDropState.onDrop()
                         true
                     } ?: false
@@ -114,7 +112,7 @@
     return dragAndDropTarget(
         shouldStartDragAndDrop = { event ->
             event.mimeTypes().contains(QsDragAndDrop.TILESPEC_MIME_TYPE) &&
-                dragAndDropState.sourceSpec.value?.let { acceptDrops(it) } ?: false
+                dragAndDropState.sourceSpec.value?.let { acceptDrops(it.tileSpec) } ?: false
         },
         target = target,
     )
@@ -137,7 +135,7 @@
             object : DragAndDropTarget {
                 override fun onDrop(event: DragAndDropEvent): Boolean {
                     return dragAndDropState.sourceSpec.value?.let {
-                        onDrop(it)
+                        onDrop(it.tileSpec)
                         dragAndDropState.onDrop()
                         true
                     } ?: false
@@ -179,7 +177,7 @@
 
                 override fun onDrop(event: DragAndDropEvent): Boolean {
                     return dragAndDropState.sourceSpec.value?.let {
-                        onDrop(it, dragAndDropState.currentPosition())
+                        onDrop(it.tileSpec, dragAndDropState.currentPosition())
                         dragAndDropState.onDrop()
                         true
                     } ?: false
@@ -190,23 +188,23 @@
         target = target,
         shouldStartDragAndDrop = { event ->
             event.mimeTypes().contains(QsDragAndDrop.TILESPEC_MIME_TYPE) &&
-                dragAndDropState.sourceSpec.value?.let { acceptDrops(it) } ?: false
+                dragAndDropState.sourceSpec.value?.let { acceptDrops(it.tileSpec) } ?: false
         },
     )
 }
 
 fun Modifier.dragAndDropTileSource(
-    tileSpec: TileSpec,
+    tile: EditTileViewModel,
     onTap: (TileSpec) -> Unit,
     onDoubleTap: (TileSpec) -> Unit,
     dragAndDropState: DragAndDropState
 ): Modifier {
     return dragAndDropSource {
         detectTapGestures(
-            onTap = { onTap(tileSpec) },
-            onDoubleTap = { onDoubleTap(tileSpec) },
+            onTap = { onTap(tile.tileSpec) },
+            onDoubleTap = { onDoubleTap(tile.tileSpec) },
             onLongPress = {
-                dragAndDropState.onStarted(tileSpec)
+                dragAndDropState.onStarted(tile)
 
                 // The tilespec from the ClipData transferred isn't actually needed as we're moving
                 // a tile within the same application. We're using a custom MIME type to limit the
@@ -216,7 +214,7 @@
                         ClipData(
                             QsDragAndDrop.CLIPDATA_LABEL,
                             arrayOf(QsDragAndDrop.TILESPEC_MIME_TYPE),
-                            ClipData.Item(tileSpec.spec)
+                            ClipData.Item(tile.tileSpec.spec)
                         )
                     )
                 )
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/EditTileListState.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/EditTileListState.kt
index 34876c4..e0fed28 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/EditTileListState.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/EditTileListState.kt
@@ -34,28 +34,25 @@
 class EditTileListState(tiles: List<EditTileViewModel>) {
     val tiles: SnapshotStateList<EditTileViewModel> = tiles.toMutableStateList()
 
-    fun move(tileSpec: TileSpec, target: TileSpec) {
-        val fromIndex = indexOf(tileSpec)
+    fun move(tile: EditTileViewModel, target: TileSpec) {
+        val fromIndex = indexOf(tile.tileSpec)
         val toIndex = indexOf(target)
 
-        if (fromIndex == -1 || toIndex == -1 || fromIndex == toIndex) {
+        if (toIndex == -1 || fromIndex == toIndex) {
             return
         }
 
-        val isMovingToCurrent = tiles[toIndex].isCurrent
-        tiles.apply { add(toIndex, removeAt(fromIndex).copy(isCurrent = isMovingToCurrent)) }
+        if (fromIndex == -1) {
+            // If tile isn't in the list, simply insert it
+            tiles.add(toIndex, tile)
+        } else {
+            // If tile is present in the list, move it
+            tiles.apply { add(toIndex, removeAt(fromIndex)) }
+        }
     }
 
-    /**
-     * Sets the [TileSpec] as a non-current tile. Use this when a tile is dragged out of the current
-     * tile grid.
-     */
-    fun removeFromCurrent(tileSpec: TileSpec) {
-        val fromIndex = indexOf(tileSpec)
-        if (fromIndex >= 0 && fromIndex < tiles.size) {
-            // Mark the moving tile as non-current
-            tiles[fromIndex] = tiles[fromIndex].copy(isCurrent = false)
-        }
+    fun remove(tileSpec: TileSpec) {
+        tiles.removeIf { it.tileSpec == tileSpec }
     }
 
     fun indexOf(tileSpec: TileSpec): Int {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PartitionedGridLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PartitionedGridLayout.kt
deleted file mode 100644
index 6c84edd..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PartitionedGridLayout.kt
+++ /dev/null
@@ -1,404 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.qs.panels.ui.compose
-
-import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.Row
-import androidx.compose.foundation.layout.Spacer
-import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.height
-import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.lazy.grid.GridCells
-import androidx.compose.foundation.lazy.grid.GridItemSpan
-import androidx.compose.foundation.lazy.grid.LazyGridScope
-import androidx.compose.foundation.rememberScrollState
-import androidx.compose.foundation.shape.RoundedCornerShape
-import androidx.compose.foundation.verticalScroll
-import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.Switch
-import androidx.compose.material3.Text
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.DisposableEffect
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.rememberUpdatedState
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.draw.drawWithContent
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.graphics.Path
-import androidx.compose.ui.graphics.PathEffect
-import androidx.compose.ui.graphics.Shape
-import androidx.compose.ui.graphics.addOutline
-import androidx.compose.ui.graphics.drawscope.Stroke
-import androidx.compose.ui.res.dimensionResource
-import androidx.compose.ui.text.font.FontWeight
-import androidx.compose.ui.unit.Dp
-import androidx.compose.ui.unit.dp
-import androidx.lifecycle.compose.collectAsStateWithLifecycle
-import com.android.compose.modifiers.background
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.qs.panels.shared.model.SizedTile
-import com.android.systemui.qs.panels.ui.viewmodel.EditTileViewModel
-import com.android.systemui.qs.panels.ui.viewmodel.PartitionedGridViewModel
-import com.android.systemui.qs.panels.ui.viewmodel.TileViewModel
-import com.android.systemui.qs.pipeline.domain.interactor.CurrentTilesInteractor
-import com.android.systemui.qs.pipeline.shared.TileSpec
-import com.android.systemui.res.R
-import javax.inject.Inject
-
-@SysUISingleton
-class PartitionedGridLayout @Inject constructor(private val viewModel: PartitionedGridViewModel) :
-    PaginatableGridLayout {
-    @Composable
-    override fun TileGrid(
-        tiles: List<TileViewModel>,
-        modifier: Modifier,
-        editModeStart: () -> Unit,
-    ) {
-        DisposableEffect(tiles) {
-            val token = Any()
-            tiles.forEach { it.startListening(token) }
-            onDispose { tiles.forEach { it.stopListening(token) } }
-        }
-        val columns by viewModel.columns.collectAsStateWithLifecycle()
-        val showLabels by viewModel.showLabels.collectAsStateWithLifecycle()
-        val largeTileHeight = tileHeight()
-        val iconTileHeight = tileHeight(showLabels)
-        val (smallTiles, largeTiles) = tiles.partition { viewModel.isIconTile(it.spec) }
-
-        TileLazyGrid(modifier = modifier, columns = GridCells.Fixed(columns)) {
-            // Large tiles
-            items(largeTiles.size, span = { GridItemSpan(2) }) { index ->
-                Tile(
-                    tile = largeTiles[index],
-                    iconOnly = false,
-                    modifier = Modifier.height(largeTileHeight)
-                )
-            }
-            fillUpRow(nTiles = largeTiles.size, columns = columns / 2)
-
-            // Small tiles
-            items(smallTiles.size) { index ->
-                Tile(
-                    tile = smallTiles[index],
-                    iconOnly = true,
-                    showLabels = showLabels,
-                    modifier = Modifier.height(iconTileHeight)
-                )
-            }
-        }
-    }
-
-    @Composable
-    override fun EditTileGrid(
-        tiles: List<EditTileViewModel>,
-        modifier: Modifier,
-        onAddTile: (TileSpec, Int) -> Unit,
-        onRemoveTile: (TileSpec) -> Unit,
-    ) {
-        val columns by viewModel.columns.collectAsStateWithLifecycle()
-        val showLabels by viewModel.showLabels.collectAsStateWithLifecycle()
-
-        val listState = rememberEditListState(tiles)
-        val dragAndDropState = rememberDragAndDropState(listState)
-
-        val (currentTiles, otherTiles) = listState.tiles.partition { it.isCurrent }
-        val addTileToEnd: (TileSpec) -> Unit by rememberUpdatedState {
-            onAddTile(it, CurrentTilesInteractor.POSITION_AT_END)
-        }
-        val onDoubleTap: (TileSpec) -> Unit by rememberUpdatedState { tileSpec ->
-            viewModel.resize(tileSpec, !viewModel.isIconTile(tileSpec))
-        }
-        val largeTileHeight = tileHeight()
-        val iconTileHeight = tileHeight(showLabels)
-        val tilePadding = dimensionResource(R.dimen.qs_tile_margin_vertical)
-
-        Column(
-            verticalArrangement = Arrangement.spacedBy(tilePadding),
-            modifier = modifier.fillMaxSize().verticalScroll(rememberScrollState())
-        ) {
-            Row(
-                modifier =
-                    Modifier.background(
-                            color = MaterialTheme.colorScheme.surfaceVariant,
-                            alpha = { 1f },
-                            shape = RoundedCornerShape(dimensionResource(R.dimen.qs_corner_radius))
-                        )
-                        .padding(tilePadding)
-            ) {
-                Column(Modifier.padding(start = tilePadding)) {
-                    Text(
-                        text = "Show text labels",
-                        color = MaterialTheme.colorScheme.onBackground,
-                        fontWeight = FontWeight.Bold
-                    )
-                    Text(
-                        text = "Display names under each tile",
-                        color = MaterialTheme.colorScheme.onBackground
-                    )
-                }
-                Spacer(modifier = Modifier.weight(1f))
-                Switch(checked = showLabels, onCheckedChange = { viewModel.setShowLabels(it) })
-            }
-
-            CurrentTiles(
-                tiles = currentTiles,
-                largeTileHeight = largeTileHeight,
-                iconTileHeight = iconTileHeight,
-                tilePadding = tilePadding,
-                onAdd = onAddTile,
-                onRemove = onRemoveTile,
-                onDoubleTap = onDoubleTap,
-                isIconOnly = viewModel::isIconTile,
-                columns = columns,
-                showLabels = showLabels,
-                dragAndDropState = dragAndDropState,
-            )
-            AvailableTiles(
-                tiles = otherTiles.filter { !dragAndDropState.isMoving(it.tileSpec) },
-                largeTileHeight = largeTileHeight,
-                iconTileHeight = iconTileHeight,
-                tilePadding = tilePadding,
-                addTileToEnd = addTileToEnd,
-                onRemove = onRemoveTile,
-                onDoubleTap = onDoubleTap,
-                isIconOnly = viewModel::isIconTile,
-                showLabels = showLabels,
-                columns = columns,
-                dragAndDropState = dragAndDropState,
-            )
-        }
-    }
-
-    override fun splitIntoPages(
-        tiles: List<TileViewModel>,
-        rows: Int,
-        columns: Int,
-    ): List<List<TileViewModel>> {
-        val (smallTiles, largeTiles) = tiles.partition { viewModel.isIconTile(it.spec) }
-
-        val sizedLargeTiles = largeTiles.map { SizedTile(it, 2) }
-        val sizedSmallTiles = smallTiles.map { SizedTile(it, 1) }
-        val largeTilesRows = PaginatableGridLayout.splitInRows(sizedLargeTiles, columns)
-        val smallTilesRows = PaginatableGridLayout.splitInRows(sizedSmallTiles, columns)
-        return (largeTilesRows + smallTilesRows).chunked(rows).map { it.flatten().map { it.tile } }
-    }
-
-    @Composable
-    private fun CurrentTiles(
-        tiles: List<EditTileViewModel>,
-        largeTileHeight: Dp,
-        iconTileHeight: Dp,
-        tilePadding: Dp,
-        onAdd: (TileSpec, Int) -> Unit,
-        onRemove: (TileSpec) -> Unit,
-        onDoubleTap: (TileSpec) -> Unit,
-        isIconOnly: (TileSpec) -> Boolean,
-        showLabels: Boolean,
-        columns: Int,
-        dragAndDropState: DragAndDropState,
-    ) {
-        val (smallTiles, largeTiles) = tiles.partition { isIconOnly(it.tileSpec) }
-
-        val largeGridHeight = gridHeight(largeTiles.size, largeTileHeight, columns / 2, tilePadding)
-        val smallGridHeight = gridHeight(smallTiles.size, iconTileHeight, columns, tilePadding)
-
-        CurrentTilesContainer {
-            TileLazyGrid(
-                columns = GridCells.Fixed(columns),
-                modifier =
-                    Modifier.height(largeGridHeight)
-                        .dragAndDropTileList(dragAndDropState, { !isIconOnly(it) }, onAdd)
-            ) {
-                editTiles(
-                    tiles = largeTiles,
-                    clickAction = ClickAction.REMOVE,
-                    onClick = onRemove,
-                    onDoubleTap = onDoubleTap,
-                    isIconOnly = { false },
-                    dragAndDropState = dragAndDropState,
-                    acceptDrops = { !isIconOnly(it) },
-                    onDrop = onAdd,
-                    indicatePosition = true,
-                )
-            }
-        }
-
-        CurrentTilesContainer {
-            TileLazyGrid(
-                columns = GridCells.Fixed(columns),
-                modifier =
-                    Modifier.height(smallGridHeight)
-                        .dragAndDropTileList(dragAndDropState, { isIconOnly(it) }, onAdd)
-            ) {
-                editTiles(
-                    tiles = smallTiles,
-                    clickAction = ClickAction.REMOVE,
-                    onClick = onRemove,
-                    onDoubleTap = onDoubleTap,
-                    isIconOnly = { true },
-                    showLabels = showLabels,
-                    dragAndDropState = dragAndDropState,
-                    acceptDrops = { isIconOnly(it) },
-                    onDrop = onAdd,
-                    indicatePosition = true,
-                )
-            }
-        }
-    }
-
-    @Composable
-    private fun AvailableTiles(
-        tiles: List<EditTileViewModel>,
-        largeTileHeight: Dp,
-        iconTileHeight: Dp,
-        tilePadding: Dp,
-        addTileToEnd: (TileSpec) -> Unit,
-        onRemove: (TileSpec) -> Unit,
-        onDoubleTap: (TileSpec) -> Unit,
-        isIconOnly: (TileSpec) -> Boolean,
-        showLabels: Boolean,
-        columns: Int,
-        dragAndDropState: DragAndDropState,
-    ) {
-        val (tilesStock, tilesCustom) = tiles.partition { it.appName == null }
-        val (smallTiles, largeTiles) = tilesStock.partition { isIconOnly(it.tileSpec) }
-
-        val largeGridHeight = gridHeight(largeTiles.size, largeTileHeight, columns / 2, tilePadding)
-        val smallGridHeight = gridHeight(smallTiles.size, iconTileHeight, columns, tilePadding)
-        val largeGridHeightCustom =
-            gridHeight(tilesCustom.size, iconTileHeight, columns, tilePadding)
-
-        // Add up the height of all three grids and add padding in between
-        val gridHeight =
-            largeGridHeight + smallGridHeight + largeGridHeightCustom + (tilePadding * 2)
-
-        val onDrop: (TileSpec, Int) -> Unit by rememberUpdatedState { tileSpec, _ ->
-            onRemove(tileSpec)
-        }
-
-        AvailableTilesContainer {
-            TileLazyGrid(
-                columns = GridCells.Fixed(columns),
-                modifier =
-                    Modifier.height(gridHeight)
-                        .dragAndDropTileList(dragAndDropState, { true }, onDrop)
-            ) {
-                // Large tiles
-                editTiles(
-                    largeTiles,
-                    ClickAction.ADD,
-                    addTileToEnd,
-                    isIconOnly,
-                    dragAndDropState,
-                    onDoubleTap = onDoubleTap,
-                    acceptDrops = { true },
-                    onDrop = onDrop,
-                )
-                fillUpRow(nTiles = largeTiles.size, columns = columns / 2)
-
-                // Small tiles
-                editTiles(
-                    smallTiles,
-                    ClickAction.ADD,
-                    addTileToEnd,
-                    isIconOnly,
-                    dragAndDropState,
-                    onDoubleTap = onDoubleTap,
-                    showLabels = showLabels,
-                    acceptDrops = { true },
-                    onDrop = onDrop,
-                )
-                fillUpRow(nTiles = smallTiles.size, columns = columns)
-
-                // Custom tiles, all icons
-                editTiles(
-                    tilesCustom,
-                    ClickAction.ADD,
-                    addTileToEnd,
-                    isIconOnly,
-                    dragAndDropState,
-                    onDoubleTap = onDoubleTap,
-                    showLabels = showLabels,
-                    acceptDrops = { true },
-                    onDrop = onDrop,
-                )
-            }
-        }
-    }
-
-    @Composable
-    private fun CurrentTilesContainer(content: @Composable () -> Unit) {
-        Box(
-            Modifier.fillMaxWidth()
-                .dashedBorder(
-                    color = MaterialTheme.colorScheme.onBackground.copy(alpha = .5f),
-                    shape = Dimensions.ContainerShape,
-                )
-                .padding(dimensionResource(R.dimen.qs_tile_margin_vertical))
-        ) {
-            content()
-        }
-    }
-
-    @Composable
-    private fun AvailableTilesContainer(content: @Composable () -> Unit) {
-        Box(
-            Modifier.fillMaxWidth()
-                .background(
-                    color = MaterialTheme.colorScheme.background,
-                    alpha = { 1f },
-                    shape = Dimensions.ContainerShape,
-                )
-                .padding(dimensionResource(R.dimen.qs_tile_margin_vertical))
-        ) {
-            content()
-        }
-    }
-
-    /** Fill up the rest of the row if it's not complete. */
-    private fun LazyGridScope.fillUpRow(nTiles: Int, columns: Int) {
-        if (nTiles % columns != 0) {
-            item(span = { GridItemSpan(maxCurrentLineSpan) }) { Spacer(Modifier) }
-        }
-    }
-
-    private fun Modifier.dashedBorder(
-        color: Color,
-        shape: Shape,
-    ): Modifier {
-        return this.drawWithContent {
-            val outline = shape.createOutline(size, layoutDirection, this)
-            val path = Path()
-            path.addOutline(outline)
-            val stroke =
-                Stroke(
-                    width = 1.dp.toPx(),
-                    pathEffect = PathEffect.dashPathEffect(floatArrayOf(10f, 10f))
-                )
-            this.drawContent()
-            drawPath(path = path, style = stroke, color = color)
-        }
-    }
-
-    private object Dimensions {
-        // Corner radius is half the height of a tile + padding
-        val ContainerShape = RoundedCornerShape(48.dp)
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/StretchedGridLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/StretchedGridLayout.kt
deleted file mode 100644
index 3e48245..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/StretchedGridLayout.kt
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.qs.panels.ui.compose
-
-import androidx.compose.foundation.layout.height
-import androidx.compose.foundation.lazy.grid.GridCells
-import androidx.compose.foundation.lazy.grid.GridItemSpan
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.DisposableEffect
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.remember
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.res.dimensionResource
-import androidx.lifecycle.compose.collectAsStateWithLifecycle
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.qs.panels.shared.model.SizedTile
-import com.android.systemui.qs.panels.shared.model.TileRow
-import com.android.systemui.qs.panels.ui.viewmodel.EditTileViewModel
-import com.android.systemui.qs.panels.ui.viewmodel.FixedColumnsSizeViewModel
-import com.android.systemui.qs.panels.ui.viewmodel.IconTilesViewModel
-import com.android.systemui.qs.panels.ui.viewmodel.TileViewModel
-import com.android.systemui.qs.pipeline.shared.TileSpec
-import com.android.systemui.res.R
-import javax.inject.Inject
-
-@SysUISingleton
-class StretchedGridLayout
-@Inject
-constructor(
-    private val iconTilesViewModel: IconTilesViewModel,
-    private val gridSizeViewModel: FixedColumnsSizeViewModel,
-) : GridLayout {
-
-    @Composable
-    override fun TileGrid(
-        tiles: List<TileViewModel>,
-        modifier: Modifier,
-        editModeStart: () -> Unit,
-    ) {
-        DisposableEffect(tiles) {
-            val token = Any()
-            tiles.forEach { it.startListening(token) }
-            onDispose { tiles.forEach { it.stopListening(token) } }
-        }
-
-        // Tile widths [normal|stretched]
-        // Icon [3 | 4]
-        // Large [6 | 8]
-        val columns = 12
-        val stretchedTiles =
-            remember(tiles) {
-                val sizedTiles =
-                    tiles.map {
-                        SizedTile(
-                            it,
-                            if (iconTilesViewModel.isIconTile(it.spec)) {
-                                3
-                            } else {
-                                6
-                            }
-                        )
-                    }
-                splitInRows(sizedTiles, columns)
-            }
-
-        TileLazyGrid(columns = GridCells.Fixed(columns), modifier = modifier) {
-            items(stretchedTiles.size, span = { GridItemSpan(stretchedTiles[it].width) }) { index ->
-                Tile(
-                    tile = stretchedTiles[index].tile,
-                    iconOnly = iconTilesViewModel.isIconTile(stretchedTiles[index].tile.spec),
-                    modifier = Modifier.height(dimensionResource(id = R.dimen.qs_tile_height))
-                )
-            }
-        }
-    }
-
-    @Composable
-    override fun EditTileGrid(
-        tiles: List<EditTileViewModel>,
-        modifier: Modifier,
-        onAddTile: (TileSpec, Int) -> Unit,
-        onRemoveTile: (TileSpec) -> Unit
-    ) {
-        val columns by gridSizeViewModel.columns.collectAsStateWithLifecycle()
-
-        DefaultEditTileGrid(
-            tiles = tiles,
-            isIconOnly = iconTilesViewModel::isIconTile,
-            columns = columns,
-            modifier = modifier,
-            onAddTile = onAddTile,
-            onRemoveTile = onRemoveTile,
-            onResize = iconTilesViewModel::resize,
-        )
-    }
-
-    private fun splitInRows(
-        tiles: List<SizedTile<TileViewModel>>,
-        columns: Int
-    ): List<SizedTile<TileViewModel>> {
-        val row = TileRow<TileViewModel>(columns)
-
-        return buildList {
-            for (tile in tiles) {
-                if (row.maybeAddTile(tile)) {
-                    if (row.isFull()) {
-                        // Row is full, no need to stretch tiles
-                        addAll(row.tiles)
-                        row.clear()
-                    }
-                } else {
-                    if (row.isFull()) {
-                        addAll(row.tiles)
-                    } else {
-                        // Stretching tiles when row isn't full
-                        addAll(row.tiles.map { it.copy(width = it.width + (it.width / 3)) })
-                    }
-                    row.clear()
-                    row.maybeAddTile(tile)
-                }
-            }
-            addAll(row.tiles)
-        }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/Tile.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/Tile.kt
index bd7956d..cb9d0f6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/Tile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/Tile.kt
@@ -294,9 +294,9 @@
     onRemoveTile: (TileSpec) -> Unit,
     onResize: (TileSpec, Boolean) -> Unit,
 ) {
-    val currentListState = rememberEditListState(tiles)
+    val (currentTiles, otherTiles) = tiles.partition { it.isCurrent }
+    val currentListState = rememberEditListState(currentTiles)
     val dragAndDropState = rememberDragAndDropState(currentListState)
-    val (currentTiles, otherTiles) = currentListState.tiles.partition { it.isCurrent }
 
     val addTileToEnd: (TileSpec) -> Unit by rememberUpdatedState {
         onAddTile(it, CurrentTilesInteractor.POSITION_AT_END)
@@ -329,7 +329,7 @@
             }
 
             CurrentTilesGrid(
-                currentTiles,
+                currentListState.tiles,
                 columns,
                 tilePadding,
                 isIconOnly,
@@ -480,16 +480,13 @@
     onClick: (TileSpec) -> Unit,
     dragAndDropState: DragAndDropState,
 ) {
-    val (otherTilesStock, otherTilesCustom) =
-        tiles.filter { !dragAndDropState.isMoving(it.tileSpec) }.partition { it.appName == null }
+    val (otherTilesStock, otherTilesCustom) = tiles.partition { it.appName == null }
     val availableTileHeight = tileHeight(true)
     val availableGridHeight = gridHeight(tiles.size, availableTileHeight, columns, tilePadding)
 
     // Available tiles
     TileLazyGrid(
-        modifier =
-            Modifier.height(availableGridHeight)
-                .dragAndDropTileList(dragAndDropState, { false }, { _, _ -> }),
+        modifier = Modifier.height(availableGridHeight),
         columns = GridCells.Fixed(columns)
     ) {
         editTiles(
@@ -594,7 +591,7 @@
                         }
                         .dragAndDropTile(dragAndDropState, viewModel.tileSpec, acceptDrops, onDrop)
                         .dragAndDropTileSource(
-                            viewModel.tileSpec,
+                            viewModel,
                             onClick,
                             onDoubleTap,
                             dragAndDropState,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt
index 97b5e87..02379e6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt
@@ -46,7 +46,7 @@
 import com.android.systemui.retail.data.repository.RetailModeRepository
 import com.android.systemui.settings.UserTracker
 import com.android.systemui.user.data.repository.UserRepository
-import com.android.systemui.util.kotlin.pairwise
+import com.android.systemui.util.kotlin.pairwiseBy
 import dagger.Lazy
 import java.io.PrintWriter
 import javax.inject.Inject
@@ -63,7 +63,6 @@
 import kotlinx.coroutines.flow.first
 import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.flowOn
-import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.withContext
 
@@ -169,17 +168,19 @@
     private val userAndTiles =
         currentUser
             .flatMapLatest { userId ->
-                tileSpecRepository.tilesSpecs(userId).map { UserAndTiles(userId, it) }
+                val currentTiles = tileSpecRepository.tilesSpecs(userId)
+                val installedComponents =
+                    installedTilesComponentRepository.getInstalledTilesComponents(userId)
+                currentTiles.combine(installedComponents) { tiles, components ->
+                    UserTilesAndComponents(userId, tiles, components)
+                }
             }
             .distinctUntilChanged()
-            .pairwise(UserAndTiles(-1, emptyList()))
+            .pairwiseBy(UserTilesAndComponents(-1, emptyList(), emptySet())) { prev, new ->
+                DataWithUserChange(data = new, userChange = prev.userId != new.userId)
+            }
             .flowOn(backgroundDispatcher)
 
-    private val installedPackagesWithTiles =
-        currentUser.flatMapLatest {
-            installedTilesComponentRepository.getInstalledTilesComponents(it)
-        }
-
     private val minTiles: Int
         get() =
             if (retailModeRepository.inRetailMode) {
@@ -194,7 +195,6 @@
         }
     }
 
-    @OptIn(ExperimentalCoroutinesApi::class)
     private fun startTileCollection() {
         scope.launch {
             launch {
@@ -205,95 +205,82 @@
             }
 
             launch(backgroundDispatcher) {
-                userAndTiles
-                    .combine(installedPackagesWithTiles) { usersAndTiles, packages ->
-                        Data(
-                            usersAndTiles.previousValue,
-                            usersAndTiles.newValue,
-                            packages,
-                        )
-                    }
-                    .collectLatest {
-                        val newTileList = it.newData.tiles
-                        val userChanged = it.oldData.userId != it.newData.userId
-                        val newUser = it.newData.userId
-                        val components = it.installedComponents
+                userAndTiles.collectLatest {
+                    val newUser = it.userId
+                    val newTileList = it.tiles
+                    val components = it.installedComponents
+                    val userChanged = it.userChange
 
-                        // Destroy all tiles that are not in the new set
-                        specsToTiles
-                            .filter {
-                                it.key !in newTileList && it.value is TileOrNotInstalled.Tile
-                            }
-                            .forEach { entry ->
-                                logger.logTileDestroyed(
-                                    entry.key,
-                                    if (userChanged) {
-                                        QSPipelineLogger.TileDestroyedReason
-                                            .TILE_NOT_PRESENT_IN_NEW_USER
-                                    } else {
-                                        QSPipelineLogger.TileDestroyedReason.TILE_REMOVED
-                                    }
-                                )
-                                (entry.value as TileOrNotInstalled.Tile).tile.destroy()
-                            }
-                        // MutableMap will keep the insertion order
-                        val newTileMap = mutableMapOf<TileSpec, TileOrNotInstalled>()
-
-                        newTileList.forEach { tileSpec ->
-                            if (tileSpec !in newTileMap) {
-                                if (
-                                    tileSpec is TileSpec.CustomTileSpec &&
-                                        tileSpec.componentName !in components
-                                ) {
-                                    newTileMap[tileSpec] = TileOrNotInstalled.NotInstalled
+                    // Destroy all tiles that are not in the new set
+                    specsToTiles
+                        .filter { it.key !in newTileList && it.value is TileOrNotInstalled.Tile }
+                        .forEach { entry ->
+                            logger.logTileDestroyed(
+                                entry.key,
+                                if (userChanged) {
+                                    QSPipelineLogger.TileDestroyedReason
+                                        .TILE_NOT_PRESENT_IN_NEW_USER
                                 } else {
-                                    // Create tile here will never try to create a CustomTile that
-                                    // is not installed
-                                    val newTile =
-                                        if (tileSpec in specsToTiles) {
-                                            processExistingTile(
-                                                tileSpec,
-                                                specsToTiles.getValue(tileSpec),
-                                                userChanged,
-                                                newUser
-                                            )
-                                                ?: createTile(tileSpec)
-                                        } else {
-                                            createTile(tileSpec)
-                                        }
-                                    if (newTile != null) {
-                                        newTileMap[tileSpec] = TileOrNotInstalled.Tile(newTile)
+                                    QSPipelineLogger.TileDestroyedReason.TILE_REMOVED
+                                }
+                            )
+                            (entry.value as TileOrNotInstalled.Tile).tile.destroy()
+                        }
+                    // MutableMap will keep the insertion order
+                    val newTileMap = mutableMapOf<TileSpec, TileOrNotInstalled>()
+
+                    newTileList.forEach { tileSpec ->
+                        if (tileSpec !in newTileMap) {
+                            if (
+                                tileSpec is TileSpec.CustomTileSpec &&
+                                    tileSpec.componentName !in components
+                            ) {
+                                newTileMap[tileSpec] = TileOrNotInstalled.NotInstalled
+                            } else {
+                                // Create tile here will never try to create a CustomTile that
+                                // is not installed
+                                val newTile =
+                                    if (tileSpec in specsToTiles) {
+                                        processExistingTile(
+                                            tileSpec,
+                                            specsToTiles.getValue(tileSpec),
+                                            userChanged,
+                                            newUser
+                                        ) ?: createTile(tileSpec)
+                                    } else {
+                                        createTile(tileSpec)
                                     }
+                                if (newTile != null) {
+                                    newTileMap[tileSpec] = TileOrNotInstalled.Tile(newTile)
                                 }
                             }
                         }
-
-                        val resolvedSpecs = newTileMap.keys.toList()
-                        specsToTiles.clear()
-                        specsToTiles.putAll(newTileMap)
-                        val newResolvedTiles =
-                            newTileMap
-                                .filter { it.value is TileOrNotInstalled.Tile }
-                                .map {
-                                    TileModel(it.key, (it.value as TileOrNotInstalled.Tile).tile)
-                                }
-
-                        _currentSpecsAndTiles.value = newResolvedTiles
-                        logger.logTilesNotInstalled(
-                            newTileMap.filter { it.value is TileOrNotInstalled.NotInstalled }.keys,
-                            newUser
-                        )
-                        if (newResolvedTiles.size < minTiles) {
-                            // We ended up with not enough tiles (some may be not installed).
-                            // Prepend the default set of tiles
-                            launch { tileSpecRepository.prependDefault(currentUser.value) }
-                        } else if (resolvedSpecs != newTileList) {
-                            // There were some tiles that couldn't be created. Change the value in
-                            // the
-                            // repository
-                            launch { tileSpecRepository.setTiles(currentUser.value, resolvedSpecs) }
-                        }
                     }
+
+                    val resolvedSpecs = newTileMap.keys.toList()
+                    specsToTiles.clear()
+                    specsToTiles.putAll(newTileMap)
+                    val newResolvedTiles =
+                        newTileMap
+                            .filter { it.value is TileOrNotInstalled.Tile }
+                            .map { TileModel(it.key, (it.value as TileOrNotInstalled.Tile).tile) }
+
+                    _currentSpecsAndTiles.value = newResolvedTiles
+                    logger.logTilesNotInstalled(
+                        newTileMap.filter { it.value is TileOrNotInstalled.NotInstalled }.keys,
+                        newUser
+                    )
+                    if (newResolvedTiles.size < minTiles) {
+                        // We ended up with not enough tiles (some may be not installed).
+                        // Prepend the default set of tiles
+                        launch { tileSpecRepository.prependDefault(currentUser.value) }
+                    } else if (resolvedSpecs != newTileList) {
+                        // There were some tiles that couldn't be created. Change the value in
+                        // the
+                        // repository
+                        launch { tileSpecRepository.setTiles(currentUser.value, resolvedSpecs) }
+                    }
+                }
             }
         }
     }
@@ -362,8 +349,7 @@
                     newQSTileFactory.get().createTile(spec.spec)
                 } else {
                     null
-                }
-                    ?: tileFactory.createTile(spec.spec)
+                } ?: tileFactory.createTile(spec.spec)
             }
         if (tile == null) {
             logger.logTileNotFoundInFactory(spec)
@@ -436,15 +422,25 @@
 
         @JvmInline value class Tile(val tile: QSTile) : TileOrNotInstalled
     }
-
-    private data class UserAndTiles(
-        val userId: Int,
-        val tiles: List<TileSpec>,
-    )
-
-    private data class Data(
-        val oldData: UserAndTiles,
-        val newData: UserAndTiles,
-        val installedComponents: Set<ComponentName>,
-    )
 }
+
+private data class UserTilesAndComponents(
+    val userId: Int,
+    val tiles: List<TileSpec>,
+    val installedComponents: Set<ComponentName>
+)
+
+private data class DataWithUserChange(
+    val userId: Int,
+    val tiles: List<TileSpec>,
+    val installedComponents: Set<ComponentName>,
+    val userChange: Boolean,
+)
+
+private fun DataWithUserChange(data: UserTilesAndComponents, userChange: Boolean) =
+    DataWithUserChange(
+        data.userId,
+        data.tiles,
+        data.installedComponents,
+        userChange,
+    )
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 6b3dfe1..abc0453 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
@@ -107,7 +107,9 @@
         set(value) {
             if (field == value) return
             field = value
-            updateHeight()
+            if (longPressEffect?.state != QSLongPressEffect.State.RUNNING_BACKWARDS_FROM_CANCEL) {
+                updateHeight()
+            }
         }
 
     override var squishinessFraction: Float = 1f
@@ -381,14 +383,6 @@
     }
 
     private fun updateHeight() {
-        // TODO(b/332900989): Find a more robust way of resetting the tile if not reset by the
-        //  launch animation.
-        if (!haveLongPressPropertiesBeenReset && longPressEffect != null) {
-            // The launch animation of a long-press effect did not reset the long-press effect so
-            // we must do it here
-            resetLongPressEffectProperties()
-            longPressEffect.resetState()
-        }
         val actualHeight =
             if (heightOverride != HeightOverrideable.NO_OVERRIDE) {
                 heightOverride
@@ -417,17 +411,17 @@
     }
 
     override fun init(tile: QSTile) {
-        val expandable = Expandable.fromView(this)
         if (longPressEffect != null) {
             isHapticFeedbackEnabled = false
             longPressEffect.qsTile = tile
-            longPressEffect.expandable = expandable
+            longPressEffect.createExpandableFromView(this)
             initLongPressEffectCallback()
             init(
                 { _: View -> longPressEffect.onTileClick() },
                 null, // Haptics and long-clicks will be handled by the [QSLongPressEffect]
             )
         } else {
+            val expandable = Expandable.fromView(this)
             init(
                 { _: View? -> tile.click(expandable) },
                 { _: View? ->
@@ -475,10 +469,10 @@
                     }
                 }
 
-                override fun onReverseAnimator() {
+                override fun onReverseAnimator(playHaptics: Boolean) {
                     longPressEffectAnimator?.let {
                         val pausedProgress = it.animatedFraction
-                        longPressEffect?.playReverseHaptics(pausedProgress)
+                        if (playHaptics) longPressEffect?.playReverseHaptics(pausedProgress)
                         it.reverse()
                     }
                 }
@@ -592,6 +586,15 @@
                     )
                 )
             )
+        } else {
+            if (isLongClickable) {
+                info.addAction(
+                    AccessibilityNodeInfo.AccessibilityAction(
+                        AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK.id,
+                        resources.getString(R.string.accessibility_long_click_tile)
+                    )
+                )
+            }
         }
         if (!TextUtils.isEmpty(accessibilityClass)) {
             info.className =
@@ -603,14 +606,6 @@
             if (Switch::class.java.name == accessibilityClass) {
                 info.isChecked = tileState
                 info.isCheckable = true
-                if (isLongClickable) {
-                    info.addAction(
-                        AccessibilityNodeInfo.AccessibilityAction(
-                            AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK.id,
-                            resources.getString(R.string.accessibility_long_click_tile)
-                        )
-                    )
-                }
             }
         }
         if (position != INVALID) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/SubtitleArrayMapping.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/SubtitleArrayMapping.kt
index 53594bb..f702da4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/SubtitleArrayMapping.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/SubtitleArrayMapping.kt
@@ -27,7 +27,6 @@
         subtitleIdsMap["cell"] = R.array.tile_states_cell
         subtitleIdsMap["battery"] = R.array.tile_states_battery
         subtitleIdsMap["dnd"] = R.array.tile_states_dnd
-        subtitleIdsMap["modes"] = R.array.tile_states_modes
         subtitleIdsMap["flashlight"] = R.array.tile_states_flashlight
         subtitleIdsMap["rotation"] = R.array.tile_states_rotation
         subtitleIdsMap["bt"] = R.array.tile_states_bt
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
index bdf935e..b927134 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
@@ -49,6 +49,7 @@
 import com.android.systemui.animation.Expandable;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.flags.RefactorFlagUtils;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.qs.QSTile.BooleanState;
@@ -105,6 +106,11 @@
     ) {
         super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger,
                 statusBarStateController, activityStarter, qsLogger);
+
+        // If the flag is on, this shouldn't run at all since the modes tile replaces the DND tile.
+        RefactorFlagUtils.INSTANCE.assertInLegacyMode(android.app.Flags.modesUi(),
+                android.app.Flags.FLAG_MODES_UI);
+
         mController = zenModeController;
         mSharedPreferences = sharedPreferences;
         mController.observe(getLifecycle(), mZenCallback);
@@ -253,18 +259,20 @@
             case Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS:
                 state.contentDescription =
                         mContext.getString(R.string.accessibility_quick_settings_dnd) + ", "
-                        + state.secondaryLabel;
+                                + state.secondaryLabel;
                 break;
             case Global.ZEN_MODE_NO_INTERRUPTIONS:
                 state.contentDescription =
                         mContext.getString(R.string.accessibility_quick_settings_dnd) + ", " +
-                        mContext.getString(R.string.accessibility_quick_settings_dnd_none_on)
+                                mContext.getString(
+                                        R.string.accessibility_quick_settings_dnd_none_on)
                                 + ", " + state.secondaryLabel;
                 break;
             case ZEN_MODE_ALARMS:
                 state.contentDescription =
                         mContext.getString(R.string.accessibility_quick_settings_dnd) + ", " +
-                        mContext.getString(R.string.accessibility_quick_settings_dnd_alarms_on)
+                                mContext.getString(
+                                        R.string.accessibility_quick_settings_dnd_alarms_on)
                                 + ", " + state.secondaryLabel;
                 break;
             default:
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ModesTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/ModesTile.kt
index a300031..2a33a16 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ModesTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ModesTile.kt
@@ -23,13 +23,15 @@
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.coroutineScope
 import androidx.lifecycle.repeatOnLifecycle
+import com.android.internal.R.attr.contentDescription
 import com.android.internal.logging.MetricsLogger
 import com.android.systemui.animation.Expandable
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.flags.RefactorFlagUtils.isUnexpectedlyInLegacyMode
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.plugins.FalsingManager
-import com.android.systemui.plugins.qs.QSTile.BooleanState
+import com.android.systemui.plugins.qs.QSTile
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.qs.QSHost
 import com.android.systemui.qs.QsEventLogger
@@ -63,7 +65,7 @@
     private val tileMapper: ModesTileMapper,
     private val userActionInteractor: ModesTileUserActionInteractor,
 ) :
-    QSTileImpl<BooleanState>(
+    QSTileImpl<QSTile.State>(
         host,
         uiEventLogger,
         backgroundLooper,
@@ -79,6 +81,8 @@
     private val config = qsTileConfigProvider.getConfig(TILE_SPEC)
 
     init {
+        /* Check if */ isUnexpectedlyInLegacyMode(Flags.modesUi(), Flags.FLAG_MODES_UI)
+
         lifecycle.coroutineScope.launch {
             lifecycle.repeatOnLifecycle(Lifecycle.State.RESUMED) {
                 dataInteractor.tileData().collect { refreshState(it) }
@@ -90,7 +94,7 @@
 
     override fun getTileLabel(): CharSequence = tileState.label
 
-    override fun newTileState() = BooleanState()
+    override fun newTileState() = QSTile.State()
 
     override fun handleClick(expandable: Expandable?) = runBlocking {
         userActionInteractor.handleClick(expandable)
@@ -98,22 +102,22 @@
 
     override fun getLongClickIntent(): Intent = userActionInteractor.longClickIntent
 
-    override fun handleUpdateState(booleanState: BooleanState?, arg: Any?) {
+    override fun handleUpdateState(state: QSTile.State?, arg: Any?) {
         if (arg is ModesTileModel) {
             tileState = tileMapper.map(config, arg)
 
-            booleanState?.apply {
-                state = tileState.activationState.legacyState
+            state?.apply {
+                this.state = tileState.activationState.legacyState
                 icon = ResourceIcon.get(tileState.iconRes ?: R.drawable.qs_dnd_icon_off)
                 label = tileLabel
                 secondaryLabel = tileState.secondaryLabel
                 contentDescription = tileState.contentDescription
-                forceExpandIcon = true
+                expandedAccessibilityClassName = tileState.expandedAccessibilityClassName
             }
         }
     }
 
     companion object {
-        const val TILE_SPEC = "modes"
+        const val TILE_SPEC = "dnd"
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
index d9546ec..1750347 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
@@ -106,7 +106,8 @@
     @Override
     @MainThread
     public void onManagedProfileRemoved() {
-        mHost.removeTile(getTileSpec());
+        // No OP as this may race with the user change in CurrentTilesInteractor.
+        // If the tile needs to be removed, AutoAdd (or AutoTileManager) will take care of that.
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
index 158eb6e..b2873c5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
@@ -736,7 +736,8 @@
         // Set network description for the carrier network when connecting to the carrier network
         // under the airplane mode ON.
         if (activeNetworkIsCellular() || isCarrierNetworkActive()) {
-            summary = context.getString(R.string.preference_summary_default_combination,
+            summary = context.getString(
+                    com.android.settingslib.R.string.preference_summary_default_combination,
                     context.getString(
                             isForDds // if nonDds is active, explains Dds status as poor connection
                                     ? (isOnNonDds ? R.string.mobile_data_poor_connection
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegate.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegate.java
index edc49cac2..f018336 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegate.java
@@ -489,6 +489,10 @@
             mMobileDataToggle.setVisibility(mCanConfigMobileData ? View.VISIBLE : View.INVISIBLE);
             mMobileToggleDivider.setVisibility(
                     mCanConfigMobileData ? View.VISIBLE : View.INVISIBLE);
+            int primaryColor = isNetworkConnected
+                    ? R.color.connected_network_primary_color
+                    : R.color.disconnected_network_primary_color;
+            mMobileToggleDivider.setBackgroundColor(dialog.getContext().getColor(primaryColor));
 
             // Display the info for the non-DDS if it's actively being used
             int autoSwitchNonDdsSubId = mInternetDialogController.getActiveAutoSwitchNonDdsSubId();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractor.kt
index 31e91aa..92efa40 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractor.kt
@@ -19,20 +19,27 @@
 import android.app.Flags
 import android.os.UserHandle
 import com.android.settingslib.notification.data.repository.ZenModeRepository
+import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
 import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
 import com.android.systemui.qs.tiles.impl.modes.domain.model.ModesTileModel
 import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.flowOn
 import kotlinx.coroutines.flow.map
 
-class ModesTileDataInteractor @Inject constructor(val zenModeRepository: ZenModeRepository) :
-    QSTileDataInteractor<ModesTileModel> {
-    private val zenModeActive =
+class ModesTileDataInteractor
+@Inject
+constructor(
+    val zenModeRepository: ZenModeRepository,
+    @Background val bgDispatcher: CoroutineDispatcher,
+) : QSTileDataInteractor<ModesTileModel> {
+    private val activeModes =
         zenModeRepository.modes
-            .map { modes -> modes.any { mode -> mode.isActive } }
+            .map { modes -> modes.filter { mode -> mode.isActive }.map { it.name } }
             .distinctUntilChanged()
 
     override fun tileData(
@@ -45,7 +52,10 @@
      *
      * TODO(b/299909989): Remove after the transition.
      */
-    fun tileData() = zenModeActive.map { ModesTileModel(isActivated = it) }
+    fun tileData() =
+        activeModes
+            .map { ModesTileModel(isActivated = it.isNotEmpty(), activeModes = it) }
+            .flowOn(bgDispatcher)
 
     override fun availability(user: UserHandle): Flow<Boolean> = flowOf(Flags.modesUi())
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractor.kt
index 4c6563d..083bf05 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractor.kt
@@ -16,14 +16,10 @@
 
 package com.android.systemui.qs.tiles.impl.modes.domain.interactor
 
-//noinspection CleanArchitectureDependencyViolation: dialog needs to be opened on click
 import android.content.Intent
 import android.provider.Settings
-import com.android.internal.jank.InteractionJankMonitor
-import com.android.systemui.animation.DialogCuj
-import com.android.systemui.animation.DialogTransitionAnimator
 import com.android.systemui.animation.Expandable
-import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.dagger.SysUISingleton
 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
@@ -31,15 +27,13 @@
 import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
 import com.android.systemui.statusbar.policy.ui.dialog.ModesDialogDelegate
 import javax.inject.Inject
-import kotlin.coroutines.CoroutineContext
-import kotlinx.coroutines.withContext
 
+@SysUISingleton
 class ModesTileUserActionInteractor
 @Inject
 constructor(
-    @Main private val coroutineContext: CoroutineContext,
-    private val qsTileIntentUserActionHandler: QSTileIntentUserInputHandler,
-    private val dialogTransitionAnimator: DialogTransitionAnimator,
+    private val qsTileIntentUserInputHandler: QSTileIntentUserInputHandler,
+    // TODO(b/353896370): The domain layer should not have to depend on the UI layer.
     private val dialogDelegate: ModesDialogDelegate,
 ) : QSTileUserActionInteractor<ModesTileModel> {
     val longClickIntent = Intent(Settings.ACTION_ZEN_MODE_SETTINGS)
@@ -51,29 +45,14 @@
                     handleClick(action.expandable)
                 }
                 is QSTileUserAction.LongClick -> {
-                    qsTileIntentUserActionHandler.handle(action.expandable, longClickIntent)
+                    qsTileIntentUserInputHandler.handle(action.expandable, longClickIntent)
                 }
             }
         }
     }
 
     suspend fun handleClick(expandable: Expandable?) {
-        // Show a dialog with the list of modes to configure. Dialogs shown by the
-        // DialogTransitionAnimator must be created and shown on the main thread, so we post it to
-        // the UI handler.
-        withContext(coroutineContext) {
-            val dialog = dialogDelegate.createDialog()
-
-            expandable
-                ?.dialogTransitionController(
-                    DialogCuj(InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN, INTERACTION_JANK_TAG)
-                )
-                ?.let { controller -> dialogTransitionAnimator.show(dialog, controller) }
-                ?: dialog.show()
-        }
-    }
-
-    companion object {
-        private const val INTERACTION_JANK_TAG = "configure_priority_modes"
+        // Show a dialog with the list of modes to configure.
+        dialogDelegate.showDialog(expandable)
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/model/ModesTileModel.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/model/ModesTileModel.kt
index e44413a..cc509ea 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/model/ModesTileModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/model/ModesTileModel.kt
@@ -15,4 +15,4 @@
  */
 
 package com.android.systemui.qs.tiles.impl.modes.domain.model
-data class ModesTileModel(val isActivated: Boolean)
+data class ModesTileModel(val isActivated: Boolean, val activeModes: List<String>)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapper.kt
index 7048ada..7afdb75 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapper.kt
@@ -17,6 +17,8 @@
 package com.android.systemui.qs.tiles.impl.modes.ui
 
 import android.content.res.Resources
+import android.icu.text.MessageFormat
+import android.widget.Button
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
@@ -24,6 +26,7 @@
 import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
 import com.android.systemui.qs.tiles.viewmodel.QSTileState
 import com.android.systemui.res.R
+import java.util.Locale
 import javax.inject.Inject
 
 class ModesTileMapper
@@ -46,19 +49,32 @@
                     contentDescription = null,
                 )
             this.icon = { icon }
-            if (data.isActivated) {
-                activationState = QSTileState.ActivationState.ACTIVE
-                secondaryLabel = "Some modes enabled idk" // TODO(b/346519570)
-            } else {
-                activationState = QSTileState.ActivationState.INACTIVE
-                secondaryLabel = "Off" // TODO(b/346519570)
-            }
-            contentDescription = label
+            activationState =
+                if (data.isActivated) {
+                    QSTileState.ActivationState.ACTIVE
+                } else {
+                    QSTileState.ActivationState.INACTIVE
+                }
+            secondaryLabel = getModesStatus(data, resources)
+            contentDescription = "$label. $secondaryLabel"
             supportedActions =
                 setOf(
                     QSTileState.UserAction.CLICK,
                     QSTileState.UserAction.LONG_CLICK,
                 )
             sideViewIcon = QSTileState.SideViewIcon.Chevron
+            expandedAccessibilityClass = Button::class
         }
+
+    private fun getModesStatus(data: ModesTileModel, resources: Resources): String {
+        val msgFormat =
+            MessageFormat(resources.getString(R.string.zen_mode_active_modes), Locale.getDefault())
+        val count = data.activeModes.count()
+        val args: MutableMap<String, Any> = HashMap()
+        args["count"] = count
+        if (count >= 1) {
+            args["mode"] = data.activeModes[0]
+        }
+        return msgFormat.format(args)
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfig.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfig.kt
index e9e9d8b..cdcefdb 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfig.kt
@@ -22,12 +22,15 @@
 import com.android.internal.logging.InstanceId
 import com.android.systemui.qs.pipeline.shared.TileSpec
 
-data class QSTileConfig(
+data class QSTileConfig
+@JvmOverloads
+constructor(
     val tileSpec: TileSpec,
     val uiConfig: QSTileUIConfig,
     val instanceId: InstanceId,
     val metricsSpec: String = tileSpec.spec,
     val policy: QSTilePolicy = QSTilePolicy.NoRestrictions,
+    val autoRemoveOnUnavailable: Boolean = true,
 )
 
 /**
@@ -38,6 +41,7 @@
 
     val iconRes: Int
         @DrawableRes get
+
     val labelRes: Int
         @StringRes get
 
@@ -48,6 +52,7 @@
     data object Empty : QSTileUIConfig {
         override val iconRes: Int
             get() = Resources.ID_NULL
+
         override val labelRes: Int
             get() = Resources.ID_NULL
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt
index ba0a8d6..045790c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt
@@ -19,7 +19,6 @@
 import android.content.Context
 import android.os.UserHandle
 import android.util.Log
-import androidx.annotation.GuardedBy
 import com.android.internal.logging.InstanceId
 import com.android.systemui.Dumpable
 import com.android.systemui.animation.Expandable
@@ -34,15 +33,16 @@
 import dagger.assisted.AssistedFactory
 import dagger.assisted.AssistedInject
 import java.io.PrintWriter
+import java.util.concurrent.CopyOnWriteArraySet
 import java.util.function.Supplier
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Job
 import kotlinx.coroutines.flow.collectIndexed
 import kotlinx.coroutines.flow.filterNotNull
-import kotlinx.coroutines.flow.first
 import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.flow.takeWhile
 import kotlinx.coroutines.launch
 
 // TODO(b/http://b/299909989): Use QSTileViewModel directly after the rollout
@@ -57,10 +57,8 @@
     private val context
         get() = qsHost.context
 
-    @GuardedBy("callbacks")
-    private val callbacks: MutableCollection<QSTile.Callback> = mutableSetOf()
-    @GuardedBy("listeningClients")
-    private val listeningClients: MutableCollection<Any> = mutableSetOf()
+    private val callbacks = CopyOnWriteArraySet<QSTile.Callback>()
+    private val listeningClients = CopyOnWriteArraySet<Any>()
 
     // Cancels the jobs when the adapter is no longer alive
     private var tileAdapterJob: Job? = null
@@ -72,7 +70,7 @@
             applicationScope.launch {
                 launch {
                     qsTileViewModel.isAvailable.collectIndexed { index, isAvailable ->
-                        if (!isAvailable) {
+                        if (!isAvailable && qsTileViewModel.config.autoRemoveOnUnavailable) {
                             qsHost.removeTile(tileSpec)
                         }
                         // qsTileViewModel.isAvailable flow often starts with isAvailable == true.
@@ -89,8 +87,9 @@
                         }
                     }
                 }
-                // Warm up tile with some initial state
-                launch { qsTileViewModel.state.first() }
+                // Warm up tile with some initial state. Because `state` is a StateFlow with initial
+                // state `null`, we collect until it's not null.
+                launch { qsTileViewModel.state.takeWhile { it == null }.collect {} }
             }
 
         // QSTileHost doesn't call this when userId is initialized
@@ -113,19 +112,17 @@
 
     override fun addCallback(callback: QSTile.Callback?) {
         callback ?: return
-        synchronized(callbacks) {
-            callbacks.add(callback)
-            state?.let(callback::onStateChanged)
-        }
+        callbacks.add(callback)
+        state?.let(callback::onStateChanged)
     }
 
     override fun removeCallback(callback: QSTile.Callback?) {
         callback ?: return
-        synchronized(callbacks) { callbacks.remove(callback) }
+        callbacks.remove(callback)
     }
 
     override fun removeCallbacks() {
-        synchronized(callbacks) { callbacks.clear() }
+        callbacks.clear()
     }
 
     override fun click(expandable: Expandable?) {
@@ -163,32 +160,28 @@
 
     override fun setListening(client: Any?, listening: Boolean) {
         client ?: return
-        synchronized(listeningClients) {
-            if (listening) {
-                listeningClients.add(client)
-                if (listeningClients.size == 1) {
-                    stateJob =
-                        qsTileViewModel.state
-                            .filterNotNull()
-                            .map { mapState(context, it, qsTileViewModel.config) }
-                            .onEach { legacyState ->
-                                synchronized(callbacks) {
-                                    callbacks.forEach { it.onStateChanged(legacyState) }
-                                }
-                            }
-                            .launchIn(applicationScope)
-                }
-            } else {
-                listeningClients.remove(client)
-                if (listeningClients.isEmpty()) {
-                    stateJob?.cancel()
-                }
+        if (listening) {
+            listeningClients.add(client)
+            if (listeningClients.size == 1) {
+                stateJob =
+                    qsTileViewModel.state
+                        .filterNotNull()
+                        .map { mapState(context, it, qsTileViewModel.config) }
+                        .onEach { legacyState ->
+                            callbacks.forEach { it.onStateChanged(legacyState) }
+                        }
+                        .launchIn(applicationScope)
+            }
+        } else {
+            listeningClients.remove(client)
+            if (listeningClients.isEmpty()) {
+                stateJob?.cancel()
             }
         }
     }
 
     override fun isListening(): Boolean =
-        synchronized(listeningClients) { listeningClients.isNotEmpty() }
+        listeningClients.isNotEmpty()
 
     override fun setDetailListening(show: Boolean) {
         // do nothing like QSTileImpl
diff --git a/packages/SystemUI/src/com/android/systemui/qs/ui/adapter/QSSceneAdapter.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/adapter/QSSceneAdapter.kt
index bb36fd5..ae2f32a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/ui/adapter/QSSceneAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/ui/adapter/QSSceneAdapter.kt
@@ -24,6 +24,7 @@
 import androidx.asynclayoutinflater.view.AsyncLayoutInflater
 import com.android.settingslib.applications.InterestingConfigChanges
 import com.android.systemui.Dumpable
+import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor
 import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
@@ -196,6 +197,7 @@
     private val qsSceneComponentFactory: QSSceneComponent.Factory,
     private val qsImplProvider: Provider<QSImpl>,
     shadeInteractor: ShadeInteractor,
+    displayStateInteractor: DisplayStateInteractor,
     dumpManager: DumpManager,
     @Main private val mainDispatcher: CoroutineDispatcher,
     @Application applicationScope: CoroutineScope,
@@ -208,6 +210,7 @@
         qsSceneComponentFactory: QSSceneComponent.Factory,
         qsImplProvider: Provider<QSImpl>,
         shadeInteractor: ShadeInteractor,
+        displayStateInteractor: DisplayStateInteractor,
         dumpManager: DumpManager,
         @Main dispatcher: CoroutineDispatcher,
         @Application scope: CoroutineScope,
@@ -216,6 +219,7 @@
         qsSceneComponentFactory,
         qsImplProvider,
         shadeInteractor,
+        displayStateInteractor,
         dumpManager,
         dispatcher,
         scope,
@@ -319,6 +323,10 @@
                     qsImpl.value?.setInSplitShade(it == ShadeMode.Split)
                 }
             }
+            launch {
+                combine(displayStateInteractor.isLargeScreen, qsImpl.filterNotNull(), ::Pair)
+                    .collect { it.second.setIsNotificationPanelFullWidth(!it.first) }
+            }
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModel.kt
index 79cdfec..b1cc55d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModel.kt
@@ -25,7 +25,6 @@
 import com.android.compose.animation.scene.UserAction
 import com.android.compose.animation.scene.UserActionResult
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.media.controls.domain.pipeline.interactor.MediaCarouselInteractor
 import com.android.systemui.qs.FooterActionsController
 import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel
@@ -38,20 +37,17 @@
 import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationsPlaceholderViewModel
 import java.util.concurrent.atomic.AtomicBoolean
 import javax.inject.Inject
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.filter
 import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.stateIn
 
 /** Models UI state and handles user input for the quick settings scene. */
 @SysUISingleton
 class QuickSettingsSceneViewModel
 @Inject
 constructor(
-    @Application private val applicationScope: CoroutineScope,
     val brightnessMirrorViewModel: BrightnessMirrorViewModel,
     val shadeHeaderViewModel: ShadeHeaderViewModel,
     val qsSceneAdapter: QSSceneAdapter,
@@ -61,55 +57,35 @@
     sceneBackInteractor: SceneBackInteractor,
     val mediaCarouselInteractor: MediaCarouselInteractor,
 ) {
-    private val backScene: StateFlow<SceneKey> =
+    private val backScene: Flow<SceneKey> =
         sceneBackInteractor.backScene
             .filter { it != Scenes.QuickSettings }
             .map { it ?: Scenes.Shade }
-            .stateIn(
-                scope = applicationScope,
-                started = SharingStarted.WhileSubscribed(),
-                initialValue = Scenes.Shade,
-            )
 
-    val destinationScenes: StateFlow<Map<UserAction, UserActionResult>> =
+    val destinationScenes: Flow<Map<UserAction, UserActionResult>> =
         combine(
-                qsSceneAdapter.isCustomizerShowing,
-                backScene,
-                transform = ::destinationScenes,
-            )
-            .stateIn(
-                scope = applicationScope,
-                started = SharingStarted.WhileSubscribed(),
-                initialValue =
-                    destinationScenes(
-                        isCustomizing = qsSceneAdapter.isCustomizerShowing.value,
-                        backScene = backScene.value,
-                    ),
-            )
+            qsSceneAdapter.isCustomizerShowing,
+            backScene,
+        ) { isCustomizing, backScene ->
+            buildMap<UserAction, UserActionResult> {
+                if (isCustomizing) {
+                    // TODO(b/332749288) Empty map so there are no back handlers and back can close
+                    // customizer
 
-    val isMediaVisible: StateFlow<Boolean> = mediaCarouselInteractor.hasAnyMediaOrRecommendation
-
-    private fun destinationScenes(
-        isCustomizing: Boolean,
-        backScene: SceneKey?,
-    ): Map<UserAction, UserActionResult> {
-        return buildMap {
-            if (isCustomizing) {
-                // TODO(b/332749288) Empty map so there are no back handlers and back can close
-                // customizer
-
-                // TODO(b/330200163) Add an Up from Bottom to be able to collapse the shade
-                // while customizing
-            } else {
-                put(Back, UserActionResult(backScene ?: Scenes.Shade))
-                put(Swipe(SwipeDirection.Up), UserActionResult(backScene ?: Scenes.Shade))
-                put(
-                    Swipe(fromSource = Edge.Bottom, direction = SwipeDirection.Up),
-                    UserActionResult(SceneFamilies.Home),
-                )
+                    // TODO(b/330200163) Add an Up from Bottom to be able to collapse the shade
+                    // while customizing
+                } else {
+                    put(Back, UserActionResult(backScene))
+                    put(Swipe(SwipeDirection.Up), UserActionResult(backScene))
+                    put(
+                        Swipe(fromSource = Edge.Bottom, direction = SwipeDirection.Up),
+                        UserActionResult(SceneFamilies.Home),
+                    )
+                }
             }
         }
-    }
+
+    val isMediaVisible: StateFlow<Boolean> = mediaCarouselInteractor.hasAnyMediaOrRecommendation
 
     private val footerActionsControllerInitialized = AtomicBoolean(false)
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneViewModel.kt
index 66fcbac..e012f2c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneViewModel.kt
@@ -21,17 +21,13 @@
 import com.android.compose.animation.scene.UserAction
 import com.android.compose.animation.scene.UserActionResult
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.scene.shared.model.SceneFamilies
 import com.android.systemui.shade.domain.interactor.ShadeInteractor
 import com.android.systemui.shade.shared.model.ShadeAlignment
 import com.android.systemui.shade.ui.viewmodel.OverlayShadeViewModel
 import javax.inject.Inject
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.flow.SharingStarted
-import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.stateIn
 
 /** Models UI state and handles user input for the Quick Settings Shade scene. */
 @SysUISingleton
@@ -41,33 +37,22 @@
     private val shadeInteractor: ShadeInteractor,
     val overlayShadeViewModel: OverlayShadeViewModel,
     val quickSettingsContainerViewModel: QuickSettingsContainerViewModel,
-    @Application applicationScope: CoroutineScope,
 ) {
 
-    val isEditing: StateFlow<Boolean> = quickSettingsContainerViewModel.editModeViewModel.isEditing
-
-    val destinationScenes: StateFlow<Map<UserAction, UserActionResult>> =
-        isEditing
-            .map { editing -> destinations(editing) }
-            .stateIn(
-                applicationScope,
-                SharingStarted.WhileSubscribed(),
-                destinations(isEditing.value)
-            )
-
-    private fun destinations(editing: Boolean): Map<UserAction, UserActionResult> {
-        return buildMap {
-            put(
-                if (shadeInteractor.shadeAlignment == ShadeAlignment.Top) {
-                    Swipe.Up
-                } else {
-                    Swipe.Down
-                },
-                UserActionResult(SceneFamilies.Home)
-            )
-            if (!editing) {
-                put(Back, UserActionResult(SceneFamilies.Home))
+    val destinationScenes: Flow<Map<UserAction, UserActionResult>> =
+        quickSettingsContainerViewModel.editModeViewModel.isEditing.map { editing ->
+            buildMap {
+                put(
+                    if (shadeInteractor.shadeAlignment == ShadeAlignment.Top) {
+                        Swipe.Up
+                    } else {
+                        Swipe.Down
+                    },
+                    UserActionResult(SceneFamilies.Home)
+                )
+                if (!editing) {
+                    put(Back, UserActionResult(SceneFamilies.Home))
+                }
             }
         }
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 371707d..15366d5 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -89,6 +89,8 @@
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dump.DumpManager;
+import com.android.systemui.contextualeducation.GestureType;
+import com.android.systemui.education.domain.interactor.KeyboardTouchpadEduStatsInteractor;
 import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
 import com.android.systemui.keyguard.KeyguardWmStateRefactor;
 import com.android.systemui.keyguard.WakefulnessLifecycle;
@@ -160,6 +162,8 @@
     private final NotificationShadeWindowController mStatusBarWinController;
     private final Provider<SceneInteractor> mSceneInteractor;
 
+    private final KeyboardTouchpadEduStatsInteractor mKeyboardTouchpadEduStatsInteractor;
+
     private final Runnable mConnectionRunnable = () ->
             internalConnectToCurrentUser("runnable: startConnectionToCurrentUser");
     private final ComponentName mRecentsComponentName;
@@ -661,7 +665,8 @@
             AssistUtils assistUtils,
             DumpManager dumpManager,
             Optional<UnfoldTransitionProgressForwarder> unfoldTransitionProgressForwarder,
-            BroadcastDispatcher broadcastDispatcher
+            BroadcastDispatcher broadcastDispatcher,
+            KeyboardTouchpadEduStatsInteractor keyboardTouchpadEduStatsInteractor
     ) {
         // b/241601880: This component should only be running for primary users or
         // secondaryUsers when visibleBackgroundUsers are supported.
@@ -698,6 +703,7 @@
         mDisplayTracker = displayTracker;
         mUnfoldTransitionProgressForwarder = unfoldTransitionProgressForwarder;
         mBroadcastDispatcher = broadcastDispatcher;
+        mKeyboardTouchpadEduStatsInteractor = keyboardTouchpadEduStatsInteractor;
 
         if (!KeyguardWmStateRefactor.isEnabled()) {
             mSysuiUnlockAnimationController = sysuiUnlockAnimationController;
@@ -929,6 +935,19 @@
         return isEnabled() && !QuickStepContract.isLegacyMode(mNavBarMode);
     }
 
+    /**
+     * Updates contextual education stats when a gesture is triggered
+     * @param isTrackpadGesture indicates if the gesture is triggered by trackpad
+     * @param gestureType type of gesture triggered
+     */
+    public void updateContextualEduStats(boolean isTrackpadGesture, GestureType gestureType) {
+        if (isTrackpadGesture) {
+            mKeyboardTouchpadEduStatsInteractor.updateShortcutTriggerTime(gestureType);
+        } else {
+            mKeyboardTouchpadEduStatsInteractor.incrementSignalCount(gestureType);
+        }
+    }
+
     public boolean isEnabled() {
         return mIsEnabled;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/CustomTraceSettingsDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/recordissue/CustomTraceSettingsDialogDelegate.kt
new file mode 100644
index 0000000..56270ce
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recordissue/CustomTraceSettingsDialogDelegate.kt
@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.recordissue
+
+import android.annotation.SuppressLint
+import android.app.AlertDialog
+import android.content.Context
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.widget.Switch
+import android.widget.TextView
+import com.android.systemui.recordissue.IssueRecordingState.Companion.TAG_TITLE_DELIMITER
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.phone.SystemUIDialog
+import com.android.traceur.PresetTraceConfigs
+import com.android.traceur.TraceConfig
+import com.android.traceur.res.R as T
+import java.util.function.Consumer
+
+class CustomTraceSettingsDialogDelegate(
+    private val factory: SystemUIDialog.Factory,
+    private val customTraceState: CustomTraceState,
+    private val tagTitles: Set<String>,
+    private val onSave: Runnable,
+) : SystemUIDialog.Delegate {
+
+    private val builder = TraceConfig.Builder(customTraceState.traceConfig)
+
+    override fun createDialog(): SystemUIDialog = factory.create(this)
+
+    override fun beforeCreate(dialog: SystemUIDialog?, savedInstanceState: Bundle?) {
+        super.beforeCreate(dialog, savedInstanceState)
+
+        dialog?.apply {
+            setTitle(R.string.custom_trace_settings_dialog_title)
+            setView(
+                LayoutInflater.from(context).inflate(R.layout.custom_trace_settings_dialog, null)
+            )
+            setPositiveButton(R.string.save) { _, _ ->
+                onSave.run()
+                customTraceState.traceConfig = builder.build()
+            }
+            setNegativeButton(R.string.cancel) { _, _ -> }
+        }
+    }
+
+    @SuppressLint("SetTextI18n")
+    override fun onCreate(dialog: SystemUIDialog?, savedInstanceState: Bundle?) {
+        super.onCreate(dialog, savedInstanceState)
+
+        dialog?.apply {
+            requireViewById<TextView>(R.id.categories).apply {
+                text =
+                    context.getString(T.string.categories) +
+                        "\n" +
+                        if (
+                            builder.tags == null ||
+                                builder.tags!! == PresetTraceConfigs.getDefaultConfig().tags
+                        ) {
+                            context.getString(R.string.notification_alert_title)
+                        } else {
+                            tagTitles
+                                .filter {
+                                    builder.tags!!.contains(it.substringBefore(TAG_TITLE_DELIMITER))
+                                }
+                                .joinToString()
+                        }
+                setOnClickListener { showCategorySelector(this) }
+            }
+            requireViewById<Switch>(R.id.attach_to_bugreport_switch).apply {
+                isChecked = builder.attachToBugreport
+                setOnCheckedChangeListener { _, isChecked -> builder.attachToBugreport = isChecked }
+            }
+            requireViewById<TextView>(R.id.cpu_buffer_size).setupSingleChoiceText(
+                T.array.buffer_size_values,
+                T.array.buffer_size_names,
+                builder.bufferSizeKb,
+                T.string.buffer_size,
+            ) {
+                builder.bufferSizeKb = it
+            }
+            val longTraceSizeText: TextView =
+                requireViewById<TextView>(R.id.long_trace_size).setupSingleChoiceText(
+                    T.array.long_trace_size_values,
+                    T.array.long_trace_size_names,
+                    builder.maxLongTraceSizeMb,
+                    T.string.max_long_trace_size,
+                ) {
+                    builder.maxLongTraceSizeMb = it
+                }
+            val longTraceDurationText: TextView =
+                requireViewById<TextView>(R.id.long_trace_duration).setupSingleChoiceText(
+                    T.array.long_trace_duration_values,
+                    T.array.long_trace_duration_names,
+                    builder.maxLongTraceDurationMinutes,
+                    T.string.max_long_trace_duration,
+                ) {
+                    builder.maxLongTraceDurationMinutes = it
+                }
+            requireViewById<Switch>(R.id.long_traces_switch).apply {
+                isChecked = builder.longTrace
+                val disabledAlpha by lazy { getDisabledAlpha(context) }
+                val alpha = if (isChecked) 1f else disabledAlpha
+                longTraceDurationText.alpha = alpha
+                longTraceSizeText.alpha = alpha
+
+                setOnCheckedChangeListener { _, isChecked ->
+                    builder.longTrace = isChecked
+                    longTraceDurationText.isEnabled = isChecked
+                    longTraceSizeText.isEnabled = isChecked
+
+                    val newAlpha = if (isChecked) 1f else disabledAlpha
+                    longTraceDurationText.alpha = newAlpha
+                    longTraceSizeText.alpha = newAlpha
+                }
+            }
+            requireViewById<Switch>(R.id.winscope_switch).apply {
+                isChecked = builder.winscope
+                setOnCheckedChangeListener { _, isChecked -> builder.winscope = isChecked }
+            }
+            requireViewById<Switch>(R.id.trace_debuggable_apps_switch).apply {
+                isChecked = builder.apps
+                setOnCheckedChangeListener { _, isChecked -> builder.apps = isChecked }
+            }
+            requireViewById<TextView>(R.id.long_traces_switch_label).text =
+                context.getString(T.string.long_traces)
+            requireViewById<TextView>(R.id.debuggable_apps_switch_label).text =
+                context.getString(T.string.trace_debuggable_applications)
+            requireViewById<TextView>(R.id.winscope_switch_label).text =
+                context.getString(T.string.winscope_tracing)
+            requireViewById<TextView>(R.id.attach_to_bugreport_switch_label).text =
+                context.getString(T.string.attach_to_bug_report)
+        }
+    }
+
+    @SuppressLint("SetTextI18n")
+    private fun showCategorySelector(root: TextView) {
+        showDialog(root.context) {
+            val tags = builder.tags ?: PresetTraceConfigs.getDefaultConfig().tags
+            val titlesToCheckmarks =
+                tagTitles.associateBy(
+                    { it },
+                    { tags.contains(it.substringBefore(TAG_TITLE_DELIMITER)) }
+                )
+            val titles = titlesToCheckmarks.keys.toTypedArray()
+            val checkmarks = titlesToCheckmarks.values.toBooleanArray()
+            val checkedTitleSuffixes =
+                titlesToCheckmarks.entries
+                    .filter { it.value }
+                    .map { it.key.substringAfter(TAG_TITLE_DELIMITER) }
+                    .toMutableSet()
+
+            val newTags = tags.toMutableSet()
+            setMultiChoiceItems(titles, checkmarks) { _, i, isChecked ->
+                val tag = titles[i].substringBefore(TAG_TITLE_DELIMITER)
+                val titleSuffix = titles[i].substringAfter(TAG_TITLE_DELIMITER)
+                if (isChecked) {
+                    newTags.add(tag)
+                    checkedTitleSuffixes.add(titleSuffix)
+                } else {
+                    newTags.remove(tag)
+                    checkedTitleSuffixes.remove(titleSuffix)
+                }
+            }
+            setPositiveButton(R.string.save) { _, _ ->
+                root.text =
+                    root.context.resources.getString(T.string.categories) +
+                        "\n" +
+                        checkedTitleSuffixes.joinToString()
+                builder.tags = newTags
+            }
+            setNeutralButton(R.string.restore_default) { _, _ ->
+                root.text =
+                    context.getString(T.string.categories) +
+                        "\n" +
+                        context.getString(R.string.notification_alert_title)
+                builder.tags = null
+            }
+            setNegativeButton(R.string.cancel) { _, _ -> }
+        }
+    }
+
+    @SuppressLint("SetTextI18n")
+    private fun TextView.setupSingleChoiceText(
+        resValues: Int,
+        resNames: Int,
+        startingValue: Int,
+        alertTitleRes: Int,
+        onChosen: Consumer<Int>,
+    ): TextView {
+        val values = resources.getStringArray(resValues).map { Integer.parseInt(it) }
+        val names = resources.getStringArray(resNames)
+        val startingIndex = values.indexOf(startingValue)
+        text = resources.getString(alertTitleRes) + "\n${names[startingIndex]}"
+
+        setOnClickListener {
+            showDialog(context) {
+                setTitle(alertTitleRes)
+                setSingleChoiceItems(names, startingIndex) { d, i ->
+                    text = resources.getString(alertTitleRes) + "\n${names[i]}"
+                    onChosen.accept(values[i])
+                    d.dismiss()
+                }
+            }
+        }
+        return this
+    }
+
+    private fun showDialog(context: Context, onBuilder: AlertDialog.Builder.() -> Unit) =
+        AlertDialog.Builder(context, R.style.Theme_SystemUI_Dialog_Alert)
+            .apply { onBuilder() }
+            .create()
+            .also { SystemUIDialog.applyFlags(it) }
+            .show()
+
+    private fun getDisabledAlpha(context: Context): Float {
+        val ta = context.obtainStyledAttributes(intArrayOf(android.R.attr.disabledAlpha))
+        val alpha = ta.getFloat(0, 0f)
+        ta.recycle()
+        return alpha
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/CustomTraceState.kt b/packages/SystemUI/src/com/android/systemui/recordissue/CustomTraceState.kt
new file mode 100644
index 0000000..14dfcc5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recordissue/CustomTraceState.kt
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.recordissue
+
+import android.content.SharedPreferences
+import com.android.traceur.PresetTraceConfigs.TraceOptions
+import com.android.traceur.PresetTraceConfigs.getDefaultConfig
+import com.android.traceur.TraceConfig
+
+class CustomTraceState(private val prefs: SharedPreferences) {
+
+    private var enabledTags: Set<String>?
+        get() = prefs.getStringSet(KEY_TAGS, getDefaultConfig().tags) ?: getDefaultConfig().tags
+        set(value) = prefs.edit().putStringSet(KEY_TAGS, value).apply()
+
+    var traceConfig: TraceConfig
+        get() = TraceConfig(options, enabledTags)
+        set(value) {
+            enabledTags = value.tags
+            options = value.options
+        }
+
+    private var options: TraceOptions
+        get() =
+            TraceOptions(
+                prefs.getInt(KEY_CUSTOM_BUFFER_SIZE_KB, getDefaultConfig().bufferSizeKb),
+                prefs.getBoolean(KEY_WINSCOPE, getDefaultConfig().winscope),
+                prefs.getBoolean(KEY_APPS, getDefaultConfig().apps),
+                prefs.getBoolean(KEY_LONG_TRACE, getDefaultConfig().longTrace),
+                prefs.getBoolean(KEY_ATTACH_TO_BUGREPORT, getDefaultConfig().attachToBugreport),
+                prefs.getInt(KEY_LONG_TRACE_SIZE_MB, getDefaultConfig().maxLongTraceSizeMb),
+                prefs.getInt(
+                    KEY_LONG_TRACE_DURATION_MINUTES,
+                    getDefaultConfig().maxLongTraceDurationMinutes
+                ),
+            )
+        set(value) {
+            prefs
+                .edit()
+                .putInt(KEY_CUSTOM_BUFFER_SIZE_KB, value.bufferSizeKb)
+                .putBoolean(KEY_WINSCOPE, value.winscope)
+                .putBoolean(KEY_APPS, value.apps)
+                .putBoolean(KEY_LONG_TRACE, value.longTrace)
+                .putBoolean(KEY_ATTACH_TO_BUGREPORT, value.attachToBugreport)
+                .putInt(KEY_LONG_TRACE_SIZE_MB, value.maxLongTraceSizeMb)
+                .putInt(KEY_LONG_TRACE_DURATION_MINUTES, value.maxLongTraceDurationMinutes)
+                .apply()
+        }
+
+    companion object {
+        private const val KEY_CUSTOM_BUFFER_SIZE_KB = "key_bufferSizeKb"
+        private const val KEY_WINSCOPE = "key_winscope"
+        private const val KEY_APPS = "key_apps"
+        private const val KEY_LONG_TRACE = "key_longTrace"
+        private const val KEY_ATTACH_TO_BUGREPORT = "key_attachToBugReport"
+        private const val KEY_LONG_TRACE_SIZE_MB = "key_maxLongTraceSizeMb"
+        private const val KEY_LONG_TRACE_DURATION_MINUTES = "key_maxLongTraceDurationInMinutes"
+        private const val KEY_TAGS = "key_tags"
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingState.kt b/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingState.kt
index 7612900..16642ab 100644
--- a/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingState.kt
+++ b/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingState.kt
@@ -38,6 +38,8 @@
     private val prefs =
         userFileManager.getSharedPreferences(TILE_SPEC, Context.MODE_PRIVATE, userTracker.userId)
 
+    val customTraceState = CustomTraceState(prefs)
+
     var takeBugreport
         get() = prefs.getBoolean(KEY_TAKE_BUG_REPORT, false)
         set(value) = prefs.edit().putBoolean(KEY_TAKE_BUG_REPORT, value).apply()
@@ -55,7 +57,12 @@
         set(value) = prefs.edit().putInt(KEY_ISSUE_TYPE_RES, value).apply()
 
     val traceConfig: TraceConfig
-        get() = ALL_ISSUE_TYPES[issueTypeRes] ?: PresetTraceConfigs.getDefaultConfig()
+        get() = ALL_ISSUE_TYPES[issueTypeRes] ?: customTraceState.traceConfig
+
+    // The 1st part of the title before the ": " is the tag, and the 2nd part is the description
+    var tagTitles: Set<String>
+        get() = prefs.getStringSet(KEY_TAG_TITLES, emptySet()) ?: emptySet()
+        set(value) = prefs.edit().putStringSet(KEY_TAG_TITLES, value).apply()
 
     private val listeners = CopyOnWriteArrayList<Runnable>()
 
@@ -81,8 +88,10 @@
         private const val KEY_TAKE_BUG_REPORT = "key_takeBugReport"
         private const val HAS_APPROVED_SCREEN_RECORDING = "HasApprovedScreenRecord"
         private const val KEY_RECORD_SCREEN = "key_recordScreen"
+        private const val KEY_TAG_TITLES = "key_tagTitles"
         const val KEY_ISSUE_TYPE_RES = "key_issueTypeRes"
         const val ISSUE_TYPE_NOT_SET = -1
+        const val TAG_TITLE_DELIMITER = ": "
 
         val ALL_ISSUE_TYPES: Map<Int, TraceConfig?> =
             hashMapOf(
@@ -90,6 +99,7 @@
                 Pair(R.string.user_interface, PresetTraceConfigs.getUiConfig()),
                 Pair(R.string.battery, PresetTraceConfigs.getBatteryConfig()),
                 Pair(R.string.thermal, PresetTraceConfigs.getThermalConfig()),
+                Pair(R.string.custom, null),
             )
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueDialogDelegate.kt
index 8a51ad4..f8b3ce1 100644
--- a/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueDialogDelegate.kt
+++ b/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueDialogDelegate.kt
@@ -35,7 +35,6 @@
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.flags.FeatureFlagsClassic
-import com.android.systemui.flags.Flags
 import com.android.systemui.flags.Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING_ENTERPRISE_POLICIES
 import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger
 import com.android.systemui.mediaprojection.SessionCreationSource
@@ -88,7 +87,7 @@
             setPositiveButton(R.string.qs_record_issue_start) { _, _ -> onStarted.run() }
         }
         bgExecutor.execute {
-            traceurMessageSender.onBoundToTraceur.add { traceurMessageSender.getTags() }
+            traceurMessageSender.onBoundToTraceur.add { traceurMessageSender.getTags(state) }
             traceurMessageSender.bindToTraceur(dialog.context)
         }
     }
@@ -154,10 +153,7 @@
             SessionCreationSource.SYSTEM_UI_SCREEN_RECORDER
         )
 
-        if (
-            flags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING) &&
-                !state.hasUserApprovedScreenRecording
-        ) {
+        if (!state.hasUserApprovedScreenRecording) {
             mainExecutor.execute {
                 ScreenCapturePermissionDialogDelegate(factory, state).createDialog().apply {
                     setOnCancelListener { screenRecordSwitch.isChecked = false }
@@ -170,18 +166,8 @@
     @MainThread
     private fun onIssueTypeClicked(context: Context, onIssueTypeSelected: Runnable) {
         val popupMenu = PopupMenu(context, issueTypeButton)
-
-        ALL_ISSUE_TYPES.keys.forEach {
-            popupMenu.menu.add(it).apply {
-                setIcon(R.drawable.arrow_pointing_down)
-                if (it != state.issueTypeRes) {
-                    iconTintList = ColorStateList.valueOf(Color.TRANSPARENT)
-                }
-                intent = Intent().putExtra(KEY_ISSUE_TYPE_RES, it)
-            }
-        }
-        popupMenu.apply {
-            setOnMenuItemClickListener {
+        val onMenuItemClickListener =
+            PopupMenu.OnMenuItemClickListener {
                 issueTypeButton.text = it.title
                 state.issueTypeRes =
                     it.intent?.getIntExtra(KEY_ISSUE_TYPE_RES, ISSUE_TYPE_NOT_SET)
@@ -189,6 +175,32 @@
                 onIssueTypeSelected.run()
                 true
             }
+        ALL_ISSUE_TYPES.keys.forEach {
+            popupMenu.menu.add(it).apply {
+                setIcon(R.drawable.arrow_pointing_down)
+                if (it != state.issueTypeRes) {
+                    iconTintList = ColorStateList.valueOf(Color.TRANSPARENT)
+                }
+                intent = Intent().putExtra(KEY_ISSUE_TYPE_RES, it)
+
+                if (it == R.string.custom) {
+                    setOnMenuItemClickListener {
+                        CustomTraceSettingsDialogDelegate(
+                                factory,
+                                state.customTraceState,
+                                state.tagTitles
+                            ) {
+                                onMenuItemClickListener.onMenuItemClick(it)
+                            }
+                            .createDialog()
+                            .show()
+                        true
+                    }
+                }
+            }
+        }
+        popupMenu.apply {
+            setOnMenuItemClickListener(onMenuItemClickListener)
             setForceShowIcon(true)
             show()
         }
diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/TraceurMessageSender.kt b/packages/SystemUI/src/com/android/systemui/recordissue/TraceurMessageSender.kt
index a31a9ef..8bfd14a 100644
--- a/packages/SystemUI/src/com/android/systemui/recordissue/TraceurMessageSender.kt
+++ b/packages/SystemUI/src/com/android/systemui/recordissue/TraceurMessageSender.kt
@@ -33,6 +33,7 @@
 import androidx.annotation.WorkerThread
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.recordissue.IssueRecordingState.Companion.TAG_TITLE_DELIMITER
 import com.android.traceur.FileSender
 import com.android.traceur.MessageConstants
 import com.android.traceur.TraceConfig
@@ -112,8 +113,8 @@
     }
 
     @WorkerThread
-    fun getTags() {
-        val replyHandler = Messenger(TagsHandler(backgroundLooper))
+    fun getTags(state: IssueRecordingState) {
+        val replyHandler = Messenger(TagsHandler(backgroundLooper, state))
         notifyTraceur(MessageConstants.TAGS_WHAT, replyTo = replyHandler)
     }
 
@@ -165,7 +166,8 @@
         }
     }
 
-    private class TagsHandler(looper: Looper) : Handler(looper) {
+    private class TagsHandler(looper: Looper, private val state: IssueRecordingState) :
+        Handler(looper) {
 
         override fun handleMessage(msg: Message) {
             if (MessageConstants.TAGS_WHAT == msg.what) {
@@ -174,16 +176,11 @@
                     msg.data.getStringArrayList(MessageConstants.BUNDLE_KEY_TAG_DESCRIPTIONS)
                 if (keys == null || values == null) {
                     throw IllegalArgumentException(
-                        "Neither keys: $keys, nor values: $values can " + "be null"
+                        "Neither keys: $keys, nor values: $values can be null"
                     )
                 }
-
-                val tags = keys.zip(values).map { "${it.first}: ${it.second}" }.toSet()
-                Log.e(
-                    TAG,
-                    "These tags: $tags will be saved and used for the Custom Trace" +
-                        " Config dialog in a future CL. This log will be removed."
-                )
+                state.tagTitles =
+                    keys.zip(values).map { it.first + TAG_TITLE_DELIMITER + it.second }.toSet()
             } else {
                 throw IllegalArgumentException("received unknown msg.what: " + msg.what)
             }
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
index 72f37fc..5b50133 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
@@ -60,6 +60,7 @@
 import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.shade.domain.interactor.ShadeInteractor
 import com.android.systemui.statusbar.NotificationShadeWindowController
+import com.android.systemui.statusbar.SysuiStatusBarStateController
 import com.android.systemui.statusbar.notification.domain.interactor.HeadsUpNotificationInteractor
 import com.android.systemui.statusbar.phone.CentralSurfaces
 import com.android.systemui.statusbar.policy.domain.interactor.DeviceProvisioningInteractor
@@ -130,6 +131,7 @@
     private val windowMgrLockscreenVisInteractor: WindowManagerLockscreenVisibilityInteractor,
     private val keyguardEnabledInteractor: KeyguardEnabledInteractor,
     private val dismissCallbackRegistry: DismissCallbackRegistry,
+    private val statusBarStateController: SysuiStatusBarStateController,
 ) : CoreStartable {
     private val centralSurfaces: CentralSurfaces?
         get() = centralSurfacesOptLazy.get().getOrNull()
@@ -170,12 +172,28 @@
 
     private fun resetShadeSessions() {
         applicationScope.launch {
-            sceneBackInteractor.backStack
-                // We are in a session if either Shade or QuickSettings is on the back stack
-                .map { backStack ->
-                    backStack.asIterable().any { it == Scenes.Shade || it == Scenes.QuickSettings }
+            combine(
+                    sceneBackInteractor.backStack
+                        // We are in a session if either Shade or QuickSettings is on the back stack
+                        .map { backStack ->
+                            backStack.asIterable().any {
+                                it == Scenes.Shade || it == Scenes.QuickSettings
+                            }
+                        }
+                        .distinctUntilChanged(),
+                    sceneInteractor.transitionState
+                        .mapNotNull { state ->
+                            // We are also in a session if either Shade or QuickSettings is the
+                            // current scene
+                            when (state) {
+                                is ObservableTransitionState.Idle -> state.currentScene
+                                is ObservableTransitionState.Transition -> state.fromScene
+                            }.let { it == Scenes.Shade || it == Scenes.QuickSettings }
+                        }
+                        .distinctUntilChanged()
+                ) { inBackStack, isCurrentScene ->
+                    inBackStack || isCurrentScene
                 }
-                .distinctUntilChanged()
                 // Once a session has ended, clear the session storage.
                 .filter { inSession -> !inSession }
                 .collect { shadeSessionStorage.clear() }
@@ -356,8 +374,13 @@
                         isOnBouncer ->
                             // When the device becomes unlocked in Bouncer, go to previous scene,
                             // or Gone.
-                            if (previousScene.value == Scenes.Lockscreen) {
-                                Scenes.Gone to "device was unlocked in Bouncer scene"
+                            if (
+                                previousScene.value == Scenes.Lockscreen ||
+                                    !statusBarStateController.leaveOpenOnKeyguardHide()
+                            ) {
+                                Scenes.Gone to
+                                    "device was unlocked in Bouncer scene and shade" +
+                                        " didn't need to be left open"
                             } else {
                                 val prevScene = previousScene.value
                                 (prevScene ?: Scenes.Gone) to
diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/model/Scene.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/model/Scene.kt
index 939d5bc..c7190c3 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/shared/model/Scene.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/shared/model/Scene.kt
@@ -19,7 +19,8 @@
 import com.android.compose.animation.scene.SceneKey
 import com.android.compose.animation.scene.UserAction
 import com.android.compose.animation.scene.UserActionResult
-import kotlinx.coroutines.flow.StateFlow
+import com.android.systemui.activatable.Activatable
+import kotlinx.coroutines.flow.Flow
 
 /**
  * Defines interface for classes that can describe a "scene".
@@ -29,11 +30,13 @@
  * based on either user action (for example, swiping down while on the lock screen scene may switch
  * to the shade scene).
  */
-interface Scene {
+interface Scene : Activatable {
 
     /** Uniquely-identifying key for this scene. The key must be unique within its container. */
     val key: SceneKey
 
+    override suspend fun activate() = Unit
+
     /**
      * The mapping between [UserAction] and destination [UserActionResult]s.
      *
@@ -54,5 +57,5 @@
      * type is not currently active in the scene and should be ignored by the framework, while the
      * current scene is this one.
      */
-    val destinationScenes: StateFlow<Map<UserAction, UserActionResult>>
+    val destinationScenes: Flow<Map<UserAction, UserActionResult>>
 }
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 c20d577..d31d6f4 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
@@ -103,6 +103,7 @@
                                 windowInsets = windowInsets,
                                 sceneByKey = sortedSceneByKey,
                                 dataSourceDelegator = dataSourceDelegator,
+                                containerConfig = containerConfig,
                             )
                             .also { it.id = R.id.scene_container_root_composable }
                     )
@@ -141,6 +142,7 @@
         windowInsets: StateFlow<WindowInsets?>,
         sceneByKey: Map<SceneKey, Scene>,
         dataSourceDelegator: SceneDataSourceDelegator,
+        containerConfig: SceneContainerConfig,
     ): View {
         return ComposeView(context).apply {
             setContent {
@@ -153,6 +155,7 @@
                             viewModel = viewModel,
                             sceneByKey =
                                 sceneByKey.mapValues { (_, scene) -> scene as ComposableScene },
+                            initialSceneKey = containerConfig.initialSceneKey,
                             dataSourceDelegator = dataSourceDelegator,
                         )
                     }
diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/GoneSceneViewModel.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/GoneSceneViewModel.kt
index 9f48ee9..b739ffe 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/GoneSceneViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/GoneSceneViewModel.kt
@@ -23,79 +23,61 @@
 import com.android.compose.animation.scene.UserAction
 import com.android.compose.animation.scene.UserActionResult
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.scene.shared.model.SceneFamilies
 import com.android.systemui.scene.shared.model.TransitionKeys.OpenBottomShade
 import com.android.systemui.scene.shared.model.TransitionKeys.ToSplitShade
 import com.android.systemui.shade.domain.interactor.ShadeInteractor
 import com.android.systemui.shade.shared.model.ShadeMode
 import javax.inject.Inject
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.flow.SharingStarted
-import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.stateIn
 
 @SysUISingleton
 class GoneSceneViewModel
 @Inject
 constructor(
-    @Application private val applicationScope: CoroutineScope,
     private val shadeInteractor: ShadeInteractor,
 ) {
-    val destinationScenes: StateFlow<Map<UserAction, UserActionResult>> =
-        shadeInteractor.shadeMode
-            .map(::destinationScenes)
-            .stateIn(
-                scope = applicationScope,
-                started = SharingStarted.WhileSubscribed(),
-                initialValue =
-                    destinationScenes(
-                        shadeMode = shadeInteractor.shadeMode.value,
-                    )
-            )
+    val destinationScenes: Flow<Map<UserAction, UserActionResult>> =
+        shadeInteractor.shadeMode.map { shadeMode ->
+            buildMap {
+                if (
+                    shadeMode is ShadeMode.Single ||
+                        // TODO(b/338577208): Remove this once we add Dual Shade invocation zones.
+                        shadeMode is ShadeMode.Dual
+                ) {
+                    if (shadeInteractor.shadeAlignment == Alignment.BottomEnd) {
+                        put(
+                            Swipe(
+                                pointerCount = 2,
+                                fromSource = Edge.Bottom,
+                                direction = SwipeDirection.Up,
+                            ),
+                            UserActionResult(SceneFamilies.QuickSettings, OpenBottomShade)
+                        )
+                    } else {
+                        put(
+                            Swipe(
+                                pointerCount = 2,
+                                fromSource = Edge.Top,
+                                direction = SwipeDirection.Down,
+                            ),
+                            UserActionResult(SceneFamilies.QuickSettings)
+                        )
+                    }
+                }
 
-    private fun destinationScenes(
-        shadeMode: ShadeMode,
-    ): Map<UserAction, UserActionResult> {
-        return buildMap {
-            if (
-                shadeMode is ShadeMode.Single ||
-                    // TODO(b/338577208): Remove this once we add Dual Shade invocation zones.
-                    shadeMode is ShadeMode.Dual
-            ) {
                 if (shadeInteractor.shadeAlignment == Alignment.BottomEnd) {
-                    put(
-                        Swipe(
-                            pointerCount = 2,
-                            fromSource = Edge.Bottom,
-                            direction = SwipeDirection.Up,
-                        ),
-                        UserActionResult(SceneFamilies.QuickSettings, OpenBottomShade)
-                    )
+                    put(Swipe.Up, UserActionResult(SceneFamilies.NotifShade, OpenBottomShade))
                 } else {
                     put(
-                        Swipe(
-                            pointerCount = 2,
-                            fromSource = Edge.Top,
-                            direction = SwipeDirection.Down,
-                        ),
-                        UserActionResult(SceneFamilies.QuickSettings)
+                        Swipe.Down,
+                        UserActionResult(
+                            SceneFamilies.NotifShade,
+                            ToSplitShade.takeIf { shadeMode is ShadeMode.Split }
+                        )
                     )
                 }
             }
-
-            if (shadeInteractor.shadeAlignment == Alignment.BottomEnd) {
-                put(Swipe.Up, UserActionResult(SceneFamilies.NotifShade, OpenBottomShade))
-            } else {
-                put(
-                    Swipe.Down,
-                    UserActionResult(
-                        SceneFamilies.NotifShade,
-                        ToSplitShade.takeIf { shadeMode is ShadeMode.Split }
-                    )
-                )
-            }
         }
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java
index 46c5861..a8a78a9 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java
@@ -171,11 +171,8 @@
         mMediaProjectionMetricsLogger.notifyProjectionInitiated(
                 getHostUid(), SessionCreationSource.SYSTEM_UI_SCREEN_RECORDER);
 
-        return (flags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING)
-                ? mScreenRecordPermissionDialogDelegateFactory
-                    .create(this, getHostUserHandle(), getHostUid(), onStartRecordingClicked)
-                : mScreenRecordDialogFactory
-                    .create(this, onStartRecordingClicked))
+        return mScreenRecordPermissionDialogDelegateFactory
+                .create(this, getHostUserHandle(), getHostUid(), onStartRecordingClicked)
                 .createDialog();
     }
 
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/PartitionedGridLayoutKosmos.kt b/packages/SystemUI/src/com/android/systemui/screenshot/InteractiveScreenshotHandler.kt
similarity index 62%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/PartitionedGridLayoutKosmos.kt
copy to packages/SystemUI/src/com/android/systemui/screenshot/InteractiveScreenshotHandler.kt
index 37c9552..26405f0 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/PartitionedGridLayoutKosmos.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/InteractiveScreenshotHandler.kt
@@ -14,11 +14,20 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.panels.domain.interactor
+package com.android.systemui.screenshot
 
-import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.qs.panels.ui.compose.PartitionedGridLayout
-import com.android.systemui.qs.panels.ui.viewmodel.partitionedGridViewModel
+import android.view.Display
 
-val Kosmos.partitionedGridLayout by
-    Kosmos.Fixture { PartitionedGridLayout(partitionedGridViewModel) }
+interface InteractiveScreenshotHandler : ScreenshotHandler {
+    fun isPendingSharedTransition(): Boolean
+
+    fun requestDismissal(event: ScreenshotEvent)
+
+    fun removeWindow()
+
+    fun onDestroy()
+
+    interface Factory {
+        fun create(display: Display): InteractiveScreenshotHandler
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/LegacyScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/LegacyScreenshotController.java
new file mode 100644
index 0000000..a77375c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/LegacyScreenshotController.java
@@ -0,0 +1,829 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.screenshot;
+
+import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
+import static android.view.WindowManager.LayoutParams.TYPE_SCREENSHOT;
+
+import static com.android.systemui.Flags.screenshotSaveImageExporter;
+import static com.android.systemui.screenshot.LogConfig.DEBUG_ANIM;
+import static com.android.systemui.screenshot.LogConfig.DEBUG_CALLBACK;
+import static com.android.systemui.screenshot.LogConfig.DEBUG_INPUT;
+import static com.android.systemui.screenshot.LogConfig.DEBUG_UI;
+import static com.android.systemui.screenshot.LogConfig.DEBUG_WINDOW;
+import static com.android.systemui.screenshot.LogConfig.logTag;
+import static com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_DISMISSED_OTHER;
+import static com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_INTERACTION_TIMEOUT;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.annotation.MainThread;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ActivityInfo;
+import android.content.res.Configuration;
+import android.graphics.Bitmap;
+import android.graphics.Insets;
+import android.graphics.Rect;
+import android.net.Uri;
+import android.os.Process;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.provider.Settings;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.view.Display;
+import android.view.ScrollCaptureResponse;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewRootImpl;
+import android.view.ViewTreeObserver;
+import android.view.WindowInsets;
+import android.view.WindowManager;
+import android.widget.Toast;
+import android.window.WindowContext;
+
+import com.android.internal.logging.UiEventLogger;
+import com.android.internal.policy.PhoneWindow;
+import com.android.settingslib.applications.InterestingConfigChanges;
+import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.broadcast.BroadcastSender;
+import com.android.systemui.clipboardoverlay.ClipboardOverlayController;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.res.R;
+import com.android.systemui.screenshot.TakeScreenshotService.RequestCallback;
+import com.android.systemui.screenshot.scroll.ScrollCaptureExecutor;
+import com.android.systemui.util.Assert;
+
+import com.google.common.util.concurrent.ListenableFuture;
+
+import dagger.assisted.Assisted;
+import dagger.assisted.AssistedFactory;
+import dagger.assisted.AssistedInject;
+
+import kotlin.Unit;
+
+import java.util.UUID;
+import java.util.concurrent.Executor;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.function.Consumer;
+
+import javax.inject.Provider;
+
+/**
+ * Controls the state and flow for screenshots.
+ */
+public class LegacyScreenshotController implements InteractiveScreenshotHandler {
+    private static final String TAG = logTag(LegacyScreenshotController.class);
+
+    // From WizardManagerHelper.java
+    private static final String SETTINGS_SECURE_USER_SETUP_COMPLETE = "user_setup_complete";
+
+    static final int SCREENSHOT_CORNER_DEFAULT_TIMEOUT_MILLIS = 6000;
+
+    private final WindowContext mContext;
+    private final FeatureFlags mFlags;
+    private final ScreenshotShelfViewProxy mViewProxy;
+    private final ScreenshotNotificationsController mNotificationsController;
+    private final ScreenshotSmartActions mScreenshotSmartActions;
+    private final UiEventLogger mUiEventLogger;
+    private final ImageExporter mImageExporter;
+    private final ImageCapture mImageCapture;
+    private final Executor mMainExecutor;
+    private final ExecutorService mBgExecutor;
+    private final BroadcastSender mBroadcastSender;
+    private final BroadcastDispatcher mBroadcastDispatcher;
+    private final ScreenshotActionsController mActionsController;
+
+    private final WindowManager mWindowManager;
+    private final WindowManager.LayoutParams mWindowLayoutParams;
+    @Nullable
+    private final ScreenshotSoundController mScreenshotSoundController;
+    private final PhoneWindow mWindow;
+    private final Display mDisplay;
+    private final ScrollCaptureExecutor mScrollCaptureExecutor;
+    private final ScreenshotNotificationSmartActionsProvider
+            mScreenshotNotificationSmartActionsProvider;
+    private final TimeoutHandler mScreenshotHandler;
+    private final UserManager mUserManager;
+    private final AssistContentRequester mAssistContentRequester;
+    private final ActionExecutor mActionExecutor;
+
+
+    private final MessageContainerController mMessageContainerController;
+    private final AnnouncementResolver mAnnouncementResolver;
+    private Bitmap mScreenBitmap;
+    private SaveImageInBackgroundTask mSaveInBgTask;
+    private boolean mScreenshotTakenInPortrait;
+    private boolean mAttachRequested;
+    private boolean mDetachRequested;
+    private Animator mScreenshotAnimation;
+    private RequestCallback mCurrentRequestCallback;
+    private String mPackageName = "";
+    private final BroadcastReceiver mCopyBroadcastReceiver;
+
+    /** Tracks config changes that require re-creating UI */
+    private final InterestingConfigChanges mConfigChanges = new InterestingConfigChanges(
+            ActivityInfo.CONFIG_ORIENTATION
+                    | ActivityInfo.CONFIG_LAYOUT_DIRECTION
+                    | ActivityInfo.CONFIG_LOCALE
+                    | ActivityInfo.CONFIG_UI_MODE
+                    | ActivityInfo.CONFIG_SCREEN_LAYOUT
+                    | ActivityInfo.CONFIG_ASSETS_PATHS);
+
+
+    @AssistedInject
+    LegacyScreenshotController(
+            Context context,
+            WindowManager windowManager,
+            FeatureFlags flags,
+            ScreenshotShelfViewProxy.Factory viewProxyFactory,
+            ScreenshotSmartActions screenshotSmartActions,
+            ScreenshotNotificationsController.Factory screenshotNotificationsControllerFactory,
+            UiEventLogger uiEventLogger,
+            ImageExporter imageExporter,
+            ImageCapture imageCapture,
+            @Main Executor mainExecutor,
+            ScrollCaptureExecutor scrollCaptureExecutor,
+            TimeoutHandler timeoutHandler,
+            BroadcastSender broadcastSender,
+            BroadcastDispatcher broadcastDispatcher,
+            ScreenshotNotificationSmartActionsProvider screenshotNotificationSmartActionsProvider,
+            ScreenshotActionsController.Factory screenshotActionsControllerFactory,
+            ActionExecutor.Factory actionExecutorFactory,
+            UserManager userManager,
+            AssistContentRequester assistContentRequester,
+            MessageContainerController messageContainerController,
+            Provider<ScreenshotSoundController> screenshotSoundController,
+            AnnouncementResolver announcementResolver,
+            @Assisted Display display
+    ) {
+        mScreenshotSmartActions = screenshotSmartActions;
+        mNotificationsController = screenshotNotificationsControllerFactory.create(
+                display.getDisplayId());
+        mUiEventLogger = uiEventLogger;
+        mImageExporter = imageExporter;
+        mImageCapture = imageCapture;
+        mMainExecutor = mainExecutor;
+        mScrollCaptureExecutor = scrollCaptureExecutor;
+        mScreenshotNotificationSmartActionsProvider = screenshotNotificationSmartActionsProvider;
+        mBgExecutor = Executors.newSingleThreadExecutor();
+        mBroadcastSender = broadcastSender;
+        mBroadcastDispatcher = broadcastDispatcher;
+
+        mScreenshotHandler = timeoutHandler;
+        mScreenshotHandler.setDefaultTimeoutMillis(SCREENSHOT_CORNER_DEFAULT_TIMEOUT_MILLIS);
+
+        mDisplay = display;
+        mWindowManager = windowManager;
+        final Context displayContext = context.createDisplayContext(display);
+        mContext = (WindowContext) displayContext.createWindowContext(TYPE_SCREENSHOT, null);
+        mFlags = flags;
+        mUserManager = userManager;
+        mMessageContainerController = messageContainerController;
+        mAssistContentRequester = assistContentRequester;
+        mAnnouncementResolver = announcementResolver;
+
+        mViewProxy = viewProxyFactory.getProxy(mContext, mDisplay.getDisplayId());
+
+        mScreenshotHandler.setOnTimeoutRunnable(() -> {
+            if (DEBUG_UI) {
+                Log.d(TAG, "Corner timeout hit");
+            }
+            mViewProxy.requestDismissal(SCREENSHOT_INTERACTION_TIMEOUT);
+        });
+
+        // Setup the window that we are going to use
+        mWindowLayoutParams = FloatingWindowUtil.getFloatingWindowParams();
+        mWindowLayoutParams.setTitle("ScreenshotAnimation");
+
+        mWindow = FloatingWindowUtil.getFloatingWindow(mContext);
+        mWindow.setWindowManager(mWindowManager, null, null);
+
+        mConfigChanges.applyNewConfig(context.getResources());
+        reloadAssets();
+
+        mActionExecutor = actionExecutorFactory.create(mWindow, mViewProxy,
+                () -> {
+                    finishDismiss();
+                    return Unit.INSTANCE;
+                });
+        mActionsController = screenshotActionsControllerFactory.getController(mActionExecutor);
+
+
+        // Sound is only reproduced from the controller of the default display.
+        if (mDisplay.getDisplayId() == Display.DEFAULT_DISPLAY) {
+            mScreenshotSoundController = screenshotSoundController.get();
+        } else {
+            mScreenshotSoundController = null;
+        }
+
+        mCopyBroadcastReceiver = new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                if (ClipboardOverlayController.COPY_OVERLAY_ACTION.equals(intent.getAction())) {
+                    mViewProxy.requestDismissal(SCREENSHOT_DISMISSED_OTHER);
+                }
+            }
+        };
+        mBroadcastDispatcher.registerReceiver(mCopyBroadcastReceiver, new IntentFilter(
+                        ClipboardOverlayController.COPY_OVERLAY_ACTION), null, null,
+                Context.RECEIVER_NOT_EXPORTED, ClipboardOverlayController.SELF_PERMISSION);
+    }
+
+    @Override
+    public void handleScreenshot(ScreenshotData screenshot, Consumer<Uri> finisher,
+            RequestCallback requestCallback) {
+        Assert.isMainThread();
+
+        mCurrentRequestCallback = requestCallback;
+        if (screenshot.getType() == WindowManager.TAKE_SCREENSHOT_FULLSCREEN
+                && screenshot.getBitmap() == null) {
+            Rect bounds = getFullScreenRect();
+            screenshot.setBitmap(mImageCapture.captureDisplay(mDisplay.getDisplayId(), bounds));
+            screenshot.setScreenBounds(bounds);
+        }
+
+        if (screenshot.getBitmap() == null) {
+            Log.e(TAG, "handleScreenshot: Screenshot bitmap was null");
+            mNotificationsController.notifyScreenshotError(
+                    R.string.screenshot_failed_to_capture_text);
+            if (mCurrentRequestCallback != null) {
+                mCurrentRequestCallback.reportError();
+            }
+            return;
+        }
+
+        mScreenBitmap = screenshot.getBitmap();
+        String oldPackageName = mPackageName;
+        mPackageName = screenshot.getPackageNameString();
+
+        if (!isUserSetupComplete(Process.myUserHandle())) {
+            Log.w(TAG, "User setup not complete, displaying toast only");
+            // User setup isn't complete, so we don't want to show any UI beyond a toast, as editing
+            // and sharing shouldn't be exposed to the user.
+            saveScreenshotAndToast(screenshot, finisher);
+            return;
+        }
+
+        mBroadcastSender.sendBroadcast(new Intent(ClipboardOverlayController.SCREENSHOT_ACTION),
+                ClipboardOverlayController.SELF_PERMISSION);
+
+        mScreenshotTakenInPortrait =
+                mContext.getResources().getConfiguration().orientation == ORIENTATION_PORTRAIT;
+
+        // Optimizations
+        mScreenBitmap.setHasAlpha(false);
+        mScreenBitmap.prepareToDraw();
+
+        prepareViewForNewScreenshot(screenshot, oldPackageName);
+
+        final UUID requestId;
+        requestId = mActionsController.setCurrentScreenshot(screenshot);
+        saveScreenshotInBackground(screenshot, requestId, finisher, result -> {
+            if (result.uri != null) {
+                ScreenshotSavedResult savedScreenshot = new ScreenshotSavedResult(
+                        result.uri, screenshot.getUserOrDefault(), result.timestamp);
+                mActionsController.setCompletedScreenshot(requestId, savedScreenshot);
+            }
+        });
+
+        if (screenshot.getTaskId() >= 0) {
+            mAssistContentRequester.requestAssistContent(
+                    screenshot.getTaskId(),
+                    assistContent ->
+                            mActionsController.onAssistContent(requestId, assistContent));
+        } else {
+            mActionsController.onAssistContent(requestId, null);
+        }
+
+        // The window is focusable by default
+        setWindowFocusable(true);
+        mViewProxy.requestFocus();
+
+        enqueueScrollCaptureRequest(requestId, screenshot.getUserHandle());
+
+        attachWindow();
+
+        boolean showFlash;
+        if (screenshot.getType() == WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE) {
+            if (screenshot.getScreenBounds() != null
+                    && aspectRatiosMatch(screenshot.getBitmap(), screenshot.getInsets(),
+                    screenshot.getScreenBounds())) {
+                showFlash = false;
+            } else {
+                showFlash = true;
+                screenshot.setInsets(Insets.NONE);
+                screenshot.setScreenBounds(new Rect(0, 0, screenshot.getBitmap().getWidth(),
+                        screenshot.getBitmap().getHeight()));
+            }
+        } else {
+            showFlash = true;
+        }
+
+        mViewProxy.prepareEntranceAnimation(
+                () -> startAnimation(screenshot.getScreenBounds(), showFlash,
+                        () -> mMessageContainerController.onScreenshotTaken(screenshot)));
+
+        mViewProxy.setScreenshot(screenshot);
+
+        // ignore system bar insets for the purpose of window layout
+        mWindow.getDecorView().setOnApplyWindowInsetsListener(
+                (v, insets) -> WindowInsets.CONSUMED);
+    }
+
+    void prepareViewForNewScreenshot(@NonNull ScreenshotData screenshot, String oldPackageName) {
+        withWindowAttached(() -> {
+            mAnnouncementResolver.getScreenshotAnnouncement(
+                    screenshot.getUserHandle().getIdentifier(),
+                    mViewProxy::announceForAccessibility);
+        });
+
+        mViewProxy.reset();
+
+        if (mViewProxy.isAttachedToWindow()) {
+            // if we didn't already dismiss for another reason
+            if (!mViewProxy.isDismissing()) {
+                mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_REENTERED, 0,
+                        oldPackageName);
+            }
+            if (DEBUG_WINDOW) {
+                Log.d(TAG, "saveScreenshot: screenshotView is already attached, resetting. "
+                        + "(dismissing=" + mViewProxy.isDismissing() + ")");
+            }
+        }
+
+        mViewProxy.setPackageName(mPackageName);
+    }
+
+    /**
+     * Requests the view to dismiss the current screenshot (may be ignored, if screenshot is already
+     * being dismissed)
+     */
+    @Override
+    public void requestDismissal(ScreenshotEvent event) {
+        mViewProxy.requestDismissal(event);
+    }
+
+    @Override
+    public boolean isPendingSharedTransition() {
+        return mActionExecutor.isPendingSharedTransition();
+    }
+
+    // Any cleanup needed when the service is being destroyed.
+    @Override
+    public void onDestroy() {
+        if (mSaveInBgTask != null) {
+            // just log success/failure for the pre-existing screenshot
+            mSaveInBgTask.setActionsReadyListener(this::logSuccessOnActionsReady);
+        }
+        removeWindow();
+        releaseMediaPlayer();
+        releaseContext();
+        mBgExecutor.shutdown();
+    }
+
+    /**
+     * Release the constructed window context.
+     */
+    private void releaseContext() {
+        mBroadcastDispatcher.unregisterReceiver(mCopyBroadcastReceiver);
+        mContext.release();
+    }
+
+    private void releaseMediaPlayer() {
+        if (mScreenshotSoundController == null) return;
+        mScreenshotSoundController.releaseScreenshotSoundAsync();
+    }
+
+    /**
+     * Update resources on configuration change. Reinflate for theme/color changes.
+     */
+    private void reloadAssets() {
+        if (DEBUG_UI) {
+            Log.d(TAG, "reloadAssets()");
+        }
+
+        mMessageContainerController.setView(mViewProxy.getView());
+        mViewProxy.setCallbacks(new ScreenshotShelfViewProxy.ScreenshotViewCallback() {
+            @Override
+            public void onUserInteraction() {
+                if (DEBUG_INPUT) {
+                    Log.d(TAG, "onUserInteraction");
+                }
+                mScreenshotHandler.resetTimeout();
+            }
+
+            @Override
+            public void onDismiss() {
+                finishDismiss();
+            }
+
+            @Override
+            public void onTouchOutside() {
+                // TODO(159460485): Remove this when focus is handled properly in the system
+                setWindowFocusable(false);
+            }
+        });
+
+        if (DEBUG_WINDOW) {
+            Log.d(TAG, "setContentView: " + mViewProxy.getView());
+        }
+        mWindow.setContentView(mViewProxy.getView());
+    }
+
+    private void enqueueScrollCaptureRequest(UUID requestId, UserHandle owner) {
+        // Wait until this window is attached to request because it is
+        // the reference used to locate the target window (below).
+        withWindowAttached(() -> {
+            requestScrollCapture(requestId, owner);
+            mWindow.peekDecorView().getViewRootImpl().setActivityConfigCallback(
+                    new ViewRootImpl.ActivityConfigCallback() {
+                        @Override
+                        public void onConfigurationChanged(Configuration overrideConfig,
+                                int newDisplayId) {
+                            if (mConfigChanges.applyNewConfig(mContext.getResources())) {
+                                // Hide the scroll chip until we know it's available in this
+                                // orientation
+                                mActionsController.onScrollChipInvalidated();
+                                // Delay scroll capture eval a bit to allow the underlying activity
+                                // to set up in the new orientation.
+                                mScreenshotHandler.postDelayed(
+                                        () -> requestScrollCapture(requestId, owner), 150);
+                                mViewProxy.updateInsets(
+                                        mWindowManager.getCurrentWindowMetrics().getWindowInsets());
+                                // Screenshot animation calculations won't be valid anymore,
+                                // so just end
+                                if (mScreenshotAnimation != null
+                                        && mScreenshotAnimation.isRunning()) {
+                                    mScreenshotAnimation.end();
+                                }
+                            }
+                        }
+                    });
+        });
+    }
+
+    private void requestScrollCapture(UUID requestId, UserHandle owner) {
+        mScrollCaptureExecutor.requestScrollCapture(
+                mDisplay.getDisplayId(),
+                mWindow.getDecorView().getWindowToken(),
+                (response) -> {
+                    mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_LONG_SCREENSHOT_IMPRESSION,
+                            0, response.getPackageName());
+                    mActionsController.onScrollChipReady(requestId,
+                            () -> onScrollButtonClicked(owner, response));
+                    return Unit.INSTANCE;
+                }
+        );
+    }
+
+    private void onScrollButtonClicked(UserHandle owner, ScrollCaptureResponse response) {
+        if (DEBUG_INPUT) {
+            Log.d(TAG, "scroll chip tapped");
+        }
+        mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_LONG_SCREENSHOT_REQUESTED, 0,
+                response.getPackageName());
+        Bitmap newScreenshot = mImageCapture.captureDisplay(mDisplay.getDisplayId(),
+                getFullScreenRect());
+        if (newScreenshot == null) {
+            Log.e(TAG, "Failed to capture current screenshot for scroll transition!");
+            return;
+        }
+        // delay starting scroll capture to make sure scrim is up before the app moves
+        mViewProxy.prepareScrollingTransition(response, mScreenBitmap, newScreenshot,
+                mScreenshotTakenInPortrait, () -> executeBatchScrollCapture(response, owner));
+    }
+
+    private void executeBatchScrollCapture(ScrollCaptureResponse response, UserHandle owner) {
+        mScrollCaptureExecutor.executeBatchScrollCapture(response,
+                () -> {
+                    final Intent intent = ActionIntentCreator.INSTANCE.createLongScreenshotIntent(
+                            owner, mContext);
+                    mContext.startActivity(intent);
+                },
+                mViewProxy::restoreNonScrollingUi,
+                mViewProxy::startLongScreenshotTransition);
+    }
+
+    private void withWindowAttached(Runnable action) {
+        View decorView = mWindow.getDecorView();
+        if (decorView.isAttachedToWindow()) {
+            action.run();
+        } else {
+            decorView.getViewTreeObserver().addOnWindowAttachListener(
+                    new ViewTreeObserver.OnWindowAttachListener() {
+                        @Override
+                        public void onWindowAttached() {
+                            mAttachRequested = false;
+                            decorView.getViewTreeObserver().removeOnWindowAttachListener(this);
+                            action.run();
+                        }
+
+                        @Override
+                        public void onWindowDetached() {
+                        }
+                    });
+
+        }
+    }
+
+    @MainThread
+    private void attachWindow() {
+        View decorView = mWindow.getDecorView();
+        if (decorView.isAttachedToWindow() || mAttachRequested) {
+            return;
+        }
+        if (DEBUG_WINDOW) {
+            Log.d(TAG, "attachWindow");
+        }
+        mAttachRequested = true;
+        mWindowManager.addView(decorView, mWindowLayoutParams);
+        decorView.requestApplyInsets();
+
+        ViewGroup layout = decorView.requireViewById(android.R.id.content);
+        layout.setClipChildren(false);
+        layout.setClipToPadding(false);
+    }
+
+    @Override
+    public void removeWindow() {
+        final View decorView = mWindow.peekDecorView();
+        if (decorView != null && decorView.isAttachedToWindow()) {
+            if (DEBUG_WINDOW) {
+                Log.d(TAG, "Removing screenshot window");
+            }
+            mWindowManager.removeViewImmediate(decorView);
+            mDetachRequested = false;
+        }
+        if (mAttachRequested && !mDetachRequested) {
+            mDetachRequested = true;
+            withWindowAttached(this::removeWindow);
+        }
+
+        mViewProxy.stopInputListening();
+    }
+
+    private void playCameraSoundIfNeeded() {
+        if (mScreenshotSoundController == null) return;
+        // the controller is not-null only on the default display controller
+        mScreenshotSoundController.playScreenshotSoundAsync();
+    }
+
+    /**
+     * Save the bitmap but don't show the normal screenshot UI.. just a toast (or notification on
+     * failure).
+     */
+    private void saveScreenshotAndToast(ScreenshotData screenshot, Consumer<Uri> finisher) {
+        // Play the shutter sound to notify that we've taken a screenshot
+        playCameraSoundIfNeeded();
+
+        if (screenshotSaveImageExporter()) {
+            saveScreenshotInBackground(screenshot, UUID.randomUUID(), finisher, result -> {
+                if (result.uri != null) {
+                    mScreenshotHandler.post(() -> Toast.makeText(mContext,
+                            R.string.screenshot_saved_title, Toast.LENGTH_SHORT).show());
+                }
+            });
+        } else {
+            saveScreenshotInWorkerThread(
+                    screenshot.getUserHandle(),
+                    /* onComplete */ finisher,
+                    /* actionsReadyListener */ imageData -> {
+                        if (DEBUG_CALLBACK) {
+                            Log.d(TAG,
+                                    "returning URI to finisher (Consumer<URI>): " + imageData.uri);
+                        }
+                        finisher.accept(imageData.uri);
+                        if (imageData.uri == null) {
+                            mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_NOT_SAVED, 0,
+                                    mPackageName);
+                            mNotificationsController.notifyScreenshotError(
+                                    R.string.screenshot_failed_to_save_text);
+                        } else {
+                            mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SAVED, 0, mPackageName);
+                            mScreenshotHandler.post(() -> Toast.makeText(mContext,
+                                    R.string.screenshot_saved_title, Toast.LENGTH_SHORT).show());
+                        }
+                    },
+                    null);
+        }
+    }
+
+    /**
+     * Starts the animation after taking the screenshot
+     */
+    private void startAnimation(Rect screenRect, boolean showFlash, Runnable onAnimationComplete) {
+        if (mScreenshotAnimation != null && mScreenshotAnimation.isRunning()) {
+            mScreenshotAnimation.cancel();
+        }
+
+        mScreenshotAnimation =
+                mViewProxy.createScreenshotDropInAnimation(screenRect, showFlash);
+        if (onAnimationComplete != null) {
+            mScreenshotAnimation.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    super.onAnimationEnd(animation);
+                    onAnimationComplete.run();
+                }
+            });
+        }
+
+        // Play the shutter sound to notify that we've taken a screenshot
+        playCameraSoundIfNeeded();
+
+        if (DEBUG_ANIM) {
+            Log.d(TAG, "starting post-screenshot animation");
+        }
+        mScreenshotAnimation.start();
+    }
+
+    /** Reset screenshot view and then call onCompleteRunnable */
+    private void finishDismiss() {
+        Log.d(TAG, "finishDismiss");
+        mActionsController.endScreenshotSession();
+        mScrollCaptureExecutor.close();
+        if (mCurrentRequestCallback != null) {
+            mCurrentRequestCallback.onFinish();
+            mCurrentRequestCallback = null;
+        }
+        mViewProxy.reset();
+        removeWindow();
+        mScreenshotHandler.cancelTimeout();
+    }
+
+    private void saveScreenshotInBackground(ScreenshotData screenshot, UUID requestId,
+            Consumer<Uri> finisher, Consumer<ImageExporter.Result> onResult) {
+        ListenableFuture<ImageExporter.Result> future = mImageExporter.export(mBgExecutor,
+                requestId, screenshot.getBitmap(), screenshot.getUserOrDefault(),
+                mDisplay.getDisplayId());
+        future.addListener(() -> {
+            try {
+                ImageExporter.Result result = future.get();
+                Log.d(TAG, "Saved screenshot: " + result);
+                logScreenshotResultStatus(result.uri, screenshot.getUserHandle());
+                onResult.accept(result);
+                if (DEBUG_CALLBACK) {
+                    Log.d(TAG, "finished background processing, Calling (Consumer<Uri>) "
+                            + "finisher.accept(\"" + result.uri + "\"");
+                }
+                finisher.accept(result.uri);
+            } catch (Exception e) {
+                Log.d(TAG, "Failed to store screenshot", e);
+                if (DEBUG_CALLBACK) {
+                    Log.d(TAG, "Calling (Consumer<Uri>) finisher.accept(null)");
+                }
+                finisher.accept(null);
+            }
+        }, mMainExecutor);
+    }
+
+    /**
+     * Creates a new worker thread and saves the screenshot to the media store.
+     */
+    private void saveScreenshotInWorkerThread(
+            UserHandle owner,
+            @NonNull Consumer<Uri> finisher,
+            @Nullable SaveImageInBackgroundTask.ActionsReadyListener actionsReadyListener,
+            @Nullable SaveImageInBackgroundTask.QuickShareActionReadyListener
+                    quickShareActionsReadyListener) {
+        SaveImageInBackgroundTask.SaveImageInBackgroundData
+                data = new SaveImageInBackgroundTask.SaveImageInBackgroundData();
+        data.image = mScreenBitmap;
+        data.finisher = finisher;
+        data.mActionsReadyListener = actionsReadyListener;
+        data.mQuickShareActionsReadyListener = quickShareActionsReadyListener;
+        data.owner = owner;
+        data.displayId = mDisplay.getDisplayId();
+
+        if (mSaveInBgTask != null) {
+            // just log success/failure for the pre-existing screenshot
+            mSaveInBgTask.setActionsReadyListener(this::logSuccessOnActionsReady);
+        }
+
+        mSaveInBgTask = new SaveImageInBackgroundTask(mContext, mFlags, mImageExporter,
+                mScreenshotSmartActions, data,
+                mScreenshotNotificationSmartActionsProvider);
+        mSaveInBgTask.execute();
+    }
+
+    /**
+     * Logs success/failure of the screenshot saving task, and shows an error if it failed.
+     */
+    private void logScreenshotResultStatus(Uri uri, UserHandle owner) {
+        if (uri == null) {
+            mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_NOT_SAVED, 0, mPackageName);
+            mNotificationsController.notifyScreenshotError(
+                    R.string.screenshot_failed_to_save_text);
+        } else {
+            mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SAVED, 0, mPackageName);
+            if (mUserManager.isManagedProfile(owner.getIdentifier())) {
+                mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SAVED_TO_WORK_PROFILE, 0,
+                        mPackageName);
+            }
+        }
+    }
+
+    /**
+     * Logs success/failure of the screenshot saving task, and shows an error if it failed.
+     */
+    private void logSuccessOnActionsReady(SaveImageInBackgroundTask.SavedImageData imageData) {
+        logScreenshotResultStatus(imageData.uri, imageData.owner);
+    }
+
+    private boolean isUserSetupComplete(UserHandle owner) {
+        return Settings.Secure.getInt(mContext.createContextAsUser(owner, 0)
+                .getContentResolver(), SETTINGS_SECURE_USER_SETUP_COMPLETE, 0) == 1;
+    }
+
+    /**
+     * Updates the window focusability.  If the window is already showing, then it updates the
+     * window immediately, otherwise the layout params will be applied when the window is next
+     * shown.
+     */
+    private void setWindowFocusable(boolean focusable) {
+        if (DEBUG_WINDOW) {
+            Log.d(TAG, "setWindowFocusable: " + focusable);
+        }
+        int flags = mWindowLayoutParams.flags;
+        if (focusable) {
+            mWindowLayoutParams.flags &= ~WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+        } else {
+            mWindowLayoutParams.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+        }
+        if (mWindowLayoutParams.flags == flags) {
+            if (DEBUG_WINDOW) {
+                Log.d(TAG, "setWindowFocusable: skipping, already " + focusable);
+            }
+            return;
+        }
+        final View decorView = mWindow.peekDecorView();
+        if (decorView != null && decorView.isAttachedToWindow()) {
+            mWindowManager.updateViewLayout(decorView, mWindowLayoutParams);
+        }
+    }
+
+    private Rect getFullScreenRect() {
+        DisplayMetrics displayMetrics = new DisplayMetrics();
+        mDisplay.getRealMetrics(displayMetrics);
+        return new Rect(0, 0, displayMetrics.widthPixels, displayMetrics.heightPixels);
+    }
+
+    /** Does the aspect ratio of the bitmap with insets removed match the bounds. */
+    private static boolean aspectRatiosMatch(Bitmap bitmap, Insets bitmapInsets,
+            Rect screenBounds) {
+        int insettedWidth = bitmap.getWidth() - bitmapInsets.left - bitmapInsets.right;
+        int insettedHeight = bitmap.getHeight() - bitmapInsets.top - bitmapInsets.bottom;
+
+        if (insettedHeight == 0 || insettedWidth == 0 || bitmap.getWidth() == 0
+                || bitmap.getHeight() == 0) {
+            if (DEBUG_UI) {
+                Log.e(TAG, "Provided bitmap and insets create degenerate region: "
+                        + bitmap.getWidth() + "x" + bitmap.getHeight() + " " + bitmapInsets);
+            }
+            return false;
+        }
+
+        float insettedBitmapAspect = ((float) insettedWidth) / insettedHeight;
+        float boundsAspect = ((float) screenBounds.width()) / screenBounds.height();
+
+        boolean matchWithinTolerance = Math.abs(insettedBitmapAspect - boundsAspect) < 0.1f;
+        if (DEBUG_UI) {
+            Log.d(TAG, "aspectRatiosMatch: don't match bitmap: " + insettedBitmapAspect
+                    + ", bounds: " + boundsAspect);
+        }
+        return matchWithinTolerance;
+    }
+
+    /** Injectable factory to create screenshot controller instances for a specific display. */
+    @AssistedFactory
+    public interface Factory extends InteractiveScreenshotHandler.Factory {
+        /**
+         * Creates an instance of the controller for that specific display.
+         *
+         * @param display                 display to capture
+         */
+        LegacyScreenshotController create(Display display);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
index 4914409..9bc3bd8 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
@@ -49,6 +49,7 @@
 import java.util.Random;
 import java.util.UUID;
 import java.util.concurrent.CompletableFuture;
+import java.util.function.Consumer;
 
 /**
  * An AsyncTask that saves an image to the media store in the background.
@@ -59,12 +60,73 @@
     private static final String SCREENSHOT_ID_TEMPLATE = "Screenshot_%s";
     private static final String SCREENSHOT_SHARE_SUBJECT_TEMPLATE = "Screenshot (%s)";
 
+    /**
+     * POD used in the AsyncTask which saves an image in the background.
+     */
+    static class SaveImageInBackgroundData {
+        public Bitmap image;
+        public Consumer<Uri> finisher;
+        public ActionsReadyListener mActionsReadyListener;
+        public QuickShareActionReadyListener mQuickShareActionsReadyListener;
+        public UserHandle owner;
+        public int displayId;
+
+        void clearImage() {
+            image = null;
+        }
+    }
+
+    /**
+     * Structure returned by the SaveImageInBackgroundTask
+     */
+    public static class SavedImageData {
+        public Uri uri;
+        public List<Notification.Action> smartActions;
+        public Notification.Action quickShareAction;
+        public UserHandle owner;
+        public String subject;  // Title for sharing
+        public Long imageTime; // Time at which screenshot was saved
+
+        /**
+         * Used to reset the return data on error
+         */
+        public void reset() {
+            uri = null;
+            smartActions = null;
+            quickShareAction = null;
+            subject = null;
+            imageTime = null;
+        }
+    }
+
+    /**
+     * Structure returned by the QueryQuickShareInBackgroundTask
+     */
+    static class QuickShareData {
+        public Notification.Action quickShareAction;
+
+        /**
+         * Used to reset the return data on error
+         */
+        public void reset() {
+            quickShareAction = null;
+        }
+    }
+
+    interface ActionsReadyListener {
+        void onActionsReady(SavedImageData imageData);
+    }
+
+    interface QuickShareActionReadyListener {
+        void onActionsReady(QuickShareData quickShareData);
+    }
+
     private final Context mContext;
     private FeatureFlags mFlags;
     private final ScreenshotSmartActions mScreenshotSmartActions;
-    private final ScreenshotController.SaveImageInBackgroundData mParams;
-    private final ScreenshotController.SavedImageData mImageData;
-    private final ScreenshotController.QuickShareData mQuickShareData;
+    private final SaveImageInBackgroundData mParams;
+    private final SavedImageData mImageData;
+    private final QuickShareData mQuickShareData;
 
     private final ScreenshotNotificationSmartActionsProvider mSmartActionsProvider;
     private String mScreenshotId;
@@ -77,15 +139,15 @@
             FeatureFlags flags,
             ImageExporter exporter,
             ScreenshotSmartActions screenshotSmartActions,
-            ScreenshotController.SaveImageInBackgroundData data,
+            SaveImageInBackgroundData data,
             ScreenshotNotificationSmartActionsProvider
                     screenshotNotificationSmartActionsProvider
     ) {
         mContext = context;
         mFlags = flags;
         mScreenshotSmartActions = screenshotSmartActions;
-        mImageData = new ScreenshotController.SavedImageData();
-        mQuickShareData = new ScreenshotController.QuickShareData();
+        mImageData = new SavedImageData();
+        mQuickShareData = new QuickShareData();
         mImageExporter = exporter;
 
         // Prepare all the output metadata
@@ -195,7 +257,7 @@
      * Update the listener run when the saving task completes. Used to avoid showing UI for the
      * first screenshot when a second one is taken.
      */
-    void setActionsReadyListener(ScreenshotController.ActionsReadyListener listener) {
+    void setActionsReadyListener(ActionsReadyListener listener) {
         mParams.mActionsReadyListener = listener;
     }
 
@@ -224,7 +286,7 @@
                     ScreenshotNotificationSmartActionsProvider.ACTION_TYPE,
                     ScreenshotNotificationSmartActionsProvider.DEFAULT_ACTION_TYPE);
             Intent intent = new Intent(context, SmartActionsReceiver.class)
-                    .putExtra(ScreenshotController.EXTRA_ACTION_INTENT, action.actionIntent)
+                    .putExtra(SmartActionsReceiver.EXTRA_ACTION_INTENT, action.actionIntent)
                     .addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
             addIntentExtras(mScreenshotId, intent, actionType, true /* smartActionsEnabled */);
             PendingIntent broadcastIntent = PendingIntent.getBroadcast(context,
@@ -240,9 +302,9 @@
     private static void addIntentExtras(String screenshotId, Intent intent, String actionType,
             boolean smartActionsEnabled) {
         intent
-                .putExtra(ScreenshotController.EXTRA_ACTION_TYPE, actionType)
-                .putExtra(ScreenshotController.EXTRA_ID, screenshotId)
-                .putExtra(ScreenshotController.EXTRA_SMART_ACTIONS_ENABLED, smartActionsEnabled);
+                .putExtra(SmartActionsReceiver.EXTRA_ACTION_TYPE, actionType)
+                .putExtra(SmartActionsReceiver.EXTRA_ID, screenshotId)
+                .putExtra(SmartActionsReceiver.EXTRA_SMART_ACTIONS_ENABLED, smartActionsEnabled);
     }
 
     /**
@@ -265,8 +327,8 @@
         }
 
         Intent wrappedIntent = new Intent(mContext, SmartActionsReceiver.class)
-                .putExtra(ScreenshotController.EXTRA_ACTION_INTENT, quickShare.actionIntent)
-                .putExtra(ScreenshotController.EXTRA_ACTION_INTENT_FILLIN,
+                .putExtra(SmartActionsReceiver.EXTRA_ACTION_INTENT, quickShare.actionIntent)
+                .putExtra(SmartActionsReceiver.EXTRA_ACTION_INTENT_FILLIN,
                         createFillInIntent(uri, imageTime))
                 .addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
         Bundle extras = quickShare.getExtras();
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index 7739009..540d4c4 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2020 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.
@@ -19,7 +19,6 @@
 import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
 import static android.view.WindowManager.LayoutParams.TYPE_SCREENSHOT;
 
-import static com.android.systemui.Flags.screenshotPrivateProfileAccessibilityAnnouncementFix;
 import static com.android.systemui.Flags.screenshotSaveImageExporter;
 import static com.android.systemui.screenshot.LogConfig.DEBUG_ANIM;
 import static com.android.systemui.screenshot.LogConfig.DEBUG_CALLBACK;
@@ -35,10 +34,6 @@
 import android.annotation.MainThread;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.app.ActivityOptions;
-import android.app.ExitTransitionCoordinator;
-import android.app.ICompatCameraControlCallback;
-import android.app.Notification;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -55,7 +50,6 @@
 import android.provider.Settings;
 import android.util.DisplayMetrics;
 import android.util.Log;
-import android.util.Pair;
 import android.view.Display;
 import android.view.ScrollCaptureResponse;
 import android.view.View;
@@ -67,7 +61,6 @@
 import android.widget.Toast;
 import android.window.WindowContext;
 
-import com.android.internal.app.ChooserActivity;
 import com.android.internal.logging.UiEventLogger;
 import com.android.internal.policy.PhoneWindow;
 import com.android.settingslib.applications.InterestingConfigChanges;
@@ -89,7 +82,6 @@
 
 import kotlin.Unit;
 
-import java.util.List;
 import java.util.UUID;
 import java.util.concurrent.Executor;
 import java.util.concurrent.ExecutorService;
@@ -101,89 +93,9 @@
 /**
  * Controls the state and flow for screenshots.
  */
-public class ScreenshotController implements ScreenshotHandler {
+public class ScreenshotController implements InteractiveScreenshotHandler {
     private static final String TAG = logTag(ScreenshotController.class);
 
-    /**
-     * POD used in the AsyncTask which saves an image in the background.
-     */
-    static class SaveImageInBackgroundData {
-        public Bitmap image;
-        public Consumer<Uri> finisher;
-        public ScreenshotController.ActionsReadyListener mActionsReadyListener;
-        public ScreenshotController.QuickShareActionReadyListener mQuickShareActionsReadyListener;
-        public UserHandle owner;
-        public int displayId;
-
-        void clearImage() {
-            image = null;
-        }
-    }
-
-    /**
-     * Structure returned by the SaveImageInBackgroundTask
-     */
-    public static class SavedImageData {
-        public Uri uri;
-        public List<Notification.Action> smartActions;
-        public Notification.Action quickShareAction;
-        public UserHandle owner;
-        public String subject;  // Title for sharing
-        public Long imageTime; // Time at which screenshot was saved
-
-        /**
-         * Used to reset the return data on error
-         */
-        public void reset() {
-            uri = null;
-            smartActions = null;
-            quickShareAction = null;
-            subject = null;
-            imageTime = null;
-        }
-    }
-
-    /**
-     * Structure returned by the QueryQuickShareInBackgroundTask
-     */
-    static class QuickShareData {
-        public Notification.Action quickShareAction;
-
-        /**
-         * Used to reset the return data on error
-         */
-        public void reset() {
-            quickShareAction = null;
-        }
-    }
-
-    interface ActionsReadyListener {
-        void onActionsReady(ScreenshotController.SavedImageData imageData);
-    }
-
-    interface QuickShareActionReadyListener {
-        void onActionsReady(ScreenshotController.QuickShareData quickShareData);
-    }
-
-    public interface TransitionDestination {
-        /**
-         * Allows the long screenshot activity to call back with a destination location (the bounds
-         * on screen of the destination for the transitioning view) and a Runnable to be run once
-         * the transition animation is complete.
-         */
-        void setTransitionDestination(Rect transitionDestination, Runnable onTransitionEnd);
-    }
-
-    // These strings are used for communicating the action invoked to
-    // ScreenshotNotificationSmartActionsProvider.
-    public static final String EXTRA_ACTION_TYPE = "android:screenshot_action_type";
-    public static final String EXTRA_ID = "android:screenshot_id";
-    public static final String EXTRA_SMART_ACTIONS_ENABLED = "android:smart_actions_enabled";
-    public static final String EXTRA_ACTION_INTENT = "android:screenshot_action_intent";
-    public static final String EXTRA_ACTION_INTENT_FILLIN =
-            "android:screenshot_action_intent_fillin";
-
-
     // From WizardManagerHelper.java
     private static final String SETTINGS_SECURE_USER_SETUP_COMPLETE = "user_setup_complete";
 
@@ -213,7 +125,6 @@
     private final ScreenshotNotificationSmartActionsProvider
             mScreenshotNotificationSmartActionsProvider;
     private final TimeoutHandler mScreenshotHandler;
-    private final ActionIntentExecutor mActionIntentExecutor;
     private final UserManager mUserManager;
     private final AssistContentRequester mAssistContentRequester;
     private final ActionExecutor mActionExecutor;
@@ -259,7 +170,6 @@
             BroadcastDispatcher broadcastDispatcher,
             ScreenshotNotificationSmartActionsProvider screenshotNotificationSmartActionsProvider,
             ScreenshotActionsController.Factory screenshotActionsControllerFactory,
-            ActionIntentExecutor actionIntentExecutor,
             ActionExecutor.Factory actionExecutorFactory,
             UserManager userManager,
             AssistContentRequester assistContentRequester,
@@ -289,7 +199,6 @@
         final Context displayContext = context.createDisplayContext(display);
         mContext = (WindowContext) displayContext.createWindowContext(TYPE_SCREENSHOT, null);
         mFlags = flags;
-        mActionIntentExecutor = actionIntentExecutor;
         mUserManager = userManager;
         mMessageContainerController = messageContainerController;
         mAssistContentRequester = assistContentRequester;
@@ -445,21 +354,11 @@
 
     void prepareViewForNewScreenshot(@NonNull ScreenshotData screenshot, String oldPackageName) {
         withWindowAttached(() -> {
-            if (screenshotPrivateProfileAccessibilityAnnouncementFix()) {
-                mAnnouncementResolver.getScreenshotAnnouncement(
-                        screenshot.getUserHandle().getIdentifier(),
-                        announcement -> {
-                            mViewProxy.announceForAccessibility(announcement);
-                        });
-            } else {
-                if (mUserManager.isManagedProfile(screenshot.getUserHandle().getIdentifier())) {
-                    mViewProxy.announceForAccessibility(mContext.getResources().getString(
-                            R.string.screenshot_saving_work_profile_title));
-                } else {
-                    mViewProxy.announceForAccessibility(
-                            mContext.getResources().getString(R.string.screenshot_saving_title));
-                }
-            }
+            mAnnouncementResolver.getScreenshotAnnouncement(
+                    screenshot.getUserHandle().getIdentifier(),
+                    announcement -> {
+                        mViewProxy.announceForAccessibility(announcement);
+                    });
         });
 
         mViewProxy.reset();
@@ -483,16 +382,19 @@
      * Requests the view to dismiss the current screenshot (may be ignored, if screenshot is already
      * being dismissed)
      */
-    void requestDismissal(ScreenshotEvent event) {
+    @Override
+    public void requestDismissal(ScreenshotEvent event) {
         mViewProxy.requestDismissal(event);
     }
 
-    boolean isPendingSharedTransition() {
+    @Override
+    public boolean isPendingSharedTransition() {
         return mActionExecutor.isPendingSharedTransition();
     }
 
     // Any cleanup needed when the service is being destroyed.
-    void onDestroy() {
+    @Override
+    public void onDestroy() {
         if (mSaveInBgTask != null) {
             // just log success/failure for the pre-existing screenshot
             mSaveInBgTask.setActionsReadyListener(this::logSuccessOnActionsReady);
@@ -580,13 +482,6 @@
                                 }
                             }
                         }
-
-                        @Override
-                        public void requestCompatCameraControl(boolean showControl,
-                                boolean transformationApplied,
-                                ICompatCameraControlCallback callback) {
-                            Log.w(TAG, "Unexpected requestCompatCameraControl callback");
-                        }
                     });
         });
     }
@@ -673,7 +568,8 @@
         layout.setClipToPadding(false);
     }
 
-    void removeWindow() {
+    @Override
+    public void removeWindow() {
         final View decorView = mWindow.peekDecorView();
         if (decorView != null && decorView.isAttachedToWindow()) {
             if (DEBUG_WINDOW) {
@@ -765,33 +661,6 @@
         mScreenshotAnimation.start();
     }
 
-    /**
-     * Supplies the necessary bits for the shared element transition to share sheet.
-     * Note that once called, the action intent to share must be sent immediately after.
-     */
-    private Pair<ActivityOptions, ExitTransitionCoordinator> createWindowTransition() {
-        ExitTransitionCoordinator.ExitTransitionCallbacks callbacks =
-                new ExitTransitionCoordinator.ExitTransitionCallbacks() {
-                    @Override
-                    public boolean isReturnTransitionAllowed() {
-                        return false;
-                    }
-
-                    @Override
-                    public void hideSharedElements() {
-                        finishDismiss();
-                    }
-
-                    @Override
-                    public void onFinish() {
-                    }
-                };
-
-        return ActivityOptions.startSharedElementAnimation(mWindow, callbacks, null,
-                Pair.create(mViewProxy.getScreenshotPreview(),
-                        ChooserActivity.FIRST_IMAGE_PREVIEW_TRANSITION_NAME));
-    }
-
     /** Reset screenshot view and then call onCompleteRunnable */
     private void finishDismiss() {
         Log.d(TAG, "finishDismiss");
@@ -838,11 +707,11 @@
     private void saveScreenshotInWorkerThread(
             UserHandle owner,
             @NonNull Consumer<Uri> finisher,
-            @Nullable ActionsReadyListener actionsReadyListener,
-            @Nullable QuickShareActionReadyListener
+            @Nullable SaveImageInBackgroundTask.ActionsReadyListener actionsReadyListener,
+            @Nullable SaveImageInBackgroundTask.QuickShareActionReadyListener
                     quickShareActionsReadyListener) {
-        ScreenshotController.SaveImageInBackgroundData
-                data = new ScreenshotController.SaveImageInBackgroundData();
+        SaveImageInBackgroundTask.SaveImageInBackgroundData
+                data = new SaveImageInBackgroundTask.SaveImageInBackgroundData();
         data.image = mScreenBitmap;
         data.finisher = finisher;
         data.mActionsReadyListener = actionsReadyListener;
@@ -881,7 +750,7 @@
     /**
      * Logs success/failure of the screenshot saving task, and shows an error if it failed.
      */
-    private void logSuccessOnActionsReady(ScreenshotController.SavedImageData imageData) {
+    private void logSuccessOnActionsReady(SaveImageInBackgroundTask.SavedImageData imageData) {
         logScreenshotResultStatus(imageData.uri, imageData.owner);
     }
 
@@ -951,12 +820,12 @@
 
     /** Injectable factory to create screenshot controller instances for a specific display. */
     @AssistedFactory
-    public interface Factory {
+    public interface Factory extends InteractiveScreenshotHandler.Factory {
         /**
          * Creates an instance of the controller for that specific display.
          *
          * @param display                 display to capture
          */
-        ScreenshotController create(Display display);
+        LegacyScreenshotController create(Display display);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/SmartActionsReceiver.java b/packages/SystemUI/src/com/android/systemui/screenshot/SmartActionsReceiver.java
index f8b22a6..f902693 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/SmartActionsReceiver.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/SmartActionsReceiver.java
@@ -17,10 +17,6 @@
 package com.android.systemui.screenshot;
 
 import static com.android.systemui.screenshot.LogConfig.DEBUG_ACTIONS;
-import static com.android.systemui.screenshot.ScreenshotController.EXTRA_ACTION_INTENT;
-import static com.android.systemui.screenshot.ScreenshotController.EXTRA_ACTION_INTENT_FILLIN;
-import static com.android.systemui.screenshot.ScreenshotController.EXTRA_ACTION_TYPE;
-import static com.android.systemui.screenshot.ScreenshotController.EXTRA_ID;
 
 import android.app.ActivityOptions;
 import android.app.PendingIntent;
@@ -36,6 +32,15 @@
  */
 public class SmartActionsReceiver extends BroadcastReceiver {
     private static final String TAG = "SmartActionsReceiver";
+    // These strings are used for communicating the action invoked to
+    // ScreenshotNotificationSmartActionsProvider.
+    public static final String EXTRA_ACTION_TYPE = "android:screenshot_action_type";
+    public static final String EXTRA_ID = "android:screenshot_id";
+    public static final String EXTRA_SMART_ACTIONS_ENABLED = "android:smart_actions_enabled";
+    public static final String EXTRA_ACTION_INTENT = "android:screenshot_action_intent";
+    public static final String EXTRA_ACTION_INTENT_FILLIN =
+            "android:screenshot_action_intent_fillin";
+
 
     private final ScreenshotSmartActions mScreenshotSmartActions;
 
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotExecutor.kt b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotExecutor.kt
index 07f6e85..50ea3bb 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotExecutor.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotExecutor.kt
@@ -74,7 +74,7 @@
 class TakeScreenshotExecutorImpl
 @Inject
 constructor(
-    private val screenshotControllerFactory: ScreenshotController.Factory,
+    private val interactiveScreenshotHandlerFactory: InteractiveScreenshotHandler.Factory,
     displayRepository: DisplayRepository,
     @Application private val mainScope: CoroutineScope,
     private val screenshotRequestProcessor: ScreenshotRequestProcessor,
@@ -83,7 +83,7 @@
     private val headlessScreenshotHandler: HeadlessScreenshotHandler,
 ) : TakeScreenshotExecutor {
     private val displays = displayRepository.displays
-    private var screenshotController: ScreenshotController? = null
+    private var screenshotController: InteractiveScreenshotHandler? = null
     private val notificationControllers = mutableMapOf<Int, ScreenshotNotificationsController>()
 
     /**
@@ -183,7 +183,7 @@
 
     /** Propagates the close system dialog signal to the ScreenshotController. */
     override fun onCloseSystemDialogsReceived() {
-        if (screenshotController?.isPendingSharedTransition == false) {
+        if (screenshotController?.isPendingSharedTransition() == false) {
             screenshotController?.requestDismissal(SCREENSHOT_DISMISSED_OTHER)
         }
     }
@@ -218,8 +218,9 @@
         }
     }
 
-    private fun getScreenshotController(display: Display): ScreenshotController {
-        val controller = screenshotController ?: screenshotControllerFactory.create(display)
+    private fun getScreenshotController(display: Display): InteractiveScreenshotHandler {
+        val controller =
+            screenshotController ?: interactiveScreenshotHandlerFactory.create(display)
         screenshotController = controller
         return controller
     }
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsActivity.java b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsActivity.java
index 8feefa4..9db1f24 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsActivity.java
@@ -46,13 +46,17 @@
 import android.os.ResultReceiver;
 import android.util.Log;
 import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
 import android.widget.Button;
 import android.widget.CheckBox;
 import android.widget.ImageView;
+import android.widget.ListPopupWindow;
 import android.widget.TextView;
 
 import androidx.activity.ComponentActivity;
 import androidx.annotation.Nullable;
+import androidx.appcompat.content.res.AppCompatResources;
 import androidx.core.graphics.Insets;
 import androidx.core.view.ViewCompat;
 import androidx.core.view.WindowInsetsCompat;
@@ -67,6 +71,7 @@
 import com.android.systemui.screenshot.scroll.CropView;
 import com.android.systemui.settings.UserTracker;
 
+import java.util.List;
 import java.util.Set;
 
 import javax.inject.Inject;
@@ -92,6 +97,7 @@
 
     private static final String TAG = AppClipsActivity.class.getSimpleName();
     private static final ApplicationInfoFlags APPLICATION_INFO_FLAGS = ApplicationInfoFlags.of(0);
+    private static final int DRAWABLE_END = 2;
 
     private final AppClipsViewModel.Factory mViewModelFactory;
     private final PackageManager mPackageManager;
@@ -192,6 +198,7 @@
         mViewModel.getResultLiveData().observe(this, this::setResultThenFinish);
         mViewModel.getErrorLiveData().observe(this, this::setErrorThenFinish);
         mViewModel.getBacklinksLiveData().observe(this, this::setBacklinksData);
+        mViewModel.mSelectedBacklinksLiveData.observe(this, this::updateBacklinksTextView);
 
         if (savedInstanceState == null) {
             int displayId = getDisplayId();
@@ -305,8 +312,8 @@
 
         if (mBacklinksIncludeDataCheckBox.getVisibility() == View.VISIBLE
                 && mBacklinksIncludeDataCheckBox.isChecked()
-                && mViewModel.getBacklinksLiveData().getValue() != null) {
-            ClipData backlinksData = mViewModel.getBacklinksLiveData().getValue().getClipData();
+                && mViewModel.mSelectedBacklinksLiveData.getValue() != null) {
+            ClipData backlinksData = mViewModel.mSelectedBacklinksLiveData.getValue().getClipData();
             data.putParcelable(EXTRA_CLIP_DATA, backlinksData);
 
             DebugLogger.INSTANCE.logcatMessage(this,
@@ -330,18 +337,80 @@
         finish();
     }
 
-    private void setBacklinksData(InternalBacklinksData backlinksData) {
+    private void setBacklinksData(List<InternalBacklinksData> backlinksData) {
         mBacklinksIncludeDataCheckBox.setVisibility(View.VISIBLE);
         mBacklinksDataTextView.setVisibility(
                 mBacklinksIncludeDataCheckBox.isChecked() ? View.VISIBLE : View.GONE);
 
-        mBacklinksDataTextView.setText(backlinksData.getClipData().getDescription().getLabel());
+        // Set up the dropdown when multiple backlinks are available.
+        if (backlinksData.size() > 1) {
+            setUpListPopupWindow(backlinksData, mBacklinksDataTextView);
+        }
+    }
 
+    private void setUpListPopupWindow(List<InternalBacklinksData> backlinksData, View anchor) {
+        ListPopupWindow listPopupWindow = new ListPopupWindow(this);
+        listPopupWindow.setAnchorView(anchor);
+        listPopupWindow.setOverlapAnchor(true);
+        listPopupWindow.setBackgroundDrawable(
+                AppCompatResources.getDrawable(this, R.drawable.backlinks_rounded_rectangle));
+        listPopupWindow.setOnItemClickListener((parent, view, position, id) -> {
+            mViewModel.mSelectedBacklinksLiveData.setValue(backlinksData.get(position));
+            listPopupWindow.dismiss();
+        });
+
+        ArrayAdapter<InternalBacklinksData> adapter = new ArrayAdapter<>(this,
+                R.layout.app_clips_backlinks_drop_down_entry) {
+            @Override
+            public View getView(int position, @Nullable View convertView, ViewGroup parent) {
+                TextView itemView = (TextView) super.getView(position, convertView, parent);
+                InternalBacklinksData data = backlinksData.get(position);
+                itemView.setText(data.getClipData().getDescription().getLabel());
+
+                Drawable icon = data.getAppIcon();
+                icon.setBounds(createBacklinksTextViewDrawableBounds());
+                itemView.setCompoundDrawablesRelative(/* start= */ icon, /* top= */ null,
+                        /* end= */ null, /* bottom= */ null);
+
+                return itemView;
+            }
+        };
+        adapter.addAll(backlinksData);
+        listPopupWindow.setAdapter(adapter);
+
+        mBacklinksDataTextView.setOnClickListener(unused -> listPopupWindow.show());
+    }
+
+    /**
+     * Updates the {@link #mBacklinksDataTextView} with the currently selected
+     * {@link InternalBacklinksData}. The {@link AppClipsViewModel#getBacklinksLiveData()} is
+     * expected to be already set when this method is called.
+     */
+    private void updateBacklinksTextView(InternalBacklinksData backlinksData) {
+        mBacklinksDataTextView.setText(backlinksData.getClipData().getDescription().getLabel());
         Drawable appIcon = backlinksData.getAppIcon();
-        int size = getResources().getDimensionPixelSize(R.dimen.appclips_backlinks_icon_size);
-        appIcon.setBounds(/* left= */ 0, /* top= */ 0, /* right= */ size, /* bottom= */ size);
+        Rect compoundDrawableBounds = createBacklinksTextViewDrawableBounds();
+        appIcon.setBounds(compoundDrawableBounds);
+
+        // Try to reuse the dropdown down arrow icon if available, will be null if never set.
+        Drawable dropDownIcon = mBacklinksDataTextView.getCompoundDrawablesRelative()[DRAWABLE_END];
+        if (mViewModel.getBacklinksLiveData().getValue().size() > 1 && dropDownIcon == null) {
+            // Set up the dropdown down arrow drawable only if it is required.
+            dropDownIcon = AppCompatResources.getDrawable(this, R.drawable.arrow_pointing_down);
+            dropDownIcon.setBounds(compoundDrawableBounds);
+            dropDownIcon.setTint(Utils.getColorAttr(this,
+                    android.R.attr.textColorSecondary).getDefaultColor());
+        }
+
         mBacklinksDataTextView.setCompoundDrawablesRelative(/* start= */ appIcon, /* top= */
-                null, /* end= */ null, /* bottom= */ null);
+                null, /* end= */ dropDownIcon, /* bottom= */ null);
+    }
+
+    private Rect createBacklinksTextViewDrawableBounds() {
+        int size = getResources().getDimensionPixelSize(R.dimen.appclips_backlinks_icon_size);
+        Rect bounds = new Rect();
+        bounds.set(/* left= */ 0, /* top= */ 0, /* right= */ size, /* bottom= */ size);
+        return bounds;
     }
 
     private void setError(int errorCode) {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsViewModel.java b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsViewModel.java
index bd9e295..3530b3f 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsViewModel.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsViewModel.java
@@ -21,12 +21,11 @@
 import static android.content.Intent.CAPTURE_CONTENT_FOR_NOTE_FAILED;
 import static android.content.Intent.CATEGORY_LAUNCHER;
 
-import static com.google.common.util.concurrent.Futures.withTimeout;
-
 import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
 
 import android.app.ActivityTaskManager.RootTaskInfo;
 import android.app.IActivityTaskManager;
+import android.app.TaskInfo;
 import android.app.WindowConfiguration;
 import android.app.assist.AssistContent;
 import android.content.ClipData;
@@ -41,7 +40,6 @@
 import android.graphics.RenderNode;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
-import android.os.RemoteException;
 import android.os.UserHandle;
 import android.util.Log;
 import android.view.Display;
@@ -94,7 +92,8 @@
     private final MutableLiveData<Bitmap> mScreenshotLiveData;
     private final MutableLiveData<Uri> mResultLiveData;
     private final MutableLiveData<Integer> mErrorLiveData;
-    private final MutableLiveData<InternalBacklinksData> mBacklinksLiveData;
+    private final MutableLiveData<List<InternalBacklinksData>> mBacklinksLiveData;
+    final MutableLiveData<InternalBacklinksData> mSelectedBacklinksLiveData;
 
     private AppClipsViewModel(AppClipsCrossProcessHelper appClipsCrossProcessHelper,
             ImageExporter imageExporter, IActivityTaskManager atmService,
@@ -112,6 +111,7 @@
         mResultLiveData = new MutableLiveData<>();
         mErrorLiveData = new MutableLiveData<>();
         mBacklinksLiveData = new MutableLiveData<>();
+        mSelectedBacklinksLiveData = new MutableLiveData<>();
     }
 
     /**
@@ -135,10 +135,11 @@
     /**
      * Triggers the Backlinks flow which:
      * <ul>
-     *     <li>Evaluates the task to query.
-     *     <li>Requests {@link AssistContent} from that task.
-     *     <li>Transforms the {@link AssistContent} into {@link ClipData} for Backlinks.
-     *     <li>The {@link ClipData} is reported to activity via {@link #getBacklinksLiveData()}.
+     *     <li>Evaluates the tasks to query.
+     *     <li>Requests {@link AssistContent} from all valid tasks.
+     *     <li>Transforms {@link AssistContent} into {@link InternalBacklinksData} for Backlinks.
+     *     <li>The {@link InternalBacklinksData}s are reported to activity via
+     *     {@link #getBacklinksLiveData()}.
      * </ul>
      *
      * @param taskIdsToIgnore id of the tasks to ignore when querying for {@link AssistContent}
@@ -146,24 +147,24 @@
      */
     void triggerBacklinks(Set<Integer> taskIdsToIgnore, int displayId) {
         DebugLogger.INSTANCE.logcatMessage(this, () -> "Backlinks triggered");
-        mBgExecutor.execute(() -> {
-            ListenableFuture<InternalBacklinksData> backlinksData = getBacklinksData(
-                    taskIdsToIgnore, displayId);
-            Futures.addCallback(backlinksData, new FutureCallback<>() {
-                @Override
-                public void onSuccess(@Nullable InternalBacklinksData result) {
-                    if (result != null) {
-                        mBacklinksLiveData.setValue(result);
-                    }
+        ListenableFuture<List<InternalBacklinksData>> backlinksData = getAllAvailableBacklinks(
+                taskIdsToIgnore, displayId);
+        Futures.addCallback(backlinksData, new FutureCallback<>() {
+            @Override
+            public void onSuccess(@Nullable List<InternalBacklinksData> result) {
+                if (result != null && !result.isEmpty()) {
+                    // Set the list of backlinks before setting the selected backlink as this is
+                    // required when updating the backlink data text view.
+                    mBacklinksLiveData.setValue(result);
+                    mSelectedBacklinksLiveData.setValue(result.get(0));
                 }
+            }
 
-                @Override
-                public void onFailure(Throwable t) {
-                    Log.e(TAG, "Error querying for Backlinks data", t);
-                }
-            }, mMainExecutor);
-
-        });
+            @Override
+            public void onFailure(Throwable t) {
+                Log.e(TAG, "Error querying for Backlinks data", t);
+            }
+        }, mMainExecutor);
     }
 
     /** Returns a {@link LiveData} that holds the captured screenshot. */
@@ -184,8 +185,11 @@
         return mErrorLiveData;
     }
 
-    /** Returns a {@link LiveData} that holds Backlinks data in {@link InternalBacklinksData}. */
-    LiveData<InternalBacklinksData> getBacklinksLiveData() {
+    /**
+     * Returns a {@link LiveData} that holds all the available Backlinks data and the currently
+     * selected index for displaying the Backlinks in the UI.
+     */
+    LiveData<List<InternalBacklinksData>> getBacklinksLiveData() {
         return mBacklinksLiveData;
     }
 
@@ -230,26 +234,58 @@
         return HardwareRenderer.createHardwareBitmap(output, bounds.width(), bounds.height());
     }
 
-    private ListenableFuture<InternalBacklinksData> getBacklinksData(Set<Integer> taskIdsToIgnore,
-            int displayId) {
-        return getAllRootTaskInfosOnDisplay(displayId)
-                .stream()
-                .filter(taskInfo -> shouldIncludeTask(taskInfo, taskIdsToIgnore))
-                .findFirst()
-                .map(this::getBacklinksDataForTaskId)
-                .orElse(Futures.immediateFuture(null));
+    private ListenableFuture<List<InternalBacklinksData>> getAllAvailableBacklinks(
+            Set<Integer> taskIdsToIgnore, int displayId) {
+        ListenableFuture<List<TaskInfo>> allTasksOnDisplayFuture = getAllTasksOnDisplay(displayId);
+
+        ListenableFuture<List<ListenableFuture<InternalBacklinksData>>> backlinksNestedListFuture =
+                Futures.transform(allTasksOnDisplayFuture, allTasksOnDisplay ->
+                        allTasksOnDisplay
+                                .stream()
+                                .filter(taskInfo -> shouldIncludeTask(taskInfo, taskIdsToIgnore))
+                                .map(this::getBacklinksDataForTaskInfo)
+                                .toList(),
+                        mBgExecutor);
+
+        return Futures.transformAsync(backlinksNestedListFuture, Futures::allAsList, mBgExecutor);
     }
 
-    private List<RootTaskInfo> getAllRootTaskInfosOnDisplay(int displayId) {
-        try {
-            return mAtmService.getAllRootTaskInfosOnDisplay(displayId);
-        } catch (RemoteException e) {
-            Log.e(TAG, String.format("Error while querying for tasks on display %d", displayId), e);
-            return Collections.emptyList();
-        }
+    /**
+     * Returns all tasks on a given display after querying {@link IActivityTaskManager} from the
+     * {@link #mBgExecutor}.
+     */
+    private ListenableFuture<List<TaskInfo>> getAllTasksOnDisplay(int displayId) {
+        SettableFuture<List<TaskInfo>> recentTasksFuture = SettableFuture.create();
+        mBgExecutor.execute(() -> {
+            try {
+                // Directly call into ActivityTaskManagerService instead of going through WMShell
+                // because WMShell is only available in the main SysUI process and App Clips runs
+                // in its own separate process as it deals with bitmaps.
+                List<TaskInfo> allTasksOnDisplay = mAtmService.getTasks(
+                                /* maxNum= */ Integer.MAX_VALUE,
+                                // PIP tasks are not visible in recents. So _not_ filtering for
+                                // tasks that are only visible in recents.
+                                /* filterOnlyVisibleRecents= */ false,
+                                /* keepIntentExtra= */ false,
+                                displayId)
+                        .stream()
+                        .map(runningTaskInfo -> (TaskInfo) runningTaskInfo)
+                        .toList();
+                recentTasksFuture.set(allTasksOnDisplay);
+            } catch (Exception e) {
+                Log.e(TAG, String.format("Error getting all tasks on displayId %d", displayId), e);
+                recentTasksFuture.set(Collections.emptyList());
+            }
+        });
+
+        return withTimeout(recentTasksFuture);
     }
 
-    private boolean shouldIncludeTask(RootTaskInfo taskInfo, Set<Integer> taskIdsToIgnore) {
+    /**
+     * Returns whether the app represented by the provided {@link TaskInfo} should be included for
+     * querying for {@link AssistContent}.
+     */
+    private boolean shouldIncludeTask(TaskInfo taskInfo, Set<Integer> taskIdsToIgnore) {
         DebugLogger.INSTANCE.logcatMessage(this,
                 () -> String.format("shouldIncludeTask taskId %d; topActivity %s", taskInfo.taskId,
                         taskInfo.topActivity));
@@ -262,11 +298,14 @@
                 && taskInfo.numActivities > 0
                 && taskInfo.topActivity != null
                 && taskInfo.topActivityInfo != null
-                && taskInfo.childTaskIds.length > 0
                 && taskInfo.getActivityType() == WindowConfiguration.ACTIVITY_TYPE_STANDARD
                 && canAppStartThroughLauncher(taskInfo.topActivity.getPackageName());
     }
 
+    /**
+     * Returns whether the app represented by the provided {@code packageName} can be launched
+     * through the all apps tray by a user.
+     */
     private boolean canAppStartThroughLauncher(String packageName) {
         // Use Intent.resolveActivity API to check if the intent resolves as that is what Android
         // uses internally when apps use Context.startActivity.
@@ -274,8 +313,12 @@
                 != null;
     }
 
-    private ListenableFuture<InternalBacklinksData> getBacklinksDataForTaskId(
-            RootTaskInfo taskInfo) {
+    /**
+     * Returns an {@link InternalBacklinksData} that represents the Backlink data internally, which
+     * is captured by querying the system using {@link TaskInfo#taskId}.
+     */
+    private ListenableFuture<InternalBacklinksData> getBacklinksDataForTaskInfo(
+            TaskInfo taskInfo) {
         DebugLogger.INSTANCE.logcatMessage(this,
                 () -> String.format("getBacklinksDataForTaskId for taskId %d; topActivity %s",
                         taskInfo.taskId, taskInfo.topActivity));
@@ -284,7 +327,13 @@
         int taskId = taskInfo.taskId;
         mAssistContentRequester.requestAssistContent(taskId, assistContent ->
                 backlinksData.set(getBacklinksDataFromAssistContent(taskInfo, assistContent)));
-        return withTimeout(backlinksData, 5L, TimeUnit.SECONDS, newSingleThreadScheduledExecutor());
+        return withTimeout(backlinksData);
+    }
+
+    /** Returns the same {@link ListenableFuture} but with a 5 {@link TimeUnit#SECONDS} timeout. */
+    private static <V> ListenableFuture<V> withTimeout(ListenableFuture<V> future) {
+        return Futures.withTimeout(future, 5L, TimeUnit.SECONDS,
+                newSingleThreadScheduledExecutor());
     }
 
     /**
@@ -306,7 +355,7 @@
      * @param content the {@link AssistContent} to map into Backlinks {@link ClipData}.
      * @return {@link InternalBacklinksData} that represents the Backlinks data along with app icon.
      */
-    private InternalBacklinksData getBacklinksDataFromAssistContent(RootTaskInfo taskInfo,
+    private InternalBacklinksData getBacklinksDataFromAssistContent(TaskInfo taskInfo,
             @Nullable AssistContent content) {
         DebugLogger.INSTANCE.logcatMessage(this,
                 () -> String.format("getBacklinksDataFromAssistContent taskId %d; topActivity %s",
@@ -365,7 +414,7 @@
         return resolvedComponent.getPackageName().equals(requiredPackageName);
     }
 
-    private String getAppNameOfTask(RootTaskInfo taskInfo) {
+    private String getAppNameOfTask(TaskInfo taskInfo) {
         return taskInfo.topActivityInfo.loadLabel(mPackageManager).toString();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java b/packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java
index 682f848..254dde4 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java
@@ -16,12 +16,17 @@
 
 package com.android.systemui.screenshot.dagger;
 
+import static com.android.systemui.Flags.screenshotUiControllerRefactor;
+
 import android.app.Service;
 import android.view.accessibility.AccessibilityManager;
 
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.screenshot.ImageCapture;
 import com.android.systemui.screenshot.ImageCaptureImpl;
+import com.android.systemui.screenshot.InteractiveScreenshotHandler;
+import com.android.systemui.screenshot.LegacyScreenshotController;
+import com.android.systemui.screenshot.ScreenshotController;
 import com.android.systemui.screenshot.ScreenshotPolicy;
 import com.android.systemui.screenshot.ScreenshotPolicyImpl;
 import com.android.systemui.screenshot.ScreenshotSoundController;
@@ -90,4 +95,15 @@
             AccessibilityManager accessibilityManager) {
         return new ScreenshotViewModel(accessibilityManager);
     }
+
+    @Provides
+    static InteractiveScreenshotHandler.Factory providesScreenshotController(
+            LegacyScreenshotController.Factory legacyScreenshotController,
+            ScreenshotController.Factory screenshotController) {
+        if (screenshotUiControllerRefactor()) {
+            return screenshotController;
+        } else {
+            return legacyScreenshotController;
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/policy/WorkProfilePolicy.kt b/packages/SystemUI/src/com/android/systemui/screenshot/policy/WorkProfilePolicy.kt
index fdf16aa..3fe3162 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/policy/WorkProfilePolicy.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/policy/WorkProfilePolicy.kt
@@ -18,6 +18,7 @@
 
 import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
 import android.app.WindowConfiguration.WINDOWING_MODE_PINNED
+import android.content.Context
 import android.os.UserHandle
 import com.android.systemui.screenshot.data.model.DisplayContentModel
 import com.android.systemui.screenshot.data.model.ProfileType
@@ -25,7 +26,7 @@
 import com.android.systemui.screenshot.policy.CapturePolicy.PolicyResult
 import com.android.systemui.screenshot.policy.CapturePolicy.PolicyResult.NotMatched
 import com.android.systemui.screenshot.policy.CaptureType.IsolatedTask
-import com.android.window.flags.Flags
+import com.android.wm.shell.shared.desktopmode.DesktopModeFlags
 import javax.inject.Inject
 import kotlinx.coroutines.flow.first
 
@@ -38,6 +39,7 @@
 @Inject
 constructor(
     private val profileTypes: ProfileTypeRepository,
+    private val context: Context,
 ) : CapturePolicy {
 
     override suspend fun check(content: DisplayContentModel): PolicyResult {
@@ -46,7 +48,7 @@
             return NotMatched(policy = NAME, reason = SHADE_EXPANDED)
         }
 
-        if (Flags.enableDesktopWindowingMode()) {
+        if (DesktopModeFlags.DESKTOP_WINDOWING_MODE.isEnabled(context)) {
             content.rootTasks.firstOrNull()?.also {
                 if (it.windowingMode == WINDOWING_MODE_FREEFORM) {
                     return NotMatched(policy = NAME, reason = DESKTOP_MODE_ENABLED)
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/scroll/LongScreenshotData.java b/packages/SystemUI/src/com/android/systemui/screenshot/scroll/LongScreenshotData.java
index ebac5bf..08c1fca 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/scroll/LongScreenshotData.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/scroll/LongScreenshotData.java
@@ -16,8 +16,9 @@
 
 package com.android.systemui.screenshot.scroll;
 
+import android.graphics.Rect;
+
 import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.screenshot.ScreenshotController;
 
 import java.util.concurrent.atomic.AtomicReference;
 
@@ -30,9 +31,18 @@
 @SysUISingleton
 public class LongScreenshotData {
     private final AtomicReference<ScrollCaptureController.LongScreenshot> mLongScreenshot;
-    private final AtomicReference<ScreenshotController.TransitionDestination>
+    private final AtomicReference<TransitionDestination>
             mTransitionDestinationCallback;
 
+    public interface TransitionDestination {
+        /**
+         * Allows the long screenshot activity to call back with a destination location (the bounds
+         * on screen of the destination for the transitioning view) and a Runnable to be run once
+         * the transition animation is complete.
+         */
+        void setTransitionDestination(Rect transitionDestination, Runnable onTransitionEnd);
+    }
+
     @Inject
     public LongScreenshotData() {
         mLongScreenshot = new AtomicReference<>();
@@ -63,15 +73,14 @@
     /**
      * Set the holder's TransitionDestination callback.
      */
-    public void setTransitionDestinationCallback(
-            ScreenshotController.TransitionDestination destination) {
+    public void setTransitionDestinationCallback(TransitionDestination destination) {
         mTransitionDestinationCallback.set(destination);
     }
 
     /**
      * Return the current TransitionDestination callback and clear.
      */
-    public ScreenshotController.TransitionDestination takeTransitionDestinationCallback() {
+    public TransitionDestination takeTransitionDestinationCallback() {
         return mTransitionDestinationCallback.getAndSet(null);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/settings/SystemSettingsRepositoryModule.kt b/packages/SystemUI/src/com/android/systemui/settings/SystemSettingsRepositoryModule.kt
new file mode 100644
index 0000000..02ce74a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/settings/SystemSettingsRepositoryModule.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.settings
+
+import android.content.ContentResolver
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.shared.settings.data.repository.SystemSettingsRepository
+import com.android.systemui.shared.settings.data.repository.SystemSettingsRepositoryImpl
+import dagger.Module
+import dagger.Provides
+import kotlinx.coroutines.CoroutineDispatcher
+
+@Module
+object SystemSettingsRepositoryModule {
+    @JvmStatic
+    @Provides
+    @SysUISingleton
+    fun provideSystemSettingsRepository(
+        contentResolver: ContentResolver,
+        @Background backgroundDispatcher: CoroutineDispatcher,
+    ): SystemSettingsRepository =
+        SystemSettingsRepositoryImpl(contentResolver, backgroundDispatcher)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
index b468d0e..05c50fe 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
@@ -39,6 +39,7 @@
 import androidx.lifecycle.repeatOnLifecycle
 import com.android.compose.theme.PlatformTheme
 import com.android.internal.annotations.VisibleForTesting
+import com.android.systemui.Flags
 import com.android.systemui.Flags.glanceableHubBackGesture
 import com.android.systemui.ambient.touch.TouchMonitor
 import com.android.systemui.ambient.touch.dagger.AmbientTouchComponent
@@ -132,13 +133,6 @@
     private var touchMonitor: TouchMonitor? = null
 
     /**
-     * The width of the area in which a right edge swipe can open the hub, in pixels. Read from
-     * resources when [initView] is called.
-     */
-    // TODO(b/320786721): support RTL layouts
-    private var rightEdgeSwipeRegionWidth: Int = 0
-
-    /**
      * True if we are currently tracking a touch intercepted by the hub, either because the hub is
      * open or being opened.
      */
@@ -264,11 +258,6 @@
 
         communalContainerView = containerView
 
-        rightEdgeSwipeRegionWidth =
-            containerView.resources.getDimensionPixelSize(
-                R.dimen.communal_right_edge_swipe_region_width
-            )
-
         val topEdgeSwipeRegionWidth =
             containerView.resources.getDimensionPixelSize(
                 R.dimen.communal_top_edge_swipe_region_height
@@ -285,7 +274,7 @@
             // Run when the touch handling lifecycle is RESUMED, meaning the hub is visible and not
             // occluded.
             lifecycleRegistry.repeatOnLifecycle(Lifecycle.State.RESUMED) {
-                // Avoid adding exclusion to right/left edges to allow back gestures.
+                // Avoid adding exclusion to end/start edges to allow back gestures.
                 val insets =
                     if (glanceableHubBackGesture()) {
                         containerView.rootWindowInsets.getInsets(WindowInsets.Type.systemGestures())
@@ -293,25 +282,38 @@
                         Insets.NONE
                     }
 
-                containerView.systemGestureExclusionRects =
-                    listOf(
-                        // Only allow swipe up to bouncer and swipe down to shade in the very
-                        // top/bottom to avoid conflicting with widgets in the hub grid.
-                        Rect(
-                            insets.left,
-                            topEdgeSwipeRegionWidth,
-                            containerView.right - insets.right,
-                            containerView.bottom - bottomEdgeSwipeRegionWidth
-                        ),
-                        // Disable back gestures on the left side of the screen, to avoid
-                        // conflicting with scene transitions.
-                        Rect(
-                            0,
-                            0,
-                            insets.right,
-                            containerView.bottom,
-                        )
+                val ltr = containerView.layoutDirection == View.LAYOUT_DIRECTION_LTR
+
+                val backGestureInset =
+                    Rect(
+                        if (ltr) 0 else insets.left,
+                        0,
+                        if (ltr) insets.right else containerView.right,
+                        containerView.bottom,
                     )
+
+                containerView.systemGestureExclusionRects =
+                    if (Flags.hubmodeFullscreenVerticalSwipe()) {
+                        listOf(
+                            // Disable back gestures on the left side of the screen, to avoid
+                            // conflicting with scene transitions.
+                            backGestureInset
+                        )
+                    } else {
+                        listOf(
+                            // Only allow swipe up to bouncer and swipe down to shade in the very
+                            // top/bottom to avoid conflicting with widgets in the hub grid.
+                            Rect(
+                                insets.left,
+                                topEdgeSwipeRegionWidth,
+                                containerView.right - insets.right,
+                                containerView.bottom - bottomEdgeSwipeRegionWidth
+                            ),
+                            // Disable back gestures on the left side of the screen, to avoid
+                            // conflicting with scene transitions.
+                            backGestureInset
+                        )
+                    }
             }
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index c1caeed..257390f 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -16,7 +16,6 @@
 
 package com.android.systemui.shade;
 
-import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
 import static android.view.View.INVISIBLE;
 import static android.view.View.VISIBLE;
 
@@ -57,7 +56,6 @@
 import android.animation.ValueAnimator;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.app.StatusBarManager;
 import android.content.ContentResolver;
 import android.content.res.Resources;
 import android.database.ContentObserver;
@@ -233,7 +231,6 @@
 import com.android.systemui.statusbar.policy.KeyguardUserSwitcherView;
 import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
 import com.android.systemui.statusbar.policy.SplitShadeStateController;
-import com.android.systemui.statusbar.window.StatusBarWindowStateController;
 import com.android.systemui.unfold.SysUIUnfoldComponent;
 import com.android.systemui.util.Compile;
 import com.android.systemui.util.Utils;
@@ -705,7 +702,6 @@
             FalsingCollector falsingCollector,
             KeyguardStateController keyguardStateController,
             StatusBarStateController statusBarStateController,
-            StatusBarWindowStateController statusBarWindowStateController,
             NotificationShadeWindowController notificationShadeWindowController,
             DozeLog dozeLog,
             DozeParameters dozeParameters,
@@ -913,7 +909,6 @@
         mMediaDataManager = mediaDataManager;
         mTapAgainViewController = tapAgainViewController;
         mSysUiState = sysUiState;
-        statusBarWindowStateController.addListener(this::onStatusBarWindowStateChanged);
         mKeyguardBypassController = bypassController;
         mUpdateMonitor = keyguardUpdateMonitor;
         mLockscreenShadeTransitionController = lockscreenShadeTransitionController;
@@ -1310,10 +1305,6 @@
     /** Updates the StatusBarViewController and updates any that depend on it. */
     public void updateStatusViewController() {
         // Re-associate the KeyguardStatusViewController
-        if (mKeyguardStatusViewController != null) {
-            mKeyguardStatusViewController.onDestroy();
-        }
-
         if (MigrateClocksToBlueprint.isEnabled()) {
             // Need a shared controller until mKeyguardStatusViewController can be removed from
             // here, due to important state being set in that controller. Rebind in order to pick
@@ -4882,16 +4873,6 @@
         return mStatusBarStateListener;
     }
 
-    private void onStatusBarWindowStateChanged(@StatusBarManager.WindowVisibleState int state) {
-        if (state != WINDOW_STATE_SHOWING
-                && mStatusBarStateController.getState() == StatusBarState.SHADE) {
-            collapse(
-                    false /* animate */,
-                    false /* delayed */,
-                    1.0f /* speedUpFactor */);
-        }
-    }
-
     /** Handles MotionEvents for the Shade. */
     public final class TouchHandler implements View.OnTouchListener, Gefingerpoken {
         private long mLastTouchDownTime = -1L;
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
index bc5cf2a..7e0454c 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
@@ -41,12 +41,13 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.WindowInsets;
-import android.view.WindowManager;
 import android.view.WindowManager.LayoutParams;
 import android.view.WindowManagerGlobal;
 
+import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.systemui.Dumpable;
+import com.android.systemui.Flags;
 import com.android.systemui.biometrics.AuthController;
 import com.android.systemui.colorextraction.SysuiColorExtractor;
 import com.android.systemui.communal.domain.interactor.CommunalInteractor;
@@ -63,6 +64,7 @@
 import com.android.systemui.scene.ui.view.WindowRootViewComponent;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.shade.domain.interactor.ShadeInteractor;
+import com.android.systemui.shade.ui.viewmodel.NotificationShadeWindowModel;
 import com.android.systemui.statusbar.NotificationShadeWindowController;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
@@ -101,7 +103,7 @@
 
     private final Context mContext;
     private final WindowRootViewComponent.Factory mWindowRootViewComponentFactory;
-    private final WindowManager mWindowManager;
+    private final ViewCaptureAwareWindowManager mWindowManager;
     private final IActivityManager mActivityManager;
     private final DozeParameters mDozeParameters;
     private final KeyguardStateController mKeyguardStateController;
@@ -130,6 +132,7 @@
             mCallbacks = new ArrayList<>();
 
     private final SysuiColorExtractor mColorExtractor;
+    private final NotificationShadeWindowModel mNotificationShadeWindowModel;
     /**
      * Layout params would be aggregated and dispatched all at once if this is > 0.
      *
@@ -145,7 +148,7 @@
     public NotificationShadeWindowControllerImpl(
             Context context,
             WindowRootViewComponent.Factory windowRootViewComponentFactory,
-            WindowManager windowManager,
+            ViewCaptureAwareWindowManager viewCaptureAwareWindowManager,
             IActivityManager activityManager,
             DozeParameters dozeParameters,
             StatusBarStateController statusBarStateController,
@@ -162,10 +165,11 @@
             ShadeWindowLogger logger,
             Lazy<SelectedUserInteractor> userInteractor,
             UserTracker userTracker,
+            NotificationShadeWindowModel notificationShadeWindowModel,
             Lazy<CommunalInteractor> communalInteractor) {
         mContext = context;
         mWindowRootViewComponentFactory = windowRootViewComponentFactory;
-        mWindowManager = windowManager;
+        mWindowManager = viewCaptureAwareWindowManager;
         mActivityManager = activityManager;
         mDozeParameters = dozeParameters;
         mKeyguardStateController = keyguardStateController;
@@ -176,6 +180,7 @@
         mKeyguardBypassController = keyguardBypassController;
         mBackgroundExecutor = backgroundExecutor;
         mColorExtractor = colorExtractor;
+        mNotificationShadeWindowModel = notificationShadeWindowModel;
         // prefix with {slow} to make sure this dumps at the END of the critical section.
         dumpManager.registerCriticalDumpable("{slow}NotificationShadeWindowControllerImpl", this);
         mAuthController = authController;
@@ -329,6 +334,14 @@
                 mCommunalInteractor.get().isCommunalVisible(),
                 this::onCommunalVisibleChanged
         );
+
+        if (!SceneContainerFlag.isEnabled() && Flags.useTransitionsForKeyguardOccluded()) {
+            collectFlow(
+                    mWindowRootView,
+                    mNotificationShadeWindowModel.isKeyguardOccluded(),
+                    this::setKeyguardOccluded
+            );
+        }
     }
 
     @Override
@@ -341,6 +354,11 @@
         mScreenBrightnessDoze = value / 255f;
     }
 
+    @Override
+    public void setDozeScreenBrightnessFloat(float value) {
+        mScreenBrightnessDoze = value;
+    }
+
     private void setKeyguardDark(boolean dark) {
         int vis = mWindowRootView.getSystemUiVisibility();
         if (dark) {
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/education/GestureType.kt b/packages/SystemUI/src/com/android/systemui/shade/QSHeaderBoundsProvider.kt
similarity index 67%
copy from packages/SystemUI/shared/src/com/android/systemui/shared/education/GestureType.kt
copy to packages/SystemUI/src/com/android/systemui/shade/QSHeaderBoundsProvider.kt
index 9a5c77a..a447e55 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/education/GestureType.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/QSHeaderBoundsProvider.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2024 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,8 +14,12 @@
  * limitations under the License.
  */
 
-package com.android.systemui.shared.education
+package com.android.systemui.shade
 
-enum class GestureType {
-    BACK_GESTURE,
-}
+import android.graphics.Rect
+
+class QSHeaderBoundsProvider(
+    val leftProvider: () -> Int,
+    val heightProvider: () -> Int,
+    val boundsOnScreenProvider: (Rect) -> Unit,
+)
diff --git a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java
index bd08685..9f61d4e 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java
@@ -43,7 +43,6 @@
 import android.util.MathUtils;
 import android.view.MotionEvent;
 import android.view.VelocityTracker;
-import android.view.View;
 import android.view.ViewConfiguration;
 import android.view.ViewGroup;
 import android.view.WindowInsets;
@@ -75,6 +74,7 @@
 import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager;
 import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.qs.QS;
+import com.android.systemui.qs.flags.QSComposeFragment;
 import com.android.systemui.res.R;
 import com.android.systemui.scene.shared.flag.SceneContainerFlag;
 import com.android.systemui.screenrecord.RecordingController;
@@ -110,6 +110,8 @@
 
 import dagger.Lazy;
 
+import kotlin.Unit;
+
 import java.io.PrintWriter;
 
 import javax.inject.Inject;
@@ -494,7 +496,15 @@
     }
 
     int getHeaderHeight() {
-        return isQsFragmentCreated() ? mQs.getHeader().getHeight() : 0;
+        if (isQsFragmentCreated()) {
+            if (QSComposeFragment.isEnabled()) {
+                return mQs.getHeaderHeight();
+            } else {
+                return mQs.getHeader().getHeight();
+            }
+        } else {
+            return 0;
+        }
     }
 
     private boolean isRemoteInputActiveWithKeyboardUp() {
@@ -664,14 +674,26 @@
                 && mKeyguardBypassController.getBypassEnabled()) || mSplitShadeEnabled) {
             return false;
         }
-        View header = keyguardShowing || mQs == null ? mKeyguardStatusBar : mQs.getHeader();
+        int headerTop, headerBottom;
+        if (keyguardShowing || mQs == null) {
+            headerTop = mKeyguardStatusBar.getTop();
+            headerBottom = mKeyguardStatusBar.getBottom();
+        } else {
+            if (QSComposeFragment.isEnabled()) {
+                headerTop = mQs.getHeaderTop();
+                headerBottom = mQs.getHeaderBottom();
+            } else {
+                headerTop = mQs.getHeader().getTop();
+                headerBottom = mQs.getHeader().getBottom();
+            }
+        }
         int frameTop = keyguardShowing
                 || mQs == null ? 0 : mQsFrame.getTop();
         mInterceptRegion.set(
                 /* left= */ (int) mQsFrame.getX(),
-                /* top= */ header.getTop() + frameTop,
+                /* top= */ headerTop + frameTop,
                 /* right= */ (int) mQsFrame.getX() + mQsFrame.getWidth(),
-                /* bottom= */ header.getBottom() + frameTop);
+                /* bottom= */ headerBottom + frameTop);
         // Also allow QS to intercept if the touch is near the notch.
         mStatusBarTouchableRegionManager.updateRegionForNotch(mInterceptRegion);
         final boolean onHeader = mInterceptRegion.contains((int) x, (int) y);
@@ -718,9 +740,18 @@
         if (mCollapsedOnDown || mBarState == KEYGUARD || getExpanded()) {
             return false;
         }
-        View header = mQs == null ? mKeyguardStatusBar : mQs.getHeader();
+        int headerBottom;
+        if (mQs == null) {
+            headerBottom = mKeyguardStatusBar.getBottom();
+        } else {
+            if (QSComposeFragment.isEnabled()) {
+                headerBottom = mQs.getHeaderBottom();
+            } else {
+                headerBottom = mQs.getHeader().getBottom();
+            }
+        }
         return downX >= mQsFrame.getX() && downX <= mQsFrame.getX() + mQsFrame.getWidth()
-                && downY <= header.getBottom();
+                && downY <= headerBottom;
     }
 
     /** Closes the Qs customizer. */
@@ -789,6 +820,9 @@
 
     /** update Qs height state */
     void setExpansionHeight(float height) {
+        if (mExpansionHeight == height) {
+            return;
+        }
         int maxHeight = getMaxExpansionHeight();
         height = Math.min(Math.max(
                 height, getMinExpansionHeight()), maxHeight);
@@ -2189,14 +2223,27 @@
                         }
                     });
             mQs.setCollapsedMediaVisibilityChangedListener((visible) -> {
-                if (mQs.getHeader().isShown()) {
+                if (mQs.isHeaderShown()) {
                     setAnimateNextNotificationBounds(
                             StackStateAnimator.ANIMATION_DURATION_STANDARD, 0);
                     mNotificationStackScrollLayoutController.animateNextTopPaddingChange();
                 }
             });
             mLockscreenShadeTransitionController.setQS(mQs);
-            mNotificationStackScrollLayoutController.setQsHeader((ViewGroup) mQs.getHeader());
+            if (QSComposeFragment.isEnabled()) {
+                QSHeaderBoundsProvider provider = new QSHeaderBoundsProvider(
+                        mQs::getHeaderLeft,
+                        mQs::getHeaderHeight,
+                        rect -> {
+                            mQs.getHeaderBoundsOnScreen(rect);
+                            return Unit.INSTANCE;
+                        }
+                );
+
+                mNotificationStackScrollLayoutController.setQsHeaderBoundsProvider(provider);
+            } else {
+                mNotificationStackScrollLayoutController.setQsHeader((ViewGroup) mQs.getHeader());
+            }
             mQs.setScrollListener(mQsScrollListener);
             updateExpansion();
         }
@@ -2208,6 +2255,9 @@
             // non-fragment and fragment code. Once we are using a fragment for the notification
             // panel, mQs will not need to be null cause it will be tied to the same lifecycle.
             if (fragment == mQs) {
+                // Clear it to remove bindings to mQs from the provider.
+                mNotificationStackScrollLayoutController.setQsHeaderBoundsProvider(null);
+                mNotificationStackScrollLayoutController.setQsHeader(null);
                 mQs = null;
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorLegacyImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorLegacyImpl.kt
index f39ee9a..9e221d3 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorLegacyImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorLegacyImpl.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.shade.domain.interactor
 
+import com.android.app.tracing.FlowTracing.traceAsCounter
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.keyguard.data.repository.KeyguardRepository
@@ -74,6 +75,7 @@
                 }
             }
             .distinctUntilChanged()
+            .traceAsCounter("panel_expansion") { (it * 100f).toInt() }
             .stateIn(scope, SharingStarted.Eagerly, 0f)
 
     override val qsExpansion: StateFlow<Float> = repository.qsExpansion
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImpl.kt
index b2142a5..9617b54 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImpl.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.shade.domain.interactor
 
+import com.android.app.tracing.FlowTracing.traceAsCounter
 import com.android.compose.animation.scene.ObservableTransitionState
 import com.android.compose.animation.scene.SceneKey
 import com.android.systemui.dagger.SysUISingleton
@@ -46,6 +47,7 @@
 ) : BaseShadeInteractor {
     override val shadeExpansion: StateFlow<Float> =
         sceneBasedExpansion(sceneInteractor, SceneFamilies.NotifShade)
+            .traceAsCounter("panel_expansion") { (it * 100f).toInt() }
             .stateIn(scope, SharingStarted.Eagerly, 0f)
 
     private val sceneBasedQsExpansion =
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/startable/ShadeStartable.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/startable/ShadeStartable.kt
index 5eb3a1c..330f53f 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/startable/ShadeStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/startable/ShadeStartable.kt
@@ -18,6 +18,7 @@
 
 import android.content.Context
 import com.android.systemui.CoreStartable
+import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor
 import com.android.systemui.common.ui.data.repository.ConfigurationRepository
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
@@ -32,6 +33,7 @@
 import com.android.systemui.shade.transition.ScrimShadeTransitionController
 import com.android.systemui.statusbar.PulseExpansionHandler
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
+import com.android.systemui.statusbar.phone.ScrimController
 import com.android.systemui.statusbar.policy.SplitShadeStateController
 import javax.inject.Inject
 import javax.inject.Provider
@@ -56,11 +58,14 @@
     private val panelExpansionInteractorProvider: Provider<PanelExpansionInteractor>,
     private val shadeExpansionStateManager: ShadeExpansionStateManager,
     private val pulseExpansionHandler: PulseExpansionHandler,
+    private val displayStateInteractor: DisplayStateInteractor,
     private val nsslc: NotificationStackScrollLayoutController,
+    private val scrimController: ScrimController,
 ) : CoreStartable {
 
     override fun start() {
         hydrateShadeLayoutWidth()
+        hydrateFullWidth()
         hydrateShadeExpansionStateManager()
         logTouchesTo(touchLog)
         scrimShadeTransitionController.init()
@@ -98,4 +103,16 @@
                 }
         }
     }
+
+    private fun hydrateFullWidth() {
+        if (SceneContainerFlag.isEnabled) {
+            applicationScope.launch {
+                displayStateInteractor.isLargeScreen.collect {
+                    val isFullWidth = !it
+                    nsslc.setIsFullWidth(isFullWidth)
+                    scrimController.setClipsQsScrim(isFullWidth)
+                }
+            }
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/NotificationShadeWindowModel.kt b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/NotificationShadeWindowModel.kt
new file mode 100644
index 0000000..e1289af
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/NotificationShadeWindowModel.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.shade.ui.viewmodel
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.map
+
+/** Models UI state for the shade window. */
+@SysUISingleton
+class NotificationShadeWindowModel
+@Inject
+constructor(
+    keyguardTransitionInteractor: KeyguardTransitionInteractor,
+) {
+    val isKeyguardOccluded: Flow<Boolean> =
+        keyguardTransitionInteractor.transitionValue(OCCLUDED).map { it == 1f }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModel.kt b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModel.kt
index 2b2aac64..f90dd3c 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModel.kt
@@ -24,8 +24,8 @@
 import com.android.compose.animation.scene.SwipeDirection
 import com.android.compose.animation.scene.UserAction
 import com.android.compose.animation.scene.UserActionResult
+import com.android.systemui.activatable.Activatable
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.media.controls.domain.pipeline.interactor.MediaCarouselInteractor
 import com.android.systemui.qs.FooterActionsController
 import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel
@@ -41,22 +41,23 @@
 import com.android.systemui.utils.coroutines.flow.flatMapLatestConflated
 import java.util.concurrent.atomic.AtomicBoolean
 import javax.inject.Inject
-import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.coroutineScope
 import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.collectLatest
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.launch
 
 /** Models UI state and handles user input for the shade scene. */
 @SysUISingleton
 class ShadeSceneViewModel
 @Inject
 constructor(
-    @Application private val applicationScope: CoroutineScope,
     val qsSceneAdapter: QSSceneAdapter,
     val shadeHeaderViewModel: ShadeHeaderViewModel,
     val brightnessMirrorViewModel: BrightnessMirrorViewModel,
@@ -66,42 +67,61 @@
     private val footerActionsController: FooterActionsController,
     private val sceneInteractor: SceneInteractor,
     private val unfoldTransitionInteractor: UnfoldTransitionInteractor,
-) {
-    val destinationScenes: StateFlow<Map<UserAction, UserActionResult>> =
+) : Activatable {
+    val destinationScenes: Flow<Map<UserAction, UserActionResult>> =
         combine(
-                shadeInteractor.shadeMode,
-                qsSceneAdapter.isCustomizerShowing,
-            ) { shadeMode, isCustomizerShowing ->
-                destinationScenes(
-                    shadeMode = shadeMode,
-                    isCustomizing = isCustomizerShowing,
-                )
+            shadeInteractor.shadeMode,
+            qsSceneAdapter.isCustomizerShowing,
+        ) { shadeMode, isCustomizerShowing ->
+            buildMap {
+                if (!isCustomizerShowing) {
+                    set(
+                        Swipe(SwipeDirection.Up),
+                        UserActionResult(
+                            SceneFamilies.Home,
+                            ToSplitShade.takeIf { shadeMode is ShadeMode.Split }
+                        )
+                    )
+                }
+
+                // TODO(b/330200163) Add an else to be able to collapse the shade while customizing
+                if (shadeMode is ShadeMode.Single) {
+                    set(Swipe(SwipeDirection.Down), UserActionResult(Scenes.QuickSettings))
+                }
             }
-            .stateIn(
-                scope = applicationScope,
-                started = SharingStarted.WhileSubscribed(),
-                initialValue =
-                    destinationScenes(
-                        shadeMode = shadeInteractor.shadeMode.value,
-                        isCustomizing = qsSceneAdapter.isCustomizerShowing.value,
-                    ),
-            )
+        }
 
     private val upDestinationSceneKey: Flow<SceneKey?> =
         destinationScenes.map { it[Swipe(SwipeDirection.Up)]?.toScene }
 
+    private val _isClickable = MutableStateFlow(false)
     /** Whether or not the shade container should be clickable. */
-    val isClickable: StateFlow<Boolean> =
-        upDestinationSceneKey
-            .flatMapLatestConflated { key ->
-                key?.let { sceneInteractor.resolveSceneFamily(key) } ?: flowOf(null)
+    val isClickable: StateFlow<Boolean> = _isClickable.asStateFlow()
+
+    /**
+     * Activates the view-model.
+     *
+     * Serves as an entrypoint to kick off coroutine work that the view-model requires in order to
+     * keep its state fresh and/or perform side-effects.
+     *
+     * Suspends the caller forever as it will keep doing work until canceled.
+     *
+     * **Must be invoked** when the scene becomes the current scene or when it becomes visible
+     * during a transition (the choice is the responsibility of the parent). Similarly, the work
+     * must be canceled when the scene stops being visible or the current scene.
+     */
+    override suspend fun activate() {
+        coroutineScope {
+            launch {
+                upDestinationSceneKey
+                    .flatMapLatestConflated { key ->
+                        key?.let { sceneInteractor.resolveSceneFamily(key) } ?: flowOf(null)
+                    }
+                    .map { it == Scenes.Lockscreen }
+                    .collectLatest { _isClickable.value = it }
             }
-            .map { it == Scenes.Lockscreen }
-            .stateIn(
-                scope = applicationScope,
-                started = SharingStarted.WhileSubscribed(),
-                initialValue = false
-            )
+        }
+    }
 
     val shadeMode: StateFlow<ShadeMode> = shadeInteractor.shadeMode
 
@@ -132,24 +152,4 @@
         }
         return footerActionsViewModelFactory.create(lifecycleOwner)
     }
-
-    private fun destinationScenes(
-        shadeMode: ShadeMode,
-        isCustomizing: Boolean,
-    ): Map<UserAction, UserActionResult> {
-        return buildMap {
-            if (!isCustomizing) {
-                set(
-                    Swipe(SwipeDirection.Up),
-                    UserActionResult(
-                        SceneFamilies.Home,
-                        ToSplitShade.takeIf { shadeMode is ShadeMode.Split }
-                    )
-                )
-            } // TODO(b/330200163) Add an else to be able to collapse the shade while customizing
-            if (shadeMode is ShadeMode.Single) {
-                set(Swipe(SwipeDirection.Down), UserActionResult(Scenes.QuickSettings))
-            }
-        }
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index c912616..cea97d6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -51,6 +51,7 @@
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.UserHandle;
+import android.os.UserManager;
 import android.util.Pair;
 import android.util.SparseArray;
 import android.view.KeyEvent;
@@ -200,6 +201,7 @@
      * event.
      */
     private int mLastUpdatedImeDisplayId = INVALID_DISPLAY;
+    private final Context mContext;
     private final DisplayTracker mDisplayTracker;
     private final @Nullable CommandRegistry mRegistry;
     private final @Nullable DumpHandler mDumpHandler;
@@ -255,12 +257,11 @@
          * Called to notify IME window status changes.
          *
          * @param displayId The id of the display to notify.
-         * @param token IME token.
          * @param vis IME visibility.
          * @param backDisposition Disposition mode of back button. It should be one of below flags:
          * @param showImeSwitcher {@code true} to show IME switch button.
          */
-        default void setImeWindowStatus(int displayId, IBinder token,  int vis,
+        default void setImeWindowStatus(int displayId, int vis,
                 @BackDispositionMode int backDisposition, boolean showImeSwitcher) { }
         default void showRecentApps(boolean triggeredFromAltTab) { }
         default void hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) { }
@@ -571,6 +572,7 @@
             DumpHandler dumpHandler,
             Lazy<PowerInteractor> powerInteractor
     ) {
+        mContext = context;
         mDisplayTracker = displayTracker;
         mRegistry = registry;
         mDumpHandler = dumpHandler;
@@ -742,7 +744,7 @@
     }
 
     @Override
-    public void setImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition,
+    public void setImeWindowStatus(int displayId, int vis, int backDisposition,
             boolean showImeSwitcher) {
         synchronized (mLock) {
             mHandler.removeMessages(MSG_SHOW_IME_BUTTON);
@@ -751,7 +753,6 @@
             args.argi2 = vis;
             args.argi3 = backDisposition;
             args.argi4 = showImeSwitcher ? 1 : 0;
-            args.arg1 = token;
             Message m = mHandler.obtainMessage(MSG_SHOW_IME_BUTTON, args);
             m.sendToTarget();
         }
@@ -1205,28 +1206,31 @@
         }
     }
 
-    private void handleShowImeButton(int displayId, IBinder token, int vis, int backDisposition,
+    private void handleShowImeButton(int displayId, int vis, int backDisposition,
             boolean showImeSwitcher) {
         if (displayId == INVALID_DISPLAY) return;
 
-        if (mLastUpdatedImeDisplayId != displayId
+        boolean isConcurrentMultiUserModeEnabled = UserManager.isVisibleBackgroundUsersEnabled()
+                && mContext.getResources().getBoolean(android.R.bool.config_perDisplayFocusEnabled)
+                && android.view.inputmethod.Flags.concurrentInputMethods();
+
+        if (!isConcurrentMultiUserModeEnabled
+                && mLastUpdatedImeDisplayId != displayId
                 && mLastUpdatedImeDisplayId != INVALID_DISPLAY) {
             // Set previous NavBar's IME window status as invisible when IME
             // window switched to another display for single-session IME case.
             sendImeInvisibleStatusForPrevNavBar();
         }
         for (int i = 0; i < mCallbacks.size(); i++) {
-            mCallbacks.get(i).setImeWindowStatus(displayId, token, vis, backDisposition,
-                    showImeSwitcher);
+            mCallbacks.get(i).setImeWindowStatus(displayId, vis, backDisposition, showImeSwitcher);
         }
         mLastUpdatedImeDisplayId = displayId;
     }
 
     private void sendImeInvisibleStatusForPrevNavBar() {
         for (int i = 0; i < mCallbacks.size(); i++) {
-            mCallbacks.get(i).setImeWindowStatus(mLastUpdatedImeDisplayId,
-                    null /* token */, IME_INVISIBLE, BACK_DISPOSITION_DEFAULT,
-                    false /* showImeSwitcher */);
+            mCallbacks.get(i).setImeWindowStatus(mLastUpdatedImeDisplayId, IME_INVISIBLE,
+                    BACK_DISPOSITION_DEFAULT, false /* showImeSwitcher */);
         }
     }
 
@@ -1542,7 +1546,7 @@
                     break;
                 case MSG_SHOW_IME_BUTTON:
                     args = (SomeArgs) msg.obj;
-                    handleShowImeButton(args.argi1 /* displayId */, (IBinder) args.arg1 /* token */,
+                    handleShowImeButton(args.argi1 /* displayId */,
                             args.argi2 /* vis */, args.argi3 /* backDisposition */,
                             args.argi4 != 0 /* showImeSwitcher */);
                     break;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java
index 707d59aa..85fad42 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java
@@ -131,10 +131,20 @@
     /** Sets the state of whether the remote input is active or not. */
     default void onRemoteInputActive(boolean remoteInputActive) {}
 
-    /** Sets the screen brightness level for when the device is dozing. */
+    /**
+     * Sets the screen brightness level for when the device is dozing.
+     * @param value The brightness value between 1 and 255
+     */
     default void setDozeScreenBrightness(int value) {}
 
     /**
+     * Sets the screen brightness level for when the device is dozing.
+     * @param value The brightness value between {@link PowerManager#BRIGHTNESS_MIN} and
+     * {@link PowerManager#BRIGHTNESS_MAX}
+     */
+    default void setDozeScreenBrightnessFloat(float value) {}
+
+    /**
      * Sets whether the screen brightness is forced to the value we use for doze mode by the status
      * bar window. No-op if the device does not support dozing.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/OWNERS b/packages/SystemUI/src/com/android/systemui/statusbar/OWNERS
new file mode 100644
index 0000000..69ebb76
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/OWNERS
@@ -0,0 +1,7 @@
+set noparent
+
+# Bug component: 78010
+
+caitlinshk@google.com
+evanlaird@google.com
+pixel@google.com
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModel.kt
index 59fd0ca..18ea0b4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModel.kt
@@ -18,6 +18,7 @@
 
 import android.view.View
 import com.android.internal.jank.InteractionJankMonitor
+import com.android.systemui.Flags
 import com.android.systemui.animation.ActivityTransitionAnimator
 import com.android.systemui.common.shared.model.ContentDescription
 import com.android.systemui.common.shared.model.Icon
@@ -59,12 +60,24 @@
                 when (state) {
                     is OngoingCallModel.NoCall -> OngoingActivityChipModel.Hidden()
                     is OngoingCallModel.InCall -> {
+                        val icon =
+                            if (
+                                Flags.statusBarCallChipNotificationIcon() &&
+                                    state.notificationIconView != null
+                            ) {
+                                OngoingActivityChipModel.ChipIcon.StatusBarView(
+                                    state.notificationIconView
+                                )
+                            } else {
+                                OngoingActivityChipModel.ChipIcon.Basic(phoneIcon)
+                            }
+
                         // This block mimics OngoingCallController#updateChip.
                         if (state.startTimeMs <= 0L) {
                             // If the start time is invalid, don't show a timer and show just an
                             // icon. See b/192379214.
                             OngoingActivityChipModel.Shown.IconOnly(
-                                icon = phoneIcon,
+                                icon = icon,
                                 colors = ColorsModel.Themed,
                                 getOnClickListener(state),
                             )
@@ -73,7 +86,7 @@
                                 state.startTimeMs - systemClock.currentTimeMillis() +
                                     systemClock.elapsedRealtime()
                             OngoingActivityChipModel.Shown.Timer(
-                                icon = phoneIcon,
+                                icon = icon,
                                 colors = ColorsModel.Themed,
                                 startTimeMs = startTimeInElapsedRealtime,
                                 getOnClickListener(state),
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModel.kt
index d9b0504..cf4e707 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModel.kt
@@ -190,12 +190,14 @@
     ): OngoingActivityChipModel.Shown {
         return OngoingActivityChipModel.Shown.Timer(
             icon =
-                Icon.Resource(
-                    CAST_TO_OTHER_DEVICE_ICON,
-                    // This string is "Casting screen"
-                    ContentDescription.Resource(
-                        R.string.cast_screen_to_other_device_chip_accessibility_label,
-                    ),
+                OngoingActivityChipModel.ChipIcon.Basic(
+                    Icon.Resource(
+                        CAST_TO_OTHER_DEVICE_ICON,
+                        // This string is "Casting screen"
+                        ContentDescription.Resource(
+                            R.string.cast_screen_to_other_device_chip_accessibility_label,
+                        ),
+                    )
                 ),
             colors = ColorsModel.Red,
             // TODO(b/332662551): Maybe use a MediaProjection API to fetch this time.
@@ -213,10 +215,12 @@
     private fun createIconOnlyCastChip(deviceName: String?): OngoingActivityChipModel.Shown {
         return OngoingActivityChipModel.Shown.IconOnly(
             icon =
-                Icon.Resource(
-                    CAST_TO_OTHER_DEVICE_ICON,
-                    // This string is just "Casting"
-                    ContentDescription.Resource(R.string.accessibility_casting),
+                OngoingActivityChipModel.ChipIcon.Basic(
+                    Icon.Resource(
+                        CAST_TO_OTHER_DEVICE_ICON,
+                        // This string is just "Casting"
+                        ContentDescription.Resource(R.string.accessibility_casting),
+                    )
                 ),
             colors = ColorsModel.Red,
             createDialogLaunchOnClickListener(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractor.kt
index 191c221..c5f78d2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractor.kt
@@ -21,11 +21,11 @@
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.log.LogBuffer
 import com.android.systemui.log.core.LogLevel
+import com.android.systemui.mediaprojection.MediaProjectionUtils.packageHasCastingCapabilities
 import com.android.systemui.mediaprojection.data.model.MediaProjectionState
 import com.android.systemui.mediaprojection.data.repository.MediaProjectionRepository
 import com.android.systemui.statusbar.chips.StatusBarChipsLog
 import com.android.systemui.statusbar.chips.mediaprojection.domain.model.ProjectionChipModel
-import com.android.systemui.util.Utils
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.SharingStarted
@@ -60,7 +60,7 @@
                     }
                     is MediaProjectionState.Projecting -> {
                         val type =
-                            if (isProjectionToOtherDevice(state.hostPackage)) {
+                            if (packageHasCastingCapabilities(packageManager, state.hostPackage)) {
                                 ProjectionChipModel.Type.CAST_TO_OTHER_DEVICE
                             } else {
                                 ProjectionChipModel.Type.SHARE_TO_APP
@@ -86,19 +86,6 @@
         scope.launch { mediaProjectionRepository.stopProjecting() }
     }
 
-    /**
-     * Returns true iff projecting to the given [packageName] means that we're projecting to a
-     * *different* device (as opposed to projecting to some application on *this* device).
-     */
-    private fun isProjectionToOtherDevice(packageName: String?): Boolean {
-        // The [isHeadlessRemoteDisplayProvider] check approximates whether a projection is to a
-        // different device or the same device, because headless remote display packages are the
-        // only kinds of packages that do cast-to-other-device. This isn't exactly perfect,
-        // because it means that any projection by those headless remote display packages will be
-        // marked as going to a different device, even if that isn't always true. See b/321078669.
-        return Utils.isHeadlessRemoteDisplayProvider(packageManager, packageName)
-    }
-
     companion object {
         private const val TAG = "MediaProjection"
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModel.kt
index fcf3de4..6ba4fef 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModel.kt
@@ -78,11 +78,13 @@
                     is ScreenRecordChipModel.Recording -> {
                         OngoingActivityChipModel.Shown.Timer(
                             icon =
-                                Icon.Resource(
-                                    ICON,
-                                    ContentDescription.Resource(
-                                        R.string.screenrecord_ongoing_screen_only,
-                                    ),
+                                OngoingActivityChipModel.ChipIcon.Basic(
+                                    Icon.Resource(
+                                        ICON,
+                                        ContentDescription.Resource(
+                                            R.string.screenrecord_ongoing_screen_only,
+                                        ),
+                                    )
                                 ),
                             colors = ColorsModel.Red,
                             startTimeMs = systemClock.elapsedRealtime(),
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModel.kt
index 85973fc..7897f93 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModel.kt
@@ -110,9 +110,11 @@
     ): OngoingActivityChipModel.Shown {
         return OngoingActivityChipModel.Shown.Timer(
             icon =
-                Icon.Resource(
-                    SHARE_TO_APP_ICON,
-                    ContentDescription.Resource(R.string.share_to_app_chip_accessibility_label),
+                OngoingActivityChipModel.ChipIcon.Basic(
+                    Icon.Resource(
+                        SHARE_TO_APP_ICON,
+                        ContentDescription.Resource(R.string.share_to_app_chip_accessibility_label),
+                    )
                 ),
             colors = ColorsModel.Red,
             // TODO(b/332662551): Maybe use a MediaProjection API to fetch this time.
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 17cf60b..26a2f91 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
@@ -17,7 +17,9 @@
 package com.android.systemui.statusbar.chips.ui.model
 
 import android.view.View
+import com.android.systemui.Flags
 import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.statusbar.StatusBarIconView
 
 /** Model representing the display of an ongoing activity as a chip in the status bar. */
 sealed class OngoingActivityChipModel {
@@ -38,7 +40,7 @@
     /** This chip should be shown with the given information. */
     abstract class Shown(
         /** The icon to show on the chip. If null, no icon will be shown. */
-        open val icon: Icon?,
+        open val icon: ChipIcon?,
         /** What colors to use for the chip. */
         open val colors: ColorsModel,
         /**
@@ -50,7 +52,7 @@
 
         /** This chip shows only an icon and nothing else. */
         data class IconOnly(
-            override val icon: Icon,
+            override val icon: ChipIcon,
             override val colors: ColorsModel,
             override val onClickListener: View.OnClickListener?,
         ) : Shown(icon, colors, onClickListener) {
@@ -59,7 +61,7 @@
 
         /** The chip shows a timer, counting up from [startTimeMs]. */
         data class Timer(
-            override val icon: Icon,
+            override val icon: ChipIcon,
             override val colors: ColorsModel,
             /**
              * The time this event started, used to show the timer.
@@ -88,4 +90,23 @@
             override val logName = "Shown.Countdown"
         }
     }
+
+    /** Represents an icon to show on the chip. */
+    sealed interface ChipIcon {
+        /**
+         * The icon is a custom icon, which is set on [impl]. The icon was likely created by an
+         * external app.
+         */
+        data class StatusBarView(val impl: StatusBarIconView) : ChipIcon {
+            init {
+                check(Flags.statusBarCallChipNotificationIcon()) {
+                    "OngoingActivityChipModel.ChipIcon.StatusBarView created even though " +
+                        "Flags.statusBarCallChipNotificationIcon is not enabled"
+                }
+            }
+        }
+
+        /** The icon is a basic resource or drawable icon that System UI created internally. */
+        data class Basic(val impl: Icon) : ChipIcon
+    }
 }
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 85f4c36..ecb6d7f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt
@@ -17,12 +17,17 @@
 package com.android.systemui.statusbar.dagger
 
 import com.android.systemui.CoreStartable
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.LogBufferFactory
 import com.android.systemui.statusbar.data.StatusBarDataLayerModule
 import com.android.systemui.statusbar.phone.LightBarController
 import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController
+import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallLog
 import com.android.systemui.statusbar.ui.SystemBarUtilsProxyImpl
 import dagger.Binds
 import dagger.Module
+import dagger.Provides
 import dagger.multibindings.ClassKey
 import dagger.multibindings.IntoMap
 
@@ -45,4 +50,13 @@
     @IntoMap
     @ClassKey(LightBarController::class)
     abstract fun bindLightBarController(impl: LightBarController): CoreStartable
+
+    companion object {
+        @Provides
+        @SysUISingleton
+        @OngoingCallLog
+        fun provideOngoingCallLogBuffer(factory: LogBufferFactory): LogBuffer {
+            return factory.create("OngoingCall", 75)
+        }
+    }
 }
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 aff57bd..e50d64b 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
@@ -50,10 +50,10 @@
 import javax.inject.Inject
 
 /**
- * Coordinates heads up notification (HUN) interactions with the notification pipeline based on
- * the HUN state reported by the [HeadsUpManager]. In this class we only consider one
- * notification, in particular the [HeadsUpManager.getTopEntry], to be HeadsUpping at a
- * time even though other notifications may be queued to heads up next.
+ * Coordinates heads up notification (HUN) interactions with the notification pipeline based on the
+ * HUN state reported by the [HeadsUpManager]. In this class we only consider one notification, in
+ * particular the [HeadsUpManager.getTopEntry], to be HeadsUpping at a time even though other
+ * notifications may be queued to heads up next.
  *
  * The current HUN, but not HUNs that are queued to heads up, will be:
  * - Lifetime extended until it's no longer heads upping.
@@ -64,7 +64,9 @@
  * Note: The inflation callback in [PreparationCoordinator] handles showing HUNs.
  */
 @CoordinatorScope
-class HeadsUpCoordinator @Inject constructor(
+class HeadsUpCoordinator
+@Inject
+constructor(
     private val mLogger: HeadsUpCoordinatorLogger,
     private val mSystemClock: SystemClock,
     private val mHeadsUpManager: HeadsUpManager,
@@ -104,8 +106,8 @@
     }
 
     /**
-     * Once the pipeline starts running, we can look through posted entries and quickly process
-     * any that don't have groups, and thus will never gave a group heads up edge case.
+     * Once the pipeline starts running, we can look through posted entries and quickly process any
+     * that don't have groups, and thus will never gave a group heads up edge case.
      */
     fun onBeforeTransformGroups(list: List<ListEntry>) {
         mNow = mSystemClock.currentTimeMillis()
@@ -128,120 +130,137 @@
      * we know that stability and [NotifPromoter]s have been applied, so we can use the location of
      * notifications in this list to determine what kind of group heads up behavior should happen.
      */
-    fun onBeforeFinalizeFilter(list: List<ListEntry>) = mHeadsUpManager.modifyHuns { hunMutator ->
-        // Nothing to do if there are no other adds/updates
-        if (mPostedEntries.isEmpty()) {
-            return@modifyHuns
-        }
-        // Calculate a bunch of information about the logical group and the locations of group
-        // entries in the nearly-finalized shade list.  These may be used in the per-group loop.
-        val postedEntriesByGroup = mPostedEntries.values.groupBy { it.entry.sbn.groupKey }
-        val logicalMembersByGroup = mNotifPipeline.allNotifs.asSequence()
-            .filter { postedEntriesByGroup.contains(it.sbn.groupKey) }
-            .groupBy { it.sbn.groupKey }
-        val groupLocationsByKey: Map<String, GroupLocation> by lazy { getGroupLocationsByKey(list) }
-        mLogger.logEvaluatingGroups(postedEntriesByGroup.size)
-        // For each group, determine which notification(s) for a group should heads up.
-        postedEntriesByGroup.forEach { (groupKey, postedEntries) ->
-            // get and classify the logical members
-            val logicalMembers = logicalMembersByGroup[groupKey] ?: emptyList()
-            val logicalSummary = logicalMembers.find { it.sbn.notification.isGroupSummary }
+    fun onBeforeFinalizeFilter(list: List<ListEntry>) =
+        mHeadsUpManager.modifyHuns { hunMutator ->
+            // Nothing to do if there are no other adds/updates
+            if (mPostedEntries.isEmpty()) {
+                return@modifyHuns
+            }
+            // Calculate a bunch of information about the logical group and the locations of group
+            // entries in the nearly-finalized shade list.  These may be used in the per-group loop.
+            val postedEntriesByGroup = mPostedEntries.values.groupBy { it.entry.sbn.groupKey }
+            val logicalMembersByGroup =
+                mNotifPipeline.allNotifs
+                    .asSequence()
+                    .filter { postedEntriesByGroup.contains(it.sbn.groupKey) }
+                    .groupBy { it.sbn.groupKey }
+            val groupLocationsByKey: Map<String, GroupLocation> by lazy {
+                getGroupLocationsByKey(list)
+            }
+            mLogger.logEvaluatingGroups(postedEntriesByGroup.size)
+            // For each group, determine which notification(s) for a group should heads up.
+            postedEntriesByGroup.forEach { (groupKey, postedEntries) ->
+                // get and classify the logical members
+                val logicalMembers = logicalMembersByGroup[groupKey] ?: emptyList()
+                val logicalSummary = logicalMembers.find { it.sbn.notification.isGroupSummary }
 
-            // Report the start of this group's evaluation
-            mLogger.logEvaluatingGroup(groupKey, postedEntries.size, logicalMembers.size)
+                // Report the start of this group's evaluation
+                mLogger.logEvaluatingGroup(groupKey, postedEntries.size, logicalMembers.size)
 
-            // If there is no logical summary, then there is no heads up to transfer
-            if (logicalSummary == null) {
-                postedEntries.forEach {
-                    handlePostedEntry(it, hunMutator, scenario = "logical-summary-missing")
+                // If there is no logical summary, then there is no heads up to transfer
+                if (logicalSummary == null) {
+                    postedEntries.forEach {
+                        handlePostedEntry(it, hunMutator, scenario = "logical-summary-missing")
+                    }
+                    return@forEach
                 }
-                return@forEach
-            }
 
-            // If summary isn't wanted to be heads up, then there is no heads up to transfer
-            if (!isGoingToShowHunStrict(logicalSummary)) {
-                postedEntries.forEach {
-                    handlePostedEntry(it, hunMutator, scenario = "logical-summary-not-heads-up")
+                // If summary isn't wanted to be heads up, then there is no heads up to transfer
+                if (!isGoingToShowHunStrict(logicalSummary)) {
+                    postedEntries.forEach {
+                        handlePostedEntry(it, hunMutator, scenario = "logical-summary-not-heads-up")
+                    }
+                    return@forEach
                 }
-                return@forEach
-            }
 
-            // The group is heads up! Overall goals:
-            //  - Maybe transfer its heads up to a child
-            //  - Also let any/all newly heads up children still heads up
-            var childToReceiveParentHeadsUp: NotificationEntry?
-            var targetType = "undefined"
+                // The group is heads up! Overall goals:
+                //  - Maybe transfer its heads up to a child
+                //  - Also let any/all newly heads up children still heads up
+                var childToReceiveParentHeadsUp: NotificationEntry?
+                var targetType = "undefined"
 
-            // If the parent is heads up, always look at the posted notification with the newest
-            // 'when', and if it is isolated with GROUP_ALERT_SUMMARY, then it should receive the
-            // parent's heads up.
-            childToReceiveParentHeadsUp =
-                findHeadsUpOverride(postedEntries, groupLocationsByKey::getLocation)
-            if (childToReceiveParentHeadsUp != null) {
-                targetType = "headsUpOverride"
-            }
-
-            // If the summary is Detached and we have not picked a receiver of the heads up, then we
-            // need to look for the best child to heads up in place of the summary.
-            val isSummaryAttached = groupLocationsByKey.contains(logicalSummary.key)
-            if (!isSummaryAttached && childToReceiveParentHeadsUp == null) {
+                // If the parent is heads up, always look at the posted notification with the newest
+                // 'when', and if it is isolated with GROUP_ALERT_SUMMARY, then it should receive
+                // the
+                // parent's heads up.
                 childToReceiveParentHeadsUp =
-                    findBestTransferChild(logicalMembers, groupLocationsByKey::getLocation)
+                    findHeadsUpOverride(postedEntries, groupLocationsByKey::getLocation)
                 if (childToReceiveParentHeadsUp != null) {
-                    targetType = "bestChild"
+                    targetType = "headsUpOverride"
                 }
-            }
 
-            // If there is no child to receive the parent heads up, then just handle the posted
-            // entries and return.
-            if (childToReceiveParentHeadsUp == null) {
-                postedEntries.forEach {
-                    handlePostedEntry(it, hunMutator, scenario = "no-transfer-target")
+                // If the summary is Detached and we have not picked a receiver of the heads up,
+                // then we
+                // need to look for the best child to heads up in place of the summary.
+                val isSummaryAttached = groupLocationsByKey.contains(logicalSummary.key)
+                if (!isSummaryAttached && childToReceiveParentHeadsUp == null) {
+                    childToReceiveParentHeadsUp =
+                        findBestTransferChild(logicalMembers, groupLocationsByKey::getLocation)
+                    if (childToReceiveParentHeadsUp != null) {
+                        targetType = "bestChild"
+                    }
                 }
-                return@forEach
-            }
 
-            // At this point we just need to initiate the transfer
-            val summaryUpdate = mPostedEntries[logicalSummary.key]
+                // If there is no child to receive the parent heads up, then just handle the posted
+                // entries and return.
+                if (childToReceiveParentHeadsUp == null) {
+                    postedEntries.forEach {
+                        handlePostedEntry(it, hunMutator, scenario = "no-transfer-target")
+                    }
+                    return@forEach
+                }
 
-            // Because we now know for certain that some child is going to heads up for this summary
-            // (as we have found a child to transfer the heads up to), mark the group as having
-            // interrupted. This will allow us to know in the future that the "should heads up"
-            // state of this group has already been handled, just not via the summary entry itself.
-            logicalSummary.setInterruption()
-            mLogger.logSummaryMarkedInterrupted(logicalSummary.key, childToReceiveParentHeadsUp.key)
+                // At this point we just need to initiate the transfer
+                val summaryUpdate = mPostedEntries[logicalSummary.key]
 
-            // If the summary was not attached, then remove the heads up from the detached summary.
-            // Otherwise we can simply ignore its posted update.
-            if (!isSummaryAttached) {
-                val summaryUpdateForRemoval = summaryUpdate?.also {
-                    it.shouldHeadsUpEver = false
-                } ?: PostedEntry(
-                        logicalSummary,
-                        wasAdded = false,
-                        wasUpdated = false,
-                        shouldHeadsUpEver = false,
-                        shouldHeadsUpAgain = false,
-                        isHeadsUpEntry = mHeadsUpManager.isHeadsUpEntry(logicalSummary.key),
-                        isBinding = isEntryBinding(logicalSummary),
+                // Because we now know for certain that some child is going to heads up for this
+                // summary
+                // (as we have found a child to transfer the heads up to), mark the group as having
+                // interrupted. This will allow us to know in the future that the "should heads up"
+                // state of this group has already been handled, just not via the summary entry
+                // itself.
+                logicalSummary.setInterruption()
+                mLogger.logSummaryMarkedInterrupted(
+                    logicalSummary.key,
+                    childToReceiveParentHeadsUp.key
                 )
-                // If we transfer the heads up notification and the summary isn't even attached,
-                // that means we should ensure the summary is no longer a heads up notification,
-                // so we remove it here.
-                handlePostedEntry(
+
+                // If the summary was not attached, then remove the heads up from the detached
+                // summary.
+                // Otherwise we can simply ignore its posted update.
+                if (!isSummaryAttached) {
+                    val summaryUpdateForRemoval =
+                        summaryUpdate?.also { it.shouldHeadsUpEver = false }
+                            ?: PostedEntry(
+                                logicalSummary,
+                                wasAdded = false,
+                                wasUpdated = false,
+                                shouldHeadsUpEver = false,
+                                shouldHeadsUpAgain = false,
+                                isHeadsUpEntry = mHeadsUpManager.isHeadsUpEntry(logicalSummary.key),
+                                isBinding = isEntryBinding(logicalSummary),
+                            )
+                    // If we transfer the heads up notification and the summary isn't even attached,
+                    // that means we should ensure the summary is no longer a heads up notification,
+                    // so we remove it here.
+                    handlePostedEntry(
                         summaryUpdateForRemoval,
                         hunMutator,
-                        scenario = "detached-summary-remove-heads-up")
-            } else if (summaryUpdate != null) {
-                mLogger.logPostedEntryWillNotEvaluate(
+                        scenario = "detached-summary-remove-heads-up"
+                    )
+                } else if (summaryUpdate != null) {
+                    mLogger.logPostedEntryWillNotEvaluate(
                         summaryUpdate,
-                        reason = "attached-summary-transferred")
-            }
+                        reason = "attached-summary-transferred"
+                    )
+                }
 
-            // Handle all posted entries -- if the child receiving the parent's heads up is in the
-            // list, then set its flags to ensure it heads up.
-            var didHeadsUpChildToReceiveParentHeadsUp = false
-            postedEntries.asSequence()
+                // Handle all posted entries -- if the child receiving the parent's heads up is in
+                // the
+                // list, then set its flags to ensure it heads up.
+                var didHeadsUpChildToReceiveParentHeadsUp = false
+                postedEntries
+                    .asSequence()
                     .filter { it.key != logicalSummary.key }
                     .forEach { postedEntry ->
                         if (childToReceiveParentHeadsUp.key == postedEntry.key) {
@@ -249,44 +268,49 @@
                             postedEntry.shouldHeadsUpEver = true
                             postedEntry.shouldHeadsUpAgain = true
                             handlePostedEntry(
-                                    postedEntry,
-                                    hunMutator,
-                                    scenario = "child-heads-up-transfer-target-$targetType")
+                                postedEntry,
+                                hunMutator,
+                                scenario = "child-heads-up-transfer-target-$targetType"
+                            )
                             didHeadsUpChildToReceiveParentHeadsUp = true
                         } else {
                             handlePostedEntry(
-                                    postedEntry,
-                                    hunMutator,
-                                    scenario = "child-heads-up-non-target")
+                                postedEntry,
+                                hunMutator,
+                                scenario = "child-heads-up-non-target"
+                            )
                         }
                     }
 
-            // If the child receiving the heads up notification was not updated on this tick
-            // (which can happen in a standard heads up transfer scenario), then construct an update
-            // so that we can apply it.
-            if (!didHeadsUpChildToReceiveParentHeadsUp) {
-                val posted = PostedEntry(
-                        childToReceiveParentHeadsUp,
-                        wasAdded = false,
-                        wasUpdated = false,
-                        shouldHeadsUpEver = true,
-                        shouldHeadsUpAgain = true,
-                        isHeadsUpEntry =
+                // If the child receiving the heads up notification was not updated on this tick
+                // (which can happen in a standard heads up transfer scenario), then construct an
+                // update
+                // so that we can apply it.
+                if (!didHeadsUpChildToReceiveParentHeadsUp) {
+                    val posted =
+                        PostedEntry(
+                            childToReceiveParentHeadsUp,
+                            wasAdded = false,
+                            wasUpdated = false,
+                            shouldHeadsUpEver = true,
+                            shouldHeadsUpAgain = true,
+                            isHeadsUpEntry =
                                 mHeadsUpManager.isHeadsUpEntry(childToReceiveParentHeadsUp.key),
-                        isBinding = isEntryBinding(childToReceiveParentHeadsUp),
-                )
-                handlePostedEntry(
+                            isBinding = isEntryBinding(childToReceiveParentHeadsUp),
+                        )
+                    handlePostedEntry(
                         posted,
                         hunMutator,
-                        scenario = "non-posted-child-heads-up-transfer-target-$targetType")
+                        scenario = "non-posted-child-heads-up-transfer-target-$targetType"
+                    )
+                }
             }
-        }
-        // After this method runs, all posted entries should have been handled (or skipped).
-        mPostedEntries.clear()
+            // After this method runs, all posted entries should have been handled (or skipped).
+            mPostedEntries.clear()
 
-        // Also take this opportunity to clean up any stale entry update times
-        cleanUpEntryTimes()
-    }
+            // Also take this opportunity to clean up any stale entry update times
+            cleanUpEntryTimes()
+        }
 
     /**
      * Find the posted child with the newest when, and return it if it is isolated and has
@@ -295,34 +319,38 @@
     private fun findHeadsUpOverride(
         postedEntries: List<PostedEntry>,
         locationLookupByKey: (String) -> GroupLocation,
-    ): NotificationEntry? = postedEntries.asSequence()
-        .filter { posted -> !posted.entry.sbn.notification.isGroupSummary }
-        .sortedBy { posted ->
-            -posted.entry.sbn.notification.getWhen()
-        }
-        .firstOrNull()
-        ?.let { posted ->
-            posted.entry.takeIf { entry ->
-                locationLookupByKey(entry.key) == GroupLocation.Isolated &&
+    ): NotificationEntry? =
+        postedEntries
+            .asSequence()
+            .filter { posted -> !posted.entry.sbn.notification.isGroupSummary }
+            .sortedBy { posted -> -posted.entry.sbn.notification.getWhen() }
+            .firstOrNull()
+            ?.let { posted ->
+                posted.entry.takeIf { entry ->
+                    locationLookupByKey(entry.key) == GroupLocation.Isolated &&
                         entry.sbn.notification.groupAlertBehavior == GROUP_ALERT_SUMMARY
+                }
             }
-        }
 
     /**
-     * Of children which are attached, look for the child to receive the notification:
-     * First prefer children which were updated, then looking for the ones with the newest 'when'
+     * Of children which are attached, look for the child to receive the notification: First prefer
+     * children which were updated, then looking for the ones with the newest 'when'
      */
     private fun findBestTransferChild(
         logicalMembers: List<NotificationEntry>,
         locationLookupByKey: (String) -> GroupLocation,
-    ): NotificationEntry? = logicalMembers.asSequence()
-        .filter { !it.sbn.notification.isGroupSummary }
-        .filter { locationLookupByKey(it.key) != GroupLocation.Detached }
-        .sortedWith(compareBy(
-            { !mPostedEntries.contains(it.key) },
-            { -it.sbn.notification.getWhen() },
-        ))
-        .firstOrNull()
+    ): NotificationEntry? =
+        logicalMembers
+            .asSequence()
+            .filter { !it.sbn.notification.isGroupSummary }
+            .filter { locationLookupByKey(it.key) != GroupLocation.Detached }
+            .sortedWith(
+                compareBy(
+                    { !mPostedEntries.contains(it.key) },
+                    { -it.sbn.notification.getWhen() },
+                )
+            )
+            .firstOrNull()
 
     private fun getGroupLocationsByKey(list: List<ListEntry>): Map<String, GroupLocation> =
         mutableMapOf<String, GroupLocation>().also { map ->
@@ -387,197 +415,217 @@
         mHeadsUpViewBinder.bindHeadsUpView(posted.entry, this::onHeadsUpViewBound)
     }
 
-    private val mNotifCollectionListener = object : NotifCollectionListener {
-        /**
-         * Notification was just added and if it should heads up, bind the view and then show it.
-         */
-        override fun onEntryAdded(entry: NotificationEntry) {
-            // First check whether this notification should launch a full screen intent, and
-            // launch it if needed.
-            val fsiDecision =
-                mVisualInterruptionDecisionProvider.makeUnloggedFullScreenIntentDecision(entry)
-            mVisualInterruptionDecisionProvider.logFullScreenIntentDecision(fsiDecision)
-            if (fsiDecision.shouldInterrupt) {
-                mLaunchFullScreenIntentProvider.launchFullScreenIntent(entry)
-            } else if (fsiDecision.wouldInterruptWithoutDnd) {
-                // If DND was the only reason this entry was suppressed, note it for potential
-                // reconsideration on later ranking updates.
-                addForFSIReconsideration(entry, mSystemClock.currentTimeMillis())
-            }
-
-            // makeAndLogHeadsUpDecision includes check for whether this notification should be
-            // filtered
-            val shouldHeadsUpEver =
-                mVisualInterruptionDecisionProvider.makeAndLogHeadsUpDecision(entry).shouldInterrupt
-            mPostedEntries[entry.key] = PostedEntry(
-                entry,
-                wasAdded = true,
-                wasUpdated = false,
-                shouldHeadsUpEver = shouldHeadsUpEver,
-                shouldHeadsUpAgain = true,
-                isHeadsUpEntry = false,
-                isBinding = false,
-            )
-
-            // Record the last updated time for this key
-            setUpdateTime(entry, mSystemClock.currentTimeMillis())
-        }
-
-        /**
-         * Notification could've updated to be heads up or not heads up. Even if it did update to
-         * heads up, if the notification specified that it only wants to heads up once, don't heads
-         * up again.
-         */
-        override fun onEntryUpdated(entry: NotificationEntry) {
-            val shouldHeadsUpEver =
-                mVisualInterruptionDecisionProvider.makeAndLogHeadsUpDecision(entry).shouldInterrupt
-            val shouldHeadsUpAgain = shouldHunAgain(entry)
-            val isHeadsUpEntry = mHeadsUpManager.isHeadsUpEntry(entry.key)
-            val isBinding = isEntryBinding(entry)
-            val posted = mPostedEntries.compute(entry.key) { _, value ->
-                value?.also { update ->
-                    update.wasUpdated = true
-                    update.shouldHeadsUpEver = shouldHeadsUpEver
-                    update.shouldHeadsUpAgain = update.shouldHeadsUpAgain || shouldHeadsUpAgain
-                    update.isHeadsUpEntry = isHeadsUpEntry
-                    update.isBinding = isBinding
-                } ?: PostedEntry(
-                    entry,
-                    wasAdded = false,
-                    wasUpdated = true,
-                    shouldHeadsUpEver = shouldHeadsUpEver,
-                    shouldHeadsUpAgain = shouldHeadsUpAgain,
-                    isHeadsUpEntry = isHeadsUpEntry,
-                    isBinding = isBinding,
-                )
-            }
-            // Handle cancelling heads up here, rather than in the OnBeforeFinalizeFilter, so that
-            // work can be done before the ShadeListBuilder is run. This prevents re-entrant
-            // behavior between this Coordinator, HeadsUpManager, and VisualStabilityManager.
-            if (posted?.shouldHeadsUpEver == false) {
-                if (posted.isHeadsUpEntry) {
-                    // We don't want this to be interrupting anymore, let's remove it
-                    mHeadsUpManager.removeNotification(posted.key, false /*removeImmediately*/)
-                } else if (posted.isBinding) {
-                    // Don't let the bind finish
-                    cancelHeadsUpBind(posted.entry)
+    private val mNotifCollectionListener =
+        object : NotifCollectionListener {
+            /**
+             * Notification was just added and if it should heads up, bind the view and then show
+             * it.
+             */
+            override fun onEntryAdded(entry: NotificationEntry) {
+                // First check whether this notification should launch a full screen intent, and
+                // launch it if needed.
+                val fsiDecision =
+                    mVisualInterruptionDecisionProvider.makeUnloggedFullScreenIntentDecision(entry)
+                mVisualInterruptionDecisionProvider.logFullScreenIntentDecision(fsiDecision)
+                if (fsiDecision.shouldInterrupt) {
+                    mLaunchFullScreenIntentProvider.launchFullScreenIntent(entry)
+                } else if (fsiDecision.wouldInterruptWithoutDnd) {
+                    // If DND was the only reason this entry was suppressed, note it for potential
+                    // reconsideration on later ranking updates.
+                    addForFSIReconsideration(entry, mSystemClock.currentTimeMillis())
                 }
+
+                // makeAndLogHeadsUpDecision includes check for whether this notification should be
+                // filtered
+                val shouldHeadsUpEver =
+                    mVisualInterruptionDecisionProvider
+                        .makeAndLogHeadsUpDecision(entry)
+                        .shouldInterrupt
+                mPostedEntries[entry.key] =
+                    PostedEntry(
+                        entry,
+                        wasAdded = true,
+                        wasUpdated = false,
+                        shouldHeadsUpEver = shouldHeadsUpEver,
+                        shouldHeadsUpAgain = true,
+                        isHeadsUpEntry = false,
+                        isBinding = false,
+                    )
+
+                // Record the last updated time for this key
+                setUpdateTime(entry, mSystemClock.currentTimeMillis())
             }
 
-            // Update last updated time for this entry
-            setUpdateTime(entry, mSystemClock.currentTimeMillis())
-        }
-
-        /**
-         * Stop showing as heads up once removed from the notification collection
-         */
-        override fun onEntryRemoved(entry: NotificationEntry, reason: Int) {
-            mPostedEntries.remove(entry.key)
-            mEntriesUpdateTimes.remove(entry.key)
-            cancelHeadsUpBind(entry)
-            val entryKey = entry.key
-            if (mHeadsUpManager.isHeadsUpEntry(entryKey)) {
-                // TODO: This should probably know the RemoteInputCoordinator's conditions,
-                //  or otherwise reference that coordinator's state, rather than replicate its logic
-                val removeImmediatelyForRemoteInput = (mRemoteInputManager.isSpinning(entryKey) &&
-                        !NotificationRemoteInputManager.FORCE_REMOTE_INPUT_HISTORY)
-                mHeadsUpManager.removeNotification(entry.key, removeImmediatelyForRemoteInput)
-            }
-        }
-
-        override fun onEntryCleanUp(entry: NotificationEntry) {
-            mHeadsUpViewBinder.abortBindCallback(entry)
-        }
-
-        /**
-         * Identify notifications whose heads-up state changes when the notification rankings are
-         * updated, and have those changed notifications heads up if necessary.
-         *
-         * This method will occur after any operations in onEntryAdded or onEntryUpdated, so any
-         * handling of ranking changes needs to take into account that we may have just made a
-         * PostedEntry for some of these notifications.
-         */
-        override fun onRankingApplied() {
-            // Because a ranking update may cause some notifications that are no longer (or were
-            // never) in mPostedEntries to need to heads up, we need to check every notification
-            // known to the pipeline.
-            for (entry in mNotifPipeline.allNotifs) {
-                // Only consider entries that are recent enough, since we want to apply a fairly
-                // strict threshold for when an entry should be updated via only ranking and not an
-                // app-provided notification update.
-                if (!isNewEnoughForRankingUpdate(entry)) continue
-
-                // The only entries we consider heads up for here are entries that have never
-                // interrupted and that now say they should heads up or FSI; if they've heads uped in
-                // the past, we don't want to incorrectly heads up a second time if there wasn't an
-                // explicit notification update.
-                if (entry.hasInterrupted()) continue
-
-                // Before potentially allowing heads-up, check for any candidates for a FSI launch.
-                // Any entry that is a candidate meets two criteria:
-                //   - was suppressed from FSI launch only by a DND suppression
-                //   - is within the recency window for reconsideration
-                // If any of these entries are no longer suppressed, launch the FSI now.
-                if (isCandidateForFSIReconsideration(entry)) {
-                    val decision =
-                        mVisualInterruptionDecisionProvider.makeUnloggedFullScreenIntentDecision(
-                            entry
-                        )
-                    if (decision.shouldInterrupt) {
-                        // Log both the launch of the full screen and also that this was via a
-                        // ranking update, and finally revoke candidacy for FSI reconsideration
-                        mLogger.logEntryUpdatedToFullScreen(entry.key, decision.logReason)
-                        mVisualInterruptionDecisionProvider.logFullScreenIntentDecision(decision)
-                        mLaunchFullScreenIntentProvider.launchFullScreenIntent(entry)
-                        mFSIUpdateCandidates.remove(entry.key)
-
-                        // if we launch the FSI then this is no longer a candidate for HUN
-                        continue
-                    } else if (decision.wouldInterruptWithoutDnd) {
-                        // decision has not changed; no need to log
-                    } else {
-                        // some other condition is now blocking FSI; log that and revoke candidacy
-                        // for FSI reconsideration
-                        mLogger.logEntryDisqualifiedFromFullScreen(entry.key, decision.logReason)
-                        mVisualInterruptionDecisionProvider.logFullScreenIntentDecision(decision)
-                        mFSIUpdateCandidates.remove(entry.key)
+            /**
+             * Notification could've updated to be heads up or not heads up. Even if it did update
+             * to heads up, if the notification specified that it only wants to heads up once, don't
+             * heads up again.
+             */
+            override fun onEntryUpdated(entry: NotificationEntry) {
+                val shouldHeadsUpEver =
+                    mVisualInterruptionDecisionProvider
+                        .makeAndLogHeadsUpDecision(entry)
+                        .shouldInterrupt
+                val shouldHeadsUpAgain = shouldHunAgain(entry)
+                val isHeadsUpEntry = mHeadsUpManager.isHeadsUpEntry(entry.key)
+                val isBinding = isEntryBinding(entry)
+                val posted =
+                    mPostedEntries.compute(entry.key) { _, value ->
+                        value?.also { update ->
+                            update.wasUpdated = true
+                            update.shouldHeadsUpEver = shouldHeadsUpEver
+                            update.shouldHeadsUpAgain =
+                                update.shouldHeadsUpAgain || shouldHeadsUpAgain
+                            update.isHeadsUpEntry = isHeadsUpEntry
+                            update.isBinding = isBinding
+                        }
+                            ?: PostedEntry(
+                                entry,
+                                wasAdded = false,
+                                wasUpdated = true,
+                                shouldHeadsUpEver = shouldHeadsUpEver,
+                                shouldHeadsUpAgain = shouldHeadsUpAgain,
+                                isHeadsUpEntry = isHeadsUpEntry,
+                                isBinding = isBinding,
+                            )
+                    }
+                // Handle cancelling heads up here, rather than in the OnBeforeFinalizeFilter, so
+                // that
+                // work can be done before the ShadeListBuilder is run. This prevents re-entrant
+                // behavior between this Coordinator, HeadsUpManager, and VisualStabilityManager.
+                if (posted?.shouldHeadsUpEver == false) {
+                    if (posted.isHeadsUpEntry) {
+                        // We don't want this to be interrupting anymore, let's remove it
+                        mHeadsUpManager.removeNotification(posted.key, false /*removeImmediately*/)
+                    } else if (posted.isBinding) {
+                        // Don't let the bind finish
+                        cancelHeadsUpBind(posted.entry)
                     }
                 }
 
-                // The cases where we should consider this notification to be updated:
-                // - if this entry is not present in PostedEntries, and is now in a shouldHeadsUp
-                //   state
-                // - if it is present in PostedEntries and the previous state of shouldHeadsUp
-                //   differs from the updated one
-                val decision =
-                    mVisualInterruptionDecisionProvider.makeUnloggedHeadsUpDecision(entry)
-                val shouldHeadsUpEver = decision.shouldInterrupt
-                val postedShouldHeadsUpEver = mPostedEntries[entry.key]?.shouldHeadsUpEver ?: false
-                val shouldUpdateEntry = postedShouldHeadsUpEver != shouldHeadsUpEver
+                // Update last updated time for this entry
+                setUpdateTime(entry, mSystemClock.currentTimeMillis())
+            }
 
-                if (shouldUpdateEntry) {
-                    mLogger.logEntryUpdatedByRanking(
-                        entry.key,
-                        shouldHeadsUpEver,
-                        decision.logReason
-                    )
-                    onEntryUpdated(entry)
+            /** Stop showing as heads up once removed from the notification collection */
+            override fun onEntryRemoved(entry: NotificationEntry, reason: Int) {
+                mPostedEntries.remove(entry.key)
+                mEntriesUpdateTimes.remove(entry.key)
+                cancelHeadsUpBind(entry)
+                val entryKey = entry.key
+                if (mHeadsUpManager.isHeadsUpEntry(entryKey)) {
+                    // TODO: This should probably know the RemoteInputCoordinator's conditions,
+                    //  or otherwise reference that coordinator's state, rather than replicate its
+                    // logic
+                    val removeImmediatelyForRemoteInput =
+                        (mRemoteInputManager.isSpinning(entryKey) &&
+                            !NotificationRemoteInputManager.FORCE_REMOTE_INPUT_HISTORY)
+                    mHeadsUpManager.removeNotification(entry.key, removeImmediatelyForRemoteInput)
+                }
+            }
+
+            override fun onEntryCleanUp(entry: NotificationEntry) {
+                mHeadsUpViewBinder.abortBindCallback(entry)
+            }
+
+            /**
+             * Identify notifications whose heads-up state changes when the notification rankings
+             * are updated, and have those changed notifications heads up if necessary.
+             *
+             * This method will occur after any operations in onEntryAdded or onEntryUpdated, so any
+             * handling of ranking changes needs to take into account that we may have just made a
+             * PostedEntry for some of these notifications.
+             */
+            override fun onRankingApplied() {
+                // Because a ranking update may cause some notifications that are no longer (or were
+                // never) in mPostedEntries to need to heads up, we need to check every notification
+                // known to the pipeline.
+                for (entry in mNotifPipeline.allNotifs) {
+                    // Only consider entries that are recent enough, since we want to apply a fairly
+                    // strict threshold for when an entry should be updated via only ranking and not
+                    // an
+                    // app-provided notification update.
+                    if (!isNewEnoughForRankingUpdate(entry)) continue
+
+                    // The only entries we consider heads up for here are entries that have never
+                    // interrupted and that now say they should heads up or FSI; if they've heads
+                    // uped in
+                    // the past, we don't want to incorrectly heads up a second time if there wasn't
+                    // an
+                    // explicit notification update.
+                    if (entry.hasInterrupted()) continue
+
+                    // Before potentially allowing heads-up, check for any candidates for a FSI
+                    // launch.
+                    // Any entry that is a candidate meets two criteria:
+                    //   - was suppressed from FSI launch only by a DND suppression
+                    //   - is within the recency window for reconsideration
+                    // If any of these entries are no longer suppressed, launch the FSI now.
+                    if (isCandidateForFSIReconsideration(entry)) {
+                        val decision =
+                            mVisualInterruptionDecisionProvider
+                                .makeUnloggedFullScreenIntentDecision(entry)
+                        if (decision.shouldInterrupt) {
+                            // Log both the launch of the full screen and also that this was via a
+                            // ranking update, and finally revoke candidacy for FSI reconsideration
+                            mLogger.logEntryUpdatedToFullScreen(entry.key, decision.logReason)
+                            mVisualInterruptionDecisionProvider.logFullScreenIntentDecision(
+                                decision
+                            )
+                            mLaunchFullScreenIntentProvider.launchFullScreenIntent(entry)
+                            mFSIUpdateCandidates.remove(entry.key)
+
+                            // if we launch the FSI then this is no longer a candidate for HUN
+                            continue
+                        } else if (decision.wouldInterruptWithoutDnd) {
+                            // decision has not changed; no need to log
+                        } else {
+                            // some other condition is now blocking FSI; log that and revoke
+                            // candidacy
+                            // for FSI reconsideration
+                            mLogger.logEntryDisqualifiedFromFullScreen(
+                                entry.key,
+                                decision.logReason
+                            )
+                            mVisualInterruptionDecisionProvider.logFullScreenIntentDecision(
+                                decision
+                            )
+                            mFSIUpdateCandidates.remove(entry.key)
+                        }
+                    }
+
+                    // The cases where we should consider this notification to be updated:
+                    // - if this entry is not present in PostedEntries, and is now in a
+                    // shouldHeadsUp
+                    //   state
+                    // - if it is present in PostedEntries and the previous state of shouldHeadsUp
+                    //   differs from the updated one
+                    val decision =
+                        mVisualInterruptionDecisionProvider.makeUnloggedHeadsUpDecision(entry)
+                    val shouldHeadsUpEver = decision.shouldInterrupt
+                    val postedShouldHeadsUpEver =
+                        mPostedEntries[entry.key]?.shouldHeadsUpEver ?: false
+                    val shouldUpdateEntry = postedShouldHeadsUpEver != shouldHeadsUpEver
+
+                    if (shouldUpdateEntry) {
+                        mLogger.logEntryUpdatedByRanking(
+                            entry.key,
+                            shouldHeadsUpEver,
+                            decision.logReason
+                        )
+                        onEntryUpdated(entry)
+                    }
                 }
             }
         }
-    }
 
-    /**
-     * Checks whether an update for a notification warrants an heads up for the user.
-     */
+    /** Checks whether an update for a notification warrants an heads up for the user. */
     private fun shouldHunAgain(entry: NotificationEntry): Boolean {
         return (!entry.hasInterrupted() ||
-                (entry.sbn.notification.flags and Notification.FLAG_ONLY_ALERT_ONCE) == 0)
+            (entry.sbn.notification.flags and Notification.FLAG_ONLY_ALERT_ONCE) == 0)
     }
 
-    /**
-     * Sets the updated time for the given entry to the specified time.
-     */
+    /** Sets the updated time for the given entry to the specified time. */
     @VisibleForTesting
     fun setUpdateTime(entry: NotificationEntry, time: Long) {
         mEntriesUpdateTimes[entry.key] = time
@@ -593,10 +641,10 @@
     }
 
     /**
-     * Checks whether the entry is new enough to be updated via ranking update.
-     * We want to avoid updating an entry too long after it was originally posted/updated when we're
-     * only reacting to a ranking change, as relevant ranking updates are expected to come in
-     * fairly soon after the posting of a notification.
+     * Checks whether the entry is new enough to be updated via ranking update. We want to avoid
+     * updating an entry too long after it was originally posted/updated when we're only reacting to
+     * a ranking change, as relevant ranking updates are expected to come in fairly soon after the
+     * posting of a notification.
      */
     private fun isNewEnoughForRankingUpdate(entry: NotificationEntry): Boolean {
         // If we don't have an update time for this key, default to "too old"
@@ -648,72 +696,92 @@
      * @see HeadsUpManager.setUserActionMayIndirectlyRemove
      * @see HeadsUpManager.canRemoveImmediately
      */
-    private val mActionPressListener = Consumer<NotificationEntry> { entry ->
-        mHeadsUpManager.setUserActionMayIndirectlyRemove(entry)
-        mExecutor.execute { endNotifLifetimeExtensionIfExtended(entry) }
-    }
-
-    private val mLifetimeExtender = object : NotifLifetimeExtender {
-        override fun getName() = TAG
-
-        override fun setCallback(callback: OnEndLifetimeExtensionCallback) {
-            mEndLifetimeExtension = callback
+    private val mActionPressListener =
+        Consumer<NotificationEntry> { entry ->
+            mHeadsUpManager.setUserActionMayIndirectlyRemove(entry)
+            mExecutor.execute { endNotifLifetimeExtensionIfExtended(entry) }
         }
 
-        override fun maybeExtendLifetime(entry: NotificationEntry, reason: Int): Boolean {
-            if (mHeadsUpManager.canRemoveImmediately(entry.key)) {
-                return false
+    private val mLifetimeExtender =
+        object : NotifLifetimeExtender {
+            override fun getName() = TAG
+
+            override fun setCallback(callback: OnEndLifetimeExtensionCallback) {
+                mEndLifetimeExtension = callback
             }
-            if (isSticky(entry)) {
-                val removeAfterMillis = mHeadsUpManager.getEarliestRemovalTime(entry.key)
-                mNotifsExtendingLifetime[entry] = mExecutor.executeDelayed({
-                    mHeadsUpManager.removeNotification(entry.key, /* releaseImmediately */ true)
-                }, removeAfterMillis)
-            } else {
-                mExecutor.execute {
-                    mHeadsUpManager.removeNotification(entry.key, /* releaseImmediately */ false)
+
+            override fun maybeExtendLifetime(entry: NotificationEntry, reason: Int): Boolean {
+                if (mHeadsUpManager.canRemoveImmediately(entry.key)) {
+                    return false
                 }
-                mNotifsExtendingLifetime[entry] = null
+                if (isSticky(entry)) {
+                    val removeAfterMillis = mHeadsUpManager.getEarliestRemovalTime(entry.key)
+                    mNotifsExtendingLifetime[entry] =
+                        mExecutor.executeDelayed(
+                            {
+                                mHeadsUpManager.removeNotification(
+                                    entry.key, /* releaseImmediately */
+                                    true
+                                )
+                            },
+                            removeAfterMillis
+                        )
+                } else {
+                    mExecutor.execute {
+                        mHeadsUpManager.removeNotification(
+                            entry.key, /* releaseImmediately */
+                            false
+                        )
+                    }
+                    mNotifsExtendingLifetime[entry] = null
+                }
+                return true
             }
-            return true
-        }
 
-        override fun cancelLifetimeExtension(entry: NotificationEntry) {
-            mNotifsExtendingLifetime.remove(entry)?.run()
-        }
-    }
-
-    private val mNotifPromoter = object : NotifPromoter(TAG) {
-        override fun shouldPromoteToTopLevel(entry: NotificationEntry): Boolean =
-            isGoingToShowHunNoRetract(entry)
-    }
-
-    val sectioner = object : NotifSectioner("HeadsUp", BUCKET_HEADS_UP) {
-        override fun isInSection(entry: ListEntry): Boolean =
-            // TODO: This check won't notice if a child of the group is going to HUN...
-            isGoingToShowHunNoRetract(entry)
-
-        override fun getComparator(): NotifComparator {
-            return object : NotifComparator("HeadsUp") {
-                override fun compare(o1: ListEntry, o2: ListEntry): Int =
-                    mHeadsUpManager.compare(o1.representativeEntry, o2.representativeEntry)
+            override fun cancelLifetimeExtension(entry: NotificationEntry) {
+                mNotifsExtendingLifetime.remove(entry)?.run()
             }
         }
 
-        override fun getHeaderNodeController(): NodeController? =
-            // TODO: remove SHOW_ALL_SECTIONS, this redundant method, and mIncomingHeaderController
-            if (RankingCoordinator.SHOW_ALL_SECTIONS) mIncomingHeaderController else null
-    }
+    private val mNotifPromoter =
+        object : NotifPromoter(TAG) {
+            override fun shouldPromoteToTopLevel(entry: NotificationEntry): Boolean =
+                isGoingToShowHunNoRetract(entry)
+        }
 
-    private val mOnHeadsUpChangedListener = object : OnHeadsUpChangedListener {
-        override fun onHeadsUpStateChanged(entry: NotificationEntry, isHeadsUp: Boolean) {
-            if (!isHeadsUp) {
-                mNotifPromoter.invalidateList("headsUpEnded: ${entry.logKey}")
-                mHeadsUpViewBinder.unbindHeadsUpView(entry)
-                endNotifLifetimeExtensionIfExtended(entry)
+    val sectioner =
+        object : NotifSectioner("HeadsUp", BUCKET_HEADS_UP) {
+            override fun isInSection(entry: ListEntry): Boolean =
+                // TODO: This check won't notice if a child of the group is going to HUN...
+                isGoingToShowHunNoRetract(entry)
+
+            override fun getComparator(): NotifComparator {
+                return object : NotifComparator("HeadsUp") {
+                    override fun compare(o1: ListEntry, o2: ListEntry): Int =
+                        mHeadsUpManager.compare(o1.representativeEntry, o2.representativeEntry)
+                }
+            }
+
+            override fun getHeaderNodeController(): NodeController? =
+                // TODO: remove SHOW_ALL_SECTIONS, this redundant method, and
+                // mIncomingHeaderController
+                if (RankingCoordinator.SHOW_ALL_SECTIONS) mIncomingHeaderController else null
+        }
+
+    private val mOnHeadsUpChangedListener =
+        object : OnHeadsUpChangedListener {
+            override fun onHeadsUpStateChanged(entry: NotificationEntry, isHeadsUp: Boolean) {
+                if (!isHeadsUp) {
+                    mNotifPromoter.invalidateList("headsUpEnded: ${entry.logKey}")
+                    mHeadsUpViewBinder.unbindHeadsUpView(entry)
+                    endNotifLifetimeExtensionIfExtended(entry)
+                }
+            }
+
+            override fun onHeadsUpAnimatingAwayEnded(entry: NotificationEntry) {
+                mNotifPromoter.invalidateList("headsUpAnimatingAwayEnded: ${entry.logKey}")
             }
         }
-    }
 
     private fun isSticky(entry: NotificationEntry) = mHeadsUpManager.isSticky(entry.key)
 
@@ -726,8 +794,9 @@
      * Whether the notification is already heads up or binding so that it can imminently heads up
      */
     private fun isAttemptingToShowHun(entry: ListEntry) =
-        mHeadsUpManager.isHeadsUpEntry(entry.key) || isEntryBinding(entry)
-                || isHeadsUpAnimatingAway(entry)
+        mHeadsUpManager.isHeadsUpEntry(entry.key) ||
+            isEntryBinding(entry) ||
+            isHeadsUpAnimatingAway(entry)
 
     private fun isHeadsUpAnimatingAway(entry: ListEntry): Boolean {
         if (!GroupHunAnimationFix.isEnabled) return false
@@ -735,19 +804,19 @@
     }
 
     /**
-     * Whether the notification is already heads up/binding per [isAttemptingToShowHun] OR if it
-     * has been updated so that it should heads up this update.  This method is permissive because
-     * it returns `true` even if the update would (in isolation of its group) cause the heads up to
-     * be retracted.  This is important for not retracting transferred group heads ups.
+     * Whether the notification is already heads up/binding per [isAttemptingToShowHun] OR if it has
+     * been updated so that it should heads up this update. This method is permissive because it
+     * returns `true` even if the update would (in isolation of its group) cause the heads up to be
+     * retracted. This is important for not retracting transferred group heads ups.
      */
     private fun isGoingToShowHunNoRetract(entry: ListEntry) =
         mPostedEntries[entry.key]?.calculateShouldBeHeadsUpNoRetract ?: isAttemptingToShowHun(entry)
 
     /**
      * If the notification has been updated, then whether it should HUN in isolation, otherwise
-     * defers to the already heads up/binding state of [isAttemptingToShowHun].  This method is
-     * strict because any update which would revoke the heads up supersedes the current
-     * heads up/binding state.
+     * defers to the already heads up/binding state of [isAttemptingToShowHun]. This method is
+     * strict because any update which would revoke the heads up supersedes the current heads
+     * up/binding state.
      */
     private fun isGoingToShowHunStrict(entry: ListEntry) =
         mPostedEntries[entry.key]?.calculateShouldBeHeadsUpStrict ?: isAttemptingToShowHun(entry)
@@ -779,14 +848,21 @@
         val key = entry.key
         val isHeadsUpAlready: Boolean
             get() = isHeadsUpEntry || isBinding
+
         val calculateShouldBeHeadsUpStrict: Boolean
             get() = shouldHeadsUpEver && (wasAdded || shouldHeadsUpAgain || isHeadsUpAlready)
+
         val calculateShouldBeHeadsUpNoRetract: Boolean
             get() = isHeadsUpAlready || (shouldHeadsUpEver && (wasAdded || shouldHeadsUpAgain))
     }
 }
 
-private enum class GroupLocation { Detached, Isolated, Summary, Child }
+private enum class GroupLocation {
+    Detached,
+    Isolated,
+    Summary,
+    Child
+}
 
 private fun Map<String, GroupLocation>.getLocation(key: String): GroupLocation =
     getOrDefault(key, GroupLocation.Detached)
@@ -804,6 +880,7 @@
 /** Mutates the HeadsUp state of notifications. */
 private interface HunMutator {
     fun updateNotification(key: String, shouldHeadsUpAgain: Boolean)
+
     fun removeNotification(key: String, releaseImmediately: Boolean)
 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/LockScreenMinimalismCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/LockScreenMinimalismCoordinator.kt
index a6605f6..a621b2a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/LockScreenMinimalismCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/LockScreenMinimalismCoordinator.kt
@@ -18,12 +18,9 @@
 
 import android.annotation.SuppressLint
 import android.app.NotificationManager
-import android.os.UserHandle
-import android.provider.Settings
 import androidx.annotation.VisibleForTesting
 import com.android.systemui.Dumpable
 import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.shade.domain.interactor.ShadeInteractor
@@ -43,23 +40,16 @@
 import com.android.systemui.statusbar.notification.stack.BUCKET_TOP_UNSEEN
 import com.android.systemui.util.asIndenting
 import com.android.systemui.util.printCollection
-import com.android.systemui.util.settings.SecureSettings
-import com.android.systemui.util.settings.SettingsProxyExt.observerFlow
 import java.io.PrintWriter
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.seconds
-import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.coroutineScope
 import kotlinx.coroutines.delay
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.collectLatest
-import kotlinx.coroutines.flow.conflate
-import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.flowOf
-import kotlinx.coroutines.flow.flowOn
 import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.onStart
 import kotlinx.coroutines.launch
 
 /**
@@ -73,12 +63,10 @@
 class LockScreenMinimalismCoordinator
 @Inject
 constructor(
-    @Background private val bgDispatcher: CoroutineDispatcher,
     private val dumpManager: DumpManager,
     private val headsUpInteractor: HeadsUpNotificationInteractor,
     private val logger: LockScreenMinimalismCoordinatorLogger,
     @Application private val scope: CoroutineScope,
-    private val secureSettings: SecureSettings,
     private val seenNotificationsInteractor: SeenNotificationsInteractor,
     private val statusBarStateController: StatusBarStateController,
     private val shadeInteractor: ShadeInteractor,
@@ -147,29 +135,7 @@
         if (NotificationMinimalismPrototype.isEnabled) {
             return flowOf(true)
         }
-        return secureSettings
-            // emit whenever the setting has changed
-            .observerFlow(
-                UserHandle.USER_ALL,
-                Settings.Secure.LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS,
-            )
-            // perform a query immediately
-            .onStart { emit(Unit) }
-            // for each change, lookup the new value
-            .map {
-                secureSettings.getIntForUser(
-                    name = Settings.Secure.LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS,
-                    def = 0,
-                    userHandle = UserHandle.USER_CURRENT,
-                ) == 1
-            }
-            // don't emit anything if nothing has changed
-            .distinctUntilChanged()
-            // perform lookups on the bg thread pool
-            .flowOn(bgDispatcher)
-            // only track the most recent emission, if events are happening faster than they can be
-            // consumed
-            .conflate()
+        return seenNotificationsInteractor.isLockScreenShowOnlyUnseenNotificationsEnabled()
     }
 
     private suspend fun trackUnseenFilterSettingChanges() {
@@ -177,6 +143,7 @@
             // update local field and invalidate if necessary
             if (isSettingEnabled != unseenFilterEnabled) {
                 unseenFilterEnabled = isSettingEnabled
+                unseenNotifications.clear()
                 unseenNotifPromoter.invalidateList("unseen setting changed")
             }
             // if the setting is enabled, then start tracking and filtering unseen notifications
@@ -190,21 +157,21 @@
     private val collectionListener =
         object : NotifCollectionListener {
             override fun onEntryAdded(entry: NotificationEntry) {
-                if (!isShadeVisible) {
+                if (unseenFilterEnabled && !isShadeVisible) {
                     logger.logUnseenAdded(entry.key)
                     unseenNotifications.add(entry)
                 }
             }
 
             override fun onEntryUpdated(entry: NotificationEntry) {
-                if (!isShadeVisible) {
+                if (unseenFilterEnabled && !isShadeVisible) {
                     logger.logUnseenUpdated(entry.key)
                     unseenNotifications.add(entry)
                 }
             }
 
             override fun onEntryRemoved(entry: NotificationEntry, reason: Int) {
-                if (unseenNotifications.remove(entry)) {
+                if (unseenFilterEnabled && unseenNotifications.remove(entry)) {
                     logger.logUnseenRemoved(entry.key)
                 }
             }
@@ -212,6 +179,7 @@
 
     private fun pickOutTopUnseenNotifs(list: List<ListEntry>) {
         if (NotificationMinimalismPrototype.isUnexpectedlyInLegacyMode()) return
+        if (!unseenFilterEnabled) return
         // Only ever elevate a top unseen notification on keyguard, not even locked shade
         if (statusBarStateController.state != StatusBarState.KEYGUARD) {
             seenNotificationsInteractor.setTopOngoingNotification(null)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/OriginalUnseenKeyguardCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/OriginalUnseenKeyguardCoordinator.kt
index 5b25b11..bfea2ba 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/OriginalUnseenKeyguardCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/OriginalUnseenKeyguardCoordinator.kt
@@ -17,17 +17,16 @@
 package com.android.systemui.statusbar.notification.collection.coordinator
 
 import android.annotation.SuppressLint
-import android.os.UserHandle
-import android.provider.Settings
 import androidx.annotation.VisibleForTesting
 import com.android.systemui.Dumpable
 import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.keyguard.data.repository.KeyguardRepository
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.scene.domain.interactor.SceneInteractor
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.statusbar.expansionChanges
 import com.android.systemui.statusbar.notification.collection.NotifPipeline
@@ -41,12 +40,9 @@
 import com.android.systemui.statusbar.policy.headsUpEvents
 import com.android.systemui.util.asIndenting
 import com.android.systemui.util.indentIfPossible
-import com.android.systemui.util.settings.SecureSettings
-import com.android.systemui.util.settings.SettingsProxyExt.observerFlow
 import java.io.PrintWriter
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.seconds
-import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Job
 import kotlinx.coroutines.coroutineScope
@@ -54,13 +50,10 @@
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableSharedFlow
 import kotlinx.coroutines.flow.collectLatest
-import kotlinx.coroutines.flow.conflate
 import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.flowOf
-import kotlinx.coroutines.flow.flowOn
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.onEach
-import kotlinx.coroutines.flow.onStart
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.yield
 
@@ -77,16 +70,15 @@
 class OriginalUnseenKeyguardCoordinator
 @Inject
 constructor(
-    @Background private val bgDispatcher: CoroutineDispatcher,
     private val dumpManager: DumpManager,
     private val headsUpManager: HeadsUpManager,
     private val keyguardRepository: KeyguardRepository,
     private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
     private val logger: KeyguardCoordinatorLogger,
     @Application private val scope: CoroutineScope,
-    private val secureSettings: SecureSettings,
     private val seenNotificationsInteractor: SeenNotificationsInteractor,
     private val statusBarStateController: StatusBarStateController,
+    private val sceneInteractor: SceneInteractor,
 ) : Coordinator, Dumpable {
 
     private val unseenNotifications = mutableSetOf<NotificationEntry>()
@@ -106,12 +98,15 @@
         // Whether or not keyguard is visible (or occluded).
         @Suppress("DEPRECATION")
         val isKeyguardPresentFlow: Flow<Boolean> =
-            keyguardTransitionInteractor
-                .transitionValue(
-                    scene = Scenes.Gone,
-                    stateWithoutSceneContainer = KeyguardState.GONE,
-                )
-                .map { it == 0f }
+            if (SceneContainerFlag.isEnabled) {
+                    sceneInteractor.transitionState.map {
+                        !it.isTransitioning(to = Scenes.Gone) && !it.isIdle(Scenes.Gone)
+                    }
+                } else {
+                    keyguardTransitionInteractor.transitions.map { step ->
+                        step.to != KeyguardState.GONE
+                    }
+                }
                 .distinctUntilChanged()
                 .onEach { trackingUnseen -> logger.logTrackingUnseen(trackingUnseen) }
 
@@ -262,29 +257,7 @@
             // TODO(b/330387368): should this really just be turned off? If so, hide the setting.
             return flowOf(false)
         }
-        return secureSettings
-            // emit whenever the setting has changed
-            .observerFlow(
-                UserHandle.USER_ALL,
-                Settings.Secure.LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS,
-            )
-            // perform a query immediately
-            .onStart { emit(Unit) }
-            // for each change, lookup the new value
-            .map {
-                secureSettings.getIntForUser(
-                    name = Settings.Secure.LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS,
-                    def = 0,
-                    userHandle = UserHandle.USER_CURRENT,
-                ) == 1
-            }
-            // don't emit anything if nothing has changed
-            .distinctUntilChanged()
-            // perform lookups on the bg thread pool
-            .flowOn(bgDispatcher)
-            // only track the most recent emission, if events are happening faster than they can be
-            // consumed
-            .conflate()
+        return seenNotificationsInteractor.isLockScreenShowOnlyUnseenNotificationsEnabled()
     }
 
     private suspend fun trackUnseenFilterSettingChanges() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt
index ac2a0d8..1e0e597a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt
@@ -20,10 +20,14 @@
 import android.os.UserHandle
 import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.server.notification.Flags.screenshareNotificationHiding
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
 import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.scene.domain.interactor.SceneInteractor
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.statusbar.NotificationLockscreenUserManager
 import com.android.systemui.statusbar.StatusBarState
-import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter
 import com.android.systemui.statusbar.notification.DynamicPrivacyController
 import com.android.systemui.statusbar.notification.collection.GroupEntry
 import com.android.systemui.statusbar.notification.collection.ListEntry
@@ -32,27 +36,33 @@
 import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope
 import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeRenderListListener
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Invalidator
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter
 import com.android.systemui.statusbar.policy.KeyguardStateController
 import com.android.systemui.statusbar.policy.SensitiveNotificationProtectionController
 import com.android.systemui.user.domain.interactor.SelectedUserInteractor
 import dagger.Binds
 import dagger.Module
 import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.mapNotNull
+import kotlinx.coroutines.launch
 
 @Module(includes = [PrivateSensitiveContentCoordinatorModule::class])
 interface SensitiveContentCoordinatorModule
 
 @Module
 interface PrivateSensitiveContentCoordinatorModule {
-    @Binds
-    fun bindCoordinator(impl: SensitiveContentCoordinatorImpl): SensitiveContentCoordinator
+    @Binds fun bindCoordinator(impl: SensitiveContentCoordinatorImpl): SensitiveContentCoordinator
 }
 
 /** Coordinates re-inflation and post-processing of sensitive notification content. */
 interface SensitiveContentCoordinator : Coordinator
 
 @CoordinatorScope
-class SensitiveContentCoordinatorImpl @Inject constructor(
+class SensitiveContentCoordinatorImpl
+@Inject
+constructor(
     private val dynamicPrivacyController: DynamicPrivacyController,
     private val lockscreenUserManager: NotificationLockscreenUserManager,
     private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
@@ -61,45 +71,85 @@
     private val selectedUserInteractor: SelectedUserInteractor,
     private val sensitiveNotificationProtectionController:
         SensitiveNotificationProtectionController,
-) : Invalidator("SensitiveContentInvalidator"),
-        SensitiveContentCoordinator,
-        DynamicPrivacyController.Listener,
-        OnBeforeRenderListListener {
-    private val onSensitiveStateChanged = Runnable() {
-        invalidateList("onSensitiveStateChanged")
-    }
+    private val deviceEntryInteractor: DeviceEntryInteractor,
+    private val sceneInteractor: SceneInteractor,
+    @Application private val scope: CoroutineScope,
+) :
+    Invalidator("SensitiveContentInvalidator"),
+    SensitiveContentCoordinator,
+    DynamicPrivacyController.Listener,
+    OnBeforeRenderListListener {
+    private var inTransitionFromLockedToGone = false
 
-    private val screenshareSecretFilter = object : NotifFilter("ScreenshareSecretFilter") {
-        val NotificationEntry.isSecret
-            get() = channel?.lockscreenVisibility == Notification.VISIBILITY_SECRET ||
-                sbn.notification?.visibility == Notification.VISIBILITY_SECRET
-        override fun shouldFilterOut(entry: NotificationEntry, now: Long): Boolean {
-            return screenshareNotificationHiding() &&
-                sensitiveNotificationProtectionController.isSensitiveStateActive &&
-                entry.isSecret
+    private val onSensitiveStateChanged = Runnable() { invalidateList("onSensitiveStateChanged") }
+
+    private val screenshareSecretFilter =
+        object : NotifFilter("ScreenshareSecretFilter") {
+            val NotificationEntry.isSecret
+                get() =
+                    channel?.lockscreenVisibility == Notification.VISIBILITY_SECRET ||
+                        sbn.notification?.visibility == Notification.VISIBILITY_SECRET
+
+            override fun shouldFilterOut(entry: NotificationEntry, now: Long): Boolean {
+                return screenshareNotificationHiding() &&
+                    sensitiveNotificationProtectionController.isSensitiveStateActive &&
+                    entry.isSecret
+            }
         }
-    }
 
     override fun attach(pipeline: NotifPipeline) {
         dynamicPrivacyController.addListener(this)
         if (screenshareNotificationHiding()) {
-            sensitiveNotificationProtectionController
-                .registerSensitiveStateListener(onSensitiveStateChanged)
+            sensitiveNotificationProtectionController.registerSensitiveStateListener(
+                onSensitiveStateChanged
+            )
         }
         pipeline.addOnBeforeRenderListListener(this)
         pipeline.addPreRenderInvalidator(this)
         if (screenshareNotificationHiding()) {
             pipeline.addFinalizeFilter(screenshareSecretFilter)
         }
+
+        if (SceneContainerFlag.isEnabled) {
+            scope.launch {
+                sceneInteractor.transitionState
+                    .mapNotNull {
+                        val transitioningToGone = it.isTransitioning(to = Scenes.Gone)
+                        val deviceEntered = deviceEntryInteractor.isDeviceEntered.value
+                        when {
+                            transitioningToGone && !deviceEntered -> true
+                            !transitioningToGone -> false
+                            else -> null
+                        }
+                    }
+                    .distinctUntilChanged()
+                    .collect {
+                        inTransitionFromLockedToGone = it
+                        invalidateList("inTransitionFromLockedToGoneChanged")
+                    }
+            }
+        }
     }
 
     override fun onDynamicPrivacyChanged(): Unit = invalidateList("onDynamicPrivacyChanged")
 
+    private val isKeyguardGoingAway: Boolean
+        get() {
+            if (SceneContainerFlag.isEnabled) {
+                return inTransitionFromLockedToGone
+            } else {
+                return keyguardStateController.isKeyguardGoingAway
+            }
+        }
+
     override fun onBeforeRenderList(entries: List<ListEntry>) {
-        if (keyguardStateController.isKeyguardGoingAway ||
+        if (
+            isKeyguardGoingAway ||
                 statusBarStateController.state == StatusBarState.KEYGUARD &&
-                keyguardUpdateMonitor.getUserUnlockedWithBiometricAndIsBypassing(
-                        selectedUserInteractor.getSelectedUserId())) {
+                    keyguardUpdateMonitor.getUserUnlockedWithBiometricAndIsBypassing(
+                        selectedUserInteractor.getSelectedUserId()
+                    )
+        ) {
             // don't update yet if:
             // - the keyguard is currently going away
             // - LS is about to be dismissed by a biometric that bypasses LS (avoid notif flash)
@@ -109,35 +159,40 @@
             return
         }
 
-        val isSensitiveContentProtectionActive = screenshareNotificationHiding() &&
-            sensitiveNotificationProtectionController.isSensitiveStateActive
+        val isSensitiveContentProtectionActive =
+            screenshareNotificationHiding() &&
+                sensitiveNotificationProtectionController.isSensitiveStateActive
         val currentUserId = lockscreenUserManager.currentUserId
         val devicePublic = lockscreenUserManager.isLockscreenPublicMode(currentUserId)
-        val deviceSensitive = (devicePublic &&
+        val deviceSensitive =
+            (devicePublic &&
                 !lockscreenUserManager.userAllowsPrivateNotificationsInPublic(currentUserId)) ||
                 isSensitiveContentProtectionActive
         val dynamicallyUnlocked = dynamicPrivacyController.isDynamicallyUnlocked
         for (entry in extractAllRepresentativeEntries(entries).filter { it.rowExists() }) {
             val notifUserId = entry.sbn.user.identifier
-            val userLockscreen = devicePublic ||
-                    lockscreenUserManager.isLockscreenPublicMode(notifUserId)
-            val userPublic = when {
-                // if we're not on the lockscreen, we're definitely private
-                !userLockscreen -> false
-                // we are on the lockscreen, so unless we're dynamically unlocked, we're
-                // definitely public
-                !dynamicallyUnlocked -> true
-                // we're dynamically unlocked, but check if the notification needs
-                // a separate challenge if it's from a work profile
-                else -> when (notifUserId) {
-                    currentUserId -> false
-                    UserHandle.USER_ALL -> false
-                    else -> lockscreenUserManager.needsSeparateWorkChallenge(notifUserId)
+            val userLockscreen =
+                devicePublic || lockscreenUserManager.isLockscreenPublicMode(notifUserId)
+            val userPublic =
+                when {
+                    // if we're not on the lockscreen, we're definitely private
+                    !userLockscreen -> false
+                    // we are on the lockscreen, so unless we're dynamically unlocked, we're
+                    // definitely public
+                    !dynamicallyUnlocked -> true
+                    // we're dynamically unlocked, but check if the notification needs
+                    // a separate challenge if it's from a work profile
+                    else ->
+                        when (notifUserId) {
+                            currentUserId -> false
+                            UserHandle.USER_ALL -> false
+                            else -> lockscreenUserManager.needsSeparateWorkChallenge(notifUserId)
+                        }
                 }
-            }
 
-            val shouldProtectNotification = screenshareNotificationHiding() &&
-                sensitiveNotificationProtectionController.shouldProtectNotification(entry)
+            val shouldProtectNotification =
+                screenshareNotificationHiding() &&
+                    sensitiveNotificationProtectionController.shouldProtectNotification(entry)
 
             val needsRedaction = lockscreenUserManager.needsRedaction(entry)
             val isSensitive = userPublic && needsRedaction
@@ -149,9 +204,7 @@
     }
 }
 
-private fun extractAllRepresentativeEntries(
-    entries: List<ListEntry>
-): Sequence<NotificationEntry> =
+private fun extractAllRepresentativeEntries(entries: List<ListEntry>): Sequence<NotificationEntry> =
     entries.asSequence().flatMap(::extractAllRepresentativeEntries)
 
 private fun extractAllRepresentativeEntries(listEntry: ListEntry): Sequence<NotificationEntry> =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java
index 696298e..3a2f95e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java
@@ -143,9 +143,12 @@
                 this::onLaunchingActivityChanged);
         mJavaAdapter.alwaysCollectFlow(mCommunalInteractor.isIdleOnCommunal(),
                 this::onCommunalShowingChanged);
-        mJavaAdapter.alwaysCollectFlow(mKeyguardTransitionInteractor.transitionValue(
-                        KeyguardState.LOCKSCREEN),
-                this::onLockscreenKeyguardStateTransitionValueChanged);
+
+        if (SceneContainerFlag.isEnabled()) {
+            mJavaAdapter.alwaysCollectFlow(mKeyguardTransitionInteractor.transitionValue(
+                            KeyguardState.LOCKSCREEN),
+                    this::onLockscreenKeyguardStateTransitionValueChanged);
+        }
 
         pipeline.setVisualStabilityManager(mNotifStabilityManager);
     }
@@ -260,7 +263,8 @@
 
     private boolean isReorderingAllowed() {
         final boolean sleepyAndDozed = mFullyDozed && mSleepy;
-        final boolean stackShowing = mPanelExpanded || mLockscreenShowing;
+        final boolean stackShowing = mPanelExpanded
+                || (SceneContainerFlag.isEnabled() && mLockscreenShowing);
         return (sleepyAndDozed || !stackShowing || mCommunalShowing) && !mPulsing;
     }
 
@@ -381,6 +385,10 @@
         }
 
         final boolean isShowing = value > 0.0f;
+        if (isShowing == mLockscreenShowing) {
+            return;
+        }
+
         mLockscreenShowing = isShowing;
         updateAllowedStates("lockscreenShowing", isShowing);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/NotificationSettingsRepositoryModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/NotificationSettingsRepositoryModule.kt
index a7970c7..af21e75 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/NotificationSettingsRepositoryModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/NotificationSettingsRepositoryModule.kt
@@ -19,14 +19,16 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.settings.SecureSettingsRepositoryModule
+import com.android.systemui.settings.SystemSettingsRepositoryModule
 import com.android.systemui.shared.notifications.data.repository.NotificationSettingsRepository
 import com.android.systemui.shared.settings.data.repository.SecureSettingsRepository
+import com.android.systemui.shared.settings.data.repository.SystemSettingsRepository
 import dagger.Module
 import dagger.Provides
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
 
-@Module(includes = [SecureSettingsRepositoryModule::class])
+@Module(includes = [SecureSettingsRepositoryModule::class, SystemSettingsRepositoryModule::class])
 object NotificationSettingsRepositoryModule {
     @Provides
     @SysUISingleton
@@ -34,10 +36,12 @@
         @Background backgroundScope: CoroutineScope,
         @Background backgroundDispatcher: CoroutineDispatcher,
         secureSettingsRepository: SecureSettingsRepository,
+        systemSettingsRepository: SystemSettingsRepository,
     ): NotificationSettingsRepository =
         NotificationSettingsRepository(
             backgroundScope,
             backgroundDispatcher,
-            secureSettingsRepository
+            secureSettingsRepository,
+            systemSettingsRepository
         )
 }
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 0dbc8c0..1cb59f1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractor.kt
@@ -15,11 +15,13 @@
 
 package com.android.systemui.statusbar.notification.domain.interactor
 
+import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.statusbar.notification.collection.render.NotifStats
 import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository
 import com.android.systemui.statusbar.notification.shared.ActiveNotificationGroupModel
 import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel
+import com.android.systemui.statusbar.notification.shared.CallType
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.flow.Flow
@@ -27,6 +29,7 @@
 import kotlinx.coroutines.flow.flowOn
 import kotlinx.coroutines.flow.map
 
+@SysUISingleton
 class ActiveNotificationsInteractor
 @Inject
 constructor(
@@ -71,6 +74,19 @@
     val allNotificationsCountValue: Int
         get() = repository.activeNotifications.value.individuals.size
 
+    /**
+     * The priority ongoing call notification, or null if there is no ongoing call.
+     *
+     * The output model is guaranteed to have [ActiveNotificationModel.callType] to be equal to
+     * [CallType.Ongoing].
+     */
+    val ongoingCallNotification: Flow<ActiveNotificationModel?> =
+        allRepresentativeNotifications.map { notifMap ->
+            // Once a call has started, its `whenTime` should stay the same, so we can use it as a
+            // stable sort value.
+            notifMap.values.filter { it.callType == CallType.Ongoing }.minByOrNull { it.whenTime }
+        }
+
     /** Are any notifications being actively presented in the notification stack? */
     val areAnyNotificationsPresent: Flow<Boolean> =
         repository.activeNotifications
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationListInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationListInteractor.kt
index ab54bda..1008451 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationListInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationListInteractor.kt
@@ -15,9 +15,18 @@
  */
 package com.android.systemui.statusbar.notification.domain.interactor
 
+import android.app.Notification.CallStyle.CALL_TYPE_INCOMING
+import android.app.Notification.CallStyle.CALL_TYPE_ONGOING
+import android.app.Notification.CallStyle.CALL_TYPE_SCREENING
+import android.app.Notification.CallStyle.CALL_TYPE_UNKNOWN
+import android.app.Notification.EXTRA_CALL_TYPE
+import android.app.PendingIntent
 import android.graphics.drawable.Icon
+import android.service.notification.StatusBarNotification
 import android.util.ArrayMap
 import com.android.app.tracing.traceSection
+import com.android.systemui.Flags
+import com.android.systemui.statusbar.StatusBarIconView
 import com.android.systemui.statusbar.notification.collection.GroupEntry
 import com.android.systemui.statusbar.notification.collection.ListEntry
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
@@ -27,6 +36,7 @@
 import com.android.systemui.statusbar.notification.shared.ActiveNotificationEntryModel
 import com.android.systemui.statusbar.notification.shared.ActiveNotificationGroupModel
 import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel
+import com.android.systemui.statusbar.notification.shared.CallType
 import javax.inject.Inject
 import kotlinx.coroutines.flow.update
 
@@ -120,10 +130,17 @@
         return result
     }
 
-    private fun NotificationEntry.toModel(): ActiveNotificationModel =
-        existingModels.createOrReuse(
+    private fun NotificationEntry.toModel(): ActiveNotificationModel {
+        val statusBarChipIcon =
+            if (Flags.statusBarCallChipNotificationIcon()) {
+                icons.statusBarChipIcon
+            } else {
+                null
+            }
+        return existingModels.createOrReuse(
             key = key,
             groupKey = sbn.groupKey,
+            whenTime = sbn.notification.`when`,
             isAmbient = sectionStyleProvider.isMinimized(this),
             isRowDismissed = isRowDismissed,
             isSilent = sectionStyleProvider.isSilent(this),
@@ -133,17 +150,22 @@
             aodIcon = icons.aodIcon?.sourceIcon,
             shelfIcon = icons.shelfIcon?.sourceIcon,
             statusBarIcon = icons.statusBarIcon?.sourceIcon,
+            statusBarChipIconView = statusBarChipIcon,
             uid = sbn.uid,
             packageName = sbn.packageName,
+            contentIntent = sbn.notification.contentIntent,
             instanceId = sbn.instanceId?.id,
             isGroupSummary = sbn.notification.isGroupSummary,
             bucket = bucket,
+            callType = sbn.toCallType(),
         )
+    }
 }
 
 private fun ActiveNotificationsStore.createOrReuse(
     key: String,
     groupKey: String?,
+    whenTime: Long,
     isAmbient: Boolean,
     isRowDismissed: Boolean,
     isSilent: Boolean,
@@ -153,16 +175,20 @@
     aodIcon: Icon?,
     shelfIcon: Icon?,
     statusBarIcon: Icon?,
+    statusBarChipIconView: StatusBarIconView?,
     uid: Int,
     packageName: String,
+    contentIntent: PendingIntent?,
     instanceId: Int?,
     isGroupSummary: Boolean,
     bucket: Int,
+    callType: CallType,
 ): ActiveNotificationModel {
     return individuals[key]?.takeIf {
         it.isCurrent(
             key = key,
             groupKey = groupKey,
+            whenTime = whenTime,
             isAmbient = isAmbient,
             isRowDismissed = isRowDismissed,
             isSilent = isSilent,
@@ -172,16 +198,20 @@
             aodIcon = aodIcon,
             shelfIcon = shelfIcon,
             statusBarIcon = statusBarIcon,
+            statusBarChipIconView = statusBarChipIconView,
             uid = uid,
             instanceId = instanceId,
             isGroupSummary = isGroupSummary,
             packageName = packageName,
+            contentIntent = contentIntent,
             bucket = bucket,
+            callType = callType,
         )
     }
         ?: ActiveNotificationModel(
             key = key,
             groupKey = groupKey,
+            whenTime = whenTime,
             isAmbient = isAmbient,
             isRowDismissed = isRowDismissed,
             isSilent = isSilent,
@@ -191,17 +221,21 @@
             aodIcon = aodIcon,
             shelfIcon = shelfIcon,
             statusBarIcon = statusBarIcon,
+            statusBarChipIconView = statusBarChipIconView,
             uid = uid,
             instanceId = instanceId,
             isGroupSummary = isGroupSummary,
             packageName = packageName,
+            contentIntent = contentIntent,
             bucket = bucket,
+            callType = callType,
         )
 }
 
 private fun ActiveNotificationModel.isCurrent(
     key: String,
     groupKey: String?,
+    whenTime: Long,
     isAmbient: Boolean,
     isRowDismissed: Boolean,
     isSilent: Boolean,
@@ -211,15 +245,19 @@
     aodIcon: Icon?,
     shelfIcon: Icon?,
     statusBarIcon: Icon?,
+    statusBarChipIconView: StatusBarIconView?,
     uid: Int,
     packageName: String,
+    contentIntent: PendingIntent?,
     instanceId: Int?,
     isGroupSummary: Boolean,
     bucket: Int,
+    callType: CallType,
 ): Boolean {
     return when {
         key != this.key -> false
         groupKey != this.groupKey -> false
+        whenTime != this.whenTime -> false
         isAmbient != this.isAmbient -> false
         isRowDismissed != this.isRowDismissed -> false
         isSilent != this.isSilent -> false
@@ -229,11 +267,14 @@
         aodIcon != this.aodIcon -> false
         shelfIcon != this.shelfIcon -> false
         statusBarIcon != this.statusBarIcon -> false
+        statusBarChipIconView != this.statusBarChipIconView -> false
         uid != this.uid -> false
         instanceId != this.instanceId -> false
         isGroupSummary != this.isGroupSummary -> false
         packageName != this.packageName -> false
+        contentIntent != this.contentIntent -> false
         bucket != this.bucket -> false
+        callType != this.callType -> false
         else -> true
     }
 }
@@ -259,3 +300,13 @@
         else -> true
     }
 }
+
+private fun StatusBarNotification.toCallType(): CallType =
+    when (this.notification.extras.getInt(EXTRA_CALL_TYPE, -1)) {
+        -1 -> CallType.None
+        CALL_TYPE_INCOMING -> CallType.Incoming
+        CALL_TYPE_ONGOING -> CallType.Ongoing
+        CALL_TYPE_SCREENING -> CallType.Screening
+        CALL_TYPE_UNKNOWN -> CallType.Unknown
+        else -> CallType.Unknown
+    }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/SeenNotificationsInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/SeenNotificationsInteractor.kt
index 948a3c2..2956432 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/SeenNotificationsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/SeenNotificationsInteractor.kt
@@ -16,21 +16,35 @@
 
 package com.android.systemui.statusbar.notification.domain.interactor
 
+import android.os.UserHandle
+import android.provider.Settings
 import android.util.IndentingPrintWriter
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
 import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository
 import com.android.systemui.statusbar.notification.shared.NotificationMinimalismPrototype
 import com.android.systemui.util.printSection
+import com.android.systemui.util.settings.SecureSettings
+import com.android.systemui.util.settings.SettingsProxyExt.observerFlow
 import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.conflate
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onStart
 
 /** Interactor for business logic associated with the notification stack. */
 @SysUISingleton
 class SeenNotificationsInteractor
 @Inject
 constructor(
+    @Background private val bgDispatcher: CoroutineDispatcher,
     private val notificationListRepository: ActiveNotificationListRepository,
+    private val secureSettings: SecureSettings,
 ) {
     /** Are any already-seen notifications currently filtered out of the shade? */
     val hasFilteredOutSeenNotifications: StateFlow<Boolean> =
@@ -81,4 +95,29 @@
                 )
             }
         }
+
+    fun isLockScreenShowOnlyUnseenNotificationsEnabled(): Flow<Boolean> =
+        secureSettings
+            // emit whenever the setting has changed
+            .observerFlow(
+                UserHandle.USER_ALL,
+                Settings.Secure.LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS,
+            )
+            // perform a query immediately
+            .onStart { emit(Unit) }
+            // for each change, lookup the new value
+            .map {
+                secureSettings.getIntForUser(
+                    name = Settings.Secure.LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS,
+                    default = 0,
+                    userHandle = UserHandle.USER_CURRENT,
+                ) == 1
+            }
+            // don't emit anything if nothing has changed
+            .distinctUntilChanged()
+            // perform lookups on the bg thread pool
+            .flowOn(bgDispatcher)
+            // only track the most recent emission, if events are happening faster than they can be
+            // consumed
+            .conflate()
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt
index 331d3cc..dc6ab41 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt
@@ -123,6 +123,13 @@
             // Construct the status bar icon view.
             val sbIcon = iconBuilder.createIconView(entry)
             sbIcon.scaleType = ImageView.ScaleType.CENTER_INSIDE
+            val sbChipIcon: StatusBarIconView?
+            if (Flags.statusBarCallChipNotificationIcon()) {
+                sbChipIcon = iconBuilder.createIconView(entry)
+                sbChipIcon.scaleType = ImageView.ScaleType.CENTER_INSIDE
+            } else {
+                sbChipIcon = null
+            }
 
             // Construct the shelf icon view.
             val shelfIcon = iconBuilder.createIconView(entry)
@@ -139,9 +146,19 @@
 
             try {
                 setIcon(entry, normalIconDescriptor, sbIcon)
+                if (Flags.statusBarCallChipNotificationIcon() && sbChipIcon != null) {
+                    setIcon(entry, normalIconDescriptor, sbChipIcon)
+                }
                 setIcon(entry, sensitiveIconDescriptor, shelfIcon)
                 setIcon(entry, sensitiveIconDescriptor, aodIcon)
-                entry.icons = IconPack.buildPack(sbIcon, shelfIcon, aodIcon, entry.icons)
+                entry.icons =
+                    IconPack.buildPack(
+                        sbIcon,
+                        sbChipIcon,
+                        shelfIcon,
+                        aodIcon,
+                        entry.icons,
+                    )
             } catch (e: InflationException) {
                 entry.icons = IconPack.buildEmptyPack(entry.icons)
                 throw e
@@ -182,6 +199,11 @@
                 setIcon(entry, normalIconDescriptor, it)
             }
 
+            entry.icons.statusBarChipIcon?.let {
+                it.setNotification(entry.sbn, notificationContentDescription)
+                setIcon(entry, normalIconDescriptor, it)
+            }
+
             entry.icons.shelfIcon?.let {
                 it.setNotification(entry.sbn, notificationContentDescription)
                 setIcon(entry, sensitiveIconDescriptor, it)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconPack.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconPack.java
index d029ce7..611cebc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconPack.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconPack.java
@@ -29,6 +29,7 @@
 
     private final boolean mAreIconsAvailable;
     @Nullable private final StatusBarIconView mStatusBarIcon;
+    @Nullable private final StatusBarIconView mStatusBarChipIcon;
     @Nullable private final StatusBarIconView mShelfIcon;
     @Nullable private final StatusBarIconView mAodIcon;
 
@@ -43,7 +44,7 @@
      * haven't been inflated yet or there was an error while inflating them).
      */
     public static IconPack buildEmptyPack(@Nullable IconPack fromSource) {
-        return new IconPack(false, null, null, null, fromSource);
+        return new IconPack(false, null, null, null, null, fromSource);
     }
 
     /**
@@ -51,20 +52,23 @@
      */
     public static IconPack buildPack(
             @NonNull StatusBarIconView statusBarIcon,
+            @Nullable StatusBarIconView statusBarChipIcon,
             @NonNull StatusBarIconView shelfIcon,
             @NonNull StatusBarIconView aodIcon,
             @Nullable IconPack source) {
-        return new IconPack(true, statusBarIcon, shelfIcon, aodIcon, source);
+        return new IconPack(true, statusBarIcon, statusBarChipIcon, shelfIcon, aodIcon, source);
     }
 
     private IconPack(
             boolean areIconsAvailable,
             @Nullable StatusBarIconView statusBarIcon,
+            @Nullable StatusBarIconView statusBarChipIcon,
             @Nullable StatusBarIconView shelfIcon,
             @Nullable StatusBarIconView aodIcon,
             @Nullable IconPack source) {
         mAreIconsAvailable = areIconsAvailable;
         mStatusBarIcon = statusBarIcon;
+        mStatusBarChipIcon = statusBarChipIcon;
         mShelfIcon = shelfIcon;
         mAodIcon = aodIcon;
         if (source != null) {
@@ -79,6 +83,17 @@
     }
 
     /**
+     * The version of the notification icon that appears inside a chip within the status bar.
+     *
+     * Separate from {@link #getStatusBarIcon()} so that we don't have to worry about detaching and
+     * re-attaching the same view when the chip appears and hides.
+     */
+    @Nullable
+    public StatusBarIconView getStatusBarChipIcon() {
+        return mStatusBarChipIcon;
+    }
+
+    /**
      * The version of the icon that appears in the "shelf" at the bottom of the notification shade.
      * In general, this icon also appears somewhere on the notification and is "sucked" into the
      * shelf as the scrolls beyond it.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt
index e04e0fa..17f401a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt
@@ -19,6 +19,9 @@
 import android.Manifest.permission.RECEIVE_EMERGENCY_BROADCAST
 import android.app.Notification
 import android.app.Notification.BubbleMetadata
+import android.app.Notification.CATEGORY_ALARM
+import android.app.Notification.CATEGORY_CAR_EMERGENCY
+import android.app.Notification.CATEGORY_CAR_WARNING
 import android.app.Notification.CATEGORY_EVENT
 import android.app.Notification.CATEGORY_REMINDER
 import android.app.Notification.VISIBILITY_PRIVATE
@@ -42,10 +45,12 @@
 import android.service.notification.Flags
 import com.android.internal.logging.UiEvent
 import com.android.internal.logging.UiEventLogger
+import com.android.internal.logging.UiEventLogger.UiEventEnum.RESERVE_NEW_UI_EVENT_ID
 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.settings.UserTracker
+import com.android.systemui.shared.notifications.domain.interactor.NotificationSettingsInteractor
 import com.android.systemui.statusbar.StatusBarState.SHADE
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
 import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl.MAX_HUN_WHEN_AGE_MS
@@ -57,7 +62,6 @@
 import com.android.systemui.statusbar.policy.HeadsUpManager
 import com.android.systemui.util.NotificationChannels
 import com.android.systemui.util.settings.GlobalSettings
-import com.android.systemui.util.settings.SystemSettings
 import com.android.systemui.util.time.SystemClock
 import com.android.wm.shell.bubbles.Bubbles
 import java.util.Optional
@@ -148,12 +152,12 @@
         }
 }
 
-class PeekDndSuppressor() :
+class PeekDndSuppressor :
     VisualInterruptionFilter(types = setOf(PEEK), reason = "suppressed by DND") {
     override fun shouldSuppress(entry: NotificationEntry) = entry.shouldSuppressPeek()
 }
 
-class PeekNotImportantSuppressor() :
+class PeekNotImportantSuppressor :
     VisualInterruptionFilter(types = setOf(PEEK), reason = "importance < HIGH") {
     override fun shouldSuppress(entry: NotificationEntry) = entry.importance < IMPORTANCE_HIGH
 }
@@ -194,12 +198,12 @@
         }
 }
 
-class PulseEffectSuppressor() :
+class PulseEffectSuppressor :
     VisualInterruptionFilter(types = setOf(PULSE), reason = "suppressed by DND") {
     override fun shouldSuppress(entry: NotificationEntry) = entry.shouldSuppressAmbient()
 }
 
-class PulseLockscreenVisibilityPrivateSuppressor() :
+class PulseLockscreenVisibilityPrivateSuppressor :
     VisualInterruptionFilter(
         types = setOf(PULSE),
         reason = "hidden by lockscreen visibility override"
@@ -208,12 +212,12 @@
         entry.ranking.lockscreenVisibilityOverride == VISIBILITY_PRIVATE
 }
 
-class PulseLowImportanceSuppressor() :
+class PulseLowImportanceSuppressor :
     VisualInterruptionFilter(types = setOf(PULSE), reason = "importance < DEFAULT") {
     override fun shouldSuppress(entry: NotificationEntry) = entry.importance < IMPORTANCE_DEFAULT
 }
 
-class HunGroupAlertBehaviorSuppressor() :
+class HunGroupAlertBehaviorSuppressor :
     VisualInterruptionFilter(
         types = setOf(PEEK, PULSE),
         reason = "suppressive group alert behavior"
@@ -222,26 +226,23 @@
         entry.sbn.let { it.isGroup && it.notification.suppressAlertingDueToGrouping() }
 }
 
-class HunSilentNotificationSuppressor() :
-    VisualInterruptionFilter(
-        types = setOf(PEEK, PULSE),
-        reason = "notification isSilent"
-    ) {
+class HunSilentNotificationSuppressor :
+    VisualInterruptionFilter(types = setOf(PEEK, PULSE), reason = "notification isSilent") {
     override fun shouldSuppress(entry: NotificationEntry) =
         entry.sbn.let { Flags.notificationSilentFlag() && it.notification.isSilent }
 }
 
-class HunJustLaunchedFsiSuppressor() :
+class HunJustLaunchedFsiSuppressor :
     VisualInterruptionFilter(types = setOf(PEEK, PULSE), reason = "just launched FSI") {
     override fun shouldSuppress(entry: NotificationEntry) = entry.hasJustLaunchedFullScreenIntent()
 }
 
-class BubbleNotAllowedSuppressor() :
-    VisualInterruptionFilter(types = setOf(BUBBLE), reason = "cannot bubble") {
+class BubbleNotAllowedSuppressor :
+    VisualInterruptionFilter(types = setOf(BUBBLE), reason = "cannot bubble", isSpammy = true) {
     override fun shouldSuppress(entry: NotificationEntry) = !entry.canBubble()
 }
 
-class BubbleNoMetadataSuppressor() :
+class BubbleNoMetadataSuppressor :
     VisualInterruptionFilter(types = setOf(BUBBLE), reason = "has no or invalid bubble metadata") {
 
     private fun isValidMetadata(metadata: BubbleMetadata?) =
@@ -264,6 +265,7 @@
 
 /**
  * Set with:
+ *
  * adb shell setprop persist.force_show_avalanche_edu_once 1 && adb shell stop; adb shell start
  */
 private const val FORCE_SHOW_AVALANCHE_EDU_ONCE = "persist.force_show_avalanche_edu_once"
@@ -273,7 +275,7 @@
 class AvalancheSuppressor(
     private val avalancheProvider: AvalancheProvider,
     private val systemClock: SystemClock,
-    private val systemSettings: SystemSettings,
+    private val settingsInteractor: NotificationSettingsInteractor,
     private val packageManager: PackageManager,
     private val uiEventLogger: UiEventLogger,
     private val context: Context,
@@ -298,7 +300,7 @@
     // education HUNs.
     private var hasShownOnceForDebug = false
 
-    private fun shouldShowEdu() : Boolean {
+    private fun shouldShowEdu(): Boolean {
         val forceShowOnce = SystemProperties.get(FORCE_SHOW_AVALANCHE_EDU_ONCE, "").equals("1")
         return !hasSeenEdu || (forceShowOnce && !hasShownOnceForDebug)
     }
@@ -309,6 +311,9 @@
         ALLOW_CALLSTYLE,
         ALLOW_CATEGORY_REMINDER,
         ALLOW_CATEGORY_EVENT,
+        ALLOW_CATEGORY_ALARM,
+        ALLOW_CATEGORY_CAR_EMERGENCY,
+        ALLOW_CATEGORY_CAR_WARNING,
         ALLOW_FSI_WITH_PERMISSION_ON,
         ALLOW_COLORIZED,
         ALLOW_EMERGENCY,
@@ -335,8 +340,13 @@
         @UiEvent(doc = "HUN allowed during avalanche because it is colorized.")
         AVALANCHE_SUPPRESSOR_HUN_ALLOWED_COLORIZED(1832),
         @UiEvent(doc = "HUN allowed during avalanche because it is an emergency notification.")
-        AVALANCHE_SUPPRESSOR_HUN_ALLOWED_EMERGENCY(1833);
-
+        AVALANCHE_SUPPRESSOR_HUN_ALLOWED_EMERGENCY(1833),
+        @UiEvent(doc = "HUN allowed during avalanche because it is an alarm.")
+        AVALANCHE_SUPPRESSOR_HUN_ALLOWED_CATEGORY_ALARM(1867),
+        @UiEvent(doc = "HUN allowed during avalanche because it is a car emergency.")
+        AVALANCHE_SUPPRESSOR_HUN_ALLOWED_CATEGORY_CAR_EMERGENCY(1868),
+        @UiEvent(doc = "HUN allowed during avalanche because it is a car warning")
+        AVALANCHE_SUPPRESSOR_HUN_ALLOWED_CATEGORY_CAR_WARNING(1869);
         override fun getId(): Int {
             return id
         }
@@ -361,34 +371,33 @@
         return true
     }
 
-    /**
-     * Show avalanche education HUN from SystemUI.
-     */
+    /** Show avalanche education HUN from SystemUI. */
     private fun showEdu() {
         val res = context.resources
-        val titleStr = res.getString(
-            com.android.systemui.res.R.string.adaptive_notification_edu_hun_title)
-        val textStr = res.getString(
-            com.android.systemui.res.R.string.adaptive_notification_edu_hun_text)
-        val actionStr = res.getString(
-            com.android.systemui.res.R.string.go_to_adaptive_notification_settings)
+        val titleStr =
+            res.getString(com.android.systemui.res.R.string.adaptive_notification_edu_hun_title)
+        val textStr =
+            res.getString(com.android.systemui.res.R.string.adaptive_notification_edu_hun_text)
+        val actionStr =
+            res.getString(com.android.systemui.res.R.string.go_to_adaptive_notification_settings)
 
         val intent = Intent(Settings.ACTION_MANAGE_ADAPTIVE_NOTIFICATIONS)
-        val pendingIntent = PendingIntent.getActivity(
-            context, 0, intent,
-            PendingIntent.FLAG_IMMUTABLE
-        )
+        val pendingIntent =
+            PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_IMMUTABLE)
 
         // Replace "System UI" app name with "Android System"
         val bundle = Bundle()
-        bundle.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME,
-                context.getString(com.android.internal.R.string.android_system_label))
+        bundle.putString(
+            Notification.EXTRA_SUBSTITUTE_APP_NAME,
+            context.getString(com.android.internal.R.string.android_system_label)
+        )
 
         val builder =
             Notification.Builder(context, NotificationChannels.ALERTS)
                 .setTicker(titleStr)
                 .setContentTitle(titleStr)
                 .setContentText(textStr)
+                .setStyle(Notification.BigTextStyle().bigText(textStr))
                 .setSmallIcon(com.android.systemui.res.R.drawable.ic_settings)
                 .setCategory(Notification.CATEGORY_SYSTEM)
                 .setTimeoutAfter(/* one day in ms */ 24 * 60 * 60 * 1000L)
@@ -399,7 +408,7 @@
 
         notificationManager.notify(SystemMessage.NOTE_ADAPTIVE_NOTIFICATIONS, builder.build())
         hasSeenEdu = true
-        hasShownOnceForDebug = true;
+        hasShownOnceForDebug = true
     }
 
     private fun calculateState(entry: NotificationEntry): State {
@@ -426,6 +435,22 @@
             return State.ALLOW_CATEGORY_REMINDER
         }
 
+        if (entry.sbn.notification.category == CATEGORY_ALARM) {
+            uiEventLogger.log(AvalancheEvent.AVALANCHE_SUPPRESSOR_HUN_ALLOWED_CATEGORY_ALARM)
+            return State.ALLOW_CATEGORY_ALARM
+        }
+
+        if (entry.sbn.notification.category == CATEGORY_CAR_EMERGENCY) {
+            uiEventLogger.log(
+                    AvalancheEvent.AVALANCHE_SUPPRESSOR_HUN_ALLOWED_CATEGORY_CAR_EMERGENCY)
+            return State.ALLOW_CATEGORY_CAR_EMERGENCY
+        }
+
+        if (entry.sbn.notification.category == CATEGORY_CAR_WARNING) {
+            uiEventLogger.log(AvalancheEvent.AVALANCHE_SUPPRESSOR_HUN_ALLOWED_CATEGORY_CAR_WARNING)
+            return State.ALLOW_CATEGORY_CAR_WARNING
+        }
+
         if (entry.sbn.notification.category == CATEGORY_EVENT) {
             uiEventLogger.log(AvalancheEvent.AVALANCHE_SUPPRESSOR_HUN_ALLOWED_CATEGORY_EVENT)
             return State.ALLOW_CATEGORY_EVENT
@@ -451,7 +476,6 @@
     }
 
     private fun isCooldownEnabled(): Boolean {
-        return systemSettings.getInt(Settings.System.NOTIFICATION_COOLDOWN_ENABLED, /* def */ 1) ==
-            1
+        return settingsInteractor.isCooldownEnabled.value
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionLogger.kt
index 1470b03..c204ea9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionLogger.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.statusbar.notification.interruption
 
+import android.util.Log
 import com.android.systemui.log.LogBuffer
 import com.android.systemui.log.core.LogLevel.DEBUG
 import com.android.systemui.log.core.LogLevel.INFO
@@ -24,11 +25,15 @@
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
 import com.android.systemui.statusbar.notification.interruption.VisualInterruptionDecisionProvider.FullScreenIntentDecision
 import com.android.systemui.statusbar.notification.logKey
+import com.android.systemui.util.Compile
 import javax.inject.Inject
 
 class VisualInterruptionDecisionLogger
 @Inject
 constructor(@NotificationInterruptLog val buffer: LogBuffer) {
+
+    val spew: Boolean = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.VERBOSE)
+
     fun logHeadsUpFeatureChanged(isEnabled: Boolean) {
         buffer.log(
             TAG,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt
index 1c476ce..8e8d9b6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt
@@ -29,6 +29,7 @@
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.settings.UserTracker
+import com.android.systemui.shared.notifications.domain.interactor.NotificationSettingsInteractor
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
 import com.android.systemui.statusbar.notification.interruption.VisualInterruptionDecisionProvider.Decision
 import com.android.systemui.statusbar.notification.interruption.VisualInterruptionDecisionProvider.FullScreenIntentDecision
@@ -72,7 +73,8 @@
     private val packageManager: PackageManager,
     private val bubbles: Optional<Bubbles>,
     private val context: Context,
-    private val notificationManager: NotificationManager
+    private val notificationManager: NotificationManager,
+    private val settingsInteractor: NotificationSettingsInteractor
 ) : VisualInterruptionDecisionProvider {
 
     init {
@@ -93,7 +95,8 @@
     private constructor(
         val decision: DecisionImpl,
         override val uiEventId: UiEventEnum? = null,
-        override val eventLogData: EventLogData? = null
+        override val eventLogData: EventLogData? = null,
+        val isSpammy: Boolean = false,
     ) : Loggable {
         companion object {
             val unsuppressed =
@@ -111,7 +114,8 @@
                 LoggableDecision(
                     DecisionImpl(shouldInterrupt = false, logReason = suppressor.reason),
                     uiEventId = suppressor.uiEventId,
-                    eventLogData = suppressor.eventLogData
+                    eventLogData = suppressor.eventLogData,
+                    isSpammy = suppressor.isSpammy,
                 )
         }
     }
@@ -183,8 +187,15 @@
 
         if (NotificationAvalancheSuppression.isEnabled) {
             addFilter(
-                AvalancheSuppressor(avalancheProvider, systemClock, systemSettings, packageManager,
-                        uiEventLogger, context, notificationManager)
+                AvalancheSuppressor(
+                    avalancheProvider,
+                    systemClock,
+                    settingsInteractor,
+                    packageManager,
+                    uiEventLogger,
+                    context,
+                    notificationManager
+                )
             )
             avalancheProvider.register()
         }
@@ -278,7 +289,9 @@
         entry: NotificationEntry,
         loggableDecision: LoggableDecision
     ) {
-        logger.logDecision(type.name, entry, loggableDecision.decision)
+        if (!loggableDecision.isSpammy || logger.spew) {
+            logger.logDecision(type.name, entry, loggableDecision.decision)
+        }
         logEvents(entry, loggableDecision)
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionSuppressor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionSuppressor.kt
index ee79727..5fe75c0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionSuppressor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionSuppressor.kt
@@ -59,6 +59,10 @@
     /** Optional data to be logged in the EventLog when this suppresses an interruption. */
     val eventLogData: EventLogData?
 
+    /** Whether the interruption is spammy and should be dropped under normal circumstances. */
+    val isSpammy: Boolean
+        get() = false
+
     /**
      * Called after the suppressor is added to the [VisualInterruptionDecisionProvider] but before
      * any other methods are called on the suppressor.
@@ -76,7 +80,7 @@
     constructor(
         types: Set<VisualInterruptionType>,
         reason: String
-    ) : this(types, reason, /* uiEventId = */ null)
+    ) : this(types, reason, /* uiEventId= */ null)
 
     /** @return true if these interruptions should be suppressed right now. */
     abstract fun shouldSuppress(): Boolean
@@ -87,12 +91,13 @@
     override val types: Set<VisualInterruptionType>,
     override val reason: String,
     override val uiEventId: UiEventEnum? = null,
-    override val eventLogData: EventLogData? = null
+    override val eventLogData: EventLogData? = null,
+    override val isSpammy: Boolean = false,
 ) : VisualInterruptionSuppressor {
     constructor(
         types: Set<VisualInterruptionType>,
         reason: String
-    ) : this(types, reason, /* uiEventId = */ null)
+    ) : this(types, reason, /* uiEventId= */ null)
 
     /**
      * @param entry the notification to consider suppressing
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 1cbb16e..06af980 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
@@ -68,6 +68,7 @@
 import com.android.app.animation.Interpolators;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.UiEventLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.internal.util.ContrastColorUtil;
@@ -1873,7 +1874,8 @@
             SmartReplyConstants smartReplyConstants,
             SmartReplyController smartReplyController,
             FeatureFlags featureFlags,
-            IStatusBarService statusBarService) {
+            IStatusBarService statusBarService,
+            UiEventLogger uiEventLogger) {
         mEntry = entry;
         mAppName = appName;
         if (mMenuRow == null) {
@@ -1901,7 +1903,9 @@
                     rivSubcomponentFactory,
                     smartReplyConstants,
                     smartReplyController,
-                    statusBarService);
+                    statusBarService,
+                    uiEventLogger
+            );
         }
         mOnUserInteractionCallback = onUserInteractionCallback;
         mBubblesManagerOptional = bubblesManagerOptional;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
index e59829b..4c76e328 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
@@ -33,6 +33,7 @@
 import androidx.annotation.VisibleForTesting;
 
 import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.UiEventLogger;
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.flags.Flags;
@@ -112,6 +113,7 @@
     private final ExpandableNotificationRowDragController mDragController;
     private final NotificationDismissibilityProvider mDismissibilityProvider;
     private final IStatusBarService mStatusBarService;
+    private final UiEventLogger mUiEventLogger;
 
     private final NotificationSettingsController mSettingsController;
 
@@ -230,7 +232,8 @@
             NotificationSettingsController settingsController,
             ExpandableNotificationRowDragController dragController,
             NotificationDismissibilityProvider dismissibilityProvider,
-            IStatusBarService statusBarService) {
+            IStatusBarService statusBarService,
+            UiEventLogger uiEventLogger) {
         mView = view;
         mListContainer = listContainer;
         mRemoteInputViewSubcomponentFactory = rivSubcomponentFactory;
@@ -265,6 +268,7 @@
         mSmartReplyController = smartReplyController;
         mDismissibilityProvider = dismissibilityProvider;
         mStatusBarService = statusBarService;
+        mUiEventLogger = uiEventLogger;
     }
 
     /**
@@ -298,7 +302,8 @@
                 mSmartReplyConstants,
                 mSmartReplyController,
                 mFeatureFlags,
-                mStatusBarService
+                mStatusBarService,
+                mUiEventLogger
         );
         mView.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
         if (mAllowLongPress) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HeadsUpStyleProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HeadsUpStyleProvider.kt
index 9d0fcd3..245b267 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HeadsUpStyleProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HeadsUpStyleProvider.kt
@@ -19,6 +19,7 @@
 import android.app.Flags
 import android.os.SystemProperties
 import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryStore
+import com.android.systemui.util.Compile
 import javax.inject.Inject
 
 /**
@@ -43,5 +44,6 @@
 
     /** developer setting to always show Minimal HUN, even if the device is not in full screen */
     private fun alwaysShow() =
-        SystemProperties.getBoolean("persist.compact_heads_up_notification.always_show", false)
+        Compile.IS_DEBUG &&
+            SystemProperties.getBoolean("persist.compact_heads_up_notification.always_show", false)
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/PartitionedGridLayoutKosmos.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationCompactHeadsUpEvent.kt
similarity index 61%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/PartitionedGridLayoutKosmos.kt
copy to packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationCompactHeadsUpEvent.kt
index 37c9552..ab5731c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/PartitionedGridLayoutKosmos.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationCompactHeadsUpEvent.kt
@@ -14,11 +14,13 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.panels.domain.interactor
+package com.android.systemui.statusbar.notification.row
 
-import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.qs.panels.ui.compose.PartitionedGridLayout
-import com.android.systemui.qs.panels.ui.viewmodel.partitionedGridViewModel
+import com.android.internal.logging.UiEvent
+import com.android.internal.logging.UiEventLogger
 
-val Kosmos.partitionedGridLayout by
-    Kosmos.Fixture { PartitionedGridLayout(partitionedGridViewModel) }
+enum class NotificationCompactHeadsUpEvent(val eventId: Int) : UiEventLogger.UiEventEnum {
+    @UiEvent(doc = "Minimal HUN is shown to the user") NOTIFICATION_COMPACT_HUN_SHOWN(1857);
+
+    override fun getId(): Int = eventId
+}
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 646d0b1..3f46902 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
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.Flags;
 import android.app.Notification;
 import android.app.PendingIntent;
 import android.content.Context;
@@ -45,6 +46,7 @@
 import androidx.annotation.MainThread;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.logging.UiEventLogger;
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
 import com.android.systemui.res.R;
@@ -58,6 +60,7 @@
 import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
 import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
 import com.android.systemui.statusbar.notification.row.shared.AsyncHybridViewInflation;
+import com.android.systemui.statusbar.notification.row.wrapper.NotificationCompactHeadsUpTemplateViewWrapper;
 import com.android.systemui.statusbar.notification.row.wrapper.NotificationCustomViewWrapper;
 import com.android.systemui.statusbar.notification.row.wrapper.NotificationHeaderViewWrapper;
 import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
@@ -205,6 +208,7 @@
     private int mUnrestrictedContentHeight;
 
     private boolean mContentAnimating;
+    private UiEventLogger mUiEventLogger;
 
     public NotificationContentView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -217,12 +221,14 @@
             RemoteInputViewSubcomponent.Factory rivSubcomponentFactory,
             SmartReplyConstants smartReplyConstants,
             SmartReplyController smartReplyController,
-            IStatusBarService statusBarService) {
+            IStatusBarService statusBarService,
+            UiEventLogger uiEventLogger) {
         mPeopleIdentifier = peopleNotificationIdentifier;
         mRemoteInputSubcomponentFactory = rivSubcomponentFactory;
         mSmartReplyConstants = smartReplyConstants;
         mSmartReplyController = smartReplyController;
         mStatusBarService = statusBarService;
+        mUiEventLogger = uiEventLogger;
         // We set root namespace so that we avoid searching children for id. Notification  might
         // contain custom view and their ids may clash with ids already existing in shade or
         // notification panel
@@ -552,6 +558,12 @@
         mHeadsUpChild = child;
         mHeadsUpWrapper = NotificationViewWrapper.wrap(getContext(), child,
                 mContainingNotification);
+
+        if (Flags.compactHeadsUpNotification()
+                && mHeadsUpWrapper instanceof NotificationCompactHeadsUpTemplateViewWrapper) {
+            logCompactHUNShownEvent();
+        }
+
         if (mContainingNotification != null) {
             applySystemActions(mHeadsUpChild, mContainingNotification.getEntry());
         }
@@ -559,6 +571,15 @@
         updateShownWrapper(mVisibleType);
     }
 
+    private void logCompactHUNShownEvent() {
+        if (mUiEventLogger == null) {
+            return;
+        }
+
+        mUiEventLogger.log(
+                NotificationCompactHeadsUpEvent.NOTIFICATION_COMPACT_HUN_SHOWN);
+    }
+
     /**
      * Sets the single-line view. Child may be null to remove the view.
      * @param child single-line content view to set
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowInflaterTask.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowInflaterTask.java
index 35afda7..9f634be 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowInflaterTask.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowInflaterTask.java
@@ -19,6 +19,7 @@
 import android.content.Context;
 import android.util.AttributeSet;
 import android.util.Log;
+import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 
@@ -89,10 +90,59 @@
         inflater.inflate(R.layout.status_bar_notification_row, parent, listenerExecutor, this);
     }
 
+    /**
+     * Inflates a new notificationView synchronously.
+     * This method is only for testing-purpose.
+     */
+    @VisibleForTesting
+    public ExpandableNotificationRow inflateSynchronously(@NonNull Context context,
+            @Nullable ViewGroup parent, @NonNull NotificationEntry entry) {
+        final LayoutInflater inflater = new BasicRowInflater(context);
+        inflater.setFactory2(makeRowInflater(entry));
+        final ExpandableNotificationRow inflate = (ExpandableNotificationRow) inflater.inflate(
+                R.layout.status_bar_notification_row,
+                parent /* root */,
+                false /* attachToRoot */);
+        return inflate;
+    }
+
     private RowAsyncLayoutInflater makeRowInflater(NotificationEntry entry) {
         return new RowAsyncLayoutInflater(entry, mSystemClock, mLogger);
     }
 
+    /**
+     * A {@link LayoutInflater} that is copy of BasicLayoutInflater.
+     */
+    private static class BasicRowInflater extends LayoutInflater {
+        private static final String[] sClassPrefixList =
+                {"android.widget.", "android.webkit.", "android.app."};
+        BasicRowInflater(Context context) {
+            super(context);
+        }
+
+        @Override
+        public LayoutInflater cloneInContext(Context newContext) {
+            return new BasicRowInflater(newContext);
+        }
+
+        @Override
+        protected View onCreateView(String name, AttributeSet attrs) throws ClassNotFoundException {
+            for (String prefix : sClassPrefixList) {
+                try {
+                    View view = createView(name, prefix, attrs);
+                    if (view != null) {
+                        return view;
+                    }
+                } catch (ClassNotFoundException e) {
+                    // In this case we want to let the base class take a crack
+                    // at it.
+                }
+            }
+
+            return super.onCreateView(name, attrs);
+        }
+    }
+
     @VisibleForTesting
     public static class RowAsyncLayoutInflater implements AsyncLayoutFactory {
         private final NotificationEntry mEntry;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/ActiveNotificationModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/ActiveNotificationModel.kt
index 5527efc..cf19938 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/ActiveNotificationModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/ActiveNotificationModel.kt
@@ -15,7 +15,9 @@
 
 package com.android.systemui.statusbar.notification.shared
 
+import android.app.PendingIntent
 import android.graphics.drawable.Icon
+import com.android.systemui.statusbar.StatusBarIconView
 import com.android.systemui.statusbar.notification.stack.PriorityBucket
 
 /**
@@ -32,6 +34,8 @@
     val key: String,
     /** Notification group key associated with this entry. */
     val groupKey: String?,
+    /** When this notification was posted. */
+    val whenTime: Long,
     /** Is this entry in the ambient / minimized section (lowest priority)? */
     val isAmbient: Boolean,
     /**
@@ -56,16 +60,22 @@
     val shelfIcon: Icon?,
     /** Icon to display in the status bar. */
     val statusBarIcon: Icon?,
+    /** Icon to display in the status bar chip. */
+    val statusBarChipIconView: StatusBarIconView?,
     /** The notifying app's [packageName]'s uid. */
     val uid: Int,
     /** The notifying app's packageName. */
     val packageName: String,
+    /** The intent to execute if UI related to this notification is clicked. */
+    val contentIntent: PendingIntent?,
     /** A small per-notification ID, used for statsd logging. */
     val instanceId: Int?,
     /** If this notification is the group summary for a group of notifications. */
     val isGroupSummary: Boolean,
     /** Indicates in which section the notification is displayed in. @see [PriorityBucket]. */
     @PriorityBucket val bucket: Int,
+    /** The call type set on the notification. */
+    val callType: CallType,
 ) : ActiveNotificationEntryModel()
 
 /** Model for a group of notifications. */
@@ -74,3 +84,17 @@
     val summary: ActiveNotificationModel,
     val children: List<ActiveNotificationModel>,
 ) : ActiveNotificationEntryModel()
+
+/** Specifies the call type set on the notification. For most notifications, will be [None]. */
+enum class CallType {
+    /** This notification isn't a call-type notification. */
+    None,
+    /** See [android.app.Notification.CallStyle.CALL_TYPE_INCOMING]. */
+    Incoming,
+    /** See [android.app.Notification.CallStyle.CALL_TYPE_ONGOING]. */
+    Ongoing,
+    /** See [android.app.Notification.CallStyle.CALL_TYPE_SCREENING]. */
+    Screening,
+    /** See [android.app.Notification.CallStyle.CALL_TYPE_UNKNOWN]. */
+    Unknown,
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/GroupHunAnimationFix.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/GroupHunAnimationFix.kt
index 5867612..3b30c86 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/GroupHunAnimationFix.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/GroupHunAnimationFix.kt
@@ -29,7 +29,7 @@
     val token: FlagToken
         get() = FlagToken(FLAG_NAME, isEnabled)
 
-    /** Are sections sorted by time? */
+    /** Return whether the fix is enabled */
     @JvmStatic
     inline val isEnabled
         get() = Flags.notificationGroupHunRemovalAnimationFix()
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 5d2b61b..20b1fff 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
@@ -89,8 +89,10 @@
 import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.flags.Flags;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.qs.flags.QSComposeFragment;
 import com.android.systemui.res.R;
 import com.android.systemui.scene.shared.flag.SceneContainerFlag;
+import com.android.systemui.shade.QSHeaderBoundsProvider;
 import com.android.systemui.shade.TouchLogger;
 import com.android.systemui.statusbar.EmptyShadeView;
 import com.android.systemui.statusbar.NotificationShelf;
@@ -351,6 +353,10 @@
     private final NotificationSection[] mSections;
     private final ArrayList<ExpandableView> mTmpSortedChildren = new ArrayList<>();
     protected ViewGroup mQsHeader;
+
+    @Nullable
+    private QSHeaderBoundsProvider mQSHeaderBoundsProvider;
+
     // Rect of QsHeader. Kept as a field just to avoid creating a new one each time.
     private final Rect mQsHeaderBound = new Rect();
     private boolean mContinuousShadowUpdate;
@@ -1179,7 +1185,21 @@
         if (!SceneContainerFlag.isEnabled()) {
             // Give The Algorithm information regarding the QS height so it can layout notifications
             // properly. Needed for some devices that grows notifications down-to-top
-            mStackScrollAlgorithm.updateQSFrameTop(mQsHeader == null ? 0 : mQsHeader.getHeight());
+            int height;
+            if (QSComposeFragment.isEnabled()) {
+                if (mQSHeaderBoundsProvider != null) {
+                    height = mQSHeaderBoundsProvider.getHeightProvider().invoke();
+                } else {
+                    height = 0;
+                }
+            } else {
+                if (mQsHeader != null) {
+                    height = mQsHeader.getHeight();
+                } else {
+                    height = 0;
+                }
+            }
+            mStackScrollAlgorithm.updateQSFrameTop(height);
         }
 
         // Once the layout has finished, we don't need to animate any scrolling clampings anymore.
@@ -1544,12 +1564,7 @@
     public void setExpandedHeight(float height) {
         final boolean skipHeightUpdate = shouldSkipHeightUpdate();
 
-        // when scene framework is enabled and in single shade, updateStackPosition is already
-        // called by updateTopPadding every time the stack moves, so skip it here to avoid
-        // flickering.
-        if (!SceneContainerFlag.isEnabled() || mShouldUseSplitNotificationShade) {
-            updateStackPosition();
-        }
+        updateStackPosition();
 
         if (!skipHeightUpdate) {
             mExpandedHeight = height;
@@ -1829,10 +1844,16 @@
     }
 
     public void setQsHeader(ViewGroup qsHeader) {
-        SceneContainerFlag.assertInLegacyMode();
+        QSComposeFragment.assertInLegacyMode();
         mQsHeader = qsHeader;
     }
 
+    public void setQsHeaderBoundsProvider(QSHeaderBoundsProvider qsHeaderBoundsProvider) {
+        SceneContainerFlag.assertInLegacyMode();
+        QSComposeFragment.isUnexpectedlyInLegacyMode();
+        mQSHeaderBoundsProvider = qsHeaderBoundsProvider;
+    }
+
     public static boolean isPinnedHeadsUp(View v) {
         if (v instanceof ExpandableNotificationRow row) {
             return row.isHeadsUp() && row.isPinned();
@@ -3750,7 +3771,20 @@
             return ev.getY() < mAmbientState.getStackTop();
         }
 
-        mQsHeader.getBoundsOnScreen(mQsHeaderBound);
+        if (QSComposeFragment.isEnabled()) {
+            if (mQSHeaderBoundsProvider == null) {
+                return false;
+            } else {
+                mQSHeaderBoundsProvider.getBoundsOnScreenProvider().invoke(mQsHeaderBound);
+            }
+        } else {
+            if (mQsHeader == null) {
+                return false;
+            } else {
+                mQsHeader.getBoundsOnScreen(mQsHeaderBound);
+            }
+        }
+
         /**
          * One-handed mode defines a feature FEATURE_ONE_HANDED of DisplayArea {@link DisplayArea}
          * that will translate down the Y-coordinate whole window screen type except for
@@ -3760,7 +3794,10 @@
          * of DisplayArea into relative coordinates for all windows, we need to correct the
          * QS Head bounds here.
          */
-        final int xOffset = Math.round(ev.getRawX() - ev.getX() + mQsHeader.getLeft());
+        int left =
+                QSComposeFragment.isEnabled() ? mQSHeaderBoundsProvider.getLeftProvider().invoke()
+                        : mQsHeader.getLeft();
+        final int xOffset = Math.round(ev.getRawX() - ev.getX() + left);
         final int yOffset = Math.round(ev.getRawY() - ev.getY());
         mQsHeaderBound.offsetTo(xOffset, yOffset);
         return mQsHeaderBound.contains((int) ev.getRawX(), (int) ev.getRawY());
@@ -3814,6 +3851,7 @@
     }
 
     void handleEmptySpaceClick(MotionEvent ev) {
+        if (SceneContainerFlag.isEnabled()) return;
         logEmptySpaceClick(ev, isBelowLastNotification(mInitialTouchX, mInitialTouchY),
                 mStatusBarState, mTouchIsClick);
         switch (ev.getActionMasked()) {
@@ -4025,6 +4063,7 @@
     }
 
     public void setOnEmptySpaceClickListener(OnEmptySpaceClickListener listener) {
+        SceneContainerFlag.assertInLegacyMode();
         mOnEmptySpaceClickListener = listener;
     }
 
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 a072ea6..608fe95 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
@@ -80,9 +80,11 @@
 import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.power.domain.interactor.PowerInteractor;
+import com.android.systemui.qs.flags.QSComposeFragment;
 import com.android.systemui.res.R;
 import com.android.systemui.scene.shared.flag.SceneContainerFlag;
 import com.android.systemui.scene.ui.view.WindowRootView;
+import com.android.systemui.shade.QSHeaderBoundsProvider;
 import com.android.systemui.shade.ShadeController;
 import com.android.systemui.shade.ShadeViewController;
 import com.android.systemui.statusbar.CommandQueue;
@@ -124,6 +126,7 @@
 import com.android.systemui.statusbar.notification.row.NotificationGuts;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
 import com.android.systemui.statusbar.notification.row.NotificationSnooze;
+import com.android.systemui.statusbar.notification.shared.GroupHunAnimationFix;
 import com.android.systemui.statusbar.notification.shared.NotificationsHeadsUpRefactor;
 import com.android.systemui.statusbar.notification.stack.ui.viewbinder.NotificationListViewBinder;
 import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
@@ -1074,6 +1077,7 @@
 
     public void setOnEmptySpaceClickListener(
             OnEmptySpaceClickListener listener) {
+        SceneContainerFlag.assertInLegacyMode();
         mView.setOnEmptySpaceClickListener(listener);
     }
 
@@ -1521,9 +1525,15 @@
      * Sets the QS header. Used to check if a touch is within its bounds.
      */
     public void setQsHeader(ViewGroup view) {
+        QSComposeFragment.assertInLegacyMode();
         mView.setQsHeader(view);
     }
 
+    public void setQsHeaderBoundsProvider(QSHeaderBoundsProvider qsHeaderBoundsProvider) {
+        QSComposeFragment.isUnexpectedlyInLegacyMode();
+        mView.setQsHeaderBoundsProvider(qsHeaderBoundsProvider);
+    }
+
     public void setAnimationsEnabled(boolean enabled) {
         mView.setAnimationsEnabled(enabled);
     }
@@ -1629,6 +1639,7 @@
     }
 
     public void setMaxTopPadding(int padding) {
+        SceneContainerFlag.assertInLegacyMode();
         mView.setMaxTopPadding(padding);
     }
 
@@ -1987,6 +1998,10 @@
                 NotificationEntry entry = row.getEntry();
                 mHeadsUpAppearanceController.updateHeader(entry);
                 mHeadsUpAppearanceController.updateHeadsUpAndPulsingRoundness(entry);
+                if (GroupHunAnimationFix.isEnabled() && !animatingAway) {
+                    // invalidate list to make sure the row is sorted to the correct section
+                    mHeadsUpManager.onEntryAnimatingAwayEnded(entry);
+                }
             });
         }
 
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 b801e5d..aee1d3e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.statusbar.notification.stack;
 
+import static androidx.core.math.MathUtils.clamp;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
@@ -899,11 +901,33 @@
                     if (shouldHunBeVisibleWhenScrolled(row.mustStayOnScreen(),
                             childState.headsUpIsVisible, row.showingPulsing(),
                             ambientState.isOnKeyguard(), row.getEntry().isStickyAndNotDemoted())) {
+                        // the height of this child before clamping it to the top
+                        float unmodifiedChildHeight = childState.height;
                         clampHunToTop(
                                 /* headsUpTop = */ headsUpTranslation,
                                 /* collapsedHeight = */ row.getCollapsedHeight(),
                                 /* viewState = */ childState
                         );
+                        float baseZ = ambientState.getBaseZHeight();
+                        if (headsUpTranslation < ambientState.getStackTop()) {
+                            // HUN displayed above the stack top, it needs a fix shadow
+                            childState.setZTranslation(baseZ + mPinnedZTranslationExtra);
+                        } else {
+                            // HUN displayed within the stack, add a shadow if it overlaps with
+                            // other elements.
+                            //
+                            // Views stack vertically from the top. Add the HUN's original height
+                            // (before clamping) to the stack top, to determine the starting
+                            // point for the remaining content.
+                            float scrollingContentTop =
+                                    ambientState.getStackTop() + unmodifiedChildHeight;
+                            updateZTranslationForHunInStack(
+                                    /* scrollingContentTop = */ scrollingContentTop,
+                                    /* scrollingContentTopPadding = */ mGapHeight,
+                                    /* baseZ = */ baseZ,
+                                    /* viewState = */ childState
+                            );
+                        }
                         if (isTopEntry && row.isAboveShelf()) {
                             clampHunToMaxTranslation(
                                     /* headsUpTop =  */ headsUpTranslation,
@@ -1040,8 +1064,30 @@
         // Transition from collapsed pinned state to fully expanded state
         // when the pinned HUN approaches its actual location (when scrolling back to top).
         final float distToRealY = newTranslation - viewState.getYTranslation();
-        viewState.height = (int) Math.max(viewState.height - distToRealY, collapsedHeight);
+        final float availableHeight = viewState.height - distToRealY;
+
         viewState.setYTranslation(newTranslation);
+        viewState.height = (int) Math.max(availableHeight, collapsedHeight);
+    }
+
+    @VisibleForTesting
+    void updateZTranslationForHunInStack(float scrollingContentTop,
+            float scrollingContentTopPadding, float baseZ, ExpandableViewState viewState) {
+        if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return;
+        float hunBottom = viewState.getYTranslation() + viewState.height;
+        float overlap = Math.max(0f, hunBottom - scrollingContentTop);
+
+        float shadowFraction = 1f;
+        if (scrollingContentTopPadding > 0f) {
+            // scrollingContentTopPadding makes a gap between the bottom of the HUN and the top
+            // of the scrolling content. Use this to animate to the full shadow.
+            shadowFraction = clamp(overlap / scrollingContentTopPadding, 0f, 1f);
+        }
+
+        if (overlap > 0.0f) {
+            // add a shadow to this HUN, because it overlaps with the scrolling stack
+            viewState.setZTranslation(baseZ + shadowFraction * mPinnedZTranslationExtra);
+        }
     }
 
     // Pin HUN to bottom of expanded QS
@@ -1066,7 +1112,7 @@
     private void clampHunToMaxTranslation(float headsUpTop, float headsUpBottom,
             ExpandableViewState viewState) {
         if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return;
-        final float maxHeight = headsUpTop - headsUpBottom;
+        final float maxHeight = Math.max(0f, headsUpBottom - headsUpTop);
         viewState.setYTranslation(Math.min(headsUpTop, viewState.getYTranslation()));
         viewState.height = (int) Math.min(maxHeight, viewState.height);
     }
@@ -1151,53 +1197,65 @@
         ExpandableViewState childViewState = child.getViewState();
         float baseZ = ambientState.getBaseZHeight();
 
-        if (child.mustStayOnScreen() && !childViewState.headsUpIsVisible
-                && !ambientState.isDozingAndNotPulsing(child)
-                && childViewState.getYTranslation() < ambientState.getTopPadding()
-                + ambientState.getStackTranslation()) {
-
-            if (childrenOnTop != 0.0f) {
-                // To elevate the later HUN over previous HUN when multiple HUNs exist
-                childrenOnTop++;
-            } else {
-                // Handles HUN shadow when Shade is opened, and AmbientState.mScrollY > 0
-                // Calculate the HUN's z-value based on its overlapping fraction with QQS Panel.
-                // When scrolling down shade to make HUN back to in-position in Notification Panel,
-                // The overlapping fraction goes to 0, and shadows hides gradually.
-                float overlap = ambientState.getTopPadding()
-                        + ambientState.getStackTranslation() - childViewState.getYTranslation();
-                // To prevent over-shadow during HUN entry
-                childrenOnTop += Math.min(
-                        1.0f,
-                        overlap / childViewState.height
-                );
-            }
-            childViewState.setZTranslation(baseZ
-                    + childrenOnTop * mPinnedZTranslationExtra);
-        } else if (isTopHun) {
-            // In case this is a new view that has never been measured before, we don't want to
-            // elevate if we are currently expanded more than the notification
-            int shelfHeight = ambientState.getShelf() == null ? 0 :
-                    ambientState.getShelf().getIntrinsicHeight();
-            float shelfStart = ambientState.getInnerHeight()
-                    - shelfHeight + ambientState.getTopPadding()
-                    + ambientState.getStackTranslation();
-            float notificationEnd = childViewState.getYTranslation() + child.getIntrinsicHeight()
-                    + mPaddingBetweenElements;
-            if (shelfStart > notificationEnd) {
-                // When the notification doesn't overlap with Notification Shelf, there's no shadow
-                childViewState.setZTranslation(baseZ);
-            } else {
-                // Give shadow to the notification if it overlaps with Notification Shelf
-                float factor = (notificationEnd - shelfStart) / shelfHeight;
-                if (Float.isNaN(factor)) { // Avoid problems when the above is 0/0.
-                    factor = 1.0f;
-                }
-                factor = Math.min(factor, 1.0f);
-                childViewState.setZTranslation(baseZ + factor * mPinnedZTranslationExtra);
-            }
-        } else {
+        if (SceneContainerFlag.isEnabled()) {
+            // SceneContainer flags off this logic, and just sets the baseZ because:
+            // - there are no overlapping HUNs anymore, no need for multiplying their shadows
+            // - shadows for HUNs overlapping with the stack are now set from updateHeadsUpStates
+            // - shadows for HUNs overlapping with the shelf are NOT set anymore, because it only
+            // happens on AOD/Pulsing, where they're displayed on a black background so a shadow
+            // wouldn't be visible.
             childViewState.setZTranslation(baseZ);
+        } else {
+            if (child.mustStayOnScreen() && !childViewState.headsUpIsVisible
+                    && !ambientState.isDozingAndNotPulsing(child)
+                    && childViewState.getYTranslation() < ambientState.getTopPadding()
+                    + ambientState.getStackTranslation()) {
+
+                if (childrenOnTop != 0.0f) {
+                    // To elevate the later HUN over previous HUN when multiple HUNs exist
+                    childrenOnTop++;
+                } else {
+                    // Handles HUN shadow when Shade is opened, and AmbientState.mScrollY > 0
+                    // Calculate the HUN's z-value based on its overlapping fraction with QQS Panel.
+                    // When scrolling down shade to make HUN back to in-position in Notif Panel,
+                    // The overlapping fraction goes to 0, and shadows hides gradually.
+                    float overlap = ambientState.getTopPadding()
+                            + ambientState.getStackTranslation() - childViewState.getYTranslation();
+                    // To prevent over-shadow during HUN entry
+                    childrenOnTop += Math.min(
+                            1.0f,
+                            overlap / childViewState.height
+                    );
+                }
+                childViewState.setZTranslation(baseZ
+                        + childrenOnTop * mPinnedZTranslationExtra);
+            } else if (isTopHun) {
+                // In case this is a new view that has never been measured before, we don't want to
+                // elevate if we are currently expanded more than the notification
+                int shelfHeight = ambientState.getShelf() == null ? 0 :
+                        ambientState.getShelf().getIntrinsicHeight();
+                float shelfStart = ambientState.getInnerHeight()
+                        - shelfHeight + ambientState.getTopPadding()
+                        + ambientState.getStackTranslation();
+                float notificationEnd =
+                        childViewState.getYTranslation() + child.getIntrinsicHeight()
+                                + mPaddingBetweenElements;
+                if (shelfStart > notificationEnd) {
+                    // When the notification doesn't overlap with Notification Shelf,
+                    // there's no shadow
+                    childViewState.setZTranslation(baseZ);
+                } else {
+                    // Give shadow to the notification if it overlaps with Notification Shelf
+                    float factor = (notificationEnd - shelfStart) / shelfHeight;
+                    if (Float.isNaN(factor)) { // Avoid problems when the above is 0/0.
+                        factor = 1.0f;
+                    }
+                    factor = Math.min(factor, 1.0f);
+                    childViewState.setZTranslation(baseZ + factor * mPinnedZTranslationExtra);
+                }
+            } else {
+                childViewState.setZTranslation(baseZ);
+            }
         }
 
         // While HUN is showing and Shade is closed: headerVisibleAmount stays 0, shadow stays.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt
index 57e52b7..2ba79a8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt
@@ -112,14 +112,22 @@
     private operator fun SceneKey.contains(scene: SceneKey) =
         sceneInteractor.isSceneInFamily(scene, this)
 
+    private val qsAllowsClipping: Flow<Boolean> =
+        combine(shadeInteractor.shadeMode, shadeInteractor.qsExpansion) { shadeMode, qsExpansion ->
+                qsExpansion < 0.5f || shadeMode != ShadeMode.Single
+            }
+            .distinctUntilChanged()
+
     /** The bounds of the notification stack in the current scene. */
     private val shadeScrimClipping: Flow<ShadeScrimClipping?> =
         combine(
+                qsAllowsClipping,
                 stackAppearanceInteractor.shadeScrimBounds,
                 stackAppearanceInteractor.shadeScrimRounding,
-            ) { bounds, rounding ->
-                bounds?.let { ShadeScrimClipping(it, rounding) }
+            ) { qsAllowsClipping, bounds, rounding ->
+                bounds?.takeIf { qsAllowsClipping }?.let { ShadeScrimClipping(it, rounding) }
             }
+            .distinctUntilChanged()
             .dumpWhileCollecting("stackClipping")
 
     fun shadeScrimShape(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt
index 1a7bc16..e8a7840 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt
@@ -91,6 +91,12 @@
     val expandFraction: Flow<Float> = shadeInteractor.anyExpansion.dumpValue("expandFraction")
 
     /**
+     * The amount [0-1] that quick settings has been opened. At 0, the shade may be open or closed;
+     * at 1, the quick settings are open.
+     */
+    val shadeToQsFraction: Flow<Float> = shadeInteractor.qsExpansion.dumpValue("shadeToQsFraction")
+
+    /**
      * The amount in px that the notification stack should scroll due to internal expansion. This
      * should only happen when a notification expansion hits the bottom of the screen, so it is
      * necessary to scroll up to keep expanding the notification.
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 a0d4ca2..ae31151 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
@@ -261,8 +261,6 @@
 
     boolean isScreenFullyOff();
 
-    boolean isCameraAllowedByAdmin();
-
     boolean isGoingToSleep();
 
     void notifyBiometricAuthModeChanged();
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 c8a4450..5209d0f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
@@ -49,6 +49,7 @@
 import com.android.systemui.emergency.EmergencyGesture;
 import com.android.systemui.emergency.EmergencyGestureModule.EmergencyGestureIntentFactory;
 import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.qs.QSHost;
 import com.android.systemui.qs.QSPanelController;
@@ -109,6 +110,7 @@
     private final Lazy<CameraLauncher> mCameraLauncherLazy;
     private final QuickSettingsController mQsController;
     private final QSHost mQSHost;
+    private final KeyguardInteractor mKeyguardInteractor;
     private static final VibrationAttributes HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES =
             VibrationAttributes.createForUsage(VibrationAttributes.USAGE_HARDWARE_FEEDBACK);
 
@@ -148,6 +150,7 @@
             UserTracker userTracker,
             QSHost qsHost,
             ActivityStarter activityStarter,
+            KeyguardInteractor keyguardInteractor,
             EmergencyGestureIntentFactory emergencyGestureIntentFactory) {
         mCentralSurfaces = centralSurfaces;
         mQsController = quickSettingsController;
@@ -176,7 +179,7 @@
         mCameraLauncherLazy = cameraLauncherLazy;
         mUserTracker = userTracker;
         mQSHost = qsHost;
-
+        mKeyguardInteractor = keyguardInteractor;
         mVibrateOnOpening = resources.getBoolean(R.bool.config_vibrateOnIconAnimation);
         mCameraLaunchGestureVibrationEffect = getCameraGestureVibrationEffect(
                 mVibratorOptional, resources);
@@ -351,6 +354,8 @@
             }
             return;
         }
+        mKeyguardInteractor.onCameraLaunchDetected(source);
+
         if (!mCentralSurfaces.isDeviceInteractive()) {
             mPowerManager.wakeUp(SystemClock.uptimeMillis(), PowerManager.WAKE_REASON_CAMERA_LAUNCH,
                     "com.android.systemui:CAMERA_GESTURE");
@@ -383,6 +388,7 @@
                 if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
                     mStatusBarKeyguardViewManager.reset(true /* hide */);
                 }
+                mCentralSurfaces.startLaunchTransitionTimeout();
                 mCameraLauncherLazy.get().launchCamera(source,
                         mPanelExpansionInteractor.isFullyCollapsed());
                 mCentralSurfaces.updateScrimController();
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 88d3e07..d4f2a93 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesEmptyImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesEmptyImpl.kt
@@ -34,72 +34,127 @@
  */
 abstract class CentralSurfacesEmptyImpl : CentralSurfaces {
     override val lifecycle = LifecycleRegistry(this)
+
     override fun updateIsKeyguard() = false
+
     override fun updateIsKeyguard(forceStateChange: Boolean) = false
+
     override fun getKeyguardMessageArea(): AuthKeyguardMessageArea? = null
+
     override fun isLaunchingActivityOverLockscreen() = false
+
     override fun isDismissingShadeForActivityLaunch() = false
+
     override fun onKeyguardViewManagerStatesUpdated() {}
+
     override fun getCommandQueuePanelsEnabled() = false
+
     override fun showWirelessChargingAnimation(batteryLevel: Int) {}
+
     override fun checkBarModes() {}
+
     override fun updateBubblesVisibility() {}
+
     override fun setInteracting(barWindow: Int, interacting: Boolean) {}
+
     override fun getDisplayWidth() = 0f
+
     override fun getDisplayHeight() = 0f
+
     override fun showKeyguard() {}
+
     override fun hideKeyguard() = false
+
     override fun showKeyguardImpl() {}
+
     override fun fadeKeyguardAfterLaunchTransition(
         beforeFading: Runnable?,
         endRunnable: Runnable?,
         cancelRunnable: Runnable?,
     ) {}
+
     override fun startLaunchTransitionTimeout() {}
+
     override fun hideKeyguardImpl(forceStateChange: Boolean) = false
+
     override fun keyguardGoingAway() {}
+
     override fun setKeyguardFadingAway(startTime: Long, delay: Long, fadeoutDuration: Long) {}
+
     override fun finishKeyguardFadingAway() {}
+
     override fun userActivity() {}
+
     override fun endAffordanceLaunch() {}
+
     override fun shouldKeyguardHideImmediately() = false
+
     override fun showBouncerWithDimissAndCancelIfKeyguard(
         performAction: OnDismissAction?,
         cancelAction: Runnable?,
     ) {}
+
     override fun getNavigationBarView(): NavigationBarView? = null
+
     override fun setBouncerShowing(bouncerShowing: Boolean) {}
+
     override fun isScreenFullyOff() = false
-    override fun isCameraAllowedByAdmin() = false
+
     override fun isGoingToSleep() = false
+
     override fun notifyBiometricAuthModeChanged() {}
+
     override fun setTransitionToFullShadeProgress(transitionToFullShadeProgress: Float) {}
+
     override fun setPrimaryBouncerHiddenFraction(expansion: Float) {}
+
     override fun updateScrimController() {}
+
     override fun shouldIgnoreTouch() = false
+
     override fun isDeviceInteractive() = false
+
     override fun handleExternalShadeWindowTouch(event: MotionEvent?) {}
+
     override fun handleCommunalHubTouch(event: MotionEvent?) {}
+
     override fun awakenDreams() {}
+
     override fun isBouncerShowing() = false
+
     override fun isBouncerShowingScrimmed() = false
+
     override fun updateNotificationPanelTouchState() {}
+
     override fun getRotation() = 0
+
     override fun setBarStateForTest(state: Int) {}
+
     override fun acquireGestureWakeLock(time: Long) {}
+
     override fun resendMessage(msg: Int) {}
+
     override fun resendMessage(msg: Any?) {}
+
     override fun setLastCameraLaunchSource(source: Int) {}
+
     override fun setLaunchCameraOnFinishedGoingToSleep(launch: Boolean) {}
+
     override fun setLaunchCameraOnFinishedWaking(launch: Boolean) {}
+
     override fun setLaunchEmergencyActionOnFinishedGoingToSleep(launch: Boolean) {}
+
     override fun setLaunchEmergencyActionOnFinishedWaking(launch: Boolean) {}
+
     override fun getQSPanelController(): QSPanelController? = null
+
     override fun getDisplayDensity() = 0f
+
     override fun setIsLaunchingActivityOverLockscreen(
         isLaunchingActivityOverLockscreen: Boolean,
         dismissShade: Boolean,
     ) {}
+
     override fun getAnimatorControllerFromNotification(
         associatedView: ExpandableNotificationRow?,
     ): ActivityTransitionAnimator.Controller? = null
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 462ae7a..6d3cad5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -47,7 +47,6 @@
 import android.app.TaskInfo;
 import android.app.UiModeManager;
 import android.app.WallpaperManager;
-import android.app.admin.DevicePolicyManager;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -883,8 +882,6 @@
 
         // start old BaseStatusBar.start().
         mWindowManagerService = WindowManagerGlobal.getWindowManagerService();
-        mDevicePolicyManager = (DevicePolicyManager) mContext.getSystemService(
-                Context.DEVICE_POLICY_SERVICE);
 
         mAccessibilityManager = (AccessibilityManager)
                 mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
@@ -916,8 +913,8 @@
                 result.mRequestedVisibleTypes, result.mPackageName, result.mLetterboxDetails);
 
         // StatusBarManagerService has a back up of IME token and it's restored here.
-        mCommandQueueCallbacks.setImeWindowStatus(mDisplayId, result.mImeToken,
-                result.mImeWindowVis, result.mImeBackDisposition, result.mShowImeSwitcher);
+        mCommandQueueCallbacks.setImeWindowStatus(mDisplayId, result.mImeWindowVis,
+                result.mImeBackDisposition, result.mShowImeSwitcher);
 
         // Set up the initial icon state
         int numIcons = result.mIcons.size();
@@ -2627,6 +2624,7 @@
                 mStackScrollerController.updateSensitivenessForOccludedWakeup();
             }
             if (mLaunchCameraWhenFinishedWaking) {
+                startLaunchTransitionTimeout();
                 mCameraLauncherLazy.get().launchCamera(mLastCameraLaunchSource,
                         mShadeSurface.isFullyCollapsed());
                 mLaunchCameraWhenFinishedWaking = false;
@@ -2701,21 +2699,6 @@
     }
 
     @Override
-    public boolean isCameraAllowedByAdmin() {
-        if (mDevicePolicyManager.getCameraDisabled(null,
-                mLockscreenUserManager.getCurrentUserId())) {
-            return false;
-        } else if (mKeyguardStateController.isShowing()
-                && mStatusBarKeyguardViewManager.isSecure()) {
-            // Check if the admin has disabled the camera specifically for the keyguard
-            return (mDevicePolicyManager.getKeyguardDisabledFeatures(null,
-                    mLockscreenUserManager.getCurrentUserId())
-                    & DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA) == 0;
-        }
-        return true;
-    }
-
-    @Override
     public boolean isGoingToSleep() {
         return mWakefulnessLifecycle.getWakefulness()
                 == WakefulnessLifecycle.WAKEFULNESS_GOING_TO_SLEEP;
@@ -2864,7 +2847,6 @@
 
     protected boolean mDeviceInteractive;
 
-    protected DevicePolicyManager mDevicePolicyManager;
     private final PowerManager mPowerManager;
     protected StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
index a32d5fe..ca1fb78b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
@@ -437,6 +437,13 @@
         mNotificationShadeWindowController.setDozeScreenBrightness(brightness);
     }
 
+
+    @Override
+    public void setDozeScreenBrightnessFloat(float brightness) {
+        mDozeLog.traceDozeScreenBrightnessFloat(brightness);
+        mNotificationShadeWindowController.setDozeScreenBrightnessFloat(brightness);
+    }
+
     @Override
     public void setAodDimmingScrim(float scrimOpacity) {
         mDozeLog.traceSetAodDimmingScrim(scrimOpacity);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
index 789a6f4..25d9cc7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
@@ -117,7 +117,7 @@
 
         @Override
         public HeadsUpEntryPhone acquire() {
-            NotificationsHeadsUpRefactor.assertInLegacyMode();
+            NotificationThrottleHun.assertInLegacyMode();
             if (!mPoolObjects.isEmpty()) {
                 return mPoolObjects.pop();
             }
@@ -126,7 +126,7 @@
 
         @Override
         public boolean release(@NonNull HeadsUpEntryPhone instance) {
-            NotificationsHeadsUpRefactor.assertInLegacyMode();
+            NotificationThrottleHun.assertInLegacyMode();
             mPoolObjects.push(instance);
             return true;
         }
@@ -308,6 +308,9 @@
         HeadsUpEntryPhone headsUpEntry = getHeadsUpEntryPhone(entry.getKey());
         if (headsUpEntry != null && headsUpEntry.mRemoteInputActive != remoteInputActive) {
             headsUpEntry.mRemoteInputActive = remoteInputActive;
+            if (ExpandHeadsUpOnInlineReply.isEnabled() && remoteInputActive) {
+                headsUpEntry.mRemoteInputActivatedAtLeastOnce = true;
+            }
             if (remoteInputActive) {
                 headsUpEntry.cancelAutoRemovalCallbacks("setRemoteInputActive(true)");
             } else {
@@ -386,10 +389,13 @@
     //  OnReorderingAllowedListener:
 
     private final OnReorderingAllowedListener mOnReorderingAllowedListener = () -> {
-        mAnimationStateHandler.setHeadsUpGoingAwayAnimationsAllowed(false);
         if (NotificationThrottleHun.isEnabled()) {
             mAvalancheController.setEnableAtRuntime(true);
+            if (mEntriesToRemoveWhenReorderingAllowed.isEmpty()) {
+                return;
+            }
         }
+        mAnimationStateHandler.setHeadsUpGoingAwayAnimationsAllowed(false);
         for (NotificationEntry entry : mEntriesToRemoveWhenReorderingAllowed) {
             if (isHeadsUpEntry(entry.getKey())) {
                 // Maybe the heads-up was removed already
@@ -422,7 +428,7 @@
     @NonNull
     @Override
     protected HeadsUpEntry createHeadsUpEntry(NotificationEntry entry) {
-        if (NotificationsHeadsUpRefactor.isEnabled()) {
+        if (NotificationThrottleHun.isEnabled()) {
             return new HeadsUpEntryPhone(entry);
         } else {
             HeadsUpEntryPhone headsUpEntry = mEntryPool.acquire();
@@ -448,7 +454,7 @@
     @Override
     protected void onEntryRemoved(HeadsUpEntry headsUpEntry) {
         super.onEntryRemoved(headsUpEntry);
-        if (!NotificationsHeadsUpRefactor.isEnabled()) {
+        if (!NotificationThrottleHun.isEnabled()) {
             mEntryPool.release((HeadsUpEntryPhone) headsUpEntry);
         }
         updateTopHeadsUpFlow();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/OWNERS b/packages/SystemUI/src/com/android/systemui/statusbar/phone/OWNERS
index 4657e9b..92a333e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/OWNERS
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/OWNERS
@@ -1,6 +1,6 @@
 per-file *Notification* = set noparent
 per-file *Notification* = file:../notification/OWNERS
 
-per-file NotificationIcon* = ccassidy@google.com, evanlaird@google.com, pixel@google.com
+per-file NotificationIcon* = file:../OWNERS
 
 per-file NotificationShadeWindowControllerImpl.java = dupin@google.com, cinek@google.com, beverlyt@google.com, pixel@google.com, juliacr@google.com
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 8f2ad40..2d775b7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -802,7 +802,10 @@
         hideAlternateBouncer(false);
         if (mKeyguardStateController.isShowing() && !isBouncerShowing()) {
             if (SceneContainerFlag.isEnabled()) {
-                mDeviceEntryInteractorLazy.get().attemptDeviceEntry();
+                mSceneInteractorLazy.get().changeScene(
+                        Scenes.Bouncer,
+                        "primary bouncer requested"
+                );
             } else {
                 mPrimaryBouncerInteractor.show(scrimmed);
             }
@@ -1103,7 +1106,9 @@
                     SysUiStatsLog.KEYGUARD_STATE_CHANGED__STATE__OCCLUDED);
             if (mCentralSurfaces.isLaunchingActivityOverLockscreen()) {
                 final Runnable postCollapseAction = () -> {
-                    mNotificationShadeWindowController.setKeyguardOccluded(isOccluded);
+                    if (!Flags.useTransitionsForKeyguardOccluded()) {
+                        mNotificationShadeWindowController.setKeyguardOccluded(isOccluded);
+                    }
                     reset(true /* hideBouncerWhenShowing */);
                 };
                 if (mCentralSurfaces.isDismissingShadeForActivityLaunch()) {
@@ -1119,7 +1124,9 @@
             SysUiStatsLog.write(SysUiStatsLog.KEYGUARD_STATE_CHANGED,
                     SysUiStatsLog.KEYGUARD_STATE_CHANGED__STATE__SHOWN);
         }
-        mNotificationShadeWindowController.setKeyguardOccluded(isOccluded);
+        if (!Flags.useTransitionsForKeyguardOccluded()) {
+            mNotificationShadeWindowController.setKeyguardOccluded(isOccluded);
+        }
 
         // setDozing(false) will call reset once we stop dozing. Also, if we're going away, there's
         // no need to reset the keyguard views as we'll be gone shortly. Resetting now could cause
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 e01556f..c046168 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
@@ -341,6 +341,7 @@
         mSysUiState.setFlag(QuickStepContract.SYSUI_STATE_DIALOG_SHOWING, true)
                 .commitUpdate(mContext.getDisplayId());
 
+        mDelegate.onStart(this);
         start();
     }
 
@@ -349,7 +350,8 @@
      * should override this method instead.
      */
     protected void start() {
-        mDelegate.onStart(this);
+        // IMPORTANT: Please do not add anything here, since subclasses are likely to override this.
+        // Instead, add things to onStop above.
     }
 
     @Override
@@ -365,6 +367,7 @@
         mSysUiState.setFlag(QuickStepContract.SYSUI_STATE_DIALOG_SHOWING, false)
                 .commitUpdate(mContext.getDisplayId());
 
+        mDelegate.onStop(this);
         stop();
     }
 
@@ -373,7 +376,8 @@
      * should override this method instead.
      */
     protected void stop() {
-        mDelegate.onStop(this);
+        // IMPORTANT: Please do not add anything here, since subclasses are likely to override this.
+        // Instead, add things to onStop above.
     }
 
     @Override
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 0320a7a..07c190d 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
@@ -661,9 +661,6 @@
         } else {
             hideOngoingActivityChip(animate);
         }
-        if (!Flags.statusBarScreenSharingChips()) {
-            mOngoingCallController.notifyChipVisibilityChanged(showOngoingActivityChip);
-        }
     }
 
     private boolean shouldHideStatusBar() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
index 9f98b54..4368239 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
@@ -23,7 +23,6 @@
 import android.app.PendingIntent
 import android.app.UidObserver
 import android.content.Context
-import android.util.Log
 import android.view.View
 import androidx.annotation.VisibleForTesting
 import com.android.internal.jank.InteractionJankMonitor
@@ -35,8 +34,11 @@
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.dump.DumpManager
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.core.LogLevel
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.res.R
+import com.android.systemui.statusbar.StatusBarIconView
 import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer
 import com.android.systemui.statusbar.chips.ui.view.ChipChronometer
 import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryStore
@@ -44,6 +46,9 @@
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
 import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection
 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
+import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor
+import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel
+import com.android.systemui.statusbar.notification.shared.CallType
 import com.android.systemui.statusbar.phone.ongoingcall.data.repository.OngoingCallRepository
 import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel
 import com.android.systemui.statusbar.policy.CallbackController
@@ -64,15 +69,16 @@
     private val context: Context,
     private val ongoingCallRepository: OngoingCallRepository,
     private val notifCollection: CommonNotifCollection,
+    private val activeNotificationsInteractor: ActiveNotificationsInteractor,
     private val systemClock: SystemClock,
     private val activityStarter: ActivityStarter,
     @Main private val mainExecutor: Executor,
     private val iActivityManager: IActivityManager,
-    private val logger: OngoingCallLogger,
     private val dumpManager: DumpManager,
     private val statusBarWindowController: StatusBarWindowController,
     private val swipeStatusBarAwayGestureHandler: SwipeStatusBarAwayGestureHandler,
     private val statusBarModeRepository: StatusBarModeRepositoryStore,
+    @OngoingCallLog private val logger: LogBuffer,
 ) : CallbackController<OngoingCallListener>, Dumpable, CoreStartable {
     private var isFullscreen: Boolean = false
     /** Non-null if there's an active call notification. */
@@ -96,6 +102,7 @@
             }
 
             override fun onEntryUpdated(entry: NotificationEntry) {
+                StatusBarUseReposForCallChip.assertInLegacyMode()
                 // We have a new call notification or our existing call notification has been
                 // updated.
                 // TODO(b/183229367): This likely won't work if you take a call from one app then
@@ -108,6 +115,9 @@
                         CallNotificationInfo(
                             entry.sbn.key,
                             entry.sbn.notification.getWhen(),
+                            // In this old listener pattern, we don't have access to the
+                            // notification icon.
+                            notificationIconView = null,
                             entry.sbn.notification.contentIntent,
                             entry.sbn.uid,
                             entry.sbn.notification.extras.getInt(
@@ -122,8 +132,20 @@
 
                     callNotificationInfo = newOngoingCallInfo
                     if (newOngoingCallInfo.isOngoing) {
+                        logger.log(
+                            TAG,
+                            LogLevel.DEBUG,
+                            { str1 = newOngoingCallInfo.key },
+                            { "Call notif *is* ongoing -> showing chip. key=$str1" },
+                        )
                         updateChip()
                     } else {
+                        logger.log(
+                            TAG,
+                            LogLevel.DEBUG,
+                            { str1 = newOngoingCallInfo.key },
+                            { "Call notif not ongoing -> hiding chip. key=$str1" },
+                        )
                         removeChip()
                     }
                 }
@@ -131,6 +153,12 @@
 
             override fun onEntryRemoved(entry: NotificationEntry, reason: Int) {
                 if (entry.sbn.key == callNotificationInfo?.key) {
+                    logger.log(
+                        TAG,
+                        LogLevel.DEBUG,
+                        { str1 = entry.sbn.key },
+                        { "Call notif removed -> hiding chip. key=$str1" },
+                    )
                     removeChip()
                 }
             }
@@ -138,7 +166,25 @@
 
     override fun start() {
         dumpManager.registerDumpable(this)
-        notifCollection.addCollectionListener(notifListener)
+
+        if (Flags.statusBarUseReposForCallChip()) {
+            scope.launch {
+                // Listening to [ActiveNotificationsInteractor] instead of using
+                // [NotifCollectionListener#onEntryUpdated] is better for two reasons:
+                // 1. ActiveNotificationsInteractor automatically filters the notification list to
+                // just notifications for the current user, which ensures we don't show a call chip
+                // for User 1's call while User 2 is active (see b/328584859).
+                // 2. ActiveNotificationsInteractor only emits notifications that are currently
+                // present in the shade, which means we know we've already inflated the icon that we
+                // might use for the call chip (see b/354930838).
+                activeNotificationsInteractor.ongoingCallNotification.collect {
+                    updateInfoFromNotifModel(it)
+                }
+            }
+        } else {
+            notifCollection.addCollectionListener(notifListener)
+        }
+
         scope.launch {
             statusBarModeRepository.defaultDisplay.isInFullscreenMode.collect {
                 isFullscreen = it
@@ -151,7 +197,8 @@
     /**
      * Sets the chip view that will contain ongoing call information.
      *
-     * Should only be called from [CollapsedStatusBarFragment].
+     * Should only be called from
+     * [com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment].
      */
     fun setChipView(chipView: View) {
         tearDownChipView()
@@ -165,15 +212,6 @@
     }
 
     /**
-     * Called when the chip's visibility may have changed.
-     *
-     * Should only be called from [CollapsedStatusBarFragment].
-     */
-    fun notifyChipVisibilityChanged(chipIsVisible: Boolean) {
-        logger.logChipVisibilityChanged(chipIsVisible)
-    }
-
-    /**
      * Returns true if there's an active ongoing call that should be displayed in a status bar chip.
      */
     fun hasOngoingCall(): Boolean {
@@ -189,8 +227,15 @@
                 callNotificationInfo
                     // This shouldn't happen, but protect against it in case
                     ?: return OngoingCallModel.NoCall
+            val icon =
+                if (Flags.statusBarCallChipNotificationIcon()) {
+                    currentInfo.notificationIconView
+                } else {
+                    null
+                }
             return OngoingCallModel.InCall(
                 startTimeMs = currentInfo.callStartTime,
+                notificationIconView = icon,
                 intent = currentInfo.intent,
             )
         } else {
@@ -210,6 +255,36 @@
         synchronized(mListeners) { mListeners.remove(listener) }
     }
 
+    private fun updateInfoFromNotifModel(notifModel: ActiveNotificationModel?) {
+        if (notifModel == null) {
+            removeChip()
+        } else if (notifModel.callType != CallType.Ongoing) {
+            logger.log(
+                TAG,
+                LogLevel.ERROR,
+                { str1 = notifModel.callType.name },
+                { "Notification Interactor sent ActiveNotificationModel with callType=$str1" },
+            )
+            removeChip()
+        } else {
+            val newOngoingCallInfo =
+                CallNotificationInfo(
+                    notifModel.key,
+                    notifModel.whenTime,
+                    notifModel.statusBarChipIconView,
+                    notifModel.contentIntent,
+                    notifModel.uid,
+                    isOngoing = true,
+                    statusBarSwipedAway = callNotificationInfo?.statusBarSwipedAway ?: false
+                )
+            if (newOngoingCallInfo == callNotificationInfo) {
+                return
+            }
+            callNotificationInfo = newOngoingCallInfo
+            updateChip()
+        }
+    }
+
     private fun updateChip() {
         val currentCallNotificationInfo = callNotificationInfo ?: return
 
@@ -250,14 +325,12 @@
             // If we failed to update the chip, don't store the call info. Then [hasOngoingCall]
             // will return false and we fall back to typical notification handling.
             callNotificationInfo = null
-
-            if (DEBUG) {
-                Log.w(
-                    TAG,
-                    "Ongoing call chip view could not be found; " +
-                        "Not displaying chip in status bar"
-                )
-            }
+            logger.log(
+                TAG,
+                LogLevel.WARNING,
+                {},
+                { "Ongoing call chip view could not be found; Not displaying chip in status bar" },
+            )
         }
     }
 
@@ -275,7 +348,6 @@
         val intent = callNotificationInfo?.intent
         if (currentChipView != null && backgroundView != null && intent != null) {
             currentChipView.setOnClickListener {
-                logger.logChipClicked()
                 activityStarter.postStartActivityDismissingKeyguard(
                     intent,
                     ActivityTransitionAnimator.Controller.fromView(
@@ -333,9 +405,7 @@
      * detected.
      */
     private fun onSwipeAwayGestureDetected() {
-        if (DEBUG) {
-            Log.d(TAG, "Swipe away gesture detected")
-        }
+        logger.log(TAG, LogLevel.DEBUG, {}, { "Swipe away gesture detected" })
         callNotificationInfo = callNotificationInfo?.copy(statusBarSwipedAway = true)
         statusBarWindowController.setOngoingProcessRequiresStatusBarVisible(false)
         swipeStatusBarAwayGestureHandler.removeOnGestureDetectedCallback(TAG)
@@ -349,6 +419,8 @@
     private data class CallNotificationInfo(
         val key: String,
         val callStartTime: Long,
+        /** The icon set as the [android.app.Notification.getSmallIcon] field. */
+        val notificationIconView: StatusBarIconView?,
         val intent: PendingIntent?,
         val uid: Int,
         /** True if the call is currently ongoing (as opposed to incoming, screening, etc.). */
@@ -368,7 +440,10 @@
         pw.println("Call app visible: ${uidObserver.isCallAppVisible}")
     }
 
-    /** Our implementation of a [IUidObserver]. */
+    /**
+     * Observer to tell us when the app that posted the ongoing call notification is visible so that
+     * we don't show the call chip at the same time (since the timers could be out-of-sync).
+     */
     inner class CallAppUidObserver : UidObserver() {
         /** True if the application managing the call is visible to the user. */
         var isCallAppVisible: Boolean = false
@@ -395,6 +470,12 @@
                     isProcessVisibleToUser(
                         iActivityManager.getUidProcessState(uid, context.opPackageName)
                     )
+                logger.log(
+                    TAG,
+                    LogLevel.DEBUG,
+                    { bool1 = isCallAppVisible },
+                    { "On uid observer registration, isCallAppVisible=$bool1" },
+                )
                 if (isRegistered) {
                     return
                 }
@@ -406,7 +487,13 @@
                 )
                 isRegistered = true
             } catch (se: SecurityException) {
-                Log.e(TAG, "Security exception when trying to set up uid observer: $se")
+                logger.log(
+                    TAG,
+                    LogLevel.ERROR,
+                    {},
+                    { "Security exception when trying to set up uid observer" },
+                    se,
+                )
             }
         }
 
@@ -431,6 +518,12 @@
             val oldIsCallAppVisible = isCallAppVisible
             isCallAppVisible = isProcessVisibleToUser(procState)
             if (oldIsCallAppVisible != isCallAppVisible) {
+                logger.log(
+                    TAG,
+                    LogLevel.DEBUG,
+                    { bool1 = isCallAppVisible },
+                    { "#onUidStateChanged. isCallAppVisible=$bool1" },
+                )
                 // Animations may be run as a result of the call's state change, so ensure
                 // the listener is notified on the main thread.
                 mainExecutor.execute { sendStateChangeEvent() }
@@ -443,5 +536,4 @@
     return entry.sbn.notification.isStyle(Notification.CallStyle::class.java)
 }
 
-private const val TAG = "OngoingCallController"
-private val DEBUG = Log.isLoggable(TAG, Log.DEBUG)
+private const val TAG = OngoingCallRepository.TAG
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/PartitionedGridLayoutKosmos.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallLog.kt
similarity index 63%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/PartitionedGridLayoutKosmos.kt
copy to packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallLog.kt
index 37c9552..5f53f87 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/PartitionedGridLayoutKosmos.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallLog.kt
@@ -14,11 +14,14 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.panels.domain.interactor
+package com.android.systemui.statusbar.phone.ongoingcall
 
-import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.qs.panels.ui.compose.PartitionedGridLayout
-import com.android.systemui.qs.panels.ui.viewmodel.partitionedGridViewModel
+import javax.inject.Qualifier
 
-val Kosmos.partitionedGridLayout by
-    Kosmos.Fixture { PartitionedGridLayout(partitionedGridViewModel) }
+/**
+ * Logs for events related to ongoing call notifications and their corresponding status bar chip.
+ */
+@Qualifier
+@MustBeDocumented
+@Retention(AnnotationRetention.RUNTIME)
+annotation class OngoingCallLog
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallLogger.kt
deleted file mode 100644
index 177f215..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallLogger.kt
+++ /dev/null
@@ -1,58 +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.phone.ongoingcall
-
-import androidx.annotation.VisibleForTesting
-import com.android.internal.logging.UiEvent
-import com.android.internal.logging.UiEventLogger
-import com.android.systemui.dagger.SysUISingleton
-import javax.inject.Inject
-
-/** A class to log events for the ongoing call chip. */
-@SysUISingleton
-class OngoingCallLogger @Inject constructor(private val logger: UiEventLogger) {
-
-    private var chipIsVisible: Boolean = false
-
-    /** Logs that the ongoing call chip was clicked. */
-    fun logChipClicked() {
-        logger.log(OngoingCallEvents.ONGOING_CALL_CLICKED)
-    }
-
-    /**
-     * If needed, logs that the ongoing call chip's visibility has changed.
-     *
-     * For now, only logs when the chip changes from not visible to visible.
-     */
-    fun logChipVisibilityChanged(chipIsVisible: Boolean) {
-        if (chipIsVisible && chipIsVisible != this.chipIsVisible) {
-            logger.log(OngoingCallEvents.ONGOING_CALL_VISIBLE)
-        }
-        this.chipIsVisible = chipIsVisible
-    }
-
-    @VisibleForTesting
-    enum class OngoingCallEvents(val metricId: Int) : UiEventLogger.UiEventEnum {
-        @UiEvent(doc = "The ongoing call chip became visible")
-        ONGOING_CALL_VISIBLE(813),
-
-        @UiEvent(doc = "The ongoing call chip was clicked")
-        ONGOING_CALL_CLICKED(814);
-
-        override fun getId() = metricId
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/StatusBarUseReposForCallChip.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/StatusBarUseReposForCallChip.kt
new file mode 100644
index 0000000..4bdd90e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/StatusBarUseReposForCallChip.kt
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone.ongoingcall
+
+import com.android.systemui.Flags
+import com.android.systemui.flags.FlagToken
+import com.android.systemui.flags.RefactorFlagUtils
+
+/** Helper for reading or using the status bar use repos for call chip flag state. */
+@Suppress("NOTHING_TO_INLINE")
+object StatusBarUseReposForCallChip {
+    /** The aconfig flag name */
+    const val FLAG_NAME = Flags.FLAG_STATUS_BAR_USE_REPOS_FOR_CALL_CHIP
+
+    /** A token used for dependency declaration */
+    val token: FlagToken
+        get() = FlagToken(FLAG_NAME, isEnabled)
+
+    /** Is the refactor enabled */
+    @JvmStatic
+    inline val isEnabled
+        get() = Flags.statusBarUseReposForCallChip()
+
+    /**
+     * Called to ensure code is only run when the flag is enabled. This protects users from the
+     * unintended behaviors caused by accidentally running new logic, while also crashing on an eng
+     * build to ensure that the refactor author catches issues in testing.
+     */
+    @JvmStatic
+    inline fun isUnexpectedlyInLegacyMode() =
+        RefactorFlagUtils.isUnexpectedlyInLegacyMode(isEnabled, FLAG_NAME)
+
+    /**
+     * Called to ensure code is only run when the flag is disabled. This will throw an exception if
+     * the flag is enabled to ensure that the refactor author catches issues in testing.
+     */
+    @JvmStatic
+    inline fun assertInLegacyMode() = RefactorFlagUtils.assertInLegacyMode(isEnabled, FLAG_NAME)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepository.kt
index 9317ebe5..f16371a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepository.kt
@@ -17,6 +17,9 @@
 package com.android.systemui.statusbar.phone.ongoingcall.data.repository
 
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.core.LogLevel
+import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallLog
 import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel
 import javax.inject.Inject
 import kotlinx.coroutines.flow.MutableStateFlow
@@ -32,7 +35,11 @@
  * classes both refer to this repository.
  */
 @SysUISingleton
-class OngoingCallRepository @Inject constructor() {
+class OngoingCallRepository
+@Inject
+constructor(
+    @OngoingCallLog private val logger: LogBuffer,
+) {
     private val _ongoingCallState = MutableStateFlow<OngoingCallModel>(OngoingCallModel.NoCall)
     /** The current ongoing call state. */
     val ongoingCallState: StateFlow<OngoingCallModel> = _ongoingCallState.asStateFlow()
@@ -42,6 +49,16 @@
      * [com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController].
      */
     fun setOngoingCallState(state: OngoingCallModel) {
+        logger.log(
+            TAG,
+            LogLevel.DEBUG,
+            { str1 = state::class.simpleName },
+            { "Repo#setOngoingCallState: $str1" },
+        )
         _ongoingCallState.value = state
     }
+
+    companion object {
+        const val TAG = "OngoingCall"
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/shared/model/OngoingCallModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/shared/model/OngoingCallModel.kt
index f81b952..34bff80 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/shared/model/OngoingCallModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/shared/model/OngoingCallModel.kt
@@ -17,14 +17,9 @@
 package com.android.systemui.statusbar.phone.ongoingcall.shared.model
 
 import android.app.PendingIntent
+import com.android.systemui.statusbar.StatusBarIconView
 
-/**
- * Represents the state of any ongoing calls.
- *
- * TODO(b/332662551): If there's an ongoing call but the user has the call app open, then we use the
- *   NoCall model, *not* the InCall model, which is confusing when looking at the logs. We may want
- *   to make that more clear, either with better logging or different models.
- */
+/** Represents the state of any ongoing calls. */
 sealed interface OngoingCallModel {
     /** There is no ongoing call. */
     data object NoCall : OngoingCallModel
@@ -37,7 +32,13 @@
      *   [com.android.systemui.util.time.SystemClock.currentTimeMillis], **not**
      *   [com.android.systemui.util.time.SystemClock.elapsedRealtime]. This value can be 0 if the
      *   user has started an outgoing call that hasn't been answered yet - see b/192379214.
+     * @property notificationIconView the [android.app.Notification.getSmallIcon] that's set on the
+     *   call notification. We may use this icon in the chip instead of the default phone icon.
      * @property intent the intent associated with the call notification.
      */
-    data class InCall(val startTimeMs: Long, val intent: PendingIntent?) : OngoingCallModel
+    data class InCall(
+        val startTimeMs: Long,
+        val notificationIconView: StatusBarIconView?,
+        val intent: PendingIntent?,
+    ) : OngoingCallModel
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/NetworkNameModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/NetworkNameModel.kt
index 99ed2d9..cd442cf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/NetworkNameModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/NetworkNameModel.kt
@@ -21,6 +21,7 @@
 import android.telephony.TelephonyManager.EXTRA_PLMN
 import android.telephony.TelephonyManager.EXTRA_SHOW_PLMN
 import android.telephony.TelephonyManager.EXTRA_SHOW_SPN
+import android.telephony.TelephonyManager.EXTRA_SPN
 import com.android.systemui.log.table.Diffable
 import com.android.systemui.log.table.TableRowLogger
 
@@ -96,7 +97,8 @@
 
 fun Intent.toNetworkNameModel(separator: String): NetworkNameModel? {
     val showSpn = getBooleanExtra(EXTRA_SHOW_SPN, false)
-    val spn = getStringExtra(EXTRA_DATA_SPN)
+    val spn = getStringExtra(EXTRA_SPN)
+    val dataSpn = getStringExtra(EXTRA_DATA_SPN)
     val showPlmn = getBooleanExtra(EXTRA_SHOW_PLMN, false)
     val plmn = getStringExtra(EXTRA_PLMN)
 
@@ -112,6 +114,12 @@
         }
         str.append(spn)
     }
+    if (showSpn && dataSpn != null) {
+        if (str.isNotEmpty()) {
+            str.append(separator)
+        }
+        str.append(dataSpn)
+    }
 
     return if (str.isNotEmpty()) NetworkNameModel.IntentDerived(str.toString()) else null
 }
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 a7c5f78..03ec41d 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
@@ -20,6 +20,7 @@
 import android.telephony.TelephonyCallback
 import android.telephony.TelephonyManager
 import android.telephony.satellite.NtnSignalStrengthCallback
+import android.telephony.satellite.SatelliteCommunicationAllowedStateCallback
 import android.telephony.satellite.SatelliteManager
 import android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_SUCCESS
 import android.telephony.satellite.SatelliteModemStateCallback
@@ -37,7 +38,6 @@
 import com.android.systemui.statusbar.pipeline.dagger.DeviceBasedSatelliteInputLog
 import com.android.systemui.statusbar.pipeline.dagger.VerboseDeviceBasedSatelliteInputLog
 import com.android.systemui.statusbar.pipeline.satellite.data.RealDeviceBasedSatelliteRepository
-import com.android.systemui.statusbar.pipeline.satellite.data.prod.DeviceBasedSatelliteRepositoryImpl.Companion.POLLING_INTERVAL_MS
 import com.android.systemui.statusbar.pipeline.satellite.data.prod.SatelliteSupport.Companion.whenSupported
 import com.android.systemui.statusbar.pipeline.satellite.data.prod.SatelliteSupport.NotSupported
 import com.android.systemui.statusbar.pipeline.satellite.data.prod.SatelliteSupport.Supported
@@ -60,11 +60,9 @@
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.collectLatest
-import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.flowOn
-import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.mapNotNull
 import kotlinx.coroutines.flow.onStart
 import kotlinx.coroutines.flow.stateIn
@@ -122,15 +120,9 @@
 }
 
 /**
- * Basically your everyday run-of-the-mill system service listener, with three notable exceptions.
+ * Basically your everyday run-of-the-mill system service listener, with two notable exceptions.
  *
- * First, there is an availability bit that we are tracking via [SatelliteManager]. See
- * [isSatelliteAllowedForCurrentLocation] for the implementation details. The thing to note about
- * this bit is that there is no callback that exists. Therefore we implement a simple polling
- * mechanism here. Since the underlying bit is location-dependent, we simply poll every hour (see
- * [POLLING_INTERVAL_MS]) and see what the current state is.
- *
- * Secondly, there are cases when simply requesting information from SatelliteManager can fail. See
+ * First, there are cases when simply requesting information from SatelliteManager can fail. See
  * [SatelliteSupport] for details on how we track the state. What's worth noting here is that
  * SUPPORTED is a stronger guarantee than [satelliteManager] being null. Therefore, the fundamental
  * data flows here ([connectionState], [signalStrength],...) are wrapped in the convenience method
@@ -138,7 +130,7 @@
  * [SupportedSatelliteManager], we can guarantee that the manager is non-null AND that it has told
  * us that satellite is supported. Therefore, we don't expect exceptions to be thrown.
  *
- * Lastly, this class is designed to wait a full minute of process uptime before making any requests
+ * Second, this class is designed to wait a full minute of process uptime before making any requests
  * to the satellite manager. The hope is that by waiting we don't have to retry due to a modem that
  * is still booting up or anything like that. We can tune or remove this behavior in the future if
  * necessary.
@@ -158,8 +150,6 @@
 
     private val satelliteManager: SatelliteManager?
 
-    override val isSatelliteAllowedForCurrentLocation: MutableStateFlow<Boolean>
-
     // Some calls into satellite manager will throw exceptions if it is not supported.
     // This is never expected to change after boot, but may need to be retried in some cases
     @get:VisibleForTesting
@@ -221,8 +211,6 @@
     init {
         satelliteManager = satelliteManagerOpt.getOrNull()
 
-        isSatelliteAllowedForCurrentLocation = MutableStateFlow(false)
-
         if (satelliteManager != null) {
             // Outer scope launch allows us to delay until MIN_UPTIME
             scope.launch {
@@ -233,10 +221,7 @@
                     { "Checked for system support. support=$str1" },
                 )
 
-                // Second, launch a job to poll for service availability based on location
-                scope.launch { pollForAvailabilityBasedOnLocation() }
-
-                // Third, register a listener to let us know if there are changes to support
+                // Second, register a listener to let us know if there are changes to support
                 scope.launch { listenForChangesToSatelliteSupport(satelliteManager) }
             }
         } else {
@@ -259,28 +244,43 @@
         return sm.checkSatelliteSupported()
     }
 
-    /*
-     * As there is no listener available for checking satellite allowed, we must poll the service.
-     * Defaulting to polling at most once every 20m while active. Subsequent OOS events will restart
-     * the job, so a flaky connection might cause more frequent checks.
-     */
-    private suspend fun pollForAvailabilityBasedOnLocation() {
+    override val isSatelliteAllowedForCurrentLocation =
         satelliteSupport
             .whenSupported(
-                supported = ::isSatelliteAllowedHasListener,
+                supported = ::isSatelliteAvailableFlow,
                 orElse = flowOf(false),
                 retrySignal = telephonyProcessCrashedEvent,
             )
-            .collectLatest { hasSubscribers ->
-                if (hasSubscribers) {
-                    while (true) {
-                        logBuffer.i { "requestIsCommunicationAllowedForCurrentLocation" }
-                        checkIsSatelliteAllowed()
-                        delay(POLLING_INTERVAL_MS)
+            .stateIn(scope, SharingStarted.Lazily, false)
+
+    private fun isSatelliteAvailableFlow(sm: SupportedSatelliteManager): Flow<Boolean> =
+        conflatedCallbackFlow {
+                val callback = SatelliteCommunicationAllowedStateCallback { allowed ->
+                    logBuffer.i({ bool1 = allowed }) {
+                        "onSatelliteCommunicationAllowedStateChanged: $bool1"
+                    }
+
+                    trySend(allowed)
+                }
+
+                var registered = false
+                try {
+                    sm.registerForCommunicationAllowedStateChanged(
+                        bgDispatcher.asExecutor(),
+                        callback
+                    )
+                    registered = true
+                } catch (e: Exception) {
+                    logBuffer.e("Error calling registerForCommunicationAllowedStateChanged", e)
+                }
+
+                awaitClose {
+                    if (registered) {
+                        sm.unregisterForCommunicationAllowedStateChanged(callback)
                     }
                 }
             }
-    }
+            .flowOn(bgDispatcher)
 
     /**
      * Register a callback with [SatelliteManager] to let us know if there is a change in satellite
@@ -410,14 +410,6 @@
             }
         }
 
-    /**
-     * Signal that we should start polling [checkIsSatelliteAllowed]. We only need to poll if there
-     * are active listeners to [isSatelliteAllowedForCurrentLocation]
-     */
-    @SuppressWarnings("unused")
-    private fun isSatelliteAllowedHasListener(sm: SupportedSatelliteManager): Flow<Boolean> =
-        isSatelliteAllowedForCurrentLocation.subscriptionCount.map { it > 0 }.distinctUntilChanged()
-
     override val connectionState =
         satelliteSupport
             .whenSupported(
@@ -485,28 +477,6 @@
             }
             .flowOn(bgDispatcher)
 
-    /** Fire off a request to check for satellite availability. Always runs on the bg context */
-    private suspend fun checkIsSatelliteAllowed() =
-        withContext(bgDispatcher) {
-            satelliteManager?.requestIsCommunicationAllowedForCurrentLocation(
-                bgDispatcher.asExecutor(),
-                object : OutcomeReceiver<Boolean, SatelliteManager.SatelliteException> {
-                    override fun onError(e: SatelliteManager.SatelliteException) {
-                        logBuffer.e(
-                            "Found exception when checking availability",
-                            e,
-                        )
-                        isSatelliteAllowedForCurrentLocation.value = false
-                    }
-
-                    override fun onResult(allowed: Boolean) {
-                        logBuffer.i { "isSatelliteAllowedForCurrentLocation: $allowed" }
-                        isSatelliteAllowedForCurrentLocation.value = allowed
-                    }
-                }
-            )
-        }
-
     private suspend fun SatelliteManager.checkSatelliteSupported(): SatelliteSupport =
         suspendCancellableCoroutine { continuation ->
             val cb =
@@ -546,9 +516,6 @@
         }
 
     companion object {
-        // TTL for satellite polling is twenty minutes
-        const val POLLING_INTERVAL_MS: Long = 1000 * 60 * 20
-
         // Let the system boot up and stabilize before we check for system support
         const val MIN_UPTIME: Long = 1000 * 60
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt
index 16bd7f8..d46aaf4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt
@@ -18,9 +18,12 @@
 
 import android.animation.Animator
 import android.animation.AnimatorListenerAdapter
+import android.annotation.IdRes
 import android.content.res.ColorStateList
 import android.graphics.drawable.GradientDrawable
 import android.view.View
+import android.view.ViewGroup
+import android.widget.FrameLayout
 import android.widget.ImageView
 import android.widget.TextView
 import androidx.lifecycle.Lifecycle
@@ -31,6 +34,7 @@
 import com.android.systemui.lifecycle.repeatWhenAttached
 import com.android.systemui.res.R
 import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.statusbar.StatusBarIconView
 import com.android.systemui.statusbar.chips.ui.binder.ChipChronometerBinder
 import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
 import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer
@@ -90,7 +94,7 @@
                 if (Flags.statusBarScreenSharingChips()) {
                     val chipView: View = view.requireViewById(R.id.ongoing_activity_chip)
                     val chipContext = chipView.context
-                    val chipIconView: ImageView =
+                    val chipDefaultIconView: ImageView =
                         chipView.requireViewById(R.id.ongoing_activity_chip_icon)
                     val chipTimeView: ChipChronometer =
                         chipView.requireViewById(R.id.ongoing_activity_chip_time)
@@ -105,16 +109,25 @@
                             when (chipModel) {
                                 is OngoingActivityChipModel.Shown -> {
                                     // Data
-                                    IconViewBinder.bindNullable(chipModel.icon, chipIconView)
+                                    setChipIcon(chipModel, chipBackgroundView, chipDefaultIconView)
                                     setChipMainContent(chipModel, chipTextView, chipTimeView)
                                     chipView.setOnClickListener(chipModel.onClickListener)
+                                    updateChipPadding(
+                                        chipModel,
+                                        chipBackgroundView,
+                                        chipTextView,
+                                        chipTimeView,
+                                    )
 
                                     // Accessibility
                                     setChipAccessibility(chipModel, chipView, chipBackgroundView)
 
                                     // Colors
                                     val textColor = chipModel.colors.text(chipContext)
-                                    chipIconView.imageTintList = ColorStateList.valueOf(textColor)
+                                    chipDefaultIconView.imageTintList =
+                                        ColorStateList.valueOf(textColor)
+                                    chipBackgroundView.getCustomIconView()?.imageTintList =
+                                        ColorStateList.valueOf(textColor)
                                     chipTimeView.setTextColor(textColor)
                                     chipTextView.setTextColor(textColor)
                                     (chipBackgroundView.background as GradientDrawable).color =
@@ -151,6 +164,69 @@
         }
     }
 
+    private fun setChipIcon(
+        chipModel: OngoingActivityChipModel.Shown,
+        backgroundView: ChipBackgroundContainer,
+        defaultIconView: ImageView,
+    ) {
+        // Always remove any previously set custom icon. If we have a new custom icon, we'll re-add
+        // it.
+        backgroundView.removeView(backgroundView.getCustomIconView())
+
+        when (val icon = chipModel.icon) {
+            null -> {
+                defaultIconView.visibility = View.GONE
+            }
+            is OngoingActivityChipModel.ChipIcon.Basic -> {
+                IconViewBinder.bind(icon.impl, defaultIconView)
+                defaultIconView.visibility = View.VISIBLE
+            }
+            is OngoingActivityChipModel.ChipIcon.StatusBarView -> {
+                // Hide the default icon since we'll show this custom icon instead.
+                defaultIconView.visibility = View.GONE
+
+                // Add the new custom icon:
+                // 1. Set up the right visual params.
+                val iconView = icon.impl
+                with(iconView) {
+                    id = CUSTOM_ICON_VIEW_ID
+                    // TODO(b/354930838): Update the content description to not include "phone" and
+                    // maybe include the app name.
+                    contentDescription =
+                        context.resources.getString(R.string.ongoing_phone_call_content_description)
+                }
+
+                // 2. If we just reinflated the view, we may need to detach the icon view from the
+                // old chip before we reattach it to the new one.
+                // See also: NotificationIconContainerViewBinder#bindIcons.
+                val currentParent = iconView.parent as? ViewGroup
+                if (currentParent != null && currentParent != backgroundView) {
+                    currentParent.removeView(iconView)
+                    currentParent.removeTransientView(iconView)
+                }
+
+                // 3: Add the icon as the starting view.
+                backgroundView.addView(
+                    iconView,
+                    /* index= */ 0,
+                    generateCustomIconLayoutParams(iconView),
+                )
+            }
+        }
+    }
+
+    private fun View.getCustomIconView(): StatusBarIconView? {
+        return this.findViewById(CUSTOM_ICON_VIEW_ID)
+    }
+
+    private fun generateCustomIconLayoutParams(iconView: ImageView): FrameLayout.LayoutParams {
+        val customIconSize =
+            iconView.context.resources.getDimensionPixelSize(
+                R.dimen.ongoing_activity_chip_embedded_padding_icon_size
+            )
+        return FrameLayout.LayoutParams(customIconSize, customIconSize)
+    }
+
     private fun setChipMainContent(
         chipModel: OngoingActivityChipModel.Shown,
         chipTextView: TextView,
@@ -180,37 +256,93 @@
                 chipTimeView.visibility = View.GONE
             }
         }
-        updateChipTextPadding(chipModel, chipTextView, chipTimeView)
     }
 
-    private fun updateChipTextPadding(
+    private fun updateChipPadding(
         chipModel: OngoingActivityChipModel.Shown,
+        backgroundView: View,
         chipTextView: TextView,
         chipTimeView: ChipChronometer,
     ) {
-        val requiresPadding = chipModel.icon != null
-        if (requiresPadding) {
-            chipTextView.addChipTextPaddingStart()
-            chipTimeView.addChipTextPaddingStart()
+        if (chipModel.icon != null) {
+            if (chipModel.icon is OngoingActivityChipModel.ChipIcon.StatusBarView) {
+                // If the icon is a custom [StatusBarIconView], then it should've come from
+                // `Notification.smallIcon`, which is required to embed its own paddings. We need to
+                // adjust the other paddings to make everything look good :)
+                backgroundView.setBackgroundPaddingForEmbeddedPaddingIcon()
+                chipTextView.setTextPaddingForEmbeddedPaddingIcon()
+                chipTimeView.setTextPaddingForEmbeddedPaddingIcon()
+            } else {
+                backgroundView.setBackgroundPaddingForNormalIcon()
+                chipTextView.setTextPaddingForNormalIcon()
+                chipTimeView.setTextPaddingForNormalIcon()
+            }
         } else {
-            chipTextView.removeChipTextPaddingStart()
-            chipTimeView.removeChipTextPaddingStart()
+            backgroundView.setBackgroundPaddingForNoIcon()
+            chipTextView.setTextPaddingForNoIcon()
+            chipTimeView.setTextPaddingForNoIcon()
         }
     }
 
-    private fun View.addChipTextPaddingStart() {
+    private fun View.setTextPaddingForEmbeddedPaddingIcon() {
+        val newPaddingEnd =
+            context.resources.getDimensionPixelSize(
+                R.dimen.ongoing_activity_chip_text_end_padding_for_embedded_padding_icon
+            )
+        setPaddingRelative(
+            // The icon should embed enough padding between the icon and time view.
+            /* start= */ 0,
+            this.paddingTop,
+            newPaddingEnd,
+            this.paddingBottom,
+        )
+    }
+
+    private fun View.setTextPaddingForNormalIcon() {
         this.setPaddingRelative(
             this.context.resources.getDimensionPixelSize(
                 R.dimen.ongoing_activity_chip_icon_text_padding
             ),
             paddingTop,
-            paddingEnd,
+            // The background view will contain the right end padding.
+            /* end= */ 0,
             paddingBottom,
         )
     }
 
-    private fun View.removeChipTextPaddingStart() {
-        this.setPaddingRelative(/* start= */ 0, paddingTop, paddingEnd, paddingBottom)
+    private fun View.setTextPaddingForNoIcon() {
+        // The background view will have even start & end paddings, so we don't want the text view
+        // to add any additional padding.
+        this.setPaddingRelative(/* start= */ 0, paddingTop, /* end= */ 0, paddingBottom)
+    }
+
+    private fun View.setBackgroundPaddingForEmbeddedPaddingIcon() {
+        val sidePadding =
+            context.resources.getDimensionPixelSize(
+                R.dimen.ongoing_activity_chip_side_padding_for_embedded_padding_icon
+            )
+        setPaddingRelative(
+            sidePadding,
+            paddingTop,
+            sidePadding,
+            paddingBottom,
+        )
+    }
+
+    private fun View.setBackgroundPaddingForNormalIcon() {
+        val sidePadding =
+            context.resources.getDimensionPixelSize(R.dimen.ongoing_activity_chip_side_padding)
+        setPaddingRelative(
+            sidePadding,
+            paddingTop,
+            sidePadding,
+            paddingBottom,
+        )
+    }
+
+    private fun View.setBackgroundPaddingForNoIcon() {
+        // The padding for the normal icon is also appropriate for no icon.
+        setBackgroundPaddingForNormalIcon()
     }
 
     private fun setChipAccessibility(
@@ -269,6 +401,10 @@
             )
             .start()
     }
+
+    companion object {
+        @IdRes private val CUSTOM_ICON_VIEW_ID = R.id.ongoing_activity_chip_custom_icon
+    }
 }
 
 /** Listener for various events that may affect the status bar's visibility. */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AvalancheController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AvalancheController.kt
index a88c6d7..5ba5c06 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AvalancheController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AvalancheController.kt
@@ -38,9 +38,10 @@
 class AvalancheController
 @Inject
 constructor(
-    dumpManager: DumpManager,
-    private val uiEventLogger: UiEventLogger,
-    @Background private val bgHandler: Handler
+        dumpManager: DumpManager,
+        private val uiEventLogger: UiEventLogger,
+        private val headsUpManagerLogger: HeadsUpManagerLogger,
+        @Background private val bgHandler: Handler
 ) : Dumpable {
 
     private val tag = "AvalancheController"
@@ -53,6 +54,7 @@
                 // allowed again.
                 logDroppedHunsInBackground(getWaitingKeys().size)
                 clearNext()
+                headsUpEntryShowing = null
             }
             if (field != value) {
                 field = value
@@ -109,32 +111,36 @@
     }
 
     /** Run or delay Runnable for given HeadsUpEntry */
-    fun update(entry: HeadsUpEntry?, runnable: Runnable?, label: String) {
+    fun update(entry: HeadsUpEntry?, runnable: Runnable?, caller: String) {
+        val isEnabled = isEnabled()
+        val key = getKey(entry)
+
         if (runnable == null) {
-            log { "Runnable is NULL, stop update." }
+            headsUpManagerLogger.logAvalancheUpdate(caller, isEnabled, key, "Runnable NULL, stop")
             return
         }
-        if (!isEnabled()) {
+        if (!isEnabled) {
+            headsUpManagerLogger.logAvalancheUpdate(caller, isEnabled, key,
+                    "NOT ENABLED, run runnable")
             runnable.run()
             return
         }
-        log { "\n " }
-        val fn = "$label => AvalancheController.update ${getKey(entry)}"
         if (entry == null) {
-            log { "Entry is NULL, stop update." }
+            headsUpManagerLogger.logAvalancheUpdate(caller, isEnabled, key, "Entry NULL, stop")
             return
         }
         if (debug) {
-            debugRunnableLabelMap[runnable] = label
+            debugRunnableLabelMap[runnable] = caller
         }
+        var outcome = ""
         if (isShowing(entry)) {
-            log { "\n$fn => update showing" }
+            outcome = "update showing"
             runnable.run()
         } else if (entry in nextMap) {
-            log { "\n$fn => update next" }
+            outcome = "update next"
             nextMap[entry]?.add(runnable)
         } else if (headsUpEntryShowing == null) {
-            log { "\n$fn => showNow" }
+            outcome = "show now"
             showNow(entry, arrayListOf(runnable))
         } else {
             // Clean up invalid state when entry is in list but not map and vice versa
@@ -156,7 +162,8 @@
                 )
             }
         }
-        logState("after $fn")
+        outcome += getStateStr()
+        headsUpManagerLogger.logAvalancheUpdate(caller, isEnabled, key, outcome)
     }
 
     @VisibleForTesting
@@ -169,32 +176,37 @@
      * Run or ignore Runnable for given HeadsUpEntry. If entry was never shown, ignore and delete
      * all Runnables associated with that entry.
      */
-    fun delete(entry: HeadsUpEntry?, runnable: Runnable?, label: String) {
+    fun delete(entry: HeadsUpEntry?, runnable: Runnable?, caller: String) {
+        val isEnabled = isEnabled()
+        val key = getKey(entry)
+
         if (runnable == null) {
-            log { "Runnable is NULL, stop delete." }
+            headsUpManagerLogger.logAvalancheDelete(caller, isEnabled, key, "Runnable NULL, stop")
             return
         }
-        if (!isEnabled()) {
+        if (!isEnabled) {
+            headsUpManagerLogger.logAvalancheDelete(caller, isEnabled, key,
+                    "NOT ENABLED, run runnable")
             runnable.run()
             return
         }
-        log { "\n " }
-        val fn = "$label => AvalancheController.delete " + getKey(entry)
         if (entry == null) {
-            log { "$fn => entry NULL, running runnable" }
+            headsUpManagerLogger.logAvalancheDelete(caller, isEnabled, key,
+                    "Entry NULL, run runnable")
             runnable.run()
             return
         }
+        var outcome = ""
         if (entry in nextMap) {
-            log { "$fn => remove from next" }
+            outcome = "remove from next"
             if (entry in nextMap) nextMap.remove(entry)
             if (entry in nextList) nextList.remove(entry)
             uiEventLogger.log(ThrottleEvent.AVALANCHE_THROTTLING_HUN_REMOVED)
         } else if (entry in debugDropSet) {
-            log { "$fn => remove from dropset" }
+            outcome = "remove from dropset"
             debugDropSet.remove(entry)
         } else if (isShowing(entry)) {
-            log { "$fn => remove showing ${getKey(entry)}" }
+            outcome = "remove showing"
             previousHunKey = getKey(headsUpEntryShowing)
             // Show the next HUN before removing this one, so that we don't tell listeners
             // onHeadsUpPinnedModeChanged, which causes
@@ -203,23 +215,27 @@
             showNext()
             runnable.run()
         } else {
-            log { "$fn => run runnable for untracked shown ${getKey(entry)}" }
+            outcome = "run runnable for untracked shown"
             runnable.run()
         }
-        logState("after $fn")
+        headsUpManagerLogger.logAvalancheDelete(caller, isEnabled(), getKey(entry), outcome)
     }
 
     /**
      * Returns duration based on
-     * 1) Whether HeadsUpEntry is the last one tracked byAvalancheController
+     * 1) Whether HeadsUpEntry is the last one tracked by AvalancheController
      * 2) The priority of the top HUN in the next batch Used by
      *    BaseHeadsUpManager.HeadsUpEntry.calculateFinishTime to shorten display duration.
      */
-    fun getDurationMs(entry: HeadsUpEntry, autoDismissMs: Int): Int {
+    fun getDurationMs(entry: HeadsUpEntry?, autoDismissMs: Int): Int {
         if (!isEnabled()) {
             // Use default duration, like we did before AvalancheController existed
             return autoDismissMs
         }
+        if (entry == null) {
+            // This should never happen
+            return autoDismissMs
+        }
         val showingList: MutableList<HeadsUpEntry> = mutableListOf()
         if (headsUpEntryShowing != null) {
             showingList.add(headsUpEntryShowing!!)
@@ -384,23 +400,12 @@
     }
 
     private fun getStateStr(): String {
-        return "SHOWING: [${getKey(headsUpEntryShowing)}]" +
-            "\nPREVIOUS: [$previousHunKey]" +
-            "\nNEXT LIST: $nextListStr" +
-            "\nNEXT MAP: $nextMapStr" +
-            "\nDROPPED: $dropSetStr" +
-            "\nENABLED: $enableAtRuntime"
-    }
-
-    private fun logState(reason: String) {
-        log {
-            "\n================================================================================="
-        }
-        log { "STATE $reason" }
-        log { getStateStr() }
-        log {
-            "=================================================================================\n"
-        }
+        return "\navalanche state:" +
+                "\n\tshowing: [${getKey(headsUpEntryShowing)}]" +
+                "\n\tprevious: [$previousHunKey]" +
+                "\n\tnext list: $nextListStr" +
+                "\n\tnext map: $nextMapStr" +
+                "\n\tdropped: $dropSetStr"
     }
 
     private val dropSetStr: String
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java
index a0eb989..3786958 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java
@@ -41,6 +41,7 @@
 import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
 import com.android.systemui.statusbar.notification.shared.NotificationThrottleHun;
 import com.android.systemui.statusbar.notification.shared.NotificationsHeadsUpRefactor;
+import com.android.systemui.statusbar.phone.ExpandHeadsUpOnInlineReply;
 import com.android.systemui.util.ListenerSet;
 import com.android.systemui.util.concurrency.DelayableExecutor;
 import com.android.systemui.util.settings.GlobalSettings;
@@ -170,7 +171,6 @@
         mLogger.logShowNotificationRequest(entry);
 
         Runnable runnable = () -> {
-            // TODO(b/315362456) log outside runnable too
             mLogger.logShowNotification(entry);
 
             // Add new entry and begin managing it
@@ -243,8 +243,10 @@
             return;
         }
         // TODO(b/328390331) move accessibility events to the view layer
-        headsUpEntry.mEntry.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
-
+        if (headsUpEntry.mEntry != null) {
+            headsUpEntry.mEntry.sendAccessibilityEvent(
+                    AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
+        }
         if (shouldHeadsUpAgain) {
             headsUpEntry.updateEntry(true /* updatePostTime */, "updateNotification");
             if (headsUpEntry != null) {
@@ -333,6 +335,9 @@
     }
 
     protected boolean shouldHeadsUpBecomePinned(@NonNull NotificationEntry entry) {
+        if (entry == null) {
+            return false;
+        }
         final HeadsUpEntry headsUpEntry = getHeadsUpEntry(entry.getKey());
         if (headsUpEntry == null) {
             // This should not happen since shouldHeadsUpBecomePinned is always called after adding
@@ -343,6 +348,15 @@
     }
 
     protected boolean hasFullScreenIntent(@NonNull NotificationEntry entry) {
+        if (entry == null) {
+            return false;
+        }
+        if (entry.getSbn() == null) {
+            return false;
+        }
+        if (entry.getSbn().getNotification() == null) {
+            return false;
+        }
         return entry.getSbn().getNotification().fullScreenIntent != null;
     }
 
@@ -425,7 +439,7 @@
             onEntryRemoved(finalHeadsUpEntry);
             // TODO(b/328390331) move accessibility events to the view layer
             entry.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
-            if (NotificationsHeadsUpRefactor.isEnabled()) {
+            if (NotificationThrottleHun.isEnabled()) {
                 finalHeadsUpEntry.cancelAutoRemovalCallbacks("removeEntry");
             } else {
                 finalHeadsUpEntry.reset();
@@ -450,6 +464,15 @@
     }
 
     /**
+     * Called to notify the listeners that the HUN animating away animation has ended.
+     */
+    public void onEntryAnimatingAwayEnded(@NonNull NotificationEntry entry) {
+        for (OnHeadsUpChangedListener listener : mListeners) {
+            listener.onHeadsUpAnimatingAwayEnded(entry);
+        }
+    }
+
+    /**
      * Manager-specific logic, that should occur, when the entry is updated, and its posted time has
      * changed.
      *
@@ -498,6 +521,9 @@
         keySet.addAll(mAvalancheController.getWaitingKeys());
         for (String key : keySet) {
             HeadsUpEntry entry = getHeadsUpEntry(key);
+            if (entry.mEntry == null) {
+                continue;
+            }
             String packageName = entry.mEntry.getSbn().getPackageName();
             String snoozeKey = snoozeKey(packageName, mUser);
             mLogger.logPackageSnoozed(snoozeKey);
@@ -565,7 +591,7 @@
         pw.print("  now="); pw.println(mSystemClock.elapsedRealtime());
         pw.print("  mUser="); pw.println(mUser);
         for (HeadsUpEntry entry: mHeadsUpEntryMap.values()) {
-            pw.print("  HeadsUpEntry="); pw.println(entry.mEntry);
+            pw.println(entry.mEntry == null ? "null" : entry.mEntry);
         }
         int n = mSnoozedPackages.size();
         pw.println("  snoozed packages: " + n);
@@ -585,7 +611,7 @@
     private boolean hasPinnedNotificationInternal() {
         for (String key : mHeadsUpEntryMap.keySet()) {
             HeadsUpEntry entry = getHeadsUpEntry(key);
-            if (entry.mEntry.isRowPinned()) {
+            if (entry.mEntry != null && entry.mEntry.isRowPinned()) {
                 return true;
             }
         }
@@ -610,7 +636,7 @@
                 // when the user unpinned all of HUNs by moving one HUN, all of HUNs should not stay
                 // on the screen.
                 if (userUnPinned && headsUpEntry.mEntry != null) {
-                    if (headsUpEntry.mEntry.mustStayOnScreen()) {
+                    if (headsUpEntry.mEntry != null && headsUpEntry.mEntry.mustStayOnScreen()) {
                         headsUpEntry.mEntry.setHeadsUpIsVisible();
                     }
                 }
@@ -686,7 +712,7 @@
             return true;
         }
         return headsUpEntry == null || headsUpEntry.wasShownLongEnough()
-                || headsUpEntry.mEntry.isRowDismissed();
+                || (headsUpEntry.mEntry != null && headsUpEntry.mEntry.isRowDismissed());
     }
 
     /**
@@ -726,6 +752,7 @@
      * of AvalancheController that take it as param.
      */
     public class HeadsUpEntry implements Comparable<HeadsUpEntry> {
+        public boolean mRemoteInputActivatedAtLeastOnce;
         public boolean mRemoteInputActive;
         public boolean mUserActionMayIndirectlyRemove;
 
@@ -741,7 +768,7 @@
         @Nullable private Runnable mCancelRemoveRunnable;
 
         public HeadsUpEntry() {
-            NotificationsHeadsUpRefactor.assertInLegacyMode();
+            NotificationThrottleHun.assertInLegacyMode();
         }
 
         public HeadsUpEntry(NotificationEntry entry) {
@@ -752,7 +779,7 @@
 
         /** Attach a NotificationEntry. */
         public void setEntry(@NonNull final NotificationEntry entry) {
-            NotificationsHeadsUpRefactor.assertInLegacyMode();
+            NotificationThrottleHun.assertInLegacyMode();
             setEntry(entry, createRemoveRunnable(entry));
         }
 
@@ -835,12 +862,23 @@
          */
         public boolean isSticky() {
             if (mEntry == null) return false;
+
+            if (ExpandHeadsUpOnInlineReply.isEnabled()) {
+                // we don't consider pinned and expanded huns as sticky after the remote input
+                // has been activated for them
+                if (!mRemoteInputActive && mRemoteInputActivatedAtLeastOnce) {
+                    return false;
+                }
+            }
+
             return (mEntry.isRowPinned() && mExpanded)
                     || mRemoteInputActive
                     || hasFullScreenIntent(mEntry);
         }
 
         public boolean isStickyForSomeTime() {
+            if (mEntry == null) return false;
+
             return mEntry.isStickyAndNotDemoted();
         }
 
@@ -853,6 +891,14 @@
         }
 
         public int compareNonTimeFields(HeadsUpEntry headsUpEntry) {
+            if (mEntry == null && headsUpEntry.mEntry == null) {
+                return 0;
+            } else if (headsUpEntry.mEntry == null) {
+                return -1;
+            } else if (mEntry == null) {
+                return 1;
+            }
+
             boolean selfFullscreen = hasFullScreenIntent(mEntry);
             boolean otherFullscreen = hasFullScreenIntent(headsUpEntry.mEntry);
             if (selfFullscreen && !otherFullscreen) {
@@ -879,6 +925,13 @@
         }
 
         public int compareTo(@NonNull HeadsUpEntry headsUpEntry) {
+            if (mEntry == null && headsUpEntry.mEntry == null) {
+                return 0;
+            } else if (headsUpEntry.mEntry == null) {
+                return -1;
+            } else if (mEntry == null) {
+                return 1;
+            }
             boolean isPinned = mEntry.isRowPinned();
             boolean otherPinned = headsUpEntry.mEntry.isRowPinned();
             if (isPinned && !otherPinned) {
@@ -923,7 +976,7 @@
         }
 
         public void reset() {
-            NotificationsHeadsUpRefactor.assertInLegacyMode();
+            NotificationThrottleHun.assertInLegacyMode();
             cancelAutoRemovalCallbacks("reset()");
             mEntry = null;
             mRemoveRunnable = null;
@@ -943,7 +996,7 @@
                     mLogger.logAutoRemoveCanceled(mEntry, reason);
                 }
             };
-            if (isHeadsUpEntry(this.mEntry.getKey())) {
+            if (mEntry != null && isHeadsUpEntry(mEntry.getKey())) {
                 mAvalancheController.update(this, runnable, reason + " cancelAutoRemovalCallbacks");
             } else {
                 // Just removed
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java
index 7b82b56..a115baa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java
@@ -185,7 +185,6 @@
 
     @Override
     public void stopCasting(CastDevice device) {
-        // TODO(b/332662551): Convert Logcat to LogBuffer.
         final boolean isProjection = device.getTag() instanceof MediaProjectionInfo;
         mLogger.logStopCasting(isProjection);
         if (isProjection) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.kt
index 28a2a1f..fcf77d5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.kt
@@ -42,6 +42,7 @@
      *   should be ranked higher and 0 if they are equal.
      */
     fun compare(a: NotificationEntry?, b: NotificationEntry?): Int
+
     /**
      * Extends the lifetime of the currently showing pulsing notification so that the pulse lasts
      * longer.
@@ -184,6 +185,8 @@
     fun unpinAll(userUnPinned: Boolean)
 
     fun updateNotification(key: String, shouldHeadsUpAgain: Boolean)
+
+    fun onEntryAnimatingAwayEnded(entry: NotificationEntry)
 }
 
 /** Sets the animation state of the HeadsUpManager. */
@@ -204,41 +207,77 @@
 /* No op impl of HeadsUpManager. */
 class HeadsUpManagerEmptyImpl @Inject constructor() : HeadsUpManager {
     override val allEntries = Stream.empty<NotificationEntry>()
+
     override fun addHeadsUpPhoneListener(listener: OnHeadsUpPhoneListenerChange) {}
+
     override fun addListener(listener: OnHeadsUpChangedListener) {}
+
     override fun addSwipedOutNotification(key: String) {}
+
     override fun canRemoveImmediately(key: String) = false
+
     override fun compare(a: NotificationEntry?, b: NotificationEntry?) = 0
+
     override fun dump(pw: PrintWriter, args: Array<out String>) {}
+
     override fun extendHeadsUp() {}
+
     override fun getEarliestRemovalTime(key: String?) = 0L
+
     override fun getTouchableRegion(): Region? = null
+
     override fun getTopEntry() = null
+
     override fun hasPinnedHeadsUp() = false
+
     override fun isHeadsUpEntry(key: String) = false
+
     override fun isHeadsUpAnimatingAwayValue() = false
+
     override fun isSnoozed(packageName: String) = false
+
     override fun isSticky(key: String?) = false
+
     override fun isTrackingHeadsUp() = false
+
     override fun onExpandingFinished() {}
+
     override fun releaseAllImmediately() {}
+
     override fun removeListener(listener: OnHeadsUpChangedListener) {}
+
     override fun removeNotification(key: String, releaseImmediately: Boolean) = false
+
     override fun removeNotification(key: String, releaseImmediately: Boolean, animate: Boolean) =
         false
+
     override fun setAnimationStateHandler(handler: AnimationStateHandler) {}
+
     override fun setExpanded(entry: NotificationEntry, expanded: Boolean) {}
+
     override fun setGutsShown(entry: NotificationEntry, gutsShown: Boolean) {}
+
     override fun setHeadsUpAnimatingAway(headsUpAnimatingAway: Boolean) {}
+
     override fun setRemoteInputActive(entry: NotificationEntry, remoteInputActive: Boolean) {}
+
     override fun setTrackingHeadsUp(tracking: Boolean) {}
+
     override fun setUser(user: Int) {}
+
     override fun setUserActionMayIndirectlyRemove(entry: NotificationEntry) {}
+
     override fun shouldSwallowClick(key: String): Boolean = false
+
     override fun showNotification(entry: NotificationEntry) {}
+
     override fun snooze() {}
+
     override fun unpinAll(userUnPinned: Boolean) {}
+
     override fun updateNotification(key: String, alert: Boolean) {}
+
+    override fun onEntryAnimatingAwayEnded(entry: NotificationEntry) {}
 }
 
 @Module
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManagerLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManagerLogger.kt
index 6ffb162..80c595f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManagerLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManagerLogger.kt
@@ -66,6 +66,30 @@
         })
     }
 
+    fun logAvalancheUpdate(caller: String, isEnabled: Boolean, notifEntryKey: String,
+                           outcome: String) {
+        buffer.log(TAG, INFO, {
+            str1 = caller
+            str2 = notifEntryKey
+            str3 = outcome
+            bool1 = isEnabled
+        }, {
+            "$str1\n\t=> AC[isEnabled:$bool1] update: $str2\n\t=> $str3"
+        })
+    }
+
+    fun logAvalancheDelete(caller: String, isEnabled: Boolean, notifEntryKey: String,
+                           outcome: String) {
+        buffer.log(TAG, INFO, {
+            str1 = caller
+            str2 = notifEntryKey
+            str3 = outcome
+            bool1 = isEnabled
+        }, {
+            "$str1\n\t=> AC[isEnabled:$bool1] delete: $str2\n\t=> $str3"
+        })
+    }
+
     fun logShowNotification(entry: NotificationEntry) {
         buffer.log(TAG, INFO, {
             str1 = entry.logKey
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/OnHeadsUpChangedListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/OnHeadsUpChangedListener.java
index 86998ab..de3bf04 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/OnHeadsUpChangedListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/OnHeadsUpChangedListener.java
@@ -48,4 +48,9 @@
      * @param isHeadsUp whether the notification is now a headsUp notification
      */
     default void onHeadsUpStateChanged(@NonNull NotificationEntry entry, boolean isHeadsUp) {}
+
+    /**
+     * Called on HUN disappearing animation ends
+     */
+    default void onHeadsUpAnimatingAwayEnded(@NonNull NotificationEntry entry) {}
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/PolicyModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/PolicyModule.kt
index cf9a78f..21ec14f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/PolicyModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/PolicyModule.kt
@@ -20,6 +20,7 @@
 import android.os.UserManager.DISALLOW_CONFIG_LOCATION
 import android.os.UserManager.DISALLOW_MICROPHONE_TOGGLE
 import android.os.UserManager.DISALLOW_SHARE_LOCATION
+import com.android.systemui.Flags
 import com.android.systemui.qs.QsEventLogger
 import com.android.systemui.qs.pipeline.shared.TileSpec
 import com.android.systemui.qs.tileimpl.QSTileImpl
@@ -67,25 +68,18 @@
 import com.android.systemui.qs.tiles.viewmodel.QSTilePolicy
 import com.android.systemui.qs.tiles.viewmodel.QSTileUIConfig
 import com.android.systemui.qs.tiles.viewmodel.QSTileViewModel
+import com.android.systemui.qs.tiles.viewmodel.StubQSTileViewModel
 import com.android.systemui.res.R
 import dagger.Binds
 import dagger.Module
 import dagger.Provides
 import dagger.multibindings.IntoMap
 import dagger.multibindings.StringKey
+import javax.inject.Provider
 
 @Module
 interface PolicyModule {
 
-    /** Inject DndTile into tileMap in QSModule */
-    @Binds @IntoMap @StringKey(DndTile.TILE_SPEC) fun bindDndTile(dndTile: DndTile): QSTileImpl<*>
-
-    /** Inject ModesTile into tileMap in QSModule */
-    @Binds
-    @IntoMap
-    @StringKey(ModesTile.TILE_SPEC)
-    fun bindModesTile(modesTile: ModesTile): QSTileImpl<*>
-
     /** Inject WorkModeTile into tileMap in QSModule */
     @Binds
     @IntoMap
@@ -136,7 +130,19 @@
         const val CAMERA_TOGGLE_TILE_SPEC = "cameratoggle"
         const val MIC_TOGGLE_TILE_SPEC = "mictoggle"
         const val DND_TILE_SPEC = "dnd"
-        const val MODES_TILE_SPEC = "modes"
+
+        /** Inject DndTile or ModesTile into tileMap in QSModule based on feature flag */
+        @Provides
+        @IntoMap
+        @StringKey(DND_TILE_SPEC)
+        fun bindDndOrModesTile(
+            // Using providers to make sure that the unused tile isn't initialised at all if the
+            // flag is off.
+            dndTile: Provider<DndTile>,
+            modesTile: Provider<ModesTile>,
+        ): QSTileImpl<*> {
+            return if (android.app.Flags.modesUi()) modesTile.get() else dndTile.get()
+        }
 
         /** Inject flashlight config */
         @Provides
@@ -283,6 +289,7 @@
                         labelRes = R.string.quick_settings_work_mode_label,
                     ),
                 instanceId = uiEventLogger.getNewInstanceId(),
+                autoRemoveOnUnavailable = false,
             )
 
         /** Inject work mode into tileViewModelMap in QSModule */
@@ -386,51 +393,51 @@
             return factory.create(MICROPHONE)
         }
 
-        /** Inject microphone toggle config */
+        /** Inject DND tile or Modes tile config based on feature flag */
         @Provides
         @IntoMap
         @StringKey(DND_TILE_SPEC)
-        fun provideDndTileConfig(uiEventLogger: QsEventLogger): QSTileConfig =
-            QSTileConfig(
-                tileSpec = TileSpec.create(DND_TILE_SPEC),
-                uiConfig =
-                    QSTileUIConfig.Resource(
-                        iconRes = R.drawable.qs_dnd_icon_off,
-                        labelRes = R.string.quick_settings_dnd_label,
-                    ),
-                instanceId = uiEventLogger.getNewInstanceId(),
-            )
-
-        @Provides
-        @IntoMap
-        @StringKey(MODES_TILE_SPEC)
-        fun provideModesTileConfig(uiEventLogger: QsEventLogger): QSTileConfig =
-            QSTileConfig(
-                tileSpec = TileSpec.create(MODES_TILE_SPEC),
-                uiConfig =
-                    QSTileUIConfig.Resource(
-                        iconRes = R.drawable.qs_dnd_icon_off,
-                        labelRes = R.string.quick_settings_modes_label,
-                    ),
-                instanceId = uiEventLogger.getNewInstanceId(),
-            )
+        fun provideDndOrModesTileConfig(uiEventLogger: QsEventLogger): QSTileConfig =
+            if (android.app.Flags.modesUi()) {
+                QSTileConfig(
+                    tileSpec = TileSpec.create(DND_TILE_SPEC),
+                    uiConfig =
+                        QSTileUIConfig.Resource(
+                            iconRes = R.drawable.qs_dnd_icon_off,
+                            labelRes = R.string.quick_settings_modes_label,
+                        ),
+                    instanceId = uiEventLogger.getNewInstanceId(),
+                )
+            } else {
+                QSTileConfig(
+                    tileSpec = TileSpec.create(DND_TILE_SPEC),
+                    uiConfig =
+                        QSTileUIConfig.Resource(
+                            iconRes = R.drawable.qs_dnd_icon_off,
+                            labelRes = R.string.quick_settings_dnd_label,
+                        ),
+                    instanceId = uiEventLogger.getNewInstanceId(),
+                )
+            }
 
         /** Inject ModesTile into tileViewModelMap in QSModule */
         @Provides
         @IntoMap
-        @StringKey(MODES_TILE_SPEC)
+        @StringKey(DND_TILE_SPEC)
         fun provideModesTileViewModel(
             factory: QSTileViewModelFactory.Static<ModesTileModel>,
             mapper: ModesTileMapper,
             stateInteractor: ModesTileDataInteractor,
             userActionInteractor: ModesTileUserActionInteractor
         ): QSTileViewModel =
-            factory.create(
-                TileSpec.create(MODES_TILE_SPEC),
-                userActionInteractor,
-                stateInteractor,
-                mapper,
-            )
+            if (android.app.Flags.modesUi() && Flags.qsNewTilesFuture())
+                factory.create(
+                    TileSpec.create(DND_TILE_SPEC),
+                    userActionInteractor,
+                    stateInteractor,
+                    mapper,
+                )
+            else StubQSTileViewModel
     }
 
     /** Inject FlashlightTile into tileMap in QSModule */
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 7a521a6..efd60f6 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
@@ -18,11 +18,15 @@
 
 import android.content.Context
 import android.provider.Settings
+import android.provider.Settings.Secure.ZEN_DURATION_FOREVER
+import android.provider.Settings.Secure.ZEN_DURATION_PROMPT
+import android.util.Log
 import androidx.concurrent.futures.await
 import com.android.settingslib.notification.data.repository.ZenModeRepository
 import com.android.settingslib.notification.modes.ZenIconLoader
 import com.android.settingslib.notification.modes.ZenMode
 import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.shared.notifications.data.repository.NotificationSettingsRepository
 import java.time.Duration
 import javax.inject.Inject
 import kotlinx.coroutines.flow.Flow
@@ -34,11 +38,16 @@
  * An interactor that performs business logic related to the status and configuration of Zen Mode
  * (or Do Not Disturb/DND Mode).
  */
-class ZenModeInteractor @Inject constructor(private val repository: ZenModeRepository) {
+class ZenModeInteractor
+@Inject
+constructor(
+    private val zenModeRepository: ZenModeRepository,
+    private val notificationSettingsRepository: NotificationSettingsRepository,
+) {
     private val iconLoader: ZenIconLoader = ZenIconLoader.getInstance()
 
     val isZenModeEnabled: Flow<Boolean> =
-        repository.globalZenMode
+        zenModeRepository.globalZenMode
             .map {
                 when (it ?: Settings.Global.ZEN_MODE_OFF) {
                     Settings.Global.ZEN_MODE_ALARMS -> true
@@ -51,7 +60,9 @@
             .distinctUntilChanged()
 
     val areNotificationsHiddenInShade: Flow<Boolean> =
-        combine(isZenModeEnabled, repository.consolidatedNotificationPolicy) { dndEnabled, policy ->
+        combine(isZenModeEnabled, zenModeRepository.consolidatedNotificationPolicy) {
+                dndEnabled,
+                policy ->
                 if (!dndEnabled) {
                     false
                 } else {
@@ -61,17 +72,45 @@
             }
             .distinctUntilChanged()
 
-    val modes: Flow<List<ZenMode>> = repository.modes
+    val modes: Flow<List<ZenMode>> = zenModeRepository.modes
 
     suspend fun getModeIcon(mode: ZenMode, context: Context): Icon {
         return Icon.Loaded(mode.getIcon(context, iconLoader).await(), contentDescription = null)
     }
 
-    fun activateMode(zenMode: ZenMode, duration: Duration? = null) {
-        repository.activateMode(zenMode, duration)
+    fun activateMode(zenMode: ZenMode) {
+        if (zenMode.isManualDnd) {
+            val duration =
+                when (zenDuration) {
+                    ZEN_DURATION_PROMPT -> {
+                        Log.e(
+                            TAG,
+                            "Interactor cannot handle showing the zen duration prompt. " +
+                                "Please use EnableZenModeDialog when this setting is active."
+                        )
+                        null
+                    }
+                    ZEN_DURATION_FOREVER -> null
+                    else -> Duration.ofMinutes(zenDuration.toLong())
+                }
+
+            zenModeRepository.activateMode(zenMode, duration)
+        } else {
+            zenModeRepository.activateMode(zenMode)
+        }
     }
 
     fun deactivateMode(zenMode: ZenMode) {
-        repository.deactivateMode(zenMode)
+        zenModeRepository.deactivateMode(zenMode)
+    }
+
+    private val zenDuration
+        get() = notificationSettingsRepository.zenDuration.value
+
+    fun shouldAskForZenDuration(mode: ZenMode): Boolean =
+        mode.isManualDnd && (zenDuration == ZEN_DURATION_PROMPT)
+
+    companion object {
+        private const val TAG = "ZenModeInteractor"
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogDelegate.kt
index 2b094d6..8aa989f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogDelegate.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogDelegate.kt
@@ -18,67 +18,155 @@
 
 import android.content.Intent
 import android.provider.Settings
+import android.util.Log
 import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
 import androidx.compose.ui.res.stringResource
+import androidx.lifecycle.DefaultLifecycleObserver
+import androidx.lifecycle.LifecycleOwner
 import com.android.compose.PlatformButton
 import com.android.compose.PlatformOutlinedButton
+import com.android.internal.annotations.VisibleForTesting
+import com.android.internal.jank.InteractionJankMonitor
+import com.android.systemui.animation.DialogCuj
 import com.android.systemui.animation.DialogTransitionAnimator
+import com.android.systemui.animation.Expandable
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.dialog.ui.composable.AlertDialogContent
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.res.R
+import com.android.systemui.statusbar.phone.ComponentSystemUIDialog
 import com.android.systemui.statusbar.phone.SystemUIDialog
 import com.android.systemui.statusbar.phone.SystemUIDialogFactory
 import com.android.systemui.statusbar.phone.create
 import com.android.systemui.statusbar.policy.ui.dialog.composable.ModeTileGrid
 import com.android.systemui.statusbar.policy.ui.dialog.viewmodel.ModesDialogViewModel
+import com.android.systemui.util.Assert
 import javax.inject.Inject
+import javax.inject.Provider
+import kotlin.coroutines.CoroutineContext
+import kotlinx.coroutines.withContext
 
+@SysUISingleton
 class ModesDialogDelegate
 @Inject
 constructor(
     private val sysuiDialogFactory: SystemUIDialogFactory,
     private val dialogTransitionAnimator: DialogTransitionAnimator,
     private val activityStarter: ActivityStarter,
-    private val viewModel: ModesDialogViewModel,
+    // Using a provider to avoid a circular dependency.
+    private val viewModel: Provider<ModesDialogViewModel>,
+    @Main private val mainCoroutineContext: CoroutineContext,
 ) : SystemUIDialog.Delegate {
+    // NOTE: This should only be accessed/written from the main thread.
+    @VisibleForTesting var currentDialog: ComponentSystemUIDialog? = null
+
     override fun createDialog(): SystemUIDialog {
-        return sysuiDialogFactory.create { dialog ->
-            AlertDialogContent(
-                title = { Text(stringResource(R.string.zen_modes_dialog_title)) },
-                content = { ModeTileGrid(viewModel) },
-                neutralButton = {
-                    PlatformOutlinedButton(
-                        onClick = {
-                            val animationController =
-                                dialogTransitionAnimator.createActivityTransitionController(
-                                    dialog.getButton(SystemUIDialog.BUTTON_NEUTRAL)
-                                )
-                            if (animationController == null) {
-                                // The controller will take care of dismissing for us after the
-                                // animation, but let's make sure we dismiss the dialog if we don't
-                                // animate it.
-                                dialog.dismiss()
-                            }
-                            activityStarter.startActivity(
-                                ZEN_MODE_SETTINGS_INTENT,
-                                true /* dismissShade */,
-                                animationController
-                            )
-                        }
-                    ) {
-                        Text(stringResource(R.string.zen_modes_dialog_settings))
+        Assert.isMainThread()
+        if (currentDialog != null) {
+            Log.w(TAG, "Dialog is already open, dismissing it and creating a new one.")
+            currentDialog?.dismiss()
+        }
+
+        currentDialog = sysuiDialogFactory.create() { ModesDialogContent(it) }
+        currentDialog
+            ?.lifecycle
+            ?.addObserver(
+                object : DefaultLifecycleObserver {
+                    override fun onStop(owner: LifecycleOwner) {
+                        Assert.isMainThread()
+                        currentDialog = null
                     }
-                },
-                positiveButton = {
-                    PlatformButton(onClick = { dialog.dismiss() }) {
-                        Text(stringResource(R.string.zen_modes_dialog_done))
-                    }
-                },
+                }
+            )
+
+        return currentDialog!!
+    }
+
+    @Composable
+    private fun ModesDialogContent(dialog: SystemUIDialog) {
+        AlertDialogContent(
+            title = { Text(stringResource(R.string.zen_modes_dialog_title)) },
+            content = { ModeTileGrid(viewModel.get()) },
+            neutralButton = {
+                PlatformOutlinedButton(onClick = { openSettings(dialog) }) {
+                    Text(stringResource(R.string.zen_modes_dialog_settings))
+                }
+            },
+            positiveButton = {
+                PlatformButton(onClick = { dialog.dismiss() }) {
+                    Text(stringResource(R.string.zen_modes_dialog_done))
+                }
+            },
+        )
+    }
+
+    private fun openSettings(dialog: SystemUIDialog) {
+        val animationController =
+            dialogTransitionAnimator.createActivityTransitionController(dialog)
+        if (animationController == null) {
+            // The controller will take care of dismissing for us after
+            // the animation, but let's make sure we dismiss the dialog
+            // if we don't animate it.
+            dialog.dismiss()
+        }
+        activityStarter.startActivity(
+            ZEN_MODE_SETTINGS_INTENT,
+            true /* dismissShade */,
+            animationController
+        )
+    }
+
+    suspend fun showDialog(expandable: Expandable? = null): SystemUIDialog {
+        // Dialogs shown by the DialogTransitionAnimator must be created and shown on the main
+        // thread, so we post it to the UI handler.
+        withContext(mainCoroutineContext) {
+            // Create the dialog if necessary
+            if (currentDialog == null) {
+                createDialog()
+            }
+
+            expandable
+                ?.dialogTransitionController(
+                    DialogCuj(InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN, INTERACTION_JANK_TAG)
+                )
+                ?.let { controller -> dialogTransitionAnimator.show(currentDialog!!, controller) }
+                ?: currentDialog!!.show()
+        }
+
+        return currentDialog!!
+    }
+
+    /**
+     * Launches the [intent] by animating from the dialog. If the dialog is not showing, just
+     * launches it normally without animating.
+     */
+    fun launchFromDialog(intent: Intent) {
+        Assert.isMainThread()
+        if (currentDialog == null) {
+            Log.w(
+                TAG,
+                "Cannot launch from dialog, the dialog is not present. " +
+                    "Will launch activity without animating."
             )
         }
+
+        val animationController =
+            currentDialog?.let { dialogTransitionAnimator.createActivityTransitionController(it) }
+        if (animationController == null) {
+            currentDialog?.dismiss()
+        }
+        activityStarter.startActivity(
+            intent,
+            true, /* dismissShade */
+            animationController,
+        )
     }
 
     companion object {
+        private const val TAG = "ModesDialogDelegate"
         private val ZEN_MODE_SETTINGS_INTENT = Intent(Settings.ACTION_ZEN_MODE_SETTINGS)
+        private const val INTERACTION_JANK_TAG = "configure_priority_modes"
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/composable/ModeTile.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/composable/ModeTile.kt
index 91bfdff..3b392c8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/composable/ModeTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/composable/ModeTile.kt
@@ -50,14 +50,14 @@
         Surface(
             color = tileColor,
             shape = RoundedCornerShape(16.dp),
-            modifier =
-                Modifier.combinedClickable(
-                    onClick = viewModel.onClick,
-                    onLongClick = viewModel.onLongClick
-                ),
         ) {
             Row(
-                modifier = Modifier.padding(20.dp),
+                modifier =
+                    Modifier.combinedClickable(
+                            onClick = viewModel.onClick,
+                            onLongClick = viewModel.onLongClick
+                        )
+                        .padding(20.dp),
                 verticalAlignment = Alignment.CenterVertically,
                 horizontalArrangement =
                     Arrangement.spacedBy(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModeTileViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModeTileViewModel.kt
index 5bd26cc..7c1cb6a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModeTileViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModeTileViewModel.kt
@@ -29,7 +29,6 @@
     val text: String,
     val subtext: String,
     val enabled: Boolean,
-    val contentDescription: String,
     val onClick: () -> Unit,
     val onLongClick: () -> Unit,
 )
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModesDialogViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModesDialogViewModel.kt
index e84c8b6..9422878f6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModesDialogViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModesDialogViewModel.kt
@@ -16,17 +16,26 @@
 
 package com.android.systemui.statusbar.policy.ui.dialog.viewmodel
 
+import android.app.Dialog
 import android.content.Context
+import android.content.Intent
+import android.provider.Settings.ACTION_AUTOMATIC_ZEN_RULE_SETTINGS
+import android.provider.Settings.EXTRA_AUTOMATIC_ZEN_RULE_ID
+import com.android.settingslib.notification.modes.EnableZenModeDialog
 import com.android.settingslib.notification.modes.ZenMode
+import com.android.settingslib.notification.modes.ZenModeDialogMetricsLogger
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.res.R
+import com.android.systemui.statusbar.phone.SystemUIDialog
 import com.android.systemui.statusbar.policy.domain.interactor.ZenModeInteractor
+import com.android.systemui.statusbar.policy.ui.dialog.ModesDialogDelegate
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.flowOn
 import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.scan
 
 /**
  * Viewmodel for the priority ("zen") modes dialog that can be opened from quick settings. It allows
@@ -39,15 +48,37 @@
     val context: Context,
     zenModeInteractor: ZenModeInteractor,
     @Background val bgDispatcher: CoroutineDispatcher,
+    private val dialogDelegate: ModesDialogDelegate,
 ) {
+    private val zenDialogMetricsLogger = ZenModeDialogMetricsLogger(context)
+
     // Modes that should be displayed in the dialog
-    // TODO(b/346519570): Include modes that have not been set up yet.
     private val visibleModes: Flow<List<ZenMode>> =
-        zenModeInteractor.modes.map {
-            it.filter { mode ->
-                mode.rule.isEnabled && (mode.isActive || mode.rule.isManualInvocationAllowed)
+        zenModeInteractor.modes
+            // While this is being collected (or in other words, while the dialog is open), we don't
+            // want a mode to disappear from the list if, for instance, the user deactivates it,
+            // since that can be confusing (similar to how we have visual stability for
+            // notifications while the shade is open).
+            // This ensures new modes are added to the list, and updates to modes already in the
+            // list are registered correctly.
+            .scan(listOf()) { prev, modes ->
+                val prevIds = prev.map { it.id }.toSet()
+
+                modes.filter { mode ->
+                    when {
+                        // Mode appeared previously -> keep it even if otherwise we may have
+                        // filtered it
+                        mode.id in prevIds -> true
+                        // Mode is enabled -> show if active (so user can toggle off), or if it
+                        // can be manually toggled on
+                        mode.rule.isEnabled -> mode.isActive || mode.rule.isManualInvocationAllowed
+                        // Mode was created as disabled, or disabled by the app that owns it ->
+                        // will be shown with a "Set up" text
+                        !mode.rule.isEnabled -> mode.status == ZenMode.Status.DISABLED_BY_OTHER
+                        else -> false
+                    }
+                }
             }
-        }
 
     val tiles: Flow<List<ModeTileViewModel>> =
         visibleModes
@@ -59,29 +90,63 @@
                         text = mode.rule.name,
                         subtext = getTileSubtext(mode),
                         enabled = mode.isActive,
-                        // TODO(b/346519570): This should be some combination of the above, e.g.
-                        //  "ON: Do Not Disturb, Until Mon 08:09"; see DndTile.
-                        contentDescription = "",
                         onClick = {
-                            if (mode.isActive) {
+                            if (!mode.rule.isEnabled) {
+                                openSettings(mode)
+                            } else if (mode.isActive) {
                                 zenModeInteractor.deactivateMode(mode)
                             } else {
-                                // TODO(b/346519570): Handle duration for DND mode.
-                                zenModeInteractor.activateMode(mode)
+                                if (mode.rule.isManualInvocationAllowed) {
+                                    if (zenModeInteractor.shouldAskForZenDuration(mode)) {
+                                        // NOTE: The dialog handles turning on the mode itself.
+                                        val dialog = makeZenModeDialog()
+                                        dialog.show()
+                                    } else {
+                                        zenModeInteractor.activateMode(mode)
+                                    }
+                                }
                             }
                         },
-                        onLongClick = {
-                            // TODO(b/346519570): Open settings page for mode.
-                        }
+                        onLongClick = { openSettings(mode) }
                     )
                 }
             }
             .flowOn(bgDispatcher)
 
+    private fun openSettings(mode: ZenMode) {
+        val intent: Intent =
+            Intent(ACTION_AUTOMATIC_ZEN_RULE_SETTINGS)
+                .putExtra(EXTRA_AUTOMATIC_ZEN_RULE_ID, mode.id)
+
+        dialogDelegate.launchFromDialog(intent)
+    }
+
     private fun getTileSubtext(mode: ZenMode): String {
-        // TODO(b/346519570): Use ZenModeConfig.getDescription for manual DND
+        if (!mode.rule.isEnabled) {
+            return context.resources.getString(R.string.zen_mode_set_up)
+        }
+        if (!mode.rule.isManualInvocationAllowed && !mode.isActive) {
+            return context.resources.getString(R.string.zen_mode_no_manual_invocation)
+        }
+
         val on = context.resources.getString(R.string.zen_mode_on)
         val off = context.resources.getString(R.string.zen_mode_off)
         return mode.rule.triggerDescription ?: if (mode.isActive) on else off
     }
+
+    private fun makeZenModeDialog(): Dialog {
+        val dialog =
+            EnableZenModeDialog(
+                    context,
+                    R.style.Theme_SystemUI_Dialog,
+                    /* cancelIsNeutral= */ true,
+                    zenDialogMetricsLogger
+                )
+                .createDialog()
+        SystemUIDialog.applyFlags(dialog)
+        SystemUIDialog.setShowForAllUsers(dialog, true)
+        SystemUIDialog.registerDismissListener(dialog)
+        SystemUIDialog.setDialogSize(dialog)
+        return dialog
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModel.kt
index 4869114..89227cf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModel.kt
@@ -24,6 +24,7 @@
 import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.statusbar.domain.interactor.KeyguardStatusBarInteractor
 import com.android.systemui.statusbar.notification.domain.interactor.HeadsUpNotificationInteractor
+import com.android.systemui.statusbar.notification.shared.NotificationsHeadsUpRefactor
 import com.android.systemui.statusbar.policy.BatteryController
 import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback
 import javax.inject.Inject
@@ -33,6 +34,7 @@
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.stateIn
 
 /**
@@ -54,12 +56,20 @@
     keyguardStatusBarInteractor: KeyguardStatusBarInteractor,
     batteryController: BatteryController,
 ) {
+
+    private val showingHeadsUpStatusBar: Flow<Boolean> =
+        if (NotificationsHeadsUpRefactor.isEnabled) {
+            headsUpNotificationInteractor.showHeadsUpStatusBar
+        } else {
+            flowOf(false)
+        }
+
     /** True if this view should be visible and false otherwise. */
     val isVisible: StateFlow<Boolean> =
         combine(
                 sceneInteractor.currentScene,
                 keyguardInteractor.isDozing,
-                headsUpNotificationInteractor.showHeadsUpStatusBar,
+                showingHeadsUpStatusBar,
             ) { currentScene, isDozing, showHeadsUpStatusBar ->
                 currentScene == Scenes.Lockscreen && !isDozing && !showHeadsUpStatusBar
             }
diff --git a/packages/SystemUI/src/com/android/systemui/toast/ToastFactory.java b/packages/SystemUI/src/com/android/systemui/toast/ToastFactory.java
index f3b9cc1..9ae6674 100644
--- a/packages/SystemUI/src/com/android/systemui/toast/ToastFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/toast/ToastFactory.java
@@ -40,14 +40,11 @@
 public class ToastFactory implements Dumpable {
     // only one ToastPlugin can be connected at a time.
     private ToastPlugin mPlugin;
-    private final LayoutInflater mLayoutInflater;
 
     @Inject
     public ToastFactory(
-            LayoutInflater layoutInflater,
             PluginManager pluginManager,
             DumpManager dumpManager) {
-        mLayoutInflater = layoutInflater;
         dumpManager.registerDumpable("ToastFactory", this);
         pluginManager.addPluginListener(
                 new PluginListener<ToastPlugin>() {
@@ -70,11 +67,12 @@
      */
     public SystemUIToast createToast(Context context, CharSequence text, String packageName,
             int userId, int orientation) {
+        LayoutInflater layoutInflater = LayoutInflater.from(context);
         if (isPluginAvailable()) {
-            return new SystemUIToast(mLayoutInflater, context, text, mPlugin.createToast(text,
+            return new SystemUIToast(layoutInflater, context, text, mPlugin.createToast(text,
                     packageName, userId), packageName, userId, orientation);
         }
-        return new SystemUIToast(mLayoutInflater, context, text, packageName, userId,
+        return new SystemUIToast(layoutInflater, context, text, packageName, userId,
                 orientation);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java b/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java
index 85a455d..bbfa32b 100644
--- a/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java
+++ b/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java
@@ -128,7 +128,7 @@
                 return;
             }
             Context displayContext = context.createDisplayContext(display);
-            mToast = mToastFactory.createToast(mContext /* sysuiContext */, text, packageName,
+            mToast = mToastFactory.createToast(displayContext /* sysuiContext */, text, packageName,
                     userHandle.getIdentifier(), mOrientation);
 
             if (mToast.getInAnimation() != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/TouchpadModule.kt b/packages/SystemUI/src/com/android/systemui/touchpad/TouchpadModule.kt
new file mode 100644
index 0000000..2f74d29
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/TouchpadModule.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.touchpad
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.touchpad.data.repository.TouchpadRepository
+import com.android.systemui.touchpad.data.repository.TouchpadRepositoryImpl
+import dagger.Binds
+import dagger.Module
+
+@Module
+abstract class TouchpadModule {
+
+    @Binds
+    @SysUISingleton
+    abstract fun bindTouchpadRepository(repository: TouchpadRepositoryImpl): TouchpadRepository
+}
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/data/repository/TouchpadRepository.kt b/packages/SystemUI/src/com/android/systemui/touchpad/data/repository/TouchpadRepository.kt
new file mode 100644
index 0000000..7131546
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/data/repository/TouchpadRepository.kt
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.touchpad.data.repository
+
+import android.hardware.input.InputManager
+import android.view.InputDevice.SOURCE_TOUCHPAD
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.inputdevice.data.repository.InputDeviceRepository
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
+
+interface TouchpadRepository {
+    /** Emits true if any touchpad is connected to the device, false otherwise. */
+    val isAnyTouchpadConnected: Flow<Boolean>
+}
+
+@SysUISingleton
+class TouchpadRepositoryImpl
+@Inject
+constructor(
+    @Background private val backgroundDispatcher: CoroutineDispatcher,
+    private val inputManager: InputManager,
+    inputDeviceRepository: InputDeviceRepository
+) : TouchpadRepository {
+
+    override val isAnyTouchpadConnected: Flow<Boolean> =
+        inputDeviceRepository.deviceChange
+            .map { (ids, _) -> ids.any { id -> isTouchpad(id) } }
+            .distinctUntilChanged()
+            .flowOn(backgroundDispatcher)
+
+    private fun isTouchpad(deviceId: Int): Boolean {
+        val device = inputManager.getInputDevice(deviceId) ?: return false
+        return device.supportsSource(SOURCE_TOUCHPAD)
+    }
+
+    companion object {
+        const val TAG = "TouchpadRepositoryImpl"
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/TouchpadKeyboardTutorialModule.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/TouchpadKeyboardTutorialModule.kt
new file mode 100644
index 0000000..8ba8db4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/TouchpadKeyboardTutorialModule.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.systemui.touchpad.tutorial
+
+import android.app.Activity
+import com.android.systemui.touchpad.tutorial.ui.view.TouchpadTutorialActivity
+import dagger.Binds
+import dagger.Module
+import dagger.multibindings.ClassKey
+import dagger.multibindings.IntoMap
+
+@Module
+interface TouchpadKeyboardTutorialModule {
+
+    @Binds
+    @IntoMap
+    @ClassKey(TouchpadTutorialActivity::class)
+    fun activity(impl: TouchpadTutorialActivity): Activity
+}
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/BackGestureTutorialScreen.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/BackGestureTutorialScreen.kt
index 396c6b8..3dca6fc 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/BackGestureTutorialScreen.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/BackGestureTutorialScreen.kt
@@ -16,12 +16,25 @@
 
 package com.android.systemui.touchpad.tutorial.ui.composable
 
+import android.graphics.ColorFilter
+import android.graphics.PorterDuff
+import android.graphics.PorterDuffColorFilter
 import androidx.activity.compose.BackHandler
+import androidx.annotation.RawRes
 import androidx.annotation.StringRes
-import androidx.compose.foundation.Image
-import androidx.compose.foundation.background
+import androidx.compose.animation.AnimatedContent
+import androidx.compose.animation.EnterTransition
+import androidx.compose.animation.ExitTransition
+import androidx.compose.animation.animateColorAsState
+import androidx.compose.animation.core.LinearEasing
+import androidx.compose.animation.core.snap
+import androidx.compose.animation.core.tween
+import androidx.compose.animation.fadeIn
+import androidx.compose.animation.fadeOut
+import androidx.compose.animation.togetherWith
 import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.BoxScope
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.Spacer
@@ -37,65 +50,158 @@
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
-import androidx.compose.ui.ExperimentalComposeUiApi
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.drawBehind
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.toArgb
 import androidx.compose.ui.input.pointer.pointerInteropFilter
 import androidx.compose.ui.platform.LocalContext
-import androidx.compose.ui.res.painterResource
 import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.unit.dp
+import com.airbnb.lottie.LottieProperty
+import com.airbnb.lottie.compose.LottieAnimation
+import com.airbnb.lottie.compose.LottieCompositionSpec
+import com.airbnb.lottie.compose.LottieConstants
+import com.airbnb.lottie.compose.LottieDynamicProperties
+import com.airbnb.lottie.compose.LottieDynamicProperty
+import com.airbnb.lottie.compose.animateLottieCompositionAsState
+import com.airbnb.lottie.compose.rememberLottieComposition
+import com.airbnb.lottie.compose.rememberLottieDynamicProperties
+import com.airbnb.lottie.compose.rememberLottieDynamicProperty
+import com.android.compose.theme.LocalAndroidColorScheme
 import com.android.systemui.res.R
-import com.android.systemui.touchpad.tutorial.ui.gesture.TouchpadGesture.BACK
+import com.android.systemui.touchpad.tutorial.ui.gesture.BackGestureMonitor
+import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState
+import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.FINISHED
+import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.IN_PROGRESS
+import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.NOT_STARTED
 import com.android.systemui.touchpad.tutorial.ui.gesture.TouchpadGestureHandler
 
-@OptIn(ExperimentalComposeUiApi::class)
+data class TutorialScreenColors(
+    val backgroundColor: Color,
+    val successBackgroundColor: Color,
+    val titleColor: Color,
+    val animationProperties: LottieDynamicProperties
+)
+
 @Composable
 fun BackGestureTutorialScreen(
     onDoneButtonClicked: () -> Unit,
     onBack: () -> Unit,
 ) {
+    val screenColors = rememberScreenColors()
     BackHandler(onBack = onBack)
-    var gestureDone by remember { mutableStateOf(false) }
+    var gestureState by remember { mutableStateOf(NOT_STARTED) }
     val swipeDistanceThresholdPx =
-        with(LocalContext.current) {
-            resources.getDimensionPixelSize(
-                com.android.internal.R.dimen.system_gestures_distance_threshold
-            )
-        }
+        LocalContext.current.resources.getDimensionPixelSize(
+            com.android.internal.R.dimen.system_gestures_distance_threshold
+        )
     val gestureHandler =
         remember(swipeDistanceThresholdPx) {
-            TouchpadGestureHandler(BACK, swipeDistanceThresholdPx, onDone = { gestureDone = true })
+            TouchpadGestureHandler(
+                BackGestureMonitor(
+                    swipeDistanceThresholdPx,
+                    gestureStateChangedCallback = { gestureState = it }
+                ),
+            )
         }
-    Box(
-        modifier =
-            Modifier.fillMaxSize()
-                // we need to use pointerInteropFilter because some info about touchpad gestures is
-                // only available in MotionEvent
-                .pointerInteropFilter(onTouchEvent = gestureHandler::onMotionEvent)
-    ) {
-        GestureTutorialContent(gestureDone, onDoneButtonClicked)
+    TouchpadGesturesHandlingBox(gestureHandler, gestureState) {
+        GestureTutorialContent(gestureState, onDoneButtonClicked, screenColors)
     }
 }
 
 @Composable
-private fun GestureTutorialContent(gestureDone: Boolean, onDoneButtonClicked: () -> Unit) {
+private fun TouchpadGesturesHandlingBox(
+    gestureHandler: TouchpadGestureHandler,
+    gestureState: GestureState,
+    modifier: Modifier = Modifier,
+    content: @Composable BoxScope.() -> Unit
+) {
+    Box(
+        modifier =
+            modifier
+                .fillMaxSize()
+                // we need to use pointerInteropFilter because some info about touchpad gestures is
+                // only available in MotionEvent
+                .pointerInteropFilter(
+                    onTouchEvent = { event ->
+                        // FINISHED is the final state so we don't need to process touches anymore
+                        if (gestureState != FINISHED) {
+                            gestureHandler.onMotionEvent(event)
+                        } else {
+                            false
+                        }
+                    }
+                )
+    ) {
+        content()
+    }
+}
+
+@Composable
+private fun rememberScreenColors(): TutorialScreenColors {
+    val onTertiary = LocalAndroidColorScheme.current.onTertiary
+    val onTertiaryFixed = LocalAndroidColorScheme.current.onTertiaryFixed
+    val onTertiaryFixedVariant = LocalAndroidColorScheme.current.onTertiaryFixedVariant
+    val tertiaryFixedDim = LocalAndroidColorScheme.current.tertiaryFixedDim
+    val surfaceContainer = MaterialTheme.colorScheme.surfaceContainer
+    val dynamicProperties =
+        rememberLottieDynamicProperties(
+            rememberColorFilterProperty(".tertiaryFixedDim", tertiaryFixedDim),
+            rememberColorFilterProperty(".onTertiaryFixed", onTertiaryFixed),
+            rememberColorFilterProperty(".onTertiary", onTertiary),
+            rememberColorFilterProperty(".onTertiaryFixedVariant", onTertiaryFixedVariant)
+        )
+    val screenColors =
+        remember(onTertiaryFixed, surfaceContainer, tertiaryFixedDim, dynamicProperties) {
+            TutorialScreenColors(
+                backgroundColor = onTertiaryFixed,
+                successBackgroundColor = surfaceContainer,
+                titleColor = tertiaryFixedDim,
+                animationProperties = dynamicProperties,
+            )
+        }
+    return screenColors
+}
+
+@Composable
+private fun GestureTutorialContent(
+    gestureState: GestureState,
+    onDoneButtonClicked: () -> Unit,
+    screenColors: TutorialScreenColors
+) {
+    val animatedColor by
+        animateColorAsState(
+            targetValue =
+                if (gestureState == FINISHED) screenColors.successBackgroundColor
+                else screenColors.backgroundColor,
+            animationSpec = tween(durationMillis = 150, easing = LinearEasing),
+            label = "backgroundColor"
+        )
     Column(
         verticalArrangement = Arrangement.Center,
         modifier =
             Modifier.fillMaxSize()
-                .background(color = MaterialTheme.colorScheme.surfaceContainer)
+                .drawBehind { drawRect(animatedColor) }
                 .padding(start = 48.dp, top = 124.dp, end = 48.dp, bottom = 48.dp)
     ) {
         Row(modifier = Modifier.fillMaxWidth().weight(1f)) {
             TutorialDescription(
                 titleTextId =
-                    if (gestureDone) R.string.touchpad_tutorial_gesture_done
+                    if (gestureState == FINISHED) R.string.touchpad_tutorial_gesture_done
                     else R.string.touchpad_back_gesture_action_title,
-                bodyTextId = R.string.touchpad_back_gesture_guidance,
+                titleColor = screenColors.titleColor,
+                bodyTextId =
+                    if (gestureState == FINISHED) R.string.touchpad_back_gesture_finished
+                    else R.string.touchpad_back_gesture_guidance,
                 modifier = Modifier.weight(1f)
             )
             Spacer(modifier = Modifier.width(76.dp))
-            TutorialAnimation(modifier = Modifier.weight(1f).padding(top = 24.dp))
+            TutorialAnimation(
+                gestureState,
+                screenColors.animationProperties,
+                modifier = Modifier.weight(1f).padding(top = 8.dp)
+            )
         }
         DoneButton(onDoneButtonClicked = onDoneButtonClicked)
     }
@@ -104,34 +210,111 @@
 @Composable
 fun TutorialDescription(
     @StringRes titleTextId: Int,
+    titleColor: Color,
     @StringRes bodyTextId: Int,
     modifier: Modifier = Modifier
 ) {
     Column(verticalArrangement = Arrangement.Top, modifier = modifier) {
-        Text(text = stringResource(id = titleTextId), style = MaterialTheme.typography.displayLarge)
+        Text(
+            text = stringResource(id = titleTextId),
+            style = MaterialTheme.typography.displayLarge,
+            color = titleColor
+        )
         Spacer(modifier = Modifier.height(16.dp))
-        Text(text = stringResource(id = bodyTextId), style = MaterialTheme.typography.bodyLarge)
+        Text(
+            text = stringResource(id = bodyTextId),
+            style = MaterialTheme.typography.bodyLarge,
+            color = Color.White
+        )
     }
 }
 
 @Composable
-fun TutorialAnimation(modifier: Modifier = Modifier) {
-    // below are just placeholder images, will be substituted by animations soon
-    Column(modifier = modifier.fillMaxWidth()) {
-        Image(
-            painter = painterResource(id = R.drawable.placeholder_touchpad_tablet_back_gesture),
-            contentDescription =
-                stringResource(
-                    id = R.string.touchpad_back_gesture_screen_animation_content_description
-                ),
-            modifier = Modifier.fillMaxWidth()
-        )
-        Spacer(modifier = Modifier.height(24.dp))
-        Image(
-            painter = painterResource(id = R.drawable.placeholder_touchpad_back_gesture),
-            contentDescription =
-                stringResource(id = R.string.touchpad_back_gesture_animation_content_description),
-            modifier = Modifier.fillMaxWidth()
-        )
+fun TutorialAnimation(
+    gestureState: GestureState,
+    animationProperties: LottieDynamicProperties,
+    modifier: Modifier = Modifier
+) {
+    Box(modifier = modifier.fillMaxWidth()) {
+        AnimatedContent(
+            targetState = gestureState,
+            transitionSpec = {
+                if (initialState == NOT_STARTED && targetState == IN_PROGRESS) {
+                    val transitionDurationMillis = 150
+                    fadeIn(
+                        animationSpec = tween(transitionDurationMillis, easing = LinearEasing)
+                    ) togetherWith
+                        fadeOut(animationSpec = snap(delayMillis = transitionDurationMillis))
+                } else {
+                    // empty transition works because all remaining transitions are from IN_PROGRESS
+                    // state which shares initial animation frame with both FINISHED and NOT_STARTED
+                    EnterTransition.None togetherWith ExitTransition.None
+                }
+            }
+        ) { gestureState ->
+            @RawRes val successAnimationId = R.raw.trackpad_back_success
+            @RawRes val educationAnimationId = R.raw.trackpad_back_edu
+            when (gestureState) {
+                NOT_STARTED -> EducationAnimation(educationAnimationId, animationProperties)
+                IN_PROGRESS -> FrozenSuccessAnimation(successAnimationId, animationProperties)
+                FINISHED -> SuccessAnimation(successAnimationId, animationProperties)
+            }
+        }
     }
 }
+
+@Composable
+private fun FrozenSuccessAnimation(
+    @RawRes successAnimationId: Int,
+    animationProperties: LottieDynamicProperties
+) {
+    val composition by rememberLottieComposition(LottieCompositionSpec.RawRes(successAnimationId))
+    LottieAnimation(
+        composition = composition,
+        progress = { 0f }, // animation should freeze on 1st frame
+        dynamicProperties = animationProperties,
+    )
+}
+
+@Composable
+private fun EducationAnimation(
+    @RawRes educationAnimationId: Int,
+    animationProperties: LottieDynamicProperties
+) {
+    val composition by rememberLottieComposition(LottieCompositionSpec.RawRes(educationAnimationId))
+    val progress by
+        animateLottieCompositionAsState(composition, iterations = LottieConstants.IterateForever)
+    LottieAnimation(
+        composition = composition,
+        progress = { progress },
+        dynamicProperties = animationProperties,
+    )
+}
+
+@Composable
+private fun SuccessAnimation(
+    @RawRes successAnimationId: Int,
+    animationProperties: LottieDynamicProperties
+) {
+    val composition by rememberLottieComposition(LottieCompositionSpec.RawRes(successAnimationId))
+    val progress by animateLottieCompositionAsState(composition, iterations = 1)
+    LottieAnimation(
+        composition = composition,
+        progress = { progress },
+        dynamicProperties = animationProperties,
+    )
+}
+
+@Composable
+fun rememberColorFilterProperty(
+    layerName: String,
+    color: Color
+): LottieDynamicProperty<ColorFilter> {
+    return rememberLottieDynamicProperty(
+        LottieProperty.COLOR_FILTER,
+        value = PorterDuffColorFilter(color.toArgb(), PorterDuff.Mode.SRC_ATOP),
+        // "**" below means match zero or more layers, so ** layerName ** means find layer with that
+        // name at any depth
+        keyPath = arrayOf("**", layerName, "**")
+    )
+}
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/BackGestureMonitor.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/BackGestureMonitor.kt
index 1fa7a0c..e3666ce 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/BackGestureMonitor.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/BackGestureMonitor.kt
@@ -16,49 +16,26 @@
 
 package com.android.systemui.touchpad.tutorial.ui.gesture
 
-import android.view.MotionEvent
 import kotlin.math.abs
 
-/**
- * Monitor for touchpad gestures that calls [gestureDoneCallback] when gesture was successfully
- * done. All tracked motion events should be passed to [processTouchpadEvent]
- */
-interface TouchpadGestureMonitor {
-
-    val gestureDistanceThresholdPx: Int
-    val gestureDoneCallback: () -> Unit
-
-    fun processTouchpadEvent(event: MotionEvent)
-}
-
+/** Monitors for touchpad back gesture, that is three fingers swiping left or right */
 class BackGestureMonitor(
     override val gestureDistanceThresholdPx: Int,
-    override val gestureDoneCallback: () -> Unit
-) : TouchpadGestureMonitor {
-
-    private var xStart = 0f
-
-    override fun processTouchpadEvent(event: MotionEvent) {
-        val action = event.actionMasked
-        when (action) {
-            MotionEvent.ACTION_DOWN -> {
-                if (isThreeFingerTouchpadSwipe(event)) {
-                    xStart = event.x
+    override val gestureStateChangedCallback: (GestureState) -> Unit
+) :
+    TouchpadGestureMonitor by ThreeFingerGestureMonitor(
+        gestureDistanceThresholdPx = gestureDistanceThresholdPx,
+        gestureStateChangedCallback = gestureStateChangedCallback,
+        donePredicate =
+            object : GestureDonePredicate {
+                override fun wasGestureDone(
+                    startX: Float,
+                    startY: Float,
+                    endX: Float,
+                    endY: Float
+                ): Boolean {
+                    val distance = abs(endX - startX)
+                    return distance >= gestureDistanceThresholdPx
                 }
             }
-            MotionEvent.ACTION_UP -> {
-                if (isThreeFingerTouchpadSwipe(event)) {
-                    val distance = abs(event.x - xStart)
-                    if (distance >= gestureDistanceThresholdPx) {
-                        gestureDoneCallback()
-                    }
-                }
-            }
-        }
-    }
-
-    private fun isThreeFingerTouchpadSwipe(event: MotionEvent): Boolean {
-        return event.classification == MotionEvent.CLASSIFICATION_MULTI_FINGER_SWIPE &&
-            event.getAxisValue(MotionEvent.AXIS_GESTURE_SWIPE_FINGER_COUNT) == 3f
-    }
-}
+    )
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/education/GestureType.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/GestureState.kt
similarity index 74%
copy from packages/SystemUI/shared/src/com/android/systemui/shared/education/GestureType.kt
copy to packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/GestureState.kt
index 9a5c77a..446875a 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/education/GestureType.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/GestureState.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2024 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,8 +14,10 @@
  * limitations under the License.
  */
 
-package com.android.systemui.shared.education
+package com.android.systemui.touchpad.tutorial.ui.gesture
 
-enum class GestureType {
-    BACK_GESTURE,
+enum class GestureState {
+    NOT_STARTED,
+    IN_PROGRESS,
+    FINISHED
 }
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureMonitor.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureMonitor.kt
new file mode 100644
index 0000000..a410f99
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureMonitor.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.touchpad.tutorial.ui.gesture
+
+/** Monitors for touchpad home gesture, that is three fingers swiping up */
+class HomeGestureMonitor(
+    override val gestureDistanceThresholdPx: Int,
+    override val gestureStateChangedCallback: (GestureState) -> Unit
+) :
+    TouchpadGestureMonitor by ThreeFingerGestureMonitor(
+        gestureDistanceThresholdPx = gestureDistanceThresholdPx,
+        gestureStateChangedCallback = gestureStateChangedCallback,
+        donePredicate =
+            object : GestureDonePredicate {
+                override fun wasGestureDone(
+                    startX: Float,
+                    startY: Float,
+                    endX: Float,
+                    endY: Float
+                ): Boolean {
+                    val distance = startY - endY
+                    return distance >= gestureDistanceThresholdPx
+                }
+            }
+    )
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/ThreeFingerGestureMonitor.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/ThreeFingerGestureMonitor.kt
new file mode 100644
index 0000000..377977c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/ThreeFingerGestureMonitor.kt
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.touchpad.tutorial.ui.gesture
+
+import android.view.MotionEvent
+
+interface GestureDonePredicate {
+    /**
+     * Should return if gesture was finished. The only events this predicate receives are ACTION_UP.
+     */
+    fun wasGestureDone(startX: Float, startY: Float, endX: Float, endY: Float): Boolean
+}
+
+/** Common implementation for all three-finger gesture monitors */
+class ThreeFingerGestureMonitor(
+    override val gestureDistanceThresholdPx: Int,
+    override val gestureStateChangedCallback: (GestureState) -> Unit,
+    private val donePredicate: GestureDonePredicate
+) : TouchpadGestureMonitor {
+
+    private var xStart = 0f
+    private var yStart = 0f
+
+    override fun processTouchpadEvent(event: MotionEvent) {
+        val action = event.actionMasked
+        when (action) {
+            MotionEvent.ACTION_DOWN -> {
+                if (isThreeFingerTouchpadSwipe(event)) {
+                    xStart = event.x
+                    yStart = event.y
+                    gestureStateChangedCallback(GestureState.IN_PROGRESS)
+                }
+            }
+            MotionEvent.ACTION_UP -> {
+                if (isThreeFingerTouchpadSwipe(event)) {
+                    if (donePredicate.wasGestureDone(xStart, yStart, event.x, event.y)) {
+                        gestureStateChangedCallback(GestureState.FINISHED)
+                    } else {
+                        gestureStateChangedCallback(GestureState.NOT_STARTED)
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGesture.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGesture.kt
deleted file mode 100644
index 4ae9c7b..0000000
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGesture.kt
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.touchpad.tutorial.ui.gesture
-
-enum class TouchpadGesture {
-    BACK,
-    HOME;
-
-    fun toMonitor(
-        swipeDistanceThresholdPx: Int,
-        gestureDoneCallback: () -> Unit
-    ): TouchpadGestureMonitor {
-        return when (this) {
-            BACK -> BackGestureMonitor(swipeDistanceThresholdPx, gestureDoneCallback)
-            else -> throw IllegalArgumentException("Not implemented yet")
-        }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureHandler.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureHandler.kt
index dc8471c..bf85b0a 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureHandler.kt
@@ -24,14 +24,9 @@
  * motion events passed to [onMotionEvent] and will filter touchpad events accordingly
  */
 class TouchpadGestureHandler(
-    touchpadGesture: TouchpadGesture,
-    swipeDistanceThresholdPx: Int,
-    onDone: () -> Unit
+    private val gestureMonitor: TouchpadGestureMonitor,
 ) {
 
-    private val gestureRecognition =
-        touchpadGesture.toMonitor(swipeDistanceThresholdPx, gestureDoneCallback = onDone)
-
     fun onMotionEvent(event: MotionEvent): Boolean {
         // events from touchpad have SOURCE_MOUSE and not SOURCE_TOUCHPAD because of legacy reasons
         val isFromTouchpad =
@@ -41,7 +36,7 @@
             event.actionMasked == MotionEvent.ACTION_DOWN &&
                 event.isButtonPressed(MotionEvent.BUTTON_PRIMARY)
         return if (isFromTouchpad && !buttonClick) {
-            gestureRecognition.processTouchpadEvent(event)
+            gestureMonitor.processTouchpadEvent(event)
             true
         } else {
             false
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureMonitor.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureMonitor.kt
new file mode 100644
index 0000000..1d2097d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureMonitor.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.touchpad.tutorial.ui.gesture
+
+import android.view.MotionEvent
+
+/**
+ * Monitor for touchpad gestures that calls [gestureStateChangedCallback] when [GestureState]
+ * changes. All tracked motion events should be passed to [processTouchpadEvent]
+ */
+interface TouchpadGestureMonitor {
+
+    val gestureDistanceThresholdPx: Int
+    val gestureStateChangedCallback: (GestureState) -> Unit
+
+    fun processTouchpadEvent(event: MotionEvent)
+}
+
+fun isThreeFingerTouchpadSwipe(event: MotionEvent) = isNFingerTouchpadSwipe(event, fingerCount = 3)
+
+fun isFourFingerTouchpadSwipe(event: MotionEvent) = isNFingerTouchpadSwipe(event, fingerCount = 4)
+
+private fun isNFingerTouchpadSwipe(event: MotionEvent, fingerCount: Int): Boolean {
+    return event.classification == MotionEvent.CLASSIFICATION_MULTI_FINGER_SWIPE &&
+        event.getAxisValue(MotionEvent.AXIS_GESTURE_SWIPE_FINGER_COUNT) == fingerCount.toFloat()
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensorImpl.java b/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensorImpl.java
index 8ab5bc6..169f865 100644
--- a/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensorImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensorImpl.java
@@ -26,8 +26,8 @@
 import com.android.systemui.util.concurrency.DelayableExecutor;
 import com.android.systemui.util.concurrency.Execution;
 
-import java.util.ArrayList;
-import java.util.List;
+import java.util.Set;
+import java.util.concurrent.CopyOnWriteArraySet;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 import javax.inject.Inject;
@@ -64,7 +64,7 @@
     ThresholdSensor mSecondaryThresholdSensor;
     private final DelayableExecutor mDelayableExecutor;
     private final Execution mExecution;
-    private final List<ThresholdSensor.Listener> mListeners = new ArrayList<>();
+    private final Set<Listener> mListeners = new CopyOnWriteArraySet<>();
     private String mTag = null;
     @VisibleForTesting protected boolean mPaused;
     private ThresholdSensorEvent mLastPrimaryEvent;
@@ -246,7 +246,7 @@
     public void unregister(ThresholdSensor.Listener listener) {
         mExecution.assertIsMainThread();
         mListeners.remove(listener);
-        if (mListeners.size() == 0) {
+        if (mListeners.isEmpty()) {
             unregisterInternal();
         }
     }
@@ -296,8 +296,7 @@
         }
         if (mLastEvent != null) {
             ThresholdSensorEvent lastEvent = mLastEvent;  // Listeners can null out mLastEvent.
-            List<ThresholdSensor.Listener> listeners = new ArrayList<>(mListeners);
-            listeners.forEach(proximitySensorListener ->
+            mListeners.forEach(proximitySensorListener ->
                     proximitySensorListener.onThresholdCrossed(lastEvent));
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/SecureSettingsImpl.java b/packages/SystemUI/src/com/android/systemui/util/settings/SecureSettingsImpl.java
index 9c98f43..f1da27f 100644
--- a/packages/SystemUI/src/com/android/systemui/util/settings/SecureSettingsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/util/settings/SecureSettingsImpl.java
@@ -22,23 +22,24 @@
 
 import androidx.annotation.NonNull;
 
-import com.android.systemui.settings.UserTracker;
 import com.android.systemui.util.kotlin.SettingsSingleThreadBackground;
 
-import javax.inject.Inject;
-
 import kotlinx.coroutines.CoroutineDispatcher;
 
+import javax.inject.Inject;
+
 class SecureSettingsImpl implements SecureSettings {
     private final ContentResolver mContentResolver;
-    private final UserTracker mUserTracker;
+    private final CurrentUserIdProvider mCurrentUserProvider;
     private final CoroutineDispatcher mBgDispatcher;
 
     @Inject
-    SecureSettingsImpl(ContentResolver contentResolver, UserTracker userTracker,
+    SecureSettingsImpl(
+            ContentResolver contentResolver,
+            CurrentUserIdProvider currentUserProvider,
             @SettingsSingleThreadBackground CoroutineDispatcher bgDispatcher) {
         mContentResolver = contentResolver;
-        mUserTracker = userTracker;
+        mCurrentUserProvider = currentUserProvider;
         mBgDispatcher = bgDispatcher;
     }
 
@@ -48,8 +49,8 @@
     }
 
     @Override
-    public UserTracker getUserTracker() {
-        return mUserTracker;
+    public CurrentUserIdProvider getCurrentUserProvider() {
+        return mCurrentUserProvider;
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.kt b/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.kt
index b5934ec..0ee997e 100644
--- a/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.kt
@@ -15,6 +15,7 @@
  */
 package com.android.systemui.util.settings
 
+import android.annotation.UserIdInt
 import android.content.ContentResolver
 import android.database.ContentObserver
 import android.net.Uri
@@ -336,7 +337,7 @@
      * @param name to look up in the table
      * @return the corresponding value, or null if not present
      */
-    fun getString(name: String): String
+    fun getString(name: String): String?
 
     /**
      * Store a name/value pair into the database.
@@ -385,15 +386,15 @@
      * an integer.
      *
      * @param name The name of the setting to retrieve.
-     * @param def Value to return if the setting is not defined.
-     * @return The setting's current value, or 'def' if it is not defined or not a valid integer.
+     * @param default Value to return if the setting is not defined.
+     * @return The setting's current value, or default if it is not defined or not a valid integer.
      */
-    fun getInt(name: String, def: Int): Int {
+    fun getInt(name: String, default: Int): Int {
         val v = getString(name)
         return try {
-            v.toInt()
+            v?.toInt() ?: default
         } catch (e: NumberFormatException) {
-            def
+            default
         }
     }
 
@@ -412,7 +413,7 @@
      */
     @Throws(SettingNotFoundException::class)
     fun getInt(name: String): Int {
-        val v = getString(name)
+        val v = getString(name) ?: throw SettingNotFoundException(name)
         return try {
             v.toInt()
         } catch (e: NumberFormatException) {
@@ -441,11 +442,11 @@
      * boolean.
      *
      * @param name The name of the setting to retrieve.
-     * @param def Value to return if the setting is not defined.
-     * @return The setting's current value, or 'def' if it is not defined or not a valid boolean.
+     * @param default Value to return if the setting is not defined.
+     * @return The setting's current value, or default if it is not defined or not a valid boolean.
      */
-    fun getBool(name: String, def: Boolean): Boolean {
-        return getInt(name, if (def) 1 else 0) != 0
+    fun getBool(name: String, default: Boolean): Boolean {
+        return getInt(name, if (default) 1 else 0) != 0
     }
 
     /**
@@ -579,13 +580,12 @@
     companion object {
         /** Convert a string to a long, or uses a default if the string is malformed or null */
         @JvmStatic
-        fun parseLongOrUseDefault(valString: String, def: Long): Long {
-            val value: Long
-            value =
+        fun parseLongOrUseDefault(valString: String?, default: Long): Long {
+            val value: Long =
                 try {
-                    valString.toLong()
+                    valString?.toLong() ?: default
                 } catch (e: NumberFormatException) {
-                    def
+                    default
                 }
             return value
         }
@@ -630,4 +630,8 @@
             }
         }
     }
+
+    fun interface CurrentUserIdProvider {
+        @UserIdInt fun getUserId(): Int
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/SystemSettingsImpl.java b/packages/SystemUI/src/com/android/systemui/util/settings/SystemSettingsImpl.java
index 406d95b..1e80357 100644
--- a/packages/SystemUI/src/com/android/systemui/util/settings/SystemSettingsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/util/settings/SystemSettingsImpl.java
@@ -22,23 +22,23 @@
 
 import androidx.annotation.NonNull;
 
-import com.android.systemui.settings.UserTracker;
 import com.android.systemui.util.kotlin.SettingsSingleThreadBackground;
 
-import javax.inject.Inject;
-
 import kotlinx.coroutines.CoroutineDispatcher;
 
+import javax.inject.Inject;
+
 class SystemSettingsImpl implements SystemSettings {
     private final ContentResolver mContentResolver;
-    private final UserTracker mUserTracker;
+    private final CurrentUserIdProvider mCurrentUserProvider;
     private final CoroutineDispatcher mBgCoroutineDispatcher;
 
     @Inject
-    SystemSettingsImpl(ContentResolver contentResolver, UserTracker userTracker,
+    SystemSettingsImpl(ContentResolver contentResolver,
+            CurrentUserIdProvider currentUserProvider,
             @SettingsSingleThreadBackground CoroutineDispatcher bgDispatcher) {
         mContentResolver = contentResolver;
-        mUserTracker = userTracker;
+        mCurrentUserProvider = currentUserProvider;
         mBgCoroutineDispatcher = bgDispatcher;
     }
 
@@ -48,8 +48,8 @@
     }
 
     @Override
-    public UserTracker getUserTracker() {
-        return mUserTracker;
+    public CurrentUserIdProvider getCurrentUserProvider() {
+        return mCurrentUserProvider;
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/UserSettingsProxy.kt b/packages/SystemUI/src/com/android/systemui/util/settings/UserSettingsProxy.kt
index 848a6e6..9ae8f03 100644
--- a/packages/SystemUI/src/com/android/systemui/util/settings/UserSettingsProxy.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/settings/UserSettingsProxy.kt
@@ -23,7 +23,6 @@
 import android.os.UserHandle
 import android.provider.Settings.SettingNotFoundException
 import com.android.app.tracing.TraceUtils.trace
-import com.android.systemui.settings.UserTracker
 import com.android.systemui.util.settings.SettingsProxy.Companion.parseFloat
 import com.android.systemui.util.settings.SettingsProxy.Companion.parseFloatOrThrow
 import com.android.systemui.util.settings.SettingsProxy.Companion.parseLongOrThrow
@@ -46,8 +45,8 @@
  * instances, unifying setting related actions in one place.
  */
 interface UserSettingsProxy : SettingsProxy {
-    /** Returns that [UserTracker] this instance was constructed with. */
-    val userTracker: UserTracker
+    val currentUserProvider: SettingsProxy.CurrentUserIdProvider
+
     /** Returns the user id for the associated [ContentResolver]. */
     var userId: Int
         get() = getContentResolver().userId
@@ -64,7 +63,7 @@
     fun getRealUserHandle(userHandle: Int): Int {
         return if (userHandle != UserHandle.USER_CURRENT) {
             userHandle
-        } else userTracker.userId
+        } else currentUserProvider.getUserId()
     }
 
     @WorkerThread
@@ -354,12 +353,12 @@
      * @param name to look up in the table
      * @return the corresponding value, or null if not present
      */
-    override fun getString(name: String): String {
+    override fun getString(name: String): String? {
         return getStringForUser(name, userId)
     }
 
     /** See [getString]. */
-    fun getStringForUser(name: String, userHandle: Int): String
+    fun getStringForUser(name: String, userHandle: Int): String?
 
     /**
      * Store a name/value pair into the database. Values written by this method will be overridden
@@ -388,17 +387,17 @@
         overrideableByRestore: Boolean
     ): Boolean
 
-    override fun getInt(name: String, def: Int): Int {
-        return getIntForUser(name, def, userId)
+    override fun getInt(name: String, default: Int): Int {
+        return getIntForUser(name, default, userId)
     }
 
     /** Similar implementation to [getInt] for the specified [userHandle]. */
-    fun getIntForUser(name: String, def: Int, userHandle: Int): Int {
+    fun getIntForUser(name: String, default: Int, userHandle: Int): Int {
         val v = getStringForUser(name, userHandle)
         return try {
-            v.toInt()
+            v?.toInt() ?: default
         } catch (e: NumberFormatException) {
-            def
+            default
         }
     }
 
@@ -408,7 +407,7 @@
     /** Similar implementation to [getInt] for the specified [userHandle]. */
     @Throws(SettingNotFoundException::class)
     fun getIntForUser(name: String, userHandle: Int): Int {
-        val v = getStringForUser(name, userHandle)
+        val v = getStringForUser(name, userHandle) ?: throw SettingNotFoundException(name)
         return try {
             v.toInt()
         } catch (e: NumberFormatException) {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/CsdWarningAction.kt b/packages/SystemUI/src/com/android/systemui/volume/CsdWarningAction.kt
new file mode 100644
index 0000000..a77acb5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/CsdWarningAction.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume
+
+import android.app.PendingIntent
+import android.app.PendingIntent.FLAG_IMMUTABLE
+import android.app.PendingIntent.FLAG_UPDATE_CURRENT
+import android.content.Context
+import android.content.Intent
+
+/**
+ * label: Notification action label text. intent: The Intent used to start Activity or Broadcast.
+ * isActivity: Defines if the pending intent should start an activity. Default is to broadcast
+ */
+data class CsdWarningAction(
+    val label: String? = null,
+    val intent: Intent? = null,
+    val isActivity: Boolean = false,
+) {
+    fun toPendingIntent(context: Context): PendingIntent? {
+        if (label == null || intent == null) {
+            return null
+        }
+        if (isActivity) {
+            return PendingIntent.getActivity(
+                context,
+                0,
+                intent,
+                PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
+            )
+        }
+        return PendingIntent.getBroadcast(context, 0, intent, FLAG_IMMUTABLE)
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/CsdWarningDialog.java b/packages/SystemUI/src/com/android/systemui/volume/CsdWarningDialog.java
index bb230e6..a63660b 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/CsdWarningDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/CsdWarningDialog.java
@@ -30,7 +30,6 @@
 import android.media.AudioManager;
 import android.provider.Settings;
 import android.util.Log;
-import android.util.Pair;
 import android.view.KeyEvent;
 import android.view.WindowManager;
 
@@ -109,7 +108,7 @@
     private long mShowTime;
 
     @VisibleForTesting public int mCachedMediaStreamVolume;
-    private Optional<ImmutableList<Pair<String, Intent>>> mActionIntents;
+    private Optional<ImmutableList<CsdWarningAction>> mActionIntents;
     private final BroadcastDispatcher mBroadcastDispatcher;
 
     /**
@@ -121,7 +120,7 @@
         CsdWarningDialog create(
                 int csdWarning,
                 Runnable onCleanup,
-                Optional<ImmutableList<Pair<String, Intent>>> actionIntents);
+                Optional<ImmutableList<CsdWarningAction>> actionIntents);
     }
 
     @AssistedInject
@@ -132,7 +131,7 @@
             NotificationManager notificationManager,
             @Background DelayableExecutor delayableExecutor,
             @Assisted Runnable onCleanup,
-            @Assisted Optional<ImmutableList<Pair<String, Intent>>> actionIntents,
+            @Assisted Optional<ImmutableList<CsdWarningAction>> actionIntents,
             BroadcastDispatcher broadcastDispatcher) {
         super(context);
         mCsdWarning = csdWarning;
@@ -351,39 +350,45 @@
         if (Flags.sounddoseCustomization()
                 && mActionIntents.isPresent()
                 && !mActionIntents.get().isEmpty()) {
-            ImmutableList<Pair<String, Intent>> actionIntentsList = mActionIntents.get();
-            for (Pair<String, Intent> intentPair : actionIntentsList) {
-                if (intentPair != null && intentPair.first != null && intentPair.second != null) {
-                    PendingIntent pendingActionIntent =
-                            PendingIntent.getBroadcast(mContext, 0, intentPair.second,
-                                    FLAG_IMMUTABLE);
-                    builder.addAction(0, intentPair.first, pendingActionIntent);
-                    // Register receiver to undo volume only when
-                    // notification conaining the undo action would be sent.
-                    if (intentPair.first == mContext.getString(R.string.volume_undo_action)) {
-                        final IntentFilter filterUndo = new IntentFilter(
-                                VolumeDialog.ACTION_VOLUME_UNDO);
-                        mBroadcastDispatcher.registerReceiver(mReceiverUndo,
-                                filterUndo,
-                                /* executor = default */ null,
-                                /* user = default */ null,
-                                Context.RECEIVER_NOT_EXPORTED,
-                                /* permission = default */ null);
+            ImmutableList<CsdWarningAction> actionIntentsList = mActionIntents.get();
+            for (CsdWarningAction action : actionIntentsList) {
+                if (action.getLabel() == null || action.getIntent() == null) {
+                    Log.w(TAG, "Null action intent received. Skipping addition to notification");
+                    continue;
+                }
+                PendingIntent pendingActionIntent = action.toPendingIntent(mContext);
+                if (pendingActionIntent == null) {
+                    Log.w(TAG, "Null pending intent received. Skipping addition to notification");
+                    continue;
+                }
+                builder.addAction(0, action.getLabel(), pendingActionIntent);
 
-                        // Register receiver to learn if notification has been dismissed.
-                        // This is required to unregister receivers to prevent leak.
-                        Intent dismissIntent = new Intent(DISMISS_CSD_NOTIFICATION)
-                                .setPackage(mContext.getPackageName());
-                        PendingIntent pendingDismissIntent = PendingIntent.getBroadcast(mContext,
-                                0, dismissIntent, FLAG_IMMUTABLE);
-                        mBroadcastDispatcher.registerReceiver(mReceiverDismissNotification,
-                                new IntentFilter(DISMISS_CSD_NOTIFICATION),
-                                /* executor = default */ null,
-                                /* user = default */ null,
-                                Context.RECEIVER_NOT_EXPORTED,
-                                /* permission = default */ null);
-                        builder.setDeleteIntent(pendingDismissIntent);
-                    }
+                // Register receiver to undo volume only when
+                // notification conaining the undo action would be sent.
+                if (action.getLabel().equals(mContext.getString(R.string.volume_undo_action))) {
+                    final IntentFilter filterUndo = new IntentFilter(
+                            VolumeDialog.ACTION_VOLUME_UNDO);
+                    mBroadcastDispatcher.registerReceiver(mReceiverUndo,
+                            filterUndo,
+                            /* executor = default */ null,
+                            /* user = default */ null,
+                            Context.RECEIVER_NOT_EXPORTED,
+                            /* permission = default */ null);
+
+                    // Register receiver to learn if notification has been dismissed.
+                    // This is required to unregister receivers to prevent leak.
+                    Intent dismissIntent = new Intent(DISMISS_CSD_NOTIFICATION)
+                            .setPackage(mContext.getPackageName());
+                    PendingIntent pendingDismissIntent = PendingIntent.getBroadcast(
+                            mContext,
+                            0, dismissIntent, FLAG_IMMUTABLE);
+                    mBroadcastDispatcher.registerReceiver(mReceiverDismissNotification,
+                            new IntentFilter(DISMISS_CSD_NOTIFICATION),
+                            /* executor = default */ null,
+                            /* user = default */ null,
+                            Context.RECEIVER_NOT_EXPORTED,
+                            /* permission = default */ null);
+                    builder.setDeleteIntent(pendingDismissIntent);
                 }
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 0770d89..e56f6b3 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -51,7 +51,6 @@
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.DialogInterface;
-import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.res.ColorStateList;
 import android.content.res.Configuration;
@@ -79,7 +78,6 @@
 import android.provider.Settings.Global;
 import android.text.InputFilter;
 import android.util.Log;
-import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseBooleanArray;
 import android.view.ContextThemeWrapper;
@@ -322,8 +320,8 @@
     private final VolumePanelFlag mVolumePanelFlag;
     private final VolumeDialogInteractor mInteractor;
     // Optional actions for soundDose
-    private Optional<ImmutableList<Pair<String, Intent>>> mCsdWarningNotificationActions =
-            Optional.of(ImmutableList.of());
+    private Optional<ImmutableList<CsdWarningAction>>
+            mCsdWarningNotificationActions = Optional.of(ImmutableList.of());
 
     public VolumeDialogImpl(
             Context context,
@@ -2231,7 +2229,7 @@
     }
 
     public void setCsdWarningNotificationActionIntents(
-            ImmutableList<Pair<String, Intent>> actionIntent) {
+            ImmutableList<CsdWarningAction> actionIntent) {
         mCsdWarningNotificationActions = Optional.of(actionIntent);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
index dc2b80c..68d12f6 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.volume;
 
+import static com.android.settingslib.flags.Flags.volumeDialogAudioSharingFix;
+
 import android.content.Context;
 import android.content.res.Configuration;
 import android.os.Handler;
@@ -26,6 +28,7 @@
 import com.android.systemui.qs.tiles.DndTile;
 import com.android.systemui.res.R;
 import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.volume.domain.interactor.AudioSharingInteractor;
 
 import java.io.PrintWriter;
 
@@ -41,23 +44,29 @@
     private boolean mEnabled;
     private final Context mContext;
     private VolumeDialogComponent mVolumeComponent;
+    private AudioSharingInteractor mAudioSharingInteractor;
 
     @Inject
-    public VolumeUI(Context context, VolumeDialogComponent volumeDialogComponent) {
+    public VolumeUI(Context context, VolumeDialogComponent volumeDialogComponent,
+            AudioSharingInteractor audioSharingInteractor) {
         mContext = context;
         mVolumeComponent = volumeDialogComponent;
+        mAudioSharingInteractor = audioSharingInteractor;
     }
 
     @Override
     public void start() {
         boolean enableVolumeUi = mContext.getResources().getBoolean(R.bool.enable_volume_ui);
         boolean enableSafetyWarning =
-            mContext.getResources().getBoolean(R.bool.enable_safety_warning);
+                mContext.getResources().getBoolean(R.bool.enable_safety_warning);
         mEnabled = enableVolumeUi || enableSafetyWarning;
         if (!mEnabled) return;
 
         mVolumeComponent.setEnableDialogs(enableVolumeUi, enableSafetyWarning);
         setDefaultVolumeController();
+        if (volumeDialogAudioSharingFix()) {
+            mAudioSharingInteractor.handlePrimaryGroupChange();
+        }
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dagger/AudioModule.kt b/packages/SystemUI/src/com/android/systemui/volume/dagger/AudioModule.kt
index eb2f71a1..5d8b6f1 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dagger/AudioModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dagger/AudioModule.kt
@@ -20,10 +20,12 @@
 import android.content.Context
 import android.media.AudioManager
 import com.android.settingslib.bluetooth.LocalBluetoothManager
+import com.android.settingslib.flags.Flags
 import com.android.settingslib.notification.domain.interactor.NotificationsSoundPolicyInteractor
 import com.android.settingslib.volume.data.repository.AudioRepository
 import com.android.settingslib.volume.data.repository.AudioRepositoryImpl
 import com.android.settingslib.volume.data.repository.AudioSharingRepository
+import com.android.settingslib.volume.data.repository.AudioSharingRepositoryEmptyImpl
 import com.android.settingslib.volume.data.repository.AudioSharingRepositoryImpl
 import com.android.settingslib.volume.domain.interactor.AudioModeInteractor
 import com.android.settingslib.volume.domain.interactor.AudioVolumeInteractor
@@ -73,19 +75,21 @@
         @Provides
         @SysUISingleton
         fun provideAudioSharingRepository(
-            @Application context: Context,
             contentResolver: ContentResolver,
             localBluetoothManager: LocalBluetoothManager?,
             @Application coroutineScope: CoroutineScope,
             @Background coroutineContext: CoroutineContext,
         ): AudioSharingRepository =
-            AudioSharingRepositoryImpl(
-                context,
-                contentResolver,
-                localBluetoothManager,
-                coroutineScope,
-                coroutineContext
-            )
+            if (Flags.enableLeAudioSharing() && localBluetoothManager != null) {
+                AudioSharingRepositoryImpl(
+                    contentResolver,
+                    localBluetoothManager,
+                    coroutineScope,
+                    coroutineContext
+                )
+            } else {
+                AudioSharingRepositoryEmptyImpl()
+            }
 
         @Provides
         @SysUISingleton
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dagger/AudioSharingModule.kt b/packages/SystemUI/src/com/android/systemui/volume/dagger/AudioSharingModule.kt
index 9f1e60e..1c80887 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dagger/AudioSharingModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dagger/AudioSharingModule.kt
@@ -16,14 +16,14 @@
 
 package com.android.systemui.volume.dagger
 
-import com.android.settingslib.volume.data.repository.AudioSharingRepository
+import com.android.settingslib.flags.Flags
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.volume.domain.interactor.AudioSharingInteractor
+import com.android.systemui.volume.domain.interactor.AudioSharingInteractorEmptyImpl
 import com.android.systemui.volume.domain.interactor.AudioSharingInteractorImpl
+import dagger.Lazy
 import dagger.Module
 import dagger.Provides
-import kotlinx.coroutines.CoroutineScope
 
 /** Dagger module for audio sharing code in the volume package */
 @Module
@@ -33,8 +33,13 @@
         @Provides
         @SysUISingleton
         fun provideAudioSharingInteractor(
-            @Application coroutineScope: CoroutineScope,
-            repository: AudioSharingRepository
-        ): AudioSharingInteractor = AudioSharingInteractorImpl(coroutineScope, repository)
+            impl: Lazy<AudioSharingInteractorImpl>,
+            emptyImpl: Lazy<AudioSharingInteractorEmptyImpl>,
+        ): AudioSharingInteractor =
+            if (Flags.volumeDialogAudioSharingFix()) {
+                impl.get()
+            } else {
+                emptyImpl.get()
+            }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/domain/interactor/AudioSharingInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/domain/interactor/AudioSharingInteractor.kt
index aba3015..2170c36 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/domain/interactor/AudioSharingInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/domain/interactor/AudioSharingInteractor.kt
@@ -17,18 +17,28 @@
 package com.android.systemui.volume.domain.interactor
 
 import android.bluetooth.BluetoothCsipSetCoordinator
+import android.media.AudioManager.STREAM_MUSIC
 import androidx.annotation.IntRange
 import com.android.settingslib.volume.data.repository.AudioSharingRepository
 import com.android.settingslib.volume.data.repository.AudioSharingRepository.Companion.AUDIO_SHARING_VOLUME_MAX
 import com.android.settingslib.volume.data.repository.AudioSharingRepository.Companion.AUDIO_SHARING_VOLUME_MIN
+import com.android.settingslib.volume.domain.interactor.AudioVolumeInteractor
+import com.android.settingslib.volume.shared.model.AudioStream
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
 import javax.inject.Inject
+import kotlin.coroutines.CoroutineContext
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.emptyFlow
+import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
 
 interface AudioSharingInteractor {
     /** Audio sharing secondary headset volume changes. */
@@ -45,6 +55,16 @@
         @IntRange(from = AUDIO_SHARING_VOLUME_MIN.toLong(), to = AUDIO_SHARING_VOLUME_MAX.toLong())
         level: Int
     )
+
+    /**
+     * Handle primary group change in audio sharing.
+     *
+     * Once the primary group is changed, we need to sync its volume to STREAM_MUSIC to make sure
+     * the volume adjustment during audio sharing can be kept after the sharing ends.
+     *
+     * TODO(b/355396988) Migrate to audio framework solution once it is in place.
+     */
+    fun handlePrimaryGroupChange()
 }
 
 @SysUISingleton
@@ -52,26 +72,60 @@
 @Inject
 constructor(
     @Application private val coroutineScope: CoroutineScope,
+    @Background private val backgroundCoroutineContext: CoroutineContext,
+    private val audioVolumeInteractor: AudioVolumeInteractor,
     private val audioSharingRepository: AudioSharingRepository
 ) : AudioSharingInteractor {
 
     override val volume: Flow<Int?> =
         combine(audioSharingRepository.secondaryGroupId, audioSharingRepository.volumeMap) {
-            secondaryGroupId,
-            volumeMap ->
-            if (secondaryGroupId == BluetoothCsipSetCoordinator.GROUP_ID_INVALID) null
-            else volumeMap.getOrDefault(secondaryGroupId, DEFAULT_VOLUME)
-        }
+                secondaryGroupId,
+                volumeMap ->
+                if (secondaryGroupId == BluetoothCsipSetCoordinator.GROUP_ID_INVALID) null
+                else volumeMap.getOrDefault(secondaryGroupId, DEFAULT_VOLUME)
+            }
+            .distinctUntilChanged()
 
     override val volumeMin: Int = AUDIO_SHARING_VOLUME_MIN
 
     override val volumeMax: Int = AUDIO_SHARING_VOLUME_MAX
 
-    override fun setStreamVolume(level: Int) {
+    override fun setStreamVolume(
+        @IntRange(from = AUDIO_SHARING_VOLUME_MIN.toLong(), to = AUDIO_SHARING_VOLUME_MAX.toLong())
+        level: Int
+    ) {
         coroutineScope.launch { audioSharingRepository.setSecondaryVolume(level) }
     }
 
+    override fun handlePrimaryGroupChange() {
+        coroutineScope.launch {
+            audioSharingRepository.primaryGroupId
+                .map { primaryGroupId -> audioSharingRepository.volumeMap.value[primaryGroupId] }
+                .filterNotNull()
+                .distinctUntilChanged()
+                .collect {
+                    // Once primary device change, we need to update the STREAM_MUSIC volume to get
+                    // align with the primary device's volume
+                    setMusicStreamVolume(it)
+                }
+        }
+    }
+
+    private suspend fun setMusicStreamVolume(volume: Int) {
+        withContext(backgroundCoroutineContext) {
+            val musicStream =
+                audioVolumeInteractor.getAudioStream(AudioStream(STREAM_MUSIC)).first()
+            val musicVolume =
+                Math.round(
+                    volume.toFloat() * (musicStream.maxVolume - musicStream.minVolume) /
+                        (AUDIO_SHARING_VOLUME_MAX - AUDIO_SHARING_VOLUME_MIN)
+                )
+            audioVolumeInteractor.setVolume(AudioStream(STREAM_MUSIC), musicVolume)
+        }
+    }
+
     private companion object {
+        const val TAG = "AudioSharingInteractor"
         const val DEFAULT_VOLUME = 20
     }
 }
@@ -82,7 +136,12 @@
     override val volumeMin: Int = EMPTY_VOLUME
     override val volumeMax: Int = EMPTY_VOLUME
 
-    override fun setStreamVolume(level: Int) {}
+    override fun setStreamVolume(
+        @IntRange(from = AUDIO_SHARING_VOLUME_MIN.toLong(), to = AUDIO_SHARING_VOLUME_MAX.toLong())
+        level: Int
+    ) {}
+
+    override fun handlePrimaryGroupChange() {}
 
     private companion object {
         const val EMPTY_VOLUME = 0
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
index 14cd202..9ca0591 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
@@ -34,7 +34,6 @@
 import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.inputmethodservice.InputMethodService;
-import android.os.IBinder;
 import android.util.Log;
 import android.view.Display;
 import android.view.KeyEvent;
@@ -379,8 +378,8 @@
             }
 
             @Override
-            public void setImeWindowStatus(int displayId, IBinder token, int vis,
-                    int backDisposition, boolean showImeSwitcher) {
+            public void setImeWindowStatus(int displayId, int vis, int backDisposition,
+                    boolean showImeSwitcher) {
                 if (displayId == mDisplayTracker.getDefaultDisplayId()
                         && (vis & InputMethodService.IME_VISIBLE) != 0) {
                     oneHanded.stopOneHanded(
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerBaseTest.java
index 07504c7..2b4fc5b 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerBaseTest.java
@@ -26,7 +26,6 @@
 
 import com.android.keyguard.logging.KeyguardLogger;
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.dump.DumpManager;
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository;
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory;
 import com.android.systemui.kosmos.KosmosJavaAdapter;
@@ -59,7 +58,6 @@
     @Mock protected KeyguardLogger mKeyguardLogger;
     @Mock protected KeyguardStatusViewController mControllerMock;
     @Mock protected ViewTreeObserver mViewTreeObserver;
-    @Mock protected DumpManager mDumpManager;
     protected FakeKeyguardRepository mFakeKeyguardRepository;
     protected FakePowerRepository mFakePowerRepository;
 
@@ -90,7 +88,6 @@
                 mKeyguardLogger,
                 mKosmos.getInteractionJankMonitor(),
                 deps.getKeyguardInteractor(),
-                mDumpManager,
                 PowerInteractorFactory.create(
                         mFakePowerRepository
                 ).getPowerInteractor()) {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
index 0696a4b..8e441a3 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
@@ -140,14 +140,6 @@
     }
 
     @Test
-    public void correctlyDump() {
-        mController.onInit();
-        verify(mDumpManager).registerDumpable(eq(mController.getInstanceName()), eq(mController));
-        mController.onDestroy();
-        verify(mDumpManager, times(1)).unregisterDumpable(eq(mController.getInstanceName()));
-    }
-
-    @Test
     public void onInit_addsOnLayoutChangeListenerToClockSwitch() {
         when(mKeyguardStatusView.findViewById(R.id.status_view_media_container)).thenReturn(
                 mMediaHostContainer);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/FullscreenMagnificationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/FullscreenMagnificationControllerTest.java
index cbd535b..530ae15 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/FullscreenMagnificationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/FullscreenMagnificationControllerTest.java
@@ -25,6 +25,7 @@
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -36,7 +37,10 @@
 import android.content.Context;
 import android.content.pm.ActivityInfo;
 import android.graphics.Rect;
+import android.graphics.drawable.GradientDrawable;
+import android.hardware.display.DisplayManager;
 import android.os.RemoteException;
+import android.platform.test.annotations.EnableFlags;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.view.Display;
@@ -54,6 +58,7 @@
 import androidx.annotation.NonNull;
 import androidx.test.filters.SmallTest;
 
+import com.android.systemui.Flags;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.res.R;
 
@@ -61,6 +66,7 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
@@ -76,6 +82,12 @@
     private static final long WAIT_TIMEOUT_S = 5L * HW_TIMEOUT_MULTIPLIER;
     private static final long ANIMATION_TIMEOUT_MS =
             5L * ANIMATION_DURATION_MS * HW_TIMEOUT_MULTIPLIER;
+
+    private static final String UNIQUE_DISPLAY_ID_PRIMARY = "000";
+    private static final String UNIQUE_DISPLAY_ID_SECONDARY = "111";
+    private static final int CORNER_RADIUS_PRIMARY = 10;
+    private static final int CORNER_RADIUS_SECONDARY = 20;
+
     private FullscreenMagnificationController mFullscreenMagnificationController;
     private SurfaceControlViewHost mSurfaceControlViewHost;
     private ValueAnimator mShowHideBorderAnimator;
@@ -83,10 +95,35 @@
     private TestableWindowManager mWindowManager;
     @Mock
     private IWindowManager mIWindowManager;
+    @Mock
+    private DisplayManager mDisplayManager;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
+        mContext = spy(mContext);
+        Display display = mock(Display.class);
+        when(display.getUniqueId()).thenReturn(UNIQUE_DISPLAY_ID_PRIMARY);
+        when(mContext.getDisplayNoVerify()).thenReturn(display);
+
+        // Override the resources to Display Primary
+        mContext.getOrCreateTestableResources()
+                .addOverride(
+                        com.android.internal.R.dimen.rounded_corner_radius,
+                        CORNER_RADIUS_PRIMARY);
+        mContext.getOrCreateTestableResources()
+                .addOverride(com.android.internal.R.dimen.rounded_corner_radius_adjustment, 0);
+        mContext.getOrCreateTestableResources()
+                .addOverride(com.android.internal.R.dimen.rounded_corner_radius_top, 0);
+        mContext.getOrCreateTestableResources()
+                .addOverride(
+                        com.android.internal.R.dimen.rounded_corner_radius_top_adjustment, 0);
+        mContext.getOrCreateTestableResources()
+                .addOverride(com.android.internal.R.dimen.rounded_corner_radius_bottom, 0);
+        mContext.getOrCreateTestableResources()
+                .addOverride(
+                        com.android.internal.R.dimen.rounded_corner_radius_bottom_adjustment, 0);
+
         getInstrumentation().runOnMainSync(() -> mSurfaceControlViewHost =
                 spy(new SurfaceControlViewHost(mContext, mContext.getDisplay(),
                         new InputTransferToken(), "FullscreenMagnification")));
@@ -101,6 +138,7 @@
                 mContext,
                 mContext.getMainThreadHandler(),
                 mContext.getMainExecutor(),
+                mDisplayManager,
                 mContext.getSystemService(AccessibilityManager.class),
                 mContext.getSystemService(WindowManager.class),
                 mIWindowManager,
@@ -259,6 +297,87 @@
         verify(mSurfaceControlViewHost).relayout(newWidth, newHeight);
     }
 
+    @EnableFlags(Flags.FLAG_UPDATE_CORNER_RADIUS_ON_DISPLAY_CHANGED)
+    @Test
+    public void enableFullscreenMagnification_applyPrimaryCornerRadius()
+            throws InterruptedException {
+        CountDownLatch transactionCommittedLatch = new CountDownLatch(1);
+        CountDownLatch animationEndLatch = new CountDownLatch(1);
+        mTransaction.addTransactionCommittedListener(
+                Runnable::run, transactionCommittedLatch::countDown);
+        mShowHideBorderAnimator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                animationEndLatch.countDown();
+            }
+        });
+
+        getInstrumentation().runOnMainSync(() ->
+                //Enable fullscreen magnification
+                mFullscreenMagnificationController
+                        .onFullscreenMagnificationActivationChanged(true));
+        assertWithMessage("Failed to wait for transaction committed")
+                .that(transactionCommittedLatch.await(WAIT_TIMEOUT_S, TimeUnit.SECONDS))
+                .isTrue();
+        assertWithMessage("Failed to wait for animation to be finished")
+                .that(animationEndLatch.await(ANIMATION_TIMEOUT_MS, TimeUnit.MILLISECONDS))
+                .isTrue();
+
+        // Verify the initial corner radius is applied
+        GradientDrawable backgroundDrawable =
+                (GradientDrawable) mSurfaceControlViewHost.getView().getBackground();
+        assertThat(backgroundDrawable.getCornerRadius()).isEqualTo(CORNER_RADIUS_PRIMARY);
+    }
+
+    @EnableFlags(Flags.FLAG_UPDATE_CORNER_RADIUS_ON_DISPLAY_CHANGED)
+    @Test
+    public void onDisplayChanged_updateCornerRadiusToSecondary() throws InterruptedException {
+        CountDownLatch transactionCommittedLatch = new CountDownLatch(1);
+        CountDownLatch animationEndLatch = new CountDownLatch(1);
+        mTransaction.addTransactionCommittedListener(
+                Runnable::run, transactionCommittedLatch::countDown);
+        mShowHideBorderAnimator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                animationEndLatch.countDown();
+            }
+        });
+
+        getInstrumentation().runOnMainSync(() ->
+                //Enable fullscreen magnification
+                mFullscreenMagnificationController
+                        .onFullscreenMagnificationActivationChanged(true));
+        assertWithMessage("Failed to wait for transaction committed")
+                .that(transactionCommittedLatch.await(WAIT_TIMEOUT_S, TimeUnit.SECONDS))
+                .isTrue();
+        assertWithMessage("Failed to wait for animation to be finished")
+                .that(animationEndLatch.await(ANIMATION_TIMEOUT_MS, TimeUnit.MILLISECONDS))
+                .isTrue();
+
+        ArgumentCaptor<DisplayManager.DisplayListener> displayListenerCaptor =
+                ArgumentCaptor.forClass(DisplayManager.DisplayListener.class);
+        verify(mDisplayManager).registerDisplayListener(displayListenerCaptor.capture(), any());
+
+        Display newDisplay = mock(Display.class);
+        when(newDisplay.getUniqueId()).thenReturn(UNIQUE_DISPLAY_ID_SECONDARY);
+        when(mContext.getDisplayNoVerify()).thenReturn(newDisplay);
+        // Override the resources to Display Secondary
+        mContext.getOrCreateTestableResources()
+                .removeOverride(com.android.internal.R.dimen.rounded_corner_radius);
+        mContext.getOrCreateTestableResources()
+                .addOverride(
+                        com.android.internal.R.dimen.rounded_corner_radius,
+                        CORNER_RADIUS_SECONDARY);
+        getInstrumentation().runOnMainSync(() ->
+                displayListenerCaptor.getValue().onDisplayChanged(Display.DEFAULT_DISPLAY));
+        waitForIdleSync();
+        // Verify the corner radius is updated
+        GradientDrawable backgroundDrawable2 =
+                (GradientDrawable) mSurfaceControlViewHost.getView().getBackground();
+        assertThat(backgroundDrawable2.getCornerRadius()).isEqualTo(CORNER_RADIUS_SECONDARY);
+    }
+
+
     private ValueAnimator newNullTargetObjectAnimator() {
         final ValueAnimator animator =
                 ObjectAnimator.ofFloat(/* target= */ null, View.ALPHA, 0f, 1f);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerWindowlessMagnifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerWindowlessMagnifierTest.java
index e01366a..6ff1b81 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerWindowlessMagnifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerWindowlessMagnifierTest.java
@@ -27,14 +27,8 @@
 
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_MAGNIFICATION_OVERLAP;
 
-import static org.hamcrest.Matchers.containsString;
-import static org.hamcrest.Matchers.hasItems;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertThat;
-import static org.junit.Assert.assertTrue;
+import static com.google.common.truth.Truth.assertThat;
+
 import static org.mockito.AdditionalAnswers.returnsSecondArg;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyFloat;
@@ -53,6 +47,8 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import static java.util.Arrays.asList;
+
 import android.animation.ValueAnimator;
 import android.annotation.IdRes;
 import android.annotation.Nullable;
@@ -72,7 +68,6 @@
 import android.provider.Settings;
 import android.testing.TestableLooper;
 import android.testing.TestableResources;
-import android.text.TextUtils;
 import android.util.Size;
 import android.view.AttachedSurfaceControl;
 import android.view.Display;
@@ -274,7 +269,7 @@
         verify(mSecureSettings).getIntForUser(
                 eq(Settings.Secure.ACCESSIBILITY_ALLOW_DIAGONAL_SCROLLING),
                 /* def */ eq(1), /* userHandle= */ anyInt());
-        assertTrue(mWindowMagnificationController.isDiagonalScrollingEnabled());
+        assertThat(mWindowMagnificationController.isDiagonalScrollingEnabled()).isTrue();
     }
 
     @Test
@@ -340,10 +335,10 @@
         final ArgumentCaptor<Rect> sourceBoundsCaptor = ArgumentCaptor.forClass(Rect.class);
         verify(mWindowMagnifierCallback, atLeast(2)).onSourceBoundsChanged(
                 (eq(mContext.getDisplayId())), sourceBoundsCaptor.capture());
-        assertEquals(mWindowMagnificationController.getCenterX(),
-                sourceBoundsCaptor.getValue().exactCenterX(), 0);
-        assertEquals(mWindowMagnificationController.getCenterY(),
-                sourceBoundsCaptor.getValue().exactCenterY(), 0);
+        assertThat(mWindowMagnificationController.getCenterX())
+                .isEqualTo(sourceBoundsCaptor.getValue().exactCenterX());
+        assertThat(mWindowMagnificationController.getCenterY())
+                .isEqualTo(sourceBoundsCaptor.getValue().exactCenterY());
     }
 
     @Test
@@ -356,7 +351,7 @@
         waitForIdleSync();
 
         List<Rect> rects = mSurfaceControlViewHost.getView().getSystemGestureExclusionRects();
-        assertFalse(rects.isEmpty());
+        assertThat(rects).isNotEmpty();
     }
 
     @Ignore("The default window size should be constrained after fixing b/288056772")
@@ -374,8 +369,8 @@
         ViewGroup.LayoutParams params = mSurfaceControlViewHost.getView().getLayoutParams();
         // The frame size should be the half of smaller value of window height/width unless it
         //exceed the max frame size.
-        assertTrue(params.width < halfScreenSize);
-        assertTrue(params.height < halfScreenSize);
+        assertThat(params.width).isLessThan(halfScreenSize);
+        assertThat(params.height).isLessThan(halfScreenSize);
     }
 
     @Test
@@ -409,7 +404,7 @@
         });
 
         verify(mMirrorWindowControl).destroyControl();
-        assertFalse(hasMagnificationOverlapFlag());
+        assertThat(hasMagnificationOverlapFlag()).isFalse();
     }
 
     @Test
@@ -468,12 +463,12 @@
         verify(mAnimationCallback, never()).onResult(eq(false));
         verify(mWindowMagnifierCallback, timeout(LAYOUT_CHANGE_TIMEOUT_MS))
                 .onSourceBoundsChanged((eq(mContext.getDisplayId())), sourceBoundsCaptor.capture());
-        assertEquals(mWindowMagnificationController.getCenterX(),
-                sourceBoundsCaptor.getValue().exactCenterX(), 0);
-        assertEquals(mWindowMagnificationController.getCenterY(),
-                sourceBoundsCaptor.getValue().exactCenterY(), 0);
-        assertEquals(mWindowMagnificationController.getCenterX(), targetCenterX, 0);
-        assertEquals(mWindowMagnificationController.getCenterY(), targetCenterY, 0);
+        assertThat(mWindowMagnificationController.getCenterX())
+                .isEqualTo(sourceBoundsCaptor.getValue().exactCenterX());
+        assertThat(mWindowMagnificationController.getCenterY())
+                .isEqualTo(sourceBoundsCaptor.getValue().exactCenterY());
+        assertThat(mWindowMagnificationController.getCenterX()).isEqualTo(targetCenterX);
+        assertThat(mWindowMagnificationController.getCenterY()).isEqualTo(targetCenterY);
     }
 
     @Test
@@ -509,12 +504,12 @@
         verify(mAnimationCallback, times(3)).onResult(eq(false));
         verify(mWindowMagnifierCallback, timeout(LAYOUT_CHANGE_TIMEOUT_MS))
                 .onSourceBoundsChanged((eq(mContext.getDisplayId())), sourceBoundsCaptor.capture());
-        assertEquals(mWindowMagnificationController.getCenterX(),
-                sourceBoundsCaptor.getValue().exactCenterX(), 0);
-        assertEquals(mWindowMagnificationController.getCenterY(),
-                sourceBoundsCaptor.getValue().exactCenterY(), 0);
-        assertEquals(mWindowMagnificationController.getCenterX(), centerX + 40, 0);
-        assertEquals(mWindowMagnificationController.getCenterY(), centerY + 40, 0);
+        assertThat(mWindowMagnificationController.getCenterX())
+                .isEqualTo(sourceBoundsCaptor.getValue().exactCenterX());
+        assertThat(mWindowMagnificationController.getCenterY())
+                .isEqualTo(sourceBoundsCaptor.getValue().exactCenterY());
+        assertThat(mWindowMagnificationController.getCenterX()).isEqualTo(centerX + 40);
+        assertThat(mWindowMagnificationController.getCenterY()).isEqualTo(centerY + 40);
     }
 
     @Test
@@ -525,10 +520,10 @@
 
         mInstrumentation.runOnMainSync(() -> mWindowMagnificationController.setScale(3.0f));
 
-        assertEquals(3.0f, mWindowMagnificationController.getScale(), 0);
+        assertThat(mWindowMagnificationController.getScale()).isEqualTo(3.0f);
         final View mirrorView = mSurfaceControlViewHost.getView();
-        assertNotNull(mirrorView);
-        assertThat(mirrorView.getStateDescription().toString(), containsString("300"));
+        assertThat(mirrorView).isNotNull();
+        assertThat(mirrorView.getStateDescription().toString()).contains("300");
     }
 
     @Test
@@ -567,12 +562,12 @@
         mInstrumentation.runOnMainSync(() -> mWindowMagnificationController.onConfigurationChanged(
                 ActivityInfo.CONFIG_ORIENTATION));
 
-        assertEquals(newRotation, mWindowMagnificationController.mRotation);
+        assertThat(mWindowMagnificationController.mRotation).isEqualTo(newRotation);
         final PointF expectedCenter = new PointF(magnifiedCenter.y,
                 displayWidth - magnifiedCenter.x);
         final PointF actualCenter = new PointF(mWindowMagnificationController.getCenterX(),
                 mWindowMagnificationController.getCenterY());
-        assertEquals(expectedCenter, actualCenter);
+        assertThat(actualCenter).isEqualTo(expectedCenter);
     }
 
     @Test
@@ -587,7 +582,7 @@
         mInstrumentation.runOnMainSync(() -> mWindowMagnificationController.onConfigurationChanged(
                 ActivityInfo.CONFIG_ORIENTATION));
 
-        assertEquals(newRotation, mWindowMagnificationController.mRotation);
+        assertThat(mWindowMagnificationController.mRotation).isEqualTo(newRotation);
     }
 
     @Test
@@ -614,12 +609,10 @@
         });
 
         // The ratio of center to window size should be the same.
-        assertEquals(expectedRatio,
-                mWindowMagnificationController.getCenterX() / testWindowBounds.width(),
-                0);
-        assertEquals(expectedRatio,
-                mWindowMagnificationController.getCenterY() / testWindowBounds.height(),
-                0);
+        assertThat(mWindowMagnificationController.getCenterX() / testWindowBounds.width())
+                .isEqualTo(expectedRatio);
+        assertThat(mWindowMagnificationController.getCenterY() / testWindowBounds.height())
+                .isEqualTo(expectedRatio);
     }
 
     @DisableFlags(Flags.FLAG_SAVE_AND_RESTORE_MAGNIFICATION_SETTINGS_BUTTONS)
@@ -657,8 +650,8 @@
         final int mirrorSurfaceMargin = mResources.getDimensionPixelSize(
                 R.dimen.magnification_mirror_surface_margin);
         // The width and height of the view include the magnification frame and the margins.
-        assertTrue(params.width == (windowFrameSize + 2 * mirrorSurfaceMargin));
-        assertTrue(params.height == (windowFrameSize + 2 * mirrorSurfaceMargin));
+        assertThat(params.width).isEqualTo(windowFrameSize + 2 * mirrorSurfaceMargin);
+        assertThat(params.height).isEqualTo(windowFrameSize + 2 * mirrorSurfaceMargin);
     }
 
     @EnableFlags(Flags.FLAG_SAVE_AND_RESTORE_MAGNIFICATION_SETTINGS_BUTTONS)
@@ -701,8 +694,8 @@
         final int mirrorSurfaceMargin = mResources.getDimensionPixelSize(
                 R.dimen.magnification_mirror_surface_margin);
         // The width and height of the view include the magnification frame and the margins.
-        assertTrue(params.width == (windowFrameSize + 2 * mirrorSurfaceMargin));
-        assertTrue(params.height == (windowFrameSize + 2 * mirrorSurfaceMargin));
+        assertThat(params.width).isEqualTo(windowFrameSize + 2 * mirrorSurfaceMargin);
+        assertThat(params.height).isEqualTo(windowFrameSize + 2 * mirrorSurfaceMargin);
     }
 
     @Test
@@ -726,8 +719,8 @@
                         WindowMagnificationSettings.MagnificationSize.MEDIUM);
         ViewGroup.LayoutParams params = mSurfaceControlViewHost.getView().getLayoutParams();
 
-        assertTrue(params.width == defaultWindowSize);
-        assertTrue(params.height == defaultWindowSize);
+        assertThat(params.width).isEqualTo(defaultWindowSize);
+        assertThat(params.height).isEqualTo(defaultWindowSize);
     }
 
     @Test
@@ -766,20 +759,29 @@
                     Float.NaN);
         });
         final View mirrorView = mSurfaceControlViewHost.getView();
-        assertNotNull(mirrorView);
+        assertThat(mirrorView).isNotNull();
         final AccessibilityNodeInfo nodeInfo = new AccessibilityNodeInfo();
 
         mirrorView.onInitializeAccessibilityNodeInfo(nodeInfo);
 
-        assertNotNull(nodeInfo.getContentDescription());
-        assertThat(nodeInfo.getStateDescription().toString(), containsString("250"));
-        assertThat(nodeInfo.getActionList(),
-                hasItems(new AccessibilityAction(R.id.accessibility_action_zoom_in, null),
-                        new AccessibilityAction(R.id.accessibility_action_zoom_out, null),
-                        new AccessibilityAction(R.id.accessibility_action_move_right, null),
-                        new AccessibilityAction(R.id.accessibility_action_move_left, null),
-                        new AccessibilityAction(R.id.accessibility_action_move_down, null),
-                        new AccessibilityAction(R.id.accessibility_action_move_up, null)));
+        assertThat(nodeInfo.getContentDescription()).isNotNull();
+        assertThat(nodeInfo.getStateDescription().toString()).contains("250");
+        assertThat(nodeInfo.getActionList()).containsExactlyElementsIn(asList(
+                new AccessibilityAction(AccessibilityAction.ACTION_CLICK.getId(),
+                        mContext.getResources().getString(
+                        R.string.magnification_open_settings_click_label)),
+                new AccessibilityAction(R.id.accessibility_action_zoom_in,
+                        mContext.getString(R.string.accessibility_control_zoom_in)),
+                new AccessibilityAction(R.id.accessibility_action_zoom_out,
+                        mContext.getString(R.string.accessibility_control_zoom_out)),
+                new AccessibilityAction(R.id.accessibility_action_move_right,
+                        mContext.getString(R.string.accessibility_control_move_right)),
+                new AccessibilityAction(R.id.accessibility_action_move_left,
+                        mContext.getString(R.string.accessibility_control_move_left)),
+                new AccessibilityAction(R.id.accessibility_action_move_down,
+                        mContext.getString(R.string.accessibility_control_move_down)),
+                new AccessibilityAction(R.id.accessibility_action_move_up,
+                        mContext.getString(R.string.accessibility_control_move_up))));
     }
 
     @Test
@@ -791,28 +793,33 @@
         });
 
         final View mirrorView = mSurfaceControlViewHost.getView();
-        assertTrue(
-                mirrorView.performAccessibilityAction(R.id.accessibility_action_zoom_out, null));
+        assertThat(mirrorView.performAccessibilityAction(R.id.accessibility_action_zoom_out, null))
+                .isTrue();
         // Minimum scale is 1.0.
         verify(mWindowMagnifierCallback).onPerformScaleAction(
                 eq(displayId), /* scale= */ eq(1.0f), /* updatePersistence= */ eq(true));
 
-        assertTrue(mirrorView.performAccessibilityAction(R.id.accessibility_action_zoom_in, null));
+        assertThat(mirrorView.performAccessibilityAction(R.id.accessibility_action_zoom_in, null))
+                .isTrue();
         verify(mWindowMagnifierCallback).onPerformScaleAction(
                 eq(displayId), /* scale= */ eq(2.5f), /* updatePersistence= */ eq(true));
 
         // TODO: Verify the final state when the mirror surface is visible.
-        assertTrue(mirrorView.performAccessibilityAction(R.id.accessibility_action_move_up, null));
-        assertTrue(
-                mirrorView.performAccessibilityAction(R.id.accessibility_action_move_down, null));
-        assertTrue(
-                mirrorView.performAccessibilityAction(R.id.accessibility_action_move_right, null));
-        assertTrue(
-                mirrorView.performAccessibilityAction(R.id.accessibility_action_move_left, null));
+        assertThat(mirrorView.performAccessibilityAction(R.id.accessibility_action_move_up, null))
+                .isTrue();
+        assertThat(
+                mirrorView.performAccessibilityAction(R.id.accessibility_action_move_down, null))
+                .isTrue();
+        assertThat(
+                mirrorView.performAccessibilityAction(R.id.accessibility_action_move_right, null))
+                .isTrue();
+        assertThat(
+                mirrorView.performAccessibilityAction(R.id.accessibility_action_move_left, null))
+                .isTrue();
         verify(mWindowMagnifierCallback, times(4)).onMove(eq(displayId));
 
-        assertTrue(mirrorView.performAccessibilityAction(
-                AccessibilityAction.ACTION_CLICK.getId(), null));
+        assertThat(mirrorView.performAccessibilityAction(
+                AccessibilityAction.ACTION_CLICK.getId(), null)).isTrue();
         verify(mWindowMagnifierCallback).onClickSettingsButton(eq(displayId));
     }
 
@@ -844,22 +851,22 @@
         View topRightCorner = getInternalView(R.id.top_right_corner);
         View topLeftCorner = getInternalView(R.id.top_left_corner);
 
-        assertEquals(View.VISIBLE, closeButton.getVisibility());
-        assertEquals(View.VISIBLE, bottomRightCorner.getVisibility());
-        assertEquals(View.VISIBLE, bottomLeftCorner.getVisibility());
-        assertEquals(View.VISIBLE, topRightCorner.getVisibility());
-        assertEquals(View.VISIBLE, topLeftCorner.getVisibility());
+        assertThat(closeButton.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(bottomRightCorner.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(bottomLeftCorner.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(topRightCorner.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(topLeftCorner.getVisibility()).isEqualTo(View.VISIBLE);
 
         final View mirrorView = mSurfaceControlViewHost.getView();
         mInstrumentation.runOnMainSync(() ->
                 mirrorView.performAccessibilityAction(AccessibilityAction.ACTION_CLICK.getId(),
                         null));
 
-        assertEquals(View.GONE, closeButton.getVisibility());
-        assertEquals(View.GONE, bottomRightCorner.getVisibility());
-        assertEquals(View.GONE, bottomLeftCorner.getVisibility());
-        assertEquals(View.GONE, topRightCorner.getVisibility());
-        assertEquals(View.GONE, topLeftCorner.getVisibility());
+        assertThat(closeButton.getVisibility()).isEqualTo(View.GONE);
+        assertThat(bottomRightCorner.getVisibility()).isEqualTo(View.GONE);
+        assertThat(bottomLeftCorner.getVisibility()).isEqualTo(View.GONE);
+        assertThat(topRightCorner.getVisibility()).isEqualTo(View.GONE);
+        assertThat(topLeftCorner.getVisibility()).isEqualTo(View.GONE);
     }
 
     @Test
@@ -901,8 +908,8 @@
         int newWindowWidth =
                 (int) ((startingWidth - 2 * mirrorSurfaceMargin) * (1 + changeWindowSizeAmount))
                         + 2 * mirrorSurfaceMargin;
-        assertEquals(newWindowWidth, actualWindowWidth.get());
-        assertEquals(startingHeight, actualWindowHeight.get());
+        assertThat(actualWindowWidth.get()).isEqualTo(newWindowWidth);
+        assertThat(actualWindowHeight.get()).isEqualTo(startingHeight);
     }
 
     @Test
@@ -943,8 +950,8 @@
         int newWindowHeight =
                 (int) ((startingHeight - 2 * mirrorSurfaceMargin) * (1 + changeWindowSizeAmount))
                         + 2 * mirrorSurfaceMargin;
-        assertEquals(startingWidth, actualWindowWidth.get());
-        assertEquals(newWindowHeight, actualWindowHeight.get());
+        assertThat(actualWindowWidth.get()).isEqualTo(startingWidth);
+        assertThat(actualWindowHeight.get()).isEqualTo(newWindowHeight);
     }
 
     @Test
@@ -963,8 +970,11 @@
         final View mirrorView = mSurfaceControlViewHost.getView();
         final AccessibilityNodeInfo accessibilityNodeInfo =
                 mirrorView.createAccessibilityNodeInfo();
-        assertFalse(accessibilityNodeInfo.getActionList().contains(
-                new AccessibilityAction(R.id.accessibility_action_increase_window_width, null)));
+        assertThat(accessibilityNodeInfo.getActionList()).doesNotContain(
+                new AccessibilityAction(
+                        R.id.accessibility_action_increase_window_width,
+                        mContext.getString(
+                                R.string.accessibility_control_increase_window_width)));
     }
 
     @Test
@@ -983,8 +993,9 @@
         final View mirrorView = mSurfaceControlViewHost.getView();
         final AccessibilityNodeInfo accessibilityNodeInfo =
                 mirrorView.createAccessibilityNodeInfo();
-        assertFalse(accessibilityNodeInfo.getActionList().contains(
-                new AccessibilityAction(R.id.accessibility_action_increase_window_height, null)));
+        assertThat(accessibilityNodeInfo.getActionList()).doesNotContain(
+                new AccessibilityAction(
+                        R.id.accessibility_action_increase_window_height, null));
     }
 
     @Test
@@ -1024,8 +1035,8 @@
         int newWindowWidth =
                 (int) ((startingSize - 2 * mirrorSurfaceMargin) * (1 - changeWindowSizeAmount))
                         + 2 * mirrorSurfaceMargin;
-        assertEquals(newWindowWidth, actualWindowWidth.get());
-        assertEquals(startingSize, actualWindowHeight.get());
+        assertThat(actualWindowWidth.get()).isEqualTo(newWindowWidth);
+        assertThat(actualWindowHeight.get()).isEqualTo(startingSize);
     }
 
     @Test
@@ -1066,8 +1077,8 @@
         int newWindowHeight =
                 (int) ((startingSize - 2 * mirrorSurfaceMargin) * (1 - changeWindowSizeAmount))
                         + 2 * mirrorSurfaceMargin;
-        assertEquals(startingSize, actualWindowWidth.get());
-        assertEquals(newWindowHeight, actualWindowHeight.get());
+        assertThat(actualWindowWidth.get()).isEqualTo(startingSize);
+        assertThat(actualWindowHeight.get()).isEqualTo(newWindowHeight);
     }
 
     @Test
@@ -1086,8 +1097,9 @@
         final View mirrorView = mSurfaceControlViewHost.getView();
         final AccessibilityNodeInfo accessibilityNodeInfo =
                 mirrorView.createAccessibilityNodeInfo();
-        assertFalse(accessibilityNodeInfo.getActionList().contains(
-                new AccessibilityAction(R.id.accessibility_action_decrease_window_width, null)));
+        assertThat(accessibilityNodeInfo.getActionList()).doesNotContain(
+                new AccessibilityAction(
+                        R.id.accessibility_action_decrease_window_width, null));
     }
 
     @Test
@@ -1106,8 +1118,9 @@
         final View mirrorView = mSurfaceControlViewHost.getView();
         final AccessibilityNodeInfo accessibilityNodeInfo =
                 mirrorView.createAccessibilityNodeInfo();
-        assertFalse(accessibilityNodeInfo.getActionList().contains(
-                new AccessibilityAction(R.id.accessibility_action_decrease_window_height, null)));
+        assertThat(accessibilityNodeInfo.getActionList()).doesNotContain(
+                new AccessibilityAction(
+                        R.id.accessibility_action_decrease_window_height, null));
     }
 
     @Test
@@ -1117,8 +1130,8 @@
                     Float.NaN);
         });
 
-        assertEquals(getContext().getResources().getString(
-                com.android.internal.R.string.android_system_label), getAccessibilityWindowTitle());
+        assertThat(getAccessibilityWindowTitle()).isEqualTo(getContext().getResources().getString(
+                com.android.internal.R.string.android_system_label));
     }
 
     @Test
@@ -1133,14 +1146,14 @@
                     Float.NaN);
         });
 
-        assertEquals(Float.NaN, mWindowMagnificationController.getScale(), 0);
+        assertThat(mWindowMagnificationController.getScale()).isEqualTo(Float.NaN);
     }
 
     @Test
     public void enableWindowMagnification_rotationIsChanged_updateRotationValue() {
         // the config orientation should not be undefined, since it would cause config.diff
         // returning 0 and thus the orientation changed would not be detected
-        assertNotEquals(ORIENTATION_UNDEFINED, mResources.getConfiguration().orientation);
+        assertThat(mResources.getConfiguration().orientation).isNotEqualTo(ORIENTATION_UNDEFINED);
 
         final Configuration config = mResources.getConfiguration();
         config.orientation = config.orientation == ORIENTATION_LANDSCAPE ? ORIENTATION_PORTRAIT
@@ -1151,7 +1164,7 @@
                 () -> mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN,
                         Float.NaN, Float.NaN));
 
-        assertEquals(newRotation, mWindowMagnificationController.mRotation);
+        assertThat(mWindowMagnificationController.mRotation).isEqualTo(newRotation);
     }
 
     @Test
@@ -1179,7 +1192,7 @@
             mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_LOCALE);
         });
 
-        assertTrue(TextUtils.equals(newA11yWindowTitle, getAccessibilityWindowTitle()));
+        assertThat(getAccessibilityWindowTitle()).isEqualTo(newA11yWindowTitle);
     }
 
     @Ignore("it's flaky in presubmit but works in abtd, filter for now. b/305654925")
@@ -1202,10 +1215,10 @@
             // maxScaleX.getAndAccumulate(mirrorView.getScaleX(), Math::max);
             final double oldMax = maxScaleX.get();
             final double newMax = Math.max(mirrorView.getScaleX(), oldMax);
-            assertTrue(maxScaleX.compareAndSet(oldMax, newMax));
+            assertThat(maxScaleX.compareAndSet(oldMax, newMax)).isTrue();
         });
 
-        assertTrue(maxScaleX.get() > 1.0);
+        assertThat(maxScaleX.get()).isGreaterThan(1.0);
     }
 
     @Test
@@ -1250,7 +1263,7 @@
 
         View dragButton = getInternalView(R.id.drag_handle);
         FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) dragButton.getLayoutParams();
-        assertEquals(Gravity.BOTTOM | Gravity.LEFT, params.gravity);
+        assertThat(params.gravity).isEqualTo(Gravity.BOTTOM | Gravity.LEFT);
     }
 
     @Test
@@ -1279,7 +1292,7 @@
 
         View dragButton = getInternalView(R.id.drag_handle);
         FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) dragButton.getLayoutParams();
-        assertEquals(Gravity.BOTTOM | Gravity.RIGHT, params.gravity);
+        assertThat(params.gravity).isEqualTo(Gravity.BOTTOM | Gravity.RIGHT);
     }
 
     @Test
@@ -1301,8 +1314,8 @@
 
         });
 
-        assertEquals(expectedWindowHeight, actualWindowHeight.get());
-        assertEquals(expectedWindowWidth, actualWindowWidth.get());
+        assertThat(actualWindowHeight.get()).isEqualTo(expectedWindowHeight);
+        assertThat(actualWindowWidth.get()).isEqualTo(expectedWindowWidth);
     }
 
     @Test
@@ -1322,8 +1335,8 @@
             actualWindowWidth.set(mSurfaceControlViewHost.getView().getLayoutParams().width);
         });
 
-        assertEquals(expectedWindowHeight, actualWindowHeight.get());
-        assertEquals(expectedWindowWidth, actualWindowWidth.get());
+        assertThat(actualWindowHeight.get()).isEqualTo(expectedWindowHeight);
+        assertThat(actualWindowWidth.get()).isEqualTo(expectedWindowWidth);
     }
 
     @Test
@@ -1343,8 +1356,8 @@
             actualWindowWidth.set(mSurfaceControlViewHost.getView().getLayoutParams().width);
         });
 
-        assertEquals(minimumWindowSize, actualWindowHeight.get());
-        assertEquals(minimumWindowSize, actualWindowWidth.get());
+        assertThat(actualWindowHeight.get()).isEqualTo(minimumWindowSize);
+        assertThat(actualWindowWidth.get()).isEqualTo(minimumWindowSize);
     }
 
     @Test
@@ -1362,8 +1375,8 @@
             actualWindowWidth.set(mSurfaceControlViewHost.getView().getLayoutParams().width);
         });
 
-        assertEquals(bounds.height(), actualWindowHeight.get());
-        assertEquals(bounds.width(), actualWindowWidth.get());
+        assertThat(actualWindowHeight.get()).isEqualTo(bounds.height());
+        assertThat(actualWindowWidth.get()).isEqualTo(bounds.width());
     }
 
     @Test
@@ -1395,8 +1408,8 @@
                             mSurfaceControlViewHost.getView().getLayoutParams().width);
                 });
 
-        assertEquals(expectedWindowHeight, actualWindowHeight.get());
-        assertEquals(expectedWindowWidth, actualWindowWidth.get());
+        assertThat(actualWindowHeight.get()).isEqualTo(expectedWindowHeight);
+        assertThat(actualWindowWidth.get()).isEqualTo(expectedWindowWidth);
     }
 
     @Test
@@ -1431,8 +1444,8 @@
                             mSurfaceControlViewHost.getView().getLayoutParams().width);
                 });
 
-        assertEquals(startingSize + 1, actualWindowHeight.get());
-        assertEquals(startingSize + 2, actualWindowWidth.get());
+        assertThat(actualWindowHeight.get()).isEqualTo(startingSize + 1);
+        assertThat(actualWindowWidth.get()).isEqualTo(startingSize + 2);
     }
 
     @Test
@@ -1460,8 +1473,8 @@
                     actualWindowWidth.set(
                             mSurfaceControlViewHost.getView().getLayoutParams().width);
                 });
-        assertEquals(startingSize + 1, actualWindowHeight.get());
-        assertEquals(startingSize, actualWindowWidth.get());
+        assertThat(actualWindowHeight.get()).isEqualTo(startingSize + 1);
+        assertThat(actualWindowWidth.get()).isEqualTo(startingSize);
     }
 
     @Test
@@ -1483,8 +1496,8 @@
             magnificationCenterY.set((int) mWindowMagnificationController.getCenterY());
         });
 
-        assertTrue(magnificationCenterX.get() < bounds.right);
-        assertTrue(magnificationCenterY.get() < bounds.bottom);
+        assertThat(magnificationCenterX.get()).isLessThan(bounds.right);
+        assertThat(magnificationCenterY.get()).isLessThan(bounds.bottom);
     }
 
     @Test
@@ -1510,7 +1523,7 @@
     private <T extends View> T getInternalView(@IdRes int idRes) {
         View mirrorView = mSurfaceControlViewHost.getView();
         T view = mirrorView.findViewById(idRes);
-        assertNotNull(view);
+        assertThat(view).isNotNull();
         return view;
     }
 
@@ -1519,14 +1532,14 @@
         return mMotionEventHelper.obtainMotionEvent(downTime, eventTime, action, x, y);
     }
 
-    private CharSequence getAccessibilityWindowTitle() {
+    private String getAccessibilityWindowTitle() {
         final View mirrorView = mSurfaceControlViewHost.getView();
         if (mirrorView == null) {
             return null;
         }
         WindowManager.LayoutParams layoutParams =
                 (WindowManager.LayoutParams) mirrorView.getLayoutParams();
-        return layoutParams.accessibilityTitle;
+        return layoutParams.accessibilityTitle.toString();
     }
 
     private boolean hasMagnificationOverlapFlag() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/extradim/ExtraDimDialogDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/accessibility/extradim/ExtraDimDialogDelegateTest.kt
new file mode 100644
index 0000000..b80836d
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/extradim/ExtraDimDialogDelegateTest.kt
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.accessibility.extradim
+
+import android.content.DialogInterface
+import android.testing.TestableLooper
+import android.view.accessibility.AccessibilityManager
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.internal.accessibility.AccessibilityShortcutController
+import com.android.internal.accessibility.common.ShortcutConstants
+import com.android.systemui.SysuiTestCase
+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.model.SysUiState
+import com.android.systemui.res.R
+import com.android.systemui.settings.UserTracker
+import com.android.systemui.statusbar.phone.SystemUIDialog
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.advanceUntilIdle
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyBoolean
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.anyLong
+import org.mockito.Mock
+import org.mockito.Mockito.`when` as whenever
+import org.mockito.junit.MockitoJUnit
+import org.mockito.junit.MockitoRule
+import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.verify
+
+/** Tests for [ExtraDimDialogDelegate]. */
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@RunWith(AndroidJUnit4::class)
+class ExtraDimDialogDelegateTest : SysuiTestCase() {
+    @get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule()
+
+    private lateinit var extraDimDialogDelegate: ExtraDimDialogDelegate
+
+    private val kosmos = Kosmos().also { it.testCase = this }
+    private val testScope = kosmos.testScope
+
+    @Mock private lateinit var dialog: SystemUIDialog
+    @Mock private lateinit var accessibilityManager: AccessibilityManager
+    @Mock private lateinit var dialogFactory: SystemUIDialog.Factory
+    @Mock private lateinit var userTracker: UserTracker
+    @Mock private lateinit var sysuiState: SysUiState
+
+    @Before
+    fun setUp() {
+        whenever(sysuiState.setFlag(anyLong(), anyBoolean())).thenReturn(sysuiState)
+        whenever(dialog.context).thenReturn(context)
+
+        extraDimDialogDelegate =
+            ExtraDimDialogDelegate(
+                context,
+                testScope.backgroundScope,
+                kosmos.testDispatcher,
+                dialogFactory,
+                accessibilityManager,
+                userTracker
+            )
+    }
+
+    @Test
+    fun clickButton_removeExtraDimShortcuts() =
+        kosmos.testScope.runTest {
+            extraDimDialogDelegate.beforeCreate(dialog, /* savedInstanceState= */ null)
+
+            val clickListener = argumentCaptor<DialogInterface.OnClickListener>()
+
+            // Verify the button has the right text
+            verify(dialog)
+                .setPositiveButton(
+                    eq(R.string.accessibility_deprecate_extra_dim_dialog_button),
+                    clickListener.capture()
+                )
+
+            clickListener.firstValue.onClick(dialog, 0)
+            advanceUntilIdle()
+            runCurrent()
+            verify(accessibilityManager)
+                .enableShortcutsForTargets(
+                    eq(false),
+                    eq(ShortcutConstants.UserShortcutType.ALL),
+                    eq(
+                        setOf(
+                            AccessibilityShortcutController.REDUCE_BRIGHT_COLORS_COMPONENT_NAME
+                                .flattenToString()
+                        )
+                    ),
+                    anyInt()
+                )
+        }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/extradim/ExtraDimDialogManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/accessibility/extradim/ExtraDimDialogManagerTest.kt
new file mode 100644
index 0000000..1386092
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/extradim/ExtraDimDialogManagerTest.kt
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.accessibility.extradim
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.plugins.ActivityStarter
+import javax.inject.Provider
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.eq
+import org.mockito.Mock
+import org.mockito.junit.MockitoJUnit
+import org.mockito.junit.MockitoRule
+import org.mockito.kotlin.verify
+
+/** Tests for [ExtraDimDialogManager]. */
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class ExtraDimDialogManagerTest : SysuiTestCase() {
+    @get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule()
+
+    private lateinit var extraDimDialogManager: ExtraDimDialogManager
+
+    @Mock private lateinit var activityStarter: ActivityStarter
+    @Mock private lateinit var dialogProvider: Provider<ExtraDimDialogDelegate>
+
+    @Before
+    fun setUp() {
+        extraDimDialogManager = ExtraDimDialogManager(dialogProvider, activityStarter)
+    }
+
+    @Test
+    fun dismissKeyguardIfNeededAndShowDialog_executeRunnableDismissingKeyguard() {
+        extraDimDialogManager.dismissKeyguardIfNeededAndShowDialog()
+        verify(activityStarter)
+            .executeRunnableDismissingKeyguard(
+                any(),
+                /* cancelAction= */ eq(null),
+                /* dismissShade= */ eq(false),
+                /* afterKeyguardGone= */ eq(true),
+                /* deferred= */ eq(false)
+            )
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/extradim/ExtraDimDialogReceiverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/accessibility/extradim/ExtraDimDialogReceiverTest.kt
new file mode 100644
index 0000000..ebe7500
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/extradim/ExtraDimDialogReceiverTest.kt
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.accessibility.extradim
+
+import android.content.Intent
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.server.display.feature.flags.Flags
+import com.android.systemui.SysuiTestCase
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.junit.MockitoJUnit
+import org.mockito.junit.MockitoRule
+import org.mockito.kotlin.never
+import org.mockito.kotlin.verify
+
+/** Tests for [ExtraDimDialogReceiver]. */
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class ExtraDimDialogReceiverTest : SysuiTestCase() {
+    @get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule()
+
+    private lateinit var extraDimDialogReceiver: ExtraDimDialogReceiver
+
+    @Mock private lateinit var extraDimDialogManager: ExtraDimDialogManager
+
+    @Before
+    fun setUp() {
+        extraDimDialogReceiver = ExtraDimDialogReceiver(extraDimDialogManager)
+        mContext
+            .getOrCreateTestableResources()
+            .addOverride(com.android.internal.R.bool.config_evenDimmerEnabled, true)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_EVEN_DIMMER)
+    fun receiveAction_flagEvenDimmerEnabled_showDialog() {
+        extraDimDialogReceiver.onReceive(mContext, Intent(ExtraDimDialogReceiver.ACTION))
+        verify(extraDimDialogManager).dismissKeyguardIfNeededAndShowDialog()
+    }
+
+    @Test
+    @DisableFlags(Flags.FLAG_EVEN_DIMMER)
+    fun receiveAction_flagEvenDimmerDisabled_neverShowDialog() {
+        extraDimDialogReceiver.onReceive(mContext, Intent(ExtraDimDialogReceiver.ACTION))
+        verify(extraDimDialogManager, never()).dismissKeyguardIfNeededAndShowDialog()
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java
index 5ea5c21..d3b7d22 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java
@@ -52,6 +52,7 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
+import com.android.internal.logging.UiEventLogger;
 import com.android.settingslib.bluetooth.BluetoothEventManager;
 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
 import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
@@ -123,6 +124,8 @@
     @Mock
     private AudioManager mAudioManager;
     @Mock
+    private UiEventLogger mUiEventLogger;
+    @Mock
     private CachedBluetoothDevice mCachedDevice;
     @Mock
     private BluetoothDevice mDevice;
@@ -179,6 +182,7 @@
                 anyInt(), any());
         assertThat(intentCaptor.getValue().getAction()).isEqualTo(
                 Settings.ACTION_HEARING_DEVICE_PAIRING_SETTINGS);
+        verify(mUiEventLogger).log(HearingDevicesUiEvent.HEARING_DEVICES_PAIR);
     }
 
     @Test
@@ -192,7 +196,7 @@
                 anyInt(), any());
         assertThat(intentCaptor.getValue().getAction()).isEqualTo(
                 HearingDevicesDialogDelegate.ACTION_BLUETOOTH_DEVICE_DETAILS);
-
+        verify(mUiEventLogger).log(HearingDevicesUiEvent.HEARING_DEVICES_GEAR_CLICK);
     }
 
     @Test
@@ -200,9 +204,10 @@
         setUpDeviceListDialog();
         when(mHearingDeviceItem.getType()).thenReturn(DeviceItemType.CONNECTED_BLUETOOTH_DEVICE);
 
-        mDialogDelegate.onDeviceItemOnClicked(mHearingDeviceItem, new View(mContext));
+        mDialogDelegate.onDeviceItemClicked(mHearingDeviceItem, new View(mContext));
 
         verify(mCachedDevice).disconnect();
+        verify(mUiEventLogger).log(HearingDevicesUiEvent.HEARING_DEVICES_DISCONNECT);
     }
 
     @Test
@@ -304,7 +309,8 @@
                 mDialogTransitionAnimator,
                 mLocalBluetoothManager,
                 new Handler(mTestableLooper.getLooper()),
-                mAudioManager
+                mAudioManager,
+                mUiEventLogger
         );
 
         mDialog = mDialogDelegate.createDialog();
@@ -326,7 +332,8 @@
                 mDialogTransitionAnimator,
                 mLocalBluetoothManager,
                 new Handler(mTestableLooper.getLooper()),
-                mAudioManager
+                mAudioManager,
+                mUiEventLogger
         );
 
         mDialog = mDialogDelegate.createDialog();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
index 1e3ee28..dc69cda 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
@@ -35,7 +35,6 @@
 import android.testing.TestableLooper
 import android.testing.TestableLooper.RunWithLooper
 import android.testing.ViewUtils
-import android.view.KeyEvent
 import android.view.View
 import android.view.WindowInsets
 import android.view.WindowManager
@@ -202,8 +201,7 @@
         val root = container.rootView
 
         // Simulate back invocation
-        container.dispatchKeyEvent(KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK))
-        container.dispatchKeyEvent(KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK))
+        container.onBackInvoked()
         waitForIdleSync()
 
         assertThat(container.parent).isNull()
@@ -217,8 +215,7 @@
         val root = container.rootView
 
         // Simulate back invocation
-        container.dispatchKeyEvent(KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK))
-        container.dispatchKeyEvent(KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK))
+        container.onBackInvoked()
         waitForIdleSync()
 
         assertThat(container.parent).isNull()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
index 6dcea14..6ba9b32 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
@@ -44,7 +44,6 @@
 import android.view.Surface
 import androidx.test.filters.SmallTest
 import com.android.app.activityTaskManager
-import com.android.systemui.Flags.FLAG_BP_TALKBACK
 import com.android.systemui.Flags.FLAG_CONSTRAINT_BP
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.biometrics.AuthController
@@ -74,7 +73,6 @@
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.res.R
-import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.withArgCaptor
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -92,6 +90,7 @@
 import org.mockito.Mock
 import org.mockito.Mockito
 import org.mockito.junit.MockitoJUnit
+import org.mockito.kotlin.any
 import org.mockito.kotlin.whenever
 import platform.test.runner.parameterized.ParameterizedAndroidJunit4
 import platform.test.runner.parameterized.Parameters
@@ -100,9 +99,10 @@
 private const val REQUEST_ID = 4L
 private const val CHALLENGE = 2L
 private const val DELAY = 1000L
-private const val OP_PACKAGE_NAME = "biometric.testapp"
+private const val OP_PACKAGE_NAME_WITH_APP_LOGO = "biometric.testapp"
 private const val OP_PACKAGE_NAME_NO_ICON = "biometric.testapp.noicon"
 private const val OP_PACKAGE_NAME_CAN_NOT_BE_FOUND = "can.not.be.found"
+private const val OP_PACKAGE_NAME_WITH_ACTIVITY_LOGO = "should.use.activiy.logo"
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
@@ -112,19 +112,19 @@
     @JvmField @Rule var mockitoRule = MockitoJUnit.rule()
 
     @Mock private lateinit var authController: AuthController
-    @Mock private lateinit var applicationInfoWithIcon: ApplicationInfo
-    @Mock private lateinit var applicationInfoNoIcon: ApplicationInfo
+    @Mock private lateinit var applicationInfoWithIconAndDescription: ApplicationInfo
+    @Mock private lateinit var applicationInfoNoIconOrDescription: ApplicationInfo
     @Mock private lateinit var activityInfo: ActivityInfo
     @Mock private lateinit var runningTaskInfo: RunningTaskInfo
 
-    private val defaultLogoIcon = context.getDrawable(R.drawable.ic_android)
-    private val defaultLogoIconWithOverrides = context.getDrawable(R.drawable.ic_add)
+    private val defaultLogoIconFromAppInfo = context.getDrawable(R.drawable.ic_android)
+    private val defaultLogoIconFromActivityInfo = context.getDrawable(R.drawable.ic_add)
     private val logoResFromApp = R.drawable.ic_cake
     private val logoDrawableFromAppRes = context.getDrawable(logoResFromApp)
     private val logoBitmapFromApp = Bitmap.createBitmap(400, 400, Bitmap.Config.RGB_565)
-    private val defaultLogoDescription = "Test Android App"
+    private val defaultLogoDescriptionFromAppInfo = "Test Android App"
+    private val defaultLogoDescriptionFromActivityInfo = "Test Coke App"
     private val logoDescriptionFromApp = "Test Cake App"
-    private val packageNameForLogoWithOverrides = "should.use.overridden.logo"
     /** Prompt panel size padding */
     private val smallHorizontalGuidelinePadding =
         context.resources.getDimensionPixelSize(
@@ -172,16 +172,21 @@
 
         // Set up default logo info and app customized info
         whenever(kosmos.packageManager.getApplicationInfo(eq(OP_PACKAGE_NAME_NO_ICON), anyInt()))
-            .thenReturn(applicationInfoNoIcon)
-        whenever(kosmos.packageManager.getApplicationInfo(eq(OP_PACKAGE_NAME), anyInt()))
-            .thenReturn(applicationInfoWithIcon)
+            .thenReturn(applicationInfoNoIconOrDescription)
         whenever(
                 kosmos.packageManager.getApplicationInfo(
-                    eq(packageNameForLogoWithOverrides),
+                    eq(OP_PACKAGE_NAME_WITH_APP_LOGO),
                     anyInt()
                 )
             )
-            .thenReturn(applicationInfoWithIcon)
+            .thenReturn(applicationInfoWithIconAndDescription)
+        whenever(
+                kosmos.packageManager.getApplicationInfo(
+                    eq(OP_PACKAGE_NAME_WITH_ACTIVITY_LOGO),
+                    anyInt()
+                )
+            )
+            .thenReturn(applicationInfoWithIconAndDescription)
         whenever(
                 kosmos.packageManager.getApplicationInfo(
                     eq(OP_PACKAGE_NAME_CAN_NOT_BE_FOUND),
@@ -191,19 +196,27 @@
             .thenThrow(NameNotFoundException())
 
         whenever(kosmos.packageManager.getActivityInfo(any(), anyInt())).thenReturn(activityInfo)
-        whenever(kosmos.iconProvider.getIcon(activityInfo)).thenReturn(defaultLogoIconWithOverrides)
-        whenever(kosmos.packageManager.getApplicationIcon(applicationInfoWithIcon))
-            .thenReturn(defaultLogoIcon)
-        whenever(kosmos.packageManager.getApplicationLabel(applicationInfoWithIcon))
-            .thenReturn(defaultLogoDescription)
+        whenever(kosmos.iconProvider.getIcon(activityInfo))
+            .thenReturn(defaultLogoIconFromActivityInfo)
+        whenever(activityInfo.loadLabel(kosmos.packageManager))
+            .thenReturn(defaultLogoDescriptionFromActivityInfo)
+
+        whenever(kosmos.packageManager.getApplicationIcon(applicationInfoWithIconAndDescription))
+            .thenReturn(defaultLogoIconFromAppInfo)
+        whenever(kosmos.packageManager.getApplicationLabel(applicationInfoWithIconAndDescription))
+            .thenReturn(defaultLogoDescriptionFromAppInfo)
+        whenever(kosmos.packageManager.getApplicationIcon(applicationInfoNoIconOrDescription))
+            .thenReturn(null)
+        whenever(kosmos.packageManager.getApplicationLabel(applicationInfoNoIconOrDescription))
+            .thenReturn("")
         whenever(kosmos.packageManager.getUserBadgedIcon(any(), any())).then { it.getArgument(0) }
         whenever(kosmos.packageManager.getUserBadgedLabel(any(), any())).then { it.getArgument(0) }
 
         context.setMockPackageManager(kosmos.packageManager)
         overrideResource(logoResFromApp, logoDrawableFromAppRes)
         overrideResource(
-            R.array.biometric_dialog_package_names_for_logo_with_overrides,
-            arrayOf(packageNameForLogoWithOverrides)
+            R.array.config_useActivityLogoForBiometricPrompt,
+            arrayOf(OP_PACKAGE_NAME_WITH_ACTIVITY_LOGO)
         )
 
         overrideResource(R.dimen.biometric_dialog_fingerprint_icon_width, mockFingerprintIconWidth)
@@ -491,7 +504,7 @@
                         assertThat(iconAsset)
                             .isEqualTo(R.raw.fingerprint_dialogue_error_to_unlock_lottie)
                         assertThat(iconContentDescriptionId)
-                            .isEqualTo(R.string.fingerprint_dialog_authenticated_confirmation)
+                            .isEqualTo(R.string.biometric_dialog_confirm)
                         assertThat(shouldAnimateIconView).isEqualTo(true)
 
                         // Confirm authentication
@@ -601,7 +614,7 @@
                             .isEqualTo(R.raw.fingerprint_dialogue_fingerprint_to_unlock_lottie)
                         assertThat(iconOverlayAsset).isEqualTo(-1)
                         assertThat(iconContentDescriptionId)
-                            .isEqualTo(R.string.fingerprint_dialog_authenticated_confirmation)
+                            .isEqualTo(R.string.biometric_dialog_confirm)
                         assertThat(shouldAnimateIconView).isEqualTo(true)
                         assertThat(shouldAnimateIconOverlay).isEqualTo(false)
                     }
@@ -1356,7 +1369,6 @@
     }
 
     @Test
-    @EnableFlags(FLAG_BP_TALKBACK)
     fun hint_for_talkback_guidance() = runGenericTest {
         val hint by collectLastValue(kosmos.promptViewModel.accessibilityHint)
 
@@ -1379,6 +1391,27 @@
     }
 
     @Test
+    fun no_hint_for_talkback_guidance_after_auth() = runGenericTest {
+        val hint by collectLastValue(kosmos.promptViewModel.accessibilityHint)
+
+        kosmos.promptViewModel.showAuthenticated(testCase.authenticatedModality, 0)
+        kosmos.promptViewModel.confirmAuthenticated()
+
+        // Touches should fall outside of sensor area
+        whenever(kosmos.udfpsUtils.getTouchInNativeCoordinates(any(), any(), any()))
+            .thenReturn(Point(0, 0))
+        whenever(kosmos.udfpsUtils.onTouchOutsideOfSensorArea(any(), any(), any(), any(), any()))
+            .thenReturn("Direction")
+
+        kosmos.promptViewModel.onAnnounceAccessibilityHint(
+            obtainMotionEvent(MotionEvent.ACTION_HOVER_ENTER),
+            true
+        )
+
+        assertThat(hint.isNullOrBlank()).isTrue()
+    }
+
+    @Test
     @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP)
     fun descriptionOverriddenByVerticalListContentView() =
         runGenericTest(description = "test description", contentView = promptContentView) {
@@ -1418,36 +1451,41 @@
     @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP)
     fun logo_nullIfPkgNameNotFound() =
         runGenericTest(packageName = OP_PACKAGE_NAME_CAN_NOT_BE_FOUND) {
-            val logo by collectLastValue(kosmos.promptViewModel.logo)
-            assertThat(logo).isNull()
+            val logoInfo by collectLastValue(kosmos.promptViewModel.logoInfo)
+            assertThat(logoInfo).isNotNull()
+            assertThat(logoInfo!!.first).isNull()
         }
 
     @Test
     @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP)
-    fun logo_defaultWithOverrides() =
-        runGenericTest(packageName = packageNameForLogoWithOverrides) {
-            val logo by collectLastValue(kosmos.promptViewModel.logo)
+    fun logo_defaultFromActivityInfo() =
+        runGenericTest(packageName = OP_PACKAGE_NAME_WITH_ACTIVITY_LOGO) {
+            val logoInfo by collectLastValue(kosmos.promptViewModel.logoInfo)
 
-            // 1. PM.getApplicationInfo(packageNameForLogoWithOverrides) is set to return
-            // applicationInfoWithIcon with defaultLogoIcon,
-            // 2. iconProvider.getIcon() is set to return defaultLogoIconForGMSCore
-            // For the apps with packageNameForLogoWithOverrides, 2 should be called instead of 1
-            assertThat(logo).isEqualTo(defaultLogoIconWithOverrides)
+            // 1. PM.getApplicationInfo(OP_PACKAGE_NAME_WITH_ACTIVITY_LOGO) is set to return
+            // applicationInfoWithIconAndDescription with "defaultLogoIconFromAppInfo",
+            // 2. iconProvider.getIcon(activityInfo) is set to return
+            // "defaultLogoIconFromActivityInfo"
+            // For the apps with OP_PACKAGE_NAME_WITH_ACTIVITY_LOGO, 2 should be called instead of 1
+            assertThat(logoInfo).isNotNull()
+            assertThat(logoInfo!!.first).isEqualTo(defaultLogoIconFromActivityInfo)
         }
 
     @Test
     @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP)
     fun logo_defaultIsNull() =
         runGenericTest(packageName = OP_PACKAGE_NAME_NO_ICON) {
-            val logo by collectLastValue(kosmos.promptViewModel.logo)
-            assertThat(logo).isNull()
+            val logoInfo by collectLastValue(kosmos.promptViewModel.logoInfo)
+            assertThat(logoInfo).isNotNull()
+            assertThat(logoInfo!!.first).isNull()
         }
 
     @Test
     @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP)
     fun logo_default() = runGenericTest {
-        val logo by collectLastValue(kosmos.promptViewModel.logo)
-        assertThat(logo).isEqualTo(defaultLogoIcon)
+        val logoInfo by collectLastValue(kosmos.promptViewModel.logoInfo)
+        assertThat(logoInfo).isNotNull()
+        assertThat(logoInfo!!.first).isEqualTo(defaultLogoIconFromAppInfo)
     }
 
     @Test
@@ -1455,47 +1493,60 @@
     fun logo_resSetByApp() =
         runGenericTest(logoRes = logoResFromApp) {
             val expectedBitmap = context.getDrawable(logoResFromApp).toBitmap()
-            val logo by collectLastValue(kosmos.promptViewModel.logo)
-            assertThat((logo as BitmapDrawable).bitmap.sameAs(expectedBitmap)).isTrue()
+            val logoInfo by collectLastValue(kosmos.promptViewModel.logoInfo)
+            assertThat(logoInfo).isNotNull()
+            assertThat((logoInfo!!.first as BitmapDrawable).bitmap.sameAs(expectedBitmap)).isTrue()
         }
 
     @Test
     @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP)
     fun logo_bitmapSetByApp() =
         runGenericTest(logoBitmap = logoBitmapFromApp) {
-            val logo by collectLastValue(kosmos.promptViewModel.logo)
-            assertThat((logo as BitmapDrawable).bitmap).isEqualTo(logoBitmapFromApp)
+            val logoInfo by collectLastValue(kosmos.promptViewModel.logoInfo)
+            assertThat((logoInfo!!.first as BitmapDrawable).bitmap).isEqualTo(logoBitmapFromApp)
         }
 
     @Test
     @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP)
     fun logoDescription_emptyIfPkgNameNotFound() =
         runGenericTest(packageName = OP_PACKAGE_NAME_CAN_NOT_BE_FOUND) {
-            val logoDescription by collectLastValue(kosmos.promptViewModel.logoDescription)
-            assertThat(logoDescription).isEqualTo("")
+            val logoInfo by collectLastValue(kosmos.promptViewModel.logoInfo)
+            assertThat(logoInfo!!.second).isEqualTo("")
+        }
+
+    @Test
+    @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP)
+    fun logoDescription_defaultFromActivityInfo() =
+        runGenericTest(packageName = OP_PACKAGE_NAME_WITH_ACTIVITY_LOGO) {
+            val logoInfo by collectLastValue(kosmos.promptViewModel.logoInfo)
+            // 1. PM.getApplicationInfo(packageNameForLogoWithOverrides) is set to return
+            // applicationInfoWithIconAndDescription with defaultLogoDescription,
+            // 2. activityInfo.loadLabel() is set to return defaultLogoDescriptionWithOverrides
+            // For the apps with packageNameForLogoWithOverrides, 2 should be called instead of 1
+            assertThat(logoInfo!!.second).isEqualTo(defaultLogoDescriptionFromActivityInfo)
         }
 
     @Test
     @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP)
     fun logoDescription_defaultIsEmpty() =
         runGenericTest(packageName = OP_PACKAGE_NAME_NO_ICON) {
-            val logoDescription by collectLastValue(kosmos.promptViewModel.logoDescription)
-            assertThat(logoDescription).isEqualTo("")
+            val logoInfo by collectLastValue(kosmos.promptViewModel.logoInfo)
+            assertThat(logoInfo!!.second).isEqualTo("")
         }
 
     @Test
     @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP)
     fun logoDescription_default() = runGenericTest {
-        val logoDescription by collectLastValue(kosmos.promptViewModel.logoDescription)
-        assertThat(logoDescription).isEqualTo(defaultLogoDescription)
+        val logoInfo by collectLastValue(kosmos.promptViewModel.logoInfo)
+        assertThat(logoInfo!!.second).isEqualTo(defaultLogoDescriptionFromAppInfo)
     }
 
     @Test
     @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP)
     fun logoDescription_setByApp() =
         runGenericTest(logoDescription = logoDescriptionFromApp) {
-            val logoDescription by collectLastValue(kosmos.promptViewModel.logoDescription)
-            assertThat(logoDescription).isEqualTo(logoDescriptionFromApp)
+            val logoInfo by collectLastValue(kosmos.promptViewModel.logoInfo)
+            assertThat(logoInfo!!.second).isEqualTo(logoDescriptionFromApp)
         }
 
     @Test
@@ -1670,7 +1721,7 @@
         logoRes: Int = 0,
         logoBitmap: Bitmap? = null,
         logoDescription: String? = null,
-        packageName: String = OP_PACKAGE_NAME,
+        packageName: String = OP_PACKAGE_NAME_WITH_APP_LOGO,
         block: suspend TestScope.() -> Unit,
     ) {
         val topActivity = ComponentName(packageName, "test app")
@@ -1929,7 +1980,7 @@
     logoResFromApp: Int = 0,
     logoBitmapFromApp: Bitmap? = null,
     logoDescriptionFromApp: String? = null,
-    packageName: String = OP_PACKAGE_NAME,
+    packageName: String = OP_PACKAGE_NAME_WITH_APP_LOGO,
 ) {
     val info =
         PromptInfo().apply {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/common/ui/domain/interactor/ConfigurationInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/common/ui/domain/interactor/ConfigurationInteractorTest.kt
index 63b4ff7..72e0726 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/common/ui/domain/interactor/ConfigurationInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/common/ui/domain/interactor/ConfigurationInteractorTest.kt
@@ -18,6 +18,7 @@
 
 import android.content.res.Configuration
 import android.graphics.Rect
+import android.util.LayoutDirection
 import android.view.Surface
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
@@ -34,6 +35,8 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
 
 @ExperimentalCoroutinesApi
 @SmallTest
@@ -70,6 +73,28 @@
         }
 
     @Test
+    fun directionalDimensionPixelSize() =
+        testScope.runTest {
+            val resourceId = 1001
+            val pixelSize = 501
+            configurationRepository.setDimensionPixelSize(resourceId, pixelSize)
+
+            val config: Configuration = mock()
+            val dimensionPixelSize by
+                collectLastValue(
+                    underTest.directionalDimensionPixelSize(LayoutDirection.LTR, resourceId)
+                )
+
+            whenever(config.layoutDirection).thenReturn(LayoutDirection.LTR)
+            configurationRepository.onConfigurationChange(config)
+            assertThat(dimensionPixelSize).isEqualTo(pixelSize)
+
+            whenever(config.layoutDirection).thenReturn(LayoutDirection.RTL)
+            configurationRepository.onConfigurationChange(config)
+            assertThat(dimensionPixelSize).isEqualTo(-pixelSize)
+        }
+
+    @Test
     fun dimensionPixelSizes() =
         testScope.runTest {
             val resourceId1 = 1001
diff --git a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricsAllowedInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricsAllowedInteractorTest.kt
new file mode 100644
index 0000000..295a626
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricsAllowedInteractorTest.kt
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.deviceentry.domain.interactor
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.data.repository.FaceSensorInfo
+import com.android.systemui.biometrics.data.repository.fakeFacePropertyRepository
+import com.android.systemui.biometrics.data.repository.fakeFingerprintPropertyRepository
+import com.android.systemui.biometrics.shared.model.SensorStrength
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.keyguard.data.repository.fakeBiometricSettingsRepository
+import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFaceAuthRepository
+import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class DeviceEntryBiometricsAllowedInteractorTest : SysuiTestCase() {
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
+    private val underTest = kosmos.deviceEntryBiometricsAllowedInteractor
+
+    @Test
+    fun isFingerprintAuthCurrentlyAllowed_true() =
+        testScope.runTest {
+            val fpAllowed by collectLastValue(underTest.isFingerprintAuthCurrentlyAllowed)
+
+            // WHEN: not locked out, no face sensor, no strong auth requirements
+            kosmos.fakeDeviceEntryFingerprintAuthRepository.setLockedOut(false)
+            kosmos.fakeFacePropertyRepository.setSensorInfo(null)
+            kosmos.fakeBiometricSettingsRepository.setIsFingerprintAuthCurrentlyAllowed(true)
+
+            // THEN fp is allowed
+            assertThat(fpAllowed).isTrue()
+        }
+
+    @Test
+    fun isFingerprintAuthCurrentlyAllowed_strongFaceLockedOut() =
+        testScope.runTest {
+            val fpAllowed by collectLastValue(underTest.isFingerprintAuthCurrentlyAllowed)
+
+            // WHEN: not locked out, face is strong & locked out,  no strong auth requirements
+            kosmos.fakeDeviceEntryFingerprintAuthRepository.setLockedOut(false)
+            kosmos.fakeFacePropertyRepository.setSensorInfo(
+                FaceSensorInfo(
+                    id = 0,
+                    strength = SensorStrength.STRONG,
+                )
+            )
+            kosmos.fakeDeviceEntryFaceAuthRepository.setLockedOut(true)
+            kosmos.fakeBiometricSettingsRepository.setIsFingerprintAuthCurrentlyAllowed(true)
+
+            // THEN fp is NOT allowed
+            assertThat(fpAllowed).isFalse()
+        }
+
+    @Test
+    fun isFingerprintAuthCurrentlyAllowed_convenienceFaceLockedOut() =
+        testScope.runTest {
+            val fpAllowed by collectLastValue(underTest.isFingerprintAuthCurrentlyAllowed)
+
+            // WHEN: not locked out, face is convenience & locked out, no strong auth requirements
+            kosmos.fakeDeviceEntryFingerprintAuthRepository.setLockedOut(false)
+            kosmos.fakeFacePropertyRepository.setSensorInfo(
+                FaceSensorInfo(
+                    id = 0,
+                    strength = SensorStrength.CONVENIENCE,
+                )
+            )
+            kosmos.fakeDeviceEntryFaceAuthRepository.setLockedOut(true)
+            kosmos.fakeBiometricSettingsRepository.setIsFingerprintAuthCurrentlyAllowed(true)
+
+            // THEN fp is allowed
+            assertThat(fpAllowed).isTrue()
+        }
+
+    @Test
+    fun isFingerprintAuthCurrentlyAllowed_primaryAuthRequired() =
+        testScope.runTest {
+            val fpAllowed by collectLastValue(underTest.isFingerprintAuthCurrentlyAllowed)
+
+            // WHEN: not locked out, no face sensor,  no strong auth requirements
+            kosmos.fakeDeviceEntryFingerprintAuthRepository.setLockedOut(false)
+            kosmos.fakeBiometricSettingsRepository.setIsFingerprintAuthCurrentlyAllowed(false)
+
+            // THEN fp is NOT allowed
+            assertThat(fpAllowed).isFalse()
+        }
+
+    @Test
+    fun isFingerprintAuthCurrentlyAllowedOnBouncer_sfps() =
+        testScope.runTest {
+            val fpAllowedOnBouncer by
+                collectLastValue(underTest.isFingerprintCurrentlyAllowedOnBouncer)
+
+            // GIVEN fingerprint is generally allowed
+            kosmos.fakeDeviceEntryFingerprintAuthRepository.setLockedOut(false)
+            kosmos.fakeBiometricSettingsRepository.setIsFingerprintAuthCurrentlyAllowed(true)
+
+            // WHEN side fps
+            kosmos.fakeFingerprintPropertyRepository.supportsSideFps()
+
+            // THEN fp is allowed on the primary bouncer
+            assertThat(fpAllowedOnBouncer).isTrue()
+        }
+
+    @Test
+    fun isFingerprintAuthCurrentlyAllowedOnBouncer_rearFps() =
+        testScope.runTest {
+            val fpAllowedOnBouncer by
+                collectLastValue(underTest.isFingerprintCurrentlyAllowedOnBouncer)
+
+            // GIVEN fingerprint is generally allowed
+            kosmos.fakeDeviceEntryFingerprintAuthRepository.setLockedOut(false)
+            kosmos.fakeBiometricSettingsRepository.setIsFingerprintAuthCurrentlyAllowed(true)
+
+            // WHEN rear fps
+            kosmos.fakeFingerprintPropertyRepository.supportsRearFps()
+
+            // THEN fp is allowed on the primary bouncer
+            assertThat(fpAllowedOnBouncer).isTrue()
+        }
+
+    @Test
+    fun isFingerprintAuthCurrentlyAllowedOnBouncer_udfps() =
+        testScope.runTest {
+            val fpAllowedOnBouncer by
+                collectLastValue(underTest.isFingerprintCurrentlyAllowedOnBouncer)
+
+            // GIVEN fp is generally allowed
+            kosmos.fakeDeviceEntryFingerprintAuthRepository.setLockedOut(false)
+            kosmos.fakeBiometricSettingsRepository.setIsFingerprintAuthCurrentlyAllowed(true)
+
+            // WHEN UDFPS
+            kosmos.fakeFingerprintPropertyRepository.supportsUdfps()
+
+            // THEN fp is never allowed on the primary bouncer
+            assertThat(fpAllowedOnBouncer).isFalse()
+        }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
index aa5edae..86da203 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
@@ -42,15 +42,22 @@
 import static org.mockito.Mockito.when;
 
 import android.content.Intent;
+import android.hardware.display.DisplayManager;
 import android.os.PowerManager;
 import android.os.UserHandle;
+import android.platform.test.annotations.RequiresFlagsDisabled;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
 import android.provider.Settings;
+import android.view.Display;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
+import com.android.internal.display.BrightnessSynchronizer;
+import com.android.server.display.feature.flags.Flags;
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.dock.DockManager;
 import com.android.systemui.keyguard.WakefulnessLifecycle;
 import com.android.systemui.statusbar.phone.DozeParameters;
 import com.android.systemui.statusbar.policy.DevicePostureController;
@@ -62,6 +69,7 @@
 import com.android.systemui.util.time.FakeSystemClock;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
@@ -74,10 +82,15 @@
 @RunWith(AndroidJUnit4.class)
 public class DozeScreenBrightnessTest extends SysuiTestCase {
 
-    private static final int DEFAULT_BRIGHTNESS = 10;
-    private static final int DIM_BRIGHTNESS = 1;
-    private static final int[] SENSOR_TO_BRIGHTNESS = new int[]{-1, 1, 2, 3, 4};
+    private static final int DEFAULT_BRIGHTNESS_INT = 10;
+    private static final float DEFAULT_BRIGHTNESS_FLOAT = 0.1f;
+    private static final int DIM_BRIGHTNESS_INT = 1;
+    private static final float DIM_BRIGHTNESS_FLOAT = 0.05f;
+    private static final int[] SENSOR_TO_BRIGHTNESS_INT = new int[]{-1, 1, 2, 3, 4};
+    private static final float[] SENSOR_TO_BRIGHTNESS_FLOAT =
+            new float[]{-1, 0.01f, 0.05f, 0.7f, 0.1f};
     private static final int[] SENSOR_TO_OPACITY = new int[]{-1, 10, 0, 0, 0};
+    private static final float DELTA = BrightnessSynchronizer.EPSILON;
 
     private DozeServiceFake mServiceFake;
     private FakeSensorManager.FakeGenericSensor mSensor;
@@ -91,23 +104,28 @@
     @Mock
     DozeParameters mDozeParameters;
     @Mock
-    DockManager mDockManager;
-    @Mock
     DevicePostureController mDevicePostureController;
     @Mock
     DozeLog mDozeLog;
     @Mock
     SystemSettings mSystemSettings;
-    private FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock());
-    private FakeThreadFactory mFakeThreadFactory = new FakeThreadFactory(mFakeExecutor);
+    @Mock
+    DisplayManager mDisplayManager;
+    private final FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock());
+    private final FakeThreadFactory mFakeThreadFactory = new FakeThreadFactory(mFakeExecutor);
 
     private DozeScreenBrightness mScreen;
 
+    @Rule
+    public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
         when(mSystemSettings.getIntForUser(eq(Settings.System.SCREEN_BRIGHTNESS), anyInt(),
-                eq(UserHandle.USER_CURRENT))).thenReturn(DEFAULT_BRIGHTNESS);
+                eq(UserHandle.USER_CURRENT))).thenReturn(PowerManager.BRIGHTNESS_ON);
+        when(mDisplayManager.getBrightness(Display.DEFAULT_DISPLAY))
+                .thenReturn(PowerManager.BRIGHTNESS_MAX);
         doAnswer(invocation -> {
             ((Runnable) invocation.getArgument(0)).run();
             return null;
@@ -117,9 +135,14 @@
         mSensorManager = new AsyncSensorManager(fakeSensorManager, mFakeThreadFactory, null);
 
         mAlwaysOnDisplayPolicy = new AlwaysOnDisplayPolicy(mContext);
-        mAlwaysOnDisplayPolicy.defaultDozeBrightness = DEFAULT_BRIGHTNESS;
-        mAlwaysOnDisplayPolicy.screenBrightnessArray = SENSOR_TO_BRIGHTNESS;
-        mAlwaysOnDisplayPolicy.dimBrightness = DIM_BRIGHTNESS;
+        mAlwaysOnDisplayPolicy.defaultDozeBrightness = DEFAULT_BRIGHTNESS_INT;
+        when(mDisplayManager.getDefaultDozeBrightness(Display.DEFAULT_DISPLAY))
+                .thenReturn(DEFAULT_BRIGHTNESS_FLOAT);
+        mAlwaysOnDisplayPolicy.screenBrightnessArray = SENSOR_TO_BRIGHTNESS_INT;
+        when(mDisplayManager.getDozeBrightnessSensorValueToBrightness(Display.DEFAULT_DISPLAY))
+                .thenReturn(SENSOR_TO_BRIGHTNESS_FLOAT);
+        mAlwaysOnDisplayPolicy.dimBrightness = DIM_BRIGHTNESS_INT;
+        mAlwaysOnDisplayPolicy.dimBrightnessFloat = DIM_BRIGHTNESS_FLOAT;
         mAlwaysOnDisplayPolicy.dimmingScrimArray = SENSOR_TO_OPACITY;
         mSensor = fakeSensorManager.getFakeLightSensor();
         mSensorInner = fakeSensorManager.getFakeLightSensor2();
@@ -135,19 +158,35 @@
                 mDozeParameters,
                 mDevicePostureController,
                 mDozeLog,
-                mSystemSettings);
+                mSystemSettings,
+                mDisplayManager);
     }
 
     @Test
-    public void testInitialize_setsScreenBrightnessToValidValue() throws Exception {
+    @RequiresFlagsDisabled(Flags.FLAG_DOZE_BRIGHTNESS_FLOAT)
+    public void testInitialize_setsScreenBrightnessToValidValue_Int() {
         mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
 
-        assertEquals(DEFAULT_BRIGHTNESS, mServiceFake.screenBrightness);
-        assertTrue(mServiceFake.screenBrightness <= PowerManager.BRIGHTNESS_ON);
+        assertEquals(DEFAULT_BRIGHTNESS_INT, mServiceFake.screenBrightnessInt);
+        assertTrue(mServiceFake.screenBrightnessInt >= PowerManager.BRIGHTNESS_OFF + 1);
+        assertTrue(mServiceFake.screenBrightnessInt <= PowerManager.BRIGHTNESS_ON);
+        assertTrue(Float.isNaN(mServiceFake.screenBrightnessFloat));
     }
 
     @Test
-    public void testAod_usesDebugValue() throws Exception {
+    @RequiresFlagsEnabled(Flags.FLAG_DOZE_BRIGHTNESS_FLOAT)
+    public void testInitialize_setsScreenBrightnessToValidValue_Float() {
+        mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
+
+        assertEquals(DEFAULT_BRIGHTNESS_FLOAT, mServiceFake.screenBrightnessFloat, DELTA);
+        assertTrue(mServiceFake.screenBrightnessFloat >= PowerManager.BRIGHTNESS_MIN);
+        assertTrue(mServiceFake.screenBrightnessFloat <= PowerManager.BRIGHTNESS_MAX);
+        assertEquals(PowerManager.BRIGHTNESS_DEFAULT, mServiceFake.screenBrightnessInt);
+    }
+
+    @Test
+    @RequiresFlagsDisabled(Flags.FLAG_DOZE_BRIGHTNESS_FLOAT)
+    public void testAod_usesDebugValue_Int() {
         mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
         mScreen.transitionTo(INITIALIZED, DOZE_AOD);
         waitForSensorManager();
@@ -157,11 +196,29 @@
         mScreen.onReceive(mContext, intent);
         mSensor.sendSensorEvent(3);
 
-        assertEquals(1, mServiceFake.screenBrightness);
+        assertEquals(SENSOR_TO_BRIGHTNESS_INT[1], mServiceFake.screenBrightnessInt);
+        assertTrue(Float.isNaN(mServiceFake.screenBrightnessFloat));
     }
 
     @Test
-    public void testAod_usesLightSensorRespectingUserSetting() {
+    @RequiresFlagsEnabled(Flags.FLAG_DOZE_BRIGHTNESS_FLOAT)
+    public void testAod_usesDebugValue_Float() {
+        mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
+        mScreen.transitionTo(INITIALIZED, DOZE_AOD);
+        waitForSensorManager();
+
+        Intent intent = new Intent(DozeScreenBrightness.ACTION_AOD_BRIGHTNESS);
+        intent.putExtra(DozeScreenBrightness.BRIGHTNESS_BUCKET, 1);
+        mScreen.onReceive(mContext, intent);
+        mSensor.sendSensorEvent(3);
+
+        assertEquals(SENSOR_TO_BRIGHTNESS_FLOAT[1], mServiceFake.screenBrightnessFloat, DELTA);
+        assertEquals(PowerManager.BRIGHTNESS_DEFAULT, mServiceFake.screenBrightnessInt);
+    }
+
+    @Test
+    @RequiresFlagsDisabled(Flags.FLAG_DOZE_BRIGHTNESS_FLOAT)
+    public void testAod_usesLightSensorRespectingUserSetting_Int() {
         int maxBrightness = 3;
         when(mSystemSettings.getIntForUser(eq(Settings.System.SCREEN_BRIGHTNESS), anyInt(),
                 eq(UserHandle.USER_CURRENT))).thenReturn(maxBrightness);
@@ -170,24 +227,59 @@
                 .thenReturn(Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL);
 
         mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
-        assertEquals(maxBrightness, mServiceFake.screenBrightness);
+        assertEquals(maxBrightness, mServiceFake.screenBrightnessInt);
+        assertTrue(Float.isNaN(mServiceFake.screenBrightnessFloat));
     }
 
     @Test
-    public void testAod_usesLightSensorNotClampingToAutoBrightnessValue() {
-        int maxBrightness = 3;
-        when(mSystemSettings.getIntForUser(eq(Settings.System.SCREEN_BRIGHTNESS), anyInt(),
-                eq(UserHandle.USER_CURRENT))).thenReturn(maxBrightness);
+    @RequiresFlagsEnabled(Flags.FLAG_DOZE_BRIGHTNESS_FLOAT)
+    public void testAod_usesLightSensorRespectingUserSetting_Float() {
+        float maxBrightness = DEFAULT_BRIGHTNESS_FLOAT / 2;
+        when(mDisplayManager.getBrightness(Display.DEFAULT_DISPLAY)).thenReturn(maxBrightness);
+        when(mSystemSettings.getIntForUser(eq(Settings.System.SCREEN_BRIGHTNESS_MODE), anyInt(),
+                eq(UserHandle.USER_CURRENT)))
+                .thenReturn(Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL);
+
+        mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
+        assertEquals(maxBrightness, mServiceFake.screenBrightnessFloat, DELTA);
+        assertEquals(PowerManager.BRIGHTNESS_DEFAULT, mServiceFake.screenBrightnessInt);
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_DOZE_BRIGHTNESS_FLOAT)
+    public void initialBrightness_clampsToAutoBrightnessValue_Float() {
+        float maxBrightnessFromAutoBrightness = DEFAULT_BRIGHTNESS_FLOAT / 2;
+        when(mDisplayManager.getBrightness(Display.DEFAULT_DISPLAY)).thenReturn(
+                maxBrightnessFromAutoBrightness
+        );
         when(mSystemSettings.getIntForUser(eq(Settings.System.SCREEN_BRIGHTNESS_MODE), anyInt(),
                 eq(UserHandle.USER_CURRENT)))
                 .thenReturn(Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
 
         mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
-        assertEquals(DEFAULT_BRIGHTNESS, mServiceFake.screenBrightness);
+        assertEquals(maxBrightnessFromAutoBrightness, mServiceFake.screenBrightnessFloat,
+                DELTA);
+        assertEquals(PowerManager.BRIGHTNESS_DEFAULT, mServiceFake.screenBrightnessInt);
     }
 
     @Test
-    public void doze_doesNotUseLightSensor() {
+    @RequiresFlagsDisabled(Flags.FLAG_DOZE_BRIGHTNESS_FLOAT)
+    public void initialBrightness_clampsToAutoBrightnessValue_Int() {
+        int maxBrightnessFromAutoBrightness = DEFAULT_BRIGHTNESS_INT / 2;
+        when(mSystemSettings.getIntForUser(eq(Settings.System.SCREEN_BRIGHTNESS), anyInt(),
+                eq(UserHandle.USER_CURRENT))).thenReturn(maxBrightnessFromAutoBrightness);
+        when(mSystemSettings.getIntForUser(eq(Settings.System.SCREEN_BRIGHTNESS_MODE), anyInt(),
+                eq(UserHandle.USER_CURRENT)))
+                .thenReturn(Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
+
+        mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
+        assertEquals(maxBrightnessFromAutoBrightness, mServiceFake.screenBrightnessInt);
+        assertTrue(Float.isNaN(mServiceFake.screenBrightnessFloat));
+    }
+
+    @Test
+    @RequiresFlagsDisabled(Flags.FLAG_DOZE_BRIGHTNESS_FLOAT)
+    public void doze_doesNotUseLightSensor_Int() {
         // GIVEN the device is DOZE and the display state changes to ON
         mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
         mScreen.transitionTo(INITIALIZED, DOZE);
@@ -197,12 +289,31 @@
         mSensor.sendSensorEvent(3);
 
         // THEN brightness is NOT changed, it's set to the default brightness
-        assertNotSame(3, mServiceFake.screenBrightness);
-        assertEquals(DEFAULT_BRIGHTNESS, mServiceFake.screenBrightness);
+        assertNotSame(SENSOR_TO_BRIGHTNESS_INT[3], mServiceFake.screenBrightnessInt);
+        assertEquals(DEFAULT_BRIGHTNESS_INT, mServiceFake.screenBrightnessInt);
+        assertTrue(Float.isNaN(mServiceFake.screenBrightnessFloat));
     }
 
     @Test
-    public void dozeSuspendTriggers_doesNotUseLightSensor() {
+    @RequiresFlagsEnabled(Flags.FLAG_DOZE_BRIGHTNESS_FLOAT)
+    public void doze_doesNotUseLightSensor_Float() {
+        // GIVEN the device is DOZE and the display state changes to ON
+        mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
+        mScreen.transitionTo(INITIALIZED, DOZE);
+        waitForSensorManager();
+
+        // WHEN new sensor event sent
+        mSensor.sendSensorEvent(3);
+
+        // THEN brightness is NOT changed, it's set to the default brightness
+        assertNotSame(SENSOR_TO_BRIGHTNESS_FLOAT[3], mServiceFake.screenBrightnessInt);
+        assertEquals(DEFAULT_BRIGHTNESS_FLOAT, mServiceFake.screenBrightnessFloat, DELTA);
+        assertEquals(PowerManager.BRIGHTNESS_DEFAULT, mServiceFake.screenBrightnessInt);
+    }
+
+    @Test
+    @RequiresFlagsDisabled(Flags.FLAG_DOZE_BRIGHTNESS_FLOAT)
+    public void dozeSuspendTriggers_doesNotUseLightSensor_Int() {
         // GIVEN the device is DOZE and the display state changes to ON
         mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
         mScreen.transitionTo(INITIALIZED, DOZE_SUSPEND_TRIGGERS);
@@ -212,12 +323,31 @@
         mSensor.sendSensorEvent(3);
 
         // THEN brightness is NOT changed, it's set to the default brightness
-        assertNotSame(3, mServiceFake.screenBrightness);
-        assertEquals(DEFAULT_BRIGHTNESS, mServiceFake.screenBrightness);
+        assertNotSame(SENSOR_TO_BRIGHTNESS_INT[3], mServiceFake.screenBrightnessInt);
+        assertEquals(DEFAULT_BRIGHTNESS_INT, mServiceFake.screenBrightnessInt);
+        assertTrue(Float.isNaN(mServiceFake.screenBrightnessFloat));
     }
 
     @Test
-    public void aod_usesLightSensor() {
+    @RequiresFlagsEnabled(Flags.FLAG_DOZE_BRIGHTNESS_FLOAT)
+    public void dozeSuspendTriggers_doesNotUseLightSensor_Float() {
+        // GIVEN the device is DOZE and the display state changes to ON
+        mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
+        mScreen.transitionTo(INITIALIZED, DOZE_SUSPEND_TRIGGERS);
+        waitForSensorManager();
+
+        // WHEN new sensor event sent
+        mSensor.sendSensorEvent(3);
+
+        // THEN brightness is NOT changed, it's set to the default brightness
+        assertNotSame(SENSOR_TO_BRIGHTNESS_FLOAT[3], mServiceFake.screenBrightnessFloat);
+        assertEquals(DEFAULT_BRIGHTNESS_FLOAT, mServiceFake.screenBrightnessFloat, DELTA);
+        assertEquals(PowerManager.BRIGHTNESS_DEFAULT, mServiceFake.screenBrightnessInt);
+    }
+
+    @Test
+    @RequiresFlagsDisabled(Flags.FLAG_DOZE_BRIGHTNESS_FLOAT)
+    public void aod_usesLightSensor_Int() {
         // GIVEN the device is DOZE_AOD and the display state changes to ON
         mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
         mScreen.transitionTo(INITIALIZED, DOZE_AOD);
@@ -227,11 +357,77 @@
         mSensor.sendSensorEvent(3);
 
         // THEN brightness is updated
-        assertEquals(3, mServiceFake.screenBrightness);
+        assertEquals(SENSOR_TO_BRIGHTNESS_INT[3], mServiceFake.screenBrightnessInt);
+        assertTrue(Float.isNaN(mServiceFake.screenBrightnessFloat));
     }
 
     @Test
-    public void docked_usesLightSensor() {
+    @RequiresFlagsEnabled(Flags.FLAG_DOZE_BRIGHTNESS_FLOAT)
+    public void aod_usesLightSensor_Float() {
+        // GIVEN the device is DOZE_AOD and the display state changes to ON
+        mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
+        mScreen.transitionTo(INITIALIZED, DOZE_AOD);
+        waitForSensorManager();
+
+        // WHEN new sensor event sent
+        mSensor.sendSensorEvent(3);
+
+        // THEN brightness is updated
+        assertEquals(SENSOR_TO_BRIGHTNESS_FLOAT[3], mServiceFake.screenBrightnessFloat, DELTA);
+        assertEquals(PowerManager.BRIGHTNESS_DEFAULT, mServiceFake.screenBrightnessInt);
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_DOZE_BRIGHTNESS_FLOAT)
+    public void lightSensorChangesInAod_doesNotClampToAutoBrightnessValue_Float() {
+        // GIVEN auto brightness reports low brightness
+        float maxBrightnessFromAutoBrightness = DEFAULT_BRIGHTNESS_FLOAT / 2;
+        when(mDisplayManager.getBrightness(Display.DEFAULT_DISPLAY))
+                .thenReturn(maxBrightnessFromAutoBrightness);
+        when(mSystemSettings.getIntForUser(eq(Settings.System.SCREEN_BRIGHTNESS_MODE), anyInt(),
+                eq(UserHandle.USER_CURRENT)))
+                .thenReturn(Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
+
+        // GIVEN the device is DOZE_AOD and the display state changes to ON
+        mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
+        mScreen.transitionTo(INITIALIZED, DOZE_AOD);
+        waitForSensorManager();
+
+        // WHEN new sensor event sent
+        mSensor.sendSensorEvent(3);
+
+        // THEN brightness is updated
+        assertEquals(SENSOR_TO_BRIGHTNESS_FLOAT[3], mServiceFake.screenBrightnessFloat, DELTA);
+        assertEquals(PowerManager.BRIGHTNESS_DEFAULT, mServiceFake.screenBrightnessInt);
+    }
+
+    @Test
+    @RequiresFlagsDisabled(Flags.FLAG_DOZE_BRIGHTNESS_FLOAT)
+    public void lightSensorChangesInAod_doesNotClampToAutoBrightnessValue_Int() {
+        // GIVEN auto brightness reports low brightness
+        int maxBrightnessFromAutoBrightness = 1;
+        when(mSystemSettings.getIntForUser(eq(Settings.System.SCREEN_BRIGHTNESS), anyInt(),
+                eq(UserHandle.USER_CURRENT))).thenReturn(maxBrightnessFromAutoBrightness);
+        when(mSystemSettings.getIntForUser(eq(Settings.System.SCREEN_BRIGHTNESS_MODE), anyInt(),
+                eq(UserHandle.USER_CURRENT)))
+                .thenReturn(Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
+
+        // GIVEN the device is DOZE_AOD and the display state changes to ON
+        mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
+        mScreen.transitionTo(INITIALIZED, DOZE_AOD);
+        waitForSensorManager();
+
+        // WHEN new sensor event sent
+        mSensor.sendSensorEvent(3);
+
+        // THEN brightness is updated
+        assertEquals(SENSOR_TO_BRIGHTNESS_INT[3], mServiceFake.screenBrightnessInt);
+        assertTrue(Float.isNaN(mServiceFake.screenBrightnessFloat));
+    }
+
+    @Test
+    @RequiresFlagsDisabled(Flags.FLAG_DOZE_BRIGHTNESS_FLOAT)
+    public void docked_usesLightSensor_Int() {
         // GIVEN the device is docked and the display state changes to ON
         mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
         mScreen.transitionTo(INITIALIZED, DOZE_AOD);
@@ -242,11 +438,29 @@
         mSensor.sendSensorEvent(3);
 
         // THEN brightness is updated
-        assertEquals(3, mServiceFake.screenBrightness);
+        assertEquals(SENSOR_TO_BRIGHTNESS_INT[3], mServiceFake.screenBrightnessInt);
+        assertTrue(Float.isNaN(mServiceFake.screenBrightnessFloat));
     }
 
     @Test
-    public void testPulsing_withoutLightSensor_setsAoDDimmingScrimTransparent() throws Exception {
+    @RequiresFlagsEnabled(Flags.FLAG_DOZE_BRIGHTNESS_FLOAT)
+    public void docked_usesLightSensor_Float() {
+        // GIVEN the device is docked and the display state changes to ON
+        mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
+        mScreen.transitionTo(INITIALIZED, DOZE_AOD);
+        mScreen.transitionTo(DOZE_AOD, DOZE_AOD_DOCKED);
+        waitForSensorManager();
+
+        // WHEN new sensor event sent
+        mSensor.sendSensorEvent(3);
+
+        // THEN brightness is updated
+        assertEquals(SENSOR_TO_BRIGHTNESS_FLOAT[3], mServiceFake.screenBrightnessFloat, DELTA);
+        assertEquals(PowerManager.BRIGHTNESS_DEFAULT, mServiceFake.screenBrightnessInt);
+    }
+
+    @Test
+    public void testPulsing_withoutLightSensor_setsAoDDimmingScrimTransparent() {
         mScreen = new DozeScreenBrightness(
                 mContext,
                 mServiceFake,
@@ -258,7 +472,8 @@
                 mDozeParameters,
                 mDevicePostureController,
                 mDozeLog,
-                mSystemSettings);
+                mSystemSettings,
+                mDisplayManager);
         mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
         mScreen.transitionTo(INITIALIZED, DOZE);
         reset(mDozeHost);
@@ -269,7 +484,8 @@
     }
 
     @Test
-    public void testScreenOffAfterPulsing_pausesLightSensor() throws Exception {
+    @RequiresFlagsDisabled(Flags.FLAG_DOZE_BRIGHTNESS_FLOAT)
+    public void testScreenOffAfterPulsing_pausesLightSensor_Int() {
         mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
         mScreen.transitionTo(INITIALIZED, DOZE);
         mScreen.transitionTo(DOZE, DOZE_REQUEST_PULSE);
@@ -280,11 +496,29 @@
 
         mSensor.sendSensorEvent(1);
 
-        assertEquals(DEFAULT_BRIGHTNESS, mServiceFake.screenBrightness);
+        assertEquals(DEFAULT_BRIGHTNESS_INT, mServiceFake.screenBrightnessInt);
+        assertTrue(Float.isNaN(mServiceFake.screenBrightnessFloat));
     }
 
     @Test
-    public void testNullSensor() throws Exception {
+    @RequiresFlagsEnabled(Flags.FLAG_DOZE_BRIGHTNESS_FLOAT)
+    public void testScreenOffAfterPulsing_pausesLightSensor_Float() {
+        mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
+        mScreen.transitionTo(INITIALIZED, DOZE);
+        mScreen.transitionTo(DOZE, DOZE_REQUEST_PULSE);
+        mScreen.transitionTo(DOZE_REQUEST_PULSE, DOZE_PULSING);
+        mScreen.transitionTo(DOZE_PULSING, DOZE_PULSE_DONE);
+        mScreen.transitionTo(DOZE_PULSE_DONE, DOZE);
+        waitForSensorManager();
+
+        mSensor.sendSensorEvent(1);
+
+        assertEquals(DEFAULT_BRIGHTNESS_FLOAT, mServiceFake.screenBrightnessFloat, DELTA);
+        assertEquals(PowerManager.BRIGHTNESS_DEFAULT, mServiceFake.screenBrightnessInt);
+    }
+
+    @Test
+    public void testNullSensor() {
         mScreen = new DozeScreenBrightness(
                 mContext,
                 mServiceFake,
@@ -296,7 +530,8 @@
                 mDozeParameters,
                 mDevicePostureController,
                 mDozeLog,
-                mSystemSettings);
+                mSystemSettings,
+                mDisplayManager);
 
         mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
         mScreen.transitionTo(INITIALIZED, DOZE_AOD);
@@ -305,7 +540,8 @@
     }
 
     @Test
-    public void testSensorsSupportPostures_closed() throws Exception {
+    @RequiresFlagsDisabled(Flags.FLAG_DOZE_BRIGHTNESS_FLOAT)
+    public void testSensorsSupportPostures_closed_Int() {
         // GIVEN the device is CLOSED
         when(mDevicePostureController.getDevicePosture()).thenReturn(
                 DevicePostureController.DEVICE_POSTURE_CLOSED);
@@ -328,7 +564,8 @@
                 mDozeParameters,
                 mDevicePostureController,
                 mDozeLog,
-                mSystemSettings);
+                mSystemSettings,
+                mDisplayManager);
 
         // GIVEN the device is in AOD
         mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
@@ -340,11 +577,56 @@
         mSensorInner.sendSensorEvent(4); // OPENED sensor
 
         // THEN brightness is updated according to the sensor for CLOSED
-        assertEquals(3, mServiceFake.screenBrightness);
+        assertEquals(SENSOR_TO_BRIGHTNESS_INT[3], mServiceFake.screenBrightnessInt);
+        assertTrue(Float.isNaN(mServiceFake.screenBrightnessFloat));
     }
 
     @Test
-    public void testSensorsSupportPostures_open() throws Exception {
+    @RequiresFlagsEnabled(Flags.FLAG_DOZE_BRIGHTNESS_FLOAT)
+    public void testSensorsSupportPostures_closed_Float() {
+        // GIVEN the device is CLOSED
+        when(mDevicePostureController.getDevicePosture()).thenReturn(
+                DevicePostureController.DEVICE_POSTURE_CLOSED);
+
+        // GIVEN closed and opened postures use different light sensors
+        mScreen = new DozeScreenBrightness(
+                mContext,
+                mServiceFake,
+                mSensorManager,
+                new Optional[]{
+                        Optional.empty() /* unknown */,
+                        Optional.of(mSensor.getSensor()) /* closed */,
+                        Optional.of(mSensorInner.getSensor()) /* half-opened */,
+                        Optional.of(mSensorInner.getSensor()) /* opened */,
+                        Optional.empty() /* flipped */
+                },
+                mDozeHost, null /* handler */,
+                mAlwaysOnDisplayPolicy,
+                mWakefulnessLifecycle,
+                mDozeParameters,
+                mDevicePostureController,
+                mDozeLog,
+                mSystemSettings,
+                mDisplayManager);
+
+        // GIVEN the device is in AOD
+        mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
+        mScreen.transitionTo(INITIALIZED, DOZE_AOD);
+        waitForSensorManager();
+
+        // WHEN new different events are sent from the inner and outer sensors
+        mSensor.sendSensorEvent(3); // CLOSED sensor
+        mSensorInner.sendSensorEvent(4); // OPENED sensor
+
+        // THEN brightness is updated according to the sensor for CLOSED
+        assertEquals(SENSOR_TO_BRIGHTNESS_FLOAT[3], mServiceFake.screenBrightnessFloat,
+                DELTA);
+        assertEquals(PowerManager.BRIGHTNESS_DEFAULT, mServiceFake.screenBrightnessInt);
+    }
+
+    @Test
+    @RequiresFlagsDisabled(Flags.FLAG_DOZE_BRIGHTNESS_FLOAT)
+    public void testSensorsSupportPostures_open_Int() {
         // GIVEN the device is OPENED
         when(mDevicePostureController.getDevicePosture()).thenReturn(
                 DevicePostureController.DEVICE_POSTURE_OPENED);
@@ -367,7 +649,8 @@
                 mDozeParameters,
                 mDevicePostureController,
                 mDozeLog,
-                mSystemSettings);
+                mSystemSettings,
+                mDisplayManager);
 
         // GIVEN device is in AOD
         mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
@@ -379,11 +662,55 @@
         mSensor.sendSensorEvent(3); // CLOSED sensor
 
         // THEN brightness is updated according to the sensor for OPENED
-        assertEquals(4, mServiceFake.screenBrightness);
+        assertEquals(SENSOR_TO_BRIGHTNESS_INT[4], mServiceFake.screenBrightnessInt);
+        assertTrue(Float.isNaN(mServiceFake.screenBrightnessFloat));
     }
 
     @Test
-    public void testSensorsSupportPostures_swapPostures() throws Exception {
+    @RequiresFlagsEnabled(Flags.FLAG_DOZE_BRIGHTNESS_FLOAT)
+    public void testSensorsSupportPostures_open_Float() {
+        // GIVEN the device is OPENED
+        when(mDevicePostureController.getDevicePosture()).thenReturn(
+                DevicePostureController.DEVICE_POSTURE_OPENED);
+
+        // GIVEN closed and opened postures use different light sensors
+        mScreen = new DozeScreenBrightness(
+                mContext,
+                mServiceFake,
+                mSensorManager,
+                new Optional[]{
+                        Optional.empty() /* unknown */,
+                        Optional.of(mSensor.getSensor()) /* closed */,
+                        Optional.of(mSensorInner.getSensor()) /* half-opened */,
+                        Optional.of(mSensorInner.getSensor()) /* opened */,
+                        Optional.empty() /* flipped */
+                },
+                mDozeHost, null /* handler */,
+                mAlwaysOnDisplayPolicy,
+                mWakefulnessLifecycle,
+                mDozeParameters,
+                mDevicePostureController,
+                mDozeLog,
+                mSystemSettings,
+                mDisplayManager);
+
+        // GIVEN device is in AOD
+        mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
+        mScreen.transitionTo(INITIALIZED, DOZE_AOD);
+        waitForSensorManager();
+
+        // WHEN new different events are sent from the inner and outer sensors
+        mSensorInner.sendSensorEvent(4); // OPENED sensor
+        mSensor.sendSensorEvent(3); // CLOSED sensor
+
+        // THEN brightness is updated according to the sensor for OPENED
+        assertEquals(SENSOR_TO_BRIGHTNESS_FLOAT[4], mServiceFake.screenBrightnessFloat, DELTA);
+        assertEquals(PowerManager.BRIGHTNESS_DEFAULT, mServiceFake.screenBrightnessInt);
+    }
+
+    @Test
+    @RequiresFlagsDisabled(Flags.FLAG_DOZE_BRIGHTNESS_FLOAT)
+    public void testSensorsSupportPostures_swapPostures_Int() {
         ArgumentCaptor<DevicePostureController.Callback> postureCallbackCaptor =
                 ArgumentCaptor.forClass(DevicePostureController.Callback.class);
         reset(mDevicePostureController);
@@ -410,7 +737,8 @@
                 mDozeParameters,
                 mDevicePostureController,
                 mDozeLog,
-                mSystemSettings);
+                mSystemSettings,
+                mDisplayManager);
         verify(mDevicePostureController).addCallback(postureCallbackCaptor.capture());
 
         // GIVEN device is in AOD
@@ -428,11 +756,65 @@
         mSensorInner.sendSensorEvent(4); // OPENED sensor
 
         // THEN brightness is updated according to the sensor for CLOSED
-        assertEquals(3, mServiceFake.screenBrightness);
+        assertEquals(SENSOR_TO_BRIGHTNESS_INT[3], mServiceFake.screenBrightnessInt);
+        assertTrue(Float.isNaN(mServiceFake.screenBrightnessFloat));
     }
 
     @Test
-    public void testNoBrightnessDeliveredAfterFinish() throws Exception {
+    @RequiresFlagsEnabled(Flags.FLAG_DOZE_BRIGHTNESS_FLOAT)
+    public void testSensorsSupportPostures_swapPostures_Float() {
+        ArgumentCaptor<DevicePostureController.Callback> postureCallbackCaptor =
+                ArgumentCaptor.forClass(DevicePostureController.Callback.class);
+        reset(mDevicePostureController);
+
+        // GIVEN the device starts up AOD OPENED
+        when(mDevicePostureController.getDevicePosture()).thenReturn(
+                DevicePostureController.DEVICE_POSTURE_OPENED);
+
+        // GIVEN closed and opened postures use different light sensors
+        mScreen = new DozeScreenBrightness(
+                mContext,
+                mServiceFake,
+                mSensorManager,
+                new Optional[]{
+                        Optional.empty() /* unknown */,
+                        Optional.of(mSensor.getSensor()) /* closed */,
+                        Optional.of(mSensorInner.getSensor()) /* half-opened */,
+                        Optional.of(mSensorInner.getSensor()) /* opened */,
+                        Optional.empty() /* flipped */
+                },
+                mDozeHost, null /* handler */,
+                mAlwaysOnDisplayPolicy,
+                mWakefulnessLifecycle,
+                mDozeParameters,
+                mDevicePostureController,
+                mDozeLog,
+                mSystemSettings,
+                mDisplayManager);
+        verify(mDevicePostureController).addCallback(postureCallbackCaptor.capture());
+
+        // GIVEN device is in AOD
+        mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
+        mScreen.transitionTo(INITIALIZED, DOZE_AOD);
+        waitForSensorManager();
+
+        // WHEN the posture changes to CLOSED
+        postureCallbackCaptor.getValue().onPostureChanged(
+                DevicePostureController.DEVICE_POSTURE_CLOSED);
+        waitForSensorManager();
+
+        // WHEN new different events are sent from the inner and outer sensors
+        mSensor.sendSensorEvent(3); // CLOSED sensor
+        mSensorInner.sendSensorEvent(4); // OPENED sensor
+
+        // THEN brightness is updated according to the sensor for CLOSED
+        assertEquals(SENSOR_TO_BRIGHTNESS_FLOAT[3], mServiceFake.screenBrightnessFloat, DELTA);
+        assertEquals(PowerManager.BRIGHTNESS_DEFAULT, mServiceFake.screenBrightnessInt);
+    }
+
+    @Test
+    @RequiresFlagsDisabled(Flags.FLAG_DOZE_BRIGHTNESS_FLOAT)
+    public void testNoBrightnessDeliveredAfterFinish_Int() {
         mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
         mScreen.transitionTo(INITIALIZED, DOZE_AOD);
         mScreen.transitionTo(DOZE_AOD, FINISH);
@@ -440,11 +822,27 @@
 
         mSensor.sendSensorEvent(1);
 
-        assertNotEquals(1, mServiceFake.screenBrightness);
+        assertNotEquals(SENSOR_TO_BRIGHTNESS_INT[1], mServiceFake.screenBrightnessInt);
+        assertTrue(Float.isNaN(mServiceFake.screenBrightnessFloat));
     }
 
     @Test
-    public void testNonPositiveBrightness_keepsPreviousBrightnessAndScrim() {
+    @RequiresFlagsEnabled(Flags.FLAG_DOZE_BRIGHTNESS_FLOAT)
+    public void testNoBrightnessDeliveredAfterFinish_Float() {
+        mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
+        mScreen.transitionTo(INITIALIZED, DOZE_AOD);
+        mScreen.transitionTo(DOZE_AOD, FINISH);
+        waitForSensorManager();
+
+        mSensor.sendSensorEvent(1);
+
+        assertNotEquals(SENSOR_TO_BRIGHTNESS_FLOAT[1], mServiceFake.screenBrightnessFloat);
+        assertEquals(PowerManager.BRIGHTNESS_DEFAULT, mServiceFake.screenBrightnessInt);
+    }
+
+    @Test
+    @RequiresFlagsDisabled(Flags.FLAG_DOZE_BRIGHTNESS_FLOAT)
+    public void testNonPositiveBrightness_keepsPreviousBrightnessAndScrim_Int() {
         mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
         mScreen.transitionTo(INITIALIZED, DOZE_AOD);
         waitForSensorManager();
@@ -452,7 +850,23 @@
         mSensor.sendSensorEvent(1);
         mSensor.sendSensorEvent(0);
 
-        assertEquals(1, mServiceFake.screenBrightness);
+        assertEquals(SENSOR_TO_BRIGHTNESS_INT[1], mServiceFake.screenBrightnessInt);
+        assertTrue(Float.isNaN(mServiceFake.screenBrightnessFloat));
+        verify(mDozeHost).setAodDimmingScrim(eq(10f / 255f));
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_DOZE_BRIGHTNESS_FLOAT)
+    public void testNonPositiveBrightness_keepsPreviousBrightnessAndScrim_Float() {
+        mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
+        mScreen.transitionTo(INITIALIZED, DOZE_AOD);
+        waitForSensorManager();
+
+        mSensor.sendSensorEvent(1);
+        mSensor.sendSensorEvent(0);
+
+        assertEquals(SENSOR_TO_BRIGHTNESS_FLOAT[1], mServiceFake.screenBrightnessFloat, DELTA);
+        assertEquals(PowerManager.BRIGHTNESS_DEFAULT, mServiceFake.screenBrightnessInt);
         verify(mDozeHost).setAodDimmingScrim(eq(10f / 255f));
     }
 
@@ -473,7 +887,8 @@
     }
 
     @Test
-    public void transitionToDoze_shouldClampBrightness_afterTimeout_clampsToDim() {
+    @RequiresFlagsDisabled(Flags.FLAG_DOZE_BRIGHTNESS_FLOAT)
+    public void transitionToDoze_shouldClampBrightness_afterTimeout_clampsToDim_Int() {
         when(mWakefulnessLifecycle.getLastSleepReason()).thenReturn(
                 PowerManager.GO_TO_SLEEP_REASON_TIMEOUT);
         when(mDozeParameters.shouldClampToDimBrightness()).thenReturn(true);
@@ -482,15 +897,38 @@
 
         // If we're dozing after a timeout, and playing the unlocked screen animation, we should
         // stay at or below dim brightness, because the screen dims just before timeout.
-        assertTrue(mServiceFake.screenBrightness <= DIM_BRIGHTNESS);
+        assertTrue(mServiceFake.screenBrightnessInt <= DIM_BRIGHTNESS_INT);
+        assertTrue(Float.isNaN(mServiceFake.screenBrightnessFloat));
 
         // Once we transition to Doze, use the doze brightness
         mScreen.transitionTo(INITIALIZED, DOZE);
-        assertEquals(mServiceFake.screenBrightness, DEFAULT_BRIGHTNESS);
+        assertEquals(mServiceFake.screenBrightnessInt, DEFAULT_BRIGHTNESS_INT);
+        assertTrue(Float.isNaN(mServiceFake.screenBrightnessFloat));
     }
 
     @Test
-    public void transitionToDoze_shouldClampBrightness_notAfterTimeout_doesNotClampToDim() {
+    @RequiresFlagsEnabled(Flags.FLAG_DOZE_BRIGHTNESS_FLOAT)
+    public void transitionToDoze_shouldClampBrightness_afterTimeout_clampsToDim_Float() {
+        when(mWakefulnessLifecycle.getLastSleepReason()).thenReturn(
+                PowerManager.GO_TO_SLEEP_REASON_TIMEOUT);
+        when(mDozeParameters.shouldClampToDimBrightness()).thenReturn(true);
+
+        mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
+
+        // If we're dozing after a timeout, and playing the unlocked screen animation, we should
+        // stay at or below dim brightness, because the screen dims just before timeout.
+        assertTrue(mServiceFake.screenBrightnessFloat <= DIM_BRIGHTNESS_FLOAT);
+        assertEquals(PowerManager.BRIGHTNESS_DEFAULT, mServiceFake.screenBrightnessInt);
+
+        // Once we transition to Doze, use the doze brightness
+        mScreen.transitionTo(INITIALIZED, DOZE);
+        assertEquals(mServiceFake.screenBrightnessFloat, DEFAULT_BRIGHTNESS_FLOAT, DELTA);
+        assertEquals(PowerManager.BRIGHTNESS_DEFAULT, mServiceFake.screenBrightnessInt);
+    }
+
+    @Test
+    @RequiresFlagsDisabled(Flags.FLAG_DOZE_BRIGHTNESS_FLOAT)
+    public void transitionToDoze_shouldClampBrightness_notAfterTimeout_doesNotClampToDim_Int() {
         when(mWakefulnessLifecycle.getLastSleepReason()).thenReturn(
                 PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON);
         when(mDozeParameters.shouldClampToDimBrightness()).thenReturn(true);
@@ -499,14 +937,36 @@
 
         // If we're playing the unlocked screen off animation after a power button press, we should
         // leave the brightness alone.
-        assertEquals(mServiceFake.screenBrightness, DEFAULT_BRIGHTNESS);
+        assertEquals(mServiceFake.screenBrightnessInt, DEFAULT_BRIGHTNESS_INT);
+        assertTrue(Float.isNaN(mServiceFake.screenBrightnessFloat));
 
         mScreen.transitionTo(INITIALIZED, DOZE);
-        assertEquals(mServiceFake.screenBrightness, DEFAULT_BRIGHTNESS);
+        assertEquals(mServiceFake.screenBrightnessInt, DEFAULT_BRIGHTNESS_INT);
+        assertTrue(Float.isNaN(mServiceFake.screenBrightnessFloat));
     }
 
     @Test
-    public void transitionToDoze_noClampBrightness_afterTimeout_noScreenOff_doesNotClampToDim() {
+    @RequiresFlagsEnabled(Flags.FLAG_DOZE_BRIGHTNESS_FLOAT)
+    public void transitionToDoze_shouldClampBrightness_notAfterTimeout_doesNotClampToDim_Float() {
+        when(mWakefulnessLifecycle.getLastSleepReason()).thenReturn(
+                PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON);
+        when(mDozeParameters.shouldClampToDimBrightness()).thenReturn(true);
+
+        mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
+
+        // If we're playing the unlocked screen off animation after a power button press, we should
+        // leave the brightness alone.
+        assertEquals(mServiceFake.screenBrightnessFloat, DEFAULT_BRIGHTNESS_FLOAT, DELTA);
+        assertEquals(PowerManager.BRIGHTNESS_DEFAULT, mServiceFake.screenBrightnessInt);
+
+        mScreen.transitionTo(INITIALIZED, DOZE);
+        assertEquals(mServiceFake.screenBrightnessFloat, DEFAULT_BRIGHTNESS_FLOAT, DELTA);
+        assertEquals(PowerManager.BRIGHTNESS_DEFAULT, mServiceFake.screenBrightnessInt);
+    }
+
+    @Test
+    @RequiresFlagsDisabled(Flags.FLAG_DOZE_BRIGHTNESS_FLOAT)
+    public void transitionToDoze_noClamp_afterTimeout_noScreenOff_doesNotClampToDim_Int() {
         when(mWakefulnessLifecycle.getLastSleepReason()).thenReturn(
                 PowerManager.GO_TO_SLEEP_REASON_TIMEOUT);
         when(mDozeParameters.shouldClampToDimBrightness()).thenReturn(false);
@@ -515,11 +975,28 @@
         mScreen.transitionTo(INITIALIZED, DOZE);
 
         // If we aren't controlling the screen off animation, we should leave the brightness alone.
-        assertEquals(mServiceFake.screenBrightness, DEFAULT_BRIGHTNESS);
+        assertEquals(mServiceFake.screenBrightnessInt, DEFAULT_BRIGHTNESS_INT);
+        assertTrue(Float.isNaN(mServiceFake.screenBrightnessFloat));
     }
 
     @Test
-    public void transitionToDoze_noClampBrightness_afterTimeout_clampsToDim() {
+    @RequiresFlagsEnabled(Flags.FLAG_DOZE_BRIGHTNESS_FLOAT)
+    public void transitionToDoze_noClamp_afterTimeout_noScreenOff_doesNotClampToDim_Float() {
+        when(mWakefulnessLifecycle.getLastSleepReason()).thenReturn(
+                PowerManager.GO_TO_SLEEP_REASON_TIMEOUT);
+        when(mDozeParameters.shouldClampToDimBrightness()).thenReturn(false);
+
+        mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
+        mScreen.transitionTo(INITIALIZED, DOZE);
+
+        // If we aren't controlling the screen off animation, we should leave the brightness alone.
+        assertEquals(mServiceFake.screenBrightnessFloat, DEFAULT_BRIGHTNESS_FLOAT, DELTA);
+        assertEquals(PowerManager.BRIGHTNESS_DEFAULT, mServiceFake.screenBrightnessInt);
+    }
+
+    @Test
+    @RequiresFlagsDisabled(Flags.FLAG_DOZE_BRIGHTNESS_FLOAT)
+    public void transitionToDoze_noClampBrightness_afterTimeout_clampsToDim_Int() {
         when(mWakefulnessLifecycle.getLastSleepReason()).thenReturn(
                 PowerManager.GO_TO_SLEEP_REASON_TIMEOUT);
         when(mWakefulnessLifecycle.getWakefulness()).thenReturn(
@@ -528,11 +1005,28 @@
 
         mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
 
-        assertTrue(mServiceFake.screenBrightness <= DIM_BRIGHTNESS);
+        assertTrue(mServiceFake.screenBrightnessInt <= DIM_BRIGHTNESS_INT);
+        assertTrue(Float.isNaN(mServiceFake.screenBrightnessFloat));
     }
 
     @Test
-    public void transitionToDoze_noClampBrigthness_notAfterTimeout_doesNotClampToDim() {
+    @RequiresFlagsEnabled(Flags.FLAG_DOZE_BRIGHTNESS_FLOAT)
+    public void transitionToDoze_noClampBrightness_afterTimeout_clampsToDim_Float() {
+        when(mWakefulnessLifecycle.getLastSleepReason()).thenReturn(
+                PowerManager.GO_TO_SLEEP_REASON_TIMEOUT);
+        when(mWakefulnessLifecycle.getWakefulness()).thenReturn(
+                WakefulnessLifecycle.WAKEFULNESS_GOING_TO_SLEEP);
+        when(mDozeParameters.shouldClampToDimBrightness()).thenReturn(false);
+
+        mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
+
+        assertTrue(mServiceFake.screenBrightnessFloat <= DIM_BRIGHTNESS_FLOAT);
+        assertEquals(PowerManager.BRIGHTNESS_DEFAULT, mServiceFake.screenBrightnessInt);
+    }
+
+    @Test
+    @RequiresFlagsDisabled(Flags.FLAG_DOZE_BRIGHTNESS_FLOAT)
+    public void transitionToDoze_noClampBrigthness_notAfterTimeout_doesNotClampToDim_Int() {
         when(mWakefulnessLifecycle.getLastSleepReason()).thenReturn(
                 PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON);
         when(mWakefulnessLifecycle.getWakefulness()).thenReturn(
@@ -542,11 +1036,29 @@
         mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
         mScreen.transitionTo(INITIALIZED, DOZE);
 
-        assertEquals(mServiceFake.screenBrightness, DEFAULT_BRIGHTNESS);
+        assertEquals(mServiceFake.screenBrightnessInt, DEFAULT_BRIGHTNESS_INT);
+        assertTrue(Float.isNaN(mServiceFake.screenBrightnessFloat));
     }
 
     @Test
-    public void transitionToAodPaused_lightSensorDisabled() {
+    @RequiresFlagsEnabled(Flags.FLAG_DOZE_BRIGHTNESS_FLOAT)
+    public void transitionToDoze_noClampBrigthness_notAfterTimeout_doesNotClampToDim_Float() {
+        when(mWakefulnessLifecycle.getLastSleepReason()).thenReturn(
+                PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON);
+        when(mWakefulnessLifecycle.getWakefulness()).thenReturn(
+                WakefulnessLifecycle.WAKEFULNESS_GOING_TO_SLEEP);
+        when(mDozeParameters.shouldClampToDimBrightness()).thenReturn(false);
+
+        mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
+        mScreen.transitionTo(INITIALIZED, DOZE);
+
+        assertEquals(mServiceFake.screenBrightnessFloat, DEFAULT_BRIGHTNESS_FLOAT, DELTA);
+        assertEquals(PowerManager.BRIGHTNESS_DEFAULT, mServiceFake.screenBrightnessInt);
+    }
+
+    @Test
+    @RequiresFlagsDisabled(Flags.FLAG_DOZE_BRIGHTNESS_FLOAT)
+    public void transitionToAodPaused_lightSensorDisabled_Int() {
         // GIVEN AOD
         mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
         mScreen.transitionTo(INITIALIZED, DOZE_AOD);
@@ -558,11 +1070,31 @@
 
         // THEN new light events don't update brightness since the light sensor was unregistered
         mSensor.sendSensorEvent(1);
-        assertEquals(mServiceFake.screenBrightness, DEFAULT_BRIGHTNESS);
+        assertEquals(mServiceFake.screenBrightnessInt, DEFAULT_BRIGHTNESS_INT);
+        assertTrue(Float.isNaN(mServiceFake.screenBrightnessFloat));
     }
 
     @Test
-    public void transitionFromAodPausedToAod_lightSensorEnabled() {
+    @RequiresFlagsEnabled(Flags.FLAG_DOZE_BRIGHTNESS_FLOAT)
+    public void transitionToAodPaused_lightSensorDisabled_Float() {
+        // GIVEN AOD
+        mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
+        mScreen.transitionTo(INITIALIZED, DOZE_AOD);
+
+        // WHEN AOD is paused
+        mScreen.transitionTo(DOZE_AOD, DOZE_AOD_PAUSING);
+        mScreen.transitionTo(DOZE_AOD, DOZE_AOD_PAUSED);
+        waitForSensorManager();
+
+        // THEN new light events don't update brightness since the light sensor was unregistered
+        mSensor.sendSensorEvent(1);
+        assertEquals(mServiceFake.screenBrightnessFloat, DEFAULT_BRIGHTNESS_FLOAT, DELTA);
+        assertEquals(PowerManager.BRIGHTNESS_DEFAULT, mServiceFake.screenBrightnessInt);
+    }
+
+    @Test
+    @RequiresFlagsDisabled(Flags.FLAG_DOZE_BRIGHTNESS_FLOAT)
+    public void transitionFromAodPausedToAod_lightSensorEnabled_Int() {
         // GIVEN AOD paused
         mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
         mScreen.transitionTo(INITIALIZED, DOZE_AOD);
@@ -577,7 +1109,54 @@
         mSensor.sendSensorEvent(1);
 
         // THEN aod brightness is updated
-        assertEquals(mServiceFake.screenBrightness, 1);
+        assertEquals(SENSOR_TO_BRIGHTNESS_INT[1], mServiceFake.screenBrightnessInt);
+        assertTrue(Float.isNaN(mServiceFake.screenBrightnessFloat));
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_DOZE_BRIGHTNESS_FLOAT)
+    public void transitionFromAodPausedToAod_lightSensorEnabled_Float() {
+        // GIVEN AOD paused
+        mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
+        mScreen.transitionTo(INITIALIZED, DOZE_AOD);
+        mScreen.transitionTo(DOZE_AOD, DOZE_AOD_PAUSING);
+        mScreen.transitionTo(DOZE_AOD, DOZE_AOD_PAUSED);
+
+        // WHEN device transitions back to AOD
+        mScreen.transitionTo(DOZE_AOD_PAUSED, DOZE_AOD);
+        waitForSensorManager();
+
+        // WHEN there are brightness changes
+        mSensor.sendSensorEvent(1);
+
+        // THEN aod brightness is updated
+        assertEquals(SENSOR_TO_BRIGHTNESS_FLOAT[1], mServiceFake.screenBrightnessFloat, DELTA);
+        assertEquals(PowerManager.BRIGHTNESS_DEFAULT, mServiceFake.screenBrightnessInt);
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_DOZE_BRIGHTNESS_FLOAT)
+    public void fallBackToIntIfFloatBrightnessUndefined() {
+        when(mDisplayManager.getDozeBrightnessSensorValueToBrightness(Display.DEFAULT_DISPLAY))
+                .thenReturn(null);
+        mScreen = new DozeScreenBrightness(
+                mContext,
+                mServiceFake,
+                mSensorManager,
+                new Optional[]{Optional.of(mSensor.getSensor())},
+                mDozeHost,
+                null /* handler */,
+                mAlwaysOnDisplayPolicy,
+                mWakefulnessLifecycle,
+                mDozeParameters,
+                mDevicePostureController,
+                mDozeLog,
+                mSystemSettings,
+                mDisplayManager);
+        mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
+
+        assertEquals(DEFAULT_BRIGHTNESS_INT, mServiceFake.screenBrightnessInt);
+        assertTrue(Float.isNaN(mServiceFake.screenBrightnessFloat));
     }
 
     private void waitForSensorManager() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeServiceFake.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeServiceFake.java
index 928b314..f55c2b7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeServiceFake.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeServiceFake.java
@@ -30,7 +30,8 @@
     public int screenState;
     public boolean screenStateSet;
     public boolean requestedWakeup;
-    public int screenBrightness;
+    public int screenBrightnessInt;
+    public float screenBrightnessFloat;
 
     public DozeServiceFake() {
         reset();
@@ -54,7 +55,12 @@
 
     @Override
     public void setDozeScreenBrightness(int brightness) {
-        screenBrightness = brightness;
+        screenBrightnessInt = brightness;
+    }
+
+    @Override
+    public void setDozeScreenBrightnessFloat(float brightness) {
+        screenBrightnessFloat = brightness;
     }
 
     public void reset() {
@@ -62,6 +68,7 @@
         screenState = Display.STATE_UNKNOWN;
         screenStateSet = false;
         requestedWakeup = false;
-        screenBrightness = PowerManager.BRIGHTNESS_DEFAULT;
+        screenBrightnessInt = PowerManager.BRIGHTNESS_DEFAULT;
+        screenBrightnessFloat = PowerManager.BRIGHTNESS_INVALID_FLOAT;
     }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/CommunalTouchHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/CommunalTouchHandlerTest.java
similarity index 82%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/CommunalTouchHandlerTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/dreams/touch/CommunalTouchHandlerTest.java
index 7936ccc..c2c94a8 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/CommunalTouchHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/CommunalTouchHandlerTest.java
@@ -23,6 +23,9 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.util.LayoutDirection;
 import android.view.GestureDetector;
 import android.view.MotionEvent;
 
@@ -68,10 +71,12 @@
         AtomicReference reference = new AtomicReference<>(null);
         when(mLifecycle.getInternalScopeRef()).thenReturn(reference);
         when(mLifecycle.getCurrentState()).thenReturn(Lifecycle.State.CREATED);
+
         mTouchHandler = new CommunalTouchHandler(
                 Optional.of(mCentralSurfaces),
                 INITIATION_WIDTH,
                 mKosmos.getCommunalInteractor(),
+                mKosmos.getConfigurationInteractor(),
                 mLifecycle
                 );
     }
@@ -127,4 +132,26 @@
                 .onScroll(motionEvent1, motionEvent2, 1, 1))
                 .isTrue();
     }
+
+    @Test
+    public void testTouchInitiationArea() {
+        final int right = 80;
+        final int bottom = 100;
+        final Rect bounds = new Rect(0, 0, right, bottom);
+
+        {
+            final Region region = new Region();
+            mTouchHandler.mLayoutDirectionCallback.accept(LayoutDirection.LTR);
+            mTouchHandler.getTouchInitiationRegion(bounds, region, null);
+            assertThat(region.getBounds()).isEqualTo(
+                    new Rect(right - INITIATION_WIDTH, 0, right, bottom));
+        }
+
+        {
+            final Region region = new Region();
+            mTouchHandler.mLayoutDirectionCallback.accept(LayoutDirection.RTL);
+            mTouchHandler.getTouchInitiationRegion(bounds, region, null);
+            assertThat(region.getBounds()).isEqualTo(new Rect(0, 0, INITIATION_WIDTH, bottom));
+        }
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsClassicDebugTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsClassicDebugTest.kt
index a1fe0f0..3388a78 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsClassicDebugTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsClassicDebugTest.kt
@@ -27,6 +27,7 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.Flags.FLAG_SYSUI_TEAMFOOD
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.settings.FakeUserTracker
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.eq
 import com.android.systemui.util.mockito.nullable
@@ -67,6 +68,7 @@
     @Mock private lateinit var systemProperties: SystemPropertiesHelper
     @Mock private lateinit var resources: Resources
     @Mock private lateinit var restarter: Restarter
+    private lateinit var userTracker: FakeUserTracker
     private val flagMap = mutableMapOf<String, Flag<*>>()
     private lateinit var broadcastReceiver: BroadcastReceiver
     private lateinit var clearCacheAction: Consumer<String>
@@ -78,9 +80,11 @@
     @Before
     fun setup() {
         MockitoAnnotations.initMocks(this)
-
         flagMap.put(teamfoodableFlagA.name, teamfoodableFlagA)
         flagMap.put(releasedFlagB.name, releasedFlagB)
+
+        userTracker = FakeUserTracker(onCreateCurrentUserContext = { mockContext })
+
         mFeatureFlagsClassicDebug =
             FeatureFlagsClassicDebug(
                 flagManager,
@@ -90,7 +94,8 @@
                 resources,
                 serverFlagReader,
                 flagMap,
-                restarter
+                restarter,
+                userTracker
             )
         mFeatureFlagsClassicDebug.init()
         verify(flagManager).onSettingsChangedAction = any()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperCategoriesRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperCategoriesRepositoryTest.kt
index 14837f2..6e883c2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperCategoriesRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperCategoriesRepositoryTest.kt
@@ -16,12 +16,28 @@
 
 package com.android.systemui.keyboard.shortcut.data.repository
 
+import android.hardware.input.fakeInputManager
+import android.view.KeyEvent.KEYCODE_A
+import android.view.KeyEvent.KEYCODE_B
+import android.view.KeyEvent.KEYCODE_C
+import android.view.KeyEvent.KEYCODE_D
+import android.view.KeyEvent.KEYCODE_E
+import android.view.KeyEvent.KEYCODE_F
+import android.view.KeyEvent.KEYCODE_G
+import android.view.KeyboardShortcutGroup
+import android.view.KeyboardShortcutInfo
 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.data.source.FakeKeyboardShortcutGroupsSource
 import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts
+import com.android.systemui.keyboard.shortcut.shared.model.Shortcut
+import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategory
+import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType
+import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCommand
+import com.android.systemui.keyboard.shortcut.shared.model.ShortcutKey
+import com.android.systemui.keyboard.shortcut.shared.model.ShortcutSubCategory
 import com.android.systemui.keyboard.shortcut.shortcutHelperAppCategoriesShortcutsSource
 import com.android.systemui.keyboard.shortcut.shortcutHelperCategoriesRepository
 import com.android.systemui.keyboard.shortcut.shortcutHelperCurrentAppShortcutsSource
@@ -47,13 +63,14 @@
 
     private val fakeSystemSource = FakeKeyboardShortcutGroupsSource()
     private val fakeMultiTaskingSource = FakeKeyboardShortcutGroupsSource()
+    private val fakeAppCategoriesSource = FakeKeyboardShortcutGroupsSource()
 
     private val kosmos =
         testKosmos().also {
             it.testDispatcher = UnconfinedTestDispatcher()
             it.shortcutHelperSystemShortcutsSource = fakeSystemSource
             it.shortcutHelperMultiTaskingShortcutsSource = fakeMultiTaskingSource
-            it.shortcutHelperAppCategoriesShortcutsSource = FakeKeyboardShortcutGroupsSource()
+            it.shortcutHelperAppCategoriesShortcutsSource = fakeAppCategoriesSource
             it.shortcutHelperInputShortcutsSource = FakeKeyboardShortcutGroupsSource()
             it.shortcutHelperCurrentAppShortcutsSource = FakeKeyboardShortcutGroupsSource()
         }
@@ -61,6 +78,7 @@
     private val repo = kosmos.shortcutHelperCategoriesRepository
     private val helper = kosmos.shortcutHelperTestHelper
     private val testScope = kosmos.testScope
+    private val fakeInputManager = kosmos.fakeInputManager
 
     @Before
     fun setUp() {
@@ -87,4 +105,74 @@
             // though fetching shortcuts again would have returned a new result.
             assertThat(secondCategories).isEqualTo(firstCategories)
         }
+
+    @Test
+    fun categories_filtersShortcutsWithUnsupportedKeyCodes() =
+        testScope.runTest {
+            fakeSystemSource.setGroups(
+                listOf(
+                    simpleGroup(
+                        simpleShortcutInfo(KEYCODE_A),
+                        simpleShortcutInfo(KEYCODE_B),
+                    ),
+                    simpleGroup(
+                        simpleShortcutInfo(KEYCODE_C),
+                    ),
+                )
+            )
+            fakeMultiTaskingSource.setGroups(
+                listOf(
+                    simpleGroup(
+                        simpleShortcutInfo(KEYCODE_D),
+                    ),
+                    simpleGroup(
+                        simpleShortcutInfo(KEYCODE_E),
+                        simpleShortcutInfo(KEYCODE_F),
+                    ),
+                )
+            )
+            fakeAppCategoriesSource.setGroups(listOf(simpleGroup(simpleShortcutInfo(KEYCODE_G))))
+
+            fakeInputManager.removeKeysFromKeyboard(deviceId = 123, KEYCODE_A, KEYCODE_D, KEYCODE_G)
+            helper.toggle(deviceId = 123)
+
+            val categories by collectLastValue(repo.categories)
+            assertThat(categories)
+                .containsExactly(
+                    ShortcutCategory(
+                        ShortcutCategoryType.System,
+                        listOf(
+                            simpleSubCategory(simpleShortcut("B")),
+                            simpleSubCategory(simpleShortcut("C")),
+                        )
+                    ),
+                    ShortcutCategory(
+                        ShortcutCategoryType.MultiTasking,
+                        listOf(
+                            simpleSubCategory(
+                                simpleShortcut("E"),
+                                simpleShortcut("F"),
+                            ),
+                        )
+                    ),
+                )
+        }
+
+    private fun simpleSubCategory(vararg shortcuts: Shortcut) =
+        ShortcutSubCategory(simpleGroupLabel, shortcuts.asList())
+
+    private fun simpleShortcut(vararg keys: String) =
+        Shortcut(
+            label = simpleShortcutLabel,
+            commands = listOf(ShortcutCommand(keys.map { ShortcutKey.Text(it) }))
+        )
+
+    private fun simpleGroup(vararg shortcuts: KeyboardShortcutInfo) =
+        KeyboardShortcutGroup(simpleGroupLabel, shortcuts.asList())
+
+    private fun simpleShortcutInfo(keyCode: Int = 0) =
+        KeyboardShortcutInfo(simpleShortcutLabel, keyCode, /* modifiers= */ 0)
+
+    private val simpleShortcutLabel = "shortcut label"
+    private val simpleGroupLabel = "group label"
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModelTest.kt
index 69fc463..6b60740 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModelTest.kt
@@ -16,6 +16,11 @@
 
 package com.android.systemui.keyboard.shortcut.ui.viewmodel
 
+import android.app.role.RoleManager
+import android.app.role.mockRoleManager
+import android.view.KeyEvent
+import android.view.KeyboardShortcutGroup
+import android.view.KeyboardShortcutInfo
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
@@ -23,7 +28,12 @@
 import com.android.systemui.coroutines.collectValues
 import com.android.systemui.keyboard.shortcut.data.source.FakeKeyboardShortcutGroupsSource
 import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts
+import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategory
 import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.CurrentApp
+import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.MultiTasking
+import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.System
+import com.android.systemui.keyboard.shortcut.shared.model.ShortcutSubCategory
+import com.android.systemui.keyboard.shortcut.shared.model.shortcut
 import com.android.systemui.keyboard.shortcut.shortcutHelperAppCategoriesShortcutsSource
 import com.android.systemui.keyboard.shortcut.shortcutHelperCurrentAppShortcutsSource
 import com.android.systemui.keyboard.shortcut.shortcutHelperInputShortcutsSource
@@ -37,7 +47,9 @@
 import com.android.systemui.kosmos.testDispatcher
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.model.sysUiState
+import com.android.systemui.settings.fakeUserTracker
 import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SHORTCUT_HELPER_SHOWING
+import com.android.systemui.util.mockito.whenever
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.test.UnconfinedTestDispatcher
@@ -69,12 +81,15 @@
     private val testScope = kosmos.testScope
     private val testHelper = kosmos.shortcutHelperTestHelper
     private val sysUiState = kosmos.sysUiState
+    private val fakeUserTracker = kosmos.fakeUserTracker
+    private val mockRoleManager = kosmos.mockRoleManager
     private val viewModel = kosmos.shortcutHelperViewModel
 
     @Before
     fun setUp() {
         fakeSystemSource.setGroups(TestShortcuts.systemGroups)
         fakeMultiTaskingSource.setGroups(TestShortcuts.multitaskingGroups)
+        fakeCurrentAppsSource.setGroups(TestShortcuts.currentAppGroups)
     }
 
     @Test
@@ -208,21 +223,21 @@
         }
 
     @Test
-    fun shortcutsUiState_featureActive_emitsActiveWithFirstCategorySelectedByDefault() =
+    fun shortcutsUiState_noCurrentAppCategory_defaultSelectedCategoryIsSystem() =
         testScope.runTest {
+            fakeCurrentAppsSource.setGroups(emptyList())
+
             val uiState by collectLastValue(viewModel.shortcutsUiState)
 
             testHelper.showFromActivity()
 
             val activeUiState = uiState as ShortcutsUiState.Active
-            assertThat(activeUiState.defaultSelectedCategory)
-                .isEqualTo(activeUiState.shortcutCategories.first().type)
+            assertThat(activeUiState.defaultSelectedCategory).isEqualTo(System)
         }
 
     @Test
-    fun shortcutsUiState_featureActive_emitsActiveWithCurrentAppsCategorySelectedWhenPresent() =
+    fun shortcutsUiState_currentAppCategoryPresent_currentAppIsDefaultSelected() =
         testScope.runTest {
-            fakeCurrentAppsSource.setGroups(TestShortcuts.currentAppGroups)
             val uiState by collectLastValue(viewModel.shortcutsUiState)
 
             testHelper.showFromActivity()
@@ -231,4 +246,144 @@
             assertThat(activeUiState.defaultSelectedCategory)
                 .isEqualTo(CurrentApp(TestShortcuts.currentAppPackageName))
         }
+
+    @Test
+    fun shortcutsUiState_currentAppIsLauncher_defaultSelectedCategoryIsSystem() =
+        testScope.runTest {
+            whenever(
+                    mockRoleManager.getRoleHoldersAsUser(
+                        RoleManager.ROLE_HOME,
+                        fakeUserTracker.userHandle
+                    )
+                )
+                .thenReturn(listOf(TestShortcuts.currentAppPackageName))
+            val uiState by collectLastValue(viewModel.shortcutsUiState)
+
+            testHelper.showFromActivity()
+
+            val activeUiState = uiState as ShortcutsUiState.Active
+            assertThat(activeUiState.defaultSelectedCategory).isEqualTo(System)
+        }
+
+    @Test
+    fun shortcutsUiState_userTypedQuery_filtersMatchingShortcutLabels() =
+        testScope.runTest {
+            fakeSystemSource.setGroups(
+                groupWithShortcutLabels("first Foo shortcut1", "first bar shortcut1"),
+                groupWithShortcutLabels("second foO shortcut2", "second bar shortcut2"),
+            )
+            fakeMultiTaskingSource.setGroups(
+                groupWithShortcutLabels("third FoO shortcut1", "third bar shortcut1")
+            )
+            val uiState by collectLastValue(viewModel.shortcutsUiState)
+
+            testHelper.showFromActivity()
+            viewModel.onSearchQueryChanged("foo")
+
+            val activeUiState = uiState as ShortcutsUiState.Active
+            assertThat(activeUiState.shortcutCategories)
+                .containsExactly(
+                    ShortcutCategory(
+                        System,
+                        subCategoryWithShortcutLabels("first Foo shortcut1"),
+                        subCategoryWithShortcutLabels("second foO shortcut2")
+                    ),
+                    ShortcutCategory(
+                        MultiTasking,
+                        subCategoryWithShortcutLabels("third FoO shortcut1")
+                    )
+                )
+        }
+
+    @Test
+    fun shortcutsUiState_userTypedQuery_noMatch_returnsEmptyList() =
+        testScope.runTest {
+            fakeSystemSource.setGroups(
+                groupWithShortcutLabels("first Foo shortcut1", "first bar shortcut1"),
+                groupWithShortcutLabels("second foO shortcut2", "second bar shortcut2"),
+            )
+            fakeMultiTaskingSource.setGroups(
+                groupWithShortcutLabels("third FoO shortcut1", "third bar shortcut1")
+            )
+            val uiState by collectLastValue(viewModel.shortcutsUiState)
+
+            testHelper.showFromActivity()
+            viewModel.onSearchQueryChanged("unmatched query")
+
+            val activeUiState = uiState as ShortcutsUiState.Active
+            assertThat(activeUiState.shortcutCategories).isEmpty()
+        }
+
+    @Test
+    fun shortcutsUiState_userTypedQuery_noMatch_returnsNullDefaultSelectedCategory() =
+        testScope.runTest {
+            fakeSystemSource.setGroups(
+                groupWithShortcutLabels("first Foo shortcut1", "first bar shortcut1"),
+                groupWithShortcutLabels("second foO shortcut2", "second bar shortcut2"),
+            )
+            fakeMultiTaskingSource.setGroups(
+                groupWithShortcutLabels("third FoO shortcut1", "third bar shortcut1")
+            )
+            val uiState by collectLastValue(viewModel.shortcutsUiState)
+
+            testHelper.showFromActivity()
+            viewModel.onSearchQueryChanged("unmatched query")
+
+            val activeUiState = uiState as ShortcutsUiState.Active
+            assertThat(activeUiState.defaultSelectedCategory).isNull()
+        }
+
+    @Test
+    fun shortcutsUiState_userTypedQuery_changesDefaultSelectedCategoryToFirstMatchingCategory() =
+        testScope.runTest {
+            fakeSystemSource.setGroups(groupWithShortcutLabels("first shortcut"))
+            fakeMultiTaskingSource.setGroups(groupWithShortcutLabels("second shortcut"))
+            val uiState by collectLastValue(viewModel.shortcutsUiState)
+
+            testHelper.showFromActivity()
+            viewModel.onSearchQueryChanged("second")
+
+            val activeUiState = uiState as ShortcutsUiState.Active
+            assertThat(activeUiState.defaultSelectedCategory).isEqualTo(MultiTasking)
+        }
+
+    @Test
+    fun shortcutsUiState_userTypedQuery_multipleCategoriesMatch_currentAppIsDefaultSelected() =
+        testScope.runTest {
+            fakeSystemSource.setGroups(groupWithShortcutLabels("first shortcut"))
+            fakeMultiTaskingSource.setGroups(groupWithShortcutLabels("second shortcut"))
+            fakeCurrentAppsSource.setGroups(groupWithShortcutLabels("third shortcut"))
+            val uiState by collectLastValue(viewModel.shortcutsUiState)
+
+            testHelper.showFromActivity()
+            viewModel.onSearchQueryChanged("shortcut")
+
+            val activeUiState = uiState as ShortcutsUiState.Active
+            assertThat(activeUiState.defaultSelectedCategory).isInstanceOf(CurrentApp::class.java)
+        }
+
+    private fun groupWithShortcutLabels(vararg shortcutLabels: String) =
+        KeyboardShortcutGroup(SIMPLE_GROUP_LABEL, shortcutLabels.map { simpleShortcutInfo(it) })
+            .apply { packageName = "test.package.name" }
+
+    private fun simpleShortcutInfo(label: String) =
+        KeyboardShortcutInfo(label, KeyEvent.KEYCODE_A, KeyEvent.META_CTRL_ON)
+
+    private fun subCategoryWithShortcutLabels(vararg shortcutLabels: String) =
+        ShortcutSubCategory(
+            label = SIMPLE_GROUP_LABEL,
+            shortcuts = shortcutLabels.map { simpleShortcut(it) },
+        )
+
+    private fun simpleShortcut(label: String) =
+        shortcut(label) {
+            command {
+                key("Ctrl")
+                key("A")
+            }
+        }
+
+    companion object {
+        private const val SIMPLE_GROUP_LABEL = "simple group"
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
index 506c5ae..29cd9a2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
@@ -30,6 +30,7 @@
 import android.view.SurfaceControlViewHost
 import androidx.test.filters.SmallTest
 import com.android.internal.widget.LockPatternUtils
+import com.android.keyguard.logging.KeyguardQuickAffordancesLogger
 import com.android.systemui.SystemUIAppComponentFactoryBase
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.animation.DialogTransitionAnimator
@@ -96,7 +97,8 @@
     @Mock private lateinit var previewSurfacePackage: SurfaceControlViewHost.SurfacePackage
     @Mock private lateinit var launchAnimator: DialogTransitionAnimator
     @Mock private lateinit var devicePolicyManager: DevicePolicyManager
-    @Mock private lateinit var logger: KeyguardQuickAffordancesMetricsLogger
+    @Mock private lateinit var logger: KeyguardQuickAffordancesLogger
+    @Mock private lateinit var metricsLogger: KeyguardQuickAffordancesMetricsLogger
 
     private lateinit var dockManager: DockManagerFake
     private lateinit var biometricSettingsRepository: FakeBiometricSettingsRepository
@@ -199,6 +201,7 @@
                 repository = { quickAffordanceRepository },
                 launchAnimator = launchAnimator,
                 logger = logger,
+                metricsLogger = metricsLogger,
                 devicePolicyManager = devicePolicyManager,
                 dockManager = dockManager,
                 biometricSettingsRepository = biometricSettingsRepository,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt
index f726aae..e251ab5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt
@@ -29,6 +29,7 @@
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.argThat
 import com.android.systemui.util.mockito.whenever
+import java.util.function.Predicate
 import junit.framework.Assert.assertEquals
 import junit.framework.Assert.assertFalse
 import junit.framework.Assert.assertTrue
@@ -46,7 +47,6 @@
 import org.mockito.Mockito.verifyNoMoreInteractions
 import org.mockito.MockitoAnnotations
 import org.mockito.kotlin.clearInvocations
-import java.util.function.Predicate
 
 @RunWith(AndroidJUnit4::class)
 @RunWithLooper
@@ -54,70 +54,134 @@
 class KeyguardUnlockAnimationControllerTest : SysuiTestCase() {
     private lateinit var keyguardUnlockAnimationController: KeyguardUnlockAnimationController
 
-    @Mock
-    private lateinit var windowManager: WindowManager
-    @Mock
-    private lateinit var keyguardViewMediator: KeyguardViewMediator
-    @Mock
-    private lateinit var keyguardStateController: KeyguardStateController
-    @Mock
-    private lateinit var keyguardViewController: KeyguardViewController
-    @Mock
-    private lateinit var featureFlags: FeatureFlags
-    @Mock
-    private lateinit var biometricUnlockController: BiometricUnlockController
-    @Mock
-    private lateinit var surfaceTransactionApplier: SyncRtSurfaceTransactionApplier
-    @Mock
-    private lateinit var statusBarStateController: SysuiStatusBarStateController
-    @Mock
-    private lateinit var notificationShadeWindowController: NotificationShadeWindowController
-    @Mock
-    private lateinit var powerManager: PowerManager
-    @Mock
-    private lateinit var wallpaperManager: WallpaperManager
+    @Mock private lateinit var windowManager: WindowManager
+    @Mock private lateinit var keyguardViewMediator: KeyguardViewMediator
+    @Mock private lateinit var keyguardStateController: KeyguardStateController
+    @Mock private lateinit var keyguardViewController: KeyguardViewController
+    @Mock private lateinit var featureFlags: FeatureFlags
+    @Mock private lateinit var biometricUnlockController: BiometricUnlockController
+    @Mock private lateinit var surfaceTransactionApplier: SyncRtSurfaceTransactionApplier
+    @Mock private lateinit var statusBarStateController: SysuiStatusBarStateController
+    @Mock private lateinit var notificationShadeWindowController: NotificationShadeWindowController
+    @Mock private lateinit var powerManager: PowerManager
+    @Mock private lateinit var wallpaperManager: WallpaperManager
 
     @Mock
     private lateinit var launcherUnlockAnimationController: ILauncherUnlockAnimationController.Stub
 
     private var surfaceControl1 = mock(SurfaceControl::class.java)
-    private var remoteTarget1 = RemoteAnimationTarget(
-            0 /* taskId */, 0, surfaceControl1, false, Rect(), Rect(), 0, Point(), Rect(), Rect(),
-            mock(WindowConfiguration::class.java), false, surfaceControl1, Rect(),
-            mock(ActivityManager.RunningTaskInfo::class.java), false)
+    private var remoteTarget1 =
+        RemoteAnimationTarget(
+            0 /* taskId */,
+            0,
+            surfaceControl1,
+            false,
+            Rect(),
+            Rect(),
+            0,
+            Point(),
+            Rect(),
+            Rect(),
+            mock(WindowConfiguration::class.java),
+            false,
+            surfaceControl1,
+            Rect(),
+            mock(ActivityManager.RunningTaskInfo::class.java),
+            false
+        )
 
     private var surfaceControl2 = mock(SurfaceControl::class.java)
-    private var remoteTarget2 = RemoteAnimationTarget(
-            1 /* taskId */, 0, surfaceControl2, false, Rect(), Rect(), 0, Point(), Rect(), Rect(),
-            mock(WindowConfiguration::class.java), false, surfaceControl2, Rect(),
-            mock(ActivityManager.RunningTaskInfo::class.java), false)
+    private var remoteTarget2 =
+        RemoteAnimationTarget(
+            1 /* taskId */,
+            0,
+            surfaceControl2,
+            false,
+            Rect(),
+            Rect(),
+            0,
+            Point(),
+            Rect(),
+            Rect(),
+            mock(WindowConfiguration::class.java),
+            false,
+            surfaceControl2,
+            Rect(),
+            mock(ActivityManager.RunningTaskInfo::class.java),
+            false
+        )
     private lateinit var remoteAnimationTargets: Array<RemoteAnimationTarget>
 
     private var surfaceControlWp = mock(SurfaceControl::class.java)
-    private var wallpaperTarget = RemoteAnimationTarget(
-            2 /* taskId */, 0, surfaceControlWp, false, Rect(), Rect(), 0, Point(), Rect(), Rect(),
-            mock(WindowConfiguration::class.java), false, surfaceControlWp, Rect(),
-            mock(ActivityManager.RunningTaskInfo::class.java), false)
+    private var wallpaperTarget =
+        RemoteAnimationTarget(
+            2 /* taskId */,
+            0,
+            surfaceControlWp,
+            false,
+            Rect(),
+            Rect(),
+            0,
+            Point(),
+            Rect(),
+            Rect(),
+            mock(WindowConfiguration::class.java),
+            false,
+            surfaceControlWp,
+            Rect(),
+            mock(ActivityManager.RunningTaskInfo::class.java),
+            false
+        )
     private lateinit var wallpaperTargets: Array<RemoteAnimationTarget>
 
     private var surfaceControlLockWp = mock(SurfaceControl::class.java)
-    private var lockWallpaperTarget = RemoteAnimationTarget(
-            3 /* taskId */, 0, surfaceControlLockWp, false, Rect(), Rect(), 0, Point(), Rect(),
-            Rect(), mock(WindowConfiguration::class.java), false, surfaceControlLockWp,
-            Rect(), mock(ActivityManager.RunningTaskInfo::class.java), false)
+    private var lockWallpaperTarget =
+        RemoteAnimationTarget(
+            3 /* taskId */,
+            0,
+            surfaceControlLockWp,
+            false,
+            Rect(),
+            Rect(),
+            0,
+            Point(),
+            Rect(),
+            Rect(),
+            mock(WindowConfiguration::class.java),
+            false,
+            surfaceControlLockWp,
+            Rect(),
+            mock(ActivityManager.RunningTaskInfo::class.java),
+            false
+        )
     private lateinit var lockWallpaperTargets: Array<RemoteAnimationTarget>
+    private var shouldPerformSmartspaceTransition = false
 
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
-        keyguardUnlockAnimationController = KeyguardUnlockAnimationController(
-            windowManager, context.resources,
-            keyguardStateController, { keyguardViewMediator }, keyguardViewController,
-            featureFlags, { biometricUnlockController }, statusBarStateController,
-            notificationShadeWindowController, powerManager, wallpaperManager
-        )
+        keyguardUnlockAnimationController =
+            object :
+                KeyguardUnlockAnimationController(
+                    windowManager,
+                    context.resources,
+                    keyguardStateController,
+                    { keyguardViewMediator },
+                    keyguardViewController,
+                    featureFlags,
+                    { biometricUnlockController },
+                    statusBarStateController,
+                    notificationShadeWindowController,
+                    powerManager,
+                    wallpaperManager
+                ) {
+                override fun shouldPerformSmartspaceTransition(): Boolean =
+                    shouldPerformSmartspaceTransition
+            }
         keyguardUnlockAnimationController.setLauncherUnlockController(
-            "", launcherUnlockAnimationController)
+            "",
+            launcherUnlockAnimationController
+        )
 
         whenever(keyguardViewController.viewRootImpl).thenReturn(mock(ViewRootImpl::class.java))
         whenever(powerManager.isInteractive).thenReturn(true)
@@ -159,8 +223,8 @@
         )
 
         val captorSb = ArgThatCaptor<SyncRtSurfaceTransactionApplier.SurfaceParams>()
-        verify(surfaceTransactionApplier, times(1)).scheduleApply(
-                captorSb.capture { sp -> sp.surface == surfaceControl1 })
+        verify(surfaceTransactionApplier, times(1))
+            .scheduleApply(captorSb.capture { sp -> sp.surface == surfaceControl1 })
 
         val params = captorSb.getLastValue()
 
@@ -171,15 +235,13 @@
 
         // Also expect we've immediately asked the keyguard view mediator to finish the remote
         // animation.
-        verify(keyguardViewMediator, times(1)).exitKeyguardAndFinishSurfaceBehindRemoteAnimation(
-            false /* cancelled */)
+        verify(keyguardViewMediator, times(1))
+            .exitKeyguardAndFinishSurfaceBehindRemoteAnimation(false /* cancelled */)
 
         verifyNoMoreInteractions(surfaceTransactionApplier)
     }
 
-    /**
-     * If we are not wake and unlocking, we expect the unlock animation to play normally.
-     */
+    /** If we are not wake and unlocking, we expect the unlock animation to play normally. */
     @Test
     fun surfaceAnimation_ifNotWakeAndUnlocking() {
         whenever(biometricUnlockController.isWakeAndUnlock).thenReturn(false)
@@ -193,18 +255,18 @@
         )
 
         // Since the animation is running, we should not have finished the remote animation.
-        verify(keyguardViewMediator, times(0)).exitKeyguardAndFinishSurfaceBehindRemoteAnimation(
-            false /* cancelled */)
+        verify(keyguardViewMediator, times(0))
+            .exitKeyguardAndFinishSurfaceBehindRemoteAnimation(false /* cancelled */)
     }
 
     @Test
     fun onWakeAndUnlock_notifiesListenerWithTrue() {
         whenever(biometricUnlockController.isWakeAndUnlock).thenReturn(true)
-        whenever(biometricUnlockController.mode).thenReturn(
-            BiometricUnlockController.MODE_WAKE_AND_UNLOCK)
+        whenever(biometricUnlockController.mode)
+            .thenReturn(BiometricUnlockController.MODE_WAKE_AND_UNLOCK)
 
-        val listener = mock(
-            KeyguardUnlockAnimationController.KeyguardUnlockAnimationListener::class.java)
+        val listener =
+            mock(KeyguardUnlockAnimationController.KeyguardUnlockAnimationListener::class.java)
         keyguardUnlockAnimationController.addKeyguardUnlockAnimationListener(listener)
 
         keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation(
@@ -221,11 +283,11 @@
     @Test
     fun onWakeAndUnlockFromDream_notifiesListenerWithFalse() {
         whenever(biometricUnlockController.isWakeAndUnlock).thenReturn(true)
-        whenever(biometricUnlockController.mode).thenReturn(
-            BiometricUnlockController.MODE_WAKE_AND_UNLOCK_FROM_DREAM)
+        whenever(biometricUnlockController.mode)
+            .thenReturn(BiometricUnlockController.MODE_WAKE_AND_UNLOCK_FROM_DREAM)
 
-        val listener = mock(
-            KeyguardUnlockAnimationController.KeyguardUnlockAnimationListener::class.java)
+        val listener =
+            mock(KeyguardUnlockAnimationController.KeyguardUnlockAnimationListener::class.java)
         keyguardUnlockAnimationController.addKeyguardUnlockAnimationListener(listener)
 
         keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation(
@@ -269,8 +331,8 @@
      * keyguard. This means this was a swipe to dismiss gesture but the user flung the keyguard and
      * lifted their finger while we were requesting the surface be made visible.
      *
-     * In this case, we should verify that we are playing the canned unlock animation and not
-     * simply fading in the surface.
+     * In this case, we should verify that we are playing the canned unlock animation and not simply
+     * fading in the surface.
      */
     @Test
     fun playCannedUnlockAnimation_ifRequestedShowSurface_andFlinging() {
@@ -293,8 +355,8 @@
      * ever happened and we're just playing the simple canned animation (happens via UDFPS unlock,
      * long press on the lock icon, etc).
      *
-     * In this case, we should verify that we are playing the canned unlock animation and not
-     * simply fading in the surface.
+     * In this case, we should verify that we are playing the canned unlock animation and not simply
+     * fading in the surface.
      */
     @Test
     fun playCannedUnlockAnimation_ifDidNotRequestShowSurface() {
@@ -332,11 +394,11 @@
         keyguardUnlockAnimationController.willUnlockWithInWindowLauncherAnimations = true
 
         keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation(
-                remoteAnimationTargets,
-                wallpaperTargets,
-                arrayOf(),
-                0 /* startTime */,
-                false /* requestedShowSurfaceBehindKeyguard */
+            remoteAnimationTargets,
+            wallpaperTargets,
+            arrayOf(),
+            0 /* startTime */,
+            false /* requestedShowSurfaceBehindKeyguard */
         )
 
         assertTrue(keyguardUnlockAnimationController.isPlayingCannedUnlockAnimation())
@@ -353,11 +415,11 @@
         var lastFadeOutAlpha = -1f
 
         keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation(
-                arrayOf(remoteTarget1, remoteTarget2),
-                wallpaperTargets,
-                lockWallpaperTargets,
-                0 /* startTime */,
-                false /* requestedShowSurfaceBehindKeyguard */
+            arrayOf(remoteTarget1, remoteTarget2),
+            wallpaperTargets,
+            lockWallpaperTargets,
+            0 /* startTime */,
+            false /* requestedShowSurfaceBehindKeyguard */
         )
 
         for (i in 0..10) {
@@ -367,19 +429,22 @@
             keyguardUnlockAnimationController.setSurfaceBehindAppearAmount(amount)
 
             val captorSb = ArgThatCaptor<SyncRtSurfaceTransactionApplier.SurfaceParams>()
-            verify(surfaceTransactionApplier, times(2)).scheduleApply(
+            verify(surfaceTransactionApplier, times(2))
+                .scheduleApply(
                     captorSb.capture { sp ->
-                        sp.surface == surfaceControlWp || sp.surface == surfaceControlLockWp })
+                        sp.surface == surfaceControlWp || sp.surface == surfaceControlLockWp
+                    }
+                )
 
             val fadeInAlpha = captorSb.getLastValue { it.surface == surfaceControlWp }.alpha
             val fadeOutAlpha = captorSb.getLastValue { it.surface == surfaceControlLockWp }.alpha
 
             if (amount == 0f) {
-                assertTrue (fadeInAlpha == 0f)
-                assertTrue (fadeOutAlpha == 1f)
+                assertTrue(fadeInAlpha == 0f)
+                assertTrue(fadeOutAlpha == 1f)
             } else if (amount == 1f) {
-                assertTrue (fadeInAlpha == 1f)
-                assertTrue (fadeOutAlpha == 0f)
+                assertTrue(fadeInAlpha == 1f)
+                assertTrue(fadeOutAlpha == 0f)
             } else {
                 assertTrue(fadeInAlpha >= lastFadeInAlpha)
                 assertTrue(fadeOutAlpha <= lastFadeOutAlpha)
@@ -389,18 +454,16 @@
         }
     }
 
-    /**
-     * If we are not wake and unlocking, we expect the unlock animation to play normally.
-     */
+    /** If we are not wake and unlocking, we expect the unlock animation to play normally. */
     @Test
     @DisableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
     fun surfaceAnimation_multipleTargets() {
         keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation(
-                arrayOf(remoteTarget1, remoteTarget2),
-                wallpaperTargets,
-                arrayOf(),
-                0 /* startTime */,
-                false /* requestedShowSurfaceBehindKeyguard */
+            arrayOf(remoteTarget1, remoteTarget2),
+            wallpaperTargets,
+            arrayOf(),
+            0 /* startTime */,
+            false /* requestedShowSurfaceBehindKeyguard */
         )
 
         // Cancel the animator so we can verify only the setSurfaceBehind call below.
@@ -412,12 +475,18 @@
         keyguardUnlockAnimationController.setSurfaceBehindAppearAmount(0.5f)
 
         val captorSb = ArgThatCaptor<SyncRtSurfaceTransactionApplier.SurfaceParams>()
-        verify(surfaceTransactionApplier, times(2)).scheduleApply(captorSb
-                .capture { sp -> sp.surface == surfaceControl1 || sp.surface == surfaceControl2 })
+        verify(surfaceTransactionApplier, times(2))
+            .scheduleApply(
+                captorSb.capture { sp ->
+                    sp.surface == surfaceControl1 || sp.surface == surfaceControl2
+                }
+            )
         val captorWp = ArgThatCaptor<SyncRtSurfaceTransactionApplier.SurfaceParams>()
-        verify(surfaceTransactionApplier, times(1).description(
-                "WallpaperSurface was expected to receive scheduleApply once"
-        )).scheduleApply(captorWp.capture { sp -> sp.surface == surfaceControlWp})
+        verify(
+                surfaceTransactionApplier,
+                times(1).description("WallpaperSurface was expected to receive scheduleApply once")
+            )
+            .scheduleApply(captorWp.capture { sp -> sp.surface == surfaceControlWp })
 
         val allParams = captorSb.getAllValues()
 
@@ -432,8 +501,8 @@
         assertTrue(remainingTargets.isEmpty())
 
         // Since the animation is running, we should not have finished the remote animation.
-        verify(keyguardViewMediator, times(0)).exitKeyguardAndFinishSurfaceBehindRemoteAnimation(
-                false /* cancelled */)
+        verify(keyguardViewMediator, times(0))
+            .exitKeyguardAndFinishSurfaceBehindRemoteAnimation(false /* cancelled */)
     }
 
     @Test
@@ -442,11 +511,11 @@
         whenever(powerManager.isInteractive).thenReturn(false)
 
         keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation(
-                remoteAnimationTargets,
-                wallpaperTargets,
-                arrayOf(),
-                0 /* startTime */,
-                false /* requestedShowSurfaceBehindKeyguard */
+            remoteAnimationTargets,
+            wallpaperTargets,
+            arrayOf(),
+            0 /* startTime */,
+            false /* requestedShowSurfaceBehindKeyguard */
         )
 
         // Cancel the animator so we can verify only the setSurfaceBehind call below.
@@ -457,12 +526,14 @@
         keyguardUnlockAnimationController.setWallpaperAppearAmount(1f, wallpaperTargets)
 
         val captorSb = ArgThatCaptor<SyncRtSurfaceTransactionApplier.SurfaceParams>()
-        verify(surfaceTransactionApplier, times(1)).scheduleApply(
-                captorSb.capture { sp -> sp.surface == surfaceControl1})
+        verify(surfaceTransactionApplier, times(1))
+            .scheduleApply(captorSb.capture { sp -> sp.surface == surfaceControl1 })
         val captorWp = ArgThatCaptor<SyncRtSurfaceTransactionApplier.SurfaceParams>()
-        verify(surfaceTransactionApplier, atLeastOnce().description("Wallpaper surface has  not " +
-                "received scheduleApply")).scheduleApply(
-                captorWp.capture { sp -> sp.surface == surfaceControlWp })
+        verify(
+                surfaceTransactionApplier,
+                atLeastOnce().description("Wallpaper surface has  not " + "received scheduleApply")
+            )
+            .scheduleApply(captorWp.capture { sp -> sp.surface == surfaceControlWp })
 
         val params = captorSb.getLastValue()
 
@@ -479,11 +550,11 @@
         whenever(powerManager.isInteractive).thenReturn(true)
 
         keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation(
-                remoteAnimationTargets,
-                wallpaperTargets,
-                arrayOf(),
-                0 /* startTime */,
-                false /* requestedShowSurfaceBehindKeyguard */
+            remoteAnimationTargets,
+            wallpaperTargets,
+            arrayOf(),
+            0 /* startTime */,
+            false /* requestedShowSurfaceBehindKeyguard */
         )
 
         // Stop the animator - we just want to test whether the override is not applied.
@@ -494,24 +565,31 @@
         keyguardUnlockAnimationController.setWallpaperAppearAmount(1f, wallpaperTargets)
 
         val captorSb = ArgThatCaptor<SyncRtSurfaceTransactionApplier.SurfaceParams>()
-        verify(surfaceTransactionApplier, times(1)).scheduleApply(
-                captorSb.capture { sp -> sp.surface == surfaceControl1 })
+        verify(surfaceTransactionApplier, times(1))
+            .scheduleApply(captorSb.capture { sp -> sp.surface == surfaceControl1 })
         val captorWp = ArgThatCaptor<SyncRtSurfaceTransactionApplier.SurfaceParams>()
-        verify(surfaceTransactionApplier, atLeastOnce().description("Wallpaper surface has  not " +
-                "received scheduleApply")).scheduleApply(
-                captorWp.capture { sp -> sp.surface == surfaceControlWp })
+        verify(
+                surfaceTransactionApplier,
+                atLeastOnce().description("Wallpaper surface has  not " + "received scheduleApply")
+            )
+            .scheduleApply(captorWp.capture { sp -> sp.surface == surfaceControlWp })
 
         val params = captorSb.getLastValue()
         assertEquals(1f, params.alpha)
         assertTrue(params.matrix.isIdentity)
-        assertEquals("Wallpaper surface was expected to have opacity 1",
-                1f, captorWp.getLastValue().alpha)
+        assertEquals(
+            "Wallpaper surface was expected to have opacity 1",
+            1f,
+            captorWp.getLastValue().alpha
+        )
 
         verifyNoMoreInteractions(surfaceTransactionApplier)
     }
 
     @Test
-    fun unlockToLauncherWithInWindowAnimations_ssViewIsVisible() {
+    fun unlockToLauncherWithInWindowAnimations_ssViewInVisible_whenPerformSSTransition() {
+        shouldPerformSmartspaceTransition = true
+
         val mockLockscreenSmartspaceView = mock(View::class.java)
         whenever(mockLockscreenSmartspaceView.visibility).thenReturn(View.VISIBLE)
         keyguardUnlockAnimationController.lockscreenSmartspace = mockLockscreenSmartspaceView
@@ -522,6 +600,19 @@
     }
 
     @Test
+    fun unlockToLauncherWithInWindowAnimations_ssViewVisible_whenNotPerformSSTransition() {
+        shouldPerformSmartspaceTransition = false
+
+        val mockLockscreenSmartspaceView = mock(View::class.java)
+        whenever(mockLockscreenSmartspaceView.visibility).thenReturn(View.VISIBLE)
+        keyguardUnlockAnimationController.lockscreenSmartspace = mockLockscreenSmartspaceView
+
+        keyguardUnlockAnimationController.unlockToLauncherWithInWindowAnimations()
+
+        verify(mockLockscreenSmartspaceView, never()).visibility = View.INVISIBLE
+    }
+
+    @Test
     fun unlockToLauncherWithInWindowAnimations_ssViewIsInvisible() {
         val mockLockscreenSmartspaceView = mock(View::class.java)
         whenever(mockLockscreenSmartspaceView.visibility).thenReturn(View.INVISIBLE)
@@ -591,7 +682,7 @@
         private var allArgs: MutableList<T> = mutableListOf()
 
         fun capture(predicate: Predicate<T>): T {
-            return argThat{x: T ->
+            return argThat { x: T ->
                 if (predicate.test(x)) {
                     allArgs.add(x)
                     return@argThat true
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
index e68a4a5..e3a38a8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -70,10 +70,10 @@
 import android.view.RemoteAnimationTarget;
 import android.view.View;
 import android.view.ViewRootImpl;
-import android.view.WindowManager;
 
 import androidx.test.filters.SmallTest;
 
+import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
 import com.android.internal.foldables.FoldGracePeriodProvider;
 import com.android.internal.logging.InstanceId;
 import com.android.internal.logging.UiEventLogger;
@@ -167,7 +167,7 @@
     private @Mock BroadcastDispatcher mBroadcastDispatcher;
     private @Mock DismissCallbackRegistry mDismissCallbackRegistry;
     private @Mock DumpManager mDumpManager;
-    private @Mock WindowManager mWindowManager;
+    private @Mock ViewCaptureAwareWindowManager mWindowManager;
     private @Mock IActivityManager mActivityManager;
     private @Mock ConfigurationController mConfigurationController;
     private @Mock PowerManager mPowerManager;
@@ -272,6 +272,7 @@
                 mShadeWindowLogger,
                 () -> mSelectedUserInteractor,
                 mUserTracker,
+                mKosmos.getNotificationShadeWindowModel(),
                 mKosmos::getCommunalInteractor);
         mFeatureFlags = new FakeFeatureFlags();
         mSetFlagsRule.enableFlags(FLAG_REFACTOR_GET_CURRENT_USER);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt
index 48a5df9..2af4d87 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt
@@ -45,6 +45,7 @@
 import java.math.RoundingMode
 import java.util.UUID
 import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.delay
 import kotlinx.coroutines.flow.dropWhile
 import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.flow.onEach
@@ -332,10 +333,11 @@
         }
 
     @Test
-    fun attemptTomanuallyUpdateTransitionWithInvalidUUIDthrowsException() {
-        underTest.updateTransition(UUID.randomUUID(), 0f, TransitionState.RUNNING)
-        assertThat(wtfHandler.failed).isTrue()
-    }
+    fun attemptTomanuallyUpdateTransitionWithInvalidUUIDthrowsException() =
+        testScope.runTest {
+            underTest.updateTransition(UUID.randomUUID(), 0f, TransitionState.RUNNING)
+            assertThat(wtfHandler.failed).isTrue()
+        }
 
     @Test
     fun attemptToManuallyUpdateTransitionAfterFINISHEDstateThrowsException() =
@@ -414,6 +416,64 @@
                 )
         }
 
+    @Test
+    fun simulateRaceConditionIsProcessedInOrderUsingUpdateTransition() =
+        testScope.runTest {
+            val ktr = KeyguardTransitionRepositoryImpl(kosmos.testDispatcher)
+            val steps by collectValues(ktr.transitions.dropWhile { step -> step.from == OFF })
+
+            // Begin a manual transition
+            val info1 = TransitionInfo(OWNER_NAME, AOD, LOCKSCREEN, animator = null)
+            launch {
+                ktr.forceDelayForRaceConditionTest = false
+                val uuid = ktr.startTransition(info1)
+
+                // Pause here to allow another transition to start
+                delay(20)
+
+                // Attempt to send an update, which should fail
+                ktr.updateTransition(uuid!!, 0.5f, TransitionState.RUNNING)
+            }
+
+            // Now start another transition, which should acquire the preempt the first
+            val info2 = TransitionInfo(OWNER_NAME, LOCKSCREEN, OCCLUDED, animator = null)
+            launch {
+                delay(10)
+                ktr.forceDelayForRaceConditionTest = true
+                ktr.startTransition(info2)
+            }
+
+            runCurrent()
+
+            // Manual transition has started
+            assertThat(steps[0])
+                .isEqualTo(
+                    TransitionStep(info1.from, info1.to, 0f, TransitionState.STARTED, OWNER_NAME)
+                )
+
+            // The second transition has requested to start, and grabbed the mutex. But it is
+            // delayed
+            advanceTimeBy(15L)
+
+            // Advancing another 10ms should now trigger the first transition to request an update,
+            // which should not happen as the second transition has the mutex
+            advanceTimeBy(10L)
+
+            // Finally, advance past the delay in the second transition so it can run
+            advanceTimeBy(50L)
+
+            assertThat(steps[1])
+                .isEqualTo(
+                    TransitionStep(info1.from, info1.to, 0f, TransitionState.CANCELED, OWNER_NAME)
+                )
+            assertThat(steps[2])
+                .isEqualTo(
+                    TransitionStep(info2.from, info2.to, 0f, TransitionState.STARTED, OWNER_NAME)
+                )
+
+            assertThat(steps.size).isEqualTo(3)
+        }
+
     private fun listWithStep(
         step: BigDecimal,
         start: BigDecimal = BigDecimal.ZERO,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorTest.kt
index a8271c1..2a2a82d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorTest.kt
@@ -31,8 +31,8 @@
 import com.android.systemui.classifier.FalsingCollector
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.coroutines.collectValues
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryBiometricsAllowedInteractor
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor
-import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFingerprintAuthInteractor
 import com.android.systemui.flags.EnableSceneContainer
 import com.android.systemui.keyguard.DismissCallbackRegistry
 import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
@@ -112,7 +112,7 @@
                 biometricSettingsRepository,
                 FakeSystemClock(),
                 keyguardUpdateMonitor,
-                { mock(DeviceEntryFingerprintAuthInteractor::class.java) },
+                { mock(DeviceEntryBiometricsAllowedInteractor::class.java) },
                 { mock(KeyguardInteractor::class.java) },
                 { mock(KeyguardTransitionInteractor::class.java) },
                 { kosmos.sceneInteractor },
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorTest.kt
index 8bc0a60..32d059b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorTest.kt
@@ -20,21 +20,26 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
 import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.flags.DisableSceneContainer
+import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
 import com.android.systemui.flags.EnableSceneContainer
 import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
-import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
 import com.android.systemui.keyguard.shared.model.DismissAction
 import com.android.systemui.keyguard.shared.model.KeyguardDone
-import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.scene.data.repository.Idle
+import com.android.systemui.scene.data.repository.Transition
 import com.android.systemui.scene.data.repository.setSceneTransition
+import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.scene.domain.resolver.notifShadeSceneFamilyResolver
+import com.android.systemui.scene.domain.resolver.quickSettingsSceneFamilyResolver
 import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
@@ -44,13 +49,12 @@
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
+@EnableSceneContainer
 @RunWith(AndroidJUnit4::class)
 class KeyguardDismissActionInteractorTest : SysuiTestCase() {
     val kosmos = testKosmos()
 
     private val keyguardRepository = kosmos.fakeKeyguardRepository
-    private val transitionRepository = kosmos.fakeKeyguardTransitionRepository
-
     private val testScope = kosmos.testScope
 
     private lateinit var dismissInteractorWithDependencies:
@@ -74,6 +78,10 @@
                 transitionInteractor = kosmos.keyguardTransitionInteractor,
                 dismissInteractor = dismissInteractorWithDependencies.interactor,
                 applicationScope = testScope.backgroundScope,
+                sceneInteractor = kosmos.sceneInteractor,
+                deviceEntryInteractor = kosmos.deviceEntryInteractor,
+                quickSettingsSceneFamilyResolver = kosmos.quickSettingsSceneFamilyResolver,
+                notifShadeSceneFamilyResolver = kosmos.notifShadeSceneFamilyResolver,
             )
     }
 
@@ -158,7 +166,6 @@
         }
 
     @Test
-    @DisableSceneContainer
     fun executeDismissAction_dismissKeyguardRequestWithoutImmediateDismissAction() =
         testScope.runTest {
             val executeDismissAction by collectLastValue(underTest.executeDismissAction)
@@ -175,33 +182,6 @@
             )
             assertThat(executeDismissAction).isNull()
 
-            // WHEN the keyguard is GONE
-            transitionRepository.sendTransitionSteps(
-                from = KeyguardState.LOCKSCREEN,
-                to = KeyguardState.GONE,
-                testScope,
-            )
-            assertThat(executeDismissAction).isNotNull()
-        }
-
-    @Test
-    @EnableSceneContainer
-    fun executeDismissAction_dismissKeyguardRequestWithoutImmediateDismissAction_scene_container() =
-        testScope.runTest {
-            val executeDismissAction by collectLastValue(underTest.executeDismissAction)
-
-            // WHEN a keyguard action will run after the keyguard is gone
-            val onDismissAction = {}
-            keyguardRepository.setDismissAction(
-                DismissAction.RunAfterKeyguardGone(
-                    dismissAction = onDismissAction,
-                    onCancelAction = {},
-                    message = "message",
-                    willAnimateOnLockscreen = true,
-                )
-            )
-            assertThat(executeDismissAction).isNull()
-
             kosmos.setSceneTransition(Idle(Scenes.Gone))
 
             assertThat(executeDismissAction).isNotNull()
@@ -210,8 +190,8 @@
     @Test
     fun resetDismissAction() =
         testScope.runTest {
+            kosmos.setSceneTransition(Idle(Scenes.Bouncer))
             val resetDismissAction by collectLastValue(underTest.resetDismissAction)
-
             keyguardRepository.setDismissAction(
                 DismissAction.RunAfterKeyguardGone(
                     dismissAction = {},
@@ -220,15 +200,40 @@
                     willAnimateOnLockscreen = true,
                 )
             )
-            transitionRepository.sendTransitionSteps(
-                from = KeyguardState.LOCKSCREEN,
-                to = KeyguardState.AOD,
-                testScope
-            )
+            assertThat(resetDismissAction).isNull()
+            kosmos.setSceneTransition(Idle(Scenes.Lockscreen))
             assertThat(resetDismissAction).isEqualTo(Unit)
         }
 
     @Test
+    fun doNotResetDismissActionOnUnlockedShade() =
+        testScope.runTest {
+            kosmos.setSceneTransition(Idle(Scenes.Bouncer))
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
+                AuthenticationMethodModel.None
+            )
+            val resetDismissAction by collectLastValue(underTest.resetDismissAction)
+            keyguardRepository.setDismissAction(
+                DismissAction.RunAfterKeyguardGone(
+                    dismissAction = {},
+                    onCancelAction = {},
+                    message = "message",
+                    willAnimateOnLockscreen = true,
+                )
+            )
+            assertThat(resetDismissAction).isNull()
+
+            kosmos.setSceneTransition(
+                Transition(
+                    from = Scenes.Bouncer,
+                    to = Scenes.NotificationsShade,
+                    progress = flowOf(1f),
+                )
+            )
+            assertThat(resetDismissAction).isNull()
+        }
+
+    @Test
     fun setDismissAction_callsCancelRunnableOnPreviousDismissAction() =
         testScope.runTest {
             val dismissAction by collectLastValue(underTest.dismissAction)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
index 7560a97..e3bdcd7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
@@ -23,6 +23,7 @@
 import androidx.test.filters.FlakyTest
 import androidx.test.filters.SmallTest
 import com.android.internal.widget.LockPatternUtils
+import com.android.keyguard.logging.KeyguardQuickAffordancesLogger
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.animation.ActivityTransitionAnimator
 import com.android.systemui.animation.DialogTransitionAnimator
@@ -232,7 +233,8 @@
     @Mock private lateinit var expandable: Expandable
     @Mock private lateinit var launchAnimator: DialogTransitionAnimator
     @Mock private lateinit var devicePolicyManager: DevicePolicyManager
-    @Mock private lateinit var logger: KeyguardQuickAffordancesMetricsLogger
+    @Mock private lateinit var logger: KeyguardQuickAffordancesLogger
+    @Mock private lateinit var metricsLogger: KeyguardQuickAffordancesMetricsLogger
 
     private lateinit var underTest: KeyguardQuickAffordanceInteractor
     private lateinit var testScope: TestScope
@@ -327,6 +329,7 @@
                 repository = { quickAffordanceRepository },
                 launchAnimator = launchAnimator,
                 logger = logger,
+                metricsLogger = metricsLogger,
                 devicePolicyManager = devicePolicyManager,
                 dockManager = dockManager,
                 biometricSettingsRepository = biometricSettingsRepository,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorSceneContainerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorSceneContainerTest.kt
index fd1bf54..591ce1a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorSceneContainerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorSceneContainerTest.kt
@@ -23,6 +23,7 @@
 import androidx.test.filters.FlakyTest
 import androidx.test.filters.SmallTest
 import com.android.internal.widget.LockPatternUtils
+import com.android.keyguard.logging.KeyguardQuickAffordancesLogger
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.animation.ActivityTransitionAnimator
 import com.android.systemui.animation.DialogTransitionAnimator
@@ -232,7 +233,8 @@
     @Mock private lateinit var expandable: Expandable
     @Mock private lateinit var launchAnimator: DialogTransitionAnimator
     @Mock private lateinit var devicePolicyManager: DevicePolicyManager
-    @Mock private lateinit var logger: KeyguardQuickAffordancesMetricsLogger
+    @Mock private lateinit var logger: KeyguardQuickAffordancesLogger
+    @Mock private lateinit var metricsLogger: KeyguardQuickAffordancesMetricsLogger
 
     private lateinit var underTest: KeyguardQuickAffordanceInteractor
     private lateinit var testScope: TestScope
@@ -327,6 +329,7 @@
                 repository = { quickAffordanceRepository },
                 launchAnimator = launchAnimator,
                 logger = logger,
+                metricsLogger = metricsLogger,
                 devicePolicyManager = devicePolicyManager,
                 dockManager = dockManager,
                 biometricSettingsRepository = biometricSettingsRepository,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
index 3b96be4..fc7f693 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
@@ -23,6 +23,7 @@
 import androidx.test.filters.SmallTest
 import com.android.internal.logging.testing.UiEventLoggerFake
 import com.android.internal.widget.LockPatternUtils
+import com.android.keyguard.logging.KeyguardQuickAffordancesLogger
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.animation.DialogTransitionAnimator
 import com.android.systemui.animation.Expandable
@@ -99,7 +100,8 @@
     @Mock private lateinit var activityStarter: ActivityStarter
     @Mock private lateinit var launchAnimator: DialogTransitionAnimator
     @Mock private lateinit var devicePolicyManager: DevicePolicyManager
-    @Mock private lateinit var logger: KeyguardQuickAffordancesMetricsLogger
+    @Mock private lateinit var logger: KeyguardQuickAffordancesLogger
+    @Mock private lateinit var metricsLogger: KeyguardQuickAffordancesMetricsLogger
     @Mock private lateinit var broadcastDispatcher: BroadcastDispatcher
     @Mock private lateinit var accessibilityManager: AccessibilityManagerWrapper
 
@@ -237,6 +239,7 @@
                         repository = { quickAffordanceRepository },
                         launchAnimator = launchAnimator,
                         logger = logger,
+                        metricsLogger = metricsLogger,
                         devicePolicyManager = devicePolicyManager,
                         dockManager = dockManager,
                         biometricSettingsRepository = biometricSettingsRepository,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt
index e89abf6..77977f3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt
@@ -23,6 +23,7 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.internal.widget.LockPatternUtils
+import com.android.keyguard.logging.KeyguardQuickAffordancesLogger
 import com.android.systemui.Flags as AConfigFlags
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.animation.DialogTransitionAnimator
@@ -93,7 +94,8 @@
     @Mock private lateinit var lockPatternUtils: LockPatternUtils
     @Mock private lateinit var keyguardStateController: KeyguardStateController
     @Mock private lateinit var launchAnimator: DialogTransitionAnimator
-    @Mock private lateinit var logger: KeyguardQuickAffordancesMetricsLogger
+    @Mock private lateinit var logger: KeyguardQuickAffordancesLogger
+    @Mock private lateinit var metricsLogger: KeyguardQuickAffordancesMetricsLogger
     @Mock private lateinit var shadeInteractor: ShadeInteractor
     @Mock
     private lateinit var aodToLockscreenTransitionViewModel: AodToLockscreenTransitionViewModel
@@ -299,6 +301,7 @@
                         repository = { quickAffordanceRepository },
                         launchAnimator = launchAnimator,
                         logger = logger,
+                        metricsLogger = metricsLogger,
                         devicePolicyManager = devicePolicyManager,
                         dockManager = dockManager,
                         biometricSettingsRepository = biometricSettingsRepository,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/lifecycle/RepeatWhenAttachedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/lifecycle/RepeatWhenAttachedTest.kt
index afab250..d3409c7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/lifecycle/RepeatWhenAttachedTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/lifecycle/RepeatWhenAttachedTest.kt
@@ -24,12 +24,14 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.util.Assert
-import com.android.systemui.util.mockito.argumentCaptor
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.DisposableHandle
 import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.awaitCancellation
+import kotlinx.coroutines.launch
 import kotlinx.coroutines.test.StandardTestDispatcher
 import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.resetMain
@@ -47,6 +49,8 @@
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.`when` as whenever
 import org.mockito.junit.MockitoJUnit
+import org.mockito.kotlin.KArgumentCaptor
+import org.mockito.kotlin.argumentCaptor
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
@@ -96,6 +100,14 @@
         }
 
     @Test(expected = IllegalStateException::class)
+    fun repeatWhenAttachedToWindow_enforcesMainThread() =
+        testScope.runTest {
+            Assert.setTestThread(null)
+
+            view.repeatWhenAttachedToWindow {}
+        }
+
+    @Test(expected = IllegalStateException::class)
     fun repeatWhenAttached_disposeEnforcesMainThread() =
         testScope.runTest {
             val disposableHandle = repeatWhenAttached()
@@ -120,6 +132,58 @@
         }
 
     @Test
+    fun repeatWhenAttachedToWindow_viewAlreadyAttached_immediatelyRunsBlock() =
+        testScope.runTest {
+            whenever(view.isAttachedToWindow).thenReturn(true)
+
+            var innerJob: Job? = null
+            backgroundScope.launch {
+                view.repeatWhenAttachedToWindow { innerJob = launch { awaitCancellation() } }
+            }
+            runCurrent()
+
+            assertThat(innerJob?.isActive).isEqualTo(true)
+        }
+
+    @Test
+    fun repeatWhenAttachedToWindow_viewStartsDetached_runsBlockWhenAttached() =
+        testScope.runTest {
+            whenever(view.isAttachedToWindow).thenReturn(false)
+            var innerJob: Job? = null
+            backgroundScope.launch {
+                view.repeatWhenAttachedToWindow { innerJob = launch { awaitCancellation() } }
+            }
+            runCurrent()
+
+            assertThat(innerJob?.isActive).isNotEqualTo(true)
+
+            whenever(view.isAttachedToWindow).thenReturn(true)
+            attachListeners.last().onViewAttachedToWindow(view)
+            runCurrent()
+
+            assertThat(innerJob?.isActive).isEqualTo(true)
+        }
+
+    @Test
+    fun repeatWhenAttachedToWindow_viewGetsDetached_cancelsBlock() =
+        testScope.runTest {
+            whenever(view.isAttachedToWindow).thenReturn(true)
+            var innerJob: Job? = null
+            backgroundScope.launch {
+                view.repeatWhenAttachedToWindow { innerJob = launch { awaitCancellation() } }
+            }
+            runCurrent()
+
+            assertThat(innerJob?.isActive).isEqualTo(true)
+
+            whenever(view.isAttachedToWindow).thenReturn(false)
+            attachListeners.last().onViewDetachedFromWindow(view)
+            runCurrent()
+
+            assertThat(innerJob?.isActive).isNotEqualTo(true)
+        }
+
+    @Test
     fun repeatWhenAttached_viewAlreadyAttached_immediatelyRunsBlock() =
         testScope.runTest {
             whenever(view.isAttachedToWindow).thenReturn(true)
@@ -145,6 +209,65 @@
         }
 
     @Test
+    fun repeatWhenWindowIsVisible_startsAlreadyVisible_immediatelyRunsBlock() =
+        testScope.runTest {
+            whenever(view.isAttachedToWindow).thenReturn(true)
+            whenever(view.windowVisibility).thenReturn(View.VISIBLE)
+
+            var innerJob: Job? = null
+            backgroundScope.launch {
+                view.repeatWhenWindowIsVisible { innerJob = launch { awaitCancellation() } }
+            }
+            runCurrent()
+
+            assertThat(innerJob?.isActive).isEqualTo(true)
+        }
+
+    @Test
+    fun repeatWhenWindowIsVisible_startsInvisible_runsBlockWhenVisible() =
+        testScope.runTest {
+            whenever(view.isAttachedToWindow).thenReturn(true)
+            whenever(view.windowVisibility).thenReturn(View.INVISIBLE)
+
+            var innerJob: Job? = null
+            backgroundScope.launch {
+                view.repeatWhenWindowIsVisible { innerJob = launch { awaitCancellation() } }
+            }
+            runCurrent()
+
+            assertThat(innerJob?.isActive).isNotEqualTo(true)
+
+            whenever(view.windowVisibility).thenReturn(View.VISIBLE)
+            argCaptor { verify(viewTreeObserver).addOnWindowVisibilityChangeListener(capture()) }
+                .forEach { it.onWindowVisibilityChanged(View.VISIBLE) }
+            runCurrent()
+
+            assertThat(innerJob?.isActive).isEqualTo(true)
+        }
+
+    @Test
+    fun repeatWhenWindowIsVisible_becomesInvisible_cancelsBlock() =
+        testScope.runTest {
+            whenever(view.isAttachedToWindow).thenReturn(true)
+            whenever(view.windowVisibility).thenReturn(View.VISIBLE)
+
+            var innerJob: Job? = null
+            backgroundScope.launch {
+                view.repeatWhenWindowIsVisible { innerJob = launch { awaitCancellation() } }
+            }
+            runCurrent()
+
+            assertThat(innerJob?.isActive).isEqualTo(true)
+
+            whenever(view.windowVisibility).thenReturn(View.INVISIBLE)
+            argCaptor { verify(viewTreeObserver).addOnWindowVisibilityChangeListener(capture()) }
+                .forEach { it.onWindowVisibilityChanged(View.INVISIBLE) }
+            runCurrent()
+
+            assertThat(innerJob?.isActive).isNotEqualTo(true)
+        }
+
+    @Test
     fun repeatWhenAttached_startsWithFocusButInvisible_CREATED() =
         testScope.runTest {
             whenever(view.isAttachedToWindow).thenReturn(true)
@@ -172,6 +295,69 @@
         }
 
     @Test
+    fun repeatWhenWindowHasFocus_startsWithFocus_immediatelyRunsBlock() =
+        testScope.runTest {
+            whenever(view.isAttachedToWindow).thenReturn(true)
+            whenever(view.windowVisibility).thenReturn(View.VISIBLE)
+            whenever(view.hasWindowFocus()).thenReturn(true)
+
+            var innerJob: Job? = null
+            backgroundScope.launch {
+                view.repeatWhenWindowHasFocus { innerJob = launch { awaitCancellation() } }
+            }
+            runCurrent()
+
+            assertThat(innerJob?.isActive).isEqualTo(true)
+        }
+
+    @Test
+    fun repeatWhenWindowHasFocus_startsWithoutFocus_runsBlockWhenFocused() =
+        testScope.runTest {
+            whenever(view.isAttachedToWindow).thenReturn(true)
+            whenever(view.windowVisibility).thenReturn(View.VISIBLE)
+            whenever(view.hasWindowFocus()).thenReturn(false)
+
+            var innerJob: Job? = null
+            backgroundScope.launch {
+                view.repeatWhenWindowHasFocus { innerJob = launch { awaitCancellation() } }
+            }
+            runCurrent()
+
+            assertThat(innerJob?.isActive).isNotEqualTo(true)
+
+            whenever(view.hasWindowFocus()).thenReturn(true)
+
+            argCaptor { verify(viewTreeObserver).addOnWindowFocusChangeListener(capture()) }
+                .forEach { it.onWindowFocusChanged(true) }
+            runCurrent()
+
+            assertThat(innerJob?.isActive).isEqualTo(true)
+        }
+
+    @Test
+    fun repeatWhenWindowHasFocus_losesFocus_cancelsBlock() =
+        testScope.runTest {
+            whenever(view.isAttachedToWindow).thenReturn(true)
+            whenever(view.windowVisibility).thenReturn(View.VISIBLE)
+            whenever(view.hasWindowFocus()).thenReturn(true)
+
+            var innerJob: Job? = null
+            backgroundScope.launch {
+                view.repeatWhenWindowHasFocus { innerJob = launch { awaitCancellation() } }
+            }
+            runCurrent()
+
+            assertThat(innerJob?.isActive).isEqualTo(true)
+
+            whenever(view.hasWindowFocus()).thenReturn(false)
+            argCaptor { verify(viewTreeObserver).addOnWindowFocusChangeListener(capture()) }
+                .forEach { it.onWindowFocusChanged(false) }
+            runCurrent()
+
+            assertThat(innerJob?.isActive).isNotEqualTo(true)
+        }
+
+    @Test
     fun repeatWhenAttached_becomesVisibleWithoutFocus_STARTED() =
         testScope.runTest {
             whenever(view.isAttachedToWindow).thenReturn(true)
@@ -180,7 +366,7 @@
             verify(viewTreeObserver).addOnWindowVisibilityChangeListener(listenerCaptor.capture())
 
             whenever(view.windowVisibility).thenReturn(View.VISIBLE)
-            listenerCaptor.value.onWindowVisibilityChanged(View.VISIBLE)
+            listenerCaptor.lastValue.onWindowVisibilityChanged(View.VISIBLE)
 
             runCurrent()
             assertThat(block.invocationCount).isEqualTo(1)
@@ -196,7 +382,7 @@
             verify(viewTreeObserver).addOnWindowFocusChangeListener(listenerCaptor.capture())
 
             whenever(view.hasWindowFocus()).thenReturn(true)
-            listenerCaptor.value.onWindowFocusChanged(true)
+            listenerCaptor.lastValue.onWindowFocusChanged(true)
 
             runCurrent()
             assertThat(block.invocationCount).isEqualTo(1)
@@ -214,9 +400,9 @@
             verify(viewTreeObserver).addOnWindowFocusChangeListener(focusCaptor.capture())
 
             whenever(view.windowVisibility).thenReturn(View.VISIBLE)
-            visibleCaptor.value.onWindowVisibilityChanged(View.VISIBLE)
+            visibleCaptor.lastValue.onWindowVisibilityChanged(View.VISIBLE)
             whenever(view.hasWindowFocus()).thenReturn(true)
-            focusCaptor.value.onWindowFocusChanged(true)
+            focusCaptor.lastValue.onWindowFocusChanged(true)
 
             runCurrent()
             assertThat(block.invocationCount).isEqualTo(1)
@@ -314,6 +500,7 @@
         val invocations: List<Invocation> = _invocations
         val invocationCount: Int
             get() = _invocations.size
+
         val latestLifecycleState: Lifecycle.State
             get() = _invocations.last().lifecycleState
 
@@ -322,3 +509,9 @@
         }
     }
 }
+
+private inline fun <reified T : Any> argCaptor(block: KArgumentCaptor<T>.() -> Unit) =
+    argumentCaptor<T>().apply { block() }
+
+private inline fun <reified T : Any> KArgumentCaptor<T>.forEach(block: (T) -> Unit): Unit =
+    allValues.forEach(block)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaControlPanelTest.kt
index fbfe41f..521aa5a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaControlPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaControlPanelTest.kt
@@ -69,6 +69,7 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.bluetooth.BroadcastDialogController
 import com.android.systemui.broadcast.BroadcastSender
+import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
 import com.android.systemui.media.controls.MediaTestUtils
 import com.android.systemui.media.controls.domain.pipeline.EMPTY_SMARTSPACE_MEDIA_DATA
 import com.android.systemui.media.controls.domain.pipeline.MediaDataManager
@@ -211,6 +212,8 @@
     @Mock private lateinit var activityIntentHelper: ActivityIntentHelper
     @Mock private lateinit var lockscreenUserManager: NotificationLockscreenUserManager
 
+    @Mock private lateinit var communalSceneInteractor: CommunalSceneInteractor
+
     @Mock private lateinit var recommendationViewHolder: RecommendationViewHolder
     @Mock private lateinit var smartspaceAction: SmartspaceAction
     private lateinit var smartspaceData: SmartspaceMediaData
@@ -271,6 +274,7 @@
                     logger,
                     keyguardStateController,
                     activityIntentHelper,
+                    communalSceneInteractor,
                     lockscreenUserManager,
                     broadcastDialogController,
                     globalSettings,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionDialogDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/permission/ShareToAppPermissionDialogDelegateTest.kt
similarity index 77%
rename from packages/SystemUI/tests/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionDialogDelegateTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/mediaprojection/permission/ShareToAppPermissionDialogDelegateTest.kt
index 7dd8028..c57aa36 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionDialogDelegateTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/permission/ShareToAppPermissionDialogDelegateTest.kt
@@ -25,28 +25,23 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.flags.FeatureFlagsClassic
-import com.android.systemui.flags.Flags
 import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.phone.AlertDialogWithDelegate
 import com.android.systemui.statusbar.phone.SystemUIDialog
-import com.android.systemui.util.mockito.mock
-import junit.framework.Assert.assertEquals
+import kotlin.test.assertEquals
 import org.junit.After
-import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.mockito.Mockito.`when` as whenever
+import org.mockito.kotlin.mock
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
-class MediaProjectionPermissionDialogDelegateTest : SysuiTestCase() {
+class ShareToAppPermissionDialogDelegateTest : SysuiTestCase() {
 
     private lateinit var dialog: AlertDialog
 
-    private val flags = mock<FeatureFlagsClassic>()
     private val appName = "Test App"
 
     private val resIdSingleApp = R.string.screen_share_permission_dialog_option_single_app
@@ -54,11 +49,6 @@
     private val resIdSingleAppDisabled =
         R.string.media_projection_entry_app_permission_dialog_single_app_disabled
 
-    @Before
-    fun setUp() {
-        whenever(flags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING)).thenReturn(true)
-    }
-
     @After
     fun teardown() {
         if (::dialog.isInitialized) {
@@ -125,39 +115,16 @@
         assertEquals(context.getString(resIdFullScreen), secondOptionText)
     }
 
-    @Test
-    fun showDialog_disableSingleApp_hasCastingCapabilities() {
-        setUpAndShowDialog(
-            mediaProjectionConfig = MediaProjectionConfig.createConfigForDefaultDisplay(),
-            hasCastingCapabilities = true
-        )
-
-        val spinner = dialog.requireViewById<Spinner>(R.id.screen_share_mode_options)
-        val secondOptionWarningText =
-            spinner.adapter
-                .getDropDownView(1, null, spinner)
-                .findViewById<TextView>(android.R.id.text2)
-                ?.text
-
-        // check that the first option is full screen and enabled
-        assertEquals(context.getString(resIdFullScreen), spinner.selectedItem)
-
-        // check that the second option is single app and disabled
-        assertEquals(context.getString(resIdSingleAppDisabled, appName), secondOptionWarningText)
-    }
-
     private fun setUpAndShowDialog(
         mediaProjectionConfig: MediaProjectionConfig? = null,
         overrideDisableSingleAppOption: Boolean = false,
-        hasCastingCapabilities: Boolean = false,
     ) {
         val delegate =
-            MediaProjectionPermissionDialogDelegate(
+            ShareToAppPermissionDialogDelegate(
                 context,
                 mediaProjectionConfig,
                 onStartRecordingClicked = {},
                 onCancelClicked = {},
-                hasCastingCapabilities,
                 appName,
                 overrideDisableSingleAppOption,
                 hostUid = 12345,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionDialogDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/permission/SystemCastPermissionDialogDelegateTest.kt
similarity index 73%
copy from packages/SystemUI/tests/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionDialogDelegateTest.kt
copy to packages/SystemUI/tests/src/com/android/systemui/mediaprojection/permission/SystemCastPermissionDialogDelegateTest.kt
index 7dd8028..59602dc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionDialogDelegateTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/permission/SystemCastPermissionDialogDelegateTest.kt
@@ -25,40 +25,32 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.flags.FeatureFlagsClassic
-import com.android.systemui.flags.Flags
 import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.phone.AlertDialogWithDelegate
 import com.android.systemui.statusbar.phone.SystemUIDialog
-import com.android.systemui.util.mockito.mock
-import junit.framework.Assert.assertEquals
+import kotlin.test.assertEquals
 import org.junit.After
-import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.mockito.Mockito.`when` as whenever
+import org.mockito.kotlin.mock
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
-class MediaProjectionPermissionDialogDelegateTest : SysuiTestCase() {
+class SystemCastPermissionDialogDelegateTest : SysuiTestCase() {
 
     private lateinit var dialog: AlertDialog
 
-    private val flags = mock<FeatureFlagsClassic>()
     private val appName = "Test App"
 
-    private val resIdSingleApp = R.string.screen_share_permission_dialog_option_single_app
-    private val resIdFullScreen = R.string.screen_share_permission_dialog_option_entire_screen
+    private val resIdSingleApp =
+        R.string.media_projection_entry_cast_permission_dialog_option_text_single_app
+    private val resIdFullScreen =
+        R.string.media_projection_entry_cast_permission_dialog_option_text_entire_screen
     private val resIdSingleAppDisabled =
         R.string.media_projection_entry_app_permission_dialog_single_app_disabled
 
-    @Before
-    fun setUp() {
-        whenever(flags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING)).thenReturn(true)
-    }
-
     @After
     fun teardown() {
         if (::dialog.isInitialized) {
@@ -108,7 +100,7 @@
     fun showDialog_disableSingleApp_forceShowPartialScreenShareTrue() {
         setUpAndShowDialog(
             mediaProjectionConfig = MediaProjectionConfig.createConfigForDefaultDisplay(),
-            overrideDisableSingleAppOption = true
+            overrideDisableSingleAppOption = true,
         )
 
         val spinner = dialog.requireViewById<Spinner>(R.id.screen_share_mode_options)
@@ -125,43 +117,20 @@
         assertEquals(context.getString(resIdFullScreen), secondOptionText)
     }
 
-    @Test
-    fun showDialog_disableSingleApp_hasCastingCapabilities() {
-        setUpAndShowDialog(
-            mediaProjectionConfig = MediaProjectionConfig.createConfigForDefaultDisplay(),
-            hasCastingCapabilities = true
-        )
-
-        val spinner = dialog.requireViewById<Spinner>(R.id.screen_share_mode_options)
-        val secondOptionWarningText =
-            spinner.adapter
-                .getDropDownView(1, null, spinner)
-                .findViewById<TextView>(android.R.id.text2)
-                ?.text
-
-        // check that the first option is full screen and enabled
-        assertEquals(context.getString(resIdFullScreen), spinner.selectedItem)
-
-        // check that the second option is single app and disabled
-        assertEquals(context.getString(resIdSingleAppDisabled, appName), secondOptionWarningText)
-    }
-
     private fun setUpAndShowDialog(
         mediaProjectionConfig: MediaProjectionConfig? = null,
         overrideDisableSingleAppOption: Boolean = false,
-        hasCastingCapabilities: Boolean = false,
     ) {
         val delegate =
-            MediaProjectionPermissionDialogDelegate(
+            SystemCastPermissionDialogDelegate(
                 context,
                 mediaProjectionConfig,
                 onStartRecordingClicked = {},
                 onCancelClicked = {},
-                hasCastingCapabilities,
                 appName,
                 overrideDisableSingleAppOption,
                 hostUid = 12345,
-                mediaProjectionMetricsLogger = mock<MediaProjectionMetricsLogger>()
+                mediaProjectionMetricsLogger = mock<MediaProjectionMetricsLogger>(),
             )
 
         dialog = AlertDialogWithDelegate(context, R.style.Theme_SystemUI_Dialog, delegate)
@@ -169,7 +138,7 @@
         SystemUIDialog.setDialogSize(dialog)
 
         dialog.window?.addSystemFlags(
-            WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS
+            WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS,
         )
 
         delegate.onCreate(dialog, savedInstanceState = null)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerImplTest.java
index 196bbb9..413aa55 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerImplTest.java
@@ -307,7 +307,7 @@
 
         mNavigationBarController.mIsLargeScreen = false;
         mNavigationBarController.mIsPhone = true;
-        assertFalse(mNavigationBarController.supportsTaskbar());
+        assertTrue(mNavigationBarController.supportsTaskbar());
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/views/NavigationBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/views/NavigationBarTest.java
index 45d77f6..a8cbbd4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/views/NavigationBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/views/NavigationBarTest.java
@@ -498,7 +498,7 @@
         defaultNavBar.init();
         externalNavBar.init();
 
-        defaultNavBar.setImeWindowStatus(DEFAULT_DISPLAY, null, IME_VISIBLE,
+        defaultNavBar.setImeWindowStatus(DEFAULT_DISPLAY, IME_VISIBLE,
                 BACK_DISPOSITION_DEFAULT, true);
 
         // Verify IME window state will be updated in default NavBar & external NavBar state reset.
@@ -510,10 +510,10 @@
         assertFalse((externalNavBar.getNavigationIconHints() & NAVIGATION_HINT_IME_SWITCHER_SHOWN)
                 != 0);
 
-        externalNavBar.setImeWindowStatus(EXTERNAL_DISPLAY_ID, null, IME_VISIBLE,
+        externalNavBar.setImeWindowStatus(EXTERNAL_DISPLAY_ID, IME_VISIBLE,
                 BACK_DISPOSITION_DEFAULT, true);
-        defaultNavBar.setImeWindowStatus(
-                DEFAULT_DISPLAY, null, IME_INVISIBLE, BACK_DISPOSITION_DEFAULT, false);
+        defaultNavBar.setImeWindowStatus(DEFAULT_DISPLAY, IME_INVISIBLE,
+                BACK_DISPOSITION_DEFAULT, false);
         // Verify IME window state will be updated in external NavBar & default NavBar state reset.
         assertEquals(NAVIGATION_HINT_BACK_ALT | NAVIGATION_HINT_IME_SHOWN
                         | NAVIGATION_HINT_IME_SWITCHER_SHOWN,
@@ -535,7 +535,7 @@
         doReturn(windowInsets).when(mockShadeWindowView).getRootWindowInsets();
 
         // Verify navbar altered back icon when an app is showing IME
-        mNavigationBar.setImeWindowStatus(DEFAULT_DISPLAY, null, IME_VISIBLE,
+        mNavigationBar.setImeWindowStatus(DEFAULT_DISPLAY, IME_VISIBLE,
                 BACK_DISPOSITION_DEFAULT, true);
         assertTrue((mNavigationBar.getNavigationIconHints() & NAVIGATION_HINT_BACK_ALT) != 0);
         assertTrue((mNavigationBar.getNavigationIconHints() & NAVIGATION_HINT_IME_SHOWN) != 0);
@@ -545,7 +545,7 @@
         // Verify navbar didn't alter and showing back icon when the keyguard is showing without
         // requesting IME insets visible.
         doReturn(true).when(mKeyguardStateController).isShowing();
-        mNavigationBar.setImeWindowStatus(DEFAULT_DISPLAY, null, IME_VISIBLE,
+        mNavigationBar.setImeWindowStatus(DEFAULT_DISPLAY, IME_VISIBLE,
                 BACK_DISPOSITION_DEFAULT, true);
         assertFalse((mNavigationBar.getNavigationIconHints() & NAVIGATION_HINT_BACK_ALT) != 0);
         assertFalse((mNavigationBar.getNavigationIconHints() & NAVIGATION_HINT_IME_SHOWN) != 0);
@@ -556,7 +556,7 @@
         // requesting IME insets visible.
         windowInsets = new WindowInsets.Builder().setVisible(ime(), true).build();
         doReturn(windowInsets).when(mockShadeWindowView).getRootWindowInsets();
-        mNavigationBar.setImeWindowStatus(DEFAULT_DISPLAY, null, IME_VISIBLE,
+        mNavigationBar.setImeWindowStatus(DEFAULT_DISPLAY, IME_VISIBLE,
                 BACK_DISPOSITION_DEFAULT, true);
         assertTrue((mNavigationBar.getNavigationIconHints() & NAVIGATION_HINT_BACK_ALT) != 0);
         assertTrue((mNavigationBar.getNavigationIconHints() & NAVIGATION_HINT_IME_SHOWN) != 0);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
index 0196f95..80a9e4c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
@@ -62,9 +62,8 @@
 import com.android.systemui.util.mockito.capture
 import com.android.systemui.util.mockito.eq
 import com.android.systemui.util.mockito.mock
-import com.android.systemui.util.mockito.whenever
 import com.android.systemui.util.mockito.withArgCaptor
-import com.android.systemui.util.settings.SecureSettings
+import com.android.systemui.util.settings.FakeSettings
 import com.android.wm.shell.bubbles.Bubble
 import com.android.wm.shell.bubbles.Bubbles
 import com.google.common.truth.Truth.assertThat
@@ -86,6 +85,7 @@
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.verifyZeroInteractions
 import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.whenever
 
 /** atest SystemUITests:NoteTaskControllerTest */
 @OptIn(ExperimentalCoroutinesApi::class)
@@ -106,10 +106,10 @@
     @Mock private lateinit var shortcutManager: ShortcutManager
     @Mock private lateinit var activityManager: ActivityManager
     @Mock private lateinit var devicePolicyManager: DevicePolicyManager
-    @Mock private lateinit var secureSettings: SecureSettings
     private val userTracker = FakeUserTracker()
     private val testDispatcher = UnconfinedTestDispatcher()
     private val testScope = TestScope(testDispatcher)
+    private val secureSettings = FakeSettings(testDispatcher) { userTracker.userId }
 
     @Before
     fun setUp() {
@@ -139,7 +139,6 @@
         whenever(activityManager.getRunningTasks(anyInt())).thenReturn(emptyList())
         whenever(userManager.isManagedProfile(workUserInfo.id)).thenReturn(true)
         whenever(context.resources).thenReturn(getContext().resources)
-        whenever(secureSettings.userTracker).thenReturn(userTracker)
     }
 
     private fun createNoteTaskController(
@@ -245,6 +244,7 @@
 
         verifyZeroInteractions(bubbles, keyguardManager, userManager, eventLogger)
     }
+
     // endregion
 
     // region showNoteTask
@@ -357,14 +357,11 @@
 
     @Test
     fun showNoteTask_defaultUserSet_shouldStartActivityWithExpectedUserAndLogUiEvent() {
-        whenever(
-                secureSettings.getIntForUser(
-                    /* name= */ eq(Settings.Secure.DEFAULT_NOTE_TASK_PROFILE),
-                    /* def= */ any(),
-                    /* userHandle= */ any()
-                )
-            )
-            .thenReturn(10)
+        secureSettings.putIntForUser(
+            /* name= */ Settings.Secure.DEFAULT_NOTE_TASK_PROFILE,
+            /* value= */ 10,
+            /* userHandle= */ userTracker.userId
+        )
         val user10 = UserHandle.of(/* userId= */ 10)
 
         val expectedInfo =
@@ -458,6 +455,7 @@
         verify(eventLogger).logNoteTaskOpened(expectedInfo)
         verifyZeroInteractions(bubbles)
     }
+
     // endregion
 
     // region setNoteTaskShortcutEnabled
@@ -535,6 +533,7 @@
         assertThat(argument.value.className)
             .isEqualTo(CreateNoteTaskShortcutActivity::class.java.name)
     }
+
     // endregion
 
     // region keyguard policy
@@ -601,6 +600,7 @@
 
         verifyNoteTaskOpenInBubbleInUser(userTracker.userHandle)
     }
+
     // endregion
 
     // region showNoteTask, COPE devices
@@ -626,14 +626,11 @@
 
     @Test
     fun showNoteTask_copeDevices_tailButtonEntryPoint_shouldStartBubbleInTheUserSelectedUser() {
-        whenever(
-                secureSettings.getIntForUser(
-                    /* name= */ eq(Settings.Secure.DEFAULT_NOTE_TASK_PROFILE),
-                    /* def= */ any(),
-                    /* userHandle= */ any()
-                )
-            )
-            .thenReturn(mainUserInfo.id)
+        secureSettings.putIntForUser(
+            /* name= */ Settings.Secure.DEFAULT_NOTE_TASK_PROFILE,
+            /* value= */ mainUserInfo.id,
+            /* userHandle= */ userTracker.userId
+        )
         whenever(devicePolicyManager.isOrganizationOwnedDeviceWithManagedProfile).thenReturn(true)
         userTracker.set(mainAndWorkProfileUsers, mainAndWorkProfileUsers.indexOf(mainUserInfo))
 
@@ -661,6 +658,7 @@
 
         verifyNoteTaskOpenInBubbleInUser(mainUserInfo.userHandle)
     }
+
     // endregion
 
     private fun verifyNoteTaskOpenInBubbleInUser(userHandle: UserHandle) {
@@ -700,6 +698,7 @@
 
         verify(controller).updateNoteTaskAsUser(user)
     }
+
     // endregion
 
     // region updateNoteTaskAsUser
@@ -729,6 +728,7 @@
         val intent = withArgCaptor { verify(context).startServiceAsUser(capture(), eq(user)) }
         assertThat(intent).hasComponentClass(NoteTaskControllerUpdateService::class.java)
     }
+
     // endregion
 
     // region internalUpdateNoteTaskAsUser
@@ -807,6 +807,7 @@
         verify(shortcutManager, never()).enableShortcuts(any())
         verify(shortcutManager, never()).updateShortcuts(any())
     }
+
     // endregion
 
     // startregion updateNoteTaskForAllUsers
@@ -821,6 +822,7 @@
         verify(controller).updateNoteTaskAsUser(mainUserInfo.userHandle)
         verify(controller).updateNoteTaskAsUser(workUserInfo.userHandle)
     }
+
     // endregion
 
     // region getUserForHandlingNotesTaking
@@ -836,14 +838,11 @@
 
     @Test
     fun getUserForHandlingNotesTaking_cope_userSelectedWorkProfile_tailButton_shouldReturnWorkProfileUser() { // ktlint-disable max-line-length
-        whenever(
-                secureSettings.getIntForUser(
-                    /* name= */ eq(Settings.Secure.DEFAULT_NOTE_TASK_PROFILE),
-                    /* def= */ any(),
-                    /* userHandle= */ any()
-                )
-            )
-            .thenReturn(workUserInfo.id)
+        secureSettings.putIntForUser(
+            /* name= */ Settings.Secure.DEFAULT_NOTE_TASK_PROFILE,
+            /* value= */ workUserInfo.id,
+            /* userHandle= */ userTracker.userId
+        )
         whenever(devicePolicyManager.isOrganizationOwnedDeviceWithManagedProfile).thenReturn(true)
         userTracker.set(mainAndWorkProfileUsers, mainAndWorkProfileUsers.indexOf(mainUserInfo))
 
@@ -854,14 +853,11 @@
 
     @Test
     fun getUserForHandlingNotesTaking_cope_userSelectedMainProfile_tailButton_shouldReturnMainProfileUser() { // ktlint-disable max-line-length
-        whenever(
-                secureSettings.getIntForUser(
-                    /* name= */ eq(Settings.Secure.DEFAULT_NOTE_TASK_PROFILE),
-                    /* def= */ any(),
-                    /* userHandle= */ any()
-                )
-            )
-            .thenReturn(mainUserInfo.id)
+        secureSettings.putIntForUser(
+            /* name= */ Settings.Secure.DEFAULT_NOTE_TASK_PROFILE,
+            /* value= */ mainUserInfo.id,
+            /* userHandle= */ userTracker.userId
+        )
         whenever(devicePolicyManager.isOrganizationOwnedDeviceWithManagedProfile).thenReturn(true)
         userTracker.set(mainAndWorkProfileUsers, mainAndWorkProfileUsers.indexOf(mainUserInfo))
 
@@ -906,6 +902,7 @@
 
         assertThat(user).isEqualTo(UserHandle.of(mainUserInfo.id))
     }
+
     // endregion
 
     // startregion startNotesRoleSetting
@@ -962,6 +959,7 @@
         assertThat(intentCaptor.value).hasAction(ACTION_MANAGE_DEFAULT_APP)
         assertThat(userCaptor.value).isEqualTo(UserHandle.of(mainUserInfo.id))
     }
+
     // endregion
 
     private companion object {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSImplTest.java
index c0d390a..206bbbf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSImplTest.java
@@ -554,6 +554,30 @@
         assertThat(mUnderTest.isKeyguardState()).isFalse();
     }
 
+    @Test
+    public void testHeaderBounds() {
+        int left = 20;
+        int top = 30;
+        int right = 200;
+        int bottom = 100;
+        setHeaderBounds(left, top, right, bottom);
+
+        assertThat(mUnderTest.getHeaderLeft()).isEqualTo(left);
+        assertThat(mUnderTest.getHeaderTop()).isEqualTo(top);
+        assertThat(mUnderTest.getHeaderBottom()).isEqualTo(bottom);
+        assertThat(mUnderTest.getHeaderHeight()).isEqualTo(bottom - top);
+    }
+
+    @Test
+    public void testHeaderBoundsOnScreen() {
+        Rect bounds = new Rect(0, 10, 100, 200);
+        setHeaderBoundsOnScreen(bounds);
+
+        Rect out = new Rect();
+        mUnderTest.getHeaderBoundsOnScreen(out);
+        assertThat(out).isEqualTo(bounds);
+    }
+
     private QSImpl instantiate() {
         setupQsComponent();
         setUpViews();
@@ -672,4 +696,19 @@
     private void setIsSmallScreen() {
         mUnderTest.setIsNotificationPanelFullWidth(true);
     }
+
+    private void setHeaderBounds(int left, int top, int right, int bottom) {
+        when(mHeader.getLeft()).thenReturn(left);
+        when(mHeader.getTop()).thenReturn(top);
+        when(mHeader.getRight()).thenReturn(right);
+        when(mHeader.getBottom()).thenReturn(bottom);
+    }
+
+    private void setHeaderBoundsOnScreen(Rect rect) {
+        doAnswer(invocation -> {
+            Rect bounds = invocation.getArgument(/* index= */ 0);
+            bounds.set(rect);
+            return null;
+        }).when(mHeader).getBoundsOnScreen(any(Rect.class));
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt
index 31652a5..f90e1e9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt
@@ -47,6 +47,7 @@
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.launch
+import kotlinx.coroutines.test.StandardTestDispatcher
 import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.advanceUntilIdle
 import kotlinx.coroutines.test.runTest
@@ -60,7 +61,8 @@
 @RunWith(AndroidJUnit4::class)
 @RunWithLooper
 class FooterActionsViewModelTest : SysuiTestCase() {
-    private val testScope = TestScope()
+    private val testDispatcher = StandardTestDispatcher()
+    private val testScope = TestScope(testDispatcher)
     private lateinit var utils: FooterActionsTestUtils
 
     private val themedContext = ContextThemeWrapper(context, R.style.Theme_SystemUI_QuickSettings)
@@ -127,7 +129,7 @@
     fun userSwitcher() = runTest {
         val picture: Drawable = mock()
         val userInfoController = FakeUserInfoController(FakeInfo(picture = picture))
-        val settings = FakeGlobalSettings()
+        val settings = FakeGlobalSettings(testDispatcher)
         val userId = 42
         val userSwitcherControllerWrapper =
             MockUserSwitcherControllerWrapper(currentUserName = "foo")
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt
index 988769f..90ffaf1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt
@@ -25,6 +25,7 @@
 import android.view.ContextThemeWrapper
 import android.view.View
 import android.view.accessibility.AccessibilityNodeInfo
+import android.widget.Button
 import android.widget.TextView
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
@@ -381,6 +382,47 @@
     }
 
     @Test
+    fun testNonSwitchA11yClass_longClickActionHasCorrectLabel() {
+        val state =
+            QSTile.State().apply {
+                expandedAccessibilityClassName = Button::class.java.name
+                handlesLongClick = true
+            }
+        tileView.changeState(state)
+        val info = AccessibilityNodeInfo(tileView)
+        tileView.onInitializeAccessibilityNodeInfo(info)
+
+        assertThat(
+                info.actionList
+                    .find {
+                        it.id == AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK.id
+                    }
+                    ?.label
+            )
+            .isEqualTo(context.getString(R.string.accessibility_long_click_tile))
+    }
+
+    @Test
+    fun testNonSwitchA11yClass_disabledByPolicy_noLongClickAction() {
+        val state =
+            QSTile.State().apply {
+                expandedAccessibilityClassName = Button::class.java.name
+                handlesLongClick = true
+                disabledByPolicy = true
+            }
+        tileView.changeState(state)
+        val info = AccessibilityNodeInfo(tileView)
+        tileView.onInitializeAccessibilityNodeInfo(info)
+
+        assertThat(
+                info.actionList.find {
+                    it.id == AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK.id
+                }
+            )
+            .isNull()
+    }
+
+    @Test
     fun onStateChange_longPressEffectActive_withInvalidDuration_doesNotInitializeEffect() {
         val state = QSTile.State() // A state that handles longPress
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DndTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DndTileTest.kt
index e01744e..6a43a61 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DndTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DndTileTest.kt
@@ -20,6 +20,7 @@
 import android.content.ContextWrapper
 import android.content.SharedPreferences
 import android.os.Handler
+import android.platform.test.annotations.DisableFlags
 import android.provider.Settings
 import android.provider.Settings.Global.ZEN_MODE_NO_INTERRUPTIONS
 import android.provider.Settings.Global.ZEN_MODE_OFF
@@ -61,6 +62,7 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
+@DisableFlags(android.app.Flags.FLAG_MODES_UI)
 class DndTileTest : SysuiTestCase() {
 
     companion object {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ModesTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ModesTileTest.kt
index 27b6ea6..a5de7cd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ModesTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ModesTileTest.kt
@@ -27,7 +27,6 @@
 import com.android.internal.logging.MetricsLogger
 import com.android.settingslib.notification.data.repository.FakeZenModeRepository
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.animation.DialogTransitionAnimator
 import com.android.systemui.classifier.FalsingManagerFake
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.plugins.statusbar.StatusBarStateController
@@ -47,10 +46,9 @@
 import com.android.systemui.util.settings.FakeSettings
 import com.android.systemui.util.settings.SecureSettings
 import com.google.common.truth.Truth.assertThat
-import kotlin.coroutines.EmptyCoroutineContext
 import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.StandardTestDispatcher
 import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
 import org.junit.After
@@ -82,13 +80,14 @@
 
     @Mock private lateinit var qsTileConfigProvider: QSTileConfigProvider
 
-    @Mock private lateinit var dialogTransitionAnimator: DialogTransitionAnimator
-
     @Mock private lateinit var dialogDelegate: ModesDialogDelegate
 
+    private val testDispatcher = UnconfinedTestDispatcher()
+    private val testScope = TestScope(testDispatcher)
+
     private val inputHandler = FakeQSTileIntentUserInputHandler()
     private val zenModeRepository = FakeZenModeRepository()
-    private val tileDataInteractor = ModesTileDataInteractor(zenModeRepository)
+    private val tileDataInteractor = ModesTileDataInteractor(zenModeRepository, testDispatcher)
     private val mapper =
         ModesTileMapper(
             context.orCreateTestableResources
@@ -100,9 +99,6 @@
             context.theme,
         )
 
-    private val testDispatcher = StandardTestDispatcher()
-    private val testScope = TestScope(testDispatcher)
-
     private lateinit var userActionInteractor: ModesTileUserActionInteractor
     private lateinit var secureSettings: SecureSettings
     private lateinit var testableLooper: TestableLooper
@@ -131,9 +127,7 @@
 
         userActionInteractor =
             ModesTileUserActionInteractor(
-                EmptyCoroutineContext,
                 inputHandler,
-                dialogTransitionAnimator,
                 dialogDelegate,
             )
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt
index e1c3911..b02cccc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt
@@ -35,6 +35,7 @@
 import com.android.systemui.broadcast.BroadcastDispatcher
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.dump.DumpManager
+import com.android.systemui.education.domain.interactor.KeyboardTouchpadEduStatsInteractor
 import com.android.systemui.keyguard.KeyguardUnlockAnimationController
 import com.android.systemui.keyguard.WakefulnessLifecycle
 import com.android.systemui.keyguard.ui.view.InWindowLauncherUnlockAnimationManager
@@ -121,6 +122,9 @@
         Optional<UnfoldTransitionProgressForwarder>
     @Mock private lateinit var broadcastDispatcher: BroadcastDispatcher
 
+    @Mock
+    private lateinit var keyboardTouchpadEduStatsInteractor: KeyboardTouchpadEduStatsInteractor
+
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
@@ -289,7 +293,8 @@
             assistUtils,
             dumpManager,
             unfoldTransitionProgressForwarder,
-            broadcastDispatcher
+            broadcastDispatcher,
+            keyboardTouchpadEduStatsInteractor
         )
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt
index ce1a885..8d84c3e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt
@@ -177,7 +177,6 @@
             .thenReturn(false)
         whenever(devicePolicyResolver.isScreenCaptureCompletelyDisabled(any<UserHandle>()))
             .thenReturn(false)
-        whenever(flags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING)).thenReturn(true)
         whenever(state.hasUserApprovedScreenRecording).thenReturn(false)
 
         val screenRecordSwitch = dialog.requireViewById<Switch>(R.id.screenrecord_switch)
@@ -200,7 +199,6 @@
             .thenReturn(false)
         whenever(devicePolicyResolver.isScreenCaptureCompletelyDisabled(any<UserHandle>()))
             .thenReturn(false)
-        whenever(flags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING)).thenReturn(false)
 
         val screenRecordSwitch = dialog.requireViewById<Switch>(R.id.screenrecord_switch)
         screenRecordSwitch.isChecked = true
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java
index 477c50b..6b16e78 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java
@@ -39,10 +39,8 @@
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 
-import androidx.annotation.Nullable;
 import androidx.test.filters.SmallTest;
 
-import com.android.systemui.Dependency;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.animation.DialogTransitionAnimator;
 import com.android.systemui.broadcast.BroadcastDispatcher;
@@ -52,12 +50,9 @@
 import com.android.systemui.mediaprojection.SessionCreationSource;
 import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDevicePolicyResolver;
 import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDisabledDialogDelegate;
-import com.android.systemui.model.SysUiState;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.settings.UserTracker;
-import com.android.systemui.statusbar.phone.DialogDelegate;
 import com.android.systemui.statusbar.phone.SystemUIDialog;
-import com.android.systemui.statusbar.phone.SystemUIDialogManager;
 import com.android.systemui.util.concurrency.FakeExecutor;
 import com.android.systemui.util.time.FakeSystemClock;
 
@@ -110,7 +105,6 @@
 
     private FakeFeatureFlags mFeatureFlags;
     private RecordingController mController;
-    private TestSystemUIDialogFactory mDialogFactory;
 
     private static final int USER_ID = 10;
 
@@ -120,14 +114,6 @@
         Context spiedContext = spy(mContext);
         when(spiedContext.getUserId()).thenReturn(TEST_USER_ID);
 
-        mDialogFactory = new TestSystemUIDialogFactory(
-                mContext,
-                Dependency.get(SystemUIDialogManager.class),
-                Dependency.get(SysUiState.class),
-                Dependency.get(BroadcastDispatcher.class),
-                Dependency.get(DialogTransitionAnimator.class)
-        );
-
         mFeatureFlags = new FakeFeatureFlags();
         when(mScreenCaptureDisabledDialogDelegate.createSysUIDialog())
                 .thenReturn(mScreenCaptureDisabledDialog);
@@ -251,7 +237,6 @@
 
     @Test
     public void testPoliciesFlagDisabled_screenCapturingNotAllowed_returnsNullDevicePolicyDialog() {
-        mFeatureFlags.set(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING, true);
         mFeatureFlags.set(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING_ENTERPRISE_POLICIES, false);
         when(mDevicePolicyResolver.isScreenCaptureCompletelyDisabled((any()))).thenReturn(true);
 
@@ -269,19 +254,7 @@
     }
 
     @Test
-    public void testPartialScreenSharingDisabled_returnsLegacyDialog() {
-        mFeatureFlags.set(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING, false);
-        mFeatureFlags.set(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING_ENTERPRISE_POLICIES, false);
-
-        Dialog dialog = mController.createScreenRecordDialog(mContext, mFeatureFlags,
-                mDialogTransitionAnimator, mActivityStarter, /* onStartRecordingClicked= */ null);
-
-        assertThat(dialog).isEqualTo(mScreenRecordSystemUIDialog);
-    }
-
-    @Test
     public void testPoliciesFlagEnabled_screenCapturingNotAllowed_returnsDevicePolicyDialog() {
-        mFeatureFlags.set(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING, true);
         mFeatureFlags.set(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING_ENTERPRISE_POLICIES, true);
         when(mDevicePolicyResolver.isScreenCaptureCompletelyDisabled((any()))).thenReturn(true);
 
@@ -293,7 +266,6 @@
 
     @Test
     public void testPoliciesFlagEnabled_screenCapturingAllowed_returnsNullDevicePolicyDialog() {
-        mFeatureFlags.set(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING, true);
         mFeatureFlags.set(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING_ENTERPRISE_POLICIES, true);
         when(mDevicePolicyResolver.isScreenCaptureCompletelyDisabled((any()))).thenReturn(false);
 
@@ -312,7 +284,6 @@
 
     @Test
     public void testPoliciesFlagEnabled_screenCapturingAllowed_logsProjectionInitiated() {
-        mFeatureFlags.set(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING, true);
         mFeatureFlags.set(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING_ENTERPRISE_POLICIES, true);
         when(mDevicePolicyResolver.isScreenCaptureCompletelyDisabled((any()))).thenReturn(false);
 
@@ -324,32 +295,4 @@
                         /* hostUid= */ myUid(),
                         SessionCreationSource.SYSTEM_UI_SCREEN_RECORDER);
     }
-
-    private static class TestSystemUIDialogFactory extends SystemUIDialog.Factory {
-
-        @Nullable private DialogDelegate<SystemUIDialog> mLastDelegate;
-        @Nullable private SystemUIDialog mLastCreatedDialog;
-
-        TestSystemUIDialogFactory(
-                Context context,
-                SystemUIDialogManager systemUIDialogManager,
-                SysUiState sysUiState,
-                BroadcastDispatcher broadcastDispatcher,
-                DialogTransitionAnimator dialogTransitionAnimator) {
-            super(
-                    context,
-                    systemUIDialogManager,
-                    sysUiState,
-                    broadcastDispatcher,
-                    dialogTransitionAnimator);
-        }
-
-        @Override
-        public SystemUIDialog create(SystemUIDialog.Delegate delegate) {
-            SystemUIDialog dialog = super.create(delegate);
-            mLastDelegate = delegate;
-            mLastCreatedDialog = dialog;
-            return dialog;
-        }
-    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogDelegateTest.kt
index cc8d7d5..11b0bdf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogDelegateTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogDelegateTest.kt
@@ -28,7 +28,6 @@
 import com.android.systemui.animation.DialogTransitionAnimator
 import com.android.systemui.broadcast.BroadcastDispatcher
 import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
 import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger
 import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorActivity
 import com.android.systemui.mediaprojection.permission.ENTIRE_SCREEN
@@ -51,7 +50,6 @@
 import org.mockito.Mockito.eq
 import org.mockito.Mockito.never
 import org.mockito.Mockito.verify
-import org.mockito.Mockito.`when` as whenever
 import org.mockito.MockitoAnnotations
 
 @SmallTest
@@ -72,8 +70,6 @@
     fun setUp() {
         MockitoAnnotations.initMocks(this)
 
-        whenever(flags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING)).thenReturn(true)
-
         val systemUIDialogFactory =
             SystemUIDialog.Factory(
                 context,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/SaveImageInBackgroundTaskTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/SaveImageInBackgroundTaskTest.kt
index 25dd9fe..5e07aef 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/SaveImageInBackgroundTaskTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/SaveImageInBackgroundTaskTest.kt
@@ -27,7 +27,6 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.flags.FakeFeatureFlags
-import com.android.systemui.screenshot.ScreenshotController.SaveImageInBackgroundData
 import com.android.systemui.screenshot.ScreenshotNotificationSmartActionsProvider.ScreenshotSmartActionType
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.eq
@@ -44,7 +43,7 @@
     private val imageExporter = mock<ImageExporter>()
     private val smartActions = mock<ScreenshotSmartActions>()
     private val smartActionsProvider = mock<ScreenshotNotificationSmartActionsProvider>()
-    private val saveImageData = SaveImageInBackgroundData()
+    private val saveImageData = SaveImageInBackgroundTask.SaveImageInBackgroundData()
     private val testScreenshotId: String = "testScreenshotId"
     private val testBitmap = mock<Bitmap>()
     private val testUser = UserHandle.getUserHandleForUid(0)
@@ -229,7 +228,7 @@
             )
         val quickSharePendingIntent =
             quickShareAction.actionIntent.intent.extras!!.getParcelable(
-                ScreenshotController.EXTRA_ACTION_INTENT,
+                SmartActionsReceiver.EXTRA_ACTION_INTENT,
                 PendingIntent::class.java
             )
 
@@ -267,7 +266,7 @@
         assertEquals(
             immutablePendingIntent,
             quickShareAction.actionIntent.intent.extras!!.getParcelable(
-                ScreenshotController.EXTRA_ACTION_INTENT,
+                SmartActionsReceiver.EXTRA_ACTION_INTENT,
                 PendingIntent::class.java
             )
         )
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/SmartActionsReceiverTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/SmartActionsReceiverTest.java
index 471fdc0..9dc5cfe 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/SmartActionsReceiverTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/SmartActionsReceiverTest.java
@@ -16,8 +16,8 @@
 
 package com.android.systemui.screenshot;
 
-import static com.android.systemui.screenshot.ScreenshotController.EXTRA_ACTION_TYPE;
-import static com.android.systemui.screenshot.ScreenshotController.EXTRA_ID;
+import static com.android.systemui.screenshot.SmartActionsReceiver.EXTRA_ACTION_TYPE;
+import static com.android.systemui.screenshot.SmartActionsReceiver.EXTRA_ID;
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
@@ -57,7 +57,7 @@
         MockitoAnnotations.initMocks(this);
         mSmartActionsReceiver = new SmartActionsReceiver(mMockScreenshotSmartActions);
         mIntent = new Intent(mContext, SmartActionsReceiver.class)
-                .putExtra(ScreenshotController.EXTRA_ACTION_INTENT, mMockPendingIntent);
+                .putExtra(SmartActionsReceiver.EXTRA_ACTION_INTENT, mMockPendingIntent);
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotExecutorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotExecutorTest.kt
index 8d3a29a..a295981 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotExecutorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotExecutorTest.kt
@@ -42,10 +42,10 @@
 @SmallTest
 class TakeScreenshotExecutorTest : SysuiTestCase() {
 
-    private val controller = mock<ScreenshotController>()
+    private val controller = mock<LegacyScreenshotController>()
     private val notificationsController0 = mock<ScreenshotNotificationsController>()
     private val notificationsController1 = mock<ScreenshotNotificationsController>()
-    private val controllerFactory = mock<ScreenshotController.Factory>()
+    private val controllerFactory = mock<InteractiveScreenshotHandler.Factory>()
     private val callback = mock<TakeScreenshotService.RequestCallback>()
     private val notificationControllerFactory = mock<ScreenshotNotificationsController.Factory>()
 
@@ -287,7 +287,7 @@
     fun onCloseSystemDialogsReceived_controllerHasPendingTransitions() =
         testScope.runTest {
             setDisplays(display(TYPE_INTERNAL, id = 0), display(TYPE_EXTERNAL, id = 1))
-            whenever(controller.isPendingSharedTransition).thenReturn(true)
+            whenever(controller.isPendingSharedTransition()).thenReturn(true)
             val onSaved = { _: Uri? -> }
             screenshotExecutor.executeScreenshots(createScreenshotRequest(), onSaved, callback)
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsActivityTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsActivityTest.java
index 9986205..a8d5008 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsActivityTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsActivityTest.java
@@ -17,6 +17,7 @@
 package com.android.systemui.screenshot.appclips;
 
 import static android.app.Activity.RESULT_OK;
+import static android.app.ActivityManager.RunningTaskInfo;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 
 import static com.android.systemui.screenshot.appclips.AppClipsEvent.SCREENSHOT_FOR_NOTE_ACCEPTED;
@@ -32,7 +33,6 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-import android.app.ActivityTaskManager.RootTaskInfo;
 import android.app.IActivityTaskManager;
 import android.app.assist.AssistContent;
 import android.content.ComponentName;
@@ -103,7 +103,7 @@
     private static final String BACKLINKS_TASK_APP_NAME = "Backlinks app";
     private static final String BACKLINKS_TASK_PACKAGE_NAME = "backlinksTaskPackageName";
 
-    private static final RootTaskInfo TASK_THAT_SUPPORTS_BACKLINKS =
+    private static final RunningTaskInfo TASK_THAT_SUPPORTS_BACKLINKS =
             createTaskInfoForBacklinksTask();
     private static final AssistContent ASSIST_CONTENT_FOR_BACKLINKS_TASK =
             createAssistContentForBacklinksTask();
@@ -233,6 +233,10 @@
         assertThat(backlinksData.getText().toString()).isEqualTo(BACKLINKS_TASK_APP_NAME);
         assertThat(backlinksData.getCompoundDrawablesRelative()[0]).isEqualTo(FAKE_DRAWABLE);
 
+        // Verify dropdown icon is not shown and there are no click listeners on text view.
+        assertThat(backlinksData.getCompoundDrawablesRelative()[2]).isNull();
+        assertThat(backlinksData.hasOnClickListeners()).isFalse();
+
         CheckBox backlinksIncludeData = mActivity.findViewById(R.id.backlinks_include_data);
         assertThat(backlinksIncludeData.getVisibility()).isEqualTo(View.VISIBLE);
         assertThat(backlinksIncludeData.getText().toString())
@@ -258,20 +262,71 @@
         assertThat(backlinksData.getVisibility()).isEqualTo(View.GONE);
     }
 
+    @Test
+    @EnableFlags(Flags.FLAG_APP_CLIPS_BACKLINKS)
+    public void appClipsLaunched_backlinks_multipleBacklinksAvailable_defaultShown()
+            throws RemoteException {
+        // Set up mocking for multiple backlinks.
+        ResolveInfo resolveInfo1 = createBacklinksTaskResolveInfo();
+
+        int taskId2 = BACKLINKS_TASK_ID + 2;
+        String package2 = BACKLINKS_TASK_PACKAGE_NAME + 2;
+        String appName2 = BACKLINKS_TASK_APP_NAME + 2;
+
+        ResolveInfo resolveInfo2 = createBacklinksTaskResolveInfo();
+        ActivityInfo activityInfo2 = resolveInfo2.activityInfo;
+        activityInfo2.name = appName2;
+        activityInfo2.packageName = package2;
+        activityInfo2.applicationInfo.packageName = package2;
+        RunningTaskInfo runningTaskInfo2 = createTaskInfoForBacklinksTask();
+        runningTaskInfo2.taskId = taskId2;
+        runningTaskInfo2.topActivity = new ComponentName(package2, "backlinksClass");
+        runningTaskInfo2.topActivityInfo = resolveInfo2.activityInfo;
+        runningTaskInfo2.baseIntent = new Intent().setComponent(runningTaskInfo2.topActivity);
+
+        when(mAtmService.getTasks(eq(Integer.MAX_VALUE), eq(false), eq(false),
+                mDisplayIdCaptor.capture()))
+                .thenReturn(List.of(TASK_THAT_SUPPORTS_BACKLINKS, runningTaskInfo2));
+        when(mPackageManager.resolveActivity(any(Intent.class), anyInt())).thenReturn(resolveInfo1,
+                resolveInfo1, resolveInfo1, resolveInfo2, resolveInfo2, resolveInfo2);
+        when(mPackageManager.loadItemIcon(any(), any())).thenReturn(FAKE_DRAWABLE);
+
+        // Using same AssistContent data for both tasks.
+        mockForAssistContent(ASSIST_CONTENT_FOR_BACKLINKS_TASK, BACKLINKS_TASK_ID);
+        mockForAssistContent(ASSIST_CONTENT_FOR_BACKLINKS_TASK, taskId2);
+
+        // Mocking complete, trigger backlinks.
+        launchActivity();
+        waitForIdleSync();
+
+        // Verify default backlink shown to user and text view has on click listener.
+        TextView backlinksData = mActivity.findViewById(R.id.backlinks_data);
+        assertThat(backlinksData.getText().toString()).isEqualTo(BACKLINKS_TASK_APP_NAME);
+        assertThat(backlinksData.hasOnClickListeners()).isTrue();
+
+        // Verify dropdown icon is not null.
+        assertThat(backlinksData.getCompoundDrawablesRelative()[2]).isNotNull();
+    }
+
     private void setUpMocksForBacklinks() throws RemoteException {
-        when(mAtmService.getAllRootTaskInfosOnDisplay(mDisplayIdCaptor.capture()))
+        when(mAtmService.getTasks(eq(Integer.MAX_VALUE), eq(false), eq(false),
+                mDisplayIdCaptor.capture()))
                 .thenReturn(List.of(TASK_THAT_SUPPORTS_BACKLINKS));
-        doAnswer(invocation -> {
-            AssistContentRequester.Callback callback = invocation.getArgument(1);
-            callback.onAssistContentAvailable(ASSIST_CONTENT_FOR_BACKLINKS_TASK);
-            return null;
-        }).when(mAssistContentRequester).requestAssistContent(eq(BACKLINKS_TASK_ID), any());
+        mockForAssistContent(ASSIST_CONTENT_FOR_BACKLINKS_TASK, BACKLINKS_TASK_ID);
         when(mPackageManager
                 .resolveActivity(any(Intent.class), anyInt()))
                 .thenReturn(createBacklinksTaskResolveInfo());
         when(mPackageManager.loadItemIcon(any(), any())).thenReturn(FAKE_DRAWABLE);
     }
 
+    private void mockForAssistContent(AssistContent expected, int taskId) {
+        doAnswer(invocation -> {
+            AssistContentRequester.Callback callback = invocation.getArgument(1);
+            callback.onAssistContentAvailable(expected);
+            return null;
+        }).when(mAssistContentRequester).requestAssistContent(eq(taskId), any());
+    }
+
     private void launchActivity() {
         launchActivity(createResultReceiver(FAKE_CONSUMER));
     }
@@ -319,8 +374,8 @@
         return resolveInfo;
     }
 
-    private static RootTaskInfo createTaskInfoForBacklinksTask() {
-        RootTaskInfo taskInfo = new RootTaskInfo();
+    private static RunningTaskInfo createTaskInfoForBacklinksTask() {
+        RunningTaskInfo taskInfo = new RunningTaskInfo();
         taskInfo.taskId = BACKLINKS_TASK_ID;
         taskInfo.isVisible = true;
         taskInfo.isRunning = true;
@@ -328,7 +383,6 @@
         taskInfo.topActivity = new ComponentName(BACKLINKS_TASK_PACKAGE_NAME, "backlinksClass");
         taskInfo.topActivityInfo = createBacklinksTaskResolveInfo().activityInfo;
         taskInfo.baseIntent = new Intent().setComponent(taskInfo.topActivity);
-        taskInfo.childTaskIds = new int[]{BACKLINKS_TASK_ID + 1};
         taskInfo.configuration.windowConfiguration.setActivityType(ACTIVITY_TYPE_STANDARD);
         return taskInfo;
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsViewModelTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsViewModelTest.java
index 193d29c..178547e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsViewModelTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsViewModelTest.java
@@ -37,7 +37,7 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-import android.app.ActivityTaskManager.RootTaskInfo;
+import android.app.ActivityManager.RunningTaskInfo;
 import android.app.IActivityTaskManager;
 import android.app.assist.AssistContent;
 import android.content.ClipData;
@@ -107,7 +107,7 @@
         mPackageManagerIntentCaptor = ArgumentCaptor.forClass(Intent.class);
 
         // Set up mocking for backlinks.
-        when(mAtmService.getAllRootTaskInfosOnDisplay(DEFAULT_DISPLAY))
+        when(mAtmService.getTasks(Integer.MAX_VALUE, false, false, DEFAULT_DISPLAY))
                 .thenReturn(List.of(createTaskInfoForBacklinksTask()));
         when(mPackageManager.resolveActivity(mPackageManagerIntentCaptor.capture(), anyInt()))
                 .thenReturn(createBacklinksTaskResolveInfo());
@@ -190,11 +190,7 @@
         Uri expectedUri = Uri.parse("https://developers.android.com");
         AssistContent contentWithUri = new AssistContent();
         contentWithUri.setWebUri(expectedUri);
-        doAnswer(invocation -> {
-            AssistContentRequester.Callback callback = invocation.getArgument(1);
-            callback.onAssistContentAvailable(contentWithUri);
-            return null;
-        }).when(mAssistContentRequester).requestAssistContent(eq(BACKLINKS_TASK_ID), any());
+        mockForAssistContent(contentWithUri, BACKLINKS_TASK_ID);
 
         mViewModel.triggerBacklinks(Collections.emptySet(), DEFAULT_DISPLAY);
         waitForIdleSync();
@@ -203,7 +199,7 @@
         assertThat(queriedIntent.getData()).isEqualTo(expectedUri);
         assertThat(queriedIntent.getAction()).isEqualTo(ACTION_VIEW);
 
-        InternalBacklinksData result = mViewModel.getBacklinksLiveData().getValue();
+        InternalBacklinksData result = mViewModel.mSelectedBacklinksLiveData.getValue();
         assertThat(result.getAppIcon()).isEqualTo(FAKE_DRAWABLE);
         ClipData clipData = result.getClipData();
         ClipDescription resultDescription = clipData.getDescription();
@@ -211,6 +207,8 @@
         assertThat(resultDescription.getMimeType(0)).isEqualTo(MIMETYPE_TEXT_URILIST);
         assertThat(clipData.getItemCount()).isEqualTo(1);
         assertThat(clipData.getItemAt(0).getUri()).isEqualTo(expectedUri);
+
+        assertThat(result).isEqualTo(mViewModel.getBacklinksLiveData().getValue().get(0));
     }
 
     @Test
@@ -218,12 +216,8 @@
         Uri expectedUri = Uri.parse("https://developers.android.com");
         AssistContent contentWithUri = new AssistContent();
         contentWithUri.setWebUri(expectedUri);
+        mockForAssistContent(contentWithUri, BACKLINKS_TASK_ID);
         resetPackageManagerMockingForUsingFallbackBacklinks();
-        doAnswer(invocation -> {
-            AssistContentRequester.Callback callback = invocation.getArgument(1);
-            callback.onAssistContentAvailable(contentWithUri);
-            return null;
-        }).when(mAssistContentRequester).requestAssistContent(eq(BACKLINKS_TASK_ID), any());
 
         mViewModel.triggerBacklinks(Collections.emptySet(), DEFAULT_DISPLAY);
         waitForIdleSync();
@@ -236,11 +230,7 @@
         Intent expectedIntent = new Intent().setPackage(BACKLINKS_TASK_PACKAGE_NAME);
         AssistContent contentWithAppProvidedIntent = new AssistContent();
         contentWithAppProvidedIntent.setIntent(expectedIntent);
-        doAnswer(invocation -> {
-            AssistContentRequester.Callback callback = invocation.getArgument(1);
-            callback.onAssistContentAvailable(contentWithAppProvidedIntent);
-            return null;
-        }).when(mAssistContentRequester).requestAssistContent(eq(BACKLINKS_TASK_ID), any());
+        mockForAssistContent(contentWithAppProvidedIntent, BACKLINKS_TASK_ID);
 
         mViewModel.triggerBacklinks(Collections.emptySet(), DEFAULT_DISPLAY);
         waitForIdleSync();
@@ -248,7 +238,7 @@
         Intent queriedIntent = mPackageManagerIntentCaptor.getValue();
         assertThat(queriedIntent.getPackage()).isEqualTo(expectedIntent.getPackage());
 
-        InternalBacklinksData result = mViewModel.getBacklinksLiveData().getValue();
+        InternalBacklinksData result = mViewModel.mSelectedBacklinksLiveData.getValue();
         assertThat(result.getAppIcon()).isEqualTo(FAKE_DRAWABLE);
         ClipData clipData = result.getClipData();
         ClipDescription resultDescription = clipData.getDescription();
@@ -263,12 +253,8 @@
         Intent expectedIntent = new Intent().setPackage(BACKLINKS_TASK_PACKAGE_NAME);
         AssistContent contentWithAppProvidedIntent = new AssistContent();
         contentWithAppProvidedIntent.setIntent(expectedIntent);
+        mockForAssistContent(contentWithAppProvidedIntent, BACKLINKS_TASK_ID);
         resetPackageManagerMockingForUsingFallbackBacklinks();
-        doAnswer(invocation -> {
-            AssistContentRequester.Callback callback = invocation.getArgument(1);
-            callback.onAssistContentAvailable(contentWithAppProvidedIntent);
-            return null;
-        }).when(mAssistContentRequester).requestAssistContent(eq(BACKLINKS_TASK_ID), any());
 
         mViewModel.triggerBacklinks(Collections.emptySet(), DEFAULT_DISPLAY);
         waitForIdleSync();
@@ -278,11 +264,7 @@
 
     @Test
     public void triggerBacklinks_shouldUpdateBacklinks_withMainLauncherIntent() {
-        doAnswer(invocation -> {
-            AssistContentRequester.Callback callback = invocation.getArgument(1);
-            callback.onAssistContentAvailable(EMPTY_ASSIST_CONTENT);
-            return null;
-        }).when(mAssistContentRequester).requestAssistContent(eq(BACKLINKS_TASK_ID), any());
+        mockForAssistContent(EMPTY_ASSIST_CONTENT, BACKLINKS_TASK_ID);
 
         mViewModel.triggerBacklinks(Collections.emptySet(), DEFAULT_DISPLAY);
         waitForIdleSync();
@@ -298,15 +280,12 @@
     @Test
     public void triggerBacklinks_withNonResolvableMainLauncherIntent_noBacklinksAvailable() {
         reset(mPackageManager);
-        doAnswer(invocation -> {
-            AssistContentRequester.Callback callback = invocation.getArgument(1);
-            callback.onAssistContentAvailable(EMPTY_ASSIST_CONTENT);
-            return null;
-        }).when(mAssistContentRequester).requestAssistContent(eq(BACKLINKS_TASK_ID), any());
+        mockForAssistContent(EMPTY_ASSIST_CONTENT, BACKLINKS_TASK_ID);
 
         mViewModel.triggerBacklinks(Collections.emptySet(), DEFAULT_DISPLAY);
         waitForIdleSync();
 
+        assertThat(mViewModel.mSelectedBacklinksLiveData.getValue()).isNull();
         assertThat(mViewModel.getBacklinksLiveData().getValue()).isNull();
     }
 
@@ -314,14 +293,15 @@
     public void triggerBacklinks_nonStandardActivityIgnored_noBacklinkAvailable()
             throws RemoteException {
         reset(mAtmService);
-        RootTaskInfo taskInfo = createTaskInfoForBacklinksTask();
+        RunningTaskInfo taskInfo = createTaskInfoForBacklinksTask();
         taskInfo.configuration.windowConfiguration.setActivityType(ACTIVITY_TYPE_HOME);
-        when(mAtmService.getAllRootTaskInfosOnDisplay(DEFAULT_DISPLAY))
+        when(mAtmService.getTasks(Integer.MAX_VALUE, false, false, DEFAULT_DISPLAY))
                 .thenReturn(List.of(taskInfo));
 
         mViewModel.triggerBacklinks(Collections.emptySet(), DEFAULT_DISPLAY);
         waitForIdleSync();
 
+        assertThat(mViewModel.mSelectedBacklinksLiveData.getValue()).isNull();
         assertThat(mViewModel.getBacklinksLiveData().getValue()).isNull();
     }
 
@@ -330,9 +310,68 @@
         mViewModel.triggerBacklinks(Set.of(BACKLINKS_TASK_ID), DEFAULT_DISPLAY);
         waitForIdleSync();
 
+        assertThat(mViewModel.mSelectedBacklinksLiveData.getValue()).isNull();
         assertThat(mViewModel.getBacklinksLiveData().getValue()).isNull();
     }
 
+    @Test
+    public void triggerBacklinks_multipleAppsOnScreen_multipleBacklinksAvailable()
+            throws RemoteException {
+        // Set up mocking for multiple backlinks.
+        reset(mAtmService, mPackageManager);
+        RunningTaskInfo runningTaskInfo1 = createTaskInfoForBacklinksTask();
+        ResolveInfo resolveInfo1 = createBacklinksTaskResolveInfo();
+
+        int taskId2 = BACKLINKS_TASK_ID + 2;
+        String package2 = BACKLINKS_TASK_PACKAGE_NAME + 2;
+        String appName2 = BACKLINKS_TASK_APP_NAME + 2;
+
+        ResolveInfo resolveInfo2 = createBacklinksTaskResolveInfo();
+        ActivityInfo activityInfo2 = resolveInfo2.activityInfo;
+        activityInfo2.name = appName2;
+        activityInfo2.packageName = package2;
+        activityInfo2.applicationInfo.packageName = package2;
+        RunningTaskInfo runningTaskInfo2 = createTaskInfoForBacklinksTask();
+        runningTaskInfo2.taskId = taskId2;
+        runningTaskInfo2.topActivity = new ComponentName(package2, "backlinksClass");
+        runningTaskInfo2.topActivityInfo = resolveInfo2.activityInfo;
+        runningTaskInfo2.baseIntent = new Intent().setComponent(runningTaskInfo2.topActivity);
+
+        // For each task, the logic queries PM 3 times, twice for verifying if an app can be
+        // launched via launcher and once with the data provided in backlink intent.
+        when(mPackageManager.resolveActivity(any(), anyInt())).thenReturn(resolveInfo1,
+                resolveInfo1, resolveInfo1, resolveInfo2, resolveInfo2, resolveInfo2);
+        when(mPackageManager.loadItemIcon(any(), any())).thenReturn(FAKE_DRAWABLE);
+        when(mAtmService.getTasks(Integer.MAX_VALUE, false, false, DEFAULT_DISPLAY))
+                .thenReturn(List.of(runningTaskInfo1, runningTaskInfo2));
+
+        // Using app provided web uri for the first backlink.
+        Uri expectedUri = Uri.parse("https://developers.android.com");
+        AssistContent contentWithUri = new AssistContent();
+        contentWithUri.setWebUri(expectedUri);
+        mockForAssistContent(contentWithUri, BACKLINKS_TASK_ID);
+
+        // Using app provided intent for the second backlink.
+        Intent expectedIntent = new Intent().setPackage(package2);
+        AssistContent contentWithAppProvidedIntent = new AssistContent();
+        contentWithAppProvidedIntent.setIntent(expectedIntent);
+        mockForAssistContent(contentWithAppProvidedIntent, taskId2);
+
+        // Set up complete, trigger the backlinks action.
+        mViewModel.triggerBacklinks(Collections.emptySet(), DEFAULT_DISPLAY);
+        waitForIdleSync();
+
+        // Verify two backlinks are received and the first backlink is set as default selected.
+        assertThat(mViewModel.mSelectedBacklinksLiveData.getValue().getClipData().getItemAt(
+                0).getUri()).isEqualTo(expectedUri);
+        List<InternalBacklinksData> actualBacklinks = mViewModel.getBacklinksLiveData().getValue();
+        assertThat(actualBacklinks).hasSize(2);
+        assertThat(actualBacklinks.get(0).getClipData().getItemAt(0).getUri())
+                .isEqualTo(expectedUri);
+        assertThat(actualBacklinks.get(1).getClipData().getItemAt(0).getIntent())
+                .isEqualTo(expectedIntent);
+    }
+
     private void resetPackageManagerMockingForUsingFallbackBacklinks() {
         ResolveInfo backlinksTaskResolveInfo = createBacklinksTaskResolveInfo();
         reset(mPackageManager);
@@ -350,7 +389,7 @@
     }
 
     private void verifyMainLauncherBacklinksIntent() {
-        InternalBacklinksData result = mViewModel.getBacklinksLiveData().getValue();
+        InternalBacklinksData result = mViewModel.mSelectedBacklinksLiveData.getValue();
         assertThat(result.getAppIcon()).isEqualTo(FAKE_DRAWABLE);
 
         ClipData clipData = result.getClipData();
@@ -368,6 +407,14 @@
                 new ComponentName(BACKLINKS_TASK_PACKAGE_NAME, BACKLINKS_TASK_APP_NAME));
     }
 
+    private void mockForAssistContent(AssistContent expected, int taskId) {
+        doAnswer(invocation -> {
+            AssistContentRequester.Callback callback = invocation.getArgument(1);
+            callback.onAssistContentAvailable(expected);
+            return null;
+        }).when(mAssistContentRequester).requestAssistContent(eq(taskId), any());
+    }
+
     private static ResolveInfo createBacklinksTaskResolveInfo() {
         ActivityInfo activityInfo = new ActivityInfo();
         activityInfo.applicationInfo = new ApplicationInfo();
@@ -379,8 +426,8 @@
         return resolveInfo;
     }
 
-    private static RootTaskInfo createTaskInfoForBacklinksTask() {
-        RootTaskInfo taskInfo = new RootTaskInfo();
+    private static RunningTaskInfo createTaskInfoForBacklinksTask() {
+        RunningTaskInfo taskInfo = new RunningTaskInfo();
         taskInfo.taskId = BACKLINKS_TASK_ID;
         taskInfo.isVisible = true;
         taskInfo.isRunning = true;
@@ -388,7 +435,6 @@
         taskInfo.topActivity = new ComponentName(BACKLINKS_TASK_PACKAGE_NAME, "backlinksClass");
         taskInfo.topActivityInfo = createBacklinksTaskResolveInfo().activityInfo;
         taskInfo.baseIntent = new Intent().setComponent(taskInfo.topActivity);
-        taskInfo.childTaskIds = new int[]{BACKLINKS_TASK_ID + 1};
         taskInfo.configuration.windowConfiguration.setActivityType(ACTIVITY_TYPE_STANDARD);
         return taskInfo;
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/policy/WorkProfilePolicyTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/policy/WorkProfilePolicyTest.kt
index 8d217fc..a5fbfb5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/policy/WorkProfilePolicyTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/policy/WorkProfilePolicyTest.kt
@@ -17,11 +17,12 @@
 package com.android.systemui.screenshot.policy
 
 import android.content.ComponentName
-import androidx.test.ext.junit.runners.AndroidJUnit4
+import android.content.Context
 import android.os.UserHandle
 import android.platform.test.annotations.DisableFlags
 import android.platform.test.annotations.EnableFlags
 import android.platform.test.flag.junit.SetFlagsRule
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.screenshot.data.model.DisplayContentModel
 import com.android.systemui.screenshot.data.model.DisplayContentScenarios.ActivityNames.FILES
@@ -49,16 +50,30 @@
 import com.android.window.flags.Flags
 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
 
 @RunWith(AndroidJUnit4::class)
 class WorkProfilePolicyTest {
-    @JvmField @Rule val setFlagsRule = SetFlagsRule()
+
+    @JvmField @Rule(order = 1) val setFlagsRule = SetFlagsRule()
+
+    @JvmField @Rule(order = 2) val mockitoRule: MockitoRule = MockitoJUnit.rule()
+
+    @Mock lateinit var mContext: Context
 
     private val kosmos = Kosmos()
-    private val policy = WorkProfilePolicy(kosmos.profileTypeRepository)
+    private lateinit var policy: WorkProfilePolicy
+
+    @Before
+    fun setUp() {
+        policy = WorkProfilePolicy(kosmos.profileTypeRepository, mContext)
+    }
 
     /**
      * There is no guarantee that every RootTaskInfo contains a non-empty list of child tasks. Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
index 967df39..5de31d8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
@@ -429,6 +429,7 @@
     fun gestureExclusionZone_setAfterInit() =
         with(kosmos) {
             testScope.runTest {
+                whenever(containerView.layoutDirection).thenReturn(View.LAYOUT_DIRECTION_LTR)
                 goToScene(CommunalScenes.Communal)
 
                 assertThat(containerView.systemGestureExclusionRects)
@@ -450,10 +451,37 @@
         }
 
     @Test
+    @DisableFlags(FLAG_GLANCEABLE_HUB_BACK_GESTURE)
+    fun gestureExclusionZone_setAfterInit_rtl() =
+        with(kosmos) {
+            testScope.runTest {
+                whenever(containerView.layoutDirection).thenReturn(View.LAYOUT_DIRECTION_RTL)
+                goToScene(CommunalScenes.Communal)
+
+                assertThat(containerView.systemGestureExclusionRects)
+                    .containsExactly(
+                        Rect(
+                            /* left= */ 0,
+                            /* top= */ TOP_SWIPE_REGION_WIDTH,
+                            /* right= */ CONTAINER_WIDTH,
+                            /* bottom= */ CONTAINER_HEIGHT - BOTTOM_SWIPE_REGION_WIDTH
+                        ),
+                        Rect(
+                            /* left= */ 0,
+                            /* top= */ 0,
+                            /* right= */ CONTAINER_WIDTH,
+                            /* bottom= */ CONTAINER_HEIGHT
+                        )
+                    )
+            }
+        }
+
+    @Test
     @EnableFlags(FLAG_GLANCEABLE_HUB_BACK_GESTURE)
     fun gestureExclusionZone_setAfterInit_backGestureEnabled() =
         with(kosmos) {
             testScope.runTest {
+                whenever(containerView.layoutDirection).thenReturn(View.LAYOUT_DIRECTION_LTR)
                 goToScene(CommunalScenes.Communal)
 
                 assertThat(containerView.systemGestureExclusionRects)
@@ -475,6 +503,32 @@
         }
 
     @Test
+    @EnableFlags(FLAG_GLANCEABLE_HUB_BACK_GESTURE)
+    fun gestureExclusionZone_setAfterInit_backGestureEnabled_rtl() =
+        with(kosmos) {
+            testScope.runTest {
+                whenever(containerView.layoutDirection).thenReturn(View.LAYOUT_DIRECTION_RTL)
+                goToScene(CommunalScenes.Communal)
+
+                assertThat(containerView.systemGestureExclusionRects)
+                    .containsExactly(
+                        Rect(
+                            /* left= */ FAKE_INSETS.left,
+                            /* top= */ TOP_SWIPE_REGION_WIDTH,
+                            /* right= */ CONTAINER_WIDTH - FAKE_INSETS.right,
+                            /* bottom= */ CONTAINER_HEIGHT - BOTTOM_SWIPE_REGION_WIDTH
+                        ),
+                        Rect(
+                            /* left= */ FAKE_INSETS.left,
+                            /* top= */ 0,
+                            /* right= */ CONTAINER_WIDTH,
+                            /* bottom= */ CONTAINER_HEIGHT
+                        )
+                    )
+            }
+        }
+
+    @Test
     fun gestureExclusionZone_unsetWhenShadeOpen() =
         with(kosmos) {
             testScope.runTest {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
index a5c4bcd..2803035 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
@@ -479,7 +479,6 @@
                 mKeyguardLogger,
                 mKosmos.getInteractionJankMonitor(),
                 mKeyguardInteractor,
-                mDumpManager,
                 mPowerInteractor));
 
         when(mAuthController.isUdfpsEnrolled(anyInt())).thenReturn(false);
@@ -698,7 +697,6 @@
                 mFalsingManager, new FalsingCollectorFake(),
                 mKeyguardStateController,
                 mStatusBarStateController,
-                mStatusBarWindowStateController,
                 mNotificationShadeWindowController,
                 mDozeLog, mDozeParameters, mCommandQueue, mVibratorHelper,
                 mLatencyTracker, mAccessibilityManager, 0, mUpdateMonitor,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplBaseTest.java
index 06a883c..e57382d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplBaseTest.java
@@ -44,7 +44,6 @@
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.fragments.FragmentHostManager;
-import com.android.systemui.keyguard.data.repository.FakeCommandQueue;
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository;
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
@@ -190,7 +189,6 @@
                 mKosmos.getKeyguardTransitionInteractor();
         KeyguardInteractor keyguardInteractor = new KeyguardInteractor(
                 mKeyguardRepository,
-                new FakeCommandQueue(),
                 powerInteractor,
                 new FakeKeyguardBouncerRepository(),
                 new ConfigurationInteractor(configurationRepository),
@@ -254,6 +252,8 @@
         when(mQsFrame.getWidth()).thenReturn(QS_FRAME_WIDTH);
         when(mQsHeader.getTop()).thenReturn(QS_FRAME_TOP);
         when(mQsHeader.getBottom()).thenReturn(QS_FRAME_BOTTOM);
+        when(mQs.getHeaderTop()).thenReturn(QS_FRAME_TOP);
+        when(mQs.getHeaderBottom()).thenReturn(QS_FRAME_BOTTOM);
         when(mPanelView.getY()).thenReturn((float) QS_FRAME_TOP);
         when(mPanelView.getHeight()).thenReturn(QS_FRAME_BOTTOM);
         when(mPanelView.findViewById(R.id.keyguard_status_view))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplTest.java
index 9a6423d..e7db469 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplTest.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.shade;
 
+import static android.platform.test.flag.junit.FlagsParameterization.progressionOf;
 import static android.view.MotionEvent.ACTION_DOWN;
 import static android.view.MotionEvent.ACTION_MOVE;
 import static android.view.MotionEvent.ACTION_POINTER_DOWN;
@@ -23,6 +24,8 @@
 import static android.view.MotionEvent.BUTTON_SECONDARY;
 import static android.view.MotionEvent.BUTTON_STYLUS_PRIMARY;
 
+import static com.android.systemui.Flags.FLAG_QS_UI_REFACTOR;
+import static com.android.systemui.Flags.FLAG_QS_UI_REFACTOR_COMPOSE_FRAGMENT;
 import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
 import static com.android.systemui.statusbar.StatusBarState.SHADE;
 
@@ -36,10 +39,10 @@
 import static org.mockito.Mockito.when;
 
 import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.FlagsParameterization;
 import android.testing.TestableLooper;
 import android.view.MotionEvent;
 
-import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.plugins.qs.QS;
@@ -52,16 +55,29 @@
 
 import java.util.List;
 
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
+import platform.test.runner.parameterized.Parameters;
+
 @SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(ParameterizedAndroidJunit4.class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 public class QuickSettingsControllerImplTest extends QuickSettingsControllerImplBaseTest {
 
+    @Parameters(name = "{0}")
+    public static List<FlagsParameterization> getParams() {
+        return progressionOf(FLAG_QS_UI_REFACTOR, FLAG_QS_UI_REFACTOR_COMPOSE_FRAGMENT);
+    }
+
+    public QuickSettingsControllerImplTest(FlagsParameterization flags) {
+        super();
+        mSetFlagsRule.setFlagsParameterization(flags);
+    }
+
     @Test
     public void testCloseQsSideEffects() {
         enableSplitShade(true);
         mQsController.setExpandImmediate(true);
-        mQsController.setExpanded(true);
+        mQsController.setExpansionHeight(800);
         mQsController.closeQs();
 
         assertThat(mQsController.getExpanded()).isEqualTo(false);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/notifications/data/repository/NotificationSettingsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/notifications/data/repository/NotificationSettingsRepositoryTest.kt
index 69cc9d5..35f2e6e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/notifications/data/repository/NotificationSettingsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/notifications/data/repository/NotificationSettingsRepositoryTest.kt
@@ -17,11 +17,13 @@
 package com.android.systemui.shared.notifications.data.repository
 
 import android.provider.Settings
+import android.provider.Settings.Secure.ZEN_DURATION_FOREVER
 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.shared.settings.data.repository.FakeSecureSettingsRepository
+import com.android.systemui.shared.settings.data.repository.FakeSystemSettingsRepository
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.test.StandardTestDispatcher
 import kotlinx.coroutines.test.TestScope
@@ -38,23 +40,26 @@
 
     private lateinit var testScope: TestScope
     private lateinit var secureSettingsRepository: FakeSecureSettingsRepository
+    private lateinit var systemSettingsRepository: FakeSystemSettingsRepository
 
     @Before
     fun setUp() {
         val testDispatcher = StandardTestDispatcher()
         testScope = TestScope(testDispatcher)
         secureSettingsRepository = FakeSecureSettingsRepository()
+        systemSettingsRepository = FakeSystemSettingsRepository()
 
         underTest =
             NotificationSettingsRepository(
-                scope = testScope.backgroundScope,
+                backgroundScope = testScope.backgroundScope,
                 backgroundDispatcher = testDispatcher,
                 secureSettingsRepository = secureSettingsRepository,
+                systemSettingsRepository = systemSettingsRepository,
             )
     }
 
     @Test
-    fun testGetIsShowNotificationsOnLockscreenEnabled() =
+    fun getIsShowNotificationsOnLockscreenEnabled() =
         testScope.runTest {
             val showNotifs by collectLastValue(underTest.isShowNotificationsOnLockScreenEnabled())
 
@@ -72,7 +77,7 @@
         }
 
     @Test
-    fun testSetIsShowNotificationsOnLockscreenEnabled() =
+    fun setIsShowNotificationsOnLockscreenEnabled() =
         testScope.runTest {
             val showNotifs by collectLastValue(underTest.isShowNotificationsOnLockScreenEnabled())
 
@@ -84,7 +89,7 @@
         }
 
     @Test
-    fun testGetIsNotificationHistoryEnabled() =
+    fun getIsNotificationHistoryEnabled() =
         testScope.runTest {
             val historyEnabled by collectLastValue(underTest.isNotificationHistoryEnabled)
 
@@ -100,4 +105,40 @@
             )
             assertThat(historyEnabled).isEqualTo(false)
         }
+
+    @Test
+    fun testGetIsCooldownEnabled() =
+        testScope.runTest {
+            val cooldownEnabled by collectLastValue(underTest.isCooldownEnabled)
+
+            systemSettingsRepository.setInt(
+                name = Settings.System.NOTIFICATION_COOLDOWN_ENABLED,
+                value = 1,
+            )
+            assertThat(cooldownEnabled).isEqualTo(true)
+
+            systemSettingsRepository.setInt(
+                name = Settings.System.NOTIFICATION_COOLDOWN_ENABLED,
+                value = 0,
+            )
+            assertThat(cooldownEnabled).isEqualTo(false)
+        }
+
+    @Test
+    fun zenDuration() =
+        testScope.runTest {
+            val zenDuration by collectLastValue(underTest.zenDuration)
+
+            secureSettingsRepository.setInt(
+                name = Settings.Secure.ZEN_DURATION,
+                value = 60,
+            )
+            assertThat(zenDuration).isEqualTo(60)
+
+            secureSettingsRepository.setInt(
+                name = Settings.Secure.ZEN_DURATION,
+                value = ZEN_DURATION_FOREVER,
+            )
+            assertThat(zenDuration).isEqualTo(ZEN_DURATION_FOREVER)
+        }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
index 9df46e5..86d21e8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
@@ -195,10 +195,9 @@
 
     @Test
     public void testShowImeButton() {
-        mCommandQueue.setImeWindowStatus(DEFAULT_DISPLAY, null, 1, 2, true);
+        mCommandQueue.setImeWindowStatus(DEFAULT_DISPLAY, 1, 2, true);
         waitForIdleSync();
-        verify(mCallbacks).setImeWindowStatus(
-                eq(DEFAULT_DISPLAY), eq(null), eq(1), eq(2), eq(true));
+        verify(mCallbacks).setImeWindowStatus(eq(DEFAULT_DISPLAY), eq(1), eq(2), eq(true));
     }
 
     @Test
@@ -206,12 +205,11 @@
         // First show in default display to update the "last updated ime display"
         testShowImeButton();
 
-        mCommandQueue.setImeWindowStatus(SECONDARY_DISPLAY, null, 1, 2, true);
+        mCommandQueue.setImeWindowStatus(SECONDARY_DISPLAY, 1, 2, true);
         waitForIdleSync();
-        verify(mCallbacks).setImeWindowStatus(eq(DEFAULT_DISPLAY), eq(null), eq(IME_INVISIBLE),
+        verify(mCallbacks).setImeWindowStatus(eq(DEFAULT_DISPLAY), eq(IME_INVISIBLE),
                 eq(BACK_DISPOSITION_DEFAULT), eq(false));
-        verify(mCallbacks).setImeWindowStatus(
-                eq(SECONDARY_DISPLAY), eq(null), eq(1), eq(2), eq(true));
+        verify(mCallbacks).setImeWindowStatus(eq(SECONDARY_DISPLAY), eq(1), eq(2), eq(true));
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/OWNERS b/packages/SystemUI/tests/src/com/android/systemui/statusbar/OWNERS
new file mode 100644
index 0000000..1c52b8d
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/OWNERS
@@ -0,0 +1,3 @@
+set noparent
+
+include /packages/SystemUI/src/com/android/systemui/statusbar/OWNERS
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/call/domain/interactor/CallChipInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/call/domain/interactor/CallChipInteractorTest.kt
index cd8a740..8f41caf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/call/domain/interactor/CallChipInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/call/domain/interactor/CallChipInteractorTest.kt
@@ -23,6 +23,7 @@
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.statusbar.phone.ongoingcall.data.repository.ongoingCallRepository
 import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel
+import com.android.systemui.statusbar.phone.ongoingcall.shared.model.inCallModel
 import com.google.common.truth.Truth.assertThat
 import kotlin.test.Test
 import kotlinx.coroutines.test.runTest
@@ -39,7 +40,7 @@
         kosmos.testScope.runTest {
             val latest by collectLastValue(underTest.ongoingCallState)
 
-            val inCall = OngoingCallModel.InCall(startTimeMs = 1000, intent = null)
+            val inCall = inCallModel(startTimeMs = 1000)
             repo.setOngoingCallState(inCall)
             assertThat(latest).isEqualTo(inCall)
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt
index 1a6b420..ce79fbd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt
@@ -17,8 +17,11 @@
 package com.android.systemui.statusbar.chips.call.ui.viewmodel
 
 import android.app.PendingIntent
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
 import android.view.View
 import androidx.test.filters.SmallTest
+import com.android.systemui.Flags.FLAG_STATUS_BAR_CALL_CHIP_NOTIFICATION_ICON
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.coroutines.collectLastValue
@@ -26,11 +29,13 @@
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.plugins.activityStarter
 import com.android.systemui.res.R
+import com.android.systemui.statusbar.StatusBarIconView
 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.view.ChipBackgroundContainer
 import com.android.systemui.statusbar.phone.ongoingcall.data.repository.ongoingCallRepository
 import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel
+import com.android.systemui.statusbar.phone.ongoingcall.shared.model.inCallModel
 import com.android.systemui.util.time.fakeSystemClock
 import com.google.common.truth.Truth.assertThat
 import kotlin.test.Test
@@ -73,7 +78,7 @@
         testScope.runTest {
             val latest by collectLastValue(underTest.chip)
 
-            repo.setOngoingCallState(OngoingCallModel.InCall(startTimeMs = 0, intent = null))
+            repo.setOngoingCallState(inCallModel(startTimeMs = 0))
 
             assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown.IconOnly::class.java)
         }
@@ -83,7 +88,7 @@
         testScope.runTest {
             val latest by collectLastValue(underTest.chip)
 
-            repo.setOngoingCallState(OngoingCallModel.InCall(startTimeMs = -2, intent = null))
+            repo.setOngoingCallState(inCallModel(startTimeMs = -2))
 
             assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown.IconOnly::class.java)
         }
@@ -93,7 +98,7 @@
         testScope.runTest {
             val latest by collectLastValue(underTest.chip)
 
-            repo.setOngoingCallState(OngoingCallModel.InCall(startTimeMs = 345, intent = null))
+            repo.setOngoingCallState(inCallModel(startTimeMs = 345))
 
             assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown.Timer::class.java)
         }
@@ -106,7 +111,7 @@
             kosmos.fakeSystemClock.setCurrentTimeMillis(3000)
             kosmos.fakeSystemClock.setElapsedRealtime(400_000)
 
-            repo.setOngoingCallState(OngoingCallModel.InCall(startTimeMs = 1000, intent = null))
+            repo.setOngoingCallState(inCallModel(startTimeMs = 1000))
 
             // The OngoingCallModel start time is relative to currentTimeMillis, so this call
             // started 2000ms ago (1000 - 3000). The OngoingActivityChipModel start time needs to be
@@ -117,29 +122,97 @@
         }
 
     @Test
-    fun chip_positiveStartTime_iconIsPhone() =
+    @DisableFlags(FLAG_STATUS_BAR_CALL_CHIP_NOTIFICATION_ICON)
+    fun chip_positiveStartTime_notifIconFlagOff_iconIsPhone() =
         testScope.runTest {
             val latest by collectLastValue(underTest.chip)
 
-            repo.setOngoingCallState(OngoingCallModel.InCall(startTimeMs = 1000, intent = null))
+            repo.setOngoingCallState(
+                inCallModel(startTimeMs = 1000, notificationIcon = mock<StatusBarIconView>())
+            )
 
-            assertThat(((latest as OngoingActivityChipModel.Shown).icon as Icon.Resource).res)
-                .isEqualTo(com.android.internal.R.drawable.ic_phone)
-            assertThat((latest as OngoingActivityChipModel.Shown).icon!!.contentDescription)
-                .isNotNull()
+            assertThat((latest as OngoingActivityChipModel.Shown).icon)
+                .isInstanceOf(OngoingActivityChipModel.ChipIcon.Basic::class.java)
+            val icon =
+                (((latest as OngoingActivityChipModel.Shown).icon)
+                        as OngoingActivityChipModel.ChipIcon.Basic)
+                    .impl as Icon.Resource
+            assertThat(icon.res).isEqualTo(com.android.internal.R.drawable.ic_phone)
+            assertThat(icon.contentDescription).isNotNull()
         }
 
     @Test
-    fun chip_zeroStartTime_iconIsPhone() =
+    @EnableFlags(FLAG_STATUS_BAR_CALL_CHIP_NOTIFICATION_ICON)
+    fun chip_positiveStartTime_notifIconFlagOn_iconIsNotifIcon() =
         testScope.runTest {
             val latest by collectLastValue(underTest.chip)
 
-            repo.setOngoingCallState(OngoingCallModel.InCall(startTimeMs = 0, intent = null))
+            val notifIcon = mock<StatusBarIconView>()
+            repo.setOngoingCallState(inCallModel(startTimeMs = 1000, notificationIcon = notifIcon))
 
-            assertThat(((latest as OngoingActivityChipModel.Shown).icon as Icon.Resource).res)
-                .isEqualTo(com.android.internal.R.drawable.ic_phone)
-            assertThat((latest as OngoingActivityChipModel.Shown).icon!!.contentDescription)
-                .isNotNull()
+            assertThat((latest as OngoingActivityChipModel.Shown).icon)
+                .isInstanceOf(OngoingActivityChipModel.ChipIcon.StatusBarView::class.java)
+            val actualIcon =
+                (((latest as OngoingActivityChipModel.Shown).icon)
+                        as OngoingActivityChipModel.ChipIcon.StatusBarView)
+                    .impl
+            assertThat(actualIcon).isEqualTo(notifIcon)
+        }
+
+    @Test
+    @DisableFlags(FLAG_STATUS_BAR_CALL_CHIP_NOTIFICATION_ICON)
+    fun chip_zeroStartTime_notifIconFlagOff_iconIsPhone() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.chip)
+
+            repo.setOngoingCallState(
+                inCallModel(startTimeMs = 0, notificationIcon = mock<StatusBarIconView>())
+            )
+
+            assertThat((latest as OngoingActivityChipModel.Shown).icon)
+                .isInstanceOf(OngoingActivityChipModel.ChipIcon.Basic::class.java)
+            val icon =
+                (((latest as OngoingActivityChipModel.Shown).icon)
+                        as OngoingActivityChipModel.ChipIcon.Basic)
+                    .impl as Icon.Resource
+            assertThat(icon.res).isEqualTo(com.android.internal.R.drawable.ic_phone)
+            assertThat(icon.contentDescription).isNotNull()
+        }
+
+    @Test
+    @EnableFlags(FLAG_STATUS_BAR_CALL_CHIP_NOTIFICATION_ICON)
+    fun chip_zeroStartTime_notifIconFlagOn_iconIsNotifIcon() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.chip)
+
+            val notifIcon = mock<StatusBarIconView>()
+            repo.setOngoingCallState(inCallModel(startTimeMs = 0, notificationIcon = notifIcon))
+
+            assertThat((latest as OngoingActivityChipModel.Shown).icon)
+                .isInstanceOf(OngoingActivityChipModel.ChipIcon.StatusBarView::class.java)
+            val actualIcon =
+                (((latest as OngoingActivityChipModel.Shown).icon)
+                        as OngoingActivityChipModel.ChipIcon.StatusBarView)
+                    .impl
+            assertThat(actualIcon).isEqualTo(notifIcon)
+        }
+
+    @Test
+    @EnableFlags(FLAG_STATUS_BAR_CALL_CHIP_NOTIFICATION_ICON)
+    fun chip_notifIconFlagOn_butNullNotifIcon_iconIsPhone() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.chip)
+
+            repo.setOngoingCallState(inCallModel(startTimeMs = 1000, notificationIcon = null))
+
+            assertThat((latest as OngoingActivityChipModel.Shown).icon)
+                .isInstanceOf(OngoingActivityChipModel.ChipIcon.Basic::class.java)
+            val icon =
+                (((latest as OngoingActivityChipModel.Shown).icon)
+                        as OngoingActivityChipModel.ChipIcon.Basic)
+                    .impl as Icon.Resource
+            assertThat(icon.res).isEqualTo(com.android.internal.R.drawable.ic_phone)
+            assertThat(icon.contentDescription).isNotNull()
         }
 
     @Test
@@ -147,7 +220,7 @@
         testScope.runTest {
             val latest by collectLastValue(underTest.chip)
 
-            repo.setOngoingCallState(OngoingCallModel.InCall(startTimeMs = 1000, intent = null))
+            repo.setOngoingCallState(inCallModel(startTimeMs = 1000))
 
             assertThat((latest as OngoingActivityChipModel.Shown).colors)
                 .isEqualTo(ColorsModel.Themed)
@@ -158,7 +231,7 @@
         testScope.runTest {
             val latest by collectLastValue(underTest.chip)
 
-            repo.setOngoingCallState(OngoingCallModel.InCall(startTimeMs = 0, intent = null))
+            repo.setOngoingCallState(inCallModel(startTimeMs = 0))
 
             assertThat((latest as OngoingActivityChipModel.Shown).colors)
                 .isEqualTo(ColorsModel.Themed)
@@ -172,7 +245,7 @@
             kosmos.fakeSystemClock.setElapsedRealtime(400_000)
 
             // Start a call
-            repo.setOngoingCallState(OngoingCallModel.InCall(startTimeMs = 1000, intent = null))
+            repo.setOngoingCallState(inCallModel(startTimeMs = 1000))
             assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
             assertThat((latest as OngoingActivityChipModel.Shown.Timer).startTimeMs)
                 .isEqualTo(398_000)
@@ -186,7 +259,7 @@
             kosmos.fakeSystemClock.setElapsedRealtime(500_000)
 
             // Start a new call, which started 1000ms ago
-            repo.setOngoingCallState(OngoingCallModel.InCall(startTimeMs = 102_000, intent = null))
+            repo.setOngoingCallState(inCallModel(startTimeMs = 102_000))
             assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
             assertThat((latest as OngoingActivityChipModel.Shown.Timer).startTimeMs)
                 .isEqualTo(499_000)
@@ -197,7 +270,7 @@
         testScope.runTest {
             val latest by collectLastValue(underTest.chip)
 
-            repo.setOngoingCallState(OngoingCallModel.InCall(startTimeMs = 1000, intent = null))
+            repo.setOngoingCallState(inCallModel(startTimeMs = 1000, intent = null))
 
             assertThat((latest as OngoingActivityChipModel.Shown).onClickListener).isNull()
         }
@@ -208,7 +281,7 @@
             val latest by collectLastValue(underTest.chip)
 
             val intent = mock<PendingIntent>()
-            repo.setOngoingCallState(OngoingCallModel.InCall(startTimeMs = 1000, intent = intent))
+            repo.setOngoingCallState(inCallModel(startTimeMs = 1000, intent = intent))
             val clickListener = (latest as OngoingActivityChipModel.Shown).onClickListener
             assertThat(clickListener).isNotNull()
 
@@ -223,7 +296,7 @@
             val latest by collectLastValue(underTest.chip)
 
             val intent = mock<PendingIntent>()
-            repo.setOngoingCallState(OngoingCallModel.InCall(startTimeMs = 0, intent = intent))
+            repo.setOngoingCallState(inCallModel(startTimeMs = 0, intent = intent))
             val clickListener = (latest as OngoingActivityChipModel.Shown).onClickListener
             assertThat(clickListener).isNotNull()
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModelTest.kt
index 02764f8..a8d2c5b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModelTest.kt
@@ -125,8 +125,11 @@
                 )
 
             assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown.Timer::class.java)
-            val icon = (latest as OngoingActivityChipModel.Shown).icon
-            assertThat((icon as Icon.Resource).res).isEqualTo(R.drawable.ic_cast_connected)
+            val icon =
+                (((latest as OngoingActivityChipModel.Shown).icon)
+                        as OngoingActivityChipModel.ChipIcon.Basic)
+                    .impl as Icon.Resource
+            assertThat(icon.res).isEqualTo(R.drawable.ic_cast_connected)
             assertThat((icon.contentDescription as ContentDescription.Resource).res)
                 .isEqualTo(R.string.cast_screen_to_other_device_chip_accessibility_label)
         }
@@ -141,8 +144,11 @@
                 MediaProjectionState.Projecting.EntireScreen(CAST_TO_OTHER_DEVICES_PACKAGE)
 
             assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown.Timer::class.java)
-            val icon = (latest as OngoingActivityChipModel.Shown).icon
-            assertThat((icon as Icon.Resource).res).isEqualTo(R.drawable.ic_cast_connected)
+            val icon =
+                (((latest as OngoingActivityChipModel.Shown).icon)
+                        as OngoingActivityChipModel.ChipIcon.Basic)
+                    .impl as Icon.Resource
+            assertThat(icon.res).isEqualTo(R.drawable.ic_cast_connected)
             assertThat((icon.contentDescription as ContentDescription.Resource).res)
                 .isEqualTo(R.string.cast_screen_to_other_device_chip_accessibility_label)
         }
@@ -176,8 +182,11 @@
                 )
 
             assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown.IconOnly::class.java)
-            val icon = (latest as OngoingActivityChipModel.Shown).icon
-            assertThat((icon as Icon.Resource).res).isEqualTo(R.drawable.ic_cast_connected)
+            val icon =
+                (((latest as OngoingActivityChipModel.Shown).icon)
+                        as OngoingActivityChipModel.ChipIcon.Basic)
+                    .impl as Icon.Resource
+            assertThat(icon.res).isEqualTo(R.drawable.ic_cast_connected)
             // This content description is just generic "Casting", not "Casting screen"
             assertThat((icon.contentDescription as ContentDescription.Resource).res)
                 .isEqualTo(R.string.accessibility_casting)
@@ -203,8 +212,11 @@
 
             // Only the projection info will show a timer
             assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown.Timer::class.java)
-            val icon = (latest as OngoingActivityChipModel.Shown).icon
-            assertThat((icon as Icon.Resource).res).isEqualTo(R.drawable.ic_cast_connected)
+            val icon =
+                (((latest as OngoingActivityChipModel.Shown).icon)
+                        as OngoingActivityChipModel.ChipIcon.Basic)
+                    .impl as Icon.Resource
+            assertThat(icon.res).isEqualTo(R.drawable.ic_cast_connected)
             // MediaProjection == screen casting, so this content description reflects that we're
             // using the MediaProjection information.
             assertThat((icon.contentDescription as ContentDescription.Resource).res)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModelTest.kt
index b4a37ee..e68fa0b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModelTest.kt
@@ -148,8 +148,11 @@
             screenRecordRepo.screenRecordState.value = ScreenRecordModel.Recording
 
             assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown.Timer::class.java)
-            val icon = (latest as OngoingActivityChipModel.Shown).icon
-            assertThat((icon as Icon.Resource).res).isEqualTo(R.drawable.ic_screenrecord)
+            val icon =
+                (((latest as OngoingActivityChipModel.Shown).icon)
+                        as OngoingActivityChipModel.ChipIcon.Basic)
+                    .impl as Icon.Resource
+            assertThat(icon.res).isEqualTo(R.drawable.ic_screenrecord)
             assertThat(icon.contentDescription).isNotNull()
         }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModelTest.kt
index 2658679..a2ef599 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModelTest.kt
@@ -133,8 +133,11 @@
                 )
 
             assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown.Timer::class.java)
-            val icon = (latest as OngoingActivityChipModel.Shown).icon
-            assertThat((icon as Icon.Resource).res).isEqualTo(R.drawable.ic_present_to_all)
+            val icon =
+                (((latest as OngoingActivityChipModel.Shown).icon)
+                        as OngoingActivityChipModel.ChipIcon.Basic)
+                    .impl as Icon.Resource
+            assertThat(icon.res).isEqualTo(R.drawable.ic_present_to_all)
             assertThat(icon.contentDescription).isNotNull()
         }
 
@@ -147,8 +150,11 @@
                 MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE)
 
             assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown.Timer::class.java)
-            val icon = (latest as OngoingActivityChipModel.Shown).icon
-            assertThat((icon as Icon.Resource).res).isEqualTo(R.drawable.ic_present_to_all)
+            val icon =
+                (((latest as OngoingActivityChipModel.Shown).icon)
+                        as OngoingActivityChipModel.ChipIcon.Basic)
+                    .impl as Icon.Resource
+            assertThat(icon.res).isEqualTo(R.drawable.ic_present_to_all)
             assertThat(icon.contentDescription).isNotNull()
         }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/viewmodel/ChipTransitionHelperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/viewmodel/ChipTransitionHelperTest.kt
index b9049e8..a724cfaa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/viewmodel/ChipTransitionHelperTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/viewmodel/ChipTransitionHelperTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.statusbar.chips.ui.viewmodel
 
+import androidx.annotation.DrawableRes
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.common.shared.model.Icon
@@ -50,7 +51,7 @@
 
             val newChip =
                 OngoingActivityChipModel.Shown.Timer(
-                    icon = Icon.Resource(R.drawable.ic_cake, contentDescription = null),
+                    icon = createIcon(R.drawable.ic_cake),
                     colors = ColorsModel.Themed,
                     startTimeMs = 100L,
                     onClickListener = null,
@@ -62,7 +63,7 @@
 
             val newerChip =
                 OngoingActivityChipModel.Shown.IconOnly(
-                    icon = Icon.Resource(R.drawable.ic_hotspot, contentDescription = null),
+                    icon = createIcon(R.drawable.ic_hotspot),
                     colors = ColorsModel.Themed,
                     onClickListener = null,
                 )
@@ -82,7 +83,7 @@
 
             val shownChip =
                 OngoingActivityChipModel.Shown.Timer(
-                    icon = Icon.Resource(R.drawable.ic_cake, contentDescription = null),
+                    icon = createIcon(R.drawable.ic_cake),
                     colors = ColorsModel.Themed,
                     startTimeMs = 100L,
                     onClickListener = null,
@@ -122,7 +123,7 @@
 
             val shownChip =
                 OngoingActivityChipModel.Shown.Timer(
-                    icon = Icon.Resource(R.drawable.ic_cake, contentDescription = null),
+                    icon = createIcon(R.drawable.ic_cake),
                     colors = ColorsModel.Themed,
                     startTimeMs = 100L,
                     onClickListener = null,
@@ -151,4 +152,7 @@
             advanceTimeBy(2)
             assertThat(latest).isEqualTo(shownChip)
         }
+
+    private fun createIcon(@DrawableRes drawable: Int) =
+        OngoingActivityChipModel.ChipIcon.Basic(Icon.Resource(drawable, contentDescription = null))
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt
index ee249f0..556ec6a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt
@@ -42,6 +42,7 @@
 import com.android.systemui.statusbar.phone.mockSystemUIDialogFactory
 import com.android.systemui.statusbar.phone.ongoingcall.data.repository.ongoingCallRepository
 import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel
+import com.android.systemui.statusbar.phone.ongoingcall.shared.model.inCallModel
 import com.android.systemui.util.time.fakeSystemClock
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -120,7 +121,7 @@
         testScope.runTest {
             screenRecordState.value = ScreenRecordModel.Recording
 
-            callRepo.setOngoingCallState(OngoingCallModel.InCall(startTimeMs = 34, intent = null))
+            callRepo.setOngoingCallState(inCallModel(startTimeMs = 34))
 
             val latest by collectLastValue(underTest.chip)
 
@@ -146,7 +147,7 @@
             screenRecordState.value = ScreenRecordModel.DoingNothing
             mediaProjectionState.value =
                 MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE)
-            callRepo.setOngoingCallState(OngoingCallModel.InCall(startTimeMs = 34, intent = null))
+            callRepo.setOngoingCallState(inCallModel(startTimeMs = 34))
 
             val latest by collectLastValue(underTest.chip)
 
@@ -160,7 +161,7 @@
             // MediaProjection covers both share-to-app and cast-to-other-device
             mediaProjectionState.value = MediaProjectionState.NotProjecting
 
-            callRepo.setOngoingCallState(OngoingCallModel.InCall(startTimeMs = 34, intent = null))
+            callRepo.setOngoingCallState(inCallModel(startTimeMs = 34))
 
             val latest by collectLastValue(underTest.chip)
 
@@ -171,7 +172,7 @@
     fun chip_higherPriorityChipAdded_lowerPriorityChipReplaced() =
         testScope.runTest {
             // Start with just the lower priority call chip
-            callRepo.setOngoingCallState(OngoingCallModel.InCall(startTimeMs = 34, intent = null))
+            callRepo.setOngoingCallState(inCallModel(startTimeMs = 34))
             mediaProjectionState.value = MediaProjectionState.NotProjecting
             screenRecordState.value = ScreenRecordModel.DoingNothing
 
@@ -205,7 +206,7 @@
             mediaProjectionState.value =
                 MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE)
 
-            callRepo.setOngoingCallState(OngoingCallModel.InCall(startTimeMs = 34, intent = null))
+            callRepo.setOngoingCallState(inCallModel(startTimeMs = 34))
 
             val latest by collectLastValue(underTest.chip)
 
@@ -335,21 +336,29 @@
 
         fun assertIsScreenRecordChip(latest: OngoingActivityChipModel?) {
             assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
-            val icon = (latest as OngoingActivityChipModel.Shown).icon
-            assertThat((icon as Icon.Resource).res).isEqualTo(R.drawable.ic_screenrecord)
+            val icon =
+                (((latest as OngoingActivityChipModel.Shown).icon)
+                        as OngoingActivityChipModel.ChipIcon.Basic)
+                    .impl as Icon.Resource
+            assertThat(icon.res).isEqualTo(R.drawable.ic_screenrecord)
         }
 
         fun assertIsShareToAppChip(latest: OngoingActivityChipModel?) {
             assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
-            val icon = (latest as OngoingActivityChipModel.Shown).icon
-            assertThat((icon as Icon.Resource).res).isEqualTo(R.drawable.ic_present_to_all)
+            val icon =
+                (((latest as OngoingActivityChipModel.Shown).icon)
+                        as OngoingActivityChipModel.ChipIcon.Basic)
+                    .impl as Icon.Resource
+            assertThat(icon.res).isEqualTo(R.drawable.ic_present_to_all)
         }
 
         fun assertIsCallChip(latest: OngoingActivityChipModel?) {
             assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
-            val icon = (latest as OngoingActivityChipModel.Shown).icon
-            assertThat((icon as Icon.Resource).res)
-                .isEqualTo(com.android.internal.R.drawable.ic_phone)
+            val icon =
+                (((latest as OngoingActivityChipModel.Shown).icon)
+                        as OngoingActivityChipModel.ChipIcon.Basic)
+                    .impl as Icon.Resource
+            assertThat(icon.res).isEqualTo(com.android.internal.R.drawable.ic_phone)
         }
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryImplTest.kt
index 7f981ee..48ae7a2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryImplTest.kt
@@ -29,6 +29,7 @@
 import com.android.internal.view.AppearanceRegion
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
+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
@@ -36,8 +37,9 @@
 import com.android.systemui.statusbar.phone.LetterboxAppearanceCalculator
 import com.android.systemui.statusbar.phone.StatusBarBoundsProvider
 import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent
-import com.android.systemui.statusbar.phone.ongoingcall.data.repository.OngoingCallRepository
+import com.android.systemui.statusbar.phone.ongoingcall.data.repository.ongoingCallRepository
 import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel
+import com.android.systemui.statusbar.phone.ongoingcall.shared.model.inCallModel
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.argumentCaptor
 import com.android.systemui.util.mockito.capture
@@ -55,6 +57,7 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class StatusBarModeRepositoryImplTest : SysuiTestCase() {
+    private val kosmos = Kosmos()
     private val testScope = TestScope()
     private val commandQueue = mock<CommandQueue>()
     private val letterboxAppearanceCalculator = mock<LetterboxAppearanceCalculator>()
@@ -63,7 +66,7 @@
         mock<StatusBarFragmentComponent>().also {
             whenever(it.boundsProvider).thenReturn(statusBarBoundsProvider)
         }
-    private val ongoingCallRepository = OngoingCallRepository()
+    private val ongoingCallRepository = kosmos.ongoingCallRepository
 
     private val underTest =
         StatusBarModePerDisplayRepositoryImpl(
@@ -394,9 +397,7 @@
         testScope.runTest {
             val latest by collectLastValue(underTest.statusBarAppearance)
 
-            ongoingCallRepository.setOngoingCallState(
-                OngoingCallModel.InCall(startTimeMs = 34, intent = null)
-            )
+            ongoingCallRepository.setOngoingCallState(inCallModel(startTimeMs = 34))
             onSystemBarAttributesChanged(
                 requestedVisibleTypes = WindowInsets.Type.navigationBars(),
             )
@@ -409,9 +410,8 @@
         testScope.runTest {
             val latest by collectLastValue(underTest.statusBarAppearance)
 
-            ongoingCallRepository.setOngoingCallState(
-                OngoingCallModel.InCall(startTimeMs = 789, intent = null)
-            )
+            ongoingCallRepository.setOngoingCallState(inCallModel(startTimeMs = 789))
+
             onSystemBarAttributesChanged(
                 requestedVisibleTypes = WindowInsets.Type.statusBars(),
                 appearance = APPEARANCE_OPAQUE_STATUS_BARS,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinatorTest.kt
index 0505727..689fc7c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinatorTest.kt
@@ -28,7 +28,11 @@
 import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.server.notification.Flags.FLAG_SCREENSHARE_NOTIFICATION_HIDING
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
+import com.android.systemui.kosmos.applicationCoroutineScope
 import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.scene.domain.interactor.SceneInteractor
 import com.android.systemui.statusbar.NotificationLockscreenUserManager
 import com.android.systemui.statusbar.RankingBuilder
 import com.android.systemui.statusbar.StatusBarState
@@ -45,6 +49,7 @@
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
 import com.android.systemui.statusbar.policy.KeyguardStateController
 import com.android.systemui.statusbar.policy.SensitiveNotificationProtectionController
+import com.android.systemui.testKosmos
 import com.android.systemui.user.domain.interactor.SelectedUserInteractor
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.eq
@@ -52,6 +57,7 @@
 import com.android.systemui.util.mockito.withArgCaptor
 import dagger.BindsInstance
 import dagger.Component
+import kotlinx.coroutines.CoroutineScope
 import org.junit.Assert.assertFalse
 import org.junit.Assert.assertTrue
 import org.junit.Test
@@ -64,6 +70,8 @@
 @RunWith(AndroidJUnit4::class)
 class SensitiveContentCoordinatorTest : SysuiTestCase() {
 
+    val kosmos = testKosmos()
+
     val dynamicPrivacyController: DynamicPrivacyController = mock()
     val lockscreenUserManager: NotificationLockscreenUserManager = mock()
     val pipeline: NotifPipeline = mock()
@@ -73,6 +81,8 @@
     val mSelectedUserInteractor: SelectedUserInteractor = mock()
     val sensitiveNotificationProtectionController: SensitiveNotificationProtectionController =
         mock()
+    val deviceEntryInteractor: DeviceEntryInteractor = mock()
+    val sceneInteractor: SceneInteractor = mock()
 
     val coordinator: SensitiveContentCoordinator =
         DaggerTestSensitiveContentCoordinatorComponent.factory()
@@ -83,7 +93,10 @@
                 statusBarStateController,
                 keyguardStateController,
                 mSelectedUserInteractor,
-                sensitiveNotificationProtectionController
+                sensitiveNotificationProtectionController,
+                deviceEntryInteractor,
+                sceneInteractor,
+                kosmos.applicationCoroutineScope,
             )
             .coordinator
 
@@ -136,8 +149,7 @@
     @Test
     @EnableFlags(FLAG_SCREENSHARE_NOTIFICATION_HIDING)
     fun screenshareSecretFilter_sensitiveInctive_noFiltersSecret() {
-        whenever(sensitiveNotificationProtectionController.isSensitiveStateActive)
-            .thenReturn(false)
+        whenever(sensitiveNotificationProtectionController.isSensitiveStateActive).thenReturn(false)
 
         coordinator.attach(pipeline)
         val filter = withArgCaptor<NotifFilter> { verify(pipeline).addFinalizeFilter(capture()) }
@@ -683,10 +695,11 @@
         val mockSbn: StatusBarNotification =
             mock<StatusBarNotification>().apply { whenever(user).thenReturn(mockUserHandle) }
         val mockRow: ExpandableNotificationRow = mock<ExpandableNotificationRow>()
-        val mockEntry = mock<NotificationEntry>().apply {
-            whenever(sbn).thenReturn(mockSbn)
-            whenever(row).thenReturn(mockRow)
-        }
+        val mockEntry =
+            mock<NotificationEntry>().apply {
+                whenever(sbn).thenReturn(mockSbn)
+                whenever(row).thenReturn(mockRow)
+            }
         whenever(lockscreenUserManager.needsRedaction(mockEntry)).thenReturn(needsRedaction)
         whenever(mockEntry.rowExists()).thenReturn(true)
         return object : ListEntry("key", 0) {
@@ -737,6 +750,9 @@
             @BindsInstance selectedUserInteractor: SelectedUserInteractor,
             @BindsInstance
             sensitiveNotificationProtectionController: SensitiveNotificationProtectionController,
+            @BindsInstance deviceEntryInteractor: DeviceEntryInteractor,
+            @BindsInstance sceneInteractor: SceneInteractor,
+            @BindsInstance @Application scope: CoroutineScope,
         ): TestSensitiveContentCoordinatorComponent
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationsListInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationsListInteractorTest.kt
index 277b887..572a0c1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationsListInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationsListInteractorTest.kt
@@ -15,6 +15,8 @@
 
 package com.android.systemui.statusbar.notification.domain.interactor
 
+import android.app.Notification
+import android.os.Bundle
 import android.service.notification.StatusBarNotification
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
@@ -139,9 +141,10 @@
 }
 
 private fun mockNotificationEntry(key: String, rank: Int = 0): NotificationEntry {
+    val mockNotification = mock<Notification> { this.extras = Bundle() }
     val mockSbn =
         mock<StatusBarNotification>() {
-            whenever(notification).thenReturn(mock())
+            whenever(notification).thenReturn(mockNotification)
             whenever(packageName).thenReturn("com.android")
         }
     return mock<NotificationEntry> {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/SeenNotificationsInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/SeenNotificationsInteractorTest.kt
deleted file mode 100644
index 3908529..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/SeenNotificationsInteractorTest.kt
+++ /dev/null
@@ -1,54 +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.domain.interactor
-
-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.statusbar.notification.data.repository.ActiveNotificationListRepository
-import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.test.runTest
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@RunWith(AndroidJUnit4::class)
-@SmallTest
-class SeenNotificationsInteractorTest : SysuiTestCase() {
-
-    private val repository = ActiveNotificationListRepository()
-    private val underTest = SeenNotificationsInteractor(repository)
-
-    @Test
-    fun testNoFilteredOutSeenNotifications() = runTest {
-        val hasFilteredOutSeenNotifications by
-            collectLastValue(underTest.hasFilteredOutSeenNotifications)
-
-        underTest.setHasFilteredOutSeenNotifications(false)
-
-        assertThat(hasFilteredOutSeenNotifications).isFalse()
-    }
-
-    @Test
-    fun testHasFilteredOutSeenNotifications() = runTest {
-        val hasFilteredOutSeenNotifications by
-            collectLastValue(underTest.hasFilteredOutSeenNotifications)
-
-        underTest.setHasFilteredOutSeenNotifications(true)
-
-        assertThat(hasFilteredOutSeenNotifications).isTrue()
-    }
-}
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 bfa816e..25138fd 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
@@ -30,9 +30,12 @@
 import android.os.Bundle
 import android.os.SystemClock
 import android.os.UserHandle
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
 import androidx.test.InstrumentationRegistry
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
+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.notification.collection.NotificationEntry
@@ -108,6 +111,28 @@
     }
 
     @Test
+    @DisableFlags(FLAG_STATUS_BAR_CALL_CHIP_NOTIFICATION_ICON)
+    fun testCreateIcons_chipNotifIconFlagDisabled_statusBarChipIconIsNull() {
+        val entry =
+            notificationEntry(hasShortcut = true, hasMessageSenderIcon = true, hasLargeIcon = true)
+        entry?.let { iconManager.createIcons(it) }
+        testScope.runCurrent()
+
+        assertThat(entry?.icons?.statusBarChipIcon).isNull()
+    }
+
+    @Test
+    @EnableFlags(FLAG_STATUS_BAR_CALL_CHIP_NOTIFICATION_ICON)
+    fun testCreateIcons_chipNotifIconFlagEnabled_statusBarChipIconIsNull() {
+        val entry =
+            notificationEntry(hasShortcut = true, hasMessageSenderIcon = true, hasLargeIcon = true)
+        entry?.let { iconManager.createIcons(it) }
+        testScope.runCurrent()
+
+        assertThat(entry?.icons?.statusBarChipIcon).isNotNull()
+    }
+
+    @Test
     fun testCreateIcons_importantConversation_shortcutIcon() {
         val entry =
             notificationEntry(hasShortcut = true, hasMessageSenderIcon = true, hasLargeIcon = true)
@@ -179,6 +204,7 @@
     }
 
     @Test
+    @EnableFlags(FLAG_STATUS_BAR_CALL_CHIP_NOTIFICATION_ICON)
     fun testCreateIcons_sensitiveImportantConversation() {
         val entry =
             notificationEntry(hasShortcut = true, hasMessageSenderIcon = true, hasLargeIcon = false)
@@ -187,11 +213,13 @@
         entry?.let { iconManager.createIcons(it) }
         testScope.runCurrent()
         assertThat(entry?.icons?.statusBarIcon?.sourceIcon).isEqualTo(shortcutIc)
+        assertThat(entry?.icons?.statusBarChipIcon?.sourceIcon).isEqualTo(shortcutIc)
         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() {
         val entry =
             notificationEntry(hasShortcut = true, hasMessageSenderIcon = true, hasLargeIcon = false)
@@ -202,6 +230,7 @@
         entry?.let { iconManager.updateIcons(it) }
         testScope.runCurrent()
         assertThat(entry?.icons?.statusBarIcon?.sourceIcon).isEqualTo(shortcutIc)
+        assertThat(entry?.icons?.statusBarChipIcon?.sourceIcon).isEqualTo(shortcutIc)
         assertThat(entry?.icons?.shelfIcon?.sourceIcon).isEqualTo(smallIc)
         assertThat(entry?.icons?.aodIcon?.sourceIcon).isEqualTo(smallIc)
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImplTest.kt
index a7f36c3..d1b1f46 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImplTest.kt
@@ -17,6 +17,9 @@
 package com.android.systemui.statusbar.notification.interruption
 
 import android.Manifest.permission
+import android.app.Notification.CATEGORY_ALARM
+import android.app.Notification.CATEGORY_CAR_EMERGENCY
+import android.app.Notification.CATEGORY_CAR_WARNING
 import android.app.Notification.CATEGORY_EVENT
 import android.app.Notification.CATEGORY_REMINDER
 import android.app.NotificationManager
@@ -66,7 +69,8 @@
             packageManager,
             Optional.of(bubbles),
             context,
-            notificationManager
+            notificationManager,
+            settingsInteractor
         )
     }
 
@@ -101,7 +105,7 @@
         whenever(avalancheProvider.startTime).thenReturn(whenAgo(10))
 
         val avalancheSuppressor = AvalancheSuppressor(
-            avalancheProvider, systemClock, systemSettings, packageManager,
+            avalancheProvider, systemClock, settingsInteractor, packageManager,
             uiEventLogger, context, notificationManager
         )
         avalancheSuppressor.hasSeenEdu = false
@@ -125,7 +129,7 @@
         whenever(avalancheProvider.startTime).thenReturn(whenAgo(10))
 
         val avalancheSuppressor = AvalancheSuppressor(
-            avalancheProvider, systemClock, systemSettings, packageManager,
+            avalancheProvider, systemClock, settingsInteractor, packageManager,
             uiEventLogger, context, notificationManager
         )
         avalancheSuppressor.hasSeenEdu = true
@@ -147,7 +151,7 @@
         avalancheProvider.startTime = whenAgo(10)
 
         withFilter(
-            AvalancheSuppressor(avalancheProvider, systemClock, systemSettings, packageManager,
+            AvalancheSuppressor(avalancheProvider, systemClock, settingsInteractor, packageManager,
                     uiEventLogger, context, notificationManager)
         ) {
             ensurePeekState()
@@ -167,7 +171,7 @@
         avalancheProvider.startTime = whenAgo(10)
 
         withFilter(
-            AvalancheSuppressor(avalancheProvider, systemClock, systemSettings, packageManager,
+            AvalancheSuppressor(avalancheProvider, systemClock, settingsInteractor, packageManager,
                     uiEventLogger, context, notificationManager)
         ) {
             ensurePeekState()
@@ -187,7 +191,7 @@
         avalancheProvider.startTime = whenAgo(10)
 
         withFilter(
-            AvalancheSuppressor(avalancheProvider, systemClock, systemSettings, packageManager,
+            AvalancheSuppressor(avalancheProvider, systemClock, settingsInteractor, packageManager,
                     uiEventLogger, context, notificationManager)
         ) {
             ensurePeekState()
@@ -205,7 +209,7 @@
         avalancheProvider.startTime = whenAgo(10)
 
         withFilter(
-            AvalancheSuppressor(avalancheProvider, systemClock, systemSettings, packageManager,
+            AvalancheSuppressor(avalancheProvider, systemClock, settingsInteractor, packageManager,
                     uiEventLogger, context, notificationManager)
         ) {
             ensurePeekState()
@@ -223,7 +227,7 @@
         avalancheProvider.startTime = whenAgo(10)
 
         withFilter(
-            AvalancheSuppressor(avalancheProvider, systemClock, systemSettings, packageManager,
+            AvalancheSuppressor(avalancheProvider, systemClock, settingsInteractor, packageManager,
                     uiEventLogger, context, notificationManager)
         ) {
             ensurePeekState()
@@ -241,7 +245,7 @@
         avalancheProvider.startTime = whenAgo(10)
 
         withFilter(
-            AvalancheSuppressor(avalancheProvider, systemClock, systemSettings, packageManager,
+            AvalancheSuppressor(avalancheProvider, systemClock, settingsInteractor, packageManager,
                     uiEventLogger, context, notificationManager)
         ) {
             ensurePeekState()
@@ -255,11 +259,66 @@
     }
 
     @Test
+    fun testAvalancheFilter_duringAvalanche_allowCategoryAlarm() {
+        avalancheProvider.startTime = whenAgo(10)
+
+        withFilter(
+            AvalancheSuppressor(avalancheProvider, systemClock, settingsInteractor, packageManager,
+                uiEventLogger, context, notificationManager)
+        ) {
+            ensurePeekState()
+            assertShouldHeadsUp(
+                buildEntry {
+                    importance = NotificationManager.IMPORTANCE_HIGH
+                    category = CATEGORY_ALARM
+                }
+            )
+        }
+    }
+
+    @Test
+    fun testAvalancheFilter_duringAvalanche_allowCategoryCarEmergency() {
+        avalancheProvider.startTime = whenAgo(10)
+
+        withFilter(
+            AvalancheSuppressor(avalancheProvider, systemClock, settingsInteractor, packageManager,
+                uiEventLogger, context, notificationManager)
+        ) {
+            ensurePeekState()
+            assertShouldHeadsUp(
+                buildEntry {
+                    importance = NotificationManager.IMPORTANCE_HIGH
+                    category = CATEGORY_CAR_EMERGENCY
+
+                }
+            )
+        }
+    }
+
+    @Test
+    fun testAvalancheFilter_duringAvalanche_allowCategoryCarWarning() {
+        avalancheProvider.startTime = whenAgo(10)
+
+        withFilter(
+            AvalancheSuppressor(avalancheProvider, systemClock, settingsInteractor, packageManager,
+                uiEventLogger, context, notificationManager)
+        ) {
+            ensurePeekState()
+            assertShouldHeadsUp(
+                buildEntry {
+                    importance = NotificationManager.IMPORTANCE_HIGH
+                    category = CATEGORY_CAR_WARNING
+                }
+            )
+        }
+    }
+
+    @Test
     fun testAvalancheFilter_duringAvalanche_allowFsi() {
         avalancheProvider.startTime = whenAgo(10)
 
         withFilter(
-            AvalancheSuppressor(avalancheProvider, systemClock, systemSettings, packageManager,
+            AvalancheSuppressor(avalancheProvider, systemClock, settingsInteractor, packageManager,
                     uiEventLogger, context, notificationManager)
         ) {
             assertFsiNotSuppressed()
@@ -271,7 +330,7 @@
         avalancheProvider.startTime = whenAgo(10)
 
         withFilter(
-            AvalancheSuppressor(avalancheProvider, systemClock, systemSettings, packageManager,
+            AvalancheSuppressor(avalancheProvider, systemClock, settingsInteractor, packageManager,
                     uiEventLogger, context, notificationManager)
         ) {
             ensurePeekState()
@@ -300,7 +359,7 @@
         setAllowedEmergencyPkg(true)
 
         withFilter(
-            AvalancheSuppressor(avalancheProvider, systemClock, systemSettings, packageManager,
+            AvalancheSuppressor(avalancheProvider, systemClock, settingsInteractor, packageManager,
                     uiEventLogger, context, notificationManager)
         ) {
             ensurePeekState()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestBase.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestBase.kt
index d5ab62b..9d3d9c1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestBase.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestBase.kt
@@ -61,6 +61,7 @@
 import com.android.systemui.log.core.LogLevel
 import com.android.systemui.res.R
 import com.android.systemui.settings.FakeUserTracker
+import com.android.systemui.shared.notifications.domain.interactor.NotificationSettingsInteractor
 import com.android.systemui.statusbar.FakeStatusBarStateController
 import com.android.systemui.statusbar.NotificationEntryHelper.modifyRanking
 import com.android.systemui.statusbar.StatusBarState.KEYGUARD
@@ -88,6 +89,7 @@
 import com.android.wm.shell.bubbles.Bubbles
 import junit.framework.Assert.assertFalse
 import junit.framework.Assert.assertTrue
+import kotlinx.coroutines.flow.MutableStateFlow
 import org.junit.Assert.assertEquals
 import org.junit.Before
 import org.junit.Test
@@ -134,6 +136,7 @@
     protected val avalancheProvider: AvalancheProvider = mock()
     protected val bubbles: Bubbles = mock()
     lateinit var systemSettings: SystemSettings
+    protected val settingsInteractor: NotificationSettingsInteractor = mock()
     protected val packageManager: PackageManager = mock()
     protected val notificationManager: NotificationManager = mock()
     protected abstract val provider: VisualInterruptionDecisionProvider
@@ -164,7 +167,7 @@
         userTracker.set(listOf(user), /* currentUserIndex = */ 0)
         systemSettings = FakeSettings()
         whenever(bubbles.canShowBubbleNotification()).thenReturn(true)
-
+        whenever(settingsInteractor.isCooldownEnabled).thenReturn(MutableStateFlow(true))
         provider.start()
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestUtil.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestUtil.kt
index f4cebd7..7fd9c9f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestUtil.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestUtil.kt
@@ -25,6 +25,7 @@
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.settings.UserTracker
+import com.android.systemui.shared.notifications.domain.interactor.NotificationSettingsInteractor
 import com.android.systemui.statusbar.notification.NotifPipelineFlags
 import com.android.systemui.statusbar.policy.BatteryController
 import com.android.systemui.statusbar.policy.DeviceProvisionedController
@@ -61,7 +62,8 @@
         packageManager: PackageManager,
         bubbles: Optional<Bubbles>,
         context: Context,
-        notificationManager: NotificationManager
+        notificationManager: NotificationManager,
+        settingsInteractor: NotificationSettingsInteractor
     ): VisualInterruptionDecisionProvider {
         return if (VisualInterruptionRefactor.isEnabled) {
             VisualInterruptionDecisionProviderImpl(
@@ -85,7 +87,8 @@
                 packageManager,
                 bubbles,
                 context,
-                notificationManager
+                notificationManager,
+                settingsInteractor
             )
         } else {
             NotificationInterruptStateProviderWrapper(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt
index e738b61..c005743 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt
@@ -25,6 +25,7 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.internal.logging.MetricsLogger
+import com.android.internal.logging.UiEventLogger
 import com.android.internal.statusbar.IStatusBarService
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.flags.FeatureFlags
@@ -108,6 +109,7 @@
     private val dragController: ExpandableNotificationRowDragController = mock()
     private val dismissibilityProvider: NotificationDismissibilityProvider = mock()
     private val statusBarService: IStatusBarService = mock()
+    private val uiEventLogger: UiEventLogger = mock()
     private lateinit var controller: ExpandableNotificationRowController
 
     @Before
@@ -147,7 +149,8 @@
                 settingsController,
                 dragController,
                 dismissibilityProvider,
-                statusBarService
+                statusBarService,
+                uiEventLogger
             )
         whenever(view.childrenContainer).thenReturn(childrenContainer)
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.kt
index 2bb610a..699e8c3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.kt
@@ -642,7 +642,7 @@
 
         return spy(NotificationContentView(mContext, /* attrs= */ null))
             .apply {
-                initialize(mPeopleNotificationIdentifier, mock(), mock(), mock(), mock())
+                initialize(mPeopleNotificationIdentifier, mock(), mock(), mock(), mock(), mock())
                 setContainingNotification(row)
                 setHeights(
                     /* smallHeight= */ contractedHeight,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
index c74a04f..d7fdce2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
@@ -54,6 +54,7 @@
 import androidx.annotation.NonNull;
 
 import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.UiEventLogger;
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.keyguard.TestScopeProvider;
 import com.android.systemui.TestableDependency;
@@ -671,7 +672,8 @@
                 mock(SmartReplyConstants.class),
                 mock(SmartReplyController.class),
                 mFeatureFlags,
-                mock(IStatusBarService.class));
+                mock(IStatusBarService.class),
+                mock(UiEventLogger.class));
 
         row.setAboveShelfChangedListener(aboveShelf -> { });
         mBindStage.getStageParams(entry).requireContentViews(extraInflationFlags);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
index 1060b62..3df4a67 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
@@ -20,6 +20,7 @@
 import static com.android.systemui.log.LogBufferHelperKt.logcatLogBuffer;
 import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
 import static com.android.systemui.statusbar.StatusBarState.SHADE;
+import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_ALL;
 import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_ALL;
 
 import static kotlinx.coroutines.flow.FlowKt.emptyFlow;
@@ -91,15 +92,17 @@
 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.data.repository.ActiveNotificationListRepository;
 import com.android.systemui.statusbar.notification.domain.interactor.SeenNotificationsInteractor;
 import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor;
 import com.android.systemui.statusbar.notification.init.NotificationsController;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
+import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
+import com.android.systemui.statusbar.notification.shared.GroupHunAnimationFix;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController.NotificationPanelEvent;
 import com.android.systemui.statusbar.notification.stack.NotificationSwipeHelper.NotificationCallback;
 import com.android.systemui.statusbar.notification.stack.ui.viewbinder.NotificationListViewBinder;
+import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
 import com.android.systemui.statusbar.phone.HeadsUpTouchHelper;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -188,15 +191,13 @@
     @Captor
     private ArgumentCaptor<StatusBarStateController.StateListener> mStateListenerArgumentCaptor;
 
-
-    private final ActiveNotificationListRepository mActiveNotificationsRepository =
-            new ActiveNotificationListRepository();
-
     private final SeenNotificationsInteractor mSeenNotificationsInteractor =
-            new SeenNotificationsInteractor(mActiveNotificationsRepository);
+            mKosmos.getSeenNotificationsInteractor();
 
     private NotificationStackScrollLayoutController mController;
 
+    private NotificationTestHelper mNotificationTestHelper;
+
     @Before
     public void setUp() {
         allowTestableLooperAsMainThread();
@@ -204,6 +205,11 @@
 
         when(mNotificationSwipeHelperBuilder.build()).thenReturn(mNotificationSwipeHelper);
         when(mKeyguardTransitionRepo.getTransitions()).thenReturn(emptyFlow());
+        mNotificationTestHelper = new NotificationTestHelper(
+                mContext,
+                mDependency,
+                TestableLooper.get(this));
+        mNotificationTestHelper.setDefaultInflationFlags(FLAG_CONTENT_VIEW_ALL);
     }
 
     @Test
@@ -227,6 +233,42 @@
     }
 
     @Test
+    @EnableFlags(GroupHunAnimationFix.FLAG_NAME)
+    public void changeHeadsUpAnimatingAwayToTrue_onEntryAnimatingAwayEndedNotCalled()
+            throws Exception {
+        // Before: bind an ExpandableNotificationRow,
+        initController(/* viewIsAttached= */ true);
+        mController.setHeadsUpAppearanceController(mock(HeadsUpAppearanceController.class));
+        NotificationListContainer listContainer = mController.getNotificationListContainer();
+        ExpandableNotificationRow row = mNotificationTestHelper.createRow();
+        listContainer.bindRow(row);
+
+        // When: call setHeadsUpAnimatingAway to change set mHeadsupDisappearRunning to true
+        row.setHeadsUpAnimatingAway(true);
+
+        // Then: mHeadsUpManager.onEntryAnimatingAwayEnded is not called
+        verify(mHeadsUpManager, never()).onEntryAnimatingAwayEnded(row.getEntry());
+    }
+
+    @Test
+    @EnableFlags(GroupHunAnimationFix.FLAG_NAME)
+    public void changeHeadsUpAnimatingAwayToFalse_onEntryAnimatingAwayEndedCalled()
+            throws Exception {
+        // Before: bind an ExpandableNotificationRow, set its mHeadsupDisappearRunning to true
+        initController(/* viewIsAttached= */ true);
+        mController.setHeadsUpAppearanceController(mock(HeadsUpAppearanceController.class));
+        NotificationListContainer listContainer = mController.getNotificationListContainer();
+        ExpandableNotificationRow row = mNotificationTestHelper.createRow();
+        listContainer.bindRow(row);
+        row.setHeadsUpAnimatingAway(true);
+
+        // When: call setHeadsUpAnimatingAway to change set mHeadsupDisappearRunning to false
+        row.setHeadsUpAnimatingAway(false);
+
+        // Then: mHeadsUpManager.onEntryAnimatingAwayEnded is called
+        verify(mHeadsUpManager).onEntryAnimatingAwayEnded(row.getEntry());
+    }
+    @Test
     public void testOnDensityOrFontScaleChanged_reInflatesFooterViews() {
         initController(/* viewIsAttached= */ true);
 
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 b799595..22b9887 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
@@ -77,7 +77,10 @@
 import com.android.systemui.flags.FakeFeatureFlags;
 import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.flags.Flags;
+import com.android.systemui.qs.flags.NewQsUI;
+import com.android.systemui.qs.flags.QSComposeFragment;
 import com.android.systemui.res.R;
+import com.android.systemui.shade.QSHeaderBoundsProvider;
 import com.android.systemui.shade.ShadeController;
 import com.android.systemui.shade.transition.LargeScreenShadeInterpolator;
 import com.android.systemui.statusbar.EmptyShadeView;
@@ -99,6 +102,8 @@
 import com.android.systemui.statusbar.policy.AvalancheController;
 import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController;
 
+import kotlin.Unit;
+
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Rule;
@@ -807,6 +812,7 @@
     }
 
     @Test
+    @DisableFlags({QSComposeFragment.FLAG_NAME, NewQsUI.FLAG_NAME})
     @DisableSceneContainer // TODO(b/312473478): address lack of QS Header
     public void testInsideQSHeader_noOffset() {
         ViewGroup qsHeader = mock(ViewGroup.class);
@@ -824,6 +830,7 @@
     }
 
     @Test
+    @DisableFlags({QSComposeFragment.FLAG_NAME, NewQsUI.FLAG_NAME})
     @DisableSceneContainer // TODO(b/312473478): address lack of QS Header
     public void testInsideQSHeader_Offset() {
         ViewGroup qsHeader = mock(ViewGroup.class);
@@ -844,6 +851,63 @@
     }
 
     @Test
+    @EnableFlags({QSComposeFragment.FLAG_NAME, NewQsUI.FLAG_NAME})
+    @DisableSceneContainer // TODO(b/312473478): address lack of QS Header
+    public void testInsideQSHeader_noOffset_qsCompose() {
+        ViewGroup qsHeader = mock(ViewGroup.class);
+        Rect boundsOnScreen = new Rect(0, 0, 1000, 1000);
+        mockBoundsOnScreen(qsHeader, boundsOnScreen);
+
+        QSHeaderBoundsProvider provider = new QSHeaderBoundsProvider(
+                () -> 0,
+                boundsOnScreen::height,
+                rect -> {
+                    qsHeader.getBoundsOnScreen(rect);
+                    return Unit.INSTANCE;
+                }
+        );
+
+        mStackScroller.setQsHeaderBoundsProvider(provider);
+        mStackScroller.setLeftTopRightBottom(0, 0, 2000, 2000);
+
+        MotionEvent event1 = transformEventForView(createMotionEvent(100f, 100f), mStackScroller);
+        assertTrue(mStackScroller.isInsideQsHeader(event1));
+
+        MotionEvent event2 = transformEventForView(createMotionEvent(1100f, 100f), mStackScroller);
+        assertFalse(mStackScroller.isInsideQsHeader(event2));
+    }
+
+    @Test
+    @EnableFlags({QSComposeFragment.FLAG_NAME, NewQsUI.FLAG_NAME})
+    @DisableSceneContainer // TODO(b/312473478): address lack of QS Header
+    public void testInsideQSHeader_Offset_qsCompose() {
+        ViewGroup qsHeader = mock(ViewGroup.class);
+        Rect boundsOnScreen = new Rect(100, 100, 1000, 1000);
+        mockBoundsOnScreen(qsHeader, boundsOnScreen);
+
+        QSHeaderBoundsProvider provider = new QSHeaderBoundsProvider(
+                () -> 0,
+                boundsOnScreen::height,
+                rect -> {
+                    qsHeader.getBoundsOnScreen(rect);
+                    return Unit.INSTANCE;
+                }
+        );
+
+        mStackScroller.setQsHeaderBoundsProvider(provider);
+        mStackScroller.setLeftTopRightBottom(200, 200, 2000, 2000);
+
+        MotionEvent event1 = transformEventForView(createMotionEvent(50f, 50f), mStackScroller);
+        assertFalse(mStackScroller.isInsideQsHeader(event1));
+
+        MotionEvent event2 = transformEventForView(createMotionEvent(150f, 150f), mStackScroller);
+        assertFalse(mStackScroller.isInsideQsHeader(event2));
+
+        MotionEvent event3 = transformEventForView(createMotionEvent(250f, 250f), mStackScroller);
+        assertTrue(mStackScroller.isInsideQsHeader(event3));
+    }
+
+    @Test
     @DisableSceneContainer // TODO(b/312473478): address disabled test
     public void setFractionToShade_recomputesStackHeight() {
         mStackScroller.setFractionToShade(1f);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
index b7ebebe..ad029d7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
@@ -92,9 +92,12 @@
     private fun px(@DimenRes id: Int): Float =
         testableResources.resources.getDimensionPixelSize(id).toFloat()
 
-    private val bigGap = px(R.dimen.notification_section_divider_height)
-    private val smallGap = px(R.dimen.notification_section_divider_height_lockscreen)
+    private val notifSectionDividerGap = px(R.dimen.notification_section_divider_height)
     private val scrimPadding = px(R.dimen.notification_side_paddings)
+    private val baseZ by lazy { ambientState.baseZHeight }
+    private val headsUpZ = px(R.dimen.heads_up_pinned_elevation)
+    private val bigGap = notifSectionDividerGap
+    private val smallGap = px(R.dimen.notification_section_divider_height_lockscreen)
 
     @Before
     fun setUp() {
@@ -219,6 +222,8 @@
 
         // Then: HUN is at the headsUpTop
         assertThat(notificationRow.viewState.yTranslation).isEqualTo(headsUpTop)
+        // And: HUN is not elevated
+        assertThat(notificationRow.viewState.zTranslation).isEqualTo(baseZ)
         // And: HUN has its full height
         assertThat(notificationRow.viewState.height).isEqualTo(intrinsicHeight)
     }
@@ -243,6 +248,8 @@
 
         // Then: HUN is translated to the headsUpTop
         assertThat(notificationRow.viewState.yTranslation).isEqualTo(headsUpTop)
+        // And: HUN is not elevated
+        assertThat(notificationRow.viewState.zTranslation).isEqualTo(baseZ)
         // And: HUN is clipped to the available space
         // newTranslation = max(150, -25)
         // distToReal = 150 - (-25)
@@ -270,6 +277,8 @@
 
         // Then: HUN is translated to the headsUpTop
         assertThat(notificationRow.viewState.yTranslation).isEqualTo(headsUpTop)
+        // And: HUN fully elevated to baseZ + headsUpZ
+        assertThat(notificationRow.viewState.zTranslation).isEqualTo(baseZ + headsUpZ)
         // And: HUN is clipped to its collapsed height
         assertThat(notificationRow.viewState.height).isEqualTo(collapsedHeight)
     }
@@ -279,16 +288,100 @@
     fun resetViewStates_defaultHun_showingQS_hunTranslatedToHeadsUpTop() {
         // Given: the shade is open and scrolled to the bottom to show the QuickSettings
         val headsUpTop = 2000f
+        val intrinsicHunHeight = 300
         fakeHunInShade(
             headsUpTop = headsUpTop,
             stackTop = 2600f, // stack scrolled below the screen
             stackCutoff = 4000f,
             collapsedHeight = 100,
-            intrinsicHeight = 300
+            intrinsicHeight = intrinsicHunHeight,
         )
         whenever(notificationRow.isAboveShelf).thenReturn(true)
 
-        resetViewStates_hunYTranslationIs(headsUpTop)
+        // When
+        stackScrollAlgorithm.resetViewStates(ambientState, 0)
+
+        // Then: HUN is translated to the headsUpTop
+        assertThat(notificationRow.viewState.yTranslation).isEqualTo(headsUpTop)
+        // And: HUN is elevated to baseZ + headsUpZ
+        assertThat(notificationRow.viewState.zTranslation).isEqualTo(baseZ + headsUpZ)
+        // And: HUN maintained its full height
+        assertThat(notificationRow.viewState.height).isEqualTo(intrinsicHunHeight)
+    }
+
+    @Test
+    @EnableSceneContainer
+    fun updateZTranslationForHunInStack_fullOverlap_hunHasFullElevation() {
+        // Given: the overlap equals to the top content padding
+        val contentTop = 280f
+        val contentTopPadding = 20f
+        val viewState =
+            ExpandableViewState().apply {
+                height = 100
+                yTranslation = 200f
+            }
+
+        // When
+        stackScrollAlgorithm.updateZTranslationForHunInStack(
+            /* scrollingContentTop = */ contentTop,
+            /* scrollingContentTopPadding */ contentTopPadding,
+            /* baseZ = */ 0f,
+            /* viewState = */ viewState,
+        )
+
+        // Then: HUN is fully elevated to baseZ + headsUpZ
+        assertThat(viewState.zTranslation).isEqualTo(headsUpZ)
+    }
+
+    @Test
+    @EnableSceneContainer
+    fun updateZTranslationForHunInStack_someOverlap_hunIsPartlyElevated() {
+        // Given: the overlap is bigger than zero, but less than the top content padding
+        val contentTop = 290f
+        val contentTopPadding = 20f
+        val viewState =
+            ExpandableViewState().apply {
+                height = 100
+                yTranslation = 200f
+            }
+
+        // When
+        stackScrollAlgorithm.updateZTranslationForHunInStack(
+            /* scrollingContentTop = */ contentTop,
+            /* scrollingContentTopPadding */ contentTopPadding,
+            /* baseZ = */ 0f,
+            /* viewState = */ viewState,
+        )
+
+        // Then: HUN is partly elevated
+        assertThat(viewState.zTranslation).apply {
+            isGreaterThan(0f)
+            isLessThan(headsUpZ)
+        }
+    }
+
+    @Test
+    @EnableSceneContainer
+    fun updateZTranslationForHunInStack_noOverlap_hunIsNotElevated() {
+        // Given: no overlap between the content and the HUN
+        val contentTop = 300f
+        val contentTopPadding = 20f
+        val viewState =
+            ExpandableViewState().apply {
+                height = 100
+                yTranslation = 200f
+            }
+
+        // When
+        stackScrollAlgorithm.updateZTranslationForHunInStack(
+            /* scrollingContentTop = */ contentTop,
+            /* scrollingContentTopPadding */ contentTopPadding,
+            /* baseZ = */ 0f,
+            /* viewState = */ viewState,
+        )
+
+        // Then: HUN is not elevated
+        assertThat(viewState.zTranslation).isEqualTo(0f)
     }
 
     @Test
@@ -964,6 +1057,7 @@
     }
 
     @Test
+    @DisableSceneContainer
     fun shadeOpened_hunFullyOverlapsQqsPanel_hunShouldHaveFullShadow() {
         // Given: shade is opened, yTranslation of HUN is 0,
         // the height of HUN equals to the height of QQS Panel,
@@ -989,6 +1083,7 @@
     }
 
     @Test
+    @DisableSceneContainer
     fun shadeOpened_hunPartiallyOverlapsQQS_hunShouldHavePartialShadow() {
         // Given: shade is opened, yTranslation of HUN is greater than 0,
         // the height of HUN is equal to the height of QQS Panel,
@@ -1419,14 +1514,16 @@
 
     /** fakes the notification row under test, to be a HUN in a fully opened shade */
     private fun fakeHunInShade(
-        headsUpTop: Float,
         collapsedHeight: Int,
         intrinsicHeight: Int,
+        headsUpTop: Float,
+        headsUpBottom: Float = headsUpTop + intrinsicHeight, // assume all the space available
         stackTop: Float,
         stackCutoff: Float = 2000f,
         fullStackHeight: Float = 3000f
     ) {
         ambientState.headsUpTop = headsUpTop
+        ambientState.headsUpBottom = headsUpBottom
         ambientState.stackTop = stackTop
         ambientState.stackCutoff = stackCutoff
 
@@ -1438,6 +1535,7 @@
         }
         stackScrollAlgorithm.setIsExpanded(true)
 
+        whenever(notificationRow.headerVisibleAmount).thenReturn(1.0f)
         whenever(notificationRow.mustStayOnScreen()).thenReturn(true)
         whenever(notificationRow.isHeadsUp).thenReturn(true)
         whenever(notificationRow.collapsedHeight).thenReturn(collapsedHeight)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/HideNotificationsInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/HideNotificationsInteractorTest.kt
index e46906f..4762527 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/HideNotificationsInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/HideNotificationsInteractorTest.kt
@@ -66,7 +66,8 @@
             repository = powerRepository,
             falsingCollector = mock(),
             screenOffAnimationController = mock(),
-            statusBarStateController = mock()
+            statusBarStateController = mock(),
+            cameraGestureHelper = mock(),
         )
 
     private val configurationRepository =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
index 5e5586d..d9e9495 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
@@ -42,6 +42,7 @@
 import com.android.systemui.assist.AssistManager;
 import com.android.systemui.emergency.EmergencyGestureModule.EmergencyGestureIntentFactory;
 import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.qs.QSHost;
 import com.android.systemui.recents.ScreenPinningRequest;
@@ -103,6 +104,7 @@
     @Mock private QSHost mQSHost;
     @Mock private ActivityStarter mActivityStarter;
     @Mock private EmergencyGestureIntentFactory mEmergencyGestureIntentFactory;
+    @Mock private KeyguardInteractor mKeyguardInteractor;
 
     CentralSurfacesCommandQueueCallbacks mSbcqCallbacks;
 
@@ -140,6 +142,7 @@
                 mUserTracker,
                 mQSHost,
                 mActivityStarter,
+                mKeyguardInteractor,
                 mEmergencyGestureIntentFactory);
 
         when(mUserTracker.getUserHandle()).thenReturn(
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 d2540a6..bd9cccd 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
@@ -150,6 +150,7 @@
 import com.android.systemui.shade.ShadeControllerImpl;
 import com.android.systemui.shade.ShadeExpansionStateManager;
 import com.android.systemui.shade.ShadeLogger;
+import com.android.systemui.shared.notifications.domain.interactor.NotificationSettingsInteractor;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.KeyboardShortcutListSearch;
 import com.android.systemui.statusbar.KeyboardShortcuts;
@@ -343,7 +344,7 @@
     @Mock private NotificationManager mNotificationManager;
     @Mock private GlanceableHubContainerController mGlanceableHubContainerController;
     @Mock private EmergencyGestureIntentFactory mEmergencyGestureIntentFactory;
-
+    @Mock private NotificationSettingsInteractor mNotificationSettingsInteractor;
     private ShadeController mShadeController;
     private final FakeSystemClock mFakeSystemClock = new FakeSystemClock();
     private final FakeGlobalSettings mFakeGlobalSettings = new FakeGlobalSettings();
@@ -403,7 +404,8 @@
                         mPackageManager,
                         Optional.of(mBubbles),
                         mContext,
-                        mNotificationManager);
+                        mNotificationManager,
+                        mNotificationSettingsInteractor);
         mVisualInterruptionDecisionProvider.start();
 
         mContext.addMockSystemService(TrustManager.class, mock(TrustManager.class));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
index cf87afb..7f33c23 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
@@ -299,27 +299,7 @@
     }
 
     @Test
-    @DisableFlags(Flags.FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX)
-    public void notifPaddingMakesUpToFullMarginInSplitShade_refactorFlagOff_usesResource() {
-        int keyguardSplitShadeTopMargin = 100;
-        int largeScreenHeaderHeightResource = 70;
-        when(mResources.getDimensionPixelSize(R.dimen.keyguard_split_shade_top_margin))
-                .thenReturn(keyguardSplitShadeTopMargin);
-        when(mResources.getDimensionPixelSize(R.dimen.large_screen_shade_header_height))
-                .thenReturn(largeScreenHeaderHeightResource);
-        mClockPositionAlgorithm.loadDimens(mContext, mResources);
-        givenLockScreen();
-        mIsSplitShade = true;
-        // WHEN the position algorithm is run
-        positionClock();
-        // THEN the notif padding makes up lacking margin (margin - header height).
-        int expectedPadding = keyguardSplitShadeTopMargin - largeScreenHeaderHeightResource;
-        assertThat(mClockPosition.stackScrollerPadding).isEqualTo(expectedPadding);
-    }
-
-    @Test
-    @EnableFlags(Flags.FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX)
-    public void notifPaddingMakesUpToFullMarginInSplitShade_refactorFlagOn_usesHelper() {
+    public void notifPaddingMakesUpToFullMarginInSplitShade_usesHelper() {
         int keyguardSplitShadeTopMargin = 100;
         int largeScreenHeaderHeightHelper = 50;
         int largeScreenHeaderHeightResource = 70;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt
index fd2dead..e670884 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt
@@ -334,6 +334,7 @@
             /* typeVisibilityMap = */ booleanArrayOf(),
             /* isRound = */ false,
             /* forceConsumingTypes = */ 0,
+            /* forceConsumingCaptionBar = */ false,
             /* suppressScrimTypes = */ 0,
             /* displayCutout = */ DisplayCutout.NO_CUTOUT,
             /* roundedCorners = */ RoundedCorners.NO_ROUNDED_CORNERS,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index 31f93b4..af5e60e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -27,6 +27,7 @@
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyFloat;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.mock;
@@ -94,6 +95,7 @@
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
 import com.android.systemui.scene.domain.interactor.SceneInteractor;
+import com.android.systemui.scene.shared.model.Scenes;
 import com.android.systemui.shade.NotificationShadeWindowView;
 import com.android.systemui.shade.ShadeController;
 import com.android.systemui.shade.ShadeExpansionChangeEvent;
@@ -166,6 +168,7 @@
     @Mock private StatusBarKeyguardViewManager.KeyguardViewManagerCallback mCallback;
     @Mock private SelectedUserInteractor mSelectedUserInteractor;
     @Mock private DeviceEntryInteractor mDeviceEntryInteractor;
+    @Mock private SceneInteractor mSceneInteractor;
 
     private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
     private PrimaryBouncerCallbackInteractor.PrimaryBouncerExpansionCallback
@@ -233,7 +236,7 @@
                         mSelectedUserInteractor,
                         () -> mock(KeyguardSurfaceBehindInteractor.class),
                         mock(JavaAdapter.class),
-                        () -> mock(SceneInteractor.class),
+                        () -> mSceneInteractor,
                         mock(StatusBarKeyguardViewManagerInteractor.class),
                         () -> mDeviceEntryInteractor) {
                     @Override
@@ -270,21 +273,23 @@
     }
 
     @Test
-    public void showBouncer_onlyWhenShowing() {
+    public void showPrimaryBouncer_onlyWhenShowing() {
         mStatusBarKeyguardViewManager.hide(0 /* startTime */, 0 /* fadeoutDuration */);
         mStatusBarKeyguardViewManager.showPrimaryBouncer(true /* scrimmed */);
         verify(mPrimaryBouncerInteractor, never()).show(anyBoolean());
         verify(mDeviceEntryInteractor, never()).attemptDeviceEntry();
+        verify(mSceneInteractor, never()).changeScene(any(), any());
     }
 
     @Test
-    public void showBouncer_notWhenBouncerAlreadyShowing() {
+    public void showPrimaryBouncer_notWhenBouncerAlreadyShowing() {
         mStatusBarKeyguardViewManager.hide(0 /* startTime */, 0 /* fadeoutDuration */);
         when(mKeyguardSecurityModel.getSecurityMode(anyInt())).thenReturn(
                 KeyguardSecurityModel.SecurityMode.Password);
         mStatusBarKeyguardViewManager.showPrimaryBouncer(true /* scrimmed */);
         verify(mPrimaryBouncerInteractor, never()).show(anyBoolean());
         verify(mDeviceEntryInteractor, never()).attemptDeviceEntry();
+        verify(mSceneInteractor, never()).changeScene(any(), any());
     }
 
     @Test
@@ -753,7 +758,7 @@
                         mSelectedUserInteractor,
                         () -> mock(KeyguardSurfaceBehindInteractor.class),
                         mock(JavaAdapter.class),
-                        () -> mock(SceneInteractor.class),
+                        () -> mSceneInteractor,
                         mock(StatusBarKeyguardViewManagerInteractor.class),
                         () -> mDeviceEntryInteractor) {
                     @Override
@@ -1104,9 +1109,9 @@
 
     @Test
     @EnableSceneContainer
-    public void showPrimaryBouncer_attemptDeviceEntry() {
+    public void showPrimaryBouncer() {
         mStatusBarKeyguardViewManager.showPrimaryBouncer(false);
-        verify(mDeviceEntryInteractor).attemptDeviceEntry();
+        verify(mSceneInteractor).changeScene(eq(Scenes.Bouncer), anyString());
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java
index 80b9e80..c523819 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java
@@ -27,6 +27,7 @@
 import static org.mockito.internal.verification.VerificationModeFactory.times;
 
 import android.content.Intent;
+import android.platform.test.annotations.DisableFlags;
 import android.platform.test.annotations.EnableFlags;
 import android.testing.TestableLooper;
 import android.view.View;
@@ -111,8 +112,8 @@
 
         verify(mStatusBarKeyguardViewManager).showBouncer(true);
     }
-
     @Test
+    @DisableFlags(ExpandHeadsUpOnInlineReply.FLAG_NAME)
     public void onMakeExpandedVisibleForRemoteInput_collapsedGroup_expandGroupExpansion() {
         // GIVEN
         final Runnable onExpandedVisibleRunner = mock(Runnable.class);
@@ -137,6 +138,7 @@
     }
 
     @Test
+    @DisableFlags(ExpandHeadsUpOnInlineReply.FLAG_NAME)
     public void onMakeExpandedVisibleForRemoteInput_expandedGroup_setUserExpandedTrue() {
         // GIVEN
         final Runnable onExpandedVisibleRunner = mock(Runnable.class);
@@ -161,6 +163,7 @@
     }
 
     @Test
+    @DisableFlags(ExpandHeadsUpOnInlineReply.FLAG_NAME)
     public void onMakeExpandedVisibleForRemoteInput_nonGroupNotifications_setUserExpandedTrue() {
         // GIVEN
         final Runnable onExpandedVisibleRunner = mock(Runnable.class);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerViaListenerTest.kt
similarity index 95%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerViaListenerTest.kt
index 5174ec7..597e2e4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerViaListenerTest.kt
@@ -31,10 +31,13 @@
 import android.widget.LinearLayout
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.internal.logging.testing.UiEventLoggerFake
 import com.android.systemui.Flags.FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS
+import com.android.systemui.Flags.FLAG_STATUS_BAR_USE_REPOS_FOR_CALL_CHIP
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.dump.DumpManager
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.log.logcatLogBuffer
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.data.repository.FakeStatusBarModeRepository
@@ -43,7 +46,8 @@
 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
 import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection
 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
-import com.android.systemui.statusbar.phone.ongoingcall.data.repository.OngoingCallRepository
+import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor
+import com.android.systemui.statusbar.phone.ongoingcall.data.repository.ongoingCallRepository
 import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel
 import com.android.systemui.statusbar.window.StatusBarWindowController
 import com.android.systemui.util.concurrency.FakeExecutor
@@ -83,14 +87,15 @@
 @RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper
 @OptIn(ExperimentalCoroutinesApi::class)
-class OngoingCallControllerTest : SysuiTestCase() {
+@DisableFlags(FLAG_STATUS_BAR_USE_REPOS_FOR_CALL_CHIP)
+class OngoingCallControllerViaListenerTest : SysuiTestCase() {
+    private val kosmos = Kosmos()
 
     private val clock = FakeSystemClock()
     private val mainExecutor = FakeExecutor(clock)
-    private val uiEventLoggerFake = UiEventLoggerFake()
     private val testScope = TestScope()
     private val statusBarModeRepository = FakeStatusBarModeRepository()
-    private val ongoingCallRepository = OngoingCallRepository()
+    private val ongoingCallRepository = kosmos.ongoingCallRepository
 
     private lateinit var controller: OngoingCallController
     private lateinit var notifCollectionListener: NotifCollectionListener
@@ -120,15 +125,16 @@
                 context,
                 ongoingCallRepository,
                 notificationCollection,
+                kosmos.activeNotificationsInteractor,
                 clock,
                 mockActivityStarter,
                 mainExecutor,
                 mockIActivityManager,
-                OngoingCallLogger(uiEventLoggerFake),
                 DumpManager(),
                 mockStatusBarWindowController,
                 mockSwipeStatusBarAwayGestureHandler,
                 statusBarModeRepository,
+                logcatLogBuffer("OngoingCallControllerViaListenerTest"),
             )
         controller.start()
         controller.addCallback(mockOngoingCallListener)
@@ -544,18 +550,6 @@
         verify(mockOngoingCallListener).onOngoingCallStateChanged(anyBoolean())
     }
 
-    @Test
-    @DisableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
-    fun chipClicked_clickEventLogged() {
-        notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
-
-        chipView.performClick()
-
-        assertThat(uiEventLoggerFake.numLogs()).isEqualTo(1)
-        assertThat(uiEventLoggerFake.eventId(0))
-            .isEqualTo(OngoingCallLogger.OngoingCallEvents.ONGOING_CALL_CLICKED.id)
-    }
-
     /** Regression test for b/212467440. */
     @Test
     @DisableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
@@ -571,18 +565,6 @@
     }
 
     @Test
-    fun notifyChipVisibilityChanged_visibleEventLogged() {
-        controller.notifyChipVisibilityChanged(true)
-
-        assertThat(uiEventLoggerFake.numLogs()).isEqualTo(1)
-        assertThat(uiEventLoggerFake.eventId(0))
-            .isEqualTo(OngoingCallLogger.OngoingCallEvents.ONGOING_CALL_VISIBLE.id)
-    }
-
-    // Other tests for notifyChipVisibilityChanged are in [OngoingCallLogger], since
-    // [OngoingCallController.notifyChipVisibilityChanged] just delegates to that class.
-
-    @Test
     @DisableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
     fun callNotificationAdded_chipIsClickable() {
         notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerViaRepoTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerViaRepoTest.kt
new file mode 100644
index 0000000..dfe01bf
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerViaRepoTest.kt
@@ -0,0 +1,711 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone.ongoingcall
+
+import android.app.ActivityManager
+import android.app.IActivityManager
+import android.app.IUidObserver
+import android.app.PendingIntent
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import android.testing.TestableLooper
+import android.view.LayoutInflater
+import android.view.View
+import android.widget.LinearLayout
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.Flags.FLAG_STATUS_BAR_CALL_CHIP_NOTIFICATION_ICON
+import com.android.systemui.Flags.FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS
+import com.android.systemui.Flags.FLAG_STATUS_BAR_USE_REPOS_FOR_CALL_CHIP
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.concurrency.fakeExecutor
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.log.logcatLogBuffer
+import com.android.systemui.plugins.activityStarter
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.StatusBarIconView
+import com.android.systemui.statusbar.data.repository.fakeStatusBarModeRepository
+import com.android.systemui.statusbar.gesture.SwipeStatusBarAwayGestureHandler
+import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection
+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.domain.interactor.activeNotificationsInteractor
+import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel
+import com.android.systemui.statusbar.notification.shared.CallType
+import com.android.systemui.statusbar.phone.ongoingcall.data.repository.ongoingCallRepository
+import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel
+import com.android.systemui.statusbar.window.StatusBarWindowController
+import com.android.systemui.util.time.fakeSystemClock
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.never
+import org.mockito.kotlin.reset
+import org.mockito.kotlin.times
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@TestableLooper.RunWithLooper
+@OptIn(ExperimentalCoroutinesApi::class)
+@EnableFlags(FLAG_STATUS_BAR_USE_REPOS_FOR_CALL_CHIP)
+class OngoingCallControllerViaRepoTest : SysuiTestCase() {
+    private val kosmos = Kosmos()
+
+    private val clock = kosmos.fakeSystemClock
+    private val mainExecutor = kosmos.fakeExecutor
+    private val testScope = kosmos.testScope
+    private val statusBarModeRepository = kosmos.fakeStatusBarModeRepository
+    private val ongoingCallRepository = kosmos.ongoingCallRepository
+    private val activeNotificationListRepository = kosmos.activeNotificationListRepository
+
+    private lateinit var controller: OngoingCallController
+
+    private val mockSwipeStatusBarAwayGestureHandler = mock<SwipeStatusBarAwayGestureHandler>()
+    private val mockOngoingCallListener = mock<OngoingCallListener>()
+    private val mockActivityStarter = kosmos.activityStarter
+    private val mockIActivityManager = mock<IActivityManager>()
+    private val mockStatusBarWindowController = mock<StatusBarWindowController>()
+
+    private lateinit var chipView: View
+
+    @Before
+    fun setUp() {
+        allowTestableLooperAsMainThread()
+        TestableLooper.get(this).runWithLooper {
+            chipView = LayoutInflater.from(mContext).inflate(R.layout.ongoing_activity_chip, null)
+        }
+
+        controller =
+            OngoingCallController(
+                testScope.backgroundScope,
+                context,
+                ongoingCallRepository,
+                mock<CommonNotifCollection>(),
+                kosmos.activeNotificationsInteractor,
+                clock,
+                mockActivityStarter,
+                mainExecutor,
+                mockIActivityManager,
+                DumpManager(),
+                mockStatusBarWindowController,
+                mockSwipeStatusBarAwayGestureHandler,
+                statusBarModeRepository,
+                logcatLogBuffer("OngoingCallControllerViaRepoTest"),
+            )
+        controller.start()
+        controller.addCallback(mockOngoingCallListener)
+        controller.setChipView(chipView)
+
+        // Let the controller get the starting value from activeNotificationsInteractor
+        testScope.runCurrent()
+        reset(mockOngoingCallListener)
+
+        whenever(
+                mockIActivityManager.getUidProcessState(
+                    eq(CALL_UID),
+                    any(),
+                )
+            )
+            .thenReturn(PROC_STATE_INVISIBLE)
+    }
+
+    @After
+    fun tearDown() {
+        controller.tearDownChipView()
+    }
+
+    @Test
+    fun interactorHasOngoingCallNotif_listenerAndRepoNotified() =
+        testScope.runTest {
+            setNotifOnRepo(
+                activeNotificationModel(
+                    key = "ongoingNotif",
+                    callType = CallType.Ongoing,
+                    uid = CALL_UID,
+                    whenTime = 567,
+                )
+            )
+
+            verify(mockOngoingCallListener).onOngoingCallStateChanged(any())
+            val repoState = ongoingCallRepository.ongoingCallState.value
+            assertThat(repoState).isInstanceOf(OngoingCallModel.InCall::class.java)
+            assertThat((repoState as OngoingCallModel.InCall).startTimeMs).isEqualTo(567)
+        }
+
+    @Test
+    @DisableFlags(FLAG_STATUS_BAR_CALL_CHIP_NOTIFICATION_ICON)
+    fun interactorHasOngoingCallNotif_notifIconFlagOff_repoHasNoNotifIcon() =
+        testScope.runTest {
+            val icon = mock<StatusBarIconView>()
+            setNotifOnRepo(
+                activeNotificationModel(
+                    key = "ongoingNotif",
+                    callType = CallType.Ongoing,
+                    uid = CALL_UID,
+                    statusBarChipIcon = icon,
+                    whenTime = 567,
+                )
+            )
+
+            val repoState = ongoingCallRepository.ongoingCallState.value
+            assertThat(repoState).isInstanceOf(OngoingCallModel.InCall::class.java)
+            assertThat((repoState as OngoingCallModel.InCall).notificationIconView).isNull()
+        }
+
+    @Test
+    @EnableFlags(FLAG_STATUS_BAR_CALL_CHIP_NOTIFICATION_ICON)
+    fun interactorHasOngoingCallNotif_notifIconFlagOn_repoHasNotifIcon() =
+        testScope.runTest {
+            val icon = mock<StatusBarIconView>()
+
+            setNotifOnRepo(
+                activeNotificationModel(
+                    key = "ongoingNotif",
+                    callType = CallType.Ongoing,
+                    uid = CALL_UID,
+                    statusBarChipIcon = icon,
+                    whenTime = 567,
+                )
+            )
+
+            val repoState = ongoingCallRepository.ongoingCallState.value
+            assertThat(repoState).isInstanceOf(OngoingCallModel.InCall::class.java)
+            assertThat((repoState as OngoingCallModel.InCall).notificationIconView).isEqualTo(icon)
+        }
+
+    @Test
+    fun notifRepoHasOngoingCallNotif_isOngoingCallNotif_windowControllerUpdated() {
+        setCallNotifOnRepo()
+
+        verify(mockStatusBarWindowController).setOngoingProcessRequiresStatusBarVisible(true)
+    }
+
+    @Test
+    fun notifRepoHasNoCallNotif_listenerAndRepoNotNotified() {
+        setNoNotifsOnRepo()
+
+        verify(mockOngoingCallListener, never()).onOngoingCallStateChanged(any())
+        assertThat(ongoingCallRepository.ongoingCallState.value)
+            .isInstanceOf(OngoingCallModel.NoCall::class.java)
+    }
+
+    @Test
+    fun notifRepoHasOngoingCallNotifThenScreeningNotif_listenerNotifiedTwice() {
+        setNotifOnRepo(
+            activeNotificationModel(
+                key = "notif",
+                callType = CallType.Ongoing,
+            )
+        )
+
+        setNotifOnRepo(
+            activeNotificationModel(
+                key = "notif",
+                callType = CallType.Screening,
+            )
+        )
+
+        verify(mockOngoingCallListener, times(2)).onOngoingCallStateChanged(any())
+    }
+
+    @Test
+    fun notifRepoHasOngoingCallNotifThenScreeningNotif_repoUpdated() {
+        setNotifOnRepo(
+            activeNotificationModel(
+                key = "notif",
+                callType = CallType.Ongoing,
+            )
+        )
+
+        setNotifOnRepo(
+            activeNotificationModel(
+                key = "notif",
+                callType = CallType.Screening,
+            )
+        )
+
+        assertThat(ongoingCallRepository.ongoingCallState.value)
+            .isInstanceOf(OngoingCallModel.NoCall::class.java)
+    }
+
+    /** Regression test for b/191472854. */
+    @Test
+    fun notifRepoHasCallNotif_notifHasNullContentIntent_noCrash() {
+        setNotifOnRepo(
+            activeNotificationModel(
+                key = "notif",
+                callType = CallType.Ongoing,
+                contentIntent = null,
+            )
+        )
+    }
+
+    /** Regression test for b/192379214. */
+    @Test
+    @DisableFlags(android.app.Flags.FLAG_SORT_SECTION_BY_TIME, FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
+    fun notifRepoHasCallNotif_notificationWhenIsZero_timeHidden() {
+        setNotifOnRepo(
+            activeNotificationModel(
+                key = "notif",
+                callType = CallType.Ongoing,
+                contentIntent = null,
+                whenTime = 0,
+            )
+        )
+
+        chipView.measure(
+            View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
+            View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
+        )
+
+        assertThat(chipView.findViewById<View>(R.id.ongoing_activity_chip_time)?.measuredWidth)
+            .isEqualTo(0)
+    }
+
+    @Test
+    @DisableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
+    fun notifRepoHasCallNotif_notificationWhenIsValid_timeShown() {
+        setNotifOnRepo(
+            activeNotificationModel(
+                key = "notif",
+                callType = CallType.Ongoing,
+                whenTime = clock.currentTimeMillis(),
+            )
+        )
+
+        chipView.measure(
+            View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
+            View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
+        )
+
+        assertThat(chipView.findViewById<View>(R.id.ongoing_activity_chip_time)?.measuredWidth)
+            .isGreaterThan(0)
+    }
+
+    /** Regression test for b/194731244. */
+    @Test
+    fun repoHasCallNotif_updatedManyTimes_uidObserverOnlyRegisteredOnce() {
+        for (i in 0 until 4) {
+            // Re-create the notification each time so that it's considered a different object and
+            // will re-trigger the whole flow.
+            setNotifOnRepo(
+                activeNotificationModel(
+                    key = "notif$i",
+                    callType = CallType.Ongoing,
+                    whenTime = 44,
+                )
+            )
+        }
+
+        verify(mockIActivityManager, times(1)).registerUidObserver(any(), any(), any(), any())
+    }
+
+    /** Regression test for b/216248574. */
+    @Test
+    fun repoHasCallNotif_getUidProcessStateThrowsException_noCrash() {
+        whenever(
+                mockIActivityManager.getUidProcessState(
+                    eq(CALL_UID),
+                    any(),
+                )
+            )
+            .thenThrow(SecurityException())
+
+        // No assert required, just check no crash
+        setCallNotifOnRepo()
+    }
+
+    /** Regression test for b/216248574. */
+    @Test
+    fun repoHasCallNotif_registerUidObserverThrowsException_noCrash() {
+        whenever(
+                mockIActivityManager.registerUidObserver(
+                    any(),
+                    any(),
+                    any(),
+                    any(),
+                )
+            )
+            .thenThrow(SecurityException())
+
+        // No assert required, just check no crash
+        setCallNotifOnRepo()
+    }
+
+    /** Regression test for b/216248574. */
+    @Test
+    fun repoHasCallNotif_packageNameProvidedToActivityManager() {
+        setCallNotifOnRepo()
+
+        val packageNameCaptor = argumentCaptor<String>()
+        verify(mockIActivityManager)
+            .registerUidObserver(any(), any(), any(), packageNameCaptor.capture())
+        assertThat(packageNameCaptor.firstValue).isNotNull()
+    }
+
+    @Test
+    fun repo_callNotifAddedThenRemoved_listenerNotified() {
+        setCallNotifOnRepo()
+        reset(mockOngoingCallListener)
+
+        setNoNotifsOnRepo()
+
+        verify(mockOngoingCallListener).onOngoingCallStateChanged(any())
+    }
+
+    @Test
+    fun repo_callNotifAddedThenRemoved_repoUpdated() {
+        setCallNotifOnRepo()
+
+        setNoNotifsOnRepo()
+
+        assertThat(ongoingCallRepository.ongoingCallState.value)
+            .isInstanceOf(OngoingCallModel.NoCall::class.java)
+    }
+
+    @Test
+    fun repo_callNotifAddedThenRemoved_windowControllerUpdated() {
+        reset(mockStatusBarWindowController)
+
+        setCallNotifOnRepo()
+
+        setNoNotifsOnRepo()
+
+        verify(mockStatusBarWindowController).setOngoingProcessRequiresStatusBarVisible(false)
+    }
+
+    @Test
+    fun hasOngoingCall_noOngoingCallNotifSent_returnsFalse() {
+        assertThat(controller.hasOngoingCall()).isFalse()
+    }
+
+    @Test
+    fun hasOngoingCall_repoHasUnrelatedNotif_returnsFalse() {
+        setNotifOnRepo(
+            activeNotificationModel(
+                key = "unrelated",
+                callType = CallType.None,
+                uid = CALL_UID,
+            )
+        )
+
+        assertThat(controller.hasOngoingCall()).isFalse()
+    }
+
+    @Test
+    fun hasOngoingCall_repoHasScreeningCall_returnsFalse() {
+        setNotifOnRepo(
+            activeNotificationModel(
+                key = "unrelated",
+                callType = CallType.Screening,
+                uid = CALL_UID,
+            )
+        )
+
+        assertThat(controller.hasOngoingCall()).isFalse()
+    }
+
+    @Test
+    fun hasOngoingCall_repoHasCallNotifAndCallAppNotVisible_returnsTrue() {
+        whenever(
+                mockIActivityManager.getUidProcessState(
+                    eq(CALL_UID),
+                    any(),
+                )
+            )
+            .thenReturn(PROC_STATE_INVISIBLE)
+
+        setNotifOnRepo(
+            activeNotificationModel(
+                key = "notif",
+                callType = CallType.Ongoing,
+                uid = CALL_UID,
+            )
+        )
+
+        assertThat(controller.hasOngoingCall()).isTrue()
+    }
+
+    @Test
+    fun hasOngoingCall_repoHasCallNotifButCallAppVisible_returnsFalse() {
+        whenever(mockIActivityManager.getUidProcessState(eq(CALL_UID), any()))
+            .thenReturn(PROC_STATE_VISIBLE)
+
+        setNotifOnRepo(
+            activeNotificationModel(
+                key = "notif",
+                callType = CallType.Ongoing,
+                uid = CALL_UID,
+            )
+        )
+
+        assertThat(controller.hasOngoingCall()).isFalse()
+    }
+
+    @Test
+    fun hasOngoingCall_repoHasCallNotifButInvalidChipView_returnsFalse() {
+        val invalidChipView = LinearLayout(context)
+        controller.setChipView(invalidChipView)
+
+        setNotifOnRepo(
+            activeNotificationModel(
+                key = "notif",
+                callType = CallType.Ongoing,
+                uid = CALL_UID,
+            )
+        )
+
+        assertThat(controller.hasOngoingCall()).isFalse()
+    }
+
+    @Test
+    fun hasOngoingCall_repoHasCallNotifThenDoesNot_returnsFalse() {
+        setCallNotifOnRepo()
+        setNoNotifsOnRepo()
+
+        assertThat(controller.hasOngoingCall()).isFalse()
+    }
+
+    @Test
+    fun hasOngoingCall_repoHasCallNotifThenScreeningNotif_returnsFalse() {
+        setCallNotifOnRepo()
+        setNotifOnRepo(
+            activeNotificationModel(
+                key = "screening",
+                callType = CallType.Screening,
+                uid = CALL_UID,
+            )
+        )
+
+        assertThat(controller.hasOngoingCall()).isFalse()
+    }
+
+    /**
+     * This test fakes a theme change during an ongoing call.
+     *
+     * When a theme change happens,
+     * [com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment] and its views get
+     * re-created, so [OngoingCallController.setChipView] gets called with a new view. If there's an
+     * active ongoing call when the theme changes, the new view needs to be updated with the call
+     * information.
+     */
+    @Test
+    fun setChipView_whenRepoHasOngoingCall_listenerNotifiedWithNewView() {
+        // Start an ongoing call.
+        setCallNotifOnRepo()
+        reset(mockOngoingCallListener)
+
+        lateinit var newChipView: View
+        TestableLooper.get(this).runWithLooper {
+            newChipView =
+                LayoutInflater.from(mContext).inflate(R.layout.ongoing_activity_chip, null)
+        }
+
+        // Change the chip view associated with the controller.
+        controller.setChipView(newChipView)
+
+        verify(mockOngoingCallListener).onOngoingCallStateChanged(any())
+    }
+
+    @Test
+    fun callProcessChangesToVisible_listenerNotified() {
+        // Start the call while the process is invisible.
+        whenever(mockIActivityManager.getUidProcessState(eq(CALL_UID), any()))
+            .thenReturn(PROC_STATE_INVISIBLE)
+        setCallNotifOnRepo()
+        reset(mockOngoingCallListener)
+
+        val captor = argumentCaptor<IUidObserver.Stub>()
+        verify(mockIActivityManager).registerUidObserver(captor.capture(), any(), any(), any())
+        val uidObserver = captor.firstValue
+
+        // Update the process to visible.
+        uidObserver.onUidStateChanged(CALL_UID, PROC_STATE_VISIBLE, 0, 0)
+        mainExecutor.advanceClockToLast()
+        mainExecutor.runAllReady()
+
+        verify(mockOngoingCallListener).onOngoingCallStateChanged(any())
+    }
+
+    @Test
+    fun callProcessChangesToInvisible_listenerNotified() {
+        // Start the call while the process is visible.
+        whenever(mockIActivityManager.getUidProcessState(eq(CALL_UID), any()))
+            .thenReturn(PROC_STATE_VISIBLE)
+        setCallNotifOnRepo()
+        reset(mockOngoingCallListener)
+
+        val captor = argumentCaptor<IUidObserver.Stub>()
+        verify(mockIActivityManager).registerUidObserver(captor.capture(), any(), any(), any())
+        val uidObserver = captor.firstValue
+
+        // Update the process to invisible.
+        uidObserver.onUidStateChanged(CALL_UID, PROC_STATE_INVISIBLE, 0, 0)
+        mainExecutor.advanceClockToLast()
+        mainExecutor.runAllReady()
+
+        verify(mockOngoingCallListener).onOngoingCallStateChanged(any())
+    }
+
+    /** Regression test for b/212467440. */
+    @Test
+    @DisableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
+    fun chipClicked_activityStarterTriggeredWithUnmodifiedIntent() {
+        val pendingIntent = mock<PendingIntent>()
+        setNotifOnRepo(
+            activeNotificationModel(
+                key = "notif",
+                uid = CALL_UID,
+                contentIntent = pendingIntent,
+                callType = CallType.Ongoing,
+            )
+        )
+
+        chipView.performClick()
+
+        // Ensure that the sysui didn't modify the notification's intent -- see b/212467440.
+        verify(mockActivityStarter).postStartActivityDismissingKeyguard(eq(pendingIntent), any())
+    }
+
+    @Test
+    @DisableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
+    fun callNotificationAdded_chipIsClickable() {
+        setCallNotifOnRepo()
+
+        assertThat(chipView.hasOnClickListeners()).isTrue()
+    }
+
+    @Test
+    @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
+    fun callNotificationAdded_newChipsEnabled_chipNotClickable() {
+        setCallNotifOnRepo()
+
+        assertThat(chipView.hasOnClickListeners()).isFalse()
+    }
+
+    @Test
+    @DisableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
+    fun fullscreenIsTrue_chipStillClickable() {
+        setCallNotifOnRepo()
+        statusBarModeRepository.defaultDisplay.isInFullscreenMode.value = true
+        testScope.runCurrent()
+
+        assertThat(chipView.hasOnClickListeners()).isTrue()
+    }
+
+    @Test
+    fun callStartedInImmersiveMode_swipeGestureCallbackAdded() {
+        statusBarModeRepository.defaultDisplay.isInFullscreenMode.value = true
+        testScope.runCurrent()
+
+        setCallNotifOnRepo()
+
+        verify(mockSwipeStatusBarAwayGestureHandler).addOnGestureDetectedCallback(any(), any())
+    }
+
+    @Test
+    fun callStartedNotInImmersiveMode_swipeGestureCallbackNotAdded() {
+        statusBarModeRepository.defaultDisplay.isInFullscreenMode.value = false
+        testScope.runCurrent()
+
+        setCallNotifOnRepo()
+
+        verify(mockSwipeStatusBarAwayGestureHandler, never())
+            .addOnGestureDetectedCallback(any(), any())
+    }
+
+    @Test
+    fun transitionToImmersiveMode_swipeGestureCallbackAdded() {
+        setCallNotifOnRepo()
+
+        statusBarModeRepository.defaultDisplay.isInFullscreenMode.value = true
+        testScope.runCurrent()
+
+        verify(mockSwipeStatusBarAwayGestureHandler).addOnGestureDetectedCallback(any(), any())
+    }
+
+    @Test
+    fun transitionOutOfImmersiveMode_swipeGestureCallbackRemoved() {
+        statusBarModeRepository.defaultDisplay.isInFullscreenMode.value = true
+        testScope.runCurrent()
+
+        setCallNotifOnRepo()
+        reset(mockSwipeStatusBarAwayGestureHandler)
+
+        statusBarModeRepository.defaultDisplay.isInFullscreenMode.value = false
+        testScope.runCurrent()
+
+        verify(mockSwipeStatusBarAwayGestureHandler).removeOnGestureDetectedCallback(any())
+    }
+
+    @Test
+    fun callEndedWhileInImmersiveMode_swipeGestureCallbackRemoved() {
+        statusBarModeRepository.defaultDisplay.isInFullscreenMode.value = true
+        testScope.runCurrent()
+        setCallNotifOnRepo()
+        reset(mockSwipeStatusBarAwayGestureHandler)
+
+        setNoNotifsOnRepo()
+
+        verify(mockSwipeStatusBarAwayGestureHandler).removeOnGestureDetectedCallback(any())
+    }
+
+    private fun setCallNotifOnRepo() {
+        setNotifOnRepo(
+            activeNotificationModel(
+                key = "ongoingNotif",
+                callType = CallType.Ongoing,
+                uid = CALL_UID,
+                contentIntent = mock<PendingIntent>(),
+            )
+        )
+    }
+
+    private fun setNotifOnRepo(notif: ActiveNotificationModel) {
+        activeNotificationListRepository.activeNotifications.value =
+            ActiveNotificationsStore.Builder().apply { addIndividualNotif(notif) }.build()
+        testScope.runCurrent()
+    }
+
+    private fun setNoNotifsOnRepo() {
+        activeNotificationListRepository.activeNotifications.value =
+            ActiveNotificationsStore.Builder().build()
+        testScope.runCurrent()
+    }
+}
+
+private const val CALL_UID = 900
+
+// A process state that represents the process being visible to the user.
+private const val PROC_STATE_VISIBLE = ActivityManager.PROCESS_STATE_TOP
+
+// A process state that represents the process being invisible to the user.
+private const val PROC_STATE_INVISIBLE = ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallLoggerTest.kt
deleted file mode 100644
index 5ce936d..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallLoggerTest.kt
+++ /dev/null
@@ -1,75 +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.phone.ongoingcall
-
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.android.internal.logging.testing.UiEventLoggerFake
-import com.android.systemui.SysuiTestCase
-import com.google.common.truth.Truth.assertThat
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@SmallTest
-@RunWith(AndroidJUnit4::class)
-class OngoingCallLoggerTest : SysuiTestCase() {
-    private val uiEventLoggerFake = UiEventLoggerFake()
-    private val ongoingCallLogger = OngoingCallLogger(uiEventLoggerFake)
-
-    @Test
-    fun logChipClicked_clickEventLogged() {
-        ongoingCallLogger.logChipClicked()
-
-        assertThat(uiEventLoggerFake.numLogs()).isEqualTo(1)
-        assertThat(uiEventLoggerFake.eventId(0))
-                .isEqualTo(OngoingCallLogger.OngoingCallEvents.ONGOING_CALL_CLICKED.id)
-    }
-
-    @Test
-    fun logChipVisibilityChanged_changeFromInvisibleToVisible_visibleEventLogged() {
-        ongoingCallLogger.logChipVisibilityChanged(false)
-        ongoingCallLogger.logChipVisibilityChanged(true)
-
-        assertThat(uiEventLoggerFake.numLogs()).isEqualTo(1)
-        assertThat(uiEventLoggerFake.eventId(0))
-                .isEqualTo(OngoingCallLogger.OngoingCallEvents.ONGOING_CALL_VISIBLE.id)
-    }
-
-    @Test
-    fun logChipVisibilityChanged_changeFromVisibleToInvisible_eventNotLogged() {
-        // Setting the chip to visible here will trigger a log
-        ongoingCallLogger.logChipVisibilityChanged(true)
-        assertThat(uiEventLoggerFake.numLogs()).isEqualTo(1)
-
-        ongoingCallLogger.logChipVisibilityChanged(false)
-
-        // Expect that there were no new logs
-        assertThat(uiEventLoggerFake.numLogs()).isEqualTo(1)
-    }
-
-    @Test
-    fun logChipVisibilityChanged_visibleThenVisibleAgain_eventNotLogged() {
-        // Setting the chip to visible here will trigger a log
-        ongoingCallLogger.logChipVisibilityChanged(true)
-        assertThat(uiEventLoggerFake.numLogs()).isEqualTo(1)
-
-        ongoingCallLogger.logChipVisibilityChanged(true)
-
-        // Expect that there were no new logs
-        assertThat(uiEventLoggerFake.numLogs()).isEqualTo(1)
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepositoryTest.kt
index 27c2366..4c6eaa5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepositoryTest.kt
@@ -19,7 +19,9 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel
+import com.android.systemui.statusbar.phone.ongoingcall.shared.model.inCallModel
 import com.google.common.truth.Truth.assertThat
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -27,11 +29,12 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class OngoingCallRepositoryTest : SysuiTestCase() {
-    private val underTest = OngoingCallRepository()
+    private val kosmos = Kosmos()
+    private val underTest = kosmos.ongoingCallRepository
 
     @Test
     fun hasOngoingCall_matchesSet() {
-        val inCallModel = OngoingCallModel.InCall(startTimeMs = 654, intent = null)
+        val inCallModel = inCallModel(startTimeMs = 654)
         underTest.setOngoingCallState(inCallModel)
 
         assertThat(underTest.ongoingCallState.value).isEqualTo(inCallModel)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/airplane/data/repository/AirplaneModeRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/airplane/data/repository/AirplaneModeRepositoryImplTest.kt
index 917e5b8..0641e17 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/airplane/data/repository/AirplaneModeRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/airplane/data/repository/AirplaneModeRepositoryImplTest.kt
@@ -62,7 +62,7 @@
     fun setUp() {
         MockitoAnnotations.initMocks(this)
 
-        settings = FakeGlobalSettings()
+        settings = FakeGlobalSettings(testContext)
 
         whenever(telephonyManager.emergencyCallbackMode).thenReturn(false)
         whenever(subscriptionManager.activeSubscriptionIdList).thenReturn(intArrayOf())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt
index 8fd0b31..9d83d5f 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
@@ -59,6 +59,7 @@
 import android.telephony.TelephonyManager.ERI_OFF
 import android.telephony.TelephonyManager.ERI_ON
 import android.telephony.TelephonyManager.EXTRA_CARRIER_ID
+import android.telephony.TelephonyManager.EXTRA_DATA_SPN
 import android.telephony.TelephonyManager.EXTRA_PLMN
 import android.telephony.TelephonyManager.EXTRA_SHOW_PLMN
 import android.telephony.TelephonyManager.EXTRA_SHOW_SPN
@@ -85,7 +86,6 @@
 import com.android.systemui.statusbar.pipeline.mobile.data.model.SystemUiCarrierConfig
 import com.android.systemui.statusbar.pipeline.mobile.data.model.SystemUiCarrierConfigTest.Companion.configWithOverride
 import com.android.systemui.statusbar.pipeline.mobile.data.model.SystemUiCarrierConfigTest.Companion.createTestConfig
-import com.android.systemui.statusbar.pipeline.mobile.data.model.toNetworkNameModel
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository.Companion.DEFAULT_NUM_LEVELS
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.MobileTelephonyHelpers.signalStrength
@@ -93,8 +93,6 @@
 import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
 import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
 import com.android.systemui.statusbar.pipeline.shared.data.model.toMobileDataActivityModel
-import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.argumentCaptor
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
 import com.android.systemui.util.mockito.withArgCaptor
@@ -112,6 +110,8 @@
 import org.mockito.Mock
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.any
+import org.mockito.kotlin.argumentCaptor
 
 @Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
 @OptIn(ExperimentalCoroutinesApi::class)
@@ -815,9 +815,11 @@
             val intent = spnIntent()
             val captor = argumentCaptor<BroadcastReceiver>()
             verify(context).registerReceiver(captor.capture(), any())
-            captor.value!!.onReceive(context, intent)
+            captor.lastValue.onReceive(context, intent)
 
-            assertThat(latest).isEqualTo(intent.toNetworkNameModel(SEP))
+            // spnIntent() sets all values to true and test strings
+            assertThat(latest)
+                .isEqualTo(NetworkNameModel.IntentDerived("$PLMN$SEP$SPN$SEP$DATA_SPN"))
 
             job.cancel()
         }
@@ -831,17 +833,19 @@
             val intent = spnIntent()
             val captor = argumentCaptor<BroadcastReceiver>()
             verify(context).registerReceiver(captor.capture(), any())
-            captor.value!!.onReceive(context, intent)
+            captor.lastValue.onReceive(context, intent)
 
-            assertThat(latest).isEqualTo(intent.toNetworkNameModel(SEP))
+            assertThat(latest)
+                .isEqualTo(NetworkNameModel.IntentDerived("$PLMN$SEP$SPN$SEP$DATA_SPN"))
 
             // WHEN an intent with a different subId is sent
             val wrongSubIntent = spnIntent(subId = 101)
 
-            captor.value!!.onReceive(context, wrongSubIntent)
+            captor.lastValue.onReceive(context, wrongSubIntent)
 
             // THEN the previous intent's name is still used
-            assertThat(latest).isEqualTo(intent.toNetworkNameModel(SEP))
+            assertThat(latest)
+                .isEqualTo(NetworkNameModel.IntentDerived("$PLMN$SEP$SPN$SEP$DATA_SPN"))
 
             job.cancel()
         }
@@ -855,9 +859,10 @@
             val intent = spnIntent()
             val captor = argumentCaptor<BroadcastReceiver>()
             verify(context).registerReceiver(captor.capture(), any())
-            captor.value!!.onReceive(context, intent)
+            captor.lastValue.onReceive(context, intent)
 
-            assertThat(latest).isEqualTo(intent.toNetworkNameModel(SEP))
+            assertThat(latest)
+                .isEqualTo(NetworkNameModel.IntentDerived("$PLMN$SEP$SPN$SEP$DATA_SPN"))
 
             val intentWithoutInfo =
                 spnIntent(
@@ -865,7 +870,7 @@
                     showPlmn = false,
                 )
 
-            captor.value!!.onReceive(context, intentWithoutInfo)
+            captor.lastValue.onReceive(context, intentWithoutInfo)
 
             assertThat(latest).isEqualTo(DEFAULT_NAME_MODEL)
 
@@ -884,10 +889,88 @@
             val intent = spnIntent()
             val captor = argumentCaptor<BroadcastReceiver>()
             verify(context).registerReceiver(captor.capture(), any())
-            captor.value!!.onReceive(context, intent)
+            captor.lastValue.onReceive(context, intent)
 
             // The value is still there despite no active subscribers
-            assertThat(underTest.networkName.value).isEqualTo(intent.toNetworkNameModel(SEP))
+            assertThat(underTest.networkName.value)
+                .isEqualTo(NetworkNameModel.IntentDerived("$PLMN$SEP$SPN$SEP$DATA_SPN"))
+        }
+
+    @Test
+    fun networkName_allFieldsSet() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.networkName)
+            val captor = argumentCaptor<BroadcastReceiver>()
+            verify(context).registerReceiver(captor.capture(), any())
+
+            val intent =
+                spnIntent(
+                    subId = SUB_1_ID,
+                    showSpn = true,
+                    spn = SPN,
+                    dataSpn = null,
+                    showPlmn = true,
+                    plmn = PLMN,
+                )
+            captor.lastValue.onReceive(context, intent)
+            assertThat(latest).isEqualTo(NetworkNameModel.IntentDerived("$PLMN$SEP$SPN"))
+        }
+
+    @Test
+    fun networkName_showPlmn_plmnNotNull_showSpn_spnNull_dataSpnNotNull() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.networkName)
+            val captor = argumentCaptor<BroadcastReceiver>()
+            verify(context).registerReceiver(captor.capture(), any())
+            val intent =
+                spnIntent(
+                    subId = SUB_1_ID,
+                    showSpn = true,
+                    spn = null,
+                    dataSpn = DATA_SPN,
+                    showPlmn = true,
+                    plmn = PLMN,
+                )
+            captor.lastValue.onReceive(context, intent)
+            assertThat(latest).isEqualTo(NetworkNameModel.IntentDerived("$PLMN$SEP$DATA_SPN"))
+        }
+
+    @Test
+    fun networkName_showPlmn_noShowSPN() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.networkName)
+            val captor = argumentCaptor<BroadcastReceiver>()
+            verify(context).registerReceiver(captor.capture(), any())
+            val intent =
+                spnIntent(
+                    subId = SUB_1_ID,
+                    showSpn = false,
+                    spn = SPN,
+                    dataSpn = DATA_SPN,
+                    showPlmn = true,
+                    plmn = PLMN,
+                )
+            captor.lastValue.onReceive(context, intent)
+            assertThat(latest).isEqualTo(NetworkNameModel.IntentDerived("$PLMN"))
+        }
+
+    @Test
+    fun networkName_showPlmn_plmnNull_showSpn() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.networkName)
+            val captor = argumentCaptor<BroadcastReceiver>()
+            verify(context).registerReceiver(captor.capture(), any())
+            val intent =
+                spnIntent(
+                    subId = SUB_1_ID,
+                    showSpn = true,
+                    spn = SPN,
+                    dataSpn = DATA_SPN,
+                    showPlmn = true,
+                    plmn = null,
+                )
+            captor.lastValue.onReceive(context, intent)
+            assertThat(latest).isEqualTo(NetworkNameModel.IntentDerived("$SPN$SEP$DATA_SPN"))
         }
 
     @Test
@@ -1128,14 +1211,16 @@
     private fun spnIntent(
         subId: Int = SUB_1_ID,
         showSpn: Boolean = true,
-        spn: String = SPN,
+        spn: String? = SPN,
+        dataSpn: String? = DATA_SPN,
         showPlmn: Boolean = true,
-        plmn: String = PLMN,
+        plmn: String? = PLMN,
     ): Intent =
         Intent(TelephonyManager.ACTION_SERVICE_PROVIDERS_UPDATED).apply {
             putExtra(EXTRA_SUBSCRIPTION_INDEX, subId)
             putExtra(EXTRA_SHOW_SPN, showSpn)
             putExtra(EXTRA_SPN, spn)
+            putExtra(EXTRA_DATA_SPN, dataSpn)
             putExtra(EXTRA_SHOW_PLMN, showPlmn)
             putExtra(EXTRA_PLMN, plmn)
         }
@@ -1148,6 +1233,7 @@
         private const val SEP = "-"
 
         private const val SPN = "testSpn"
+        private const val DATA_SPN = "testDataSpn"
         private const val PLMN = "testPlmn"
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImplTest.kt
index 73ac6e3..af4f647 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,6 +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.SatelliteManager
 import android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED
 import android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_DATAGRAM_RETRYING
@@ -44,7 +45,6 @@
 import com.android.systemui.log.core.FakeLogBuffer
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.MobileTelephonyHelpers
 import com.android.systemui.statusbar.pipeline.satellite.data.prod.DeviceBasedSatelliteRepositoryImpl.Companion.MIN_UPTIME
-import com.android.systemui.statusbar.pipeline.satellite.data.prod.DeviceBasedSatelliteRepositoryImpl.Companion.POLLING_INTERVAL_MS
 import com.android.systemui.statusbar.pipeline.satellite.shared.model.SatelliteConnectionState
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.whenever
@@ -54,11 +54,8 @@
 import java.util.Optional
 import kotlin.test.Test
 import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.launchIn
-import kotlinx.coroutines.flow.onEach
 import kotlinx.coroutines.test.StandardTestDispatcher
 import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.advanceTimeBy
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
@@ -71,6 +68,7 @@
 import org.mockito.Mockito.times
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.doThrow
 
 @Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
 @OptIn(ExperimentalCoroutinesApi::class)
@@ -186,152 +184,86 @@
         }
 
     @Test
-    fun isSatelliteAllowed_readsSatelliteManagerState_enabled() =
+    fun isSatelliteAllowed_listensToSatelliteManagerCallback() =
         testScope.runTest {
             setupDefaultRepo()
-            // GIVEN satellite is allowed in this location
-            val allowed = true
-
-            doAnswer {
-                    val receiver = it.arguments[1] as OutcomeReceiver<Boolean, SatelliteException>
-                    receiver.onResult(allowed)
-                    null
-                }
-                .`when`(satelliteManager)
-                .requestIsCommunicationAllowedForCurrentLocation(
-                    any(),
-                    any<OutcomeReceiver<Boolean, SatelliteException>>()
-                )
 
             val latest by collectLastValue(underTest.isSatelliteAllowedForCurrentLocation)
+            runCurrent()
 
-            assertThat(latest).isTrue()
-        }
-
-    @Test
-    fun isSatelliteAllowed_readsSatelliteManagerState_disabled() =
-        testScope.runTest {
-            setupDefaultRepo()
-            // GIVEN satellite is not allowed in this location
-            val allowed = false
-
-            doAnswer {
-                    val receiver = it.arguments[1] as OutcomeReceiver<Boolean, SatelliteException>
-                    receiver.onResult(allowed)
-                    null
+            val callback =
+                withArgCaptor<SatelliteCommunicationAllowedStateCallback> {
+                    verify(satelliteManager)
+                        .registerForCommunicationAllowedStateChanged(any(), capture())
                 }
-                .`when`(satelliteManager)
-                .requestIsCommunicationAllowedForCurrentLocation(
-                    any(),
-                    any<OutcomeReceiver<Boolean, SatelliteException>>()
-                )
 
-            val latest by collectLastValue(underTest.isSatelliteAllowedForCurrentLocation)
+            // WHEN satellite manager says it's not available
+            callback.onSatelliteCommunicationAllowedStateChanged(false)
 
-            assertThat(latest).isFalse()
-        }
-
-    @Test
-    fun isSatelliteAllowed_pollsOnTimeout() =
-        testScope.runTest {
-            setupDefaultRepo()
-            // GIVEN satellite is not allowed in this location
-            var allowed = false
-
-            doAnswer {
-                    val receiver = it.arguments[1] as OutcomeReceiver<Boolean, SatelliteException>
-                    receiver.onResult(allowed)
-                    null
-                }
-                .`when`(satelliteManager)
-                .requestIsCommunicationAllowedForCurrentLocation(
-                    any(),
-                    any<OutcomeReceiver<Boolean, SatelliteException>>()
-                )
-
-            val latest by collectLastValue(underTest.isSatelliteAllowedForCurrentLocation)
-
+            // THEN it's not!
             assertThat(latest).isFalse()
 
-            // WHEN satellite becomes enabled
-            allowed = true
+            // WHEN satellite manager says it's changed to available
+            callback.onSatelliteCommunicationAllowedStateChanged(true)
 
-            // WHEN the timeout has not yet been reached
-            advanceTimeBy(POLLING_INTERVAL_MS / 2)
-
-            // THEN the value is still false
-            assertThat(latest).isFalse()
-
-            // WHEN time advances beyond the polling interval
-            advanceTimeBy(POLLING_INTERVAL_MS / 2 + 1)
-
-            // THEN then new value is emitted
+            // THEN it is!
             assertThat(latest).isTrue()
         }
 
     @Test
-    fun isSatelliteAllowed_pollingRestartsWhenCollectionRestarts() =
-        testScope.runTest {
-            setupDefaultRepo()
-            // Use the old school launch/cancel so we can simulate subscribers arriving and leaving
-
-            var latest: Boolean? = false
-            var job =
-                underTest.isSatelliteAllowedForCurrentLocation.onEach { latest = it }.launchIn(this)
-
-            // GIVEN satellite is not allowed in this location
-            var allowed = false
-
-            doAnswer {
-                    val receiver = it.arguments[1] as OutcomeReceiver<Boolean, SatelliteException>
-                    receiver.onResult(allowed)
-                    null
-                }
-                .`when`(satelliteManager)
-                .requestIsCommunicationAllowedForCurrentLocation(
-                    any(),
-                    any<OutcomeReceiver<Boolean, SatelliteException>>()
-                )
-
-            assertThat(latest).isFalse()
-
-            // WHEN satellite becomes enabled
-            allowed = true
-
-            // WHEN the job is restarted
-            advanceTimeBy(POLLING_INTERVAL_MS / 2)
-
-            job.cancel()
-            job =
-                underTest.isSatelliteAllowedForCurrentLocation.onEach { latest = it }.launchIn(this)
-
-            // THEN the value is re-fetched
-            assertThat(latest).isTrue()
-
-            job.cancel()
-        }
-
-    @Test
     fun isSatelliteAllowed_falseWhenErrorOccurs() =
         testScope.runTest {
             setupDefaultRepo()
-            doAnswer {
-                    val receiver = it.arguments[1] as OutcomeReceiver<Boolean, SatelliteException>
-                    receiver.onError(SatelliteException(1 /* unused */))
-                    null
-                }
-                .`when`(satelliteManager)
-                .requestIsCommunicationAllowedForCurrentLocation(
-                    any(),
-                    any<OutcomeReceiver<Boolean, SatelliteException>>()
-                )
 
+            // GIVEN SatelliteManager gon' throw exceptions when we ask to register the callback
+            doThrow(RuntimeException("Test exception"))
+                .`when`(satelliteManager)
+                .registerForCommunicationAllowedStateChanged(any(), any())
+
+            // WHEN the latest value is requested (and thus causes an exception to be thrown)
             val latest by collectLastValue(underTest.isSatelliteAllowedForCurrentLocation)
 
+            // THEN the value is just false, and we didn't crash!
             assertThat(latest).isFalse()
         }
 
     @Test
+    fun isSatelliteAllowed_reRegistersOnTelephonyProcessCrash() =
+        testScope.runTest {
+            setupDefaultRepo()
+            val latest by collectLastValue(underTest.isSatelliteAllowedForCurrentLocation)
+            runCurrent()
+
+            val callback =
+                withArgCaptor<SatelliteCommunicationAllowedStateCallback> {
+                    verify(satelliteManager)
+                        .registerForCommunicationAllowedStateChanged(any(), capture())
+                }
+
+            val telephonyCallback =
+                MobileTelephonyHelpers.getTelephonyCallbackForType<
+                    TelephonyCallback.RadioPowerStateListener
+                >(
+                    telephonyManager
+                )
+
+            // GIVEN satellite is currently provisioned
+            callback.onSatelliteCommunicationAllowedStateChanged(true)
+
+            assertThat(latest).isTrue()
+
+            // WHEN a crash event happens (detected by radio state change)
+            telephonyCallback.onRadioPowerStateChanged(TelephonyManager.RADIO_POWER_ON)
+            runCurrent()
+            telephonyCallback.onRadioPowerStateChanged(TelephonyManager.RADIO_POWER_OFF)
+            runCurrent()
+
+            // THEN listener is re-registered
+            verify(satelliteManager, times(2))
+                .registerForCommunicationAllowedStateChanged(any(), any())
+        }
+
+    @Test
     fun satelliteProvisioned_notSupported_defaultFalse() =
         testScope.runTest {
             // GIVEN satellite is not supported
@@ -363,24 +295,21 @@
         testScope.runTest {
             // GIVEN satellite is supported on device
             doAnswer {
-                val callback: OutcomeReceiver<Boolean, SatelliteException> =
-                    it.getArgument(1) as OutcomeReceiver<Boolean, SatelliteException>
-                callback.onResult(true)
-            }
+                    val callback: OutcomeReceiver<Boolean, SatelliteException> =
+                        it.getArgument(1) as OutcomeReceiver<Boolean, SatelliteException>
+                    callback.onResult(true)
+                }
                 .whenever(satelliteManager)
                 .requestIsSupported(any(), any())
 
             // GIVEN satellite returns an error when asked if provisioned
             doAnswer {
-                val receiver = it.arguments[1] as OutcomeReceiver<Boolean, SatelliteException>
-                receiver.onError(SatelliteException(SATELLITE_RESULT_ERROR))
-                null
-            }
+                    val receiver = it.arguments[1] as OutcomeReceiver<Boolean, SatelliteException>
+                    receiver.onError(SatelliteException(SATELLITE_RESULT_ERROR))
+                    null
+                }
                 .whenever(satelliteManager)
-                .requestIsProvisioned(
-                    any(),
-                    any<OutcomeReceiver<Boolean, SatelliteException>>()
-                )
+                .requestIsProvisioned(any(), any<OutcomeReceiver<Boolean, SatelliteException>>())
 
             // GIVEN we've been up long enough to start querying
             systemClock.setUptimeMillis(Process.getStartUptimeMillis() + MIN_UPTIME)
@@ -409,10 +338,10 @@
         testScope.runTest {
             // GIVEN satellite is supported on device
             doAnswer {
-                val callback: OutcomeReceiver<Boolean, SatelliteException> =
-                    it.getArgument(1) as OutcomeReceiver<Boolean, SatelliteException>
-                callback.onResult(true)
-            }
+                    val callback: OutcomeReceiver<Boolean, SatelliteException> =
+                        it.getArgument(1) as OutcomeReceiver<Boolean, SatelliteException>
+                    callback.onResult(true)
+                }
                 .whenever(satelliteManager)
                 .requestIsSupported(any(), any())
 
@@ -779,10 +708,10 @@
             .requestIsSupported(any(), any())
 
         doAnswer {
-            val callback: OutcomeReceiver<Boolean, SatelliteException> =
-                it.getArgument(1) as OutcomeReceiver<Boolean, SatelliteException>
-            callback.onResult(initialSatelliteIsProvisioned)
-        }
+                val callback: OutcomeReceiver<Boolean, SatelliteException> =
+                    it.getArgument(1) as OutcomeReceiver<Boolean, SatelliteException>
+                callback.onResult(initialSatelliteIsProvisioned)
+            }
             .whenever(satelliteManager)
             .requestIsProvisioned(any(), any())
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogDelegateTest.kt
new file mode 100644
index 0000000..bf0a39b
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogDelegateTest.kt
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
+package com.android.systemui.statusbar.policy.ui.dialog
+
+import android.app.Dialog
+import android.content.Intent
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.animation.ActivityTransitionAnimator
+import com.android.systemui.animation.mockActivityTransitionAnimatorController
+import com.android.systemui.animation.mockDialogTransitionAnimator
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.kosmos.mainCoroutineContext
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.plugins.activityStarter
+import com.android.systemui.runOnMainThreadAndWaitForIdleSync
+import com.android.systemui.statusbar.phone.SystemUIDialog
+import com.android.systemui.statusbar.phone.systemUIDialogFactory
+import com.android.systemui.statusbar.policy.ui.dialog.viewmodel.modesDialogViewModel
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class ModesDialogDelegateTest : SysuiTestCase() {
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
+    private val activityStarter = kosmos.activityStarter
+    private val mockDialogTransitionAnimator = kosmos.mockDialogTransitionAnimator
+    private val mockAnimationController = kosmos.mockActivityTransitionAnimatorController
+    private lateinit var underTest: ModesDialogDelegate
+
+    @Before
+    fun setup() {
+        whenever(
+                mockDialogTransitionAnimator.createActivityTransitionController(
+                    any<SystemUIDialog>(),
+                    eq(null)
+                )
+            )
+            .thenReturn(mockAnimationController)
+
+        underTest =
+            ModesDialogDelegate(
+                kosmos.systemUIDialogFactory,
+                mockDialogTransitionAnimator,
+                activityStarter,
+                { kosmos.modesDialogViewModel },
+                kosmos.mainCoroutineContext,
+            )
+    }
+
+    @Test
+    fun launchFromDialog_whenDialogNotOpen() {
+        val intent: Intent = mock()
+
+        runOnMainThreadAndWaitForIdleSync { underTest.launchFromDialog(intent) }
+
+        verify(activityStarter)
+            .startActivity(eq(intent), eq(true), eq<ActivityTransitionAnimator.Controller?>(null))
+    }
+
+    @Test
+    fun launchFromDialog_whenDialogOpen() =
+        testScope.runTest {
+            val intent: Intent = mock()
+            lateinit var dialog: Dialog
+
+            runOnMainThreadAndWaitForIdleSync {
+                kosmos.applicationCoroutineScope.launch { dialog = underTest.showDialog() }
+                runCurrent()
+                underTest.launchFromDialog(intent)
+            }
+
+            verify(mockDialogTransitionAnimator)
+                .createActivityTransitionController(any<Dialog>(), eq(null))
+            verify(activityStarter).startActivity(eq(intent), eq(true), eq(mockAnimationController))
+
+            runOnMainThreadAndWaitForIdleSync { dialog.dismiss() }
+        }
+
+    @Test
+    fun dismiss_clearsDialogReference() {
+        val dialog = runOnMainThreadAndWaitForIdleSync { underTest.createDialog() }
+
+        assertThat(underTest.currentDialog).isEqualTo(dialog)
+
+        runOnMainThreadAndWaitForIdleSync {
+            dialog.show()
+            dialog.dismiss()
+        }
+
+        assertThat(underTest.currentDialog).isNull()
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java b/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java
index 8df37ce..666bdd6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java
@@ -134,7 +134,6 @@
                 mNotificationManager,
                 mAccessibilityManager,
                 new ToastFactory(
-                        mLayoutInflater,
                         mPluginManager,
                         mDumpManager),
                 mToastLogger);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/touchpad/data/repository/TouchpadRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/touchpad/data/repository/TouchpadRepositoryTest.kt
new file mode 100644
index 0000000..3783af5
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/touchpad/data/repository/TouchpadRepositoryTest.kt
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.touchpad.data.repository
+
+import android.hardware.input.FakeInputManager
+import android.hardware.input.InputManager.InputDeviceListener
+import android.hardware.input.fakeInputManager
+import android.testing.TestableLooper
+import android.view.InputDevice
+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.inputdevice.data.repository.InputDeviceRepository
+import com.android.systemui.testKosmos
+import com.android.systemui.utils.os.FakeHandler
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatchers.eq
+import org.mockito.Captor
+import org.mockito.Mockito
+import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.anyOrNull
+import org.mockito.kotlin.whenever
+
+@SmallTest
+@TestableLooper.RunWithLooper
+@RunWith(AndroidJUnit4::class)
+class TouchpadRepositoryTest : SysuiTestCase() {
+
+    @Captor private lateinit var deviceListenerCaptor: ArgumentCaptor<InputDeviceListener>
+    private lateinit var fakeInputManager: FakeInputManager
+
+    private lateinit var underTest: TouchpadRepository
+    private lateinit var dispatcher: CoroutineDispatcher
+    private lateinit var inputDeviceRepo: InputDeviceRepository
+    private lateinit var testScope: TestScope
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+        fakeInputManager = testKosmos().fakeInputManager
+        dispatcher = StandardTestDispatcher()
+        testScope = TestScope(dispatcher)
+        val handler = FakeHandler(TestableLooper.get(this).looper)
+        inputDeviceRepo =
+            InputDeviceRepository(handler, testScope.backgroundScope, fakeInputManager.inputManager)
+        underTest =
+            TouchpadRepositoryImpl(dispatcher, fakeInputManager.inputManager, inputDeviceRepo)
+    }
+
+    @Test
+    fun emitsDisconnected_ifNothingIsConnected() =
+        testScope.runTest {
+            val initialState = underTest.isAnyTouchpadConnected.first()
+            assertThat(initialState).isFalse()
+        }
+
+    @Test
+    fun emitsConnected_ifTouchpadAlreadyConnectedAtTheStart() =
+        testScope.runTest {
+            fakeInputManager.addDevice(TOUCHPAD_ID, TOUCHPAD)
+            val initialValue = underTest.isAnyTouchpadConnected.first()
+            assertThat(initialValue).isTrue()
+        }
+
+    @Test
+    fun emitsConnected_whenNewTouchpadConnects() =
+        testScope.runTest {
+            captureDeviceListener()
+            val isTouchpadConnected by collectLastValue(underTest.isAnyTouchpadConnected)
+
+            fakeInputManager.addDevice(TOUCHPAD_ID, TOUCHPAD)
+
+            assertThat(isTouchpadConnected).isTrue()
+        }
+
+    @Test
+    fun emitsDisconnected_whenDeviceWithIdDoesNotExist() =
+        testScope.runTest {
+            captureDeviceListener()
+            val isTouchpadConnected by collectLastValue(underTest.isAnyTouchpadConnected)
+            whenever(fakeInputManager.inputManager.getInputDevice(eq(NULL_DEVICE_ID)))
+                .thenReturn(null)
+            fakeInputManager.addDevice(NULL_DEVICE_ID, InputDevice.SOURCE_UNKNOWN)
+            assertThat(isTouchpadConnected).isFalse()
+        }
+
+    @Test
+    fun emitsDisconnected_whenTouchpadDisconnects() =
+        testScope.runTest {
+            captureDeviceListener()
+            val isTouchpadConnected by collectLastValue(underTest.isAnyTouchpadConnected)
+
+            fakeInputManager.addDevice(TOUCHPAD_ID, TOUCHPAD)
+            assertThat(isTouchpadConnected).isTrue()
+
+            fakeInputManager.removeDevice(TOUCHPAD_ID)
+            assertThat(isTouchpadConnected).isFalse()
+        }
+
+    private suspend fun captureDeviceListener() {
+        underTest.isAnyTouchpadConnected.first()
+        Mockito.verify(fakeInputManager.inputManager)
+            .registerInputDeviceListener(deviceListenerCaptor.capture(), anyOrNull())
+        fakeInputManager.registerInputDeviceListener(deviceListenerCaptor.value)
+    }
+
+    @Test
+    fun emitsDisconnected_whenNonTouchpadConnects() =
+        testScope.runTest {
+            captureDeviceListener()
+            val isTouchpadConnected by collectLastValue(underTest.isAnyTouchpadConnected)
+
+            fakeInputManager.addDevice(NON_TOUCHPAD_ID, InputDevice.SOURCE_KEYBOARD)
+            assertThat(isTouchpadConnected).isFalse()
+        }
+
+    @Test
+    fun emitsDisconnected_whenTouchpadDisconnectsAndWasAlreadyConnectedAtTheStart() =
+        testScope.runTest {
+            captureDeviceListener()
+            val isTouchpadConnected by collectLastValue(underTest.isAnyTouchpadConnected)
+
+            fakeInputManager.removeDevice(TOUCHPAD_ID)
+            assertThat(isTouchpadConnected).isFalse()
+        }
+
+    @Test
+    fun emitsConnected_whenAnotherDeviceDisconnects() =
+        testScope.runTest {
+            captureDeviceListener()
+            val isTouchpadConnected by collectLastValue(underTest.isAnyTouchpadConnected)
+
+            fakeInputManager.addDevice(TOUCHPAD_ID, TOUCHPAD)
+            fakeInputManager.removeDevice(NON_TOUCHPAD_ID)
+
+            assertThat(isTouchpadConnected).isTrue()
+        }
+
+    @Test
+    fun emitsConnected_whenOneTouchpadDisconnectsButAnotherRemainsConnected() =
+        testScope.runTest {
+            captureDeviceListener()
+            val isTouchpadConnected by collectLastValue(underTest.isAnyTouchpadConnected)
+
+            fakeInputManager.addDevice(TOUCHPAD_ID, TOUCHPAD)
+            fakeInputManager.addDevice(ANOTHER_TOUCHPAD_ID, TOUCHPAD)
+            fakeInputManager.removeDevice(TOUCHPAD_ID)
+
+            assertThat(isTouchpadConnected).isTrue()
+        }
+
+    private companion object {
+        private const val TOUCHPAD_ID = 1
+        private const val NON_TOUCHPAD_ID = 2
+        private const val ANOTHER_TOUCHPAD_ID = 3
+        private const val NULL_DEVICE_ID = 4
+
+        private const val TOUCHPAD = InputDevice.SOURCE_TOUCHPAD
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/touchpad/tutorial/ui/gesture/BackGestureMonitorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/touchpad/tutorial/ui/gesture/BackGestureMonitorTest.kt
index cf0db7b..9064f7c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/touchpad/tutorial/ui/gesture/BackGestureMonitorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/touchpad/tutorial/ui/gesture/BackGestureMonitorTest.kt
@@ -17,17 +17,13 @@
 package com.android.systemui.touchpad.tutorial.ui.gesture
 
 import android.view.MotionEvent
-import android.view.MotionEvent.ACTION_DOWN
-import android.view.MotionEvent.ACTION_MOVE
-import android.view.MotionEvent.ACTION_POINTER_DOWN
-import android.view.MotionEvent.ACTION_POINTER_UP
-import android.view.MotionEvent.ACTION_UP
-import android.view.MotionEvent.AXIS_GESTURE_SWIPE_FINGER_COUNT
-import android.view.MotionEvent.CLASSIFICATION_MULTI_FINGER_SWIPE
-import android.view.MotionEvent.CLASSIFICATION_TWO_FINGER_SWIPE
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.FINISHED
+import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.IN_PROGRESS
+import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.NOT_STARTED
+import com.android.systemui.touchpad.tutorial.ui.gesture.MultiFingerGesture.Companion.SWIPE_DISTANCE
 import com.google.common.truth.Truth.assertThat
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -36,125 +32,57 @@
 @RunWith(AndroidJUnit4::class)
 class BackGestureMonitorTest : SysuiTestCase() {
 
-    private var gestureDoneWasCalled = false
-    private val gestureDoneCallback = { gestureDoneWasCalled = true }
-    private val gestureMonitor = BackGestureMonitor(SWIPE_DISTANCE.toInt(), gestureDoneCallback)
+    private var gestureState = NOT_STARTED
+    private val gestureMonitor =
+        BackGestureMonitor(
+            gestureDistanceThresholdPx = SWIPE_DISTANCE.toInt(),
+            gestureStateChangedCallback = { gestureState = it }
+        )
 
-    companion object {
-        const val SWIPE_DISTANCE = 100f
+    @Test
+    fun triggersGestureFinishedForThreeFingerGestureRight() {
+        assertStateAfterEvents(events = ThreeFingerGesture.swipeRight(), expectedState = FINISHED)
     }
 
     @Test
-    fun triggersGestureDoneForThreeFingerGestureRight() {
-        val events =
-            listOf(
-                threeFingerEvent(ACTION_DOWN, x = 0f, y = 0f),
-                threeFingerEvent(ACTION_POINTER_DOWN, x = 0f, y = 0f),
-                threeFingerEvent(ACTION_POINTER_DOWN, x = 0f, y = 0f),
-                threeFingerEvent(ACTION_MOVE, x = SWIPE_DISTANCE / 2, y = 0f),
-                threeFingerEvent(ACTION_POINTER_UP, x = SWIPE_DISTANCE, y = 0f),
-                threeFingerEvent(ACTION_POINTER_UP, x = SWIPE_DISTANCE, y = 0f),
-                threeFingerEvent(ACTION_UP, x = SWIPE_DISTANCE, y = 0f),
-            )
-
-        events.forEach { gestureMonitor.processTouchpadEvent(it) }
-
-        assertThat(gestureDoneWasCalled).isTrue()
+    fun triggersGestureFinishedForThreeFingerGestureLeft() {
+        assertStateAfterEvents(events = ThreeFingerGesture.swipeLeft(), expectedState = FINISHED)
     }
 
     @Test
-    fun triggersGestureDoneForThreeFingerGestureLeft() {
-        val events =
-            listOf(
-                threeFingerEvent(ACTION_DOWN, x = SWIPE_DISTANCE, y = 0f),
-                threeFingerEvent(ACTION_POINTER_DOWN, x = SWIPE_DISTANCE, y = 0f),
-                threeFingerEvent(ACTION_POINTER_DOWN, x = SWIPE_DISTANCE, y = 0f),
-                threeFingerEvent(ACTION_MOVE, x = SWIPE_DISTANCE / 2, y = 0f),
-                threeFingerEvent(ACTION_POINTER_UP, x = 0f, y = 0f),
-                threeFingerEvent(ACTION_POINTER_UP, x = 0f, y = 0f),
-                threeFingerEvent(ACTION_UP, x = 0f, y = 0f),
-            )
-
-        events.forEach { gestureMonitor.processTouchpadEvent(it) }
-
-        assertThat(gestureDoneWasCalled).isTrue()
-    }
-
-    private fun threeFingerEvent(action: Int, x: Float, y: Float): MotionEvent {
-        return motionEvent(
-            action = action,
-            x = x,
-            y = y,
-            classification = CLASSIFICATION_MULTI_FINGER_SWIPE,
-            axisValues = mapOf(AXIS_GESTURE_SWIPE_FINGER_COUNT to 3f)
+    fun triggersGestureProgressForThreeFingerGestureStarted() {
+        assertStateAfterEvents(
+            events = ThreeFingerGesture.startEvents(x = 0f, y = 0f),
+            expectedState = IN_PROGRESS
         )
     }
 
     @Test
-    fun doesntTriggerGestureDone_onThreeFingersSwipeUp() {
-        val events =
-            listOf(
-                threeFingerEvent(ACTION_DOWN, x = 0f, y = 0f),
-                threeFingerEvent(ACTION_POINTER_DOWN, x = 0f, y = 0f),
-                threeFingerEvent(ACTION_POINTER_DOWN, x = 0f, y = 0f),
-                threeFingerEvent(ACTION_MOVE, x = 0f, y = SWIPE_DISTANCE / 2),
-                threeFingerEvent(ACTION_POINTER_UP, x = 0f, y = SWIPE_DISTANCE),
-                threeFingerEvent(ACTION_POINTER_UP, x = 0f, y = SWIPE_DISTANCE),
-                threeFingerEvent(ACTION_UP, x = 0f, y = SWIPE_DISTANCE),
-            )
-
-        events.forEach { gestureMonitor.processTouchpadEvent(it) }
-
-        assertThat(gestureDoneWasCalled).isFalse()
+    fun doesntTriggerGestureFinished_onGestureDistanceTooShort() {
+        assertStateAfterEvents(
+            events = ThreeFingerGesture.swipeLeft(distancePx = SWIPE_DISTANCE / 2),
+            expectedState = NOT_STARTED
+        )
     }
 
     @Test
-    fun doesntTriggerGestureDone_onTwoFingersSwipe() {
-        fun twoFingerEvent(action: Int, x: Float, y: Float) =
-            motionEvent(
-                action = action,
-                x = x,
-                y = y,
-                classification = CLASSIFICATION_TWO_FINGER_SWIPE,
-                axisValues = mapOf(AXIS_GESTURE_SWIPE_FINGER_COUNT to 2f)
-            )
-        val events =
-            listOf(
-                twoFingerEvent(ACTION_DOWN, x = 0f, y = 0f),
-                twoFingerEvent(ACTION_MOVE, x = SWIPE_DISTANCE / 2, y = 0f),
-                twoFingerEvent(ACTION_UP, x = SWIPE_DISTANCE, y = 0f),
-            )
-
-        events.forEach { gestureMonitor.processTouchpadEvent(it) }
-
-        assertThat(gestureDoneWasCalled).isFalse()
+    fun doesntTriggerGestureFinished_onThreeFingersSwipeInOtherDirections() {
+        assertStateAfterEvents(events = ThreeFingerGesture.swipeUp(), expectedState = NOT_STARTED)
+        assertStateAfterEvents(events = ThreeFingerGesture.swipeDown(), expectedState = NOT_STARTED)
     }
 
     @Test
-    fun doesntTriggerGestureDone_onFourFingersSwipe() {
-        fun fourFingerEvent(action: Int, x: Float, y: Float) =
-            motionEvent(
-                action = action,
-                x = x,
-                y = y,
-                classification = CLASSIFICATION_MULTI_FINGER_SWIPE,
-                axisValues = mapOf(AXIS_GESTURE_SWIPE_FINGER_COUNT to 4f)
-            )
-        val events =
-            listOf(
-                fourFingerEvent(ACTION_DOWN, x = 0f, y = 0f),
-                fourFingerEvent(ACTION_POINTER_DOWN, x = 0f, y = 0f),
-                fourFingerEvent(ACTION_POINTER_DOWN, x = 0f, y = 0f),
-                fourFingerEvent(ACTION_POINTER_DOWN, x = 0f, y = 0f),
-                fourFingerEvent(ACTION_MOVE, x = SWIPE_DISTANCE / 2, y = 0f),
-                fourFingerEvent(ACTION_POINTER_UP, x = SWIPE_DISTANCE, y = 0f),
-                fourFingerEvent(ACTION_POINTER_UP, x = SWIPE_DISTANCE, y = 0f),
-                fourFingerEvent(ACTION_POINTER_UP, x = SWIPE_DISTANCE, y = 0f),
-                fourFingerEvent(ACTION_UP, x = SWIPE_DISTANCE, y = 0f),
-            )
+    fun doesntTriggerGestureFinished_onTwoFingersSwipe() {
+        assertStateAfterEvents(events = TwoFingerGesture.swipeRight(), expectedState = NOT_STARTED)
+    }
 
+    @Test
+    fun doesntTriggerGestureFinished_onFourFingersSwipe() {
+        assertStateAfterEvents(events = FourFingerGesture.swipeRight(), expectedState = NOT_STARTED)
+    }
+
+    private fun assertStateAfterEvents(events: List<MotionEvent>, expectedState: GestureState) {
         events.forEach { gestureMonitor.processTouchpadEvent(it) }
-
-        assertThat(gestureDoneWasCalled).isFalse()
+        assertThat(gestureState).isEqualTo(expectedState)
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureMonitorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureMonitorTest.kt
new file mode 100644
index 0000000..6aefbe9
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureMonitorTest.kt
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.touchpad.tutorial.ui.gesture
+
+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.touchpad.tutorial.ui.gesture.GestureState.FINISHED
+import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.IN_PROGRESS
+import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.NOT_STARTED
+import com.android.systemui.touchpad.tutorial.ui.gesture.MultiFingerGesture.Companion.SWIPE_DISTANCE
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class HomeGestureMonitorTest : SysuiTestCase() {
+
+    private var gestureState = NOT_STARTED
+    private val gestureMonitor =
+        HomeGestureMonitor(
+            gestureDistanceThresholdPx = SWIPE_DISTANCE.toInt(),
+            gestureStateChangedCallback = { gestureState = it }
+        )
+
+    @Test
+    fun triggersGestureFinishedForThreeFingerGestureUp() {
+        assertStateAfterEvents(events = ThreeFingerGesture.swipeUp(), expectedState = FINISHED)
+    }
+
+    @Test
+    fun triggersGestureProgressForThreeFingerGestureStarted() {
+        assertStateAfterEvents(
+            events = ThreeFingerGesture.startEvents(x = 0f, y = 0f),
+            expectedState = IN_PROGRESS
+        )
+    }
+
+    @Test
+    fun doesntTriggerGestureFinished_onGestureDistanceTooShort() {
+        assertStateAfterEvents(
+            events = ThreeFingerGesture.swipeUp(distancePx = SWIPE_DISTANCE / 2),
+            expectedState = NOT_STARTED
+        )
+    }
+
+    @Test
+    fun doesntTriggerGestureFinished_onThreeFingersSwipeInOtherDirections() {
+        assertStateAfterEvents(events = ThreeFingerGesture.swipeDown(), expectedState = NOT_STARTED)
+        assertStateAfterEvents(events = ThreeFingerGesture.swipeLeft(), expectedState = NOT_STARTED)
+        assertStateAfterEvents(
+            events = ThreeFingerGesture.swipeRight(),
+            expectedState = NOT_STARTED
+        )
+    }
+
+    @Test
+    fun doesntTriggerGestureFinished_onTwoFingersSwipe() {
+        assertStateAfterEvents(events = TwoFingerGesture.swipeUp(), expectedState = NOT_STARTED)
+    }
+
+    @Test
+    fun doesntTriggerGestureFinished_onFourFingersSwipe() {
+        assertStateAfterEvents(events = FourFingerGesture.swipeUp(), expectedState = NOT_STARTED)
+    }
+
+    private fun assertStateAfterEvents(events: List<MotionEvent>, expectedState: GestureState) {
+        events.forEach { gestureMonitor.processTouchpadEvent(it) }
+        assertThat(gestureState).isEqualTo(expectedState)
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureBuilder.kt b/packages/SystemUI/tests/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureBuilder.kt
new file mode 100644
index 0000000..296d4dc
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureBuilder.kt
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.touchpad.tutorial.ui.gesture
+
+import android.view.MotionEvent
+import android.view.MotionEvent.ACTION_DOWN
+import android.view.MotionEvent.ACTION_MOVE
+import android.view.MotionEvent.ACTION_POINTER_DOWN
+import android.view.MotionEvent.ACTION_POINTER_UP
+import android.view.MotionEvent.ACTION_UP
+import com.android.systemui.touchpad.tutorial.ui.gesture.MultiFingerGesture.Companion.DEFAULT_X
+import com.android.systemui.touchpad.tutorial.ui.gesture.MultiFingerGesture.Companion.DEFAULT_Y
+
+/**
+ * Interface for gesture builders which support creating list of [MotionEvent] for common swipe
+ * gestures. For simple usage see swipe* methods or use [createEvents] for more specific scenarios.
+ */
+interface MultiFingerGesture {
+
+    companion object {
+        const val SWIPE_DISTANCE = 100f
+        const val DEFAULT_X = 500f
+        const val DEFAULT_Y = 500f
+    }
+
+    fun swipeUp(distancePx: Float = SWIPE_DISTANCE) = createEvents { move(deltaY = -distancePx) }
+
+    fun swipeDown(distancePx: Float = SWIPE_DISTANCE) = createEvents { move(deltaY = distancePx) }
+
+    fun swipeRight(distancePx: Float = SWIPE_DISTANCE) = createEvents { move(deltaX = distancePx) }
+
+    fun swipeLeft(distancePx: Float = SWIPE_DISTANCE) = createEvents { move(deltaX = -distancePx) }
+
+    /**
+     * Creates gesture with provided move events. Note that move event's x and y is always relative
+     * to the starting one
+     */
+    fun createEvents(moveEvents: GestureBuilder.() -> Unit): List<MotionEvent>
+}
+
+object ThreeFingerGesture : MultiFingerGesture {
+    override fun createEvents(moveEvents: GestureBuilder.() -> Unit): List<MotionEvent> {
+        return touchpadGesture(
+            startEvents = { x, y -> startEvents(x, y) },
+            moveEvents = GestureBuilder(::threeFingerEvent).apply { moveEvents() }.events,
+            endEvents = { x, y -> endEvents(x, y) }
+        )
+    }
+
+    fun startEvents(x: Float, y: Float): List<MotionEvent> {
+        return listOf(
+            threeFingerEvent(ACTION_DOWN, x, y),
+            threeFingerEvent(ACTION_POINTER_DOWN, x, y),
+            threeFingerEvent(ACTION_POINTER_DOWN, x, y)
+        )
+    }
+
+    private fun endEvents(x: Float, y: Float): List<MotionEvent> {
+        return listOf(
+            threeFingerEvent(ACTION_POINTER_UP, x, y),
+            threeFingerEvent(ACTION_POINTER_UP, x, y),
+            threeFingerEvent(ACTION_UP, x, y)
+        )
+    }
+
+    private fun threeFingerEvent(
+        action: Int,
+        x: Float = DEFAULT_X,
+        y: Float = DEFAULT_Y
+    ): MotionEvent {
+        return touchpadEvent(
+            action = action,
+            x = x,
+            y = y,
+            classification = MotionEvent.CLASSIFICATION_MULTI_FINGER_SWIPE,
+            axisValues = mapOf(MotionEvent.AXIS_GESTURE_SWIPE_FINGER_COUNT to 3f)
+        )
+    }
+}
+
+object FourFingerGesture : MultiFingerGesture {
+
+    override fun createEvents(moveEvents: GestureBuilder.() -> Unit): List<MotionEvent> {
+        return touchpadGesture(
+            startEvents = { x, y -> startEvents(x, y) },
+            moveEvents = GestureBuilder(::fourFingerEvent).apply { moveEvents() }.events,
+            endEvents = { x, y -> endEvents(x, y) }
+        )
+    }
+
+    private fun startEvents(x: Float, y: Float): List<MotionEvent> {
+        return listOf(
+            fourFingerEvent(ACTION_DOWN, x, y),
+            fourFingerEvent(ACTION_POINTER_DOWN, x, y),
+            fourFingerEvent(ACTION_POINTER_DOWN, x, y),
+            fourFingerEvent(ACTION_POINTER_DOWN, x, y)
+        )
+    }
+
+    private fun endEvents(x: Float, y: Float): List<MotionEvent> {
+        return listOf(
+            fourFingerEvent(ACTION_POINTER_UP, x, y),
+            fourFingerEvent(ACTION_POINTER_UP, x, y),
+            fourFingerEvent(ACTION_POINTER_UP, x, y),
+            fourFingerEvent(ACTION_UP, x, y)
+        )
+    }
+
+    private fun fourFingerEvent(
+        action: Int,
+        x: Float = DEFAULT_X,
+        y: Float = DEFAULT_Y
+    ): MotionEvent {
+        return touchpadEvent(
+            action = action,
+            x = x,
+            y = y,
+            classification = MotionEvent.CLASSIFICATION_MULTI_FINGER_SWIPE,
+            axisValues = mapOf(MotionEvent.AXIS_GESTURE_SWIPE_FINGER_COUNT to 4f)
+        )
+    }
+}
+
+object TwoFingerGesture : MultiFingerGesture {
+
+    override fun createEvents(moveEvents: GestureBuilder.() -> Unit): List<MotionEvent> {
+        return touchpadGesture(
+            startEvents = { x, y -> listOf(twoFingerEvent(ACTION_DOWN, x, y)) },
+            moveEvents = GestureBuilder(::twoFingerEvent).apply { moveEvents() }.events,
+            endEvents = { x, y -> listOf(twoFingerEvent(ACTION_UP, x, y)) }
+        )
+    }
+
+    private fun twoFingerEvent(
+        action: Int,
+        x: Float = DEFAULT_X,
+        y: Float = DEFAULT_Y
+    ): MotionEvent {
+        return touchpadEvent(
+            action = action,
+            x = x,
+            y = y,
+            classification = MotionEvent.CLASSIFICATION_TWO_FINGER_SWIPE,
+            axisValues = mapOf(MotionEvent.AXIS_GESTURE_SWIPE_FINGER_COUNT to 2f)
+        )
+    }
+}
+
+private fun touchpadGesture(
+    startEvents: (Float, Float) -> List<MotionEvent>,
+    moveEvents: List<MotionEvent>,
+    endEvents: (Float, Float) -> List<MotionEvent>
+): List<MotionEvent> {
+    val lastX = moveEvents.last().x
+    val lastY = moveEvents.last().y
+    return startEvents(DEFAULT_X, DEFAULT_Y) + moveEvents + endEvents(lastX, lastY)
+}
+
+class GestureBuilder internal constructor(val eventBuilder: (Int, Float, Float) -> MotionEvent) {
+
+    val events = mutableListOf<MotionEvent>()
+
+    fun move(deltaX: Float = 0f, deltaY: Float = 0f) {
+        events.add(eventBuilder(ACTION_MOVE, DEFAULT_X + deltaX, DEFAULT_Y + deltaY))
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureBuilderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureBuilderTest.kt
new file mode 100644
index 0000000..13ebb42
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureBuilderTest.kt
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.touchpad.tutorial.ui.gesture
+
+import android.view.MotionEvent
+import android.view.MotionEvent.ACTION_DOWN
+import android.view.MotionEvent.ACTION_MOVE
+import android.view.MotionEvent.ACTION_POINTER_DOWN
+import android.view.MotionEvent.ACTION_POINTER_UP
+import android.view.MotionEvent.ACTION_UP
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.touchpad.tutorial.ui.gesture.MultiFingerGesture.Companion.DEFAULT_X
+import com.android.systemui.touchpad.tutorial.ui.gesture.MultiFingerGesture.Companion.DEFAULT_Y
+import com.google.common.truth.Truth.assertWithMessage
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class TouchpadGestureBuilderTest : SysuiTestCase() {
+
+    @Test
+    fun threeFingerSwipeProducesCorrectEvents() {
+        val events = ThreeFingerGesture.swipeRight()
+
+        events.forEach {
+            assertWithMessage("Event has three finger event parameters")
+                .that(isThreeFingerTouchpadSwipe(it))
+                .isTrue()
+        }
+        val actions = events.map { it.actionMasked }
+        assertWithMessage("Events have expected action type")
+            .that(actions)
+            .containsExactly(
+                ACTION_DOWN,
+                ACTION_POINTER_DOWN,
+                ACTION_POINTER_DOWN,
+                ACTION_MOVE,
+                ACTION_POINTER_UP,
+                ACTION_POINTER_UP,
+                ACTION_UP
+            )
+            .inOrder()
+    }
+
+    @Test
+    fun fourFingerSwipeProducesCorrectEvents() {
+        val events = FourFingerGesture.swipeUp()
+
+        events.forEach {
+            assertWithMessage("Event has four finger event parameters")
+                .that(isFourFingerTouchpadSwipe(it))
+                .isTrue()
+        }
+        val actions = events.map { it.actionMasked }
+        assertWithMessage("Events have expected action type")
+            .that(actions)
+            .containsExactly(
+                ACTION_DOWN,
+                ACTION_POINTER_DOWN,
+                ACTION_POINTER_DOWN,
+                ACTION_POINTER_DOWN,
+                ACTION_MOVE,
+                ACTION_POINTER_UP,
+                ACTION_POINTER_UP,
+                ACTION_POINTER_UP,
+                ACTION_UP
+            )
+            .inOrder()
+    }
+
+    @Test
+    fun twoFingerSwipeProducesCorrectEvents() {
+        val events = TwoFingerGesture.swipeLeft()
+
+        events.forEach {
+            assertWithMessage("Event has two finger event parameters")
+                .that(isTwoFingerGesture(it))
+                .isTrue()
+        }
+        val actions = events.map { it.actionMasked }
+        assertWithMessage("Events have expected action type")
+            .that(actions)
+            .containsExactlyElementsIn(listOf(ACTION_DOWN, ACTION_MOVE, ACTION_UP))
+            .inOrder()
+    }
+
+    private fun isTwoFingerGesture(event: MotionEvent): Boolean {
+        return event.classification == MotionEvent.CLASSIFICATION_TWO_FINGER_SWIPE &&
+            event.getAxisValue(MotionEvent.AXIS_GESTURE_SWIPE_FINGER_COUNT) == 2f
+    }
+
+    @Test
+    fun gestureBuilderProducesCorrectEventCoordinates() {
+        val events =
+            ThreeFingerGesture.createEvents {
+                move(deltaX = 50f)
+                move(deltaX = 100f)
+            }
+        val positions = events.map { it.x to it.y }
+        assertWithMessage("Events have expected coordinates")
+            .that(positions)
+            .containsExactly(
+                // down events
+                DEFAULT_X to DEFAULT_Y,
+                DEFAULT_X to DEFAULT_Y,
+                DEFAULT_X to DEFAULT_Y,
+                // move events
+                DEFAULT_X + 50f to DEFAULT_Y,
+                DEFAULT_X + 100f to DEFAULT_Y,
+                // up events
+                DEFAULT_X + 100f to DEFAULT_Y,
+                DEFAULT_X + 100f to DEFAULT_Y,
+                DEFAULT_X + 100f to DEFAULT_Y
+            )
+            .inOrder()
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureHandlerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureHandlerTest.kt
index 769f264..3a01d4f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureHandlerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureHandlerTest.kt
@@ -20,19 +20,14 @@
 import android.view.InputDevice.SOURCE_TOUCHSCREEN
 import android.view.MotionEvent
 import android.view.MotionEvent.ACTION_DOWN
-import android.view.MotionEvent.ACTION_HOVER_ENTER
-import android.view.MotionEvent.ACTION_MOVE
-import android.view.MotionEvent.ACTION_POINTER_DOWN
-import android.view.MotionEvent.ACTION_POINTER_UP
-import android.view.MotionEvent.ACTION_UP
-import android.view.MotionEvent.AXIS_GESTURE_SWIPE_FINGER_COUNT
-import android.view.MotionEvent.CLASSIFICATION_MULTI_FINGER_SWIPE
 import android.view.MotionEvent.TOOL_TYPE_FINGER
 import android.view.MotionEvent.TOOL_TYPE_MOUSE
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.touchpad.tutorial.ui.gesture.TouchpadGesture.BACK
+import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.FINISHED
+import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.NOT_STARTED
+import com.android.systemui.touchpad.tutorial.ui.gesture.MultiFingerGesture.Companion.SWIPE_DISTANCE
 import com.google.common.truth.Truth.assertThat
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -41,12 +36,14 @@
 @RunWith(AndroidJUnit4::class)
 class TouchpadGestureHandlerTest : SysuiTestCase() {
 
-    private var gestureDone = false
-    private val handler = TouchpadGestureHandler(BACK, SWIPE_DISTANCE) { gestureDone = true }
-
-    companion object {
-        const val SWIPE_DISTANCE = 100
-    }
+    private var gestureState = NOT_STARTED
+    private val handler =
+        TouchpadGestureHandler(
+            BackGestureMonitor(
+                gestureDistanceThresholdPx = SWIPE_DISTANCE.toInt(),
+                gestureStateChangedCallback = { gestureState = it }
+            )
+        )
 
     @Test
     fun handlesEventsFromTouchpad() {
@@ -84,41 +81,14 @@
     fun triggersGestureDoneForThreeFingerGesture() {
         backGestureEvents().forEach { handler.onMotionEvent(it) }
 
-        assertThat(gestureDone).isTrue()
+        assertThat(gestureState).isEqualTo(FINISHED)
     }
 
     private fun backGestureEvents(): List<MotionEvent> {
-        // list of motion events read from device while doing back gesture
-        val y = 100f
-        return listOf(
-            touchpadEvent(ACTION_HOVER_ENTER, x = 759f, y = y, pointerCount = 1),
-            threeFingerTouchpadEvent(ACTION_DOWN, x = 759f, y = y, pointerCount = 1),
-            threeFingerTouchpadEvent(ACTION_POINTER_DOWN, x = 759f, y = y, pointerCount = 2),
-            threeFingerTouchpadEvent(ACTION_POINTER_DOWN, x = 759f, y = y, pointerCount = 3),
-            threeFingerTouchpadEvent(ACTION_MOVE, x = 767f, y = y, pointerCount = 3),
-            threeFingerTouchpadEvent(ACTION_MOVE, x = 785f, y = y, pointerCount = 3),
-            threeFingerTouchpadEvent(ACTION_MOVE, x = 814f, y = y, pointerCount = 3),
-            threeFingerTouchpadEvent(ACTION_MOVE, x = 848f, y = y, pointerCount = 3),
-            threeFingerTouchpadEvent(ACTION_MOVE, x = 943f, y = y, pointerCount = 3),
-            threeFingerTouchpadEvent(ACTION_POINTER_UP, x = 943f, y = y, pointerCount = 3),
-            threeFingerTouchpadEvent(ACTION_POINTER_UP, x = 943f, y = y, pointerCount = 2),
-            threeFingerTouchpadEvent(ACTION_UP, x = 943f, y = y, pointerCount = 1)
-        )
-    }
-
-    private fun threeFingerTouchpadEvent(
-        action: Int,
-        x: Float,
-        y: Float,
-        pointerCount: Int
-    ): MotionEvent {
-        return touchpadEvent(
-            action = action,
-            x = x,
-            y = y,
-            pointerCount = pointerCount,
-            classification = CLASSIFICATION_MULTI_FINGER_SWIPE,
-            axisValues = mapOf(AXIS_GESTURE_SWIPE_FINGER_COUNT to 3f)
-        )
+        return ThreeFingerGesture.createEvents {
+            move(deltaX = SWIPE_DISTANCE / 4)
+            move(deltaX = SWIPE_DISTANCE / 2)
+            move(deltaX = SWIPE_DISTANCE)
+        }
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/settings/SettingsProxyTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/settings/SettingsProxyTest.kt
index 5ac6110..b0acd03 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/settings/SettingsProxyTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/settings/SettingsProxyTest.kt
@@ -275,6 +275,18 @@
     }
 
     @Test
+    fun getInt_keyMalformed_returnDefaultValue() {
+        mSettings.putString(TEST_SETTING, "nan")
+        assertThat(mSettings.getInt(TEST_SETTING, 5)).isEqualTo(5)
+    }
+
+    @Test
+    fun getInt_keyMalformed_throwException() {
+        mSettings.putString(TEST_SETTING, "nan")
+        assertThrows(SettingNotFoundException::class.java) { mSettings.getInt(TEST_SETTING) }
+    }
+
+    @Test
     fun getBool_keyPresent_returnValidValue() {
         mSettings.putBool(TEST_SETTING, true)
         assertThat(mSettings.getBool(TEST_SETTING)).isTrue()
@@ -323,6 +335,18 @@
     }
 
     @Test
+    fun getLong_keyMalformed_throwException() {
+        mSettings.putString(TEST_SETTING, "nan")
+        assertThrows(SettingNotFoundException::class.java) { mSettings.getLong(TEST_SETTING) }
+    }
+
+    @Test
+    fun getLong_keyMalformed_returnDefaultValue() {
+        mSettings.putString(TEST_SETTING, "nan")
+        assertThat(mSettings.getLong(TEST_SETTING, 2L)).isEqualTo(2L)
+    }
+
+    @Test
     fun getFloat_keyPresent_returnValidValue() {
         mSettings.putFloat(TEST_SETTING, 2.5F)
         assertThat(mSettings.getFloat(TEST_SETTING)).isEqualTo(2.5F)
@@ -346,6 +370,18 @@
         assertThat(mSettings.getFloat(TEST_SETTING, 2.5F)).isEqualTo(2.5F)
     }
 
+    @Test
+    fun getFloat_keyMalformed_throwException() {
+        mSettings.putString(TEST_SETTING, "nan")
+        assertThrows(SettingNotFoundException::class.java) { mSettings.getFloat(TEST_SETTING) }
+    }
+
+    @Test
+    fun getFloat_keyMalformed_returnDefaultValue() {
+        mSettings.putString(TEST_SETTING, "nan")
+        assertThat(mSettings.getFloat(TEST_SETTING, 2.5F)).isEqualTo(2.5F)
+    }
+
     private class FakeSettingsProxy(val testDispatcher: CoroutineDispatcher) : SettingsProxy {
 
         private val mContentResolver = mock(ContentResolver::class.java)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/settings/UserSettingsProxyTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/settings/UserSettingsProxyTest.kt
index 5f7420d..eaeece9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/settings/UserSettingsProxyTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/settings/UserSettingsProxyTest.kt
@@ -17,18 +17,16 @@
 package com.android.systemui.util.settings
 
 import android.content.ContentResolver
-import android.content.pm.UserInfo
 import android.database.ContentObserver
 import android.net.Uri
 import android.os.Handler
 import android.os.Looper
 import android.provider.Settings
+import android.provider.Settings.SettingNotFoundException
 import android.testing.TestableLooper
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.settings.FakeUserTracker
-import com.android.systemui.settings.UserTracker
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -51,29 +49,21 @@
 @TestableLooper.RunWithLooper
 class UserSettingsProxyTest : SysuiTestCase() {
 
-    private var mUserTracker = FakeUserTracker()
+    private var userId = MAIN_USER_ID
     private val testDispatcher = StandardTestDispatcher()
-    private var mSettings: UserSettingsProxy = FakeUserSettingsProxy(mUserTracker, testDispatcher)
+    private var mSettings: UserSettingsProxy = FakeUserSettingsProxy({ userId }, testDispatcher)
     private var mContentObserver = object : ContentObserver(Handler(Looper.getMainLooper())) {}
     private lateinit var testScope: TestScope
 
     @Before
     fun setUp() {
-        mUserTracker.set(
-            listOf(UserInfo(MAIN_USER_ID, "main", UserInfo.FLAG_MAIN)),
-            selectedUserIndex = 0
-        )
         testScope = TestScope(testDispatcher)
     }
 
     @Test
     fun registerContentObserverForUser_inputString_success() =
         testScope.runTest {
-            mSettings.registerContentObserverForUserSync(
-                TEST_SETTING,
-                mContentObserver,
-                mUserTracker.userId
-            )
+            mSettings.registerContentObserverForUserSync(TEST_SETTING, mContentObserver, userId)
             verify(mSettings.getContentResolver())
                 .registerContentObserver(
                     eq(TEST_SETTING_URI),
@@ -86,11 +76,7 @@
     @Test
     fun registerContentObserverForUserSuspend_inputString_success() =
         testScope.runTest {
-            mSettings.registerContentObserverForUser(
-                TEST_SETTING,
-                mContentObserver,
-                mUserTracker.userId
-            )
+            mSettings.registerContentObserverForUser(TEST_SETTING, mContentObserver, userId)
             verify(mSettings.getContentResolver())
                 .registerContentObserver(
                     eq(TEST_SETTING_URI),
@@ -103,11 +89,7 @@
     @Test
     fun registerContentObserverForUserAsync_inputString_success() =
         testScope.runTest {
-            mSettings.registerContentObserverForUserAsync(
-                TEST_SETTING,
-                mContentObserver,
-                mUserTracker.userId
-            )
+            mSettings.registerContentObserverForUserAsync(TEST_SETTING, mContentObserver, userId)
             testScope.advanceUntilIdle()
             verify(mSettings.getContentResolver())
                 .registerContentObserver(
@@ -125,7 +107,7 @@
                 TEST_SETTING,
                 notifyForDescendants = true,
                 mContentObserver,
-                mUserTracker.userId
+                userId
             )
             verify(mSettings.getContentResolver())
                 .registerContentObserver(
@@ -143,7 +125,7 @@
                 TEST_SETTING,
                 notifyForDescendants = true,
                 mContentObserver,
-                mUserTracker.userId
+                userId
             )
             verify(mSettings.getContentResolver())
                 .registerContentObserver(
@@ -163,7 +145,7 @@
                 TEST_SETTING,
                 notifyForDescendants = true,
                 mContentObserver,
-                mUserTracker.userId
+                userId
             )
             testScope.advanceUntilIdle()
             verify(mSettings.getContentResolver())
@@ -178,11 +160,7 @@
     @Test
     fun registerContentObserverForUser_inputUri_success() =
         testScope.runTest {
-            mSettings.registerContentObserverForUserSync(
-                TEST_SETTING_URI,
-                mContentObserver,
-                mUserTracker.userId
-            )
+            mSettings.registerContentObserverForUserSync(TEST_SETTING_URI, mContentObserver, userId)
             verify(mSettings.getContentResolver())
                 .registerContentObserver(
                     eq(TEST_SETTING_URI),
@@ -195,11 +173,7 @@
     @Test
     fun registerContentObserverForUserSuspend_inputUri_success() =
         testScope.runTest {
-            mSettings.registerContentObserverForUser(
-                TEST_SETTING_URI,
-                mContentObserver,
-                mUserTracker.userId
-            )
+            mSettings.registerContentObserverForUser(TEST_SETTING_URI, mContentObserver, userId)
             verify(mSettings.getContentResolver())
                 .registerContentObserver(
                     eq(TEST_SETTING_URI),
@@ -215,7 +189,7 @@
             mSettings.registerContentObserverForUserAsync(
                 TEST_SETTING_URI,
                 mContentObserver,
-                mUserTracker.userId
+                userId
             )
             testScope.advanceUntilIdle()
 
@@ -238,7 +212,7 @@
             mSettings.registerContentObserverForUserAsync(
                 TEST_SETTING_URI,
                 mContentObserver,
-                mUserTracker.userId,
+                userId,
                 runnable
             )
             testScope.advanceUntilIdle()
@@ -252,7 +226,7 @@
                 TEST_SETTING_URI,
                 notifyForDescendants = true,
                 mContentObserver,
-                mUserTracker.userId
+                userId
             )
             verify(mSettings.getContentResolver())
                 .registerContentObserver(
@@ -270,14 +244,12 @@
                 TEST_SETTING_URI,
                 notifyForDescendants = true,
                 mContentObserver,
-                mUserTracker.userId
+                userId
             )
             verify(mSettings.getContentResolver())
                 .registerContentObserver(
                     eq(TEST_SETTING_URI),
-                    eq(
-                        true,
-                    ),
+                    eq(true),
                     eq(mContentObserver),
                     eq(MAIN_USER_ID)
                 )
@@ -290,7 +262,7 @@
                 TEST_SETTING_URI,
                 notifyForDescendants = true,
                 mContentObserver,
-                mUserTracker.userId
+                userId
             )
             testScope.advanceUntilIdle()
             verify(mSettings.getContentResolver())
@@ -433,6 +405,18 @@
     }
 
     @Test
+    fun getInt_keyMalformed_returnDefaultValue() {
+        mSettings.putString(TEST_SETTING, "nan")
+        assertThat(mSettings.getInt(TEST_SETTING, 5)).isEqualTo(5)
+    }
+
+    @Test
+    fun getInt_keyMalformed_throwException() {
+        mSettings.putString(TEST_SETTING, "nan")
+        assertThrows(SettingNotFoundException::class.java) { mSettings.getInt(TEST_SETTING) }
+    }
+
+    @Test
     fun getIntForUser_multipleUsers__validResult() {
         mSettings.putIntForUser(TEST_SETTING, 1, MAIN_USER_ID)
         mSettings.putIntForUser(TEST_SETTING, 2, SECONDARY_USER_ID)
@@ -501,6 +485,18 @@
     }
 
     @Test
+    fun getLong_keyMalformed_throwException() {
+        mSettings.putString(TEST_SETTING, "nan")
+        assertThrows(SettingNotFoundException::class.java) { mSettings.getLong(TEST_SETTING) }
+    }
+
+    @Test
+    fun getLong_keyMalformed_returnDefaultValue() {
+        mSettings.putString(TEST_SETTING, "nan")
+        assertThat(mSettings.getLong(TEST_SETTING, 2L)).isEqualTo(2L)
+    }
+
+    @Test
     fun getLongForUser_multipleUsers__validResult() {
         mSettings.putLongForUser(TEST_SETTING, 1L, MAIN_USER_ID)
         mSettings.putLongForUser(TEST_SETTING, 2L, SECONDARY_USER_ID)
@@ -535,6 +531,18 @@
     }
 
     @Test
+    fun getFloat_keyMalformed_throwException() {
+        mSettings.putString(TEST_SETTING, "nan")
+        assertThrows(SettingNotFoundException::class.java) { mSettings.getFloat(TEST_SETTING) }
+    }
+
+    @Test
+    fun getFloat_keyMalformed_returnDefaultValue() {
+        mSettings.putString(TEST_SETTING, "nan")
+        assertThat(mSettings.getFloat(TEST_SETTING, 2.5F)).isEqualTo(2.5F)
+    }
+
+    @Test
     fun getFloatForUser_multipleUsers__validResult() {
         mSettings.putFloatForUser(TEST_SETTING, 1F, MAIN_USER_ID)
         mSettings.putFloatForUser(TEST_SETTING, 2F, SECONDARY_USER_ID)
@@ -548,7 +556,7 @@
      * This class uses a mock of [ContentResolver] to test the [ContentObserver] registration APIs.
      */
     private class FakeUserSettingsProxy(
-        override val userTracker: UserTracker,
+        override val currentUserProvider: SettingsProxy.CurrentUserIdProvider,
         val testDispatcher: CoroutineDispatcher
     ) : UserSettingsProxy {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/CsdWarningDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/CsdWarningDialogTest.java
index 49aedcc..bebf1cf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/volume/CsdWarningDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/CsdWarningDialogTest.java
@@ -36,7 +36,6 @@
 import android.media.AudioManager;
 import android.platform.test.annotations.EnableFlags;
 import android.testing.TestableLooper;
-import android.util.Pair;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
@@ -70,6 +69,8 @@
     private CsdWarningDialog mDialog;
     private static final String DISMISS_CSD_NOTIFICATION =
             "com.android.systemui.volume.DISMISS_CSD_NOTIFICATION";
+    private final Optional<ImmutableList<CsdWarningAction>> mEmptyActions =
+            Optional.of(ImmutableList.of());
 
     @Before
     public void setup() {
@@ -87,7 +88,7 @@
         // instantiate directly instead of via factory; we don't want executor to be @Background
         mDialog = new CsdWarningDialog(CSD_WARNING_DOSE_REACHED_1X, mContext,
                 mAudioManager, mNotificationManager, executor, null,
-                Optional.of(ImmutableList.of(new Pair("", new Intent()))),
+                mEmptyActions,
                 mFakeBroadcastDispatcher);
 
         mDialog.show();
@@ -104,7 +105,7 @@
         FakeExecutor executor =  new FakeExecutor(new FakeSystemClock());
         mDialog = new CsdWarningDialog(CSD_WARNING_DOSE_REPEATED_5X, mContext,
                 mAudioManager, mNotificationManager, executor, null,
-                Optional.of(ImmutableList.of(new Pair("", new Intent()))),
+                mEmptyActions,
                 mFakeBroadcastDispatcher);
 
         mDialog.show();
@@ -121,7 +122,7 @@
                 .setPackage(mContext.getPackageName());
         mDialog = new CsdWarningDialog(CSD_WARNING_DOSE_REPEATED_5X, mContext,
                 mAudioManager, mNotificationManager, executor, null,
-                Optional.of(ImmutableList.of(new Pair("Undo", undoIntent))),
+                Optional.of(ImmutableList.of(new CsdWarningAction("Undo", undoIntent, false))),
                 mFakeBroadcastDispatcher);
 
         when(mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC)).thenReturn(25);
@@ -148,7 +149,7 @@
                 .setPackage(mContext.getPackageName());
         mDialog = new CsdWarningDialog(CSD_WARNING_DOSE_REPEATED_5X, mContext,
                 mAudioManager, mNotificationManager, executor, null,
-                Optional.of(ImmutableList.of(new Pair("Undo", undoIntent))),
+                Optional.of(ImmutableList.of(new CsdWarningAction("Undo", undoIntent, false))),
                 mFakeBroadcastDispatcher);
         Intent dismissIntent = new Intent(DISMISS_CSD_NOTIFICATION)
                 .setPackage(mContext.getPackageName());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java
index 3f5dc82..8b7d921 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java
@@ -252,7 +252,6 @@
     }
 
     @Test
-    @EnableFlags(Flags.FLAG_VOLUME_DIALOG_AUDIO_SHARING_FIX)
     public void handleAudioSharingStreamVolumeChanges_updateState() {
         ArgumentCaptor<VolumeDialogController.State> stateCaptor =
                 ArgumentCaptor.forClass(VolumeDialogController.State.class);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
index b5cbf59..caa1779 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
@@ -44,7 +44,6 @@
 import static org.mockito.Mockito.when;
 
 import android.app.KeyguardManager;
-import android.content.Intent;
 import android.content.res.Configuration;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
@@ -57,7 +56,6 @@
 import android.provider.Settings;
 import android.testing.TestableLooper;
 import android.util.Log;
-import android.util.Pair;
 import android.view.Gravity;
 import android.view.InputDevice;
 import android.view.MotionEvent;
@@ -166,7 +164,7 @@
             new CsdWarningDialog.Factory() {
                 @Override
                 public CsdWarningDialog create(int warningType, Runnable onCleanup,
-                        Optional<ImmutableList<Pair<String, Intent>>> actionIntents) {
+                        Optional<ImmutableList<CsdWarningAction>> actionIntents) {
                     return mCsdWarningDialog;
                 }
             };
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index 60b5b5d..e5e04dc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -98,6 +98,8 @@
 import androidx.annotation.Nullable;
 import androidx.test.filters.SmallTest;
 
+import com.android.app.viewcapture.ViewCapture;
+import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
 import com.android.internal.colorextraction.ColorExtractor;
 import com.android.internal.logging.UiEventLogger;
 import com.android.internal.statusbar.IStatusBarService;
@@ -121,6 +123,8 @@
 import com.android.systemui.shade.ShadeController;
 import com.android.systemui.shade.ShadeWindowLogger;
 import com.android.systemui.shade.domain.interactor.ShadeInteractor;
+import com.android.systemui.shade.ui.viewmodel.NotificationShadeWindowModel;
+import com.android.systemui.shared.notifications.domain.interactor.NotificationSettingsInteractor;
 import com.android.systemui.shared.system.QuickStepContract;
 import com.android.systemui.statusbar.NotificationEntryHelper;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
@@ -210,6 +214,7 @@
 import java.util.Optional;
 import java.util.concurrent.Executor;
 
+import kotlin.Lazy;
 import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
 import platform.test.runner.parameterized.Parameters;
 
@@ -342,9 +347,12 @@
     private Icon mAppBubbleIcon;
     @Mock
     private Display mDefaultDisplay;
+    @Mock
+    private Lazy<ViewCapture> mLazyViewCapture;
 
     private final KosmosJavaAdapter mKosmos = new KosmosJavaAdapter(this);
     private ShadeInteractor mShadeInteractor;
+    private NotificationShadeWindowModel mNotificationShadeWindowModel;
     private ShellTaskOrganizer mShellTaskOrganizer;
     private TaskViewTransitions mTaskViewTransitions;
 
@@ -403,11 +411,13 @@
         when(deviceEntryUdfpsInteractor.isUdfpsSupported()).thenReturn(MutableStateFlow(false));
 
         mShadeInteractor = mKosmos.getShadeInteractor();
+        mNotificationShadeWindowModel = mKosmos.getNotificationShadeWindowModel();
 
         mNotificationShadeWindowController = new NotificationShadeWindowControllerImpl(
                 mContext,
                 new FakeWindowRootViewComponent.Factory(mNotificationShadeWindowView),
-                mWindowManager,
+                new ViewCaptureAwareWindowManager(mWindowManager, mLazyViewCapture,
+                        /* isViewCaptureEnabled= */ false),
                 mActivityManager,
                 mDozeParameters,
                 mStatusBarStateController,
@@ -424,6 +434,7 @@
                 mShadeWindowLogger,
                 () -> mSelectedUserInteractor,
                 mUserTracker,
+                mNotificationShadeWindowModel,
                 mKosmos::getCommunalInteractor
         );
         mNotificationShadeWindowController.fetchWindowRootView();
@@ -480,7 +491,8 @@
                         mock(PackageManager.class),
                         Optional.of(mock(Bubbles.class)),
                         mContext,
-                        mock(NotificationManager.class)
+                        mock(NotificationManager.class),
+                        mock(NotificationSettingsInteractor.class)
                         );
         interruptionDecisionProvider.start();
 
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/PartitionedGridLayoutKosmos.kt b/packages/SystemUI/tests/utils/src/android/app/role/RoleManagerKosmos.kt
similarity index 67%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/PartitionedGridLayoutKosmos.kt
copy to packages/SystemUI/tests/utils/src/android/app/role/RoleManagerKosmos.kt
index 37c9552..356bc86 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/PartitionedGridLayoutKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/android/app/role/RoleManagerKosmos.kt
@@ -14,11 +14,11 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.panels.domain.interactor
+package android.app.role
 
 import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.qs.panels.ui.compose.PartitionedGridLayout
-import com.android.systemui.qs.panels.ui.viewmodel.partitionedGridViewModel
+import com.android.systemui.util.mockito.mock
 
-val Kosmos.partitionedGridLayout by
-    Kosmos.Fixture { PartitionedGridLayout(partitionedGridViewModel) }
+val Kosmos.mockRoleManager: RoleManager by Kosmos.Fixture { mock() }
+
+var Kosmos.roleManager: RoleManager by Kosmos.Fixture { mockRoleManager }
diff --git a/packages/SystemUI/tests/utils/src/android/hardware/input/FakeInputManager.kt b/packages/SystemUI/tests/utils/src/android/hardware/input/FakeInputManager.kt
index c4f93d1..ee36cad 100644
--- a/packages/SystemUI/tests/utils/src/android/hardware/input/FakeInputManager.kt
+++ b/packages/SystemUI/tests/utils/src/android/hardware/input/FakeInputManager.kt
@@ -16,9 +16,12 @@
 
 package android.hardware.input
 
+import android.hardware.input.InputManager.InputDeviceListener
 import android.view.InputDevice
 import android.view.KeyCharacterMap
 import android.view.KeyCharacterMap.VIRTUAL_KEYBOARD
+import android.view.KeyEvent
+import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
 import org.mockito.ArgumentMatchers.anyInt
@@ -38,6 +41,14 @@
             .build()
 
     private val devices = mutableMapOf<Int, InputDevice>(VIRTUAL_KEYBOARD to virtualKeyboard)
+    private val allKeyCodes = (0..KeyEvent.MAX_KEYCODE)
+    private val supportedKeyCodesByDeviceId =
+        mutableMapOf(
+            // Mark all keys supported by default
+            VIRTUAL_KEYBOARD to allKeyCodes.toMutableSet()
+        )
+
+    private var inputDeviceListener: InputDeviceListener? = null
 
     val inputManager =
         mock<InputManager> {
@@ -61,13 +72,36 @@
             whenever(enableInputDevice(anyInt())).thenAnswer { invocation ->
                 setDeviceEnabled(invocation, enabled = true)
             }
+            whenever(deviceHasKeys(any(), any())).thenAnswer { invocation ->
+                val deviceId = invocation.arguments[0] as Int
+                val keyCodes = invocation.arguments[1] as IntArray
+                val supportedKeyCodes = supportedKeyCodesByDeviceId[deviceId]!!
+                return@thenAnswer keyCodes.map { supportedKeyCodes.contains(it) }.toBooleanArray()
+            }
         }
 
+    fun addPhysicalKeyboardIfNotPresent(deviceId: Int, enabled: Boolean = true) {
+        if (devices.containsKey(deviceId)) {
+            return
+        }
+        addPhysicalKeyboard(deviceId, enabled)
+    }
+
+    fun registerInputDeviceListener(listener: InputDeviceListener) {
+        // TODO (b/355422259): handle this by listening to inputManager.registerInputDeviceListener
+        inputDeviceListener = listener
+    }
+
     fun addPhysicalKeyboard(id: Int, enabled: Boolean = true) {
         check(id > 0) { "Physical keyboard ids have to be > 0" }
         addKeyboard(id, enabled)
     }
 
+    fun removeKeysFromKeyboard(deviceId: Int, vararg keyCodes: Int) {
+        addPhysicalKeyboardIfNotPresent(deviceId)
+        supportedKeyCodesByDeviceId[deviceId]!!.removeAll(keyCodes.asList())
+    }
+
     private fun addKeyboard(id: Int, enabled: Boolean = true) {
         devices[id] =
             InputDevice.Builder()
@@ -77,6 +111,17 @@
                 .setEnabled(enabled)
                 .setKeyCharacterMap(keyCharacterMap)
                 .build()
+        supportedKeyCodesByDeviceId[id] = allKeyCodes.toMutableSet()
+    }
+
+    fun addDevice(id: Int, sources: Int) {
+        devices[id] = InputDevice.Builder().setId(id).setSources(sources).build()
+        inputDeviceListener?.onInputDeviceAdded(id)
+    }
+
+    fun removeDevice(id: Int) {
+        devices.remove(id)
+        inputDeviceListener?.onInputDeviceRemoved(id)
     }
 
     private fun InputDevice.copy(
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java
index a124b34..27a2cab 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java
@@ -271,10 +271,14 @@
     }
 
     protected void waitForIdleSync() {
-        if (mHandler == null) {
-            mHandler = new Handler(Looper.getMainLooper());
+        if (isRobolectricTest()) {
+            mRealInstrumentation.waitForIdleSync();
+        } else {
+            if (mHandler == null) {
+                mHandler = new Handler(Looper.getMainLooper());
+            }
+            waitForIdleSync(mHandler);
         }
-        waitForIdleSync(mHandler);
     }
 
     protected void waitForUiOffloadThread() {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCaseExt.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCaseExt.kt
index 46259a6..d3dccb0 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCaseExt.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCaseExt.kt
@@ -20,3 +20,11 @@
 import com.android.systemui.kosmos.testCase
 
 fun SysuiTestCase.testKosmos(): Kosmos = Kosmos().apply { testCase = this@testKosmos }
+
+/** Run [f] on the main thread and return its result once completed. */
+fun <T : Any> SysuiTestCase.runOnMainThreadAndWaitForIdleSync(f: () -> T): T {
+    lateinit var result: T
+    context.mainExecutor.execute { result = f() }
+    waitForIdleSync()
+    return result
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/TestMocksModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/TestMocksModule.kt
index 0b6b816..5063140 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/TestMocksModule.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/TestMocksModule.kt
@@ -33,6 +33,7 @@
 import com.android.systemui.animation.DialogTransitionAnimator
 import com.android.systemui.biometrics.AuthController
 import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
+import com.android.systemui.camera.CameraGestureHelper
 import com.android.systemui.communal.domain.interactor.CommunalInteractor
 import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
 import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
@@ -143,6 +144,7 @@
     @get:Provides val primaryBouncerInteractor: PrimaryBouncerInteractor = mock(),
     @get:Provides val keyguardStateController: KeyguardStateController = mock(),
     @get:Provides val globalSettings: GlobalSettings = mock(),
+    @get:Provides val cameraGestureHelper: CameraGestureHelper = mock(),
 
     // log buffers
     @get:[Provides BroadcastDispatcherLog]
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/PartitionedGridLayoutKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/activatable/ActivatableExt.kt
similarity index 63%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/PartitionedGridLayoutKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/activatable/ActivatableExt.kt
index 37c9552..1f04a44 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/PartitionedGridLayoutKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/activatable/ActivatableExt.kt
@@ -14,11 +14,12 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.panels.domain.interactor
+package com.android.systemui.activatable
 
-import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.qs.panels.ui.compose.PartitionedGridLayout
-import com.android.systemui.qs.panels.ui.viewmodel.partitionedGridViewModel
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.test.TestScope
 
-val Kosmos.partitionedGridLayout by
-    Kosmos.Fixture { PartitionedGridLayout(partitionedGridViewModel) }
+/** Activates [activatable] for the duration of the test. */
+fun Activatable.activateIn(testScope: TestScope) {
+    testScope.backgroundScope.launch { activate() }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/animation/ActivityTransitionAnimatorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/animation/ActivityTransitionAnimatorKosmos.kt
index b23767e..5ac41ec 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/animation/ActivityTransitionAnimatorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/animation/ActivityTransitionAnimatorKosmos.kt
@@ -18,6 +18,10 @@
 
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testCase
+import com.android.systemui.util.mockito.mock
+
+val Kosmos.mockActivityTransitionAnimatorController by
+    Kosmos.Fixture { mock<ActivityTransitionAnimator.Controller>() }
 
 val Kosmos.activityTransitionAnimator by
     Kosmos.Fixture {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorKosmos.kt
index 9236bd2..63323b2 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorKosmos.kt
@@ -19,9 +19,10 @@
 import com.android.keyguard.keyguardUpdateMonitor
 import com.android.systemui.biometrics.data.repository.fingerprintPropertyRepository
 import com.android.systemui.bouncer.data.repository.keyguardBouncerRepository
-import com.android.systemui.deviceentry.domain.interactor.deviceEntryFingerprintAuthInteractor
+import com.android.systemui.deviceentry.domain.interactor.deviceEntryBiometricsAllowedInteractor
 import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
 import com.android.systemui.keyguard.data.repository.biometricSettingsRepository
+import com.android.systemui.keyguard.data.repository.deviceEntryFaceAuthRepository
 import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
 import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
 import com.android.systemui.kosmos.Kosmos
@@ -42,7 +43,7 @@
             biometricSettingsRepository = biometricSettingsRepository,
             systemClock = systemClock,
             keyguardUpdateMonitor = keyguardUpdateMonitor,
-            deviceEntryFingerprintAuthInteractor = { deviceEntryFingerprintAuthInteractor },
+            deviceEntryBiometricsAllowedInteractor = { deviceEntryBiometricsAllowedInteractor },
             keyguardInteractor = { keyguardInteractor },
             keyguardTransitionInteractor = { keyguardTransitionInteractor },
             scope = testScope.backgroundScope,
@@ -55,6 +56,7 @@
     this.keyguardBouncerRepository.setPrimaryShow(false)
     this.biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true)
     this.biometricSettingsRepository.setIsFingerprintAuthCurrentlyAllowed(true)
+    this.deviceEntryFaceAuthRepository.setLockedOut(false)
     whenever(this.keyguardUpdateMonitor.isFingerprintLockedOut).thenReturn(false)
     whenever(this.keyguardStateController.isUnlocked).thenReturn(false)
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/ui/viewmodel/BouncerMessageViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/ui/viewmodel/BouncerMessageViewModelKosmos.kt
index de5f0f3..e70631e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/ui/viewmodel/BouncerMessageViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/ui/viewmodel/BouncerMessageViewModelKosmos.kt
@@ -22,8 +22,8 @@
 import com.android.systemui.bouncer.domain.interactor.simBouncerInteractor
 import com.android.systemui.bouncer.shared.flag.composeBouncerFlags
 import com.android.systemui.deviceentry.domain.interactor.biometricMessageInteractor
+import com.android.systemui.deviceentry.domain.interactor.deviceEntryBiometricsAllowedInteractor
 import com.android.systemui.deviceentry.domain.interactor.deviceEntryFaceAuthInteractor
-import com.android.systemui.deviceentry.domain.interactor.deviceEntryFingerprintAuthInteractor
 import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
@@ -45,7 +45,7 @@
             biometricMessageInteractor = biometricMessageInteractor,
             faceAuthInteractor = deviceEntryFaceAuthInteractor,
             deviceUnlockedInteractor = deviceUnlockedInteractor,
-            fingerprintInteractor = deviceEntryFingerprintAuthInteractor,
+            deviceEntryBiometricsAllowedInteractor = deviceEntryBiometricsAllowedInteractor,
             flags = composeBouncerFlags,
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/PartitionedGridLayoutKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/camera/CameraGestureHelperKosmos.kt
similarity index 61%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/PartitionedGridLayoutKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/camera/CameraGestureHelperKosmos.kt
index 37c9552..931567e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/PartitionedGridLayoutKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/camera/CameraGestureHelperKosmos.kt
@@ -14,11 +14,16 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.panels.domain.interactor
+package com.android.systemui.camera
 
 import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.qs.panels.ui.compose.PartitionedGridLayout
-import com.android.systemui.qs.panels.ui.viewmodel.partitionedGridViewModel
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.whenever
+import org.mockito.Mockito.mock
 
-val Kosmos.partitionedGridLayout by
-    Kosmos.Fixture { PartitionedGridLayout(partitionedGridViewModel) }
+val Kosmos.cameraGestureHelper: CameraGestureHelper by
+    Kosmos.Fixture<CameraGestureHelper> {
+        mock(CameraGestureHelper::class.java).also { helper ->
+            whenever(helper.canCameraGestureBeLaunched(any())).thenReturn(true)
+        }
+    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/CommunalSceneRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/CommunalSceneRepositoryKosmos.kt
index a7a18a0..ef297d2 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/CommunalSceneRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/CommunalSceneRepositoryKosmos.kt
@@ -20,7 +20,7 @@
 import com.android.systemui.kosmos.Kosmos.Fixture
 import com.android.systemui.kosmos.applicationCoroutineScope
 
-val Kosmos.fakeCommunalSceneRepository by Fixture {
+var Kosmos.fakeCommunalSceneRepository by Fixture {
     FakeCommunalSceneRepository(applicationScope = applicationCoroutineScope)
 }
 
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalWidgetRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalWidgetRepository.kt
index f7ce367..c00454f 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalWidgetRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalWidgetRepository.kt
@@ -2,6 +2,9 @@
 
 import android.appwidget.AppWidgetProviderInfo
 import android.content.ComponentName
+import android.content.pm.ActivityInfo
+import android.content.pm.ApplicationInfo
+import android.graphics.Bitmap
 import android.os.UserHandle
 import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
 import com.android.systemui.communal.widgets.WidgetConfigurator
@@ -13,6 +16,8 @@
 /** Fake implementation of [CommunalWidgetRepository] */
 class FakeCommunalWidgetRepository(private val coroutineScope: CoroutineScope) :
     CommunalWidgetRepository {
+    private val fakeDatabase = mutableMapOf<Int, CommunalWidgetContentModel>()
+
     private val _communalWidgets = MutableStateFlow<List<CommunalWidgetContentModel>>(emptyList())
     override val communalWidgets: Flow<List<CommunalWidgetContentModel>> = _communalWidgets
 
@@ -38,12 +43,54 @@
         }
     }
 
-    override fun deleteWidget(widgetId: Int) {
-        if (_communalWidgets.value.none { it.appWidgetId == widgetId }) {
-            return
-        }
+    fun addWidget(
+        appWidgetId: Int,
+        componentName: String = "pkg/cls",
+        priority: Int = 0,
+        category: Int = AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD,
+        userId: Int = 0,
+    ) {
+        fakeDatabase[appWidgetId] =
+            CommunalWidgetContentModel.Available(
+                appWidgetId = appWidgetId,
+                priority = priority,
+                providerInfo =
+                    AppWidgetProviderInfo().apply {
+                        provider = ComponentName.unflattenFromString(componentName)!!
+                        widgetCategory = category
+                        providerInfo =
+                            ActivityInfo().apply {
+                                applicationInfo =
+                                    ApplicationInfo().apply {
+                                        uid = userId * UserHandle.PER_USER_RANGE
+                                    }
+                            }
+                    },
+            )
+        _communalWidgets.value = fakeDatabase.values.toList()
+    }
 
-        _communalWidgets.value = _communalWidgets.value.filter { it.appWidgetId != widgetId }
+    fun addPendingWidget(
+        appWidgetId: Int,
+        componentName: String = "pkg/cls",
+        priority: Int = 0,
+        icon: Bitmap? = null,
+        userId: Int = 0,
+    ) {
+        fakeDatabase[appWidgetId] =
+            CommunalWidgetContentModel.Pending(
+                appWidgetId = appWidgetId,
+                priority = priority,
+                componentName = ComponentName.unflattenFromString(componentName)!!,
+                icon = icon,
+                user = UserHandle(userId),
+            )
+        _communalWidgets.value = fakeDatabase.values.toList()
+    }
+
+    override fun deleteWidget(widgetId: Int) {
+        fakeDatabase.remove(widgetId)
+        _communalWidgets.value = fakeDatabase.values.toList()
     }
 
     override fun restoreWidgets(oldToNewWidgetIdMap: Map<Int, Int>) {}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricsAllowedInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricsAllowedInteractorKosmos.kt
new file mode 100644
index 0000000..4357289
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricsAllowedInteractorKosmos.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.
+ */
+
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
+package com.android.systemui.deviceentry.domain.interactor
+
+import com.android.systemui.biometrics.data.repository.facePropertyRepository
+import com.android.systemui.kosmos.Kosmos
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+
+val Kosmos.deviceEntryBiometricsAllowedInteractor by
+    Kosmos.Fixture {
+        DeviceEntryBiometricsAllowedInteractor(
+            deviceEntryFingerprintAuthInteractor = deviceEntryFingerprintAuthInteractor,
+            deviceEntryFaceAuthInteractor = deviceEntryFaceAuthInteractor,
+            biometricSettingsInteractor = deviceEntryBiometricSettingsInteractor,
+            facePropertyRepository = facePropertyRepository,
+        )
+    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/education/data/repository/FakeContextualEducationRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/education/data/repository/FakeContextualEducationRepository.kt
index 5410882..3816e1b 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/education/data/repository/FakeContextualEducationRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/education/data/repository/FakeContextualEducationRepository.kt
@@ -16,8 +16,8 @@
 
 package com.android.systemui.education.data.repository
 
+import com.android.systemui.contextualeducation.GestureType
 import com.android.systemui.education.data.model.GestureEduModel
-import com.android.systemui.shared.education.GestureType
 import java.time.Clock
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
@@ -28,11 +28,15 @@
     private val userGestureMap = mutableMapOf<Int, GestureEduModel>()
     private val _gestureEduModels = MutableStateFlow(GestureEduModel())
     private val gestureEduModelsFlow = _gestureEduModels.asStateFlow()
+    private var currentUser: Int = 0
 
     override fun setUser(userId: Int) {
         if (!userGestureMap.contains(userId)) {
             userGestureMap[userId] = GestureEduModel()
         }
+        // save data of current user to the map
+        userGestureMap[currentUser] = _gestureEduModels.value
+        // switch to data of new user
         _gestureEduModels.value = userGestureMap[userId]!!
     }
 
@@ -41,13 +45,15 @@
     }
 
     override suspend fun incrementSignalCount(gestureType: GestureType) {
+        val originalModel = _gestureEduModels.value
         _gestureEduModels.value =
-            GestureEduModel(
+            originalModel.copy(
                 signalCount = _gestureEduModels.value.signalCount + 1,
             )
     }
 
     override suspend fun updateShortcutTriggerTime(gestureType: GestureType) {
-        _gestureEduModels.value = GestureEduModel(lastShortcutTriggeredTime = clock.instant())
+        val originalModel = _gestureEduModels.value
+        _gestureEduModels.value = originalModel.copy(lastShortcutTriggeredTime = clock.instant())
     }
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/education/domain/interactor/ContextualEducationInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/education/domain/interactor/ContextualEducationInteractorKosmos.kt
index 5b2dc2b..a7b322b 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/education/domain/interactor/ContextualEducationInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/education/domain/interactor/ContextualEducationInteractorKosmos.kt
@@ -18,6 +18,7 @@
 
 import com.android.systemui.education.data.repository.contextualEducationRepository
 import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testDispatcher
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.user.domain.interactor.selectedUserInteractor
 
@@ -25,6 +26,7 @@
     Kosmos.Fixture {
         ContextualEducationInteractor(
             backgroundScope = testScope.backgroundScope,
+            backgroundDispatcher = testDispatcher,
             repository = contextualEducationRepository,
             selectedUserInteractor = selectedUserInteractor
         )
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorKosmos.kt
index 8f84e04..fb4e901 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorKosmos.kt
@@ -19,6 +19,14 @@
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
 
+var Kosmos.keyboardTouchpadEduInteractor by
+    Kosmos.Fixture {
+        KeyboardTouchpadEduInteractor(
+            backgroundScope = testScope.backgroundScope,
+            contextualEducationInteractor = contextualEducationInteractor
+        )
+    }
+
 var Kosmos.keyboardTouchpadEduStatsInteractor by
     Kosmos.Fixture {
         KeyboardTouchpadEduStatsInteractorImpl(
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/haptics/qs/QSLongPressEffectKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/haptics/qs/QSLongPressEffectKosmos.kt
index 28355e1..ca748b66 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/haptics/qs/QSLongPressEffectKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/haptics/qs/QSLongPressEffectKosmos.kt
@@ -16,10 +16,16 @@
 
 package com.android.systemui.haptics.qs
 
-import com.android.systemui.classifier.falsingManager
 import com.android.systemui.haptics.vibratorHelper
 import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.log.core.FakeLogBuffer
 import com.android.systemui.statusbar.policy.keyguardStateController
 
 val Kosmos.qsLongPressEffect by
-    Kosmos.Fixture { QSLongPressEffect(vibratorHelper, keyguardStateController, falsingManager) }
+    Kosmos.Fixture {
+        QSLongPressEffect(
+            vibratorHelper,
+            keyguardStateController,
+            FakeLogBuffer.Factory.create(),
+        )
+    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/inputmethod/data/repository/FakeInputMethodRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/inputmethod/data/repository/FakeInputMethodRepository.kt
index 8e4461d..444baa0 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/inputmethod/data/repository/FakeInputMethodRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/inputmethod/data/repository/FakeInputMethodRepository.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.inputmethod.data.repository
 
+import android.os.UserHandle
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.inputmethod.data.model.InputMethodModel
 import kotlinx.coroutines.flow.Flow
@@ -40,14 +41,15 @@
     }
 
     override suspend fun enabledInputMethods(
-        userId: Int,
-        fetchSubtypes: Boolean,
+        user: UserHandle,
+        fetchSubtypes: Boolean
     ): Flow<InputMethodModel> {
-        return usersToEnabledInputMethods[userId] ?: flowOf()
+        return usersToEnabledInputMethods[user.identifier] ?: flowOf()
     }
 
-    override suspend fun selectedInputMethodSubtypes(): List<InputMethodModel.Subtype> =
-        selectedInputMethodSubtypes
+    override suspend fun selectedInputMethodSubtypes(
+        user: UserHandle,
+    ): List<InputMethodModel.Subtype> = selectedInputMethodSubtypes
 
     override suspend fun showInputMethodPicker(displayId: Int, showAuxiliarySubtypes: Boolean) {
         inputMethodPickerShownDisplayId = displayId
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 c423b62..c2a03d4 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
@@ -16,6 +16,7 @@
 
 package com.android.systemui.keyboard.shortcut
 
+import android.app.role.mockRoleManager
 import android.content.applicationContext
 import android.content.res.mainResources
 import android.hardware.input.fakeInputManager
@@ -41,6 +42,7 @@
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.model.sysUiState
 import com.android.systemui.settings.displayTracker
+import com.android.systemui.settings.fakeUserTracker
 
 var Kosmos.shortcutHelperAppCategoriesShortcutsSource: KeyboardShortcutGroupsSource by
     Kosmos.Fixture {
@@ -117,6 +119,8 @@
 val Kosmos.shortcutHelperViewModel by
     Kosmos.Fixture {
         ShortcutHelperViewModel(
+            mockRoleManager,
+            fakeUserTracker,
             applicationCoroutineScope,
             testDispatcher,
             shortcutHelperStateInteractor,
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 6ca5cd8..8b45662 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
@@ -95,7 +95,7 @@
     }
 
     fun toggle(deviceId: Int) {
-        fakeInputManager.addPhysicalKeyboard(deviceId)
+        fakeInputManager.addPhysicalKeyboardIfNotPresent(deviceId)
         fakeCommandQueue.doForEachCallback { it.toggleKeyboardShortcutsMenu(deviceId) }
     }
 
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/data/source/FakeKeyboardShortcutGroupsSource.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/data/source/FakeKeyboardShortcutGroupsSource.kt
index 2bab1a4..d4cb6ff 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/data/source/FakeKeyboardShortcutGroupsSource.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/data/source/FakeKeyboardShortcutGroupsSource.kt
@@ -24,6 +24,10 @@
 
     override suspend fun shortcutGroups(deviceId: Int): List<KeyboardShortcutGroup> = groups
 
+    fun setGroups(vararg groups: KeyboardShortcutGroup) {
+        this.groups = groups.asList()
+    }
+
     fun setGroups(groups: List<KeyboardShortcutGroup>) {
         this.groups = groups
     }
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 87143ef..727de9e 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
@@ -22,6 +22,7 @@
 import com.android.systemui.keyguard.shared.model.BiometricUnlockMode
 import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
 import com.android.systemui.keyguard.shared.model.BiometricUnlockSource
+import com.android.systemui.keyguard.shared.model.CameraLaunchSourceModel
 import com.android.systemui.keyguard.shared.model.DismissAction
 import com.android.systemui.keyguard.shared.model.DozeTransitionModel
 import com.android.systemui.keyguard.shared.model.KeyguardDone
@@ -138,6 +139,8 @@
     private val _canIgnoreAuthAndReturnToGone = MutableStateFlow(false)
     override val canIgnoreAuthAndReturnToGone = _canIgnoreAuthAndReturnToGone.asStateFlow()
 
+    override val onCameraLaunchDetected = MutableStateFlow(CameraLaunchSourceModel())
+
     override fun setQuickSettingsVisible(isVisible: Boolean) {
         _isQuickSettingsVisible.value = isVisible
     }
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 b5ea619..616f2b6 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
@@ -283,7 +283,7 @@
         )
     }
 
-    override fun updateTransition(
+    override suspend fun updateTransition(
         transitionId: UUID,
         @FloatRange(from = 0.0, to = 1.0) value: Float,
         state: TransitionState
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractorKosmos.kt
index 079852a..494f08b 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractorKosmos.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.keyguard.domain.interactor
 
+import com.android.systemui.communal.domain.interactor.communalSceneInteractor
 import com.android.systemui.communal.domain.interactor.communalSettingsInteractor
 import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository
 import com.android.systemui.kosmos.Kosmos
@@ -38,5 +39,6 @@
             transitionInteractor = keyguardTransitionInteractor,
             powerInteractor = powerInteractor,
             keyguardOcclusionInteractor = keyguardOcclusionInteractor,
+            communalSceneInteractor = communalSceneInteractor,
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractorKosmos.kt
index 317294f..c694114 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractorKosmos.kt
@@ -16,7 +16,7 @@
 
 package com.android.systemui.keyguard.domain.interactor
 
-import com.android.systemui.communal.domain.interactor.communalInteractor
+import com.android.systemui.communal.domain.interactor.communalSceneInteractor
 import com.android.systemui.keyguard.data.repository.biometricSettingsRepository
 import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
 import com.android.systemui.keyguard.data.repository.keyguardRepository
@@ -37,7 +37,7 @@
             mainDispatcher = testDispatcher,
             keyguardInteractor = keyguardInteractor,
             powerInteractor = powerInteractor,
-            communalInteractor = communalInteractor,
+            communalSceneInteractor = communalSceneInteractor,
             keyguardOcclusionInteractor = keyguardOcclusionInteractor,
             biometricSettingsRepository = biometricSettingsRepository,
             keyguardRepository = keyguardRepository,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractorKosmos.kt
index c216945..7827655 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractorKosmos.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.keyguard.domain.interactor
 
 import com.android.systemui.communal.domain.interactor.communalInteractor
+import com.android.systemui.communal.domain.interactor.communalSceneInteractor
 import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.applicationCoroutineScope
@@ -37,5 +38,6 @@
             powerInteractor = powerInteractor,
             communalInteractor = communalInteractor,
             keyguardOcclusionInteractor = keyguardOcclusionInteractor,
+            communalSceneInteractor = communalSceneInteractor,
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorKosmos.kt
index 42ee152..3c369d7 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorKosmos.kt
@@ -17,7 +17,7 @@
 package com.android.systemui.keyguard.domain.interactor
 
 import com.android.keyguard.keyguardSecurityModel
-import com.android.systemui.communal.domain.interactor.communalInteractor
+import com.android.systemui.communal.domain.interactor.communalSceneInteractor
 import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.applicationCoroutineScope
@@ -36,7 +36,7 @@
             bgDispatcher = testDispatcher,
             mainDispatcher = testDispatcher,
             keyguardInteractor = keyguardInteractor,
-            communalInteractor = communalInteractor,
+            communalSceneInteractor = communalSceneInteractor,
             keyguardSecurityModel = keyguardSecurityModel,
             selectedUserInteractor = selectedUserInteractor,
             powerInteractor = powerInteractor,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorKosmos.kt
index 2c6d44f..957f092 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorKosmos.kt
@@ -16,9 +16,13 @@
 
 package com.android.systemui.keyguard.domain.interactor
 
+import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
 import com.android.systemui.keyguard.data.repository.keyguardRepository
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
+import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.scene.domain.resolver.notifShadeSceneFamilyResolver
+import com.android.systemui.scene.domain.resolver.quickSettingsSceneFamilyResolver
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 
 @ExperimentalCoroutinesApi
@@ -29,5 +33,9 @@
             transitionInteractor = keyguardTransitionInteractor,
             dismissInteractor = keyguardDismissInteractor,
             applicationScope = testScope.backgroundScope,
+            sceneInteractor = sceneInteractor,
+            deviceEntryInteractor = deviceEntryInteractor,
+            quickSettingsSceneFamilyResolver = quickSettingsSceneFamilyResolver,
+            notifShadeSceneFamilyResolver = notifShadeSceneFamilyResolver,
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorFactory.kt
index be8048e..9b7bca6 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorFactory.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorFactory.kt
@@ -27,8 +27,8 @@
 import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
 import com.android.systemui.bouncer.ui.BouncerView
 import com.android.systemui.classifier.FalsingCollector
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryBiometricsAllowedInteractor
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor
-import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFingerprintAuthInteractor
 import com.android.systemui.keyguard.DismissCallbackRegistry
 import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
@@ -87,7 +87,7 @@
                 FakeBiometricSettingsRepository(),
                 FakeSystemClock(),
                 keyguardUpdateMonitor,
-                { mock(DeviceEntryFingerprintAuthInteractor::class.java) },
+                { mock(DeviceEntryBiometricsAllowedInteractor::class.java) },
                 { mock(KeyguardInteractor::class.java) },
                 { mock(KeyguardTransitionInteractor::class.java) },
                 { mock(SceneInteractor::class.java) },
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 b5ca964..a95609e 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
@@ -21,7 +21,6 @@
 import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
 import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
 import com.android.systemui.flags.FakeFeatureFlags
-import com.android.systemui.keyguard.data.repository.FakeCommandQueue
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.TransitionStep
@@ -49,7 +48,6 @@
     fun create(
         featureFlags: FakeFeatureFlags = FakeFeatureFlags(),
         repository: FakeKeyguardRepository = FakeKeyguardRepository(),
-        commandQueue: FakeCommandQueue = FakeCommandQueue(),
         bouncerRepository: FakeKeyguardBouncerRepository = FakeKeyguardBouncerRepository(),
         configurationRepository: FakeConfigurationRepository = FakeConfigurationRepository(),
         shadeRepository: FakeShadeRepository = FakeShadeRepository(),
@@ -87,7 +85,6 @@
                 }
         return WithDependencies(
             repository = repository,
-            commandQueue = commandQueue,
             featureFlags = featureFlags,
             bouncerRepository = bouncerRepository,
             configurationRepository = configurationRepository,
@@ -95,7 +92,6 @@
             powerInteractor = powerInteractor,
             KeyguardInteractor(
                 repository = repository,
-                commandQueue = commandQueue,
                 powerInteractor = powerInteractor,
                 bouncerRepository = bouncerRepository,
                 configurationInteractor = ConfigurationInteractor(configurationRepository),
@@ -112,7 +108,6 @@
 
     data class WithDependencies(
         val repository: FakeKeyguardRepository,
-        val commandQueue: FakeCommandQueue,
         val featureFlags: FakeFeatureFlags,
         val bouncerRepository: FakeKeyguardBouncerRepository,
         val configurationRepository: FakeConfigurationRepository,
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 81d8f0b..5ab56e9 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
@@ -18,7 +18,6 @@
 
 import com.android.systemui.bouncer.data.repository.keyguardBouncerRepository
 import com.android.systemui.common.ui.domain.interactor.configurationInteractor
-import com.android.systemui.keyguard.data.repository.fakeCommandQueue
 import com.android.systemui.keyguard.data.repository.keyguardRepository
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
@@ -31,7 +30,6 @@
     Kosmos.Fixture {
         KeyguardInteractor(
             repository = keyguardRepository,
-            commandQueue = fakeCommandQueue,
             powerInteractor = powerInteractor,
             bouncerRepository = keyguardBouncerRepository,
             configurationInteractor = configurationInteractor,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/PartitionedGridLayoutKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/gesture/data/GestureRepositoryKosmos.kt
similarity index 62%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/PartitionedGridLayoutKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/gesture/data/GestureRepositoryKosmos.kt
index 37c9552..9bd346e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/PartitionedGridLayoutKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/gesture/data/GestureRepositoryKosmos.kt
@@ -14,11 +14,12 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.panels.domain.interactor
+package com.android.systemui.keyguard.gesture.data
 
 import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.qs.panels.ui.compose.PartitionedGridLayout
-import com.android.systemui.qs.panels.ui.viewmodel.partitionedGridViewModel
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.navigationbar.gestural.data.respository.GestureRepository
+import com.android.systemui.navigationbar.gestural.data.respository.GestureRepositoryImpl
 
-val Kosmos.partitionedGridLayout by
-    Kosmos.Fixture { PartitionedGridLayout(partitionedGridViewModel) }
+val Kosmos.gestureRepository: GestureRepository by
+    Kosmos.Fixture { GestureRepositoryImpl(testDispatcher) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/PartitionedGridLayoutKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/gesture/domain/GestureInteractorKosmos.kt
similarity index 60%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/PartitionedGridLayoutKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/gesture/domain/GestureInteractorKosmos.kt
index 37c9552..658aaa6 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/PartitionedGridLayoutKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/gesture/domain/GestureInteractorKosmos.kt
@@ -14,11 +14,14 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.panels.domain.interactor
+package com.android.systemui.keyguard.gesture.domain
 
+import com.android.systemui.keyguard.gesture.data.gestureRepository
 import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.qs.panels.ui.compose.PartitionedGridLayout
-import com.android.systemui.qs.panels.ui.viewmodel.partitionedGridViewModel
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.navigationbar.gestural.domain.GestureInteractor
 
-val Kosmos.partitionedGridLayout by
-    Kosmos.Fixture { PartitionedGridLayout(partitionedGridViewModel) }
+val Kosmos.gestureInteractor: GestureInteractor by
+    Kosmos.Fixture {
+        GestureInteractor(gestureRepository = gestureRepository, scope = applicationCoroutineScope)
+    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinderKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinderKosmos.kt
index 6eb8a49..2919d3f 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinderKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinderKosmos.kt
@@ -25,6 +25,7 @@
 import com.android.systemui.common.ui.domain.interactor.configurationInteractor
 import com.android.systemui.deviceentry.domain.interactor.deviceEntryUdfpsInteractor
 import com.android.systemui.deviceentry.ui.viewmodel.AlternateBouncerUdfpsAccessibilityOverlayViewModel
+import com.android.systemui.keyguard.dismissCallbackRegistry
 import com.android.systemui.keyguard.ui.SwipeUpAnywhereGestureHandler
 import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerDependencies
 import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerMessageAreaViewModel
@@ -49,6 +50,7 @@
             alternateBouncerDependencies = { alternateBouncerDependencies },
             windowManager = { windowManager },
             layoutInflater = { mockedLayoutInflater },
+            dismissCallbackRegistry = dismissCallbackRegistry,
         )
     }
 
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToLockscreenTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToLockscreenTransitionViewModelKosmos.kt
new file mode 100644
index 0000000..6c644ee
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToLockscreenTransitionViewModelKosmos.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
+package com.android.systemui.keyguard.ui.viewmodel
+
+import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+
+val Kosmos.alternateBouncerToLockscreenTransitionViewModel by Fixture {
+    AlternateBouncerToLockscreenTransitionViewModel(
+        animationFlow = keyguardTransitionAnimationFlow,
+    )
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt
index 3c5baa5..82860fc 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt
@@ -40,6 +40,8 @@
         keyguardTransitionInteractor = keyguardTransitionInteractor,
         notificationsKeyguardInteractor = notificationsKeyguardInteractor,
         alternateBouncerToGoneTransitionViewModel = alternateBouncerToGoneTransitionViewModel,
+        alternateBouncerToLockscreenTransitionViewModel =
+            alternateBouncerToLockscreenTransitionViewModel,
         aodToGoneTransitionViewModel = aodToGoneTransitionViewModel,
         aodToLockscreenTransitionViewModel = aodToLockscreenTransitionViewModel,
         aodToOccludedTransitionViewModel = aodToOccludedTransitionViewModel,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
index 0666820..e6bd24b 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
@@ -59,7 +59,9 @@
 import com.android.systemui.shade.data.repository.shadeRepository
 import com.android.systemui.shade.domain.interactor.shadeInteractor
 import com.android.systemui.shade.shadeController
+import com.android.systemui.shade.ui.viewmodel.notificationShadeWindowModel
 import com.android.systemui.statusbar.chips.ui.viewmodel.ongoingActivityChipsViewModel
+import com.android.systemui.statusbar.notification.domain.interactor.seenNotificationsInteractor
 import com.android.systemui.statusbar.notification.stack.domain.interactor.headsUpNotificationInteractor
 import com.android.systemui.statusbar.notification.stack.domain.interactor.sharedNotificationContainerInteractor
 import com.android.systemui.statusbar.phone.scrimController
@@ -98,6 +100,7 @@
     val communalRepository by lazy { kosmos.fakeCommunalSceneRepository }
     val communalTransitionViewModel by lazy { kosmos.communalTransitionViewModel }
     val headsUpNotificationInteractor by lazy { kosmos.headsUpNotificationInteractor }
+    val seenNotificationsInteractor by lazy { kosmos.seenNotificationsInteractor }
     val keyguardRepository by lazy { kosmos.fakeKeyguardRepository }
     val keyguardBouncerRepository by lazy { kosmos.fakeKeyguardBouncerRepository }
     val keyguardInteractor by lazy { kosmos.keyguardInteractor }
@@ -138,6 +141,7 @@
     val shadeController by lazy { kosmos.shadeController }
     val shadeRepository by lazy { kosmos.shadeRepository }
     val shadeInteractor by lazy { kosmos.shadeInteractor }
+    val notificationShadeWindowModel by lazy { kosmos.notificationShadeWindowModel }
     val wifiInteractor by lazy { kosmos.wifiInteractor }
     val fakeWifiRepository by lazy { kosmos.fakeWifiRepository }
     val volumeDialogInteractor by lazy { kosmos.volumeDialogInteractor }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/power/domain/interactor/PowerInteractorFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/power/domain/interactor/PowerInteractorFactory.kt
index d92ace9..9a07c4e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/power/domain/interactor/PowerInteractorFactory.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/power/domain/interactor/PowerInteractorFactory.kt
@@ -42,6 +42,7 @@
                     falsingCollector,
                     screenOffAnimationController,
                     statusBarStateController,
+                    mock(),
                 )
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/power/domain/interactor/PowerInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/power/domain/interactor/PowerInteractorKosmos.kt
index 8486691..d50091e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/power/domain/interactor/PowerInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/power/domain/interactor/PowerInteractorKosmos.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.power.domain.interactor
 
+import com.android.systemui.camera.cameraGestureHelper
 import com.android.systemui.classifier.falsingCollector
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.plugins.statusbar.statusBarStateController
@@ -29,5 +30,6 @@
             falsingCollector = falsingCollector,
             screenOffAnimationController = screenOffAnimationController,
             statusBarStateController = statusBarStateController,
+            cameraGestureHelper = { cameraGestureHelper },
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/GridLayoutTypeInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/GridLayoutTypeInteractorKosmos.kt
index 5568c6c..34e99d3 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/GridLayoutTypeInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/GridLayoutTypeInteractorKosmos.kt
@@ -20,24 +20,13 @@
 import com.android.systemui.qs.panels.data.repository.gridLayoutTypeRepository
 import com.android.systemui.qs.panels.shared.model.GridLayoutType
 import com.android.systemui.qs.panels.shared.model.InfiniteGridLayoutType
-import com.android.systemui.qs.panels.shared.model.PartitionedGridLayoutType
 import com.android.systemui.qs.panels.ui.compose.GridLayout
 
 val Kosmos.gridLayoutTypeInteractor by
     Kosmos.Fixture { GridLayoutTypeInteractor(gridLayoutTypeRepository) }
 
 val Kosmos.gridLayoutMap: Map<GridLayoutType, GridLayout> by
-    Kosmos.Fixture {
-        mapOf(
-            Pair(PartitionedGridLayoutType, partitionedGridLayout),
-            Pair(InfiniteGridLayoutType, infiniteGridLayout)
-        )
-    }
+    Kosmos.Fixture { mapOf(Pair(InfiniteGridLayoutType, infiniteGridLayout)) }
 
 var Kosmos.gridConsistencyInteractorsMap: Map<GridLayoutType, GridTypeConsistencyInteractor> by
-    Kosmos.Fixture {
-        mapOf(
-            Pair(PartitionedGridLayoutType, noopGridConsistencyInteractor),
-            Pair(InfiniteGridLayoutType, infiniteGridConsistencyInteractor)
-        )
-    }
+    Kosmos.Fixture { mapOf(Pair(InfiniteGridLayoutType, infiniteGridConsistencyInteractor)) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/TileGridViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/TileGridViewModelKosmos.kt
index 6625bb5..9481fca 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/TileGridViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/TileGridViewModelKosmos.kt
@@ -20,7 +20,7 @@
 import com.android.systemui.kosmos.applicationCoroutineScope
 import com.android.systemui.qs.panels.domain.interactor.gridLayoutMap
 import com.android.systemui.qs.panels.domain.interactor.gridLayoutTypeInteractor
-import com.android.systemui.qs.panels.domain.interactor.partitionedGridLayout
+import com.android.systemui.qs.panels.domain.interactor.infiniteGridLayout
 import com.android.systemui.qs.pipeline.domain.interactor.currentTilesInteractor
 
 val Kosmos.tileGridViewModel by
@@ -29,7 +29,7 @@
             gridLayoutTypeInteractor,
             gridLayoutMap,
             currentTilesInteractor,
-            partitionedGridLayout,
+            infiniteGridLayout,
             applicationCoroutineScope,
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractorKosmos.kt
new file mode 100644
index 0000000..2ecfb45
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractorKosmos.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.impl.modes.domain.interactor
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.qs.tiles.base.actions.qsTileIntentUserInputHandler
+import com.android.systemui.statusbar.policy.ui.dialog.modesDialogDelegate
+import javax.inject.Provider
+
+val Kosmos.modesTileUserActionInteractor: ModesTileUserActionInteractor by
+    Kosmos.Fixture {
+        ModesTileUserActionInteractor(
+            qsTileIntentUserInputHandler,
+            Provider { modesDialogDelegate }.get(),
+        )
+    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneViewModelKosmos.kt
index c56c56c..299b22e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneViewModelKosmos.kt
@@ -17,7 +17,6 @@
 package com.android.systemui.qs.ui.viewmodel
 
 import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.kosmos.applicationCoroutineScope
 import com.android.systemui.shade.domain.interactor.shadeInteractor
 import com.android.systemui.shade.ui.viewmodel.overlayShadeViewModel
 
@@ -27,6 +26,5 @@
             shadeInteractor = shadeInteractor,
             overlayShadeViewModel = overlayShadeViewModel,
             quickSettingsContainerViewModel = quickSettingsContainerViewModel,
-            applicationScope = applicationCoroutineScope,
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneKosmos.kt
index fff3b14..dd93141 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneKosmos.kt
@@ -2,7 +2,6 @@
 
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.Kosmos.Fixture
-import com.android.systemui.kosmos.testScope
 import com.android.systemui.scene.shared.model.FakeScene
 import com.android.systemui.scene.shared.model.SceneContainerConfig
 import com.android.systemui.scene.shared.model.Scenes
@@ -18,16 +17,7 @@
     )
 }
 
-val Kosmos.fakeScenes by Fixture {
-    sceneKeys
-        .map { key ->
-            FakeScene(
-                scope = testScope.backgroundScope,
-                key = key,
-            )
-        }
-        .toSet()
-}
+val Kosmos.fakeScenes by Fixture { sceneKeys.map { key -> FakeScene(key) }.toSet() }
 
 val Kosmos.scenes by Fixture { fakeScenes }
 
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/startable/SceneContainerStartableKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/startable/SceneContainerStartableKosmos.kt
index 03a42bc..8e76a0b 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/startable/SceneContainerStartableKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/startable/SceneContainerStartableKosmos.kt
@@ -34,6 +34,7 @@
 import com.android.systemui.kosmos.Kosmos.Fixture
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.model.sysUiState
+import com.android.systemui.plugins.statusbar.statusBarStateController
 import com.android.systemui.power.domain.interactor.powerInteractor
 import com.android.systemui.scene.domain.interactor.sceneBackInteractor
 import com.android.systemui.scene.domain.interactor.sceneContainerOcclusionInteractor
@@ -46,6 +47,7 @@
 import com.android.systemui.statusbar.notificationShadeWindowController
 import com.android.systemui.statusbar.phone.centralSurfacesOptional
 import com.android.systemui.statusbar.policy.domain.interactor.deviceProvisioningInteractor
+import com.android.systemui.statusbar.sysuiStatusBarStateController
 
 val Kosmos.sceneContainerStartable by Fixture {
     SceneContainerStartable(
@@ -77,5 +79,6 @@
         windowMgrLockscreenVisInteractor = windowManagerLockscreenVisibilityInteractor,
         keyguardEnabledInteractor = keyguardEnabledInteractor,
         dismissCallbackRegistry = dismissCallbackRegistry,
+        statusBarStateController = sysuiStatusBarStateController,
     )
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/model/FakeScene.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/model/FakeScene.kt
index eeaa9db..64e3526 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/model/FakeScene.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/model/FakeScene.kt
@@ -19,16 +19,12 @@
 import com.android.compose.animation.scene.SceneKey
 import com.android.compose.animation.scene.UserAction
 import com.android.compose.animation.scene.UserActionResult
-import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.channels.Channel
-import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.onCompletion
 import kotlinx.coroutines.flow.onStart
 import kotlinx.coroutines.flow.receiveAsFlow
-import kotlinx.coroutines.flow.stateIn
 
 class FakeScene(
-    val scope: CoroutineScope,
     override val key: SceneKey,
 ) : Scene {
     var isDestinationScenesBeingCollected = false
@@ -40,11 +36,6 @@
             .receiveAsFlow()
             .onStart { isDestinationScenesBeingCollected = true }
             .onCompletion { isDestinationScenesBeingCollected = false }
-            .stateIn(
-                scope = scope,
-                started = SharingStarted.WhileSubscribed(),
-                initialValue = emptyMap(),
-            )
 
     suspend fun setDestinationScenes(value: Map<UserAction, UserActionResult>) {
         destinationScenesChannel.send(value)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/settings/FakeUserTracker.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/settings/FakeUserTracker.kt
index 2ca338a..f3d5b7d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/settings/FakeUserTracker.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/settings/FakeUserTracker.kt
@@ -33,7 +33,7 @@
     private var _userHandle: UserHandle = UserHandle.of(_userId),
     private var _userInfo: UserInfo = mock(),
     private var _userProfiles: List<UserInfo> = emptyList(),
-    userContentResolver: ContentResolver = MockContentResolver(),
+    userContentResolverProvider: () -> ContentResolver = { MockContentResolver() },
     userContext: Context = mock(),
     private val onCreateCurrentUserContext: (Context) -> Context = { mock() },
 ) : UserTracker {
@@ -41,14 +41,19 @@
 
     override val userId: Int
         get() = _userId
+
     override val userHandle: UserHandle
         get() = _userHandle
+
     override val userInfo: UserInfo
         get() = _userInfo
+
     override val userProfiles: List<UserInfo>
         get() = _userProfiles
 
-    override val userContentResolver: ContentResolver = userContentResolver
+    // userContentResolver is lazy because Ravenwood doesn't support MockContentResolver()
+    // and we still want to allow people use this class for tests that don't use it.
+    override val userContentResolver: ContentResolver by lazy { userContentResolverProvider() }
     override val userContext: Context = userContext
 
     override fun addCallback(callback: UserTracker.Callback, executor: Executor) {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/startable/ShadeStartableKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/startable/ShadeStartableKosmos.kt
index 79b80bc..a1f157f1 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/startable/ShadeStartableKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/startable/ShadeStartableKosmos.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.shade.domain.startable
 
 import android.content.applicationContext
+import com.android.systemui.biometrics.domain.interactor.displayStateInteractor
 import com.android.systemui.common.ui.data.repository.configurationRepository
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.Kosmos.Fixture
@@ -28,6 +29,7 @@
 import com.android.systemui.shade.domain.interactor.panelExpansionInteractor
 import com.android.systemui.shade.transition.ScrimShadeTransitionController
 import com.android.systemui.statusbar.notification.stack.notificationStackScrollLayoutController
+import com.android.systemui.statusbar.phone.scrimController
 import com.android.systemui.statusbar.policy.splitShadeStateController
 import com.android.systemui.statusbar.pulseExpansionHandler
 import com.android.systemui.util.mockito.mock
@@ -48,6 +50,8 @@
         panelExpansionInteractorProvider = { panelExpansionInteractor },
         shadeExpansionStateManager = shadeExpansionStateManager,
         pulseExpansionHandler = pulseExpansionHandler,
+        displayStateInteractor = displayStateInteractor,
         nsslc = notificationStackScrollLayoutController,
+        scrimController = scrimController,
     )
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/PartitionedGridLayoutKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeWindowModelKosmos.kt
similarity index 67%
rename from packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/PartitionedGridLayoutKosmos.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeWindowModelKosmos.kt
index 37c9552..cd4fab8 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/PartitionedGridLayoutKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeWindowModelKosmos.kt
@@ -14,11 +14,10 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.panels.domain.interactor
+package com.android.systemui.shade.ui.viewmodel
 
+import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
 import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.qs.panels.ui.compose.PartitionedGridLayout
-import com.android.systemui.qs.panels.ui.viewmodel.partitionedGridViewModel
 
-val Kosmos.partitionedGridLayout by
-    Kosmos.Fixture { PartitionedGridLayout(partitionedGridViewModel) }
+val Kosmos.notificationShadeWindowModel: NotificationShadeWindowModel by
+    Kosmos.Fixture { NotificationShadeWindowModel(keyguardTransitionInteractor) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelKosmos.kt
index 989c3a5..2c5a0f4 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelKosmos.kt
@@ -17,7 +17,6 @@
 package com.android.systemui.shade.ui.viewmodel
 
 import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.kosmos.applicationCoroutineScope
 import com.android.systemui.media.controls.domain.pipeline.interactor.mediaCarouselInteractor
 import com.android.systemui.qs.footerActionsController
 import com.android.systemui.qs.footerActionsViewModelFactory
@@ -30,7 +29,6 @@
 val Kosmos.shadeSceneViewModel: ShadeSceneViewModel by
     Kosmos.Fixture {
         ShadeSceneViewModel(
-            applicationScope = applicationCoroutineScope,
             shadeHeaderViewModel = shadeHeaderViewModel,
             qsSceneAdapter = qsSceneAdapter,
             brightnessMirrorViewModel = brightnessMirrorViewModel,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shared/notifications/data/repository/NotificationSettingsRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shared/notifications/data/repository/NotificationSettingsRepositoryKosmos.kt
index a75d2bc..6373851 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shared/notifications/data/repository/NotificationSettingsRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shared/notifications/data/repository/NotificationSettingsRepositoryKosmos.kt
@@ -20,12 +20,14 @@
 import com.android.systemui.kosmos.testDispatcher
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.shared.settings.data.repository.secureSettingsRepository
+import com.android.systemui.shared.settings.data.repository.systemSettingsRepository
 
 val Kosmos.notificationSettingsRepository by
     Kosmos.Fixture {
         NotificationSettingsRepository(
-            scope = testScope.backgroundScope,
+            backgroundScope = testScope.backgroundScope,
             backgroundDispatcher = testDispatcher,
             secureSettingsRepository = secureSettingsRepository,
+            systemSettingsRepository = systemSettingsRepository,
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/PartitionedGridLayoutKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shared/settings/data/repository/SystemSettingsRepositoryKosmos.kt
similarity index 67%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/PartitionedGridLayoutKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/shared/settings/data/repository/SystemSettingsRepositoryKosmos.kt
index 37c9552..01f19ae 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/PartitionedGridLayoutKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shared/settings/data/repository/SystemSettingsRepositoryKosmos.kt
@@ -14,11 +14,10 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.panels.domain.interactor
+package com.android.systemui.shared.settings.data.repository
 
 import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.qs.panels.ui.compose.PartitionedGridLayout
-import com.android.systemui.qs.panels.ui.viewmodel.partitionedGridViewModel
 
-val Kosmos.partitionedGridLayout by
-    Kosmos.Fixture { PartitionedGridLayout(partitionedGridViewModel) }
+var Kosmos.systemSettingsRepository: SystemSettingsRepository by
+    Kosmos.Fixture { fakeSystemSettingsRepository }
+val Kosmos.fakeSystemSettingsRepository by Kosmos.Fixture { FakeSystemSettingsRepository() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/coordinator/LockScreenMinimalismCoordinatorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/coordinator/LockScreenMinimalismCoordinatorKosmos.kt
index 77d97bb..933ebf0 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/coordinator/LockScreenMinimalismCoordinatorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/coordinator/LockScreenMinimalismCoordinatorKosmos.kt
@@ -18,23 +18,19 @@
 
 import com.android.systemui.dump.dumpManager
 import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.kosmos.testDispatcher
 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.domain.interactor.seenNotificationsInteractor
 import com.android.systemui.statusbar.notification.stack.domain.interactor.headsUpNotificationInteractor
-import com.android.systemui.util.settings.fakeSettings
 
 var Kosmos.lockScreenMinimalismCoordinator by
     Kosmos.Fixture {
         LockScreenMinimalismCoordinator(
-            bgDispatcher = testDispatcher,
             dumpManager = dumpManager,
             headsUpInteractor = headsUpNotificationInteractor,
             logger = lockScreenMinimalismCoordinatorLogger,
             scope = testScope.backgroundScope,
-            secureSettings = fakeSettings,
             seenNotificationsInteractor = seenNotificationsInteractor,
             statusBarStateController = statusBarStateController,
             shadeInteractor = shadeInteractor,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/model/ActiveNotificationModelBuilder.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/model/ActiveNotificationModelBuilder.kt
index 9c5c486..76bdc0d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/model/ActiveNotificationModelBuilder.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/model/ActiveNotificationModelBuilder.kt
@@ -16,14 +16,18 @@
 
 package com.android.systemui.statusbar.notification.data.model
 
+import android.app.PendingIntent
 import android.graphics.drawable.Icon
+import com.android.systemui.statusbar.StatusBarIconView
 import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel
+import com.android.systemui.statusbar.notification.shared.CallType
 import com.android.systemui.statusbar.notification.stack.BUCKET_UNKNOWN
 
 /** Simple ActiveNotificationModel builder for use in tests. */
 fun activeNotificationModel(
     key: String,
     groupKey: String? = null,
+    whenTime: Long = 0L,
     isAmbient: Boolean = false,
     isRowDismissed: Boolean = false,
     isSilent: Boolean = false,
@@ -33,15 +37,19 @@
     aodIcon: Icon? = null,
     shelfIcon: Icon? = null,
     statusBarIcon: Icon? = null,
+    statusBarChipIcon: StatusBarIconView? = null,
     uid: Int = 0,
     instanceId: Int? = null,
     isGroupSummary: Boolean = false,
     packageName: String = "pkg",
+    contentIntent: PendingIntent? = null,
     bucket: Int = BUCKET_UNKNOWN,
+    callType: CallType = CallType.None,
 ) =
     ActiveNotificationModel(
         key = key,
         groupKey = groupKey,
+        whenTime = whenTime,
         isAmbient = isAmbient,
         isRowDismissed = isRowDismissed,
         isSilent = isSilent,
@@ -51,9 +59,12 @@
         aodIcon = aodIcon,
         shelfIcon = shelfIcon,
         statusBarIcon = statusBarIcon,
+        statusBarChipIconView = statusBarChipIcon,
         uid = uid,
         packageName = packageName,
+        contentIntent = contentIntent,
         instanceId = instanceId,
         isGroupSummary = isGroupSummary,
         bucket = bucket,
+        callType = callType,
     )
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/domain/interactor/SeenNotificationsInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/domain/interactor/SeenNotificationsInteractorKosmos.kt
index c1e0419..b19e221 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/domain/interactor/SeenNotificationsInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/domain/interactor/SeenNotificationsInteractorKosmos.kt
@@ -16,12 +16,32 @@
 
 package com.android.systemui.statusbar.notification.domain.interactor
 
+import android.os.UserHandle
+import android.provider.Settings
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.kosmos.testDispatcher
 import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository
+import com.android.systemui.util.settings.fakeSettings
 
 val Kosmos.seenNotificationsInteractor by Fixture {
     SeenNotificationsInteractor(
+        bgDispatcher = testDispatcher,
         notificationListRepository = activeNotificationListRepository,
+        secureSettings = fakeSettings,
     )
 }
+
+var Kosmos.lockScreenShowOnlyUnseenNotificationsSetting: Boolean
+    get() =
+        fakeSettings.getIntForUser(
+            Settings.Secure.LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS,
+            UserHandle.USER_CURRENT,
+        ) == 1
+    set(value) {
+        fakeSettings.putIntForUser(
+            Settings.Secure.LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS,
+            if (value) 1 else 2,
+            UserHandle.USER_CURRENT,
+        )
+    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt
index 16dc50f..0b309b5 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt
@@ -27,6 +27,7 @@
 import androidx.core.os.bundleOf
 import com.android.internal.config.sysui.SystemUiDeviceConfigFlags
 import com.android.internal.logging.MetricsLogger
+import com.android.internal.logging.UiEventLogger
 import com.android.internal.statusbar.IStatusBarService
 import com.android.systemui.TestableDependency
 import com.android.systemui.classifier.FalsingManagerFake
@@ -318,14 +319,9 @@
         // NOTE: This flag is read when the ExpandableNotificationRow is inflated, so it needs to be
         //  set, but we do not want to override an existing value that is needed by a specific test.
 
-        val rowFuture: SettableFuture<ExpandableNotificationRow> = SettableFuture.create()
         val rowInflaterTask =
             RowInflaterTask(mFakeSystemClock, Mockito.mock(RowInflaterTaskLogger::class.java))
-        rowInflaterTask.inflate(context, null, entry, MoreExecutors.directExecutor()) { inflatedRow
-            ->
-            rowFuture.set(inflatedRow)
-        }
-        val row = rowFuture.get(1, TimeUnit.SECONDS)
+        val row = rowInflaterTask.inflateSynchronously(context, null, entry)
 
         entry.row = row
         mIconManager.createIcons(entry)
@@ -357,7 +353,8 @@
             mSmartReplyConstants,
             mSmartReplyController,
             featureFlags,
-            Mockito.mock(IStatusBarService::class.java)
+            Mockito.mock(IStatusBarService::class.java),
+            Mockito.mock(UiEventLogger::class.java)
         )
         row.setAboveShelfChangedListener { aboveShelf: Boolean -> }
         mBindStage.getStageParams(entry).requireContentViews(extraInflationFlags)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepositoryKosmos.kt
index 12014a0..faa2b33 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepositoryKosmos.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.statusbar.phone.ongoingcall.data.repository
 
 import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.log.logcatLogBuffer
 
 val Kosmos.ongoingCallRepository: OngoingCallRepository by
-    Kosmos.Fixture { OngoingCallRepository() }
+    Kosmos.Fixture { OngoingCallRepository(logcatLogBuffer("OngoingCallRepositoryKosmos")) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ongoingcall/shared/model/OngoingCallModelBuilder.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ongoingcall/shared/model/OngoingCallModelBuilder.kt
new file mode 100644
index 0000000..3963d7c
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ongoingcall/shared/model/OngoingCallModelBuilder.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone.ongoingcall.shared.model
+
+import android.app.PendingIntent
+import com.android.systemui.statusbar.StatusBarIconView
+
+/** Helper for building [OngoingCallModel.InCall] instances in tests. */
+fun inCallModel(
+    startTimeMs: Long,
+    notificationIcon: StatusBarIconView? = null,
+    intent: PendingIntent? = null
+) = OngoingCallModel.InCall(startTimeMs, notificationIcon, intent)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorKosmos.kt
index 78242b6..aef0828 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorKosmos.kt
@@ -18,10 +18,12 @@
 
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.shared.notifications.data.repository.notificationSettingsRepository
 import com.android.systemui.statusbar.policy.data.repository.zenModeRepository
 
 val Kosmos.zenModeInteractor by Fixture {
     ZenModeInteractor(
-        repository = zenModeRepository,
+        zenModeRepository = zenModeRepository,
+        notificationSettingsRepository = notificationSettingsRepository,
     )
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogDelegateKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogDelegateKosmos.kt
new file mode 100644
index 0000000..99bb479
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogDelegateKosmos.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.policy.ui.dialog
+
+import com.android.systemui.animation.dialogTransitionAnimator
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.mainCoroutineContext
+import com.android.systemui.plugins.activityStarter
+import com.android.systemui.statusbar.phone.systemUIDialogFactory
+import com.android.systemui.statusbar.policy.ui.dialog.viewmodel.modesDialogViewModel
+import com.android.systemui.util.mockito.mock
+
+val Kosmos.mockModesDialogDelegate by Kosmos.Fixture { mock<ModesDialogDelegate>() }
+
+var Kosmos.modesDialogDelegate: ModesDialogDelegate by
+    Kosmos.Fixture {
+        ModesDialogDelegate(
+            systemUIDialogFactory,
+            dialogTransitionAnimator,
+            activityStarter,
+            { modesDialogViewModel },
+            mainCoroutineContext,
+        )
+    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModesDialogViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModesDialogViewModelKosmos.kt
new file mode 100644
index 0000000..00020f8
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModesDialogViewModelKosmos.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.policy.ui.dialog.viewmodel
+
+import android.content.mockedContext
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.statusbar.policy.domain.interactor.zenModeInteractor
+import com.android.systemui.statusbar.policy.ui.dialog.modesDialogDelegate
+import javax.inject.Provider
+
+val Kosmos.modesDialogViewModel: ModesDialogViewModel by
+    Kosmos.Fixture {
+        ModesDialogViewModel(
+            mockedContext,
+            zenModeInteractor,
+            testDispatcher,
+            Provider { modesDialogDelegate }.get(),
+        )
+    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeGlobalSettings.java b/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeGlobalSettings.java
index 476b7d8..65f4122 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeGlobalSettings.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeGlobalSettings.java
@@ -38,6 +38,11 @@
 
     public static final Uri CONTENT_URI = Uri.parse("content://settings/fake_global");
 
+    /**
+     * @deprecated Please use FakeGlobalSettings(testDispatcher) to provide the same dispatcher used
+     * by main test scope.
+     */
+    @Deprecated
     public FakeGlobalSettings() {
         mDispatcher = StandardTestDispatcher(/* scheduler = */ null, /* name = */ null);
     }
@@ -46,6 +51,7 @@
         mDispatcher = dispatcher;
     }
 
+    @NonNull
     @Override
     public ContentResolver getContentResolver() {
         throw new UnsupportedOperationException(
@@ -53,6 +59,7 @@
                         + "GlobalSettings.registerContentObserver helpful instead.");
     }
 
+    @NonNull
     @Override
     public CoroutineDispatcher getBackgroundDispatcher() {
         return mDispatcher;
@@ -60,7 +67,7 @@
 
     @Override
     public void registerContentObserverSync(Uri uri, boolean notifyDescendants,
-            ContentObserver settingsObserver) {
+            @NonNull ContentObserver settingsObserver) {
         List<ContentObserver> observers;
         mContentObserversAllUsers.putIfAbsent(uri.toString(), new ArrayList<>());
         observers = mContentObserversAllUsers.get(uri.toString());
@@ -68,25 +75,26 @@
     }
 
     @Override
-    public void unregisterContentObserverSync(ContentObserver settingsObserver) {
+    public void unregisterContentObserverSync(@NonNull ContentObserver settingsObserver) {
         for (Map.Entry<String, List<ContentObserver>> entry :
                 mContentObserversAllUsers.entrySet()) {
             entry.getValue().remove(settingsObserver);
         }
     }
 
+    @NonNull
     @Override
-    public Uri getUriFor(String name) {
+    public Uri getUriFor(@NonNull String name) {
         return Uri.withAppendedPath(CONTENT_URI, name);
     }
 
     @Override
-    public String getString(String name) {
+    public String getString(@NonNull String name) {
         return mValues.get(getUriFor(name).toString());
     }
 
     @Override
-    public boolean putString(String name, String value) {
+    public boolean putString(@NonNull String name, @NonNull String value) {
         return putString(name, value, null, false);
     }
 
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeGlobalSettingsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeGlobalSettingsKosmos.kt
index df6fc41..35fa2af 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeGlobalSettingsKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeGlobalSettingsKosmos.kt
@@ -18,5 +18,6 @@
 
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.kosmos.testDispatcher
 
-val Kosmos.fakeGlobalSettings: FakeGlobalSettings by Fixture { FakeGlobalSettings() }
+val Kosmos.fakeGlobalSettings: FakeGlobalSettings by Fixture { FakeGlobalSettings(testDispatcher) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettings.java b/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettings.java
index e35da11..d117466 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettings.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettings.java
@@ -26,8 +26,7 @@
 import android.util.Pair;
 
 import androidx.annotation.NonNull;
-
-import com.android.systemui.settings.UserTracker;
+import androidx.annotation.VisibleForTesting;
 
 import kotlinx.coroutines.CoroutineDispatcher;
 
@@ -47,44 +46,64 @@
     @UserIdInt
     private int mUserId = UserHandle.USER_CURRENT;
 
+    private final CurrentUserIdProvider mCurrentUserProvider;
+
+    /**
+     * @deprecated Please use FakeSettings(testDispatcher) to provide the same dispatcher used
+     * by main test scope.
+     */
+    @Deprecated
     public FakeSettings() {
         mDispatcher = StandardTestDispatcher(/* scheduler = */ null, /* name = */ null);
+        mCurrentUserProvider = () -> mUserId;
     }
 
     public FakeSettings(CoroutineDispatcher dispatcher) {
         mDispatcher = dispatcher;
+        mCurrentUserProvider = () -> mUserId;
     }
 
-    public FakeSettings(String initialKey, String initialValue) {
-        mDispatcher = StandardTestDispatcher(/* scheduler = */ null, /* name = */ null);
+    public FakeSettings(CoroutineDispatcher dispatcher, CurrentUserIdProvider currentUserProvider) {
+        mDispatcher = dispatcher;
+        mCurrentUserProvider = currentUserProvider;
+    }
+
+    @VisibleForTesting
+    FakeSettings(String initialKey, String initialValue) {
+        this();
         putString(initialKey, initialValue);
     }
 
-    public FakeSettings(Map<String, String> initialValues) {
-        mDispatcher = StandardTestDispatcher(/* scheduler = */ null, /* name = */ null);
+    @VisibleForTesting
+    FakeSettings(Map<String, String> initialValues) {
+        this();
         for (Map.Entry<String, String> kv : initialValues.entrySet()) {
             putString(kv.getKey(), kv.getValue());
         }
     }
 
     @Override
+    @NonNull
     public ContentResolver getContentResolver() {
-        return null;
+        throw new UnsupportedOperationException(
+                "FakeSettings.getContentResolver is not implemented");
     }
 
+    @NonNull
     @Override
-    public UserTracker getUserTracker() {
-        return null;
+    public CurrentUserIdProvider getCurrentUserProvider() {
+        return mCurrentUserProvider;
     }
 
+    @NonNull
     @Override
     public CoroutineDispatcher getBackgroundDispatcher() {
         return mDispatcher;
     }
 
     @Override
-    public void registerContentObserverForUserSync(Uri uri, boolean notifyDescendants,
-            ContentObserver settingsObserver, int userHandle) {
+    public void registerContentObserverForUserSync(@NonNull Uri uri, boolean notifyDescendants,
+            @NonNull ContentObserver settingsObserver, int userHandle) {
         List<ContentObserver> observers;
         if (userHandle == UserHandle.USER_ALL) {
             mContentObserversAllUsers.putIfAbsent(uri.toString(), new ArrayList<>());
@@ -98,19 +117,18 @@
     }
 
     @Override
-    public void unregisterContentObserverSync(ContentObserver settingsObserver) {
-        for (SettingsKey key : mContentObservers.keySet()) {
-            List<ContentObserver> observers = mContentObservers.get(key);
+    public void unregisterContentObserverSync(@NonNull ContentObserver settingsObserver) {
+        for (List<ContentObserver> observers : mContentObservers.values()) {
             observers.remove(settingsObserver);
         }
-        for (String key : mContentObserversAllUsers.keySet()) {
-            List<ContentObserver> observers = mContentObserversAllUsers.get(key);
+        for (List<ContentObserver> observers : mContentObserversAllUsers.values()) {
             observers.remove(settingsObserver);
         }
     }
 
+    @NonNull
     @Override
-    public Uri getUriFor(String name) {
+    public Uri getUriFor(@NonNull String name) {
         return Uri.withAppendedPath(CONTENT_URI, name);
     }
 
@@ -124,33 +142,34 @@
     }
 
     @Override
-    public String getString(String name) {
+    public String getString(@NonNull String name) {
         return getStringForUser(name, getUserId());
     }
 
     @Override
-    public String getStringForUser(String name, int userHandle) {
+    public String getStringForUser(@NonNull String name, int userHandle) {
         return mValues.get(new SettingsKey(userHandle, getUriFor(name).toString()));
     }
 
     @Override
-    public boolean putString(String name, String value, boolean overrideableByRestore) {
+    public boolean putString(@NonNull String name, @NonNull String value,
+            boolean overrideableByRestore) {
         return putStringForUser(name, value, null, false, getUserId(), overrideableByRestore);
     }
 
     @Override
-    public boolean putString(String name, String value) {
+    public boolean putString(@NonNull String name, @NonNull String value) {
         return putString(name, value, false);
     }
 
     @Override
-    public boolean putStringForUser(String name, String value, int userHandle) {
+    public boolean putStringForUser(@NonNull String name, @NonNull String value, int userHandle) {
         return putStringForUser(name, value, null, false, userHandle, false);
     }
 
     @Override
-    public boolean putStringForUser(String name, String value, String tag, boolean makeDefault,
-            int userHandle, boolean overrideableByRestore) {
+    public boolean putStringForUser(@NonNull String name, @NonNull String value, String tag,
+            boolean makeDefault, int userHandle, boolean overrideableByRestore) {
         SettingsKey key = new SettingsKey(userHandle, getUriFor(name).toString());
         mValues.put(key, value);
 
@@ -166,7 +185,8 @@
     }
 
     @Override
-    public boolean putString(@NonNull String name, String value, String tag, boolean makeDefault) {
+    public boolean putString(@NonNull String name, @NonNull String value, @NonNull String tag,
+            boolean makeDefault) {
         return putString(name, value);
     }
 
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettingsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettingsKosmos.kt
index bcb5848..76ef202 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettingsKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettingsKosmos.kt
@@ -18,5 +18,9 @@
 
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.settings.userTracker
 
-val Kosmos.fakeSettings: FakeSettings by Fixture { FakeSettings() }
+val Kosmos.fakeSettings: FakeSettings by Fixture {
+    FakeSettings(testDispatcher) { userTracker.userId }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/FakeAudioSharingRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/FakeAudioSharingRepository.kt
index d391750..0a617d1 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/FakeAudioSharingRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/FakeAudioSharingRepository.kt
@@ -16,10 +16,7 @@
 
 package com.android.systemui.volume.data.repository
 
-import androidx.annotation.IntRange
 import com.android.settingslib.volume.data.repository.AudioSharingRepository
-import com.android.settingslib.volume.data.repository.AudioSharingRepository.Companion.AUDIO_SHARING_VOLUME_MAX
-import com.android.settingslib.volume.data.repository.AudioSharingRepository.Companion.AUDIO_SHARING_VOLUME_MIN
 import com.android.settingslib.volume.data.repository.GroupIdToVolumes
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
@@ -27,11 +24,14 @@
 
 class FakeAudioSharingRepository : AudioSharingRepository {
     private val mutableInAudioSharing: MutableStateFlow<Boolean> = MutableStateFlow(false)
+    private val mutablePrimaryGroupId: MutableStateFlow<Int> =
+        MutableStateFlow(TEST_GROUP_ID_INVALID)
     private val mutableSecondaryGroupId: MutableStateFlow<Int> =
         MutableStateFlow(TEST_GROUP_ID_INVALID)
     private val mutableVolumeMap: MutableStateFlow<GroupIdToVolumes> = MutableStateFlow(emptyMap())
 
     override val inAudioSharing: Flow<Boolean> = mutableInAudioSharing
+    override val primaryGroupId: StateFlow<Int> = mutablePrimaryGroupId
     override val secondaryGroupId: StateFlow<Int> = mutableSecondaryGroupId
     override val volumeMap: StateFlow<GroupIdToVolumes> = mutableVolumeMap
 
@@ -41,6 +41,10 @@
         mutableInAudioSharing.value = state
     }
 
+    fun setPrimaryGroupId(groupId: Int) {
+        mutablePrimaryGroupId.value = groupId
+    }
+
     fun setSecondaryGroupId(groupId: Int) {
         mutableSecondaryGroupId.value = groupId
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/domain/interactor/AudioSharingInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/domain/interactor/AudioSharingInteractorKosmos.kt
index 03981bb..ce8aba5 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/domain/interactor/AudioSharingInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/domain/interactor/AudioSharingInteractorKosmos.kt
@@ -18,12 +18,15 @@
 
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.kosmos.backgroundCoroutineContext
 import com.android.systemui.volume.data.repository.audioSharingRepository
 
 val Kosmos.audioSharingInteractor by
     Kosmos.Fixture {
         AudioSharingInteractorImpl(
             applicationCoroutineScope,
+            backgroundCoroutineContext,
+            audioVolumeInteractor,
             audioSharingRepository,
         )
     }
diff --git a/packages/VpnDialogs/res/values-fr-rCA/strings.xml b/packages/VpnDialogs/res/values-fr-rCA/strings.xml
index 9ae0cb2..3118ffe 100644
--- a/packages/VpnDialogs/res/values-fr-rCA/strings.xml
+++ b/packages/VpnDialogs/res/values-fr-rCA/strings.xml
@@ -24,7 +24,7 @@
     <string name="duration" msgid="3584782459928719435">"Durée :"</string>
     <string name="data_transmitted" msgid="7988167672982199061">"Date d\'envoi :"</string>
     <string name="data_received" msgid="4062776929376067820">"Reçu le :"</string>
-    <string name="data_value_format" msgid="2192466557826897580">"<xliff:g id="NUMBER_0">%1$s</xliff:g> octets / <xliff:g id="NUMBER_1">%2$s</xliff:g> paquets"</string>
+    <string name="data_value_format" msgid="2192466557826897580">"<xliff:g id="NUMBER_0">%1$s</xliff:g> octets/<xliff:g id="NUMBER_1">%2$s</xliff:g> paquets"</string>
     <string name="always_on_disconnected_title" msgid="1906740176262776166">"Impossible de se connecter au RPV permanent"</string>
     <string name="always_on_disconnected_message" msgid="555634519845992917">"<xliff:g id="VPN_APP_0">%1$s</xliff:g> est configuré pour rester connecté en permanence, mais n\'arrive pas à se connecter en ce moment. Votre téléphone utilisera un réseau public jusqu\'à ce qu\'il puisse se reconnecter à <xliff:g id="VPN_APP_1">%1$s</xliff:g>."</string>
     <string name="always_on_disconnected_message_lockdown" msgid="4232225539869452120">"<xliff:g id="VPN_APP">%1$s</xliff:g> est configuré pour rester connecté en permanence, mais n\'arrive pas à se connecter en ce moment. Vous n\'aurez pas de connexion jusqu\'à ce que le RPV arrive à se reconnecter."</string>
diff --git a/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java b/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java
index cbbce1a..8663593 100644
--- a/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java
+++ b/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java
@@ -120,6 +120,14 @@
     static final String SYSTEM_GENERATION = "system_gen";
     static final String LOCK_GENERATION = "lock_gen";
 
+    static final String DEVICE_CONFIG_WIDTH = "device_config_width";
+
+    static final String DEVICE_CONFIG_HEIGHT = "device_config_height";
+
+    static final String DEVICE_CONFIG_SECONDARY_WIDTH = "device_config_secondary_width";
+
+    static final String DEVICE_CONFIG_SECONDARY_HEIGHT = "device_config_secondary_height";
+
     static final float DEFAULT_ACCEPTABLE_PARALLAX = 0.2f;
 
     // If this file exists, it means we exceeded our quota last time
@@ -175,6 +183,16 @@
             // disk churn.
             final int lastSysGeneration = sharedPrefs.getInt(SYSTEM_GENERATION, /* defValue= */ -1);
             final int lastLockGeneration = sharedPrefs.getInt(LOCK_GENERATION, /* defValue= */ -1);
+
+            final int deviceConfigWidth = sharedPrefs.getInt(
+                    DEVICE_CONFIG_WIDTH, /* defValue= */ -1);
+            final int deviceConfigHeight = sharedPrefs.getInt(
+                    DEVICE_CONFIG_HEIGHT, /* defValue= */ -1);
+            final int deviceConfigSecondaryWidth = sharedPrefs.getInt(
+                    DEVICE_CONFIG_SECONDARY_WIDTH, /* defValue= */ -1);
+            final int deviceConfigSecondaryHeight = sharedPrefs.getInt(
+                    DEVICE_CONFIG_SECONDARY_HEIGHT, /* defValue= */ -1);
+
             final int sysGeneration = mWallpaperManager.getWallpaperId(FLAG_SYSTEM);
             final int lockGeneration = mWallpaperManager.getWallpaperId(FLAG_LOCK);
             final boolean sysChanged = (sysGeneration != lastSysGeneration);
@@ -195,7 +213,11 @@
             backupWallpaperInfoFile(/* sysOrLockChanged= */ sysChanged || lockChanged, data);
             backupSystemWallpaperFile(sharedPrefs, sysChanged, sysGeneration, data);
             backupLockWallpaperFileIfItExists(sharedPrefs, lockChanged, lockGeneration, data);
-            backupDeviceInfoFile(data);
+
+            final boolean isDeviceConfigChanged = isDeviceConfigChanged(deviceConfigWidth,
+                    deviceConfigHeight, deviceConfigSecondaryWidth, deviceConfigSecondaryHeight);
+
+            backupDeviceInfoFile(sharedPrefs, isDeviceConfigChanged, data);
         } catch (Exception e) {
             Slog.e(TAG, "Unable to back up wallpaper", e);
             mEventLogger.onBackupException(e);
@@ -209,50 +231,72 @@
         }
     }
 
+    private boolean isDeviceConfigChanged(int width, int height, int secondaryWidth,
+            int secondaryHeight) {
+        Point currentDimensions = getScreenDimensions();
+        Display smallerDisplay = getSmallerDisplayIfExists();
+        Point currentSecondaryDimensions = smallerDisplay != null ? getRealSize(smallerDisplay) :
+                new Point(0, 0);
+
+        return (currentDimensions.x != width
+                || currentDimensions.y != height
+                || currentSecondaryDimensions.x != secondaryWidth
+                || currentSecondaryDimensions.y != secondaryHeight);
+    }
+
     /**
      * This method backs up the device dimension information. The device data will always get
      * overwritten when triggering a backup
      */
-    private void backupDeviceInfoFile(FullBackupDataOutput data)
+    private void backupDeviceInfoFile(SharedPreferences sharedPrefs, boolean isDeviceConfigChanged,
+            FullBackupDataOutput data)
             throws IOException {
         final File deviceInfoStage = new File(getFilesDir(), WALLPAPER_BACKUP_DEVICE_INFO_STAGE);
 
-        // save the dimensions of the device with xml formatting
-        Point dimensions = getScreenDimensions();
-        Display smallerDisplay = getSmallerDisplayIfExists();
-        Point secondaryDimensions = smallerDisplay != null ? getRealSize(smallerDisplay) :
-                new Point(0, 0);
+        if (isDeviceConfigChanged) {
+            // save the dimensions of the device with xml formatting
+            Point dimensions = getScreenDimensions();
+            Display smallerDisplay = getSmallerDisplayIfExists();
+            Point secondaryDimensions = smallerDisplay != null ? getRealSize(smallerDisplay) :
+                    new Point(0, 0);
 
-        deviceInfoStage.createNewFile();
-        FileOutputStream fstream = new FileOutputStream(deviceInfoStage, false);
-        TypedXmlSerializer out = Xml.resolveSerializer(fstream);
-        out.startDocument(null, true);
-        out.startTag(null, "dimensions");
+            deviceInfoStage.createNewFile();
+            FileOutputStream fstream = new FileOutputStream(deviceInfoStage, false);
+            TypedXmlSerializer out = Xml.resolveSerializer(fstream);
+            out.startDocument(null, true);
+            out.startTag(null, "dimensions");
 
-        out.startTag(null, "width");
-        out.text(String.valueOf(dimensions.x));
-        out.endTag(null, "width");
+            out.startTag(null, "width");
+            out.text(String.valueOf(dimensions.x));
+            out.endTag(null, "width");
 
-        out.startTag(null, "height");
-        out.text(String.valueOf(dimensions.y));
-        out.endTag(null, "height");
+            out.startTag(null, "height");
+            out.text(String.valueOf(dimensions.y));
+            out.endTag(null, "height");
 
-        if (smallerDisplay != null) {
-            out.startTag(null, "secondarywidth");
-            out.text(String.valueOf(secondaryDimensions.x));
-            out.endTag(null, "secondarywidth");
+            if (smallerDisplay != null) {
+                out.startTag(null, "secondarywidth");
+                out.text(String.valueOf(secondaryDimensions.x));
+                out.endTag(null, "secondarywidth");
 
-            out.startTag(null, "secondaryheight");
-            out.text(String.valueOf(secondaryDimensions.y));
-            out.endTag(null, "secondaryheight");
+                out.startTag(null, "secondaryheight");
+                out.text(String.valueOf(secondaryDimensions.y));
+                out.endTag(null, "secondaryheight");
+            }
+
+            out.endTag(null, "dimensions");
+            out.endDocument();
+            fstream.flush();
+            FileUtils.sync(fstream);
+            fstream.close();
+
+            SharedPreferences.Editor editor = sharedPrefs.edit();
+            editor.putInt(DEVICE_CONFIG_WIDTH, dimensions.x);
+            editor.putInt(DEVICE_CONFIG_HEIGHT, dimensions.y);
+            editor.putInt(DEVICE_CONFIG_SECONDARY_WIDTH, secondaryDimensions.x);
+            editor.putInt(DEVICE_CONFIG_SECONDARY_HEIGHT, secondaryDimensions.y);
+            editor.apply();
         }
-
-        out.endTag(null, "dimensions");
-        out.endDocument();
-        fstream.flush();
-        FileUtils.sync(fstream);
-        fstream.close();
-
         if (DEBUG) Slog.v(TAG, "Storing device dimension data");
         backupFile(deviceInfoStage, data);
     }
diff --git a/ravenwood/Android.bp b/ravenwood/Android.bp
index e2eb09f..7c8fd42 100644
--- a/ravenwood/Android.bp
+++ b/ravenwood/Android.bp
@@ -68,7 +68,10 @@
     srcs: [
         "runtime-common-ravenwood-src/**/*.java",
     ],
-    visibility: ["//frameworks/base"],
+    visibility: [
+        // Some tests need to access the utilities.
+        ":__subpackages__",
+    ],
 }
 
 java_library {
@@ -182,6 +185,9 @@
 // want to verify they're unbundled.  The "impl" library above is what
 // ships inside the Ravenwood environment to actually drive any API
 // access to implementation details.
+// This library needs to be statically linked to mainline tests as well,
+// which need to be able to run on multiple API levels, so we can't use
+// test APIs in this module.
 java_library {
     name: "ravenwood-junit",
     srcs: [
@@ -189,7 +195,7 @@
         "junit-stub-src/**/*.java",
         "junit-flag-src/**/*.java",
     ],
-    sdk_version: "test_current",
+    sdk_version: "module_current",
     static_libs: [
         "ravenwood-runtime-common",
         "ravenwood-runtime-common-device",
@@ -315,6 +321,9 @@
 
 android_ravenwood_libgroup {
     name: "ravenwood-runtime",
+    data: [
+        "framework-res",
+    ],
     libs: [
         "100-framework-minus-apex.ravenwood",
         "200-kxml2-android",
@@ -327,6 +336,11 @@
         "services.core.ravenwood-jarjar",
         "services.fakes.ravenwood-jarjar",
 
+        // ICU
+        "core-icu4j-for-host.ravenwood",
+        "icu4j-icudata-jarjar",
+        "icu4j-icutzdata-jarjar",
+
         // Provide runtime versions of utils linked in below
         "junit",
         "truth",
diff --git a/ravenwood/TEST_MAPPING b/ravenwood/TEST_MAPPING
index f6885e1..fbf27fa 100644
--- a/ravenwood/TEST_MAPPING
+++ b/ravenwood/TEST_MAPPING
@@ -12,6 +12,9 @@
     {
       "name": "RavenwoodBivalentTest_device"
     },
+    {
+      "name": "RavenwoodResApkTest"
+    },
     // The sysui tests should match vendor/unbundled_google/packages/SystemUIGoogle/TEST_MAPPING
     {
       "name": "SystemUIGoogleTests",
diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
index 68b5aeb..825c91a 100644
--- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
+++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
@@ -18,7 +18,7 @@
 
 import static android.os.Process.FIRST_APPLICATION_UID;
 import static android.os.Process.SYSTEM_UID;
-import static android.os.UserHandle.USER_SYSTEM;
+import static android.os.UserHandle.SYSTEM;
 
 import static org.junit.Assert.fail;
 
@@ -115,7 +115,7 @@
 
     private static final AtomicInteger sNextPid = new AtomicInteger(100);
 
-    int mCurrentUser = USER_SYSTEM;
+    int mCurrentUser = SYSTEM.getIdentifier();
 
     /**
      * Unless the test author requests differently, run as "nobody", and give each collection of
diff --git a/ravenwood/resapk_test/Android.bp b/ravenwood/resapk_test/Android.bp
new file mode 100644
index 0000000..c145765
--- /dev/null
+++ b/ravenwood/resapk_test/Android.bp
@@ -0,0 +1,30 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_ravenwood_test {
+    name: "RavenwoodResApkTest",
+
+    resource_apk: "RavenwoodResApkTest-apk",
+
+    libs: [
+        // Normally, tests shouldn't directly access it, but we need to access RavenwoodCommonUtils
+        // in this test.
+        "ravenwood-runtime-common-ravenwood",
+    ],
+    static_libs: [
+        "androidx.annotation_annotation",
+        "androidx.test.ext.junit",
+        "androidx.test.rules",
+    ],
+    srcs: [
+        "test/**/*.java",
+    ],
+    sdk_version: "test_current",
+    auto_gen_config: true,
+}
diff --git a/ravenwood/resapk_test/apk/Android.bp b/ravenwood/resapk_test/apk/Android.bp
new file mode 100644
index 0000000..10ed5e2
--- /dev/null
+++ b/ravenwood/resapk_test/apk/Android.bp
@@ -0,0 +1,14 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_app {
+    name: "RavenwoodResApkTest-apk",
+
+    sdk_version: "current",
+}
diff --git a/ravenwood/resapk_test/apk/AndroidManifest.xml b/ravenwood/resapk_test/apk/AndroidManifest.xml
new file mode 100644
index 0000000..f34d8b2
--- /dev/null
+++ b/ravenwood/resapk_test/apk/AndroidManifest.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2024 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.ravenwood.restest_apk">
+</manifest>
diff --git a/ravenwood/resapk_test/apk/res/values/strings.xml b/ravenwood/resapk_test/apk/res/values/strings.xml
new file mode 100644
index 0000000..23d4c0f
--- /dev/null
+++ b/ravenwood/resapk_test/apk/res/values/strings.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2024 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Test string 1 -->
+    <string name="test_string_1" translatable="false" >Test String 1</string>
+</resources>
diff --git a/ravenwood/resapk_test/test/com/android/ravenwood/resapk_test/RavenwoodResApkTest.java b/ravenwood/resapk_test/test/com/android/ravenwood/resapk_test/RavenwoodResApkTest.java
new file mode 100644
index 0000000..1029ed2
--- /dev/null
+++ b/ravenwood/resapk_test/test/com/android/ravenwood/resapk_test/RavenwoodResApkTest.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.ravenwood.resapk_test;
+
+
+import static junit.framework.TestCase.assertTrue;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.ravenwood.common.RavenwoodCommonUtils;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+
+@RunWith(AndroidJUnit4.class)
+public class RavenwoodResApkTest {
+    /**
+     * Ensure the file "ravenwood-res.apk" exists.
+     * TODO Check the content of it, once Ravenwood supports resources. The file should
+     * be a copy of RavenwoodResApkTest-apk.apk
+     */
+    @Test
+    public void testResApkExists() {
+        var file = "ravenwood-res-apks/ravenwood-res.apk";
+
+        assertTrue(new File(file).exists());
+    }
+
+    @Test
+    public void testFrameworkResExists() {
+        var file = "ravenwood-data/framework-res.apk";
+
+        assertTrue(new File(
+                RavenwoodCommonUtils.getRavenwoodRuntimePath() + "/" + file).exists());
+    }
+}
diff --git a/ravenwood/runtime-common-src/com/android/ravenwood/common/JvmWorkaround.java b/ravenwood/runtime-common-src/com/android/ravenwood/common/JvmWorkaround.java
index ee28099..0238baa 100644
--- a/ravenwood/runtime-common-src/com/android/ravenwood/common/JvmWorkaround.java
+++ b/ravenwood/runtime-common-src/com/android/ravenwood/common/JvmWorkaround.java
@@ -16,6 +16,7 @@
 package com.android.ravenwood.common;
 
 import java.io.FileDescriptor;
+import java.io.IOException;
 
 /**
  * Collection of methods to workaround limitation in the hostside JVM.
@@ -44,6 +45,11 @@
     public abstract int getFdInt(FileDescriptor fd);
 
     /**
+     * Equivalent to Android's Os.close(fd).
+     */
+    public abstract void closeFd(FileDescriptor fd) throws IOException;
+
+    /**
      * Placeholder implementation for the host side.
      *
      * Even on the host side, we don't want to throw just because the class is loaded,
@@ -64,5 +70,10 @@
         public int getFdInt(FileDescriptor fd) {
             throw calledOnHostside();
         }
+
+        @Override
+        public void closeFd(FileDescriptor fd) {
+            throw calledOnHostside();
+        }
     }
 }
diff --git a/ravenwood/runtime-common-src/com/android/ravenwood/common/OpenJdkWorkaround.java b/ravenwood/runtime-common-src/com/android/ravenwood/common/OpenJdkWorkaround.java
index 9aedaab..a260147 100644
--- a/ravenwood/runtime-common-src/com/android/ravenwood/common/OpenJdkWorkaround.java
+++ b/ravenwood/runtime-common-src/com/android/ravenwood/common/OpenJdkWorkaround.java
@@ -16,6 +16,8 @@
 package com.android.ravenwood.common;
 
 import java.io.FileDescriptor;
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
 
 class OpenJdkWorkaround extends JvmWorkaround {
     @Override
@@ -43,4 +45,19 @@
                     + " perhaps JRE has changed?", e);
         }
     }
+
+    @Override
+    public void closeFd(FileDescriptor fd) throws IOException {
+        try {
+            final Object obj = Class.forName("jdk.internal.access.SharedSecrets").getMethod(
+                    "getJavaIOFileDescriptorAccess").invoke(null);
+            Class.forName("jdk.internal.access.JavaIOFileDescriptorAccess").getMethod(
+                    "close", FileDescriptor.class).invoke(obj, fd);
+        } catch (InvocationTargetException e) {
+            SneakyThrow.sneakyThrow(e.getTargetException());
+        } catch (ReflectiveOperationException e) {
+            throw new RuntimeException("Failed to interact with raw FileDescriptor internals;"
+                    + " perhaps JRE has changed?", e);
+        }
+    }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/OnTaskActionClickListener.kt b/ravenwood/runtime-common-src/com/android/ravenwood/common/SneakyThrow.java
similarity index 60%
rename from libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/OnTaskActionClickListener.kt
rename to ravenwood/runtime-common-src/com/android/ravenwood/common/SneakyThrow.java
index 14b9e7f..0dbf7df 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/OnTaskActionClickListener.kt
+++ b/ravenwood/runtime-common-src/com/android/ravenwood/common/SneakyThrow.java
@@ -13,15 +13,21 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.wm.shell.windowdecor.common
+package com.android.ravenwood.common;
 
-/** A callback to be invoked when a Task's window decor element is clicked. */
-fun interface OnTaskActionClickListener {
+public class SneakyThrow {
+
+    private SneakyThrow() {
+    }
+
     /**
-     * Called when a task's decor element has been clicked.
-     *
-     * @param taskId the id of the task.
-     * @param tag a readable identifier for the element.
+     * Throw checked exceptions without the need to declare in method signature
      */
-    fun onClick(taskId: Int, tag: String)
+    public static void sneakyThrow(Throwable t) {
+        SneakyThrow.<RuntimeException>sneakyThrow_(t);
+    }
+
+    private static <T extends Throwable> void sneakyThrow_(Throwable t) throws T {
+        throw (T) t;
+    }
 }
diff --git a/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/ParcelFileDescriptor_host.java b/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/ParcelFileDescriptor_host.java
index 8fe6853..5a3589d 100644
--- a/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/ParcelFileDescriptor_host.java
+++ b/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/ParcelFileDescriptor_host.java
@@ -16,98 +16,16 @@
 
 package com.android.platform.test.ravenwood.nativesubstitution;
 
-import static android.os.ParcelFileDescriptor.MODE_APPEND;
-import static android.os.ParcelFileDescriptor.MODE_CREATE;
-import static android.os.ParcelFileDescriptor.MODE_READ_ONLY;
-import static android.os.ParcelFileDescriptor.MODE_READ_WRITE;
-import static android.os.ParcelFileDescriptor.MODE_TRUNCATE;
-import static android.os.ParcelFileDescriptor.MODE_WORLD_READABLE;
-import static android.os.ParcelFileDescriptor.MODE_WORLD_WRITEABLE;
-import static android.os.ParcelFileDescriptor.MODE_WRITE_ONLY;
-
-import com.android.internal.annotations.GuardedBy;
 import com.android.ravenwood.common.JvmWorkaround;
 
-import java.io.File;
 import java.io.FileDescriptor;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.RandomAccessFile;
-import java.util.HashMap;
-import java.util.Map;
 
 public class ParcelFileDescriptor_host {
-    /**
-     * Since we don't have a great way to keep an unmanaged {@code FileDescriptor} reference
-     * alive, we keep a strong reference to the {@code RandomAccessFile} we used to open it. This
-     * gives us a way to look up the original parent object when closing later.
-     */
-    @GuardedBy("sActive")
-    private static final Map<FileDescriptor, RandomAccessFile> sActive = new HashMap<>();
-
-    public static void native_setFdInt$ravenwood(FileDescriptor fd, int fdInt) {
+    public static void setFdInt(FileDescriptor fd, int fdInt) {
         JvmWorkaround.getInstance().setFdInt(fd, fdInt);
     }
 
-    public static int native_getFdInt$ravenwood(FileDescriptor fd) {
+    public static int getFdInt(FileDescriptor fd) {
         return JvmWorkaround.getInstance().getFdInt(fd);
     }
-
-    public static FileDescriptor native_open$ravenwood(File file, int pfdMode) throws IOException {
-        if ((pfdMode & MODE_CREATE) != 0 && !file.exists()) {
-            throw new FileNotFoundException();
-        }
-
-        final String modeString;
-        if ((pfdMode & MODE_READ_WRITE) == MODE_READ_WRITE) {
-            modeString = "rw";
-        } else if ((pfdMode & MODE_WRITE_ONLY) == MODE_WRITE_ONLY) {
-            modeString = "rw";
-        } else if ((pfdMode & MODE_READ_ONLY) == MODE_READ_ONLY) {
-            modeString = "r";
-        } else {
-            throw new IllegalArgumentException();
-        }
-
-        final RandomAccessFile raf = new RandomAccessFile(file, modeString);
-
-        // Now that we have a real file on disk, match requested flags
-        if ((pfdMode & MODE_TRUNCATE) != 0) {
-            raf.setLength(0);
-        }
-        if ((pfdMode & MODE_APPEND) != 0) {
-            raf.seek(raf.length());
-        }
-        if ((pfdMode & MODE_WORLD_READABLE) != 0) {
-            file.setReadable(true, false);
-        }
-        if ((pfdMode & MODE_WORLD_WRITEABLE) != 0) {
-            file.setWritable(true, false);
-        }
-
-        final FileDescriptor fd = raf.getFD();
-        synchronized (sActive) {
-            sActive.put(fd, raf);
-        }
-        return fd;
-    }
-
-    public static void native_close$ravenwood(FileDescriptor fd) {
-        final RandomAccessFile raf;
-        synchronized (sActive) {
-            raf = sActive.remove(fd);
-        }
-        try {
-            if (raf != null) {
-                raf.close();
-            } else {
-                // Odd, we don't remember opening this ourselves, but let's release the
-                // underlying resource as requested
-                System.err.println("Closing unknown FileDescriptor: " + fd);
-                new FileOutputStream(fd).close();
-            }
-        } catch (IOException ignored) {
-        }
-    }
 }
diff --git a/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/Parcel_host.java b/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/Parcel_host.java
index 22e11e1..cb00b3e 100644
--- a/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/Parcel_host.java
+++ b/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/Parcel_host.java
@@ -15,6 +15,11 @@
  */
 package com.android.platform.test.ravenwood.nativesubstitution;
 
+import android.system.ErrnoException;
+import android.system.Os;
+import android.util.Log;
+
+import java.io.FileDescriptor;
 import java.nio.ByteBuffer;
 import java.nio.charset.StandardCharsets;
 import java.util.Arrays;
@@ -31,6 +36,8 @@
  * {@link ByteBuffer} wouldn't allow...)
  */
 public class Parcel_host {
+    private static final String TAG = "Parcel";
+
     private Parcel_host() {
     }
 
@@ -50,6 +57,11 @@
     // TODO Use the actual value from Parcel.java.
     private static final int OK = 0;
 
+    private final Map<Integer, FileDescriptor> mFdMap = new ConcurrentHashMap<>();
+
+    private static final int FD_PLACEHOLDER = 0xDEADBEEF;
+    private static final int FD_PAYLOAD_SIZE = 8;
+
     private void validate() {
         if (mDeleted) {
             // TODO: Put more info
@@ -67,6 +79,7 @@
         return p;
     }
 
+    /** Native method substitution */
     public static long nativeCreate() {
         final long id = sNextId.getAndIncrement();
         final Parcel_host p = new Parcel_host();
@@ -80,7 +93,8 @@
         mSize = 0;
         mPos = 0;
         mSensitive = false;
-        mAllowFds = false;
+        mAllowFds = true;
+        mFdMap.clear();
     }
 
     private void updateSize() {
@@ -89,16 +103,19 @@
         }
     }
 
+    /** Native method substitution */
     public static void nativeDestroy(long nativePtr) {
         getInstance(nativePtr).mDeleted = true;
         sInstances.remove(nativePtr);
     }
 
+    /** Native method substitution */
     public static void nativeFreeBuffer(long nativePtr) {
         getInstance(nativePtr).freeBuffer();
     }
 
-    public void freeBuffer() {
+    /** Native method substitution */
+    private void freeBuffer() {
         init();
     }
 
@@ -137,32 +154,47 @@
         }
     }
 
+    /** Native method substitution */
     public static void nativeMarkSensitive(long nativePtr) {
         getInstance(nativePtr).mSensitive = true;
     }
+
+    /** Native method substitution */
     public static int nativeDataSize(long nativePtr) {
         return getInstance(nativePtr).mSize;
     }
+
+    /** Native method substitution */
     public static int nativeDataAvail(long nativePtr) {
         var p = getInstance(nativePtr);
         return p.mSize - p.mPos;
     }
+
+    /** Native method substitution */
     public static int nativeDataPosition(long nativePtr) {
         return getInstance(nativePtr).mPos;
     }
+
+    /** Native method substitution */
     public static int nativeDataCapacity(long nativePtr) {
         return getInstance(nativePtr).mBuffer.length;
     }
+
+    /** Native method substitution */
     public static void nativeSetDataSize(long nativePtr, int size) {
         var p = getInstance(nativePtr);
         p.ensureCapacity(size);
         getInstance(nativePtr).mSize = size;
     }
+
+    /** Native method substitution */
     public static void nativeSetDataPosition(long nativePtr, int pos) {
         var p = getInstance(nativePtr);
         // TODO: Should this change the size or the capacity??
         p.mPos = pos;
     }
+
+    /** Native method substitution */
     public static void nativeSetDataCapacity(long nativePtr, int size) {
         if (size < 0) {
             throw new IllegalArgumentException("size < 0: size=" + size);
@@ -173,20 +205,25 @@
         }
     }
 
+    /** Native method substitution */
     public static boolean nativePushAllowFds(long nativePtr, boolean allowFds) {
         var p = getInstance(nativePtr);
         var prev = p.mAllowFds;
         p.mAllowFds = allowFds;
         return prev;
     }
+
+    /** Native method substitution */
     public static void nativeRestoreAllowFds(long nativePtr, boolean lastValue) {
         getInstance(nativePtr).mAllowFds = lastValue;
     }
 
+    /** Native method substitution */
     public static void nativeWriteByteArray(long nativePtr, byte[] b, int offset, int len) {
         nativeWriteBlob(nativePtr, b, offset, len);
     }
 
+    /** Native method substitution */
     public static void nativeWriteBlob(long nativePtr, byte[] b, int offset, int len) {
         var p = getInstance(nativePtr);
 
@@ -205,6 +242,7 @@
         }
     }
 
+    /** Native method substitution */
     public static int nativeWriteInt(long nativePtr, int value) {
         var p = getInstance(nativePtr);
         p.ensureMoreCapacity(Integer.BYTES);
@@ -219,14 +257,19 @@
         return OK;
     }
 
+    /** Native method substitution */
     public static int nativeWriteLong(long nativePtr, long value) {
         nativeWriteInt(nativePtr, (int) (value >>> 32));
         nativeWriteInt(nativePtr, (int) (value));
         return OK;
     }
+
+    /** Native method substitution */
     public static int nativeWriteFloat(long nativePtr, float val) {
         return nativeWriteInt(nativePtr, Float.floatToIntBits(val));
     }
+
+    /** Native method substitution */
     public static int nativeWriteDouble(long nativePtr, double val) {
         return nativeWriteLong(nativePtr, Double.doubleToLongBits(val));
     }
@@ -235,6 +278,7 @@
         return ((val + 3) / 4) * 4;
     }
 
+    /** Native method substitution */
     public static void nativeWriteString8(long nativePtr, String val) {
         if (val == null) {
             nativeWriteBlob(nativePtr, null, 0, 0);
@@ -243,15 +287,19 @@
             nativeWriteBlob(nativePtr, bytes, 0, bytes.length);
         }
     }
+
+    /** Native method substitution */
     public static void nativeWriteString16(long nativePtr, String val) {
         // Just reuse String8
         nativeWriteString8(nativePtr, val);
     }
 
+    /** Native method substitution */
     public static byte[] nativeCreateByteArray(long nativePtr) {
         return nativeReadBlob(nativePtr);
     }
 
+    /** Native method substitution */
     public static boolean nativeReadByteArray(long nativePtr, byte[] dest, int destLen) {
         if (dest == null) {
             return false;
@@ -271,6 +319,7 @@
         return true;
     }
 
+    /** Native method substitution */
     public static byte[] nativeReadBlob(long nativePtr) {
         var p = getInstance(nativePtr);
         if (p.mSize - p.mPos < 4) {
@@ -295,6 +344,8 @@
 
         return bytes;
     }
+
+    /** Native method substitution */
     public static int nativeReadInt(long nativePtr) {
         var p = getInstance(nativePtr);
 
@@ -310,19 +361,24 @@
 
         return ret;
     }
+
+    /** Native method substitution */
     public static long nativeReadLong(long nativePtr) {
         return (((long) nativeReadInt(nativePtr)) << 32)
                 | (((long) nativeReadInt(nativePtr)) & 0xffff_ffffL);
     }
 
+    /** Native method substitution */
     public static float nativeReadFloat(long nativePtr) {
         return Float.intBitsToFloat(nativeReadInt(nativePtr));
     }
 
+    /** Native method substitution */
     public static double nativeReadDouble(long nativePtr) {
         return Double.longBitsToDouble(nativeReadLong(nativePtr));
     }
 
+    /** Native method substitution */
     public static String nativeReadString8(long nativePtr) {
         final var bytes = nativeReadBlob(nativePtr);
         if (bytes == null) {
@@ -334,10 +390,13 @@
         return nativeReadString8(nativePtr);
     }
 
+    /** Native method substitution */
     public static byte[] nativeMarshall(long nativePtr) {
         var p = getInstance(nativePtr);
         return Arrays.copyOf(p.mBuffer, p.mSize);
     }
+
+    /** Native method substitution */
     public static void nativeUnmarshall(
             long nativePtr, byte[] data, int offset, int length) {
         var p = getInstance(nativePtr);
@@ -346,6 +405,8 @@
         p.mPos += length;
         p.updateSize();
     }
+
+    /** Native method substitution */
     public static int nativeCompareData(long thisNativePtr, long otherNativePtr) {
         var a = getInstance(thisNativePtr);
         var b = getInstance(otherNativePtr);
@@ -355,6 +416,8 @@
             return -1;
         }
     }
+
+    /** Native method substitution */
     public static boolean nativeCompareDataInRange(
             long ptrA, int offsetA, long ptrB, int offsetB, int length) {
         var a = getInstance(ptrA);
@@ -368,6 +431,8 @@
         return Arrays.equals(Arrays.copyOfRange(a.mBuffer, offsetA, offsetA + length),
                 Arrays.copyOfRange(b.mBuffer, offsetB, offsetB + length));
     }
+
+    /** Native method substitution */
     public static void nativeAppendFrom(
             long thisNativePtr, long otherNativePtr, int srcOffset, int length) {
         var dst = getInstance(thisNativePtr);
@@ -382,25 +447,84 @@
         // TODO: Update the other's position?
     }
 
-    public static boolean nativeHasFileDescriptors(long nativePtr) {
-        // Assume false for now, because we don't support writing FDs yet.
-        return false;
-    }
-
-    public static boolean nativeHasFileDescriptorsInRange(
-            long nativePtr, int offset, int length) {
-        // Assume false for now, because we don't support writing FDs yet.
-        return false;
-    }
-
+    /** Native method substitution */
     public static boolean nativeHasBinders(long nativePtr) {
         // Assume false for now, because we don't support adding binders.
         return false;
     }
 
+    /** Native method substitution */
     public static boolean nativeHasBindersInRange(
             long nativePtr, int offset, int length) {
         // Assume false for now, because we don't support writing FDs yet.
         return false;
     }
-}
+
+    /** Native method substitution */
+    public static void nativeWriteFileDescriptor(long nativePtr, java.io.FileDescriptor val) {
+        var p = getInstance(nativePtr);
+
+        if (!p.mAllowFds) {
+            // Simulate the FDS_NOT_ALLOWED case in frameworks/base/core/jni/android_util_Binder.cpp
+            throw new RuntimeException("Not allowed to write file descriptors here");
+        }
+
+        FileDescriptor dup = null;
+        try {
+            dup = Os.dup(val);
+        } catch (ErrnoException e) {
+            throw new RuntimeException(e);
+        }
+        p.mFdMap.put(p.mPos, dup);
+
+        // Parcel.cpp writes two int32s for a FD.
+        // Make sure FD_PAYLOAD_SIZE is in sync with this code.
+        nativeWriteInt(nativePtr, FD_PLACEHOLDER);
+        nativeWriteInt(nativePtr, FD_PLACEHOLDER);
+    }
+
+    /** Native method substitution */
+    public static java.io.FileDescriptor nativeReadFileDescriptor(long nativePtr) {
+        var p = getInstance(nativePtr);
+
+        var pos = p.mPos;
+        var fd = p.mFdMap.get(pos);
+
+        if (fd == null) {
+            Log.w(TAG, "nativeReadFileDescriptor: Not a FD at pos #" + pos);
+            return null;
+        }
+        nativeReadInt(nativePtr);
+        nativeReadInt(nativePtr);
+        return fd;
+    }
+
+    /** Native method substitution */
+    public static boolean nativeHasFileDescriptors(long nativePtr) {
+        var p = getInstance(nativePtr);
+        return p.mFdMap.size() > 0;
+    }
+
+    /** Native method substitution */
+    public static boolean nativeHasFileDescriptorsInRange(long nativePtr, int offset, int length) {
+        var p = getInstance(nativePtr);
+
+        // Original code: hasFileDescriptorsInRange() in frameworks/native/libs/binder/Parcel.cpp
+        if (offset < 0 || length < 0) {
+            throw new IllegalArgumentException("Negative value not allowed: offset=" + offset
+                    + " length=" + length);
+        }
+        long limit = (long) offset + (long) length;
+        if (limit > p.mSize) {
+            throw new IllegalArgumentException("Out of range: offset=" + offset
+                    + " length=" + length + " dataSize=" + p.mSize);
+        }
+
+        for (var pos : p.mFdMap.keySet()) {
+            if (offset <= pos && (pos + FD_PAYLOAD_SIZE - 1) < (offset + length)) {
+                return true;
+            }
+        }
+        return false;
+    }
+}
\ No newline at end of file
diff --git a/ravenwood/runtime-helper-src/libcore-fake/android/system/Os.java b/ravenwood/runtime-helper-src/libcore-fake/android/system/Os.java
index 8a1fe62..ecaa816 100644
--- a/ravenwood/runtime-helper-src/libcore-fake/android/system/Os.java
+++ b/ravenwood/runtime-helper-src/libcore-fake/android/system/Os.java
@@ -15,9 +15,11 @@
  */
 package android.system;
 
+import com.android.ravenwood.common.JvmWorkaround;
 import com.android.ravenwood.common.RavenwoodRuntimeNative;
 
 import java.io.FileDescriptor;
+import java.io.IOException;
 
 /**
  * OS class replacement used on Ravenwood. For now, we just implement APIs as we need them...
@@ -53,4 +55,18 @@
     public static StructStat stat(String path) throws ErrnoException {
         return RavenwoodRuntimeNative.stat(path);
     }
+
+    /** Ravenwood version of the OS API. */
+    public static void close(FileDescriptor fd) throws ErrnoException {
+        try {
+            JvmWorkaround.getInstance().closeFd(fd);
+        } catch (IOException e) {
+            // The only valid error on Linux that can happen is EIO
+            throw new ErrnoException("close", OsConstants.EIO);
+        }
+    }
+
+    public static FileDescriptor open(String path, int flags, int mode) throws ErrnoException {
+        return RavenwoodRuntimeNative.open(path, flags, mode);
+    }
 }
diff --git a/ravenwood/runtime-helper-src/libcore-fake/com/android/ravenwood/common/RavenwoodRuntimeNative.java b/ravenwood/runtime-helper-src/libcore-fake/com/android/ravenwood/common/RavenwoodRuntimeNative.java
index e9b305e..beba833 100644
--- a/ravenwood/runtime-helper-src/libcore-fake/com/android/ravenwood/common/RavenwoodRuntimeNative.java
+++ b/ravenwood/runtime-helper-src/libcore-fake/com/android/ravenwood/common/RavenwoodRuntimeNative.java
@@ -48,6 +48,8 @@
 
     public static native StructStat stat(String path) throws ErrnoException;
 
+    private static native int nOpen(String path, int flags, int mode) throws ErrnoException;
+
     public static long lseek(FileDescriptor fd, long offset, int whence) throws ErrnoException {
         return nLseek(JvmWorkaround.getInstance().getFdInt(fd), offset, whence);
     }
@@ -67,7 +69,7 @@
     public static FileDescriptor dup(FileDescriptor fd) throws ErrnoException {
         var fdInt = nDup(JvmWorkaround.getInstance().getFdInt(fd));
 
-        var retFd = new java.io.FileDescriptor();
+        var retFd = new FileDescriptor();
         JvmWorkaround.getInstance().setFdInt(retFd, fdInt);
         return retFd;
     }
@@ -83,4 +85,12 @@
 
         return nFstat(fdInt);
     }
+
+    public static FileDescriptor open(String path, int flags, int mode) throws ErrnoException {
+        int fd = nOpen(path, flags, mode);
+        if (fd < 0) return null;
+        var retFd = new FileDescriptor();
+        JvmWorkaround.getInstance().setFdInt(retFd, fd);
+        return retFd;
+    }
 }
diff --git a/ravenwood/runtime-jni/ravenwood_runtime.cpp b/ravenwood/runtime-jni/ravenwood_runtime.cpp
index e0a3e1c..c804928 100644
--- a/ravenwood/runtime-jni/ravenwood_runtime.cpp
+++ b/ravenwood/runtime-jni/ravenwood_runtime.cpp
@@ -18,9 +18,11 @@
 #include <sys/stat.h>
 #include <string.h>
 #include <unistd.h>
+#include <string>
 #include <nativehelper/JNIHelp.h>
 #include <nativehelper/ScopedLocalRef.h>
 #include <nativehelper/ScopedUtfChars.h>
+#include <nativehelper/ScopedPrimitiveArray.h>
 
 #include "jni.h"
 #include "utils/Log.h"
@@ -49,6 +51,43 @@
 static jclass g_StructStat;
 static jclass g_StructTimespecClass;
 
+// We have to explicitly decode the string to real UTF-8, because when using GetStringUTFChars
+// we only get modified UTF-8, which is not the platform string type used in host JVM.
+struct ScopedRealUtf8Chars {
+    ScopedRealUtf8Chars(JNIEnv* env, jstring s) : valid_(false) {
+        if (s == nullptr) {
+            jniThrowNullPointerException(env);
+            return;
+        }
+        jclass clazz = env->GetObjectClass(s);
+        jmethodID getBytes = env->GetMethodID(clazz, "getBytes", "(Ljava/lang/String;)[B");
+
+        ScopedLocalRef<jstring> utf8(env, env->NewStringUTF("UTF-8"));
+        ScopedLocalRef<jbyteArray> jbytes(env,
+            (jbyteArray) env->CallObjectMethod(s, getBytes, utf8.get()));
+
+        ScopedByteArrayRO bytes(env, jbytes.get());
+        string_.append((const char *) bytes.get(), bytes.size());
+        valid_ = true;
+    }
+
+    const char* c_str() const {
+        return valid_ ? string_.c_str() : nullptr;
+    }
+
+    size_t size() const {
+        return string_.size();
+    }
+
+    const char& operator[](size_t n) const {
+        return string_[n];
+    }
+
+private:
+    std::string string_;
+    bool valid_;
+};
+
 static jclass findClass(JNIEnv* env, const char* name) {
     ScopedLocalRef<jclass> localClass(env, env->FindClass(name));
     jclass result = reinterpret_cast<jclass>(env->NewGlobalRef(localClass.get()));
@@ -99,7 +138,7 @@
 }
 
 static jobject doStat(JNIEnv* env, jstring javaPath, bool isLstat) {
-    ScopedUtfChars path(env, javaPath);
+    ScopedRealUtf8Chars path(env, javaPath);
     if (path.c_str() == NULL) {
         return NULL;
     }
@@ -167,6 +206,14 @@
     return doStat(env, javaPath, false);
 }
 
+static jint Linux_open(JNIEnv* env, jobject, jstring javaPath, jint flags, jint mode) {
+    ScopedRealUtf8Chars path(env, javaPath);
+    if (path.c_str() == NULL) {
+        return -1;
+    }
+    return throwIfMinusOne(env, "open", TEMP_FAILURE_RETRY(open(path.c_str(), flags, mode)));
+}
+
 // ---- Registration ----
 
 static const JNINativeMethod sMethods[] =
@@ -179,6 +226,7 @@
     { "nFstat", "(I)Landroid/system/StructStat;", (void*)nFstat },
     { "lstat", "(Ljava/lang/String;)Landroid/system/StructStat;", (void*)Linux_lstat },
     { "stat", "(Ljava/lang/String;)Landroid/system/StructStat;", (void*)Linux_stat },
+    { "nOpen", "(Ljava/lang/String;II)I", (void*)Linux_open },
 };
 
 extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
diff --git a/ravenwood/test-authors.md b/ravenwood/test-authors.md
index 0a0b200..c29fb7f 100644
--- a/ravenwood/test-authors.md
+++ b/ravenwood/test-authors.md
@@ -46,7 +46,7 @@
 * Write your unit test just like you would for an Android device:
 
 ```
-import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.annotations.DisabledOnRavenwood;
 import android.platform.test.ravenwood.RavenwoodRule;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -66,7 +66,7 @@
 * APIs available under Ravenwood are stateless by default.  If your test requires explicit states (such as defining the UID you’re running under, or requiring a main `Looper` thread), add a `RavenwoodRule` to declare that:
 
 ```
-import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.annotations.DisabledOnRavenwood;
 import android.platform.test.ravenwood.RavenwoodRule;
 
 import androidx.test.runner.AndroidJUnit4;
@@ -165,7 +165,7 @@
     }
 
     @Test
-    @IgnoreUnderRavenwood(blockedBy = PackageManager.class)
+    @DisabledOnRavenwood(blockedBy = PackageManager.class)
     public void testComplex() {
         // Complex test that runs on devices, but is ignored under Ravenwood
     }
diff --git a/ravenwood/texts/ravenwood-annotation-allowed-classes.txt b/ravenwood/texts/ravenwood-annotation-allowed-classes.txt
index bdc3577..68f185e 100644
--- a/ravenwood/texts/ravenwood-annotation-allowed-classes.txt
+++ b/ravenwood/texts/ravenwood-annotation-allowed-classes.txt
@@ -143,6 +143,7 @@
 android.os.HandlerExecutor
 android.os.HandlerThread
 android.os.IBinder
+android.os.LocaleList
 android.os.Looper
 android.os.Message
 android.os.MessageQueue
diff --git a/services/accessibility/Android.bp b/services/accessibility/Android.bp
index 311addb..efa1397 100644
--- a/services/accessibility/Android.bp
+++ b/services/accessibility/Android.bp
@@ -26,6 +26,7 @@
     },
     srcs: [
         ":services.accessibility-sources",
+        ":statslog-accessibility-java-gen",
         "//frameworks/base/packages/SettingsLib/RestrictedLockUtils:SettingsLibRestrictedLockUtilsSrc",
     ],
     libs: [
@@ -37,7 +38,6 @@
         "a11ychecker-protos-java-proto-lite",
         "com_android_server_accessibility_flags_lib",
         "//frameworks/base/packages/SystemUI/aconfig:com_android_systemui_flags_lib",
-
     ],
 }
 
@@ -81,3 +81,12 @@
         "java/**/a11ychecker/proto/*.proto",
     ],
 }
+
+genrule {
+    name: "statslog-accessibility-java-gen",
+    tools: ["stats-log-api-gen"],
+    cmd: "$(location stats-log-api-gen) --java $(out) --module accessibility" +
+        " --javaPackage com.android.server.accessibility.a11ychecker" +
+        " --javaClass AccessibilityCheckerStatsLog --minApiLevel 34",
+    out: ["java/com/android/server/accessibility/a11ychecker/AccessibilityCheckerStatsLog.java"],
+}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 30c743e..45fcf6b 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -41,6 +41,7 @@
 import static android.provider.Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED;
 import static android.view.accessibility.AccessibilityManager.FlashNotificationReason;
 
+import static com.android.hardware.input.Flags.keyboardA11yMouseKeys;
 import static com.android.internal.accessibility.AccessibilityShortcutController.ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME;
 import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_COMPONENT_NAME;
 import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME;
@@ -57,7 +58,6 @@
 import static com.android.internal.util.FunctionalUtils.ignoreRemoteException;
 import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
 import static com.android.server.accessibility.AccessibilityUserState.doesShortcutTargetsStringContain;
-import static com.android.hardware.input.Flags.keyboardA11yMouseKeys;
 import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
 
 import android.accessibilityservice.AccessibilityGestureEvent;
@@ -825,25 +825,27 @@
     @VisibleForTesting
     boolean onPackagesForceStoppedLocked(
             String[] packages, AccessibilityUserState userState) {
-        final List<String> continuousServicePackages =
+        final Set<String> packageSet = new HashSet<>(List.of(packages));
+        final ArrayList<ComponentName> continuousServices = new ArrayList<>(
                 userState.mInstalledServices.stream().filter(service ->
                         (service.flags & FLAG_REQUEST_ACCESSIBILITY_BUTTON)
                                 == FLAG_REQUEST_ACCESSIBILITY_BUTTON
-                ).map(service -> service.getComponentName().flattenToString()).toList();
+                ).map(AccessibilityServiceInfo::getComponentName).toList());
+
+        // Filter out continuous packages that are not from the array of stopped packages.
+        continuousServices.removeIf(
+                continuousName -> !packageSet.contains(continuousName.getPackageName()));
 
         boolean enabledServicesChanged = false;
         final Iterator<ComponentName> it = userState.mEnabledServices.iterator();
         while (it.hasNext()) {
             final ComponentName comp = it.next();
             final String compPkg = comp.getPackageName();
-            for (String pkg : packages) {
-                if (compPkg.equals(pkg)) {
-                    it.remove();
-                    userState.getBindingServicesLocked().remove(comp);
-                    userState.getCrashedServicesLocked().remove(comp);
-                    enabledServicesChanged = true;
-                    break;
-                }
+            if (packageSet.contains(compPkg)) {
+                it.remove();
+                userState.getBindingServicesLocked().remove(comp);
+                userState.getCrashedServicesLocked().remove(comp);
+                enabledServicesChanged = true;
             }
         }
         if (enabledServicesChanged) {
@@ -855,8 +857,8 @@
         // Remove any button targets that match any stopped continuous services
         Set<String> buttonTargets = userState.getShortcutTargetsLocked(SOFTWARE);
         boolean buttonTargetsChanged = buttonTargets.removeIf(
-                target -> continuousServicePackages.stream().anyMatch(
-                        pkg -> Objects.equals(target, pkg)));
+                target -> continuousServices.stream().anyMatch(
+                        continuousName -> continuousName.flattenToString().equals(target)));
         if (buttonTargetsChanged) {
             userState.updateShortcutTargetsLocked(buttonTargets, SOFTWARE);
             persistColonDelimitedSetToSettingLocked(
@@ -2641,7 +2643,8 @@
         }
     }
 
-    private <T> void persistColonDelimitedSetToSettingLocked(String settingName, int userId,
+    @VisibleForTesting
+    <T> void persistColonDelimitedSetToSettingLocked(String settingName, int userId,
             Set<T> set, Function<T, String> toString) {
         persistColonDelimitedSetToSettingLocked(settingName, userId, set,
                 toString, /* defaultEmptyString= */ null);
@@ -4087,11 +4090,7 @@
             boolean enable, @UserShortcutType int shortcutTypes,
             @NonNull List<String> shortcutTargets, @UserIdInt int userId) {
         enableShortcutsForTargets_enforcePermission();
-        if ((shortcutTypes & GESTURE) == GESTURE
-                && !android.provider.Flags.a11yStandaloneGestureEnabled()) {
-            throw new IllegalArgumentException(
-                    "GESTURE type shortcuts are disabled by feature flag");
-        }
+
         for (int shortcutType : USER_SHORTCUT_TYPES) {
             if ((shortcutTypes & shortcutType) == shortcutType) {
                 enableShortcutForTargets(enable, shortcutType, shortcutTargets, userId);
@@ -4102,6 +4101,13 @@
     private void enableShortcutForTargets(
             boolean enable, @UserShortcutType int shortcutType,
             @NonNull List<String> shortcutTargets, @UserIdInt int userId) {
+        if (shortcutType == UserShortcutType.GESTURE
+                && !android.provider.Flags.a11yStandaloneGestureEnabled()) {
+            Slog.w(LOG_TAG,
+                    "GESTURE type shortcuts are disabled by feature flag");
+            return;
+        }
+
         final String shortcutTypeSettingKey = ShortcutUtils.convertToKey(shortcutType);
         if (shortcutType == UserShortcutType.TRIPLETAP
                 || shortcutType == UserShortcutType.TWOFINGER_DOUBLETAP) {
diff --git a/services/accessibility/java/com/android/server/accessibility/a11ychecker/AccessibilityCheckerConstants.java b/services/accessibility/java/com/android/server/accessibility/a11ychecker/AccessibilityCheckerConstants.java
new file mode 100644
index 0000000..5a98a40
--- /dev/null
+++ b/services/accessibility/java/com/android/server/accessibility/a11ychecker/AccessibilityCheckerConstants.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.accessibility.a11ychecker;
+
+import java.time.Duration;
+
+/**
+ * Constants used by the accessibility checker.
+ *
+ * @hide
+ */
+final class AccessibilityCheckerConstants {
+
+    // The min required duration between two consecutive runs of the a11y checker.
+    static final Duration MIN_DURATION_BETWEEN_CHECKS = Duration.ofMinutes(1);
+
+    // The max number of cached results at a time.
+    static final int MAX_CACHE_CAPACITY = 10000;
+}
diff --git a/services/accessibility/java/com/android/server/accessibility/a11ychecker/AccessibilityCheckerManager.java b/services/accessibility/java/com/android/server/accessibility/a11ychecker/AccessibilityCheckerManager.java
new file mode 100644
index 0000000..f7a59a4b
--- /dev/null
+++ b/services/accessibility/java/com/android/server/accessibility/a11ychecker/AccessibilityCheckerManager.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.accessibility.a11ychecker;
+
+import static com.android.server.accessibility.a11ychecker.AccessibilityCheckerConstants.MAX_CACHE_CAPACITY;
+import static com.android.server.accessibility.a11ychecker.AccessibilityCheckerConstants.MIN_DURATION_BETWEEN_CHECKS;
+
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.UserIdInt;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.util.Slog;
+import android.view.accessibility.AccessibilityNodeInfo;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.accessibility.Flags;
+import com.android.server.accessibility.a11ychecker.A11yCheckerProto.AccessibilityCheckResultReported;
+
+import com.google.android.apps.common.testing.accessibility.framework.AccessibilityCheckPreset;
+import com.google.android.apps.common.testing.accessibility.framework.AccessibilityHierarchyCheck;
+import com.google.android.apps.common.testing.accessibility.framework.AccessibilityHierarchyCheckResult;
+import com.google.android.apps.common.testing.accessibility.framework.uielement.AccessibilityHierarchy;
+import com.google.android.apps.common.testing.accessibility.framework.uielement.AccessibilityHierarchyAndroid;
+
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+
+/**
+ * The class responsible for running AccessibilityChecks on cached nodes and caching the results for
+ * logging. Results are cached and capped to limit the logging frequency and size.
+ *
+ * @hide
+ */
+public final class AccessibilityCheckerManager {
+    private static final String LOG_TAG = "AccessibilityCheckerManager";
+
+    private final PackageManager mPackageManager;
+    private final Set<AccessibilityHierarchyCheck> mHierarchyChecks;
+    private final ATFHierarchyBuilder mATFHierarchyBuilder;
+    private final Set<AccessibilityCheckResultReported> mCachedResults = new HashSet<>();
+
+    @VisibleForTesting
+    final A11yCheckerTimer mTimer = new A11yCheckerTimer();
+
+    public AccessibilityCheckerManager(Context context) {
+        this(AccessibilityCheckPreset.getAccessibilityHierarchyChecksForPreset(
+                        AccessibilityCheckPreset.LATEST),
+                (nodeInfo) -> AccessibilityHierarchyAndroid.newBuilder(nodeInfo, context).build(),
+                context.getPackageManager());
+    }
+
+    @VisibleForTesting
+    AccessibilityCheckerManager(
+            Set<AccessibilityHierarchyCheck> hierarchyChecks,
+            ATFHierarchyBuilder atfHierarchyBuilder,
+            PackageManager packageManager) {
+        this.mHierarchyChecks = hierarchyChecks;
+        this.mATFHierarchyBuilder = atfHierarchyBuilder;
+        this.mPackageManager = packageManager;
+    }
+
+    /**
+     * If eligible, runs AccessibilityChecks on the given nodes and caches the results for later
+     * logging. Returns the check results for the given nodes.
+     */
+    @RequiresPermission(allOf = {android.Manifest.permission.INTERACT_ACROSS_USERS_FULL})
+    public Set<AccessibilityCheckResultReported> maybeRunA11yChecker(
+            List<AccessibilityNodeInfo> nodes, @Nullable String sourceEventClassName,
+            ComponentName a11yServiceComponentName, @UserIdInt int userId) {
+        if (!shouldRunA11yChecker()) {
+            return Set.of();
+        }
+
+        Set<AccessibilityCheckResultReported> allResults = new HashSet<>();
+        String defaultBrowserName = mPackageManager.getDefaultBrowserPackageNameAsUser(userId);
+
+        try {
+            for (AccessibilityNodeInfo nodeInfo : nodes) {
+                // Skip browser results because they are mostly related to web content and not the
+                // browser app itself.
+                if (nodeInfo.getPackageName() == null
+                        || nodeInfo.getPackageName().toString().equals(defaultBrowserName)) {
+                    continue;
+                }
+                List<AccessibilityHierarchyCheckResult> checkResults = runChecksOnNode(nodeInfo);
+                Set<AccessibilityCheckResultReported> filteredResults =
+                        AccessibilityCheckerUtils.processResults(nodeInfo, checkResults,
+                                sourceEventClassName, mPackageManager, a11yServiceComponentName);
+                allResults.addAll(filteredResults);
+            }
+            mCachedResults.addAll(allResults);
+        } catch (RuntimeException e) {
+            Slog.e(LOG_TAG, "An unknown error occurred while running a11y checker.", e);
+        }
+        return allResults;
+    }
+
+    private List<AccessibilityHierarchyCheckResult> runChecksOnNode(
+            AccessibilityNodeInfo nodeInfo) {
+        AccessibilityHierarchy checkableHierarchy = mATFHierarchyBuilder.getATFCheckableHierarchy(
+                nodeInfo);
+        List<AccessibilityHierarchyCheckResult> checkResults = new ArrayList<>();
+        for (AccessibilityHierarchyCheck check : mHierarchyChecks) {
+            checkResults.addAll(check.runCheckOnHierarchy(checkableHierarchy));
+        }
+        return checkResults;
+    }
+
+    public Set<AccessibilityCheckResultReported> getCachedResults() {
+        return Collections.unmodifiableSet(mCachedResults);
+    }
+
+    @VisibleForTesting
+    boolean shouldRunA11yChecker() {
+        if (!Flags.enableA11yCheckerLogging() || mCachedResults.size() == MAX_CACHE_CAPACITY) {
+            return false;
+        }
+        if (mTimer.getLastCheckTime() == null || mTimer.getLastCheckTime().plus(
+                MIN_DURATION_BETWEEN_CHECKS).isBefore(Instant.now())) {
+            mTimer.setLastCheckTime(Instant.now());
+            return true;
+        }
+        return false;
+    }
+
+    /** Timer class to facilitate testing with fake times. */
+    @VisibleForTesting
+    static class A11yCheckerTimer {
+        private Instant mLastCheckTime = null;
+
+        Instant getLastCheckTime() {
+            return mLastCheckTime;
+        }
+
+        void setLastCheckTime(Instant newTime) {
+            mLastCheckTime = newTime;
+        }
+    }
+
+    /** AccessibilityHierarchy wrapper to facilitate testing with fake hierarchies. */
+    @VisibleForTesting
+    interface ATFHierarchyBuilder {
+        AccessibilityHierarchy getATFCheckableHierarchy(AccessibilityNodeInfo nodeInfo);
+    }
+}
diff --git a/services/accessibility/java/com/android/server/accessibility/a11ychecker/AccessibilityCheckerStatsdLogger.java b/services/accessibility/java/com/android/server/accessibility/a11ychecker/AccessibilityCheckerStatsdLogger.java
new file mode 100644
index 0000000..1b3ec5a
--- /dev/null
+++ b/services/accessibility/java/com/android/server/accessibility/a11ychecker/AccessibilityCheckerStatsdLogger.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.accessibility.a11ychecker;
+
+import android.util.Slog;
+
+import com.android.server.accessibility.a11ychecker.A11yCheckerProto.AccessibilityCheckResultReported;
+
+import java.util.Set;
+
+
+/**
+ * Wraps the StatsdLogger for AccessibilityCheckResultReported.
+ *
+ * @hide
+ */
+public class AccessibilityCheckerStatsdLogger {
+    private static final int ATOM_ID = 910;
+    private static final String LOG_TAG = "AccessibilityCheckerStatsdLogger";
+
+    /**
+     * Writes results to statsd.
+     */
+    public static void logResults(Set<AccessibilityCheckResultReported> results) {
+        Slog.i(LOG_TAG, String.format("Writing %d AccessibilityCheckResultReported events",
+                results.size()));
+
+        for (AccessibilityCheckResultReported result : results) {
+            AccessibilityCheckerStatsLog.write(ATOM_ID,
+                    result.getPackageName(),
+                    result.getAppVersionCode(),
+                    result.getUiElementPath(),
+                    result.getActivityName(),
+                    result.getWindowTitle(),
+                    result.getSourceComponentName(),
+                    result.getSourceVersionCode(),
+                    result.getResultCheckClass().getNumber(),
+                    result.getResultType().getNumber(),
+                    result.getResultId());
+        }
+    }
+}
diff --git a/services/accessibility/java/com/android/server/accessibility/a11ychecker/AccessibilityCheckerUtils.java b/services/accessibility/java/com/android/server/accessibility/a11ychecker/AccessibilityCheckerUtils.java
index 55af9a0..fa0fed2 100644
--- a/services/accessibility/java/com/android/server/accessibility/a11ychecker/AccessibilityCheckerUtils.java
+++ b/services/accessibility/java/com/android/server/accessibility/a11ychecker/AccessibilityCheckerUtils.java
@@ -19,11 +19,9 @@
 
 import android.annotation.Nullable;
 import android.content.ComponentName;
-import android.content.Context;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.util.Slog;
-import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityNodeInfo;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -62,6 +60,7 @@
 public class AccessibilityCheckerUtils {
 
     private static final String LOG_TAG = "AccessibilityCheckerUtils";
+
     @VisibleForTesting
     // LINT.IfChange
     static final Map<Class<? extends AccessibilityHierarchyCheck>, AccessibilityCheckClass>
@@ -94,30 +93,24 @@
     // LINT.ThenChange(/services/accessibility/java/com/android/server/accessibility/a11ychecker/proto/a11ychecker.proto)
 
     static Set<AccessibilityCheckResultReported> processResults(
-            Context context,
             AccessibilityNodeInfo nodeInfo,
             List<AccessibilityHierarchyCheckResult> checkResults,
-            @Nullable AccessibilityEvent accessibilityEvent,
-            ComponentName a11yServiceComponentName) {
-        return processResults(nodeInfo, checkResults, accessibilityEvent,
-                context.getPackageManager(), a11yServiceComponentName);
-    }
-
-    @VisibleForTesting
-    static Set<AccessibilityCheckResultReported> processResults(
-            AccessibilityNodeInfo nodeInfo,
-            List<AccessibilityHierarchyCheckResult> checkResults,
-            @Nullable AccessibilityEvent accessibilityEvent,
+            @Nullable String activityClassName,
             PackageManager packageManager,
             ComponentName a11yServiceComponentName) {
         String appPackageName = nodeInfo.getPackageName().toString();
+        String nodePath = AccessibilityNodePathBuilder.createNodePath(nodeInfo);
+        if (nodePath == null) {
+            return Set.of();
+        }
         AccessibilityCheckResultReported.Builder builder;
         try {
             builder = AccessibilityCheckResultReported.newBuilder()
                     .setPackageName(appPackageName)
                     .setAppVersionCode(getAppVersionCode(packageManager, appPackageName))
-                    .setUiElementPath(AccessibilityNodePathBuilder.createNodePath(nodeInfo))
-                    .setActivityName(getActivityName(packageManager, accessibilityEvent))
+                    .setUiElementPath(nodePath)
+                    .setActivityName(
+                            getActivityName(packageManager, appPackageName, activityClassName))
                     .setWindowTitle(getWindowTitle(nodeInfo))
                     .setSourceComponentName(a11yServiceComponentName.flattenToString())
                     .setSourceVersionCode(
@@ -147,31 +140,23 @@
     }
 
     /**
-     * Returns the simple class name of the Activity providing the cache update, if available,
+     * Returns the simple class name of the Activity associated with the window, if available,
      * or an empty String if not.
      */
     @VisibleForTesting
     static String getActivityName(
-            PackageManager packageManager, @Nullable AccessibilityEvent accessibilityEvent) {
-        if (accessibilityEvent == null) {
+            PackageManager packageManager, String packageName, @Nullable String activityClassName) {
+        if (activityClassName == null) {
             return "";
         }
-        CharSequence activityName = accessibilityEvent.getClassName();
-        if (accessibilityEvent.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED
-                && accessibilityEvent.getPackageName() != null
-                && activityName != null) {
-            try {
-                // Check class is for a valid Activity.
-                packageManager
-                        .getActivityInfo(
-                                new ComponentName(accessibilityEvent.getPackageName().toString(),
-                                        activityName.toString()), 0);
-                int qualifierEnd = activityName.toString().lastIndexOf('.');
-                return activityName.toString().substring(qualifierEnd + 1);
-            } catch (PackageManager.NameNotFoundException e) {
-                // No need to spam the logs. This is very frequent when the class doesn't match
-                // an activity.
-            }
+        try {
+            // Check class is for a valid Activity.
+            packageManager.getActivityInfo(new ComponentName(packageName, activityClassName), 0);
+            int qualifierEnd = activityClassName.lastIndexOf('.');
+            return activityClassName.substring(qualifierEnd + 1);
+        } catch (PackageManager.NameNotFoundException e) {
+            // No need to spam the logs. This is very frequent when the class doesn't match
+            // an activity.
         }
         return "";
     }
diff --git a/services/accessibility/java/com/android/server/accessibility/a11ychecker/AccessibilityNodePathBuilder.java b/services/accessibility/java/com/android/server/accessibility/a11ychecker/AccessibilityNodePathBuilder.java
index bbfb217..465ce0d 100644
--- a/services/accessibility/java/com/android/server/accessibility/a11ychecker/AccessibilityNodePathBuilder.java
+++ b/services/accessibility/java/com/android/server/accessibility/a11ychecker/AccessibilityNodePathBuilder.java
@@ -47,12 +47,15 @@
      *
      * <p>This format is consistent with elements paths in Pre-Launch Reports and the Accessibility
      * Scanner, starting from the window's root node instead of the first resource name.
-     * TODO (b/344607035): link to ClusteringUtils when AATF is merged in main.
+     * See {@link com.google.android.apps.common.testing.accessibility.framework.ClusteringUtils}.
      */
     public static @Nullable String createNodePath(@NonNull AccessibilityNodeInfo nodeInfo) {
+        String packageName = nodeInfo.getPackageName().toString();
+        if (packageName == null) {
+            return null;
+        }
         StringBuilder resourceIdBuilder = getNodePathBuilder(nodeInfo);
-        return resourceIdBuilder == null ? null : String.valueOf(nodeInfo.getPackageName()) + ':'
-                + resourceIdBuilder;
+        return resourceIdBuilder == null ? null : packageName + ':' + resourceIdBuilder;
     }
 
     private static @Nullable StringBuilder getNodePathBuilder(AccessibilityNodeInfo nodeInfo) {
@@ -84,20 +87,23 @@
     //Returns the part of the element's View ID resource name after the qualifier
     // "package_name:id/"  or the last '/', when available. Otherwise, returns the element's
     // simple class name.
-    private static CharSequence getShortUiElementName(AccessibilityNodeInfo nodeInfo) {
+    private static @Nullable CharSequence getShortUiElementName(AccessibilityNodeInfo nodeInfo) {
         String viewIdResourceName = nodeInfo.getViewIdResourceName();
-        if (viewIdResourceName != null) {
-            String idQualifier = ":id/";
-            int idQualifierStartIndex = viewIdResourceName.indexOf(idQualifier);
-            int unqualifiedNameStartIndex = idQualifierStartIndex == -1 ? 0
-                    : (idQualifierStartIndex + idQualifier.length());
-            return viewIdResourceName.substring(unqualifiedNameStartIndex);
+        if (viewIdResourceName == null) {
+            return getSimpleClassName(nodeInfo);
         }
-        return getSimpleClassName(nodeInfo);
+        String idQualifier = ":id/";
+        int idQualifierStartIndex = viewIdResourceName.indexOf(idQualifier);
+        int unqualifiedNameStartIndex =
+                idQualifierStartIndex == -1 ? 0 : (idQualifierStartIndex + idQualifier.length());
+        return viewIdResourceName.substring(unqualifiedNameStartIndex);
     }
 
-    private static CharSequence getSimpleClassName(AccessibilityNodeInfo nodeInfo) {
+    private static @Nullable CharSequence getSimpleClassName(AccessibilityNodeInfo nodeInfo) {
         CharSequence name = nodeInfo.getClassName();
+        if (name == null) {
+            return null;
+        }
         for (int i = name.length() - 1; i > 0; i--) {
             char ithChar = name.charAt(i);
             if (ithChar == '.' || ithChar == '$') {
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/MultiTap.java b/services/accessibility/java/com/android/server/accessibility/gestures/MultiTap.java
index af5433e..95559802 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/MultiTap.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/MultiTap.java
@@ -43,12 +43,18 @@
 
     public MultiTap(Context context, int taps, int gesture,
             GestureMatcher.StateChangeListener listener) {
+        this(context, taps, gesture,
+                ViewConfiguration.getDoubleTapTimeout(), listener);
+    }
+
+    public MultiTap(Context context, int taps, int gesture, int multiTapTimeout,
+            GestureMatcher.StateChangeListener listener) {
         super(gesture, new Handler(context.getMainLooper()), listener);
         mTargetTaps = taps;
+        mDoubleTapTimeout = multiTapTimeout;
         mDoubleTapSlop = ViewConfiguration.get(context).getScaledDoubleTapSlop();
         mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
         mTapTimeout = ViewConfiguration.getTapTimeout();
-        mDoubleTapTimeout = ViewConfiguration.getDoubleTapTimeout();
         clear();
     }
 
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/MultiTapAndHold.java b/services/accessibility/java/com/android/server/accessibility/gestures/MultiTapAndHold.java
index 8363eca..15e1278 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/MultiTapAndHold.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/MultiTapAndHold.java
@@ -30,6 +30,11 @@
         super(context, taps, gesture, listener);
     }
 
+    public MultiTapAndHold(Context context, int taps, int gesture, int multiTapTimeout,
+            GestureMatcher.StateChangeListener listener) {
+        super(context, taps, gesture, multiTapTimeout, listener);
+    }
+
     @Override
     protected void onDown(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
         super.onDown(event, rawEvent, policyFlags);
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
index e9c3fbd..159022b 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
@@ -523,6 +523,7 @@
             mScaleGestureDetector = new ScaleGestureDetector(context, this, Handler.getMain());
             mScaleGestureDetector.setQuickScaleEnabled(false);
             mScrollGestureDetector = new GestureDetector(context, this, Handler.getMain());
+            mScrollGestureDetector.setIsLongpressEnabled(false);
         }
 
         @Override
@@ -1167,9 +1168,8 @@
 
         DetectingState(Context context) {
             mLongTapMinDelay = ViewConfiguration.getLongPressTimeout();
-            mMultiTapMaxDelay = ViewConfiguration.getDoubleTapTimeout()
-                    + context.getResources().getInteger(
-                    R.integer.config_screen_magnification_multi_tap_adjustment);
+            mMultiTapMaxDelay =
+                    MagnificationGestureMatcher.getMagnificationMultiTapTimeout(context);
             mSwipeMinDistance = ViewConfiguration.get(context).getScaledTouchSlop();
             mMultiTapMaxDistance = ViewConfiguration.get(context).getScaledDoubleTapSlop();
         }
@@ -1658,11 +1658,12 @@
         }
         float dX = event.getX() - firstPointerDownLocation.x;
         float dY = event.getY() - firstPointerDownLocation.y;
-        if (isAtLeftEdge() && dX > 0) {
+        if (isAtLeftEdge() && isScrollingLeft(dX, dY)) {
             return OVERSCROLL_LEFT_EDGE;
-        } else if (isAtRightEdge() && dX < 0) {
+        } else if (isAtRightEdge() && isScrollingRight(dX, dY)) {
             return OVERSCROLL_RIGHT_EDGE;
-        } else if ((isAtTopEdge() && dY > 0) || (isAtBottomEdge() && dY < 0)) {
+        } else if ((isAtTopEdge() && isScrollingUp(dX, dY))
+                || (isAtBottomEdge() && isScrollingDown(dX, dY))) {
             return OVERSCROLL_VERTICAL_EDGE;
         }
         return OVERSCROLL_NONE;
@@ -1672,18 +1673,34 @@
         return mFullScreenMagnificationController.isAtLeftEdge(mDisplayId, mOverscrollEdgeSlop);
     }
 
+    private static boolean isScrollingLeft(float dX, float dY) {
+        return Math.abs(dX) > Math.abs(dY) && dX > 0;
+    }
+
     private boolean isAtRightEdge() {
         return mFullScreenMagnificationController.isAtRightEdge(mDisplayId, mOverscrollEdgeSlop);
     }
 
+    private static boolean isScrollingRight(float dX, float dY) {
+        return Math.abs(dX) > Math.abs(dY) && dX < 0;
+    }
+
     private boolean isAtTopEdge() {
         return mFullScreenMagnificationController.isAtTopEdge(mDisplayId, mOverscrollEdgeSlop);
     }
 
+    private static boolean isScrollingUp(float dX, float dY) {
+        return Math.abs(dX) < Math.abs(dY) && dY > 0;
+    }
+
     private boolean isAtBottomEdge() {
         return mFullScreenMagnificationController.isAtBottomEdge(mDisplayId, mOverscrollEdgeSlop);
     }
 
+    private static boolean isScrollingDown(float dX, float dY) {
+        return Math.abs(dX) < Math.abs(dY) && dY < 0;
+    }
+
     private boolean pointerValid(PointF pointerDownLocation) {
         return !(Float.isNaN(pointerDownLocation.x) && Float.isNaN(pointerDownLocation.y));
     }
@@ -1876,6 +1893,7 @@
         private MotionEventInfo mEvent;
         SinglePanningState(Context context) {
             mScrollGestureDetector = new GestureDetector(context, this, Handler.getMain());
+            mScrollGestureDetector.setIsLongpressEnabled(false);
         }
 
         @Override
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java
index 75d01f5..6f1141f 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java
@@ -470,12 +470,14 @@
                         mDetectSingleFingerTripleTap
                                 ? MagnificationGestureMatcher.GESTURE_TRIPLE_TAP
                                 : MagnificationGestureMatcher.GESTURE_SINGLE_TAP,
+                        MagnificationGestureMatcher.getMagnificationMultiTapTimeout(mContext),
                         null));
                 mGestureMatchers.add(new MultiTapAndHold(context,
                         mDetectSingleFingerTripleTap ? 3 : 1,
                         mDetectSingleFingerTripleTap
                                 ? MagnificationGestureMatcher.GESTURE_TRIPLE_TAP_AND_HOLD
                                 : MagnificationGestureMatcher.GESTURE_SINGLE_TAP_AND_HOLD,
+                        MagnificationGestureMatcher.getMagnificationMultiTapTimeout(mContext),
                         null));
                 mGestureMatchers.add(new TwoFingersDownOrSwipe(context));
 
@@ -495,12 +497,16 @@
                         mDetectSingleFingerTripleTap ? 3 : 1,
                         mDetectSingleFingerTripleTap
                                 ? MagnificationGestureMatcher.GESTURE_TRIPLE_TAP
-                                : MagnificationGestureMatcher.GESTURE_SINGLE_TAP, null);
+                                : MagnificationGestureMatcher.GESTURE_SINGLE_TAP,
+                        MagnificationGestureMatcher.getMagnificationMultiTapTimeout(mContext),
+                        null);
                 final MultiTapAndHold multiTapAndHold = new MultiTapAndHold(context,
                         mDetectSingleFingerTripleTap ? 3 : 1,
                         mDetectSingleFingerTripleTap
                                 ? MagnificationGestureMatcher.GESTURE_TRIPLE_TAP_AND_HOLD
-                                : MagnificationGestureMatcher.GESTURE_SINGLE_TAP_AND_HOLD, null);
+                                : MagnificationGestureMatcher.GESTURE_SINGLE_TAP_AND_HOLD,
+                        MagnificationGestureMatcher.getMagnificationMultiTapTimeout(mContext),
+                        null);
                 mGesturesObserver = new MagnificationGesturesObserver(this,
                         new SimpleSwipe(context),
                         multiTap,
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 76f6d17..569615e 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -173,27 +173,35 @@
 
 class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBackupProvider,
         OnCrossProfileWidgetProvidersChangeListener {
+    // Name of the tag associated with the system logs generated by this service.
     private static final String TAG = "AppWidgetServiceImpl";
-
+    // Simple flag to enable/disable debug logging.
     private static final boolean DEBUG = Build.IS_DEBUGGABLE;
 
+    // String constants for XML schema migration related to changes in keyguard package.
     private static final String OLD_KEYGUARD_HOST_PACKAGE = "android";
     private static final String NEW_KEYGUARD_HOST_PACKAGE = "com.android.keyguard";
     private static final int KEYGUARD_HOST_ID = 0x4b455947;
 
+    // Filename for app widgets state persisted on disk.
     private static final String STATE_FILENAME = "appwidgets.xml";
 
+    // XML tag for widget size options of each individual widget when persisted on disk.
     private static final String KEY_SIZES = "sizes";
 
+    // Minimum amount of time in millieconds before a widget is updated.
     private static final int MIN_UPDATE_PERIOD = DEBUG ? 0 : 30 * 60 * 1000; // 30 minutes
 
+    // Default value of {@link Provider#tag} and {@link Host#tag}.
     private static final int TAG_UNDEFINED = -1;
 
+    // Default uid of {@link ProviderId} when corresponding app haven't been installed yet.
     private static final int UNKNOWN_UID = -1;
 
+    // Default return value when we can't find the parent of a given profileId.
     private static final int UNKNOWN_USER_ID = -10;
 
-    // Bump if the stored widgets need to be upgraded.
+    // Version of XML schema for app widgets. Bump if the stored widgets need to be upgraded.
     private static final int CURRENT_VERSION = 1;
 
     // Every widget update request is associated which an increasing sequence number. This is
@@ -205,9 +213,12 @@
             Duration.ofHours(1).toMillis();
     // Default max API calls per reset interval for generated preview API rate limiting.
     private static final int DEFAULT_GENERATED_PREVIEW_MAX_CALLS_PER_INTERVAL = 2;
-
+    // XML attribute for widget ids that are pending deletion.
+    // See {@link Provider#pendingDeletedWidgetIds}.
     private static final String PENDING_DELETED_IDS_ATTR = "pending_deleted_ids";
 
+    // Handles user and package related broadcasts.
+    // See {@link #registerBroadcastReceiver}
     private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
@@ -249,18 +260,27 @@
     private final HashMap<Pair<Integer, FilterComparison>, HashSet<Integer>>
             mRemoteViewsServicesAppWidgets = new HashMap<>();
 
+    // Synchronization lock for internal states in this service.
+    // TODO: Add GuardedBy annotation on states that need to be guarded.
     private final Object mLock = new Object();
 
+    // Instances of actual widgets currently bound to each host.
     private final ArrayList<Widget> mWidgets = new ArrayList<>();
+    // Information about the host apps that has one or more widgets bound to it.
     private final ArrayList<Host> mHosts = new ArrayList<>();
+    // Information about the provider apps who indicates that they provide App Widgets
+    // in their manifest.
     private final ArrayList<Provider> mProviders = new ArrayList<>();
-
+    // Pairs of (userId, packageName) which has explicit consent from user to
+    // hold the MODIFY_APPWIDGET_BIND_PERMISSIONS permission.
+    // See {@link AppWidgetManager#setBindAppWidgetPermission}
     private final ArraySet<Pair<Integer, String>> mPackagesWithBindWidgetPermission =
             new ArraySet<>();
-
+    // Ids of users whose widgets/provider/hosts have been loaded from disk.
     private final SparseBooleanArray mLoadedUserIds = new SparseBooleanArray();
-
+    // Synchronization lock dedicated to {@link #mWidgetPackages}.
     private final Object mWidgetPackagesLock = new Object();
+    // Set of packages that has at least one widget bounded by a host, keyed on userId.
     private final SparseArray<ArraySet<String>> mWidgetPackages = new SparseArray<>();
 
     private BackupRestoreController mBackupRestoreController;
@@ -280,18 +300,30 @@
 
     private SecurityPolicy mSecurityPolicy;
 
+    // Handler to the background thread that saves states to disk.
     private Handler mSaveStateHandler;
+    // Handler to the foreground thread that handles broadcasts related to user
+    // and package events, as well as various internal events within
+    // AppWidgetService.
     private Handler mCallbackHandler;
-
+    // Map of user id to the next app widget id (monotonically increasing integer)
+    // that can be allocated for a new app widget.
+    // See {@link AppWidgetHost#allocateAppWidgetId}.
     private final SparseIntArray mNextAppWidgetIds = new SparseIntArray();
-
+    // Indicates whether the device is running in safe mode.
     private boolean mSafeMode;
+    // Load time validation of maximum memory can be used in widget bitmaps.
     private int mMaxWidgetBitmapMemory;
+    // Feature flag that indicates whether
+    // {@link AppWidgetManager#ACTION_APPWIDGET_ENABLED} and
+    // {@linkAppWidgetManager#ACTION_APPWIDGET_UPDATE} are combined into a
+    // single broadcast.
     private boolean mIsCombinedBroadcastEnabled;
 
     // Mark widget lifecycle broadcasts as 'interactive'
     private Bundle mInteractiveBroadcast;
-
+    // Counter that keeps track of how many times generated preview API are
+    // being called to ensure they are subject to rate limiting.
     private ApiCounter mGeneratedPreviewsApiCounter;
 
     AppWidgetServiceImpl(Context context) {
@@ -345,17 +377,29 @@
         LocalServices.addService(AppWidgetManagerInternal.class, new AppWidgetManagerLocal());
     }
 
+    /**
+     * Returns the maximum memory can be used in widget bitmaps, in respect to
+     * the display size. Note this should only be called after
+     * {@link #computeMaximumWidgetBitmapMemory} is invoked.
+     */
     @Override
     public int getMaxBitmapMemory() {
         return mMaxWidgetBitmapMemory;
     }
 
+    /**
+     * Signals that system services (esp. ActivityManagerService) are ready.
+     */
     void systemServicesReady() {
         mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
         mAppOpsManagerInternal = LocalServices.getService(AppOpsManagerInternal.class);
         mUsageStatsManagerInternal = LocalServices.getService(UsageStatsManagerInternal.class);
     }
 
+    /**
+     * Computes the maximum memory can be used in widget bitmaps, in respect to
+     * the display size.
+     */
     private void computeMaximumWidgetBitmapMemory() {
         Display display = mContext.getDisplayNoVerify();
         Point size = new Point();
@@ -365,6 +409,10 @@
         mMaxWidgetBitmapMemory = 6 * size.x * size.y;
     }
 
+    /**
+     * Callback function that persists the states of the widgets to disk,
+     * should be scheduled on a background thread.
+     */
     private boolean handleSaveMessage(Message msg) {
         final int userId = msg.what;
         SparseArray<byte[]> userIdToBytesMapping;
@@ -403,6 +451,9 @@
         return true;
     }
 
+    /**
+     * Register receivers for system broadcasts, esp. broadcasts from package manager.
+     */
     private void registerBroadcastReceiver() {
         // Register for broadcasts about package install, etc., so we can
         // update the provider list.
@@ -438,6 +489,13 @@
                 suspendPackageFilter, null, mCallbackHandler);
     }
 
+    /**
+     * Listens to cross-profile widget providers changes.
+     *
+     * @see #onCrossProfileWidgetProvidersChanged
+     * @see DevicePolicyManager#addCrossProfileWidgetProvider
+     * @see DevicePolicyManager#removeCrossProfileWidgetProvider
+     */
     private void registerOnCrossProfileProvidersChangedListener() {
         // The device policy is an optional component.
         if (mDevicePolicyManagerInternal != null) {
@@ -449,6 +507,11 @@
         mSafeMode = safeMode;
     }
 
+    /**
+     * Handles broadcasts from package manager, add/remove/update widget
+     * providers in respect to changes in corresponding packages.
+     * Note: When a package is archived, it is treated as removed.
+     */
     private void onPackageBroadcastReceived(Intent intent, int userId) {
         final String action = intent.getAction();
         boolean added = false;
@@ -553,6 +616,10 @@
         }
     }
 
+    /**
+     * Clears the generated previews for all widgets belonging to the given UID.
+     * @return true if any previews were cleared.
+     */
     @GuardedBy("mLock")
     private boolean clearPreviewsForUidLocked(int clearedUid) {
         boolean changed = false;
@@ -584,6 +651,11 @@
         }
     }
 
+    /**
+     * Reload all widgets' masked state for the given user or profile.
+     * Keep track of whether the given user or profile is locked, in quiet mode,
+     * suspended or stopped.
+     */
     private void reloadWidgetsMaskedState(int userId) {
         final long identity = Binder.clearCallingIdentity();
         try {
@@ -827,6 +899,10 @@
         }
     }
 
+    /**
+     * Unmask widgets of the specified provider. Notify the host to remove the masked views
+     * if previously masked.
+     */
     private void unmaskWidgetsViewsLocked(Provider provider) {
         final int widgetCount = provider.widgets.size();
         for (int j = 0; j < widgetCount; j++) {
@@ -837,6 +913,10 @@
         }
     }
 
+    /**
+     * Called when a new package is installed, and updates {@link HostId} in corresponding
+     * {@link Host}.
+     */
     private void resolveHostUidLocked(String pkg, int uid) {
         final int N = mHosts.size();
         for (int i = 0; i < N; i++) {
@@ -851,11 +931,28 @@
         }
     }
 
+    /**
+     * Load widgets/providers/hosts for the specified user and all of its enabled
+     * child profiles from disk if not already loaded.
+     *
+     * @param userId the user id to load
+     *
+     * @see #ensureGroupStateLoadedLocked(int, boolean)
+     */
     @GuardedBy("mLock")
     private void ensureGroupStateLoadedLocked(int userId) {
         ensureGroupStateLoadedLocked(userId, /* enforceUserUnlockingOrUnlocked */ true );
     }
 
+    /**
+     * Load widgets/providers/hosts for the specified user and all of its enabled
+     * child profiles from disk if not already loaded.
+     *
+     * @param userId the user id to load
+     * @param enforceUserUnlockingOrUnlocked if true, the user must be unlocked or unlocking
+     * @throws IllegalStateException if the user or profile is not unlocked or unlocking and
+     * {@code enforceUserUnlockingOrUnlocked} is true
+     */
     @GuardedBy("mLock")
     private void ensureGroupStateLoadedLocked(int userId, boolean enforceUserUnlockingOrUnlocked) {
         if (enforceUserUnlockingOrUnlocked && !isUserRunningAndUnlocked(userId)) {
@@ -971,6 +1068,9 @@
         }
     }
 
+    /**
+     * Called by {@link AppWidgetHost} to start listening for updates from specified widgets.
+     */
     @Override
     public ParceledListSlice<PendingHostUpdate> startListening(IAppWidgetHost callbacks,
             String callingPackage, int hostId, int[] appWidgetIds) {
@@ -1017,6 +1117,10 @@
         }
     }
 
+    /**
+     * Called by {@link AppWidgetHost} to stop listening for updates from all
+     * widgets bounded to this host.
+     */
     @Override
     public void stopListening(String callingPackage, int hostId) {
         final int userId = UserHandle.getCallingUserId();
@@ -1045,6 +1149,10 @@
         }
     }
 
+    /**
+     * Creates a new instance of app widget and associate it with the specified host.
+     * Allocate a new app widget id for the new instance.
+     */
     @Override
     public int allocateAppWidgetId(String callingPackage, int hostId) {
         final int userId = UserHandle.getCallingUserId();
@@ -1094,6 +1202,12 @@
         }
     }
 
+    /**
+     * Called by {@link AppWidgetHost} to mark all widgets associated with this host
+     * to be visually hidden (for state tracking).
+     *
+     * @see AppOpsManagerInternal#updateAppWidgetVisibility
+     */
     @Override
     public void setAppWidgetHidden(String callingPackage, int hostId) {
         final int userId = UserHandle.getCallingUserId();
@@ -1117,6 +1231,11 @@
         }
     }
 
+    /**
+     * Deletes specified widget.
+     * Note: appWidgetId is a monotonic increasing number, so the appWidgetId cannot be
+     * reclaimed by a new widget.
+     */
     @Override
     public void deleteAppWidgetId(String callingPackage, int appWidgetId) {
         final int userId = UserHandle.getCallingUserId();
@@ -1151,8 +1270,19 @@
         }
     }
 
+    /**
+     * Query if a given package was granted permission by the user to bind app widgets.
+     *
+     * <p class="note">You need the MODIFY_APPWIDGET_BIND_PERMISSIONS permission
+     *
+     * @param packageName The package for which the permission is being queried
+     * @param userId The user id of the user under which the package runs.
+     * @return true if the package was granted permission by the user to bind app widgets
+     *
+     * @see AppWidgetManager#hasBindAppWidgetPermission(String, int)
+     */
     @Override
-    public boolean hasBindAppWidgetPermission(String packageName, int grantId) {
+    public boolean hasBindAppWidgetPermission(String packageName, int userId) {
         if (DEBUG) {
             Slog.i(TAG, "hasBindAppWidgetPermission() " + UserHandle.getCallingUserId());
         }
@@ -1162,20 +1292,31 @@
 
         synchronized (mLock) {
             // The grants are stored in user state wich gets the grant.
-            ensureGroupStateLoadedLocked(grantId);
+            ensureGroupStateLoadedLocked(userId);
 
-            final int packageUid = getUidForPackage(packageName, grantId);
+            final int packageUid = getUidForPackage(packageName, userId);
             if (packageUid < 0) {
                 return false;
             }
 
-            Pair<Integer, String> packageId = Pair.create(grantId, packageName);
+            Pair<Integer, String> packageId = Pair.create(userId, packageName);
             return mPackagesWithBindWidgetPermission.contains(packageId);
         }
     }
 
+    /**
+     * Changes any user-granted permission for the given package to bind app widgets.
+     *
+     * <p class="note">You need the MODIFY_APPWIDGET_BIND_PERMISSIONS permission
+     *
+     * @param packageName The package whose permission is being changed
+     * @param userId The user under which the package is running.
+     * @param permission Whether to give the package permission to bind widgets
+     *
+     * @see AppWidgetManager#setBindAppWidgetPermission(String, int, boolean)
+     */
     @Override
-    public void setBindAppWidgetPermission(String packageName, int grantId,
+    public void setBindAppWidgetPermission(String packageName, int userId,
             boolean grantPermission) {
         if (DEBUG) {
             Slog.i(TAG, "setBindAppWidgetPermission() " + UserHandle.getCallingUserId());
@@ -1186,24 +1327,42 @@
 
         synchronized (mLock) {
             // The grants are stored in user state wich gets the grant.
-            ensureGroupStateLoadedLocked(grantId);
+            ensureGroupStateLoadedLocked(userId);
 
-            final int packageUid = getUidForPackage(packageName, grantId);
+            final int packageUid = getUidForPackage(packageName, userId);
             if (packageUid < 0) {
                 return;
             }
 
-            Pair<Integer, String> packageId = Pair.create(grantId, packageName);
+            Pair<Integer, String> packageId = Pair.create(userId, packageName);
             if (grantPermission) {
                 mPackagesWithBindWidgetPermission.add(packageId);
             } else {
                 mPackagesWithBindWidgetPermission.remove(packageId);
             }
 
-            saveGroupStateAsync(grantId);
+            saveGroupStateAsync(userId);
         }
     }
 
+    /**
+     * Called by {@link AppWidgetHost} to start app widget provider configure
+     * activity for result.
+     * This method is used if the provider is in a profile different from the host
+     * as the host is not allowed to start an activity in another profile.
+     * <p>
+     * Note that the provided app widget has to be bound for this method to work.
+     * </p>
+     *
+     * @param callingPackage Package that calls this method.
+     * @param appWidgetId The bound app widget whose provider's config activity to start.
+     * @param intentFlags Optional intent flags.
+     * @return IntentSender to start the config activity.
+     * @throws IllegalArgumentException If the widget is not found.
+     *
+     * @see AppWidgetProviderInfo#getProfile()
+     * @see AppWidgetHost#startAppWidgetConfigureActivityForResult
+     */
     @Override
     public IntentSender createAppWidgetConfigIntentSender(String callingPackage, int appWidgetId,
             final int intentFlags) {
@@ -1259,6 +1418,23 @@
         }
     }
 
+    /**
+     * Associates an {@link Widget} (as specified by {@code appWidgetId}) with
+     * a {@link Provider} (as specified by {@code providerComponent}) from
+     * a specific user/profile, if applicable.
+     *
+     * Note: The {@link Widget} itself is already associated with its {@link Host}
+     * in {@link #allocateAppWidgetId}.
+     *
+     * @param callingPackage The package that calls this method.
+     * @param appWidgetId The id of theapp widget to bind.
+     * @param providerProfileId The user/profile id of the provider.
+     * @param providerComponent The {@link ComponentName} that provides the widget.
+     * @param options The options to pass to the provider.
+     * @see AppWidgetManager#bindAppWidgetIdIfAllowed(int, ComponentName)
+     * @see AppWidgetManager#bindAppWidgetIdIfAllowed(int, ComponentName, Bundle)
+     * @see AppWidgetManager#bindAppWidgetIdIfAllowed(int, UserHandle, ComponentName, Bundle)
+     */
     @Override
     public boolean bindAppWidgetId(String callingPackage, int appWidgetId,
             int providerProfileId, ComponentName providerComponent, Bundle options) {
@@ -1368,6 +1544,17 @@
         return true;
     }
 
+    /**
+     * Get the list of appWidgetIds that have been bound to the given AppWidget
+     * provider.
+     *
+     * Note: User can create multiple instances of {@link Widget} that are
+     * supplied by the same {@link Provider}.
+     *
+     * @param provider The {@link android.content.BroadcastReceiver} that is the
+     *            AppWidget provider to find appWidgetIds for.
+     * @see AppWidgetManager#getAppWidgetIds(ComponentName)
+     */
     @Override
     public int[] getAppWidgetIds(ComponentName componentName) {
         final int userId = UserHandle.getCallingUserId();
@@ -1395,6 +1582,13 @@
         }
     }
 
+    /**
+     * Gets a list of appWidgetIds that are bound to the specified host.
+     *
+     * @param callingPackage The package that calls this method.
+     * @param hostId id of the {@link Host}.
+     * @rerurn int[] list of appWidgetIds that are bound to this host.
+     */
     @Override
     public int[] getAppWidgetIdsForHost(String callingPackage, int hostId) {
         final int userId = UserHandle.getCallingUserId();
@@ -1422,6 +1616,31 @@
         }
     }
 
+    /**
+     * Binds the RemoteViewsService for a given appWidgetId and intent.
+     * This method is used by {@link RemoteViewsAdapter} to establish a connection
+     * to the {@link RemoteViewsService} that provides data for the adapter.
+     *
+     * The appWidgetId specified must already be bound to the calling AppWidgetHost via
+     * {@link android.appwidget.AppWidgetManager#bindAppWidgetId AppWidgetManager.bindAppWidgetId()}.
+     *
+     * Note: Since {@link AppWidgetManager#setRemoteAdapter(int, RemoteViewsAdapter))} is deprecated,
+     * this method is effectively deprecated as well.
+     *
+     * @param callingPackage The package that calls this method.
+     * @param appWidgetId   The AppWidget instance for which to bind the RemoteViewsService.
+     * @param intent        The intent of the service which will be providing the data to the
+     *                      RemoteViewsAdapter.
+     * @param caller        Caller's {@link IApplicationThread}, see
+     *                      {@link Context#getIApplicationThread()}
+     * @param activityToken Caller's {@link IBinder}, see {@link Context#getActivityToken()}
+     * @param connection    The callback interface to be notified when a connection is made or lost.
+     * @param flags         Flags used for binding to the service. Only
+     *                     {@link Context#BIND_AUTO_CREATE} and
+     *                     {@link Context#BIND_FOREGROUND_SERVICE_WHILE_AWAKE} are supported.
+     *
+     * @see AppWidgetManager#setRemoteAdapter(int, RemoteViewsAdapter)
+     */
     @Override
     public boolean bindRemoteViewsService(String callingPackage, int appWidgetId, Intent intent,
             IApplicationThread caller, IBinder activtiyToken, IServiceConnection connection,
@@ -4173,6 +4392,11 @@
         }
     }
 
+    /**
+     * Callback functions that add/update/remove widget providers in respect to
+     * changes in a specific child profile (e.g. deleting a work profile)
+     * depicted by DevicePolicyManager.
+     */
     @Override
     public void onCrossProfileWidgetProvidersChanged(int userId, List<String> packages) {
         final int parentId = mSecurityPolicy.getProfileParent(userId);
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index eae516e..9f7fb57 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -1985,12 +1985,13 @@
         }
 
         @Override
-        public void setAutofillFailure(int sessionId, @NonNull List<AutofillId> ids, int userId) {
+        public void setAutofillFailure(
+                int sessionId, @NonNull List<AutofillId> ids, boolean isRefill, int userId) {
             synchronized (mLock) {
                 final AutofillManagerServiceImpl service =
                         peekServiceForUserWithLocalBinderIdentityLocked(userId);
                 if (service != null) {
-                    service.setAutofillFailureLocked(sessionId, getCallingUid(), ids);
+                    service.setAutofillFailureLocked(sessionId, getCallingUid(), ids, isRefill);
                 } else if (sVerbose) {
                     Slog.v(TAG, "setAutofillFailure(): no service for " + userId);
                 }
@@ -2011,6 +2012,46 @@
         }
 
         @Override
+        public void notifyNotExpiringResponseDuringAuth(int sessionId, int userId) {
+            synchronized (mLock) {
+                final AutofillManagerServiceImpl service =
+                        peekServiceForUserWithLocalBinderIdentityLocked(userId);
+                if (service != null) {
+                    service.notifyNotExpiringResponseDuringAuth(sessionId, getCallingUid());
+                } else if (sVerbose) {
+                    Slog.v(TAG, "notifyNotExpiringResponseDuringAuth(): no service for " + userId);
+                }
+            }
+        }
+
+        @Override
+        public void notifyViewEnteredIgnoredDuringAuthCount(int sessionId, int userId) {
+            synchronized (mLock) {
+                final AutofillManagerServiceImpl service =
+                        peekServiceForUserWithLocalBinderIdentityLocked(userId);
+                if (service != null) {
+                    service.notifyViewEnteredIgnoredDuringAuthCount(sessionId, getCallingUid());
+                } else if (sVerbose) {
+                    Slog.v(TAG, "notifyNotExpiringResponseDuringAuth(): no service for " + userId);
+                }
+            }
+        }
+
+        @Override
+        public void setAutofillIdsAttemptedForRefill(
+                int sessionId, @NonNull List<AutofillId> ids, int userId) {
+            synchronized (mLock) {
+                final AutofillManagerServiceImpl service =
+                        peekServiceForUserWithLocalBinderIdentityLocked(userId);
+                if (service != null) {
+                    service.setAutofillIdsAttemptedForRefill(sessionId, ids, getCallingUid());
+                } else if (sVerbose) {
+                    Slog.v(TAG, "setAutofillIdsAttemptedForRefill(): no service for " + userId);
+                }
+            }
+        }
+
+        @Override
         public void finishSession(int sessionId, int userId,
                 @AutofillCommitReason int commitReason) {
             synchronized (mLock) {
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 2bf319e..c9f8929 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -464,7 +464,8 @@
     }
 
     @GuardedBy("mLock")
-    void setAutofillFailureLocked(int sessionId, int uid, @NonNull List<AutofillId> ids) {
+    void setAutofillFailureLocked(
+            int sessionId, int uid, @NonNull List<AutofillId> ids, boolean isRefill) {
         if (!isEnabledLocked()) {
             Slog.wtf(TAG, "Service not enabled");
             return;
@@ -474,7 +475,7 @@
             Slog.v(TAG, "setAutofillFailure(): no session for " + sessionId + "(" + uid + ")");
             return;
         }
-        session.setAutofillFailureLocked(ids);
+        session.setAutofillFailureLocked(ids, isRefill);
     }
 
     @GuardedBy("mLock")
@@ -492,6 +493,52 @@
     }
 
     @GuardedBy("mLock")
+    void notifyNotExpiringResponseDuringAuth(int sessionId, int uid) {
+        if (!isEnabledLocked()) {
+            Slog.wtf(TAG, "Service not enabled");
+            return;
+        }
+        final Session session = mSessions.get(sessionId);
+        if (session == null || uid != session.uid) {
+            Slog.v(TAG, "notifyNotExpiringResponseDuringAuth(): no session for "
+                    + sessionId + "(" + uid + ")");
+            return;
+        }
+        session.setNotifyNotExpiringResponseDuringAuth();
+    }
+
+    @GuardedBy("mLock")
+    void notifyViewEnteredIgnoredDuringAuthCount(int sessionId, int uid) {
+        if (!isEnabledLocked()) {
+            Slog.wtf(TAG, "Service not enabled");
+            return;
+        }
+        final Session session = mSessions.get(sessionId);
+        if (session == null || uid != session.uid) {
+            Slog.v(TAG, "notifyViewEnteredIgnoredDuringAuthCount(): no session for "
+                    + sessionId + "(" + uid + ")");
+            return;
+        }
+        session.setLogViewEnteredIgnoredDuringAuth();
+    }
+
+    @GuardedBy("mLock")
+    public void setAutofillIdsAttemptedForRefill(
+            int sessionId, @NonNull List<AutofillId> ids, int uid) {
+        if (!isEnabledLocked()) {
+            Slog.wtf(TAG, "Service not enabled");
+            return;
+        }
+        final Session session = mSessions.get(sessionId);
+        if (session == null || uid != session.uid) {
+            Slog.v(TAG, "setAutofillIdsAttemptedForRefill(): no session for "
+                    + sessionId + "(" + uid + ")");
+            return;
+        }
+        session.setAutofillIdsAttemptedForRefillLocked(ids);
+    }
+
+    @GuardedBy("mLock")
     void finishSessionLocked(int sessionId, int uid, @AutofillCommitReason int commitReason) {
         if (!isEnabledLocked()) {
             Slog.wtf(TAG, "Service not enabled");
diff --git a/services/autofill/java/com/android/server/autofill/Helper.java b/services/autofill/java/com/android/server/autofill/Helper.java
index ad8f5e1..668852b 100644
--- a/services/autofill/java/com/android/server/autofill/Helper.java
+++ b/services/autofill/java/com/android/server/autofill/Helper.java
@@ -31,12 +31,14 @@
 import android.metrics.LogMaker;
 import android.os.UserManager;
 import android.service.autofill.Dataset;
+import android.service.autofill.FillResponse;
 import android.service.autofill.InternalSanitizer;
 import android.service.autofill.SaveInfo;
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Slog;
+import android.util.SparseArray;
 import android.view.Display;
 import android.view.View;
 import android.view.WindowManager;
@@ -374,4 +376,50 @@
     private interface ViewNodeFilter {
         boolean matches(ViewNode node);
     }
+
+    public static class SaveInfoStats {
+        public int saveInfoCount;
+        public int saveDataTypeCount;
+
+        public SaveInfoStats(int saveInfoCount, int saveDataTypeCount) {
+            this.saveInfoCount = saveInfoCount;
+            this.saveDataTypeCount = saveDataTypeCount;
+        }
+    }
+
+    /**
+     * Get statistic information of save info given a sparse array of fill responses.
+     *
+     * Specifically the statistic includes
+     *   1. how many save info the current session has.
+     *   2. How many distinct save data types current session has.
+     *
+     * @return SaveInfoStats returns the above two number in a SaveInfoStats object
+     */
+    public static SaveInfoStats getSaveInfoStatsFromFillResponses(
+            SparseArray<FillResponse> fillResponses) {
+        if (fillResponses == null) {
+            if (sVerbose) {
+                Slog.v(TAG, "getSaveInfoStatsFromFillResponses(): fillResponse sparse array is "
+                        + "null");
+            }
+            return new SaveInfoStats(-1, -1);
+        }
+        int numSaveInfos = 0;
+        int numSaveDataTypes = 0;
+        ArraySet<Integer> saveDataTypeSeen = new ArraySet<>();
+        final int numResponses = fillResponses.size();
+        for (int responseNum = 0; responseNum < numResponses; responseNum++) {
+            final FillResponse response = fillResponses.valueAt(responseNum);
+            if (response != null && response.getSaveInfo() != null) {
+                numSaveInfos += 1;
+                int saveDataType = response.getSaveInfo().getType();
+                if (!saveDataTypeSeen.contains(saveDataType)) {
+                    saveDataTypeSeen.add(saveDataType);
+                    numSaveDataTypes += 1;
+                }
+            }
+        }
+        return new SaveInfoStats(numSaveInfos, numSaveDataTypes);
+    }
 }
diff --git a/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java b/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java
index a5ec2ba..930af5e 100644
--- a/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java
+++ b/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java
@@ -58,6 +58,7 @@
 import static com.android.server.autofill.Helper.sVerbose;
 
 import android.annotation.IntDef;
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.ComponentName;
 import android.content.Context;
@@ -685,6 +686,19 @@
     }
 
     /**
+     * Set views_fillable_total_count as long as mEventInternal presents.
+     */
+    public void maybeUpdateViewFillablesForRefillAttempt(List<AutofillId> autofillIds) {
+        mEventInternal.ifPresent(event -> {
+            // These autofill ids would be the ones being re-attempted.
+            event.mAutofillIdsAttemptedAutofill = new ArraySet<>(autofillIds);
+            // These autofill id's are being refilled, so they had failed previously.
+            // Note that these autofillIds correspond to the new autofill ids after relayout.
+            event.mFailedAutofillIds = new ArraySet<>(autofillIds);
+        });
+    }
+
+    /**
      * Set how many views are filtered from fill because they are not in current session
      */
     public void maybeSetFilteredFillableViewsCount(int filteredViewsCount) {
@@ -697,9 +711,16 @@
      * Set views_filled_failure_count using failure count as long as mEventInternal
      * presents.
      */
-    public void maybeSetViewFillFailureCounts(int failureCount) {
+    public void maybeSetViewFillFailureCounts(@NonNull List<AutofillId> ids, boolean isRefill) {
         mEventInternal.ifPresent(event -> {
-            event.mViewFillFailureCount = failureCount;
+            int failureCount = ids.size();
+            if (isRefill) {
+                event.mViewFailedOnRefillCount = failureCount;
+            } else {
+                event.mViewFillFailureCount = failureCount;
+                event.mViewFailedPriorToRefillCount = failureCount;
+                event.mFailedAutofillIds = new ArraySet<>(ids);
+            }
         });
     }
 
@@ -719,7 +740,7 @@
      * Set views_filled_failure_count using failure count as long as mEventInternal
      * presents.
      */
-    public void maybeAddSuccessId(AutofillId autofillId) {
+    public synchronized void maybeAddSuccessId(AutofillId autofillId) {
         mEventInternal.ifPresent(event -> {
             ArraySet<AutofillId> autofillIds = event.mAutofillIdsAttemptedAutofill;
             if (autofillIds == null) {
@@ -727,9 +748,21 @@
                         + " successfully filled");
                 event.mViewFilledButUnexpectedCount++;
             } else if (autofillIds.contains(autofillId)) {
-                if (sVerbose) {
-                    Slog.v(TAG, "Logging autofill for id:" + autofillId);
+                ArraySet<AutofillId> failedIds = event.mFailedAutofillIds;
+                if (failedIds.contains(autofillId)) {
+                    if (sVerbose) {
+                        Slog.v(TAG, "Logging autofill refill of id:" + autofillId);
+                    }
+                    // This indicates the success after refill attempt
+                    event.mViewFilledSuccessfullyOnRefillCount++;
+                    // Remove so if we don't reprocess duplicate requests
+                    failedIds.remove(autofillId);
+                } else {
+                    if (sVerbose) {
+                        Slog.v(TAG, "Logging autofill for id:" + autofillId);
+                    }
                 }
+                // Common actions to take irrespective of being filled by refill attempt or not.
                 event.mViewFillSuccessCount++;
                 autofillIds.remove(autofillId);
                 event.mAlreadyFilledAutofillIds.add(autofillId);
@@ -746,6 +779,23 @@
         });
     }
 
+    /**
+     * Set how many views are filtered from fill because they are not in current session
+     */
+    public void maybeSetNotifyNotExpiringResponseDuringAuth() {
+        mEventInternal.ifPresent(event -> {
+            event.mFixExpireResponseDuringAuthCount++;
+        });
+    }
+    /**
+     * Set how many views are filtered from fill because they are not in current session
+     */
+    public void notifyViewEnteredIgnoredDuringAuthCount() {
+        mEventInternal.ifPresent(event -> {
+            event.mNotifyViewEnteredIgnoredDuringAuthCount++;
+        });
+    }
+
     public void logAndEndEvent() {
         if (!mEventInternal.isPresent()) {
             Slog.w(TAG, "Shouldn't be logging AutofillPresentationEventReported again for same "
@@ -854,7 +904,6 @@
                 mCallingAppUid,
                 event.mIsCredentialRequest,
                 event.mWebviewRequestedCredential,
-                event.mFilteredFillabaleViewCount,
                 event.mViewFillableTotalCount,
                 event.mViewFillFailureCount,
                 event.mFocusedId,
@@ -868,6 +917,7 @@
                 event.mFocusedVirtualAutofillId,
                 event.mFieldFirstLength,
                 event.mFieldLastLength,
+                event.mFilteredFillabaleViewCount,
                 event.mViewFailedPriorToRefillCount,
                 event.mViewFilledSuccessfullyOnRefillCount,
                 event.mViewFailedOnRefillCount,
@@ -933,6 +983,7 @@
         int mNotifyViewEnteredIgnoredDuringAuthCount = 0;
 
         ArraySet<AutofillId> mAutofillIdsAttemptedAutofill;
+        ArraySet<AutofillId> mFailedAutofillIds = new ArraySet<>();
         ArraySet<AutofillId> mAlreadyFilledAutofillIds = new ArraySet<>();
 
         // Not logged - used for internal logic
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index c6ddc16..b7508b4 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -70,6 +70,7 @@
 import static com.android.server.autofill.Helper.containsCharsInOrder;
 import static com.android.server.autofill.Helper.createSanitizers;
 import static com.android.server.autofill.Helper.getNumericValue;
+import static com.android.server.autofill.Helper.SaveInfoStats;
 import static com.android.server.autofill.Helper.sDebug;
 import static com.android.server.autofill.Helper.sVerbose;
 import static com.android.server.autofill.Helper.toArray;
@@ -3203,11 +3204,6 @@
         return saveInfo == null ? 0 : saveInfo.getFlags();
     }
 
-    static class SaveInfoStats {
-        public int saveInfoCount;
-        public int saveDataTypeCount;
-    }
-
     /**
      * Get statistic information of save info in current session. Specifically
      *   1. how many save info the current session has.
@@ -3217,42 +3213,13 @@
      */
     @GuardedBy("mLock")
     private SaveInfoStats getSaveInfoStatsLocked() {
-        SaveInfoStats retSaveInfoStats = new SaveInfoStats();
-        retSaveInfoStats.saveInfoCount = -1;
-        retSaveInfoStats.saveDataTypeCount = -1;
-
         if (mContexts == null) {
             if (sVerbose) {
                 Slog.v(TAG, "getSaveInfoStatsLocked(): mContexts is null");
             }
-        } else if (mResponses == null) {
-            // Happens when the activity / session was finished before the service replied, or
-            // when the service cannot autofill it (and returned a null response).
-            if (sVerbose) {
-                Slog.v(TAG, "getSaveInfoStatsLocked(): mResponses is null");
-            }
-            return retSaveInfoStats;
-        } else {
-            int numSaveInfos = 0;
-            int numSaveDataTypes = 0;
-            ArraySet<Integer> saveDataTypeSeen = new ArraySet<>();
-            final int numResponses = mResponses.size();
-            for (int responseNum = 0; responseNum < numResponses; responseNum++) {
-                final FillResponse response = mResponses.valueAt(responseNum);
-                if (response != null && response.getSaveInfo() != null) {
-                    numSaveInfos += 1;
-                    int saveDataType = response.getSaveInfo().getType();
-                    if (!saveDataTypeSeen.contains(saveDataType)) {
-                        saveDataTypeSeen.add(saveDataType);
-                        numSaveDataTypes += 1;
-                    }
-                }
-            }
-            retSaveInfoStats.saveInfoCount = numSaveInfos;
-            retSaveInfoStats.saveDataTypeCount = numSaveDataTypes;
+            return new SaveInfoStats(-1, -1);
         }
-
-        return retSaveInfoStats;
+        return Helper.getSaveInfoStatsFromFillResponses(mResponses);
     }
 
     /**
@@ -5393,6 +5360,8 @@
             saveTriggerId = null;
         }
 
+        boolean hasAuthentication = (response.getAuthentication() != null);
+
         // Must also track that are part of datasets, otherwise the FillUI won't be hidden when
         // they go away (if they're not savable).
 
@@ -5412,6 +5381,9 @@
                         }
                     }
                 }
+                if (dataset.getAuthentication() != null) {
+                    hasAuthentication = true;
+                }
             }
         }
 
@@ -5423,7 +5395,7 @@
                         + " hasSaveInfo: " + (saveInfo != null));
             }
             mClient.setTrackedViews(id, toArray(trackedViews), mSaveOnAllViewsInvisible,
-                    saveOnFinish, toArray(fillableIds), saveTriggerId);
+                    saveOnFinish, toArray(fillableIds), saveTriggerId, hasAuthentication);
         } catch (RemoteException e) {
             Slog.w(TAG, "Cannot set tracked ids", e);
         }
@@ -5433,7 +5405,7 @@
      * Sets the state of views that failed to autofill.
      */
     @GuardedBy("mLock")
-    void setAutofillFailureLocked(@NonNull List<AutofillId> ids) {
+    void setAutofillFailureLocked(@NonNull List<AutofillId> ids, boolean isRefill) {
         if (sVerbose && !ids.isEmpty()) {
             Slog.v(TAG, "Total views that failed to populate: " + ids.size());
         }
@@ -5451,7 +5423,7 @@
                 Slog.v(TAG, "Changed state of " + id + " to " + viewState.getStateAsString());
             }
         }
-        mPresentationStatsEventLogger.maybeSetViewFillFailureCounts(ids.size());
+        mPresentationStatsEventLogger.maybeSetViewFillFailureCounts(ids, isRefill);
     }
 
     /**
@@ -5468,6 +5440,23 @@
         mPresentationStatsEventLogger.maybeAddSuccessId(id);
     }
 
+    /**
+     * Sets the state of views that failed to autofill.
+     */
+    void setNotifyNotExpiringResponseDuringAuth() {
+        synchronized (mLock) {
+            mPresentationStatsEventLogger.maybeSetNotifyNotExpiringResponseDuringAuth();
+        }
+    }
+    /**
+     * Sets the state of views that failed to autofill.
+     */
+    void setLogViewEnteredIgnoredDuringAuth() {
+        synchronized (mLock) {
+            mPresentationStatsEventLogger.notifyViewEnteredIgnoredDuringAuthCount();
+        }
+    }
+
     @GuardedBy("mLock")
     private void replaceResponseLocked(@NonNull FillResponse oldResponse,
             @NonNull FillResponse newResponse, @Nullable Bundle newClientState) {
@@ -6698,6 +6687,11 @@
         }
     }
 
+    @GuardedBy("mLock")
+    public void setAutofillIdsAttemptedForRefillLocked(@NonNull List<AutofillId> ids) {
+        mPresentationStatsEventLogger.maybeUpdateViewFillablesForRefillAttempt(ids);
+    }
+
     private AutoFillUI getUiForShowing() {
         synchronized (mLock) {
             mUi.setCallback(this);
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java b/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
index 3d53deb..4fc9d55 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
@@ -103,9 +103,10 @@
                     String packageName = getNextArgRequired();
                     String address = getNextArgRequired();
                     String deviceProfile = getNextArg();
+                    boolean selfManaged = getNextBooleanArg();
                     final MacAddress macAddress = MacAddress.fromString(address);
                     mAssociationRequestsProcessor.createAssociation(userId, packageName, macAddress,
-                            deviceProfile, deviceProfile, /* associatedDevice */ null, false,
+                            deviceProfile, deviceProfile, /* associatedDevice */ null, selfManaged,
                             /* callback */ null, /* resultReceiver */ null);
                 }
                 break;
@@ -462,6 +463,17 @@
         }
     }
 
+    private boolean getNextBooleanArg() {
+        String arg = getNextArg();
+        if (arg == null || "false".equalsIgnoreCase(arg)) {
+            return false;
+        } else if ("true".equalsIgnoreCase(arg)) {
+            return Boolean.parseBoolean(arg);
+        } else {
+            throw new IllegalArgumentException("Expected a boolean argument but was: " + arg);
+        }
+    }
+
     @Override
     public void onHelp() {
         PrintWriter pw = getOutPrintWriter();
@@ -470,7 +482,7 @@
         pw.println("      Print this help text.");
         pw.println("  list USER_ID");
         pw.println("      List all Associations for a user.");
-        pw.println("  associate USER_ID PACKAGE MAC_ADDRESS [DEVICE_PROFILE]");
+        pw.println("  associate USER_ID PACKAGE MAC_ADDRESS [DEVICE_PROFILE] [SELF_MANAGED]");
         pw.println("      Create a new Association.");
         pw.println("  disassociate USER_ID PACKAGE MAC_ADDRESS");
         pw.println("      Remove an existing Association.");
diff --git a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
index 988a213..84b5c39 100644
--- a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
+++ b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
@@ -28,7 +28,6 @@
 import android.app.WindowConfiguration;
 import android.app.compat.CompatChanges;
 import android.companion.virtual.VirtualDeviceManager.ActivityListener;
-import android.companion.virtual.flags.Flags;
 import android.compat.annotation.ChangeId;
 import android.compat.annotation.EnabledSince;
 import android.content.AttributionSource;
@@ -108,12 +107,11 @@
     private boolean mActivityLaunchAllowedByDefault;
     @NonNull
     @GuardedBy("mGenericWindowPolicyControllerLock")
-    private final Set<ComponentName> mActivityPolicyExemptions;
+    private final ArraySet<ComponentName> mActivityPolicyExemptions;
     private final boolean mCrossTaskNavigationAllowedByDefault;
     @NonNull
     private final ArraySet<ComponentName> mCrossTaskNavigationExemptions;
     @Nullable
-    private final ComponentName mPermissionDialogComponent;
     private final Object mGenericWindowPolicyControllerLock = new Object();
     @Nullable private final ActivityBlockedCallback mActivityBlockedCallback;
 
@@ -178,7 +176,6 @@
             @NonNull Set<ComponentName> activityPolicyExemptions,
             boolean crossTaskNavigationAllowedByDefault,
             @NonNull Set<ComponentName> crossTaskNavigationExemptions,
-            @Nullable ComponentName permissionDialogComponent,
             @Nullable ActivityListener activityListener,
             @Nullable ActivityBlockedCallback activityBlockedCallback,
             @Nullable SecureWindowCallback secureWindowCallback,
@@ -190,10 +187,9 @@
         mAttributionSource = attributionSource;
         mAllowedUsers = allowedUsers;
         mActivityLaunchAllowedByDefault = activityLaunchAllowedByDefault;
-        mActivityPolicyExemptions = activityPolicyExemptions;
+        mActivityPolicyExemptions = new ArraySet<>(activityPolicyExemptions);
         mCrossTaskNavigationAllowedByDefault = crossTaskNavigationAllowedByDefault;
         mCrossTaskNavigationExemptions = new ArraySet<>(crossTaskNavigationExemptions);
-        mPermissionDialogComponent = permissionDialogComponent;
         mActivityBlockedCallback = activityBlockedCallback;
         setInterestedWindowFlags(windowFlags, systemWindowFlags);
         mActivityListener = activityListener;
@@ -287,28 +283,15 @@
     public boolean canActivityBeLaunched(@NonNull ActivityInfo activityInfo,
             @Nullable Intent intent, @WindowConfiguration.WindowingMode int windowingMode,
             int launchingFromDisplayId, boolean isNewTask) {
-        if (Flags.interceptIntentsBeforeApplyingPolicy()) {
-            if (mIntentListenerCallback != null && intent != null
-                    && mIntentListenerCallback.shouldInterceptIntent(intent)) {
-                logActivityLaunchBlocked("Virtual device intercepting intent");
-                return false;
-            }
-            if (!canContainActivity(activityInfo, windowingMode, launchingFromDisplayId,
-                    isNewTask)) {
-                notifyActivityBlocked(activityInfo);
-                return false;
-            }
-        } else {
-            if (!canContainActivity(activityInfo, windowingMode, launchingFromDisplayId,
-                    isNewTask)) {
-                notifyActivityBlocked(activityInfo);
-                return false;
-            }
-            if (mIntentListenerCallback != null && intent != null
-                    && mIntentListenerCallback.shouldInterceptIntent(intent)) {
-                logActivityLaunchBlocked("Virtual device intercepting intent");
-                return false;
-            }
+        if (mIntentListenerCallback != null && intent != null
+                && mIntentListenerCallback.shouldInterceptIntent(intent)) {
+            logActivityLaunchBlocked("Virtual device intercepting intent");
+            return false;
+        }
+        if (!canContainActivity(activityInfo, windowingMode, launchingFromDisplayId,
+                isNewTask)) {
+            notifyActivityBlocked(activityInfo);
+            return false;
         }
         return true;
     }
@@ -370,14 +353,6 @@
             return false;
         }
 
-        // mPermissionDialogComponent being null means we don't want to block permission Dialogs
-        // based on FLAG_STREAM_PERMISSIONS
-        if (mPermissionDialogComponent != null
-                && mPermissionDialogComponent.equals(activityComponent)) {
-            logActivityLaunchBlocked("Permission dialog not allowed on virtual device");
-            return false;
-        }
-
         return true;
     }
 
@@ -487,11 +462,9 @@
                 && displayId != INVALID_DISPLAY) {
             mActivityBlockedCallback.onActivityBlocked(displayId, activityInfo);
         }
-        if (android.companion.virtualdevice.flags.Flags.metricsCollection()) {
-            Counter.logIncrementWithUid(
-                    "virtual_devices.value_activity_blocked_count",
-                    mAttributionSource.getUid());
-        }
+        Counter.logIncrementWithUid(
+                "virtual_devices.value_activity_blocked_count",
+                mAttributionSource.getUid());
     }
 
     private static boolean isAllowedByPolicy(boolean allowedByDefault,
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 d091ce8..8da58cf 100644
--- a/services/companion/java/com/android/server/companion/virtual/InputController.java
+++ b/services/companion/java/com/android/server/companion/virtual/InputController.java
@@ -842,11 +842,9 @@
                             deviceName, inputDeviceId));
         }
 
-        if (android.companion.virtualdevice.flags.Flags.metricsCollection()) {
-            String metricId = getMetricIdForInputType(type);
-            if (metricId != null) {
-                Counter.logIncrementWithUid(metricId, mAttributionSource.getUid());
-            }
+        String metricId = getMetricIdForInputType(type);
+        if (metricId != null) {
+            Counter.logIncrementWithUid(metricId, mAttributionSource.getUid());
         }
     }
 
diff --git a/services/companion/java/com/android/server/companion/virtual/SensorController.java b/services/companion/java/com/android/server/companion/virtual/SensorController.java
index 0655685..8d075db 100644
--- a/services/companion/java/com/android/server/companion/virtual/SensorController.java
+++ b/services/companion/java/com/android/server/companion/virtual/SensorController.java
@@ -145,11 +145,9 @@
             mSensorDescriptors.put(sensorToken, sensorDescriptor);
             mVirtualSensors.put(handle, sensor);
         }
-        if (android.companion.virtualdevice.flags.Flags.metricsCollection()) {
-            Counter.logIncrementWithUid(
-                    "virtual_devices.value_virtual_sensors_created_count",
-                    mAttributionSource.getUid());
-        }
+        Counter.logIncrementWithUid(
+                "virtual_devices.value_virtual_sensors_created_count",
+                mAttributionSource.getUid());
     }
 
     boolean sendSensorEvent(@NonNull IBinder token, @NonNull VirtualSensorEvent event) {
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 ee7d0ae..ed2c90d 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
@@ -29,8 +29,6 @@
 import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_CLIPBOARD;
 import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_RECENTS;
 import static android.companion.virtualdevice.flags.Flags.virtualCameraServiceDiscovery;
-import static android.companion.virtualdevice.flags.Flags.intentInterceptionActionMatchingFix;
-import static android.content.pm.PackageManager.ACTION_REQUEST_PERMISSIONS;
 
 import android.annotation.EnforcePermission;
 import android.annotation.NonNull;
@@ -208,7 +206,6 @@
     @GuardedBy("mVirtualDeviceLock")
     @NonNull
     private final Set<ComponentName> mActivityPolicyExemptions;
-    private final ComponentName mPermissionDialogComponent;
 
     private ActivityListener createListenerAdapter() {
         return new ActivityListener() {
@@ -343,11 +340,6 @@
         if (mCameraAccessController != null) {
             mCameraAccessController.startObservingIfNeeded();
         }
-        if (!Flags.streamPermissions()) {
-            mPermissionDialogComponent = getPermissionDialogComponent();
-        } else {
-            mPermissionDialogComponent = null;
-        }
         mVirtualCameraController = virtualCameraController;
         try {
             token.linkToDeath(this, 0);
@@ -558,6 +550,36 @@
         }
     }
 
+    @Override // Binder call
+    @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
+    public void addActivityPolicyExemptionForDisplay(
+            int displayId, @NonNull ComponentName componentName) {
+        super.addActivityPolicyExemptionForDisplay_enforcePermission();
+        if (!android.companion.virtualdevice.flags.Flags.activityControlApi()) {
+            return;
+        }
+        synchronized (mVirtualDeviceLock) {
+            checkDisplayOwnedByVirtualDeviceLocked(displayId);
+            mVirtualDisplays.get(displayId).getWindowPolicyController()
+                    .addActivityPolicyExemption(componentName);
+        }
+    }
+
+    @Override // Binder call
+    @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
+    public void removeActivityPolicyExemptionForDisplay(
+            int displayId, @NonNull ComponentName componentName) {
+        super.removeActivityPolicyExemptionForDisplay_enforcePermission();
+        if (!android.companion.virtualdevice.flags.Flags.activityControlApi()) {
+            return;
+        }
+        synchronized (mVirtualDeviceLock) {
+            checkDisplayOwnedByVirtualDeviceLocked(displayId);
+            mVirtualDisplays.get(displayId).getWindowPolicyController()
+                    .removeActivityPolicyExemption(componentName);
+        }
+    }
+
     private void sendPendingIntent(int displayId, PendingIntent pendingIntent)
             throws PendingIntent.CanceledException {
         final ActivityOptions options = ActivityOptions.makeBasic().setLaunchDisplayId(displayId);
@@ -657,12 +679,7 @@
             @Nullable IAudioConfigChangedCallback configChangedCallback) {
         super.onAudioSessionStarting_enforcePermission();
         synchronized (mVirtualDeviceLock) {
-            if (!mVirtualDisplays.contains(displayId)) {
-                throw new SecurityException(
-                        "Cannot start audio session for a display not associated with this virtual "
-                                + "device");
-            }
-
+            checkDisplayOwnedByVirtualDeviceLocked(displayId);
             if (mVirtualAudioController == null) {
                 mVirtualAudioController = new VirtualAudioController(mContext, mAttributionSource);
                 GenericWindowPolicyController gwpc = mVirtualDisplays.get(
@@ -706,6 +723,9 @@
                 break;
             case POLICY_TYPE_ACTIVITY:
                 synchronized (mVirtualDeviceLock) {
+                    if (getDevicePolicy(policyType) != devicePolicy) {
+                        mActivityPolicyExemptions.clear();
+                    }
                     mDevicePolicies.put(policyType, devicePolicy);
                     for (int i = 0; i < mVirtualDisplays.size(); i++) {
                         mVirtualDisplays.valueAt(i).getWindowPolicyController()
@@ -736,6 +756,33 @@
 
     @Override // Binder call
     @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
+    public void setDevicePolicyForDisplay(int displayId,
+            @VirtualDeviceParams.DynamicDisplayPolicyType int policyType,
+            @VirtualDeviceParams.DevicePolicy int devicePolicy) {
+        super.setDevicePolicyForDisplay_enforcePermission();
+        if (!android.companion.virtualdevice.flags.Flags.activityControlApi()) {
+            return;
+        }
+        synchronized (mVirtualDeviceLock) {
+            checkDisplayOwnedByVirtualDeviceLocked(displayId);
+            switch (policyType) {
+                case POLICY_TYPE_RECENTS:
+                    mVirtualDisplays.get(displayId).getWindowPolicyController()
+                            .setShowInHostDeviceRecents(devicePolicy == DEVICE_POLICY_DEFAULT);
+                    break;
+                case POLICY_TYPE_ACTIVITY:
+                    mVirtualDisplays.get(displayId).getWindowPolicyController()
+                            .setActivityLaunchDefaultAllowed(devicePolicy == DEVICE_POLICY_DEFAULT);
+                    break;
+                default:
+                    throw new IllegalArgumentException("Device policy " + policyType
+                            + " cannot be changed for a specific display. ");
+            }
+        }
+    }
+
+    @Override // Binder call
+    @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     public void createVirtualDpad(VirtualDpadConfig config, @NonNull IBinder deviceToken) {
         super.createVirtualDpad_enforcePermission();
         Objects.requireNonNull(config);
@@ -1243,7 +1290,6 @@
                 /* crossTaskNavigationExemptions= */crossTaskNavigationAllowedByDefault
                         ? mParams.getBlockedCrossTaskNavigations()
                         : mParams.getAllowedCrossTaskNavigations(),
-                mPermissionDialogComponent,
                 mActivityListenerAdapter,
                 this::onActivityBlocked,
                 this::onSecureWindowShown,
@@ -1255,13 +1301,6 @@
         return gwpc;
     }
 
-    private ComponentName getPermissionDialogComponent() {
-        Intent intent = new Intent(ACTION_REQUEST_PERMISSIONS);
-        PackageManager packageManager = mContext.getPackageManager();
-        intent.setPackage(packageManager.getPermissionControllerPackageName());
-        return intent.resolveActivity(packageManager);
-    }
-
     int createVirtualDisplay(@NonNull VirtualDisplayConfig virtualDisplayConfig,
             @NonNull IVirtualDisplayCallback callback, String packageName) {
         GenericWindowPolicyController gwpc;
@@ -1273,7 +1312,7 @@
                 this, gwpc, packageName);
         gwpc.setDisplayId(displayId, /* isMirrorDisplay= */ Flags.interactiveScreenMirror()
                 && mDisplayManagerInternal.getDisplayIdToMirror(displayId)
-                    != Display.INVALID_DISPLAY);
+                != Display.INVALID_DISPLAY);
 
         boolean showPointer;
         synchronized (mVirtualDeviceLock) {
@@ -1304,11 +1343,9 @@
             Binder.restoreCallingIdentity(token);
         }
 
-        if (android.companion.virtualdevice.flags.Flags.metricsCollection()) {
-            Counter.logIncrementWithUid(
-                    "virtual_devices.value_virtual_display_created_count",
-                    mAttributionSource.getUid());
-        }
+        Counter.logIncrementWithUid(
+                "virtual_devices.value_virtual_display_created_count",
+                mAttributionSource.getUid());
         return displayId;
     }
 
@@ -1376,11 +1413,9 @@
             showToastWhereUidIsRunning(uid, com.android.internal.R.string.vdm_secure_window,
                     Toast.LENGTH_LONG, mContext.getMainLooper());
 
-            if (android.companion.virtualdevice.flags.Flags.metricsCollection()) {
-                Counter.logIncrementWithUid(
-                        "virtual_devices.value_secure_window_blocked_count",
-                        mAttributionSource.getUid());
-            }
+            Counter.logIncrementWithUid(
+                    "virtual_devices.value_secure_window_blocked_count",
+                    mAttributionSource.getUid());
         }
     }
 
@@ -1441,20 +1476,24 @@
 
     @SuppressWarnings("AndroidFrameworkRequiresPermission")
     private void checkVirtualInputDeviceDisplayIdAssociation(int displayId) {
+        // The INJECT_EVENTS permission allows for injecting input to any window / display.
         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.INJECT_EVENTS)
-                    == PackageManager.PERMISSION_GRANTED) {
-            // The INJECT_EVENTS permission allows for injecting input to any window / display.
-            return;
-        }
-        synchronized (mVirtualDeviceLock) {
-            if (!mVirtualDisplays.contains(displayId)) {
-                throw new SecurityException(
-                        "Cannot create a virtual input device for display " + displayId
-                                + " which not associated with this virtual device");
+                    != PackageManager.PERMISSION_GRANTED) {
+            synchronized (mVirtualDeviceLock) {
+                checkDisplayOwnedByVirtualDeviceLocked(displayId);
             }
         }
     }
 
+    @GuardedBy("mVirtualDeviceLock")
+    private void checkDisplayOwnedByVirtualDeviceLocked(int displayId) {
+        if (!mVirtualDisplays.contains(displayId)) {
+            throw new SecurityException(
+                    "Invalid displayId: Display " + displayId
+                            + " is not associated with this virtual device");
+        }
+    }
+
     /**
      * Release resources tied to virtual display owned by this VirtualDevice instance.
      *
@@ -1579,8 +1618,8 @@
                 // Explicitly match the actions because the intent filter will match any intent
                 // without an explicit action. If the intent has no action, then require that there
                 // are no actions specified in the filter either.
-                boolean explicitActionMatch = !intentInterceptionActionMatchingFix()
-                        || intent.getAction() != null || intentFilter.countActions() == 0;
+                boolean explicitActionMatch =
+                        intent.getAction() != null || intentFilter.countActions() == 0;
                 if (explicitActionMatch && intentFilter.match(
                         intent.getAction(), intent.getType(), intent.getScheme(), intent.getData(),
                         intent.getCategories(), TAG) >= 0) {
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 1be1d2b..3cd1ca4 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
@@ -474,9 +474,7 @@
                     deviceId,
                     cameraAccessController, mPendingTrampolineCallback, activityListener,
                     soundEffectListener, runningAppsChangedCallback, params);
-            if (Flags.expressMetrics()) {
-                Counter.logIncrement("virtual_devices.value_virtual_devices_created_count");
-            }
+            Counter.logIncrement("virtual_devices.value_virtual_devices_created_count");
 
             synchronized (mVirtualDeviceManagerLock) {
                 if (!Flags.persistentDeviceIdApi() && mVirtualDevices.size() == 0) {
@@ -500,11 +498,9 @@
                     }
                 });
             }
-            if (android.companion.virtualdevice.flags.Flags.metricsCollection()) {
-                Counter.logIncrementWithUid(
-                        "virtual_devices.value_virtual_devices_created_with_uid_count",
-                        attributionSource.getUid());
-            }
+            Counter.logIncrementWithUid(
+                    "virtual_devices.value_virtual_devices_created_with_uid_count",
+                    attributionSource.getUid());
             return virtualDevice;
         }
 
diff --git a/services/companion/java/com/android/server/companion/virtual/audio/VirtualAudioController.java b/services/companion/java/com/android/server/companion/virtual/audio/VirtualAudioController.java
index 4bffb76..d3b3945 100644
--- a/services/companion/java/com/android/server/companion/virtual/audio/VirtualAudioController.java
+++ b/services/companion/java/com/android/server/companion/virtual/audio/VirtualAudioController.java
@@ -77,11 +77,9 @@
         mAudioPlaybackDetector = new AudioPlaybackDetector(context);
         mAudioRecordingDetector = new AudioRecordingDetector(context);
 
-        if (android.companion.virtualdevice.flags.Flags.metricsCollection()) {
-            Counter.logIncrementWithUid(
-                    "virtual_devices.value_virtual_audio_created_count",
-                    attributionSource.getUid());
-        }
+        Counter.logIncrementWithUid(
+                "virtual_devices.value_virtual_audio_created_count",
+                attributionSource.getUid());
     }
 
     /**
diff --git a/services/companion/java/com/android/server/companion/virtual/camera/VirtualCameraController.java b/services/companion/java/com/android/server/companion/virtual/camera/VirtualCameraController.java
index 62efafb..7b81ef3 100644
--- a/services/companion/java/com/android/server/companion/virtual/camera/VirtualCameraController.java
+++ b/services/companion/java/com/android/server/companion/virtual/camera/VirtualCameraController.java
@@ -102,11 +102,9 @@
         } catch (RemoteException e) {
             e.rethrowFromSystemServer();
         }
-        if (android.companion.virtualdevice.flags.Flags.metricsCollection()) {
-            Counter.logIncrementWithUid(
-                    "virtual_devices.value_virtual_camera_created_count",
-                    attributionSource.getUid());
-        }
+        Counter.logIncrementWithUid(
+                "virtual_devices.value_virtual_camera_created_count",
+                attributionSource.getUid());
     }
 
     /**
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 9d4310c..363c1d8 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -148,7 +148,7 @@
         "android.hardware.common-V2-java",
         "android.hardware.light-V2.0-java",
         "android.hardware.gnss-V2-java",
-        "android.hardware.vibrator-V2-java",
+        "android.hardware.vibrator-V3-java",
         "app-compat-annotations",
         "framework-tethering.stubs.module_lib",
         "keepanno-annotations",
diff --git a/services/core/java/com/android/server/MmsServiceBroker.java b/services/core/java/com/android/server/MmsServiceBroker.java
index ced7773..11de258 100644
--- a/services/core/java/com/android/server/MmsServiceBroker.java
+++ b/services/core/java/com/android/server/MmsServiceBroker.java
@@ -130,17 +130,18 @@
         }
 
         @Override
-        public void sendMessage(int subId, String callingPkg, Uri contentUri, String locationUrl,
-                Bundle configOverrides, PendingIntent sentIntent, long messageId,
+        public void sendMessage(int subId, int callingUser, String callingPkg,
+                Uri contentUri, String locationUrl, Bundle configOverrides,
+                PendingIntent sentIntent, long messageId,
                 String attributionTag) throws RemoteException {
             returnPendingIntentWithError(sentIntent);
         }
 
         @Override
-        public void downloadMessage(int subId, String callingPkg, String locationUrl,
-                Uri contentUri, Bundle configOverrides, PendingIntent downloadedIntent,
-                long messageId, String attributionTag)
-                throws RemoteException {
+        public void downloadMessage(int subId, int callingUser, String callingPkg,
+                String locationUrl, Uri contentUri, Bundle configOverrides,
+                PendingIntent downloadedIntent,
+                long messageId, String attributionTag) throws RemoteException {
             returnPendingIntentWithError(downloadedIntent);
         }
 
@@ -151,8 +152,9 @@
         }
 
         @Override
-        public Uri importMultimediaMessage(String callingPkg, Uri contentUri, String messageId,
-                long timestampSecs, boolean seen, boolean read) throws RemoteException {
+        public Uri importMultimediaMessage(int callingUser, String callingPkg,
+                Uri contentUri, String messageId, long timestampSecs,
+                boolean seen, boolean read) throws RemoteException {
             return null;
         }
 
@@ -187,8 +189,8 @@
         }
 
         @Override
-        public Uri addMultimediaMessageDraft(String callingPkg, Uri contentUri)
-                throws RemoteException {
+        public Uri addMultimediaMessageDraft(int callingUser, String callingPkg,
+                Uri contentUri) throws RemoteException {
             return null;
         }
 
@@ -333,9 +335,9 @@
         private static final String PHONE_PACKAGE_NAME = "com.android.phone";
 
         @Override
-        public void sendMessage(int subId, String callingPkg, Uri contentUri,
-                String locationUrl, Bundle configOverrides, PendingIntent sentIntent,
-                long messageId, String attributionTag)
+        public void sendMessage(int subId, int callingUser, String callingPkg,
+                Uri contentUri, String locationUrl, Bundle configOverrides,
+                PendingIntent sentIntent, long messageId, String attributionTag)
                 throws RemoteException {
             Slog.d(TAG, "sendMessage() by " + callingPkg);
             mContext.enforceCallingPermission(Manifest.permission.SEND_SMS, "Send MMS message");
@@ -360,14 +362,15 @@
                     CarrierMessagingService.SERVICE_INTERFACE,
                     Intent.FLAG_GRANT_READ_URI_PERMISSION,
                     subId);
-            getServiceGuarded().sendMessage(subId, callingPkg, contentUri, locationUrl,
-                    configOverrides, sentIntent, messageId, attributionTag);
+            getServiceGuarded().sendMessage(subId, getCallingUserId(), callingPkg, contentUri,
+                    locationUrl, configOverrides, sentIntent, messageId, attributionTag);
         }
 
         @Override
-        public void downloadMessage(int subId, String callingPkg, String locationUrl,
-                Uri contentUri, Bundle configOverrides, PendingIntent downloadedIntent,
-                long messageId, String attributionTag) throws RemoteException {
+        public void downloadMessage(int subId, int callingUser, String callingPkg,
+                String locationUrl, Uri contentUri, Bundle configOverrides,
+                PendingIntent downloadedIntent, long messageId, String attributionTag)
+                throws RemoteException {
             Slog.d(TAG, "downloadMessage() by " + callingPkg);
             mContext.enforceCallingPermission(Manifest.permission.RECEIVE_MMS,
                     "Download MMS message");
@@ -381,8 +384,8 @@
                     Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION,
                     subId);
 
-            getServiceGuarded().downloadMessage(subId, callingPkg, locationUrl, contentUri,
-                    configOverrides, downloadedIntent, messageId, attributionTag);
+            getServiceGuarded().downloadMessage(subId, getCallingUserId(), callingPkg, locationUrl,
+                    contentUri, configOverrides, downloadedIntent, messageId, attributionTag);
         }
 
         @Override
@@ -399,8 +402,8 @@
         }
 
         @Override
-        public Uri importMultimediaMessage(String callingPkg, Uri contentUri,
-                String messageId, long timestampSecs, boolean seen, boolean read)
+        public Uri importMultimediaMessage(int callingUser, String callingPkg,
+                Uri contentUri, String messageId, long timestampSecs, boolean seen, boolean read)
                 throws RemoteException {
             if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(),
                     callingPkg, null, null) != AppOpsManager.MODE_ALLOWED) {
@@ -408,8 +411,8 @@
                 // while writing the TelephonyProvider
                 return FAKE_MMS_SENT_URI;
             }
-            return getServiceGuarded().importMultimediaMessage(
-                    callingPkg, contentUri, messageId, timestampSecs, seen, read);
+            return getServiceGuarded().importMultimediaMessage(getCallingUserId(), callingPkg,
+                    contentUri, messageId, timestampSecs, seen, read);
         }
 
         @Override
@@ -467,15 +470,16 @@
         }
 
         @Override
-        public Uri addMultimediaMessageDraft(String callingPkg, Uri contentUri)
-                throws RemoteException {
+        public Uri addMultimediaMessageDraft(int callingUser, String callingPkg,
+                Uri contentUri) throws RemoteException {
             if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(),
                     callingPkg, null, null) != AppOpsManager.MODE_ALLOWED) {
                 // Silently fail AppOps failure due to not being the default SMS app
                 // while writing the TelephonyProvider
                 return FAKE_MMS_DRAFT_URI;
             }
-            return getServiceGuarded().addMultimediaMessageDraft(callingPkg, contentUri);
+            return getServiceGuarded().addMultimediaMessageDraft(getCallingUserId(), callingPkg,
+                    contentUri);
         }
 
         @Override
@@ -572,4 +576,13 @@
         if (info == null) return INVALID_SIM_SLOT_INDEX;
         return info.getSimSlotIndex();
     }
+
+    /**
+     * Retrieves the  calling user id.
+     * @return The id of the calling user.
+     */
+    private int getCallingUserId() {
+        return Binder.getCallingUserHandle().getIdentifier();
+    }
+
 }
diff --git a/services/core/java/com/android/server/PackageWatchdog.java b/services/core/java/com/android/server/PackageWatchdog.java
index d42a3dc..0815384 100644
--- a/services/core/java/com/android/server/PackageWatchdog.java
+++ b/services/core/java/com/android/server/PackageWatchdog.java
@@ -155,6 +155,15 @@
     private static final int DEFAULT_MAJOR_USER_IMPACT_LEVEL_THRESHOLD =
             PackageHealthObserverImpact.USER_IMPACT_LEVEL_71;
 
+    // Comma separated list of all packages exempt from user impact level threshold. If a package
+    // in the list is crash looping, all the mitigations including factory reset will be performed.
+    private static final String PACKAGES_EXEMPT_FROM_IMPACT_LEVEL_THRESHOLD =
+            "persist.device_config.configuration.packages_exempt_from_impact_level_threshold";
+
+    // Comma separated list of default packages exempt from user impact level threshold.
+    private static final String DEFAULT_PACKAGES_EXEMPT_FROM_IMPACT_LEVEL_THRESHOLD =
+            "com.android.systemui";
+
     private long mNumberOfNativeCrashPollsRemaining;
 
     private static final int DB_VERSION = 1;
@@ -201,6 +210,8 @@
     private final DeviceConfig.OnPropertiesChangedListener
             mOnPropertyChangedListener = this::onPropertyChanged;
 
+    private final Set<String> mPackagesExemptFromImpactLevelThreshold = new ArraySet<>();
+
     // The set of packages that have been synced with the ExplicitHealthCheckController
     @GuardedBy("mLock")
     private Set<String> mRequestedHealthCheckPackages = new ArraySet<>();
@@ -523,7 +534,7 @@
                               @FailureReasons int failureReason,
                               int currentObserverImpact,
                               int mitigationCount) {
-        if (currentObserverImpact < getUserImpactLevelLimit()) {
+        if (allowMitigations(currentObserverImpact, versionedPackage)) {
             synchronized (mLock) {
                 mLastMitigation = mSystemClock.uptimeMillis();
             }
@@ -531,6 +542,13 @@
         }
     }
 
+    private boolean allowMitigations(int currentObserverImpact,
+            VersionedPackage versionedPackage) {
+        return currentObserverImpact < getUserImpactLevelLimit()
+                || getPackagesExemptFromImpactLevelThreshold().contains(
+                versionedPackage.getPackageName());
+    }
+
     private long getMitigationWindowMs() {
         return SystemProperties.getLong(MITIGATION_WINDOW_MS, DEFAULT_MITIGATION_WINDOW_MS);
     }
@@ -662,6 +680,15 @@
                 DEFAULT_MAJOR_USER_IMPACT_LEVEL_THRESHOLD);
     }
 
+    private Set<String> getPackagesExemptFromImpactLevelThreshold() {
+        if (mPackagesExemptFromImpactLevelThreshold.isEmpty()) {
+            String packageNames = SystemProperties.get(PACKAGES_EXEMPT_FROM_IMPACT_LEVEL_THRESHOLD,
+                    DEFAULT_PACKAGES_EXEMPT_FROM_IMPACT_LEVEL_THRESHOLD);
+            return Set.of(packageNames.split("\\s*,\\s*"));
+        }
+        return mPackagesExemptFromImpactLevelThreshold;
+    }
+
     /** Possible severity values of the user impact of a {@link PackageHealthObserver#execute}. */
     @Retention(SOURCE)
     @IntDef(value = {PackageHealthObserverImpact.USER_IMPACT_LEVEL_0,
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 3633d0f..33cf842 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -55,6 +55,7 @@
 import android.telephony.BarringInfo;
 import android.telephony.CallQuality;
 import android.telephony.CallState;
+import android.telephony.CarrierConfigManager;
 import android.telephony.CellIdentity;
 import android.telephony.CellInfo;
 import android.telephony.CellSignalStrength;
@@ -426,6 +427,7 @@
     private boolean[] mSCBMStarted;
 
     private boolean[] mCarrierRoamingNtnMode = null;
+    private boolean[] mCarrierRoamingNtnEligible = null;
 
     /**
      * Per-phone map of precise data connection state. The key of the map is the pair of transport
@@ -726,6 +728,7 @@
             mSCBMReason = copyOf(mSCBMReason, mNumPhones);
             mSCBMStarted = copyOf(mSCBMStarted, mNumPhones);
             mCarrierRoamingNtnMode = copyOf(mCarrierRoamingNtnMode, mNumPhones);
+            mCarrierRoamingNtnEligible = copyOf(mCarrierRoamingNtnEligible, mNumPhones);
             // ds -> ss switch.
             if (mNumPhones < oldNumPhones) {
                 cutListToSize(mCellInfo, mNumPhones);
@@ -785,6 +788,7 @@
                 mSCBMReason[i] = TelephonyManager.STOP_REASON_UNKNOWN;
                 mSCBMStarted[i] = false;
                 mCarrierRoamingNtnMode[i] = false;
+                mCarrierRoamingNtnEligible[i] = false;
             }
         }
     }
@@ -859,6 +863,7 @@
         mSCBMReason = new int[numPhones];
         mSCBMStarted = new boolean[numPhones];
         mCarrierRoamingNtnMode = new boolean[numPhones];
+        mCarrierRoamingNtnEligible = new boolean[numPhones];
 
         for (int i = 0; i < numPhones; i++) {
             mCallState[i] =  TelephonyManager.CALL_STATE_IDLE;
@@ -903,6 +908,7 @@
             mSCBMReason[i] = TelephonyManager.STOP_REASON_UNKNOWN;
             mSCBMStarted[i] = false;
             mCarrierRoamingNtnMode[i] = false;
+            mCarrierRoamingNtnEligible[i] = false;
         }
 
         mAppOps = mContext.getSystemService(AppOpsManager.class);
@@ -1518,6 +1524,15 @@
                         remove(r.binder);
                     }
                 }
+                if (events.contains(
+                        TelephonyCallback.EVENT_CARRIER_ROAMING_NTN_ELIGIBLE_STATE_CHANGED)) {
+                    try {
+                        r.callback.onCarrierRoamingNtnEligibleStateChanged(
+                                mCarrierRoamingNtnEligible[r.phoneId]);
+                    } catch (RemoteException ex) {
+                        remove(r.binder);
+                    }
+                }
             }
         }
     }
@@ -3536,6 +3551,53 @@
         }
     }
 
+    /**
+     * Notify external listeners that device eligibility to connect to carrier roaming
+     * non-terrestrial network changed.
+     *
+     * @param subId subscription ID.
+     * @param eligible {@code true} when the device is eligible for satellite
+     * communication if all the following conditions are met:
+     * <ul>
+     * <li>Any subscription on the device supports P2P satellite messaging which is defined by
+     * {@link CarrierConfigManager#KEY_SATELLITE_ATTACH_SUPPORTED_BOOL} </li>
+     * <li>{@link CarrierConfigManager#KEY_CARRIER_ROAMING_NTN_CONNECT_TYPE_INT} set to
+     * {@link CarrierConfigManager#CARRIER_ROAMING_NTN_CONNECT_MANUAL} </li>
+     * <li>The device is in {@link ServiceState#STATE_OUT_OF_SERVICE}, not connected to Wi-Fi,
+     * and the hysteresis timer defined by {@link CarrierConfigManager
+     * #KEY_CARRIER_SUPPORTED_SATELLITE_NOTIFICATION_HYSTERESIS_SEC_INT} is expired. </li>
+     * </ul>
+     */
+    public void notifyCarrierRoamingNtnEligibleStateChanged(int subId, boolean eligible) {
+        if (!checkNotifyPermission("notifyCarrierRoamingNtnEligibleStateChanged")) {
+            log("notifyCarrierRoamingNtnEligibleStateChanged: caller does not have required "
+                    + "permissions.");
+            return;
+        }
+
+        if (VDBG) {
+            log("notifyCarrierRoamingNtnEligibleStateChanged: "
+                    + "subId=" + subId + " eligible" + eligible);
+        }
+
+        synchronized (mRecords) {
+            int phoneId = getPhoneIdFromSubId(subId);
+            mCarrierRoamingNtnEligible[phoneId] = eligible;
+            for (Record r : mRecords) {
+                if (r.matchTelephonyCallbackEvent(
+                        TelephonyCallback.EVENT_CARRIER_ROAMING_NTN_ELIGIBLE_STATE_CHANGED)
+                        && idMatch(r, subId, phoneId)) {
+                    try {
+                        r.callback.onCarrierRoamingNtnEligibleStateChanged(eligible);
+                    } catch (RemoteException ex) {
+                        mRemoveList.add(r.binder);
+                    }
+                }
+            }
+            handleRemoveListLocked();
+        }
+    }
+
     @NeverCompile // Avoid size overhead of debugging code.
     @Override
     public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
@@ -3589,6 +3651,8 @@
                 pw.println("mECBMStarted=" + mECBMStarted[i]);
                 pw.println("mSCBMReason=" + mSCBMReason[i]);
                 pw.println("mSCBMStarted=" + mSCBMStarted[i]);
+                pw.println("mCarrierRoamingNtnMode=" + mCarrierRoamingNtnMode[i]);
+                pw.println("mCarrierRoamingNtnEligible=" + mCarrierRoamingNtnEligible[i]);
 
                 // We need to obfuscate package names, and primitive arrays' native toString is ugly
                 Pair<List<String>, int[]> carrierPrivilegeState = mCarrierPrivilegeStates.get(i);
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 458749d..2af5316 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -72,6 +72,7 @@
 import android.content.pm.UserInfo;
 import android.database.Cursor;
 import android.database.sqlite.SQLiteCantOpenDatabaseException;
+import android.database.sqlite.SQLiteException;
 import android.database.sqlite.SQLiteFullException;
 import android.database.sqlite.SQLiteStatement;
 import android.os.Binder;
@@ -185,6 +186,12 @@
 
     final Context mContext;
 
+    private static final int[] INTERESTING_APP_OPS = new int[] {
+        AppOpsManager.OP_GET_ACCOUNTS,
+        AppOpsManager.OP_READ_CONTACTS,
+        AppOpsManager.OP_WRITE_CONTACTS,
+    };
+
     private final PackageManager mPackageManager;
     private final AppOpsManager mAppOpsManager;
     private UserManager mUserManager;
@@ -388,74 +395,48 @@
         }.register(mContext, mHandler.getLooper(), UserHandle.ALL, true);
 
         // Cancel account request notification if an app op was preventing the account access
-        mAppOpsManager.startWatchingMode(AppOpsManager.OP_GET_ACCOUNTS, null,
-                new AppOpsManager.OnOpChangedInternalListener() {
-            @Override
-            public void onOpChanged(int op, String packageName) {
-                try {
-                    final int userId = ActivityManager.getCurrentUser();
-                    final int uid = mPackageManager.getPackageUidAsUser(packageName, userId);
-                    final int mode = mAppOpsManager.checkOpNoThrow(
-                            AppOpsManager.OP_GET_ACCOUNTS, uid, packageName);
-                    if (mode == AppOpsManager.MODE_ALLOWED) {
-                        final long identity = Binder.clearCallingIdentity();
-                        try {
-                            UserAccounts accounts = getUserAccounts(userId);
-                            cancelAccountAccessRequestNotificationIfNeeded(
-                                    packageName, uid, true, accounts);
-                        } finally {
-                            Binder.restoreCallingIdentity(identity);
-                        }
-                    }
-                } catch (NameNotFoundException e) {
-                    /* ignore */
-                } catch (SQLiteCantOpenDatabaseException e) {
-                    Log.w(TAG, "Can't read accounts database", e);
-                    return;
-                }
-            }
-        });
+        for (int i = 0; i < INTERESTING_APP_OPS.length; ++i) {
+            mAppOpsManager.startWatchingMode(INTERESTING_APP_OPS[i], null,
+                    new OnInterestingAppOpChangedListener());
+        }
 
-        // Cancel account request notification if a permission was preventing the account access
-        mPackageManager.addOnPermissionsChangeListener(
-                (int uid) -> {
-            // Permission changes cause requires updating accounts cache.
+        // Clear the accounts cache on permission changes.
+        // The specific permissions we care about are backed by AppOps, so just
+        // let the change events on those handle clearing any notifications.
+        mPackageManager.addOnPermissionsChangeListener((int uid) -> {
             AccountManager.invalidateLocalAccountsDataCaches();
-
-            Account[] accounts = null;
-            String[] packageNames = mPackageManager.getPackagesForUid(uid);
-            if (packageNames != null) {
-                final int userId = UserHandle.getUserId(uid);
-                final long identity = Binder.clearCallingIdentity();
-                try {
-                    for (String packageName : packageNames) {
-                                // if app asked for permission we need to cancel notification even
-                                // for O+ applications.
-                                if (mPackageManager.checkPermission(
-                                        Manifest.permission.GET_ACCOUNTS,
-                                        packageName) != PackageManager.PERMISSION_GRANTED) {
-                                    continue;
-                                }
-
-                        if (accounts == null) {
-                            accounts = getAccountsOrEmptyArray(null, userId, "android");
-                            if (ArrayUtils.isEmpty(accounts)) {
-                                return;
-                            }
-                        }
-                        UserAccounts userAccounts = getUserAccounts(UserHandle.getUserId(uid));
-                        for (Account account : accounts) {
-                            cancelAccountAccessRequestNotificationIfNeeded(
-                                    account, uid, packageName, true, userAccounts);
-                        }
-                    }
-                } finally {
-                    Binder.restoreCallingIdentity(identity);
-                }
-            }
         });
     }
 
+    private class OnInterestingAppOpChangedListener
+            extends AppOpsManager.OnOpChangedInternalListener {
+        @Override
+        public void onOpChanged(int op, String packageName) {
+            final int userId = ActivityManager.getCurrentUser();
+            final int packageUid;
+            try {
+                packageUid = mPackageManager.getPackageUidAsUser(packageName, userId);
+            } catch (NameNotFoundException e) {
+                /* ignore */
+                return;
+            }
+
+            final int mode = mAppOpsManager.checkOpNoThrow(op, packageUid, packageName);
+            if (mode != AppOpsManager.MODE_ALLOWED) {
+                return;
+            }
+
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                cancelAccountAccessRequestNotificationIfNeeded(
+                        packageName, packageUid, true, getUserAccounts(userId));
+            } catch (SQLiteCantOpenDatabaseException e) {
+                Log.w(TAG, "Can't read accounts database", e);
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+    }
 
     boolean getBindInstantServiceAllowed(int userId) {
         return  mAuthenticatorCache.getBindInstantServiceAllowed(userId);
@@ -1461,8 +1442,8 @@
                 List<Integer> uids;
                 try {
                     uids = accounts.accountsDb.findAllUidGrants();
-                } catch (SQLiteCantOpenDatabaseException e) {
-                    Log.w(TAG, "Could not delete grants for user = " + accounts.userId);
+                } catch (SQLiteException e) {
+                    Log.w(TAG, "Could not delete grants for user = " + accounts.userId, e);
                     return;
                 }
                 for (int uid : uids) {
@@ -4464,6 +4445,9 @@
                     opPackageName,
                     visibleAccountTypes,
                     false /* includeUserManagedNotVisible */);
+        } catch (SQLiteException e) {
+            Log.w(TAG, "Could not get accounts for user " + userId, e);
+            return new Account[]{};
         } finally {
             restoreCallingIdentity(identityToken);
         }
@@ -4539,7 +4523,7 @@
         try {
             return getAccountsAsUserForPackage(type, userId, opPackageName /* callingPackage */, -1,
                     opPackageName, false /* includeUserManagedNotVisible */);
-        } catch (SQLiteCantOpenDatabaseException e) {
+        } catch (SQLiteException e) {
             Log.e(TAG, "Could not get accounts for user " + userId, e);
             return new Account[]{};
         }
@@ -4549,7 +4533,7 @@
     private Account[] getAccountsOrEmptyArray(String type, int userId, String opPackageName) {
         try {
             return getAccountsAsUser(type, userId, opPackageName);
-        } catch (SQLiteCantOpenDatabaseException e) {
+        } catch (SQLiteException e) {
             Log.w(TAG, "Could not get accounts for user " + userId, e);
             return new Account[]{};
         }
@@ -4612,6 +4596,9 @@
                     opPackageName,
                     visibleAccountTypes,
                     includeUserManagedNotVisible);
+        } catch (SQLiteException e) {
+            Log.w(TAG, "Could not get accounts for user " + userId, e);
+            return new Account[]{};
         } finally {
             restoreCallingIdentity(identityToken);
         }
@@ -4699,12 +4686,17 @@
 
     public Account[] getSharedAccountsAsUser(int userId) {
         userId = handleIncomingUser(userId);
-        UserAccounts accounts = getUserAccounts(userId);
-        synchronized (accounts.dbLock) {
-            List<Account> accountList = accounts.accountsDb.getSharedAccounts();
-            Account[] accountArray = new Account[accountList.size()];
-            accountList.toArray(accountArray);
-            return accountArray;
+        try {
+            UserAccounts accounts = getUserAccounts(userId);
+            synchronized (accounts.dbLock) {
+                List<Account> accountList = accounts.accountsDb.getSharedAccounts();
+                Account[] accountArray = new Account[accountList.size()];
+                accountList.toArray(accountArray);
+                return accountArray;
+            }
+        } catch (SQLiteException e) {
+            Log.w(TAG, "Could not get shared accounts for user " + userId, e);
+            return new Account[]{};
         }
     }
 
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 0fa5260..2e1416b 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -6653,9 +6653,10 @@
 
             // If unbound while waiting to start and there is no connection left in this service,
             // remove the pending service
-            if (s.getConnections().isEmpty()) {
+            if (s.getConnections().isEmpty() && !s.startRequested) {
                 mPendingServices.remove(s);
                 mPendingBringups.remove(s);
+                if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Removed pending service: " + s);
             }
 
             if (c.hasFlag(Context.BIND_AUTO_CREATE)) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 69ee8fc..33f33fb 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -1716,6 +1716,12 @@
      */
     @Nullable volatile ContentCaptureManagerInternal mContentCaptureService;
 
+    /**
+     * The interface to the freezer.
+     */
+    @NonNull
+    private final Freezer mFreezer;
+
     /*
      * The default duration for the binder heavy hitter auto sampler
      */
@@ -2506,6 +2512,7 @@
             @Nullable UserController userController) {
         mInjector = injector;
         mContext = mInjector.getContext();
+        mFreezer = injector.getFreezer();
         mUiContext = null;
         mAppErrors = injector.getAppErrors();
         mPackageWatchdog = null;
@@ -2555,6 +2562,7 @@
         LockGuard.installLock(this, LockGuard.INDEX_ACTIVITY);
         mInjector = new Injector(systemContext);
         mContext = systemContext;
+        mFreezer = mInjector.getFreezer();
 
         mFactoryTest = FactoryTest.getMode();
         mSystemThread = ActivityThread.currentActivityThread();
@@ -16961,16 +16969,24 @@
 
         int userId = UserHandle.getCallingUserId();
 
-        if (UserManager.isVisibleBackgroundUsersEnabled() && userId != getCurrentUserId()) {
-            // The check is added mainly for auto devices. On auto devices, it is possible that
-            // multiple users are visible simultaneously using visible background users.
-            // In such cases, it is desired that only the current user (not the visible background
-            // user) can change the locale and other persistent settings of the device.
-            Slog.w(TAG, "Only current user is allowed to update persistent configuration if "
-                    + "visible background users are enabled. Current User" + getCurrentUserId()
-                    + ". Calling User: " + userId);
-            throw new SecurityException("Only current user is allowed to update persistent "
-                    + "configuration.");
+        if (UserManager.isVisibleBackgroundUsersEnabled()) {
+            final long origId = Binder.clearCallingIdentity();
+            try {
+                if (userId != getCurrentUserId()) {
+                    // The check is added mainly for auto devices. On auto devices, it is
+                    // possible that multiple users are visible simultaneously using visible
+                    // background users. In such cases, it is desired that only the current user
+                    // (not the visible background user) can change the locale and other persistent
+                    // settings of the device.
+                    Slog.w(TAG, "Only current user is allowed to update persistent configuration "
+                            + "if visible background users are enabled. Current User"
+                            + getCurrentUserId() + ". Calling User: " + userId);
+                    throw new SecurityException("Only current user is allowed to update persistent "
+                            + "configuration.");
+                }
+            } finally {
+                Binder.restoreCallingIdentity(origId);
+            }
         }
 
         mActivityTaskManager.updatePersistentConfiguration(values, userId);
@@ -20911,6 +20927,11 @@
         public IntentFirewall getIntentFirewall() {
             return null;
         }
+
+        /** @return the default Freezer. */
+        public Freezer getFreezer() {
+            return new Freezer();
+        }
     }
 
     @Override
@@ -21014,7 +21035,7 @@
         final long token = Binder.clearCallingIdentity();
 
         try {
-            return CachedAppOptimizer.isFreezerSupported();
+            return mFreezer.isFreezerSupported();
         } finally {
             Binder.restoreCallingIdentity(token);
         }
@@ -21169,4 +21190,9 @@
     void clearPendingTopAppLocked() {
         mPendingStartActivityUids.clear();
     }
+
+    @NonNull
+    Freezer getFreezer() {
+        return mFreezer;
+    }
 }
diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java
index ee7033e..415f78a 100644
--- a/services/core/java/com/android/server/am/AppErrors.java
+++ b/services/core/java/com/android/server/am/AppErrors.java
@@ -65,7 +65,6 @@
 import com.android.server.LocalServices;
 import com.android.server.PackageWatchdog;
 import com.android.server.pm.UserManagerInternal;
-import com.android.server.pm.UserManagerService;
 import com.android.server.usage.AppStandbyInternal;
 import com.android.server.wm.WindowProcessController;
 
@@ -1027,7 +1026,8 @@
                 isBackground &= (userId != profileId);
             }
             int visibleUserId = getVisibleUserId(userId);
-            boolean isVisibleUser = isVisibleBackgroundUser(visibleUserId);
+            boolean isVisibleUser = LocalServices.getService(UserManagerInternal.class)
+                    .isVisibleBackgroundFullUser(visibleUserId);
             boolean showBackground = Settings.Secure.getIntForUser(mContext.getContentResolver(),
                     Settings.Secure.ANR_SHOW_BACKGROUND, 0, visibleUserId) != 0;
             if (isBackground && !showBackground && !isVisibleUser) {
@@ -1050,7 +1050,7 @@
                         mContext.getContentResolver(),
                         Settings.Secure.SHOW_FIRST_CRASH_DIALOG_DEV_OPTION,
                         0,
-                        mService.mUserController.getCurrentUserId()) != 0;
+                        visibleUserId) != 0;
                 final String packageName = proc.info.packageName;
                 final boolean crashSilenced = mAppsNotReportingCrashes != null
                         && mAppsNotReportingCrashes.contains(proc.info.packageName);
@@ -1183,26 +1183,6 @@
     }
 
     /**
-     * Checks if the given user is a visible background user, which is a full, background user
-     * assigned to secondary displays on the devices that have
-     * {@link UserManager#isVisibleBackgroundUsersEnabled()
-     * config_multiuserVisibleBackgroundUsers enabled} (for example, passenger users on
-     * automotive builds, using the display associated with their seats).
-     *
-     * @see UserManager#isUserVisible()
-     */
-    private boolean isVisibleBackgroundUser(int userId) {
-        if (!UserManager.isVisibleBackgroundUsersEnabled()) {
-            return false;
-        }
-        boolean isForeground = mService.mUserController.getCurrentUserId() == userId;
-        boolean isProfile = UserManagerService.getInstance().isProfile(userId);
-        boolean isVisible = LocalServices.getService(UserManagerInternal.class)
-                .isUserVisible(userId);
-        return isVisible && !isForeground && !isProfile;
-    }
-
-    /**
      * Information about a process that is currently marked as bad.
      */
     static final class BadProcessInfo {
diff --git a/services/core/java/com/android/server/am/AppExitInfoTracker.java b/services/core/java/com/android/server/am/AppExitInfoTracker.java
index 47b65eb..1f88657 100644
--- a/services/core/java/com/android/server/am/AppExitInfoTracker.java
+++ b/services/core/java/com/android/server/am/AppExitInfoTracker.java
@@ -353,8 +353,8 @@
     }
 
     /** Called when there is a low memory kill */
-    void scheduleNoteLmkdProcKilled(final int pid, final int uid) {
-        mKillHandler.obtainMessage(KillHandler.MSG_LMKD_PROC_KILLED, pid, uid)
+    void scheduleNoteLmkdProcKilled(final int pid, final int uid, final int rssKb) {
+        mKillHandler.obtainMessage(KillHandler.MSG_LMKD_PROC_KILLED, pid, uid, Long.valueOf(rssKb))
                 .sendToTarget();
     }
 
@@ -401,9 +401,9 @@
 
             if (lmkd != null) {
                 updateExistingExitInfoRecordLocked(info, null,
-                        ApplicationExitInfo.REASON_LOW_MEMORY);
+                        ApplicationExitInfo.REASON_LOW_MEMORY, (Long) lmkd.second);
             } else if (zygote != null) {
-                updateExistingExitInfoRecordLocked(info, (Integer) zygote.second, null);
+                updateExistingExitInfoRecordLocked(info, (Integer) zygote.second, null, null);
             } else {
                 scheduleLogToStatsdLocked(info, false);
             }
@@ -486,7 +486,7 @@
      */
     @GuardedBy("mLock")
     private void updateExistingExitInfoRecordLocked(ApplicationExitInfo info,
-            Integer status, Integer reason) {
+            Integer status, Integer reason, Long rssKb) {
         if (info == null || !isFresh(info.getTimestamp())) {
             // if the record is way outdated, don't update it then (because of potential pid reuse)
             return;
@@ -513,6 +513,9 @@
                 immediateLog = true;
             }
         }
+        if (rssKb != null) {
+            info.setRss(rssKb.longValue());
+        }
         scheduleLogToStatsdLocked(info, immediateLog);
     }
 
@@ -523,7 +526,7 @@
      */
     @GuardedBy("mLock")
     private boolean updateExitInfoIfNecessaryLocked(
-            int pid, int uid, Integer status, Integer reason) {
+            int pid, int uid, Integer status, Integer reason, Long rssKb) {
         Integer k = mIsolatedUidRecords.getUidByIsolatedUid(uid);
         if (k != null) {
             uid = k;
@@ -552,7 +555,7 @@
                 // always be the first one we se as `getExitInfosLocked()` returns them sorted
                 // by most-recent-first.
                 isModified[0] = true;
-                updateExistingExitInfoRecordLocked(info, status, reason);
+                updateExistingExitInfoRecordLocked(info, status, reason, rssKb);
                 return FOREACH_ACTION_STOP_ITERATION;
             }
             return FOREACH_ACTION_NONE;
@@ -1668,11 +1671,11 @@
             switch (msg.what) {
                 case MSG_LMKD_PROC_KILLED:
                     mAppExitInfoSourceLmkd.onProcDied(msg.arg1 /* pid */, msg.arg2 /* uid */,
-                            null /* status */);
+                            null /* status */, (Long) msg.obj /* rss_kb */);
                     break;
                 case MSG_CHILD_PROC_DIED:
                     mAppExitInfoSourceZygote.onProcDied(msg.arg1 /* pid */, msg.arg2 /* uid */,
-                            (Integer) msg.obj /* status */);
+                            (Integer) msg.obj /* status */, null /* rss_kb */);
                     break;
                 case MSG_PROC_DIED: {
                     ApplicationExitInfo raw = (ApplicationExitInfo) msg.obj;
@@ -1833,7 +1836,7 @@
             }
         }
 
-        void onProcDied(final int pid, final int uid, final Integer status) {
+        void onProcDied(final int pid, final int uid, final Integer status, final Long rssKb) {
             if (DEBUG_PROCESSES) {
                 Slog.i(TAG, mTag + ": proc died: pid=" + pid + " uid=" + uid
                         + ", status=" + status);
@@ -1846,8 +1849,12 @@
             // Unlikely but possible: the record has been created
             // Let's update it if we could find a ApplicationExitInfo record
             synchronized (mLock) {
-                if (!updateExitInfoIfNecessaryLocked(pid, uid, status, mPresetReason)) {
-                    addLocked(pid, uid, status);
+                if (!updateExitInfoIfNecessaryLocked(pid, uid, status, mPresetReason, rssKb)) {
+                    if (rssKb != null) {
+                        addLocked(pid, uid, rssKb);     // lmkd
+                    } else {
+                        addLocked(pid, uid, status);    // zygote
+                    }
                 }
 
                 // Notify any interesed party regarding the lmkd kills
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 8df4e77..6333159 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -124,6 +124,7 @@
 import com.android.server.pm.UserManagerInternal;
 import com.android.server.power.optimization.Flags;
 import com.android.server.power.stats.AggregatedPowerStatsConfig;
+import com.android.server.power.stats.AmbientDisplayPowerStatsProcessor;
 import com.android.server.power.stats.AudioPowerStatsProcessor;
 import com.android.server.power.stats.BatteryExternalStatsWorker;
 import com.android.server.power.stats.BatteryStatsDumpHelperImpl;
@@ -142,6 +143,8 @@
 import com.android.server.power.stats.PowerStatsScheduler;
 import com.android.server.power.stats.PowerStatsStore;
 import com.android.server.power.stats.PowerStatsUidResolver;
+import com.android.server.power.stats.ScreenPowerStatsProcessor;
+import com.android.server.power.stats.SensorPowerStatsProcessor;
 import com.android.server.power.stats.SystemServerCpuThreadReader.SystemServiceCpuThreadTimes;
 import com.android.server.power.stats.VideoPowerStatsProcessor;
 import com.android.server.power.stats.WifiPowerStatsProcessor;
@@ -488,6 +491,20 @@
                 .setProcessor(
                         new CpuPowerStatsProcessor(mPowerProfile, mCpuScalingPolicies));
 
+        config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_SCREEN)
+                .trackDeviceStates(
+                        AggregatedPowerStatsConfig.STATE_POWER,
+                        AggregatedPowerStatsConfig.STATE_SCREEN)
+                .trackUidStates(
+                        AggregatedPowerStatsConfig.STATE_POWER,
+                        AggregatedPowerStatsConfig.STATE_SCREEN)
+                .setProcessor(
+                        new ScreenPowerStatsProcessor(mPowerProfile));
+
+        config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_AMBIENT_DISPLAY,
+                        BatteryConsumer.POWER_COMPONENT_SCREEN)
+                .setProcessor(new AmbientDisplayPowerStatsProcessor());
+
         config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)
                 .trackDeviceStates(
                         AggregatedPowerStatsConfig.STATE_POWER,
@@ -579,6 +596,17 @@
                 .setProcessor(
                         new GnssPowerStatsProcessor(mPowerProfile, mPowerStatsUidResolver));
 
+        config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_SENSORS)
+                .trackDeviceStates(
+                        AggregatedPowerStatsConfig.STATE_POWER,
+                        AggregatedPowerStatsConfig.STATE_SCREEN)
+                .trackUidStates(
+                        AggregatedPowerStatsConfig.STATE_POWER,
+                        AggregatedPowerStatsConfig.STATE_SCREEN,
+                        AggregatedPowerStatsConfig.STATE_PROCESS_STATE)
+                .setProcessor(new SensorPowerStatsProcessor(
+                        () -> mContext.getSystemService(SensorManager.class)));
+
         config.trackCustomPowerComponents(CustomEnergyConsumerPowerStatsProcessor::new)
                 .trackDeviceStates(
                         AggregatedPowerStatsConfig.STATE_POWER,
@@ -592,6 +620,10 @@
 
     private void setPowerStatsThrottlePeriods(BatteryStatsImpl.BatteryStatsConfig.Builder builder,
             String configString) {
+        if (configString == null) {
+            return;
+        }
+
         Matcher matcher = Pattern.compile("([^:]+):(\\d+)\\s*").matcher(configString);
         while (matcher.find()) {
             String powerComponentName = matcher.group(1);
@@ -636,6 +668,18 @@
                 BatteryConsumer.POWER_COMPONENT_CPU,
                 Flags.streamlinedBatteryStats());
 
+        mStats.setPowerStatsCollectorEnabled(BatteryConsumer.POWER_COMPONENT_SCREEN,
+                Flags.streamlinedMiscBatteryStats());
+        mBatteryUsageStatsProvider.setPowerStatsExporterEnabled(
+                BatteryConsumer.POWER_COMPONENT_SCREEN,
+                Flags.streamlinedMiscBatteryStats());
+
+        mStats.setPowerStatsCollectorEnabled(BatteryConsumer.POWER_COMPONENT_AMBIENT_DISPLAY,
+                Flags.streamlinedMiscBatteryStats());
+        mBatteryUsageStatsProvider.setPowerStatsExporterEnabled(
+                BatteryConsumer.POWER_COMPONENT_AMBIENT_DISPLAY,
+                Flags.streamlinedMiscBatteryStats());
+
         mStats.setPowerStatsCollectorEnabled(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO,
                 Flags.streamlinedConnectivityBatteryStats());
         mBatteryUsageStatsProvider.setPowerStatsExporterEnabled(
@@ -678,6 +722,10 @@
                 BatteryConsumer.POWER_COMPONENT_GNSS,
                 Flags.streamlinedMiscBatteryStats());
 
+        mBatteryUsageStatsProvider.setPowerStatsExporterEnabled(
+                BatteryConsumer.POWER_COMPONENT_SENSORS,
+                Flags.streamlinedMiscBatteryStats());
+
         mStats.setPowerStatsCollectorEnabled(BatteryConsumer.POWER_COMPONENT_CAMERA,
                 Flags.streamlinedMiscBatteryStats());
         mBatteryUsageStatsProvider.setPowerStatsExporterEnabled(
@@ -1212,7 +1260,14 @@
                                     .setMinConsumedPowerThreshold(minConsumedPowerThreshold)
                                     .build();
                     bus = getBatteryUsageStats(List.of(query)).get(0);
-                    return new StatsPerUidLogger(new FrameworkStatsLogger()).logStats(bus, data);
+                    final int pullResult =
+                            new StatsPerUidLogger(new FrameworkStatsLogger()).logStats(bus, data);
+                    try {
+                        bus.close();
+                    } catch (IOException e) {
+                        Slog.w(TAG, "Failure close BatteryUsageStats", e);
+                    }
+                    return pullResult;
                 }
                 default:
                     throw new UnsupportedOperationException("Unknown tagId=" + atomTag);
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index 1c4ffbb..11e8353 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -664,6 +664,8 @@
     private final ProcessDependencies mProcessDependencies;
     private final ProcLocksReader mProcLocksReader;
 
+    private final Freezer mFreezer;
+
     public CachedAppOptimizer(ActivityManagerService am) {
         this(am, null, new DefaultProcessDependencies());
     }
@@ -680,6 +682,7 @@
         mTestCallback = callback;
         mSettingsObserver = new SettingsContentObserver();
         mProcLocksReader = new ProcLocksReader();
+        mFreezer = mAm.getFreezer();
     }
 
     /**
@@ -1050,89 +1053,6 @@
     }
 
     /**
-     * Informs binder that a process is about to be frozen. If freezer is enabled on a process via
-     * this method, this method will synchronously dispatch all pending transactions to the
-     * specified pid. This method will not add significant latencies when unfreezing.
-     * After freezing binder calls, binder will block all transaction to the frozen pid, and return
-     * an error to the sending process.
-     *
-     * @param pid the target pid for which binder transactions are to be frozen
-     * @param freeze specifies whether to flush transactions and then freeze (true) or unfreeze
-     * binder for the specificed pid.
-     * @param timeoutMs the timeout in milliseconds to wait for the binder interface to freeze
-     * before giving up.
-     *
-     * @throws RuntimeException in case a flush/freeze operation could not complete successfully.
-     * @return 0 if success, or -EAGAIN indicating there's pending transaction.
-     */
-    public static native int freezeBinder(int pid, boolean freeze, int timeoutMs);
-
-    /**
-     * Retrieves binder freeze info about a process.
-     * @param pid the pid for which binder freeze info is to be retrieved.
-     *
-     * @throws RuntimeException if the operation could not complete successfully.
-     * @return a bit field reporting the binder freeze info for the process.
-     */
-    private static native int getBinderFreezeInfo(int pid);
-
-    /**
-     * Returns the path to be checked to verify whether the freezer is supported by this system.
-     * @return absolute path to the file
-     */
-    private static native String getFreezerCheckPath();
-
-    /**
-     * Check if task_profiles.json includes valid freezer profiles and actions
-     * @return false if there are invalid profiles or actions
-     */
-    private static native boolean isFreezerProfileValid();
-
-    /**
-     * Determines whether the freezer is supported by this system
-     */
-    public static boolean isFreezerSupported() {
-        boolean supported = false;
-        FileReader fr = null;
-
-        try {
-            String path = getFreezerCheckPath();
-            Slog.d(TAG_AM, "Checking cgroup freezer: " + path);
-            fr = new FileReader(path);
-            char state = (char) fr.read();
-
-            if (state == '1' || state == '0') {
-                // Also check freezer binder ioctl
-                Slog.d(TAG_AM, "Checking binder freezer ioctl");
-                getBinderFreezeInfo(Process.myPid());
-
-                // Check if task_profiles.json contains invalid profiles
-                Slog.d(TAG_AM, "Checking freezer profiles");
-                supported = isFreezerProfileValid();
-            } else {
-                Slog.e(TAG_AM, "Unexpected value in cgroup.freeze");
-            }
-        } catch (java.io.FileNotFoundException e) {
-            Slog.w(TAG_AM, "File cgroup.freeze not present");
-        } catch (RuntimeException e) {
-            Slog.w(TAG_AM, "Unable to read freezer info");
-        } catch (Exception e) {
-            Slog.w(TAG_AM, "Unable to read cgroup.freeze: " + e.toString());
-        }
-
-        if (fr != null) {
-            try {
-                fr.close();
-            } catch (java.io.IOException e) {
-                Slog.e(TAG_AM, "Exception closing cgroup.freeze: " + e.toString());
-            }
-        }
-
-        Slog.d(TAG_AM, "Freezer supported: " + supported);
-        return supported;
-    }
-
-    /**
      * Reads the flag value from DeviceConfig to determine whether app freezer
      * should be enabled, and starts the freeze/compaction thread if needed.
      */
@@ -1146,7 +1066,7 @@
         } else if ("enabled".equals(configOverride)
                 || DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT,
                     KEY_USE_FREEZER, DEFAULT_USE_FREEZER)) {
-            mUseFreezer = isFreezerSupported();
+            mUseFreezer = mFreezer.isFreezerSupported();
             updateFreezerDebounceTimeout();
             updateFreezerExemptInstPkg();
         } else {
@@ -1528,7 +1448,7 @@
         boolean processKilled = false;
 
         try {
-            int freezeInfo = getBinderFreezeInfo(pid);
+            int freezeInfo = mFreezer.getBinderFreezeInfo(pid);
 
             if ((freezeInfo & SYNC_RECEIVED_WHILE_FROZEN) != 0) {
                 Slog.d(TAG_AM, "pid " + pid + " " + app.processName
@@ -1562,7 +1482,7 @@
         long freezeTime = opt.getFreezeUnfreezeTime();
 
         try {
-            freezeBinder(pid, false, FREEZE_BINDER_TIMEOUT_MS);
+            mFreezer.freezeBinder(pid, false, FREEZE_BINDER_TIMEOUT_MS);
         } catch (RuntimeException e) {
             Slog.e(TAG_AM, "Unable to unfreeze binder for " + pid + " " + app.processName
                     + ". Killing it");
@@ -1574,7 +1494,7 @@
 
         try {
             traceAppFreeze(app.processName, pid, reason);
-            Process.setProcessFrozen(pid, app.uid, false);
+            mFreezer.setProcessFrozen(pid, app.uid, false);
 
             opt.setFreezeUnfreezeTime(SystemClock.uptimeMillis());
             opt.setFrozen(false);
@@ -1617,7 +1537,7 @@
             }
             Slog.d(TAG_AM, "quick sync unfreeze " + pid + " for " +  reason);
             try {
-                freezeBinder(pid, false, FREEZE_BINDER_TIMEOUT_MS);
+                mFreezer.freezeBinder(pid, false, FREEZE_BINDER_TIMEOUT_MS);
             } catch (RuntimeException e) {
                 Slog.e(TAG_AM, "Unable to quick unfreeze binder for " + pid);
                 return;
@@ -1625,7 +1545,7 @@
 
             try {
                 traceAppFreeze(app.processName, pid, reason);
-                Process.setProcessFrozen(pid, app.uid, false);
+                mFreezer.setProcessFrozen(pid, app.uid, false);
             } catch (Exception e) {
                 Slog.e(TAG_AM, "Unable to quick unfreeze " + pid);
             }
@@ -2394,7 +2314,7 @@
                 // Freeze binder interface before the process, to flush any
                 // transactions that might be pending.
                 try {
-                    if (freezeBinder(pid, true, FREEZE_BINDER_TIMEOUT_MS) != 0) {
+                    if (mFreezer.freezeBinder(pid, true, FREEZE_BINDER_TIMEOUT_MS) != 0) {
                         handleBinderFreezerFailure(proc, "outstanding txns");
                         return;
                     }
@@ -2413,7 +2333,7 @@
 
                 try {
                     traceAppFreeze(proc.processName, pid, -1);
-                    Process.setProcessFrozen(pid, proc.uid, true);
+                    mFreezer.setProcessFrozen(pid, proc.uid, true);
                     opt.setFreezeUnfreezeTime(SystemClock.uptimeMillis());
                     opt.setFrozen(true);
                     opt.setHasCollectedFrozenPSS(false);
@@ -2452,7 +2372,7 @@
 
             try {
                 // post-check to prevent races
-                int freezeInfo = getBinderFreezeInfo(pid);
+                int freezeInfo = mFreezer.getBinderFreezeInfo(pid);
 
                 if ((freezeInfo & TXNS_PENDING_WHILE_FROZEN) != 0) {
                     synchronized (mProcLock) {
@@ -2620,6 +2540,22 @@
     }
 
     /**
+     * Freeze or unfreeze a process.  This should only be used for testing.
+     */
+    @VisibleForTesting
+    void forceFreezeForTest(ProcessRecord proc, boolean freeze) {
+        synchronized (mAm) {
+            synchronized (mProcLock) {
+                if (freeze) {
+                    forceFreezeAppAsyncLSP(proc);
+                } else {
+                    unfreezeAppInternalLSP(proc, UNFREEZE_REASON_NONE, true);
+                }
+            }
+        }
+    }
+
+    /**
      * Sending binder transactions to frozen apps most likely indicates there's a bug. Log it and
      * kill the frozen apps if they 1) receive sync binder transactions while frozen, or 2) miss
      * async binder transactions due to kernel binder buffer running out.
@@ -2660,7 +2596,7 @@
         for (int i = 0; i < pids.size(); i++) {
             int current = pids.get(i);
             try {
-                int freezeInfo = getBinderFreezeInfo(current);
+                int freezeInfo = mFreezer.getBinderFreezeInfo(current);
 
                 if ((freezeInfo & SYNC_RECEIVED_WHILE_FROZEN) != 0) {
                     killProcess(current, "Sync transaction while frozen",
diff --git a/services/core/java/com/android/server/am/ContentProviderHelper.java b/services/core/java/com/android/server/am/ContentProviderHelper.java
index 4ff1367..1314521 100644
--- a/services/core/java/com/android/server/am/ContentProviderHelper.java
+++ b/services/core/java/com/android/server/am/ContentProviderHelper.java
@@ -538,6 +538,8 @@
                             if (!pr.hasProvider(cpi.name)) {
                                 checkTime(startTime, "getContentProviderImpl: scheduling install");
                                 pr.installProvider(cpi.name, cpr);
+                                mService.mOomAdjuster.unfreezeTemporarily(proc,
+                                        CachedAppOptimizer.UNFREEZE_REASON_GET_PROVIDER);
                                 try {
                                     thread.scheduleInstallProvider(cpi);
                                 } catch (RemoteException e) {
@@ -1804,10 +1806,12 @@
                         ActivityManagerService.WAIT_FOR_CONTENT_PROVIDER_TIMEOUT_MSG, cpr);
             }
             final int userId = UserHandle.getUserId(cpr.uid);
+            boolean removed = false;
             // Don't remove from provider map if it doesn't match
             // could be a new content provider is starting
             if (mProviderMap.getProviderByClass(cpr.name, userId) == cpr) {
                 mProviderMap.removeProviderByClass(cpr.name, userId);
+                removed = true;
             }
             String[] names = cpr.info.authority.split(";");
             for (int j = 0; j < names.length; j++) {
@@ -1815,8 +1819,12 @@
                 // could be a new content provider is starting
                 if (mProviderMap.getProviderByName(names[j], userId) == cpr) {
                     mProviderMap.removeProviderByName(names[j], userId);
+                    removed = true;
                 }
             }
+            if (removed && cpr.proc != null) {
+                cpr.proc.mProviders.removeProvider(cpr.info.name);
+            }
         }
 
         for (int i = cpr.connections.size() - 1; i >= 0; i--) {
diff --git a/services/core/java/com/android/server/am/CoreSettingsObserver.java b/services/core/java/com/android/server/am/CoreSettingsObserver.java
index 91b64f8..6bb56c9 100644
--- a/services/core/java/com/android/server/am/CoreSettingsObserver.java
+++ b/services/core/java/com/android/server/am/CoreSettingsObserver.java
@@ -25,7 +25,6 @@
 import android.os.Bundle;
 import android.provider.DeviceConfig;
 import android.provider.Settings;
-import android.text.TextFlags;
 import android.widget.WidgetFlags;
 
 import com.android.internal.R;
@@ -163,22 +162,6 @@
                 WidgetFlags.KEY_MAGNIFIER_ASPECT_RATIO, float.class,
                 WidgetFlags.MAGNIFIER_ASPECT_RATIO_DEFAULT));
 
-        sDeviceConfigEntries.add(new DeviceConfigEntry<Boolean>(
-                TextFlags.NAMESPACE, TextFlags.ENABLE_NEW_CONTEXT_MENU,
-                TextFlags.KEY_ENABLE_NEW_CONTEXT_MENU, boolean.class,
-                TextFlags.ENABLE_NEW_CONTEXT_MENU_DEFAULT));
-
-        // Register all text aconfig flags.
-        for (int i = 0; i < TextFlags.TEXT_ACONFIGS_FLAGS.length; i++) {
-            final String flag = TextFlags.TEXT_ACONFIGS_FLAGS[i];
-            final boolean defaultValue = TextFlags.TEXT_ACONFIG_DEFAULT_VALUE[i];
-            sDeviceConfigEntries.add(new DeviceConfigEntry<Boolean>(
-                    TextFlags.NAMESPACE,
-                    flag,
-                    TextFlags.getKeyForFlag(flag),
-                    boolean.class,
-                    defaultValue));
-        }
         // add other device configs here...
     }
     private static volatile boolean sDeviceConfigContextEntriesLoaded = false;
diff --git a/services/core/java/com/android/server/am/Freezer.java b/services/core/java/com/android/server/am/Freezer.java
new file mode 100644
index 0000000..3b3cf55
--- /dev/null
+++ b/services/core/java/com/android/server/am/Freezer.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.am;
+
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
+
+import android.os.Process;
+
+/**
+ * A collection of interfaces to manage the freezer.  All access to the freezer goes through an
+ * instance of this class.  The class can be overridden for testing.
+ *
+ * Methods may be called without external synchronization.  Multiple instances of this class can be
+ * used concurrently.
+ */
+class Freezer {
+
+    /**
+     * Freeze or unfreeze the specified process.
+     *
+     * @param pid Identifier of the process to freeze or unfreeze.
+     * @param uid Identifier of the user the process is running under.
+     * @param frozen Specify whether to free (true) or unfreeze (false).
+     */
+    public void setProcessFrozen(int pid, int uid, boolean frozen) {
+        Process.setProcessFrozen(pid, uid, frozen);
+    }
+
+    /**
+     * Informs binder that a process is about to be frozen. If freezer is enabled on a process via
+     * this method, this method will synchronously dispatch all pending transactions to the
+     * specified pid. This method will not add significant latencies when unfreezing.
+     * After freezing binder calls, binder will block all transaction to the frozen pid, and return
+     * an error to the sending process.
+     *
+     * @param pid the target pid for which binder transactions are to be frozen
+     * @param freeze specifies whether to flush transactions and then freeze (true) or unfreeze
+     * binder for the specified pid.
+     * @param timeoutMs the timeout in milliseconds to wait for the binder interface to freeze
+     * before giving up.
+     *
+     * @throws RuntimeException in case a flush/freeze operation could not complete successfully.
+     * @return 0 if success, or -EAGAIN indicating there's pending transaction.
+     */
+    public int freezeBinder(int pid, boolean freeze, int timeoutMs) {
+        return nativeFreezeBinder(pid, freeze, timeoutMs);
+    }
+
+    /**
+     * Retrieves binder freeze info about a process.
+     * @param pid the pid for which binder freeze info is to be retrieved.
+     *
+     * @throws RuntimeException if the operation could not complete successfully.
+     * @return a bit field reporting the binder freeze info for the process.
+     */
+    public int getBinderFreezeInfo(int pid) {
+        return nativeGetBinderFreezeInfo(pid);
+    }
+
+    /**
+     * Determines whether the freezer is supported by this system.
+     * @return true if the freezer is supported.
+     */
+    public boolean isFreezerSupported() {
+        return nativeIsFreezerSupported();
+    }
+
+    // Native methods
+
+    /**
+     * Informs binder that a process is about to be frozen. If freezer is enabled on a process via
+     * this method, this method will synchronously dispatch all pending transactions to the
+     * specified pid. This method will not add significant latencies when unfreezing.
+     * After freezing binder calls, binder will block all transaction to the frozen pid, and return
+     * an error to the sending process.
+     *
+     * @param pid the target pid for which binder transactions are to be frozen
+     * @param freeze specifies whether to flush transactions and then freeze (true) or unfreeze
+     * binder for the specified pid.
+     * @param timeoutMs the timeout in milliseconds to wait for the binder interface to freeze
+     * before giving up.
+     *
+     * @throws RuntimeException in case a flush/freeze operation could not complete successfully.
+     * @return 0 if success, or -EAGAIN indicating there's pending transaction.
+     */
+    private static native int nativeFreezeBinder(int pid, boolean freeze, int timeoutMs);
+
+    /**
+     * Retrieves binder freeze info about a process.
+     * @param pid the pid for which binder freeze info is to be retrieved.
+     *
+     * @throws RuntimeException if the operation could not complete successfully.
+     * @return a bit field reporting the binder freeze info for the process.
+     */
+    private static native int nativeGetBinderFreezeInfo(int pid);
+
+    /**
+     * Return 0 if the freezer is supported on this platform and -1 otherwise.
+     */
+    private static native boolean nativeIsFreezerSupported();
+}
diff --git a/services/core/java/com/android/server/am/OWNERS b/services/core/java/com/android/server/am/OWNERS
index b517631..d6f04db 100644
--- a/services/core/java/com/android/server/am/OWNERS
+++ b/services/core/java/com/android/server/am/OWNERS
@@ -39,6 +39,7 @@
 per-file ContentProviderHelper.java = varunshah@google.com, omakoto@google.com, jsharkey@google.com, yamasani@google.com
 
 per-file CachedAppOptimizer.java = file:/PERFORMANCE_OWNERS
+per-file Freezer.java = file:/PERFORMANCE_OWNERS
 
 # Multiuser
 per-file User* = file:/MULTIUSER_OWNERS
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 504c54a..ab63e24 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -2864,6 +2864,11 @@
                         }
                     }
 
+                    if (newAdj == clientAdj && app.isolated) {
+                        // Make bound isolated processes have slightly worse score than their client
+                        newAdj = clientAdj + 1;
+                    }
+
                     if (adj >  newAdj) {
                         adj = newAdj;
                         if (state.setCurRawAdj(adj, dryRun)) {
diff --git a/services/core/java/com/android/server/am/PendingIntentController.java b/services/core/java/com/android/server/am/PendingIntentController.java
index f336120..3b0147c 100644
--- a/services/core/java/com/android/server/am/PendingIntentController.java
+++ b/services/core/java/com/android/server/am/PendingIntentController.java
@@ -149,21 +149,6 @@
                         ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED);
             }
 
-            if (opts != null && opts.isPendingIntentBackgroundActivityLaunchAllowedByPermission()) {
-                Slog.wtf(TAG,
-                        "Resetting option pendingIntentBackgroundActivityLaunchAllowedByPermission"
-                                + " which is set by the pending intent creator ("
-                                + packageName
-                                + ") because this option is meant for the pending intent sender");
-                if (CompatChanges.isChangeEnabled(PendingIntent.PENDING_INTENT_OPTIONS_CHECK,
-                        callingUid)) {
-                    throw new IllegalArgumentException(
-                            "pendingIntentBackgroundActivityLaunchAllowedByPermission "
-                                    + "can not be set by creator of a PendingIntent");
-                }
-                opts.setPendingIntentBackgroundActivityLaunchAllowedByPermission(false);
-            }
-
             final boolean noCreate = (flags & PendingIntent.FLAG_NO_CREATE) != 0;
             final boolean cancelCurrent = (flags & PendingIntent.FLAG_CANCEL_CURRENT) != 0;
             final boolean updateCurrent = (flags & PendingIntent.FLAG_UPDATE_CURRENT) != 0;
diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java
index 8eef71e..6857b6b 100644
--- a/services/core/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/core/java/com/android/server/am/PendingIntentRecord.java
@@ -19,9 +19,11 @@
 import static android.app.ActivityManager.PROCESS_STATE_TOP;
 import static android.app.ActivityManager.START_SUCCESS;
 import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED;
+import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS;
 import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_COMPAT;
 import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_DENIED;
 import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED;
+import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_IF_VISIBLE;
 import static android.os.Process.ROOT_UID;
 import static android.os.Process.SYSTEM_UID;
 
@@ -367,17 +369,6 @@
     }
 
     /**
-     * Return true if the activity options allows PendingIntent to use caller's BAL permission.
-     */
-    public static boolean isPendingIntentBalAllowedByPermission(
-            @Nullable ActivityOptions activityOptions) {
-        if (activityOptions == null) {
-            return false;
-        }
-        return activityOptions.isPendingIntentBackgroundActivityLaunchAllowedByPermission();
-    }
-
-    /**
      * Return the {@link BackgroundStartPrivileges} the activity options grant the PendingIntent to
      * use caller's BAL permission.
      */
@@ -404,6 +395,8 @@
                 return BackgroundStartPrivileges.NONE;
             case MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED:
                 return getDefaultBackgroundStartPrivileges(callingUid, callingPackage);
+            case MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS:
+            case MODE_BACKGROUND_ACTIVITY_START_ALLOW_IF_VISIBLE:
             case MODE_BACKGROUND_ACTIVITY_START_ALLOWED:
             case MODE_BACKGROUND_ACTIVITY_START_COMPAT:
             default:
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 779aabe..bb0c24b 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -951,12 +951,14 @@
                             try {
                                 switch (inputData.readInt()) {
                                     case LMK_PROCKILL:
-                                        if (receivedLen != 12) {
+                                        if (receivedLen != 16) {
                                             return false;
                                         }
                                         final int pid = inputData.readInt();
                                         final int uid = inputData.readInt();
-                                        mAppExitInfoTracker.scheduleNoteLmkdProcKilled(pid, uid);
+                                        final int rssKb = inputData.readInt();
+                                        mAppExitInfoTracker.scheduleNoteLmkdProcKilled(pid, uid,
+                                                rssKb);
                                         return true;
                                     case LMK_KILL_OCCURRED:
                                         if (receivedLen
@@ -3003,7 +3005,7 @@
         return freezePackageCgroup(packageUID, false);
     }
 
-    private static void freezeBinderAndPackageCgroup(List<Pair<ProcessRecord, Boolean>> procs,
+    private void freezeBinderAndPackageCgroup(List<Pair<ProcessRecord, Boolean>> procs,
                                                      int packageUID) {
         // Freeze all binder processes under the target UID (whose cgroup is about to be frozen).
         // Since we're going to kill these, we don't need to unfreze them later.
@@ -3017,7 +3019,7 @@
                 try {
                     int rc;
                     do {
-                        rc = CachedAppOptimizer.freezeBinder(pid, true, 10 /* timeout_ms */);
+                        rc = mService.getFreezer().freezeBinder(pid, true, 10 /* timeout_ms */);
                     } while (rc == -EAGAIN && nRetries++ < 1);
                     if (rc != 0) Slog.e(TAG, "Unable to freeze binder for " + pid + ": " + rc);
                 } catch (RuntimeException e) {
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index 7f43fae..3123268 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -174,6 +174,7 @@
         "haptics",
         "hardware_backed_security_mainline",
         "input",
+        "incremental",
         "llvm_and_toolchains",
         "lse_desktop_experience",
         "machine_learning",
diff --git a/services/core/java/com/android/server/appop/AttributedOp.java b/services/core/java/com/android/server/appop/AttributedOp.java
index 8cf47d0..430be03 100644
--- a/services/core/java/com/android/server/appop/AttributedOp.java
+++ b/services/core/java/com/android/server/appop/AttributedOp.java
@@ -109,7 +109,7 @@
                 uidState, flags);
 
         mAppOpsService.mHistoricalRegistry.incrementOpAccessedCount(parent.op, parent.uid,
-                parent.packageName, tag, uidState, flags, accessTime,
+                parent.packageName, persistentDeviceId, tag, uidState, flags, accessTime,
                 AppOpsManager.ATTRIBUTION_FLAGS_NONE, AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE);
     }
 
@@ -253,8 +253,8 @@
 
         if (isStarted) {
             mAppOpsService.mHistoricalRegistry.incrementOpAccessedCount(parent.op, parent.uid,
-                    parent.packageName, tag, uidState, flags, startTime, attributionFlags,
-                    attributionChainId);
+                    parent.packageName, persistentDeviceId, tag, uidState, flags, startTime,
+                    attributionFlags, attributionChainId);
         }
     }
 
@@ -333,7 +333,7 @@
                     finishedEvent);
 
             mAppOpsService.mHistoricalRegistry.increaseOpAccessDuration(parent.op, parent.uid,
-                    parent.packageName, tag, event.getUidState(),
+                    parent.packageName, persistentDeviceId, tag, event.getUidState(),
                     event.getFlags(), finishedEvent.getNoteTime(), finishedEvent.getDuration(),
                     event.getAttributionFlags(), event.getAttributionChainId());
 
@@ -441,8 +441,9 @@
             event.setStartElapsedTime(SystemClock.elapsedRealtime());
             event.setStartTime(startTime);
             mAppOpsService.mHistoricalRegistry.incrementOpAccessedCount(parent.op, parent.uid,
-                    parent.packageName, tag, event.getUidState(), event.getFlags(), startTime,
-                    event.getAttributionFlags(), event.getAttributionChainId());
+                    parent.packageName, persistentDeviceId, tag, event.getUidState(),
+                    event.getFlags(), startTime, event.getAttributionFlags(),
+                    event.getAttributionChainId());
             if (shouldSendActive) {
                 mAppOpsService.scheduleOpActiveChangedIfNeededLocked(parent.op, parent.uid,
                         parent.packageName, tag, event.getVirtualDeviceId(), true,
diff --git a/services/core/java/com/android/server/appop/DiscreteRegistry.java b/services/core/java/com/android/server/appop/DiscreteRegistry.java
index 5d83ad6..539dbca 100644
--- a/services/core/java/com/android/server/appop/DiscreteRegistry.java
+++ b/services/core/java/com/android/server/appop/DiscreteRegistry.java
@@ -41,6 +41,7 @@
 import static android.app.AppOpsManager.OP_RESERVED_FOR_TESTING;
 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;
@@ -52,6 +53,7 @@
 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;
@@ -76,7 +78,7 @@
 import java.time.temporal.ChronoUnit;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Collections;
+import java.util.Comparator;
 import java.util.Date;
 import java.util.List;
 import java.util.Objects;
@@ -164,6 +166,8 @@
     private static final String TAG_OP = "o";
     private static final String ATTR_OP_ID = "op";
 
+    private static final String ATTR_DEVICE_ID = "di";
+
     private static final String TAG_TAG = "a";
     private static final String ATTR_TAG = "at";
 
@@ -196,11 +200,14 @@
     private boolean mDebugMode = false;
 
     DiscreteRegistry(Object inMemoryLock) {
+        this(inMemoryLock, new File(new File(Environment.getDataSystemDirectory(), "appops"),
+                "discrete"));
+    }
+
+    DiscreteRegistry(Object inMemoryLock, File discreteAccessDir) {
         mInMemoryLock = inMemoryLock;
         synchronized (mOnDiskLock) {
-            mDiscreteAccessDir = new File(
-                    new File(Environment.getDataSystemDirectory(), "appops"),
-                    "discrete");
+            mDiscreteAccessDir = discreteAccessDir;
             createDiscreteAccessDirLocked();
             int largestChainId = readLargestChainIdFromDiskLocked();
             synchronized (mInMemoryLock) {
@@ -245,16 +252,24 @@
                 DEFAULT_DISCRETE_OPS);
     }
 
-    void recordDiscreteAccess(int uid, String packageName, int op, @Nullable String attributionTag,
-            @AppOpsManager.OpFlags int flags, @AppOpsManager.UidState int uidState, long accessTime,
-            long accessDuration, @AppOpsManager.AttributionFlags int attributionFlags,
-            int attributionChainId) {
+    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) {
         if (!isDiscreteOp(op, flags)) {
             return;
         }
         synchronized (mInMemoryLock) {
-            mDiscreteOps.addDiscreteAccess(op, uid, packageName, attributionTag, flags, uidState,
-                    accessTime, accessDuration, attributionFlags, attributionChainId);
+            if (Flags.deviceAwareAppOpNewSchemaEnabled()) {
+                mDiscreteOps.addDiscreteAccess(op, uid, packageName, deviceId, attributionTag,
+                        flags, uidState, accessTime, accessDuration, attributionFlags,
+                        attributionChainId);
+            } else {
+                mDiscreteOps.addDiscreteAccess(op, uid, packageName, PERSISTENT_DEVICE_ID_DEFAULT,
+                        attributionTag, flags, uidState, accessTime, accessDuration,
+                        attributionFlags, attributionChainId);
+            }
+
         }
     }
 
@@ -294,7 +309,6 @@
         discreteOps.filter(beginTimeMillis, endTimeMillis, filter, uidFilter, packageNameFilter,
                 opNamesFilter, attributionTagFilter, flagsFilter, attributionChains);
         discreteOps.applyToHistoricalOps(result, attributionChains);
-        return;
     }
 
     private int readLargestChainIdFromDiskLocked() {
@@ -355,28 +369,36 @@
                 String pkg = pkgs.keyAt(pkgNum);
                 int nOps = ops.size();
                 for (int opNum = 0; opNum < nOps; opNum++) {
-                    ArrayMap<String, List<DiscreteOpEvent>> attrOps =
-                            ops.valueAt(opNum).mAttributedOps;
                     int op = ops.keyAt(opNum);
-                    int nAttrOps = attrOps.size();
-                    for (int attrOpNum = 0; attrOpNum < nAttrOps; attrOpNum++) {
-                        List<DiscreteOpEvent> opEvents = attrOps.valueAt(attrOpNum);
-                        String attributionTag = attrOps.keyAt(attrOpNum);
-                        int nOpEvents = opEvents.size();
-                        for (int opEventNum = 0; opEventNum < nOpEvents; opEventNum++) {
-                            DiscreteOpEvent event = opEvents.get(opEventNum);
-                            if (event == null
-                                    || event.mAttributionChainId == ATTRIBUTION_CHAIN_ID_NONE
-                                    || (event.mAttributionFlags & ATTRIBUTION_FLAG_TRUSTED) == 0) {
-                                continue;
-                            }
+                    ArrayMap<String, DiscreteDeviceOp> deviceOps =
+                            ops.valueAt(opNum).mDeviceAttributedOps;
 
-                            if (!chains.containsKey(event.mAttributionChainId)) {
-                                chains.put(event.mAttributionChainId,
-                                        new AttributionChain(attributionExemptPkgs));
+                    int nDeviceOps = deviceOps.size();
+                    for (int deviceNum = 0; deviceNum < nDeviceOps; deviceNum++) {
+                        ArrayMap<String, List<DiscreteOpEvent>> attrOps =
+                                deviceOps.valueAt(deviceNum).mAttributedOps;
+
+                        int nAttrOps = attrOps.size();
+                        for (int attrOpNum = 0; attrOpNum < nAttrOps; attrOpNum++) {
+                            List<DiscreteOpEvent> opEvents = attrOps.valueAt(attrOpNum);
+                            String attributionTag = attrOps.keyAt(attrOpNum);
+                            int nOpEvents = opEvents.size();
+                            for (int opEventNum = 0; opEventNum < nOpEvents; opEventNum++) {
+                                DiscreteOpEvent event = opEvents.get(opEventNum);
+                                if (event == null
+                                        || event.mAttributionChainId == ATTRIBUTION_CHAIN_ID_NONE
+                                        || (event.mAttributionFlags & ATTRIBUTION_FLAG_TRUSTED)
+                                                == 0) {
+                                    continue;
+                                }
+
+                                if (!chains.containsKey(event.mAttributionChainId)) {
+                                    chains.put(event.mAttributionChainId,
+                                            new AttributionChain(attributionExemptPkgs));
+                                }
+                                chains.get(event.mAttributionChainId)
+                                        .addEvent(pkg, uid, attributionTag, op, event);
                             }
-                            chains.get(event.mAttributionChainId)
-                                    .addEvent(pkg, uid, attributionTag, op, event);
                         }
                     }
                 }
@@ -464,7 +486,7 @@
         createDiscreteAccessDir();
     }
 
-    private DiscreteOps getAllDiscreteOps() {
+    DiscreteOps getAllDiscreteOps() {
         DiscreteOps discreteOps = new DiscreteOps(0);
 
         synchronized (mOnDiskLock) {
@@ -608,7 +630,7 @@
         }
     }
 
-    private final class DiscreteOps {
+    static final class DiscreteOps {
         ArrayMap<Integer, DiscreteUidOps> mUids;
         int mChainIdOffset;
         int mLargestChainId;
@@ -634,8 +656,9 @@
         }
 
         void addDiscreteAccess(int op, int uid, @NonNull String packageName,
-                @Nullable String attributionTag, @AppOpsManager.OpFlags int flags,
-                @AppOpsManager.UidState int uidState, long accessTime, long accessDuration,
+                @NonNull String deviceId, @Nullable String attributionTag,
+                @AppOpsManager.OpFlags int flags, @AppOpsManager.UidState int uidState,
+                long accessTime, long accessDuration,
                 @AppOpsManager.AttributionFlags int attributionFlags, int attributionChainId) {
             int offsetChainId = attributionChainId;
             if (attributionChainId != ATTRIBUTION_CHAIN_ID_NONE) {
@@ -649,8 +672,9 @@
                     mChainIdOffset = -1 * attributionChainId;
                 }
             }
-            getOrCreateDiscreteUidOps(uid).addDiscreteAccess(op, packageName, attributionTag, flags,
-                    uidState, accessTime, accessDuration, attributionFlags, offsetChainId);
+            getOrCreateDiscreteUidOps(uid).addDiscreteAccess(op, packageName, deviceId,
+                    attributionTag, flags, uidState, accessTime, accessDuration, attributionFlags,
+                    offsetChainId);
         }
 
         private void filter(long beginTimeMillis, long endTimeMillis,
@@ -837,7 +861,7 @@
         }
     }
 
-    private final class DiscreteUidOps {
+    static final class DiscreteUidOps {
         ArrayMap<String, DiscretePackageOps> mPackages;
 
         DiscreteUidOps() {
@@ -889,12 +913,13 @@
             mPackages.remove(packageName);
         }
 
-        void addDiscreteAccess(int op, @NonNull String packageName, @Nullable String attributionTag,
-                @AppOpsManager.OpFlags int flags, @AppOpsManager.UidState int uidState,
-                long accessTime, long accessDuration,
+        void addDiscreteAccess(int op, @NonNull String packageName, @NonNull String deviceId,
+                @Nullable String attributionTag, @AppOpsManager.OpFlags int flags,
+                @AppOpsManager.UidState int uidState, long accessTime, long accessDuration,
                 @AppOpsManager.AttributionFlags int attributionFlags, int attributionChainId) {
-            getOrCreateDiscretePackageOps(packageName).addDiscreteAccess(op, attributionTag, flags,
-                    uidState, accessTime, accessDuration, attributionFlags, attributionChainId);
+            getOrCreateDiscretePackageOps(packageName).addDiscreteAccess(op, deviceId,
+                    attributionTag, flags, uidState, accessTime, accessDuration,
+                    attributionFlags, attributionChainId);
         }
 
         private DiscretePackageOps getOrCreateDiscretePackageOps(String packageName) {
@@ -948,7 +973,7 @@
         }
     }
 
-    private final class DiscretePackageOps {
+    static final class DiscretePackageOps {
         ArrayMap<Integer, DiscreteOp> mPackageOps;
 
         DiscretePackageOps() {
@@ -959,12 +984,12 @@
             return mPackageOps.isEmpty();
         }
 
-        void addDiscreteAccess(int op, @Nullable String attributionTag,
+        void addDiscreteAccess(int op, @NonNull String deviceId, @Nullable String attributionTag,
                 @AppOpsManager.OpFlags int flags, @AppOpsManager.UidState int uidState,
                 long accessTime, long accessDuration,
                 @AppOpsManager.AttributionFlags int attributionFlags, int attributionChainId) {
-            getOrCreateDiscreteOp(op).addDiscreteAccess(attributionTag, flags, uidState, accessTime,
-                    accessDuration, attributionFlags, attributionChainId);
+            getOrCreateDiscreteOp(op).addDiscreteAccess(deviceId, attributionTag, flags, uidState,
+                    accessTime, accessDuration, attributionFlags, attributionChainId);
         }
 
         void merge(DiscretePackageOps other) {
@@ -1056,10 +1081,148 @@
         }
     }
 
-    private final class DiscreteOp {
-        ArrayMap<String, List<DiscreteOpEvent>> mAttributedOps;
+    static final class DiscreteOp {
+        ArrayMap<String, DiscreteDeviceOp> mDeviceAttributedOps;
 
         DiscreteOp() {
+            mDeviceAttributedOps = new ArrayMap<>();
+        }
+
+        boolean isEmpty() {
+            return mDeviceAttributedOps.isEmpty();
+        }
+
+        void merge(DiscreteOp other) {
+            int nDevices = other.mDeviceAttributedOps.size();
+            for (int i = 0; i < nDevices; i++) {
+                String deviceId = other.mDeviceAttributedOps.keyAt(i);
+                DiscreteDeviceOp otherDeviceOps = other.mDeviceAttributedOps.valueAt(i);
+                getOrCreateDiscreteDeviceOp(deviceId).merge(otherDeviceOps);
+            }
+        }
+
+        // Note: Update this method when we want to filter by device Id.
+        private void filter(long beginTimeMillis, long endTimeMillis,
+                @AppOpsManager.HistoricalOpsRequestFilter int filter,
+                @Nullable String attributionTagFilter, @AppOpsManager.OpFlags int flagsFilter,
+                int currentUid, String currentPkgName, int currentOp,
+                ArrayMap<Integer, AttributionChain> attributionChains) {
+            int nDevices = mDeviceAttributedOps.size();
+            for (int i = nDevices - 1; i >= 0; i--) {
+                mDeviceAttributedOps.valueAt(i).filter(beginTimeMillis, endTimeMillis, filter,
+                        attributionTagFilter, flagsFilter, currentUid, currentPkgName, currentOp,
+                        attributionChains);
+                if (mDeviceAttributedOps.valueAt(i).isEmpty()) {
+                    mDeviceAttributedOps.removeAt(i);
+                }
+            }
+        }
+
+        private void offsetHistory(long offset) {
+            int nDevices = mDeviceAttributedOps.size();
+            for (int i = 0; i < nDevices; i++) {
+                mDeviceAttributedOps.valueAt(i).offsetHistory(offset);
+            }
+        }
+
+        void addDiscreteAccess(@NonNull String deviceId, @Nullable String attributionTag,
+                @AppOpsManager.OpFlags int flags, @AppOpsManager.UidState int uidState,
+                long accessTime, long accessDuration,
+                @AppOpsManager.AttributionFlags int attributionFlags, int attributionChainId) {
+            getOrCreateDiscreteDeviceOp(deviceId).addDiscreteAccess(attributionTag, flags, uidState,
+                    accessTime, accessDuration, attributionFlags, attributionChainId);
+        }
+
+        private DiscreteDeviceOp getOrCreateDiscreteDeviceOp(String deviceId) {
+            return mDeviceAttributedOps.computeIfAbsent(deviceId, k -> new DiscreteDeviceOp());
+        }
+
+        // TODO: b/308716962 Retrieve discrete histories from all devices and integrate them with
+        // HistoricalOps
+        private void applyToHistory(AppOpsManager.HistoricalOps result, int uid,
+                @NonNull String packageName, int op,
+                @NonNull ArrayMap<Integer, AttributionChain> attributionChains) {
+            if (mDeviceAttributedOps.get(PERSISTENT_DEVICE_ID_DEFAULT) != null) {
+                mDeviceAttributedOps.get(PERSISTENT_DEVICE_ID_DEFAULT).applyToHistory(result, uid,
+                        packageName, op, attributionChains);
+            }
+        }
+
+        private void dump(@NonNull PrintWriter pw, @NonNull SimpleDateFormat sdf,
+                @NonNull Date date, @NonNull String prefix, int nDiscreteOps) {
+            int nDevices = mDeviceAttributedOps.size();
+            for (int i = 0; i < nDevices; i++) {
+                pw.print(prefix);
+                pw.print("Device: ");
+                pw.print(mDeviceAttributedOps.keyAt(i));
+                pw.println();
+                mDeviceAttributedOps.valueAt(i).dump(pw, sdf, date, prefix + "  ",
+                        nDiscreteOps);
+            }
+        }
+
+        void serialize(TypedXmlSerializer out) throws Exception {
+            int nDevices = mDeviceAttributedOps.size();
+            for (int i = 0; i < nDevices; i++) {
+                String deviceId = mDeviceAttributedOps.keyAt(i);
+                mDeviceAttributedOps.valueAt(i).serialize(out, deviceId);
+            }
+        }
+
+        void deserialize(TypedXmlPullParser parser, long beginTimeMillis) throws Exception {
+            int outerDepth = parser.getDepth();
+            while (XmlUtils.nextElementWithin(parser, outerDepth)) {
+                if (TAG_TAG.equals(parser.getName())) {
+                    String attributionTag = parser.getAttributeValue(null, ATTR_TAG);
+
+                    int innerDepth = parser.getDepth();
+                    while (XmlUtils.nextElementWithin(parser, innerDepth)) {
+                        if (TAG_ENTRY.equals(parser.getName())) {
+                            long noteTime = parser.getAttributeLong(null, ATTR_NOTE_TIME);
+                            long noteDuration = parser.getAttributeLong(null, ATTR_NOTE_DURATION,
+                                    -1);
+                            int uidState = parser.getAttributeInt(null, ATTR_UID_STATE);
+                            int opFlags = parser.getAttributeInt(null, ATTR_FLAGS);
+                            int attributionFlags = parser.getAttributeInt(null,
+                                    ATTR_ATTRIBUTION_FLAGS, AppOpsManager.ATTRIBUTION_FLAGS_NONE);
+                            int attributionChainId = parser.getAttributeInt(null, ATTR_CHAIN_ID,
+                                    AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE);
+                            String deviceId = parser.getAttributeValue(null, ATTR_DEVICE_ID);
+                            if (deviceId == null) {
+                                deviceId = PERSISTENT_DEVICE_ID_DEFAULT;
+                            }
+                            if (noteTime + noteDuration < beginTimeMillis) {
+                                continue;
+                            }
+
+                            DiscreteDeviceOp deviceOps = getOrCreateDiscreteDeviceOp(deviceId);
+                            List<DiscreteOpEvent> events =
+                                    deviceOps.getOrCreateDiscreteOpEventsList(attributionTag);
+                            DiscreteOpEvent event = new DiscreteOpEvent(noteTime, noteDuration,
+                                    uidState, opFlags, attributionFlags, attributionChainId);
+                            events.add(event);
+                        }
+                    }
+                }
+            }
+
+            int nDeviceOps = mDeviceAttributedOps.size();
+            for (int i = 0; i < nDeviceOps; i++) {
+                DiscreteDeviceOp deviceOp = mDeviceAttributedOps.valueAt(i);
+
+                int nAttrOps = deviceOp.mAttributedOps.size();
+                for (int j = 0; j < nAttrOps; j++) {
+                    List<DiscreteOpEvent> events = deviceOp.mAttributedOps.valueAt(j);
+                    events.sort(Comparator.comparingLong(a -> a.mNoteTime));
+                }
+            }
+        }
+    }
+
+    static final class DiscreteDeviceOp {
+        ArrayMap<String, List<DiscreteOpEvent>> mAttributedOps;
+
+        DiscreteDeviceOp() {
             mAttributedOps = new ArrayMap<>();
         }
 
@@ -1067,7 +1230,7 @@
             return mAttributedOps.isEmpty();
         }
 
-        void merge(DiscreteOp other) {
+        void merge(DiscreteDeviceOp other) {
             int nTags = other.mAttributedOps.size();
             for (int i = 0; i < nTags; i++) {
                 String tag = other.mAttributedOps.keyAt(i);
@@ -1097,7 +1260,7 @@
                         currentUid, currentPkgName, currentOp, mAttributedOps.keyAt(i),
                         attributionChains);
                 mAttributedOps.put(tag, list);
-                if (list.size() == 0) {
+                if (list.isEmpty()) {
                     mAttributedOps.removeAt(i);
                 }
             }
@@ -1125,8 +1288,7 @@
             List<DiscreteOpEvent> attributedOps = getOrCreateDiscreteOpEventsList(
                     attributionTag);
 
-            int nAttributedOps = attributedOps.size();
-            int i = nAttributedOps;
+            int i = attributedOps.size();
             for (; i > 0; i--) {
                 DiscreteOpEvent previousOp = attributedOps.get(i - 1);
                 if (discretizeTimeStamp(previousOp.mNoteTime) < discretizeTimeStamp(accessTime)) {
@@ -1148,12 +1310,8 @@
         }
 
         private List<DiscreteOpEvent> getOrCreateDiscreteOpEventsList(String attributionTag) {
-            List<DiscreteOpEvent> result = mAttributedOps.get(attributionTag);
-            if (result == null) {
-                result = new ArrayList<>();
-                mAttributedOps.put(attributionTag, result);
-            }
-            return result;
+            return mAttributedOps.computeIfAbsent(attributionTag,
+                    k -> new ArrayList<>());
         }
 
         private void applyToHistory(AppOpsManager.HistoricalOps result, int uid,
@@ -1167,8 +1325,7 @@
                 for (int j = 0; j < nEvents; j++) {
                     DiscreteOpEvent event = events.get(j);
                     AppOpsManager.OpEventProxyInfo proxy = null;
-                    if (event.mAttributionChainId != ATTRIBUTION_CHAIN_ID_NONE
-                            && attributionChains != null) {
+                    if (event.mAttributionChainId != ATTRIBUTION_CHAIN_ID_NONE) {
                         AttributionChain chain = attributionChains.get(event.mAttributionChainId);
                         if (chain != null && chain.isComplete()
                                 && chain.isStart(packageName, uid, tag, op, event)
@@ -1198,65 +1355,31 @@
                 int first = nDiscreteOps < 1 ? 0 : max(0, nOps - nDiscreteOps);
                 for (int j = first; j < nOps; j++) {
                     ops.get(j).dump(pw, sdf, date, prefix + "  ");
-
                 }
             }
         }
 
-        void serialize(TypedXmlSerializer out) throws Exception {
+        void serialize(TypedXmlSerializer out, String deviceId) throws Exception {
             int nAttributions = mAttributedOps.size();
             for (int i = 0; i < nAttributions; i++) {
                 out.startTag(null, TAG_TAG);
                 String tag = mAttributedOps.keyAt(i);
                 if (tag != null) {
-                    out.attribute(null, ATTR_TAG, mAttributedOps.keyAt(i));
+                    out.attribute(null, ATTR_TAG, tag);
                 }
                 List<DiscreteOpEvent> ops = mAttributedOps.valueAt(i);
                 int nOps = ops.size();
                 for (int j = 0; j < nOps; j++) {
                     out.startTag(null, TAG_ENTRY);
-                    ops.get(j).serialize(out);
+                    ops.get(j).serialize(out, deviceId);
                     out.endTag(null, TAG_ENTRY);
                 }
                 out.endTag(null, TAG_TAG);
             }
         }
-
-        void deserialize(TypedXmlPullParser parser, long beginTimeMillis) throws Exception {
-            int outerDepth = parser.getDepth();
-            while (XmlUtils.nextElementWithin(parser, outerDepth)) {
-                if (TAG_TAG.equals(parser.getName())) {
-                    String attributionTag = parser.getAttributeValue(null, ATTR_TAG);
-                    List<DiscreteOpEvent> events = getOrCreateDiscreteOpEventsList(
-                            attributionTag);
-                    int innerDepth = parser.getDepth();
-                    while (XmlUtils.nextElementWithin(parser, innerDepth)) {
-                        if (TAG_ENTRY.equals(parser.getName())) {
-                            long noteTime = parser.getAttributeLong(null, ATTR_NOTE_TIME);
-                            long noteDuration = parser.getAttributeLong(null, ATTR_NOTE_DURATION,
-                                    -1);
-                            int uidState = parser.getAttributeInt(null, ATTR_UID_STATE);
-                            int opFlags = parser.getAttributeInt(null, ATTR_FLAGS);
-                            int attributionFlags = parser.getAttributeInt(null,
-                                    ATTR_ATTRIBUTION_FLAGS, AppOpsManager.ATTRIBUTION_FLAGS_NONE);
-                            int attributionChainId = parser.getAttributeInt(null, ATTR_CHAIN_ID,
-                                    AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE);
-                            if (noteTime + noteDuration < beginTimeMillis) {
-                                continue;
-                            }
-                            DiscreteOpEvent event = new DiscreteOpEvent(noteTime, noteDuration,
-                                    uidState, opFlags, attributionFlags, attributionChainId);
-                            events.add(event);
-                        }
-                    }
-                    Collections.sort(events, (a, b) -> a.mNoteTime < b.mNoteTime ? -1
-                            : (a.mNoteTime == b.mNoteTime ? 0 : 1));
-                }
-            }
-        }
     }
 
-    private final class DiscreteOpEvent {
+    static final class DiscreteOpEvent {
         final long mNoteTime;
         final long mNoteDuration;
         final @AppOpsManager.UidState int mUidState;
@@ -1306,7 +1429,7 @@
             pw.println();
         }
 
-        private void serialize(TypedXmlSerializer out) throws Exception {
+        private void serialize(TypedXmlSerializer out, String deviceId) throws Exception {
             out.attributeLong(null, ATTR_NOTE_TIME, mNoteTime);
             if (mNoteDuration != -1) {
                 out.attributeLong(null, ATTR_NOTE_DURATION, mNoteDuration);
@@ -1317,6 +1440,9 @@
             if (mAttributionChainId != AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE) {
                 out.attributeInt(null, ATTR_CHAIN_ID, mAttributionChainId);
             }
+            if (!Objects.equals(deviceId, PERSISTENT_DEVICE_ID_DEFAULT)) {
+                out.attribute(null, ATTR_DEVICE_ID, deviceId);
+            }
             out.attributeInt(null, ATTR_UID_STATE, mUidState);
             out.attributeInt(null, ATTR_FLAGS, mOpFlag);
         }
diff --git a/services/core/java/com/android/server/appop/HistoricalRegistry.java b/services/core/java/com/android/server/appop/HistoricalRegistry.java
index dbd47d0..fffb108 100644
--- a/services/core/java/com/android/server/appop/HistoricalRegistry.java
+++ b/services/core/java/com/android/server/appop/HistoricalRegistry.java
@@ -472,9 +472,9 @@
     }
 
     void incrementOpAccessedCount(int op, int uid, @NonNull String packageName,
-            @Nullable String attributionTag, @UidState int uidState, @OpFlags int flags,
-            long accessTime, @AppOpsManager.AttributionFlags int attributionFlags,
-            int attributionChainId) {
+            @NonNull String deviceId, @Nullable String attributionTag, @UidState int uidState,
+            @OpFlags int flags, long accessTime,
+            @AppOpsManager.AttributionFlags int attributionFlags, int attributionChainId) {
         synchronized (mInMemoryLock) {
             if (mMode == AppOpsManager.HISTORICAL_MODE_ENABLED_ACTIVE) {
                 if (!isPersistenceInitializedMLocked()) {
@@ -485,8 +485,9 @@
                         System.currentTimeMillis()).increaseAccessCount(op, uid, packageName,
                         attributionTag, uidState, flags, 1);
 
-                mDiscreteRegistry.recordDiscreteAccess(uid, packageName, op, attributionTag,
-                        flags, uidState, accessTime, -1, attributionFlags, attributionChainId);
+                mDiscreteRegistry.recordDiscreteAccess(uid, packageName, deviceId, op,
+                        attributionTag, flags, uidState, accessTime, -1, attributionFlags,
+                        attributionChainId);
             }
         }
     }
@@ -507,8 +508,8 @@
     }
 
     void increaseOpAccessDuration(int op, int uid, @NonNull String packageName,
-            @Nullable String attributionTag, @UidState int uidState, @OpFlags int flags,
-            long eventStartTime, long increment,
+            @NonNull String deviceId, @Nullable String attributionTag, @UidState int uidState,
+            @OpFlags int flags, long eventStartTime, long increment,
             @AppOpsManager.AttributionFlags int attributionFlags, int attributionChainId) {
         synchronized (mInMemoryLock) {
             if (mMode == AppOpsManager.HISTORICAL_MODE_ENABLED_ACTIVE) {
@@ -519,9 +520,9 @@
                 getUpdatedPendingHistoricalOpsMLocked(
                         System.currentTimeMillis()).increaseAccessDuration(op, uid, packageName,
                         attributionTag, uidState, flags, increment);
-                mDiscreteRegistry.recordDiscreteAccess(uid, packageName, op, attributionTag,
-                        flags, uidState, eventStartTime, increment, attributionFlags,
-                        attributionChainId);
+                mDiscreteRegistry.recordDiscreteAccess(uid, packageName, deviceId, op,
+                        attributionTag, flags, uidState, eventStartTime, increment,
+                        attributionFlags, attributionChainId);
             }
         }
     }
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 25b9228..ca907c5 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -1432,6 +1432,8 @@
         }
         mCurCommunicationPortId = portId;
 
+        mAudioService.postScoDeviceActive(isBluetoothScoActive());
+
         final int nbDispatchers = mCommDevDispatchers.beginBroadcast();
         for (int i = 0; i < nbDispatchers; i++) {
             try {
@@ -2797,6 +2799,10 @@
             return;
         }
         final SettingsAdapter settingsAdapter = mAudioService.getSettings();
+        if (settingsAdapter == null) {
+            Log.e(TAG, "No settings adapter when saving AdiDeviceState: " + deviceSettings);
+            return;
+        }
         try {
             boolean res = settingsAdapter.putSecureStringForUser(mAudioService.getContentResolver(),
                     Settings.Secure.AUDIO_DEVICE_INVENTORY,
@@ -2812,6 +2818,12 @@
     private String readDeviceSettings() {
         final SettingsAdapter settingsAdapter = mAudioService.getSettings();
         final ContentResolver contentResolver = mAudioService.getContentResolver();
+        if (settingsAdapter == null || contentResolver == null) {
+            // should not happen, throw Exception for stack trace
+            Log.e(TAG, "No settings adapter or content resolver to read device settings",
+                    new Exception("readDeviceSettings_NPE"));
+            return "";
+        }
         return settingsAdapter.getSecureStringForUser(contentResolver,
                 Settings.Secure.AUDIO_DEVICE_INVENTORY, UserHandle.USER_CURRENT);
     }
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index ca69f31..8d8a54e 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -31,6 +31,7 @@
 import static android.media.audio.Flags.automaticBtDeviceType;
 
 import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
+import static com.android.media.audio.Flags.asDeviceConnectionFailure;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -529,6 +530,17 @@
         }
     };
 
+    /**
+     * package-protected for unit testing only
+     * Returns the currently connected devices
+     * @return the collection of connected devices
+     */
+    /*package*/ @NonNull Collection<DeviceInfo> getConnectedDevices() {
+        synchronized (mDevicesLock) {
+            return mConnectedDevices.values();
+        }
+    }
+
     // List of devices actually connected to AudioPolicy (through AudioSystem), only one
     // by device type, which is used as the key, value is the DeviceInfo generated key.
     // For the moment only for A2DP sink devices.
@@ -598,8 +610,9 @@
     /**
      * Class to store info about connected devices.
      * Use makeDeviceListKey() to make a unique key for this list.
+     * Package-protected for unit tests
      */
-    private static class DeviceInfo {
+    /*package*/ static class DeviceInfo {
         final int mDeviceType;
         final @NonNull String mDeviceName;
         final @NonNull String mDeviceAddress;
@@ -762,13 +775,27 @@
     // Always executed on AudioDeviceBroker message queue
     /*package*/ void onRestoreDevices() {
         synchronized (mDevicesLock) {
+            int res;
+            List<DeviceInfo> failedReconnectionDeviceList = new ArrayList<>(/*initialCapacity*/ 0);
             //TODO iterate on mApmConnectedDevices instead once it handles all device types
             for (DeviceInfo di : mConnectedDevices.values()) {
-                mAudioSystem.setDeviceConnectionState(new AudioDeviceAttributes(di.mDeviceType,
+                res = mAudioSystem.setDeviceConnectionState(new AudioDeviceAttributes(
+                        di.mDeviceType,
                         di.mDeviceAddress,
                         di.mDeviceName),
                         AudioSystem.DEVICE_STATE_AVAILABLE,
                         di.mDeviceCodecFormat);
+                if (asDeviceConnectionFailure() && res != AudioSystem.AUDIO_STATUS_OK) {
+                    failedReconnectionDeviceList.add(di);
+                }
+            }
+            if (asDeviceConnectionFailure()) {
+                for (DeviceInfo di : failedReconnectionDeviceList) {
+                    AudioService.sDeviceLogger.enqueueAndSlog(
+                            "Device inventory restore failed to reconnect " + di,
+                            EventLogger.Event.ALOGE, TAG);
+                    mConnectedDevices.remove(di.getKey(), di);
+                }
             }
             mAppliedStrategyRolesInt.clear();
             mAppliedPresetRolesInt.clear();
@@ -2070,8 +2097,9 @@
                     "APM failed to make available A2DP device addr="
                             + Utils.anonymizeBluetoothAddress(address)
                             + " error=" + res).printSlog(EventLogger.Event.ALOGE, TAG));
-            // TODO: connection failed, stop here
-            // TODO: return;
+            if (asDeviceConnectionFailure()) {
+                return;
+            }
         } else {
             AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
                     "A2DP sink device addr=" + Utils.anonymizeBluetoothAddress(address)
@@ -2336,8 +2364,7 @@
                     "APM failed to make unavailable A2DP device addr="
                             + Utils.anonymizeBluetoothAddress(address)
                             + " error=" + res).printSlog(EventLogger.Event.ALOGE, TAG));
-            // TODO:  failed to disconnect, stop here
-            // TODO: return;
+            // not taking further action: proceeding as if disconnection from APM worked
         } else {
             AudioService.sDeviceLogger.enqueue((new EventLogger.StringEvent(
                     "A2DP device addr=" + Utils.anonymizeBluetoothAddress(address)
@@ -2383,8 +2410,9 @@
                     "APM failed to make available A2DP source device addr="
                             + Utils.anonymizeBluetoothAddress(address)
                             + " error=" + res).printSlog(EventLogger.Event.ALOGE, TAG));
-            // TODO: connection failed, stop here
-            // TODO: return
+            if (asDeviceConnectionFailure()) {
+                return;
+            }
         } else {
             AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
                     "A2DP source device addr=" + Utils.anonymizeBluetoothAddress(address)
@@ -2402,6 +2430,7 @@
         mAudioSystem.setDeviceConnectionState(ada,
                 AudioSystem.DEVICE_STATE_UNAVAILABLE,
                 AudioSystem.AUDIO_FORMAT_DEFAULT);
+        // always remove regardless of the result
         mConnectedDevices.remove(
                 DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, address));
         mDeviceBroker.postCheckCommunicationDeviceRemoval(ada);
@@ -2418,9 +2447,18 @@
 
         AudioDeviceAttributes ada = new AudioDeviceAttributes(
                 DEVICE_OUT_HEARING_AID, address, name);
-        mAudioSystem.setDeviceConnectionState(ada,
+        final int res = mAudioSystem.setDeviceConnectionState(ada,
                 AudioSystem.DEVICE_STATE_AVAILABLE,
                 AudioSystem.AUDIO_FORMAT_DEFAULT);
+        if (asDeviceConnectionFailure() && res != AudioSystem.AUDIO_STATUS_OK) {
+            AudioService.sDeviceLogger.enqueueAndSlog(
+                    "APM failed to make available HearingAid addr=" + address
+                            + " error=" + res,
+                    EventLogger.Event.ALOGE, TAG);
+            return;
+        }
+        AudioService.sDeviceLogger.enqueueAndSlog("HearingAid made available addr=" + address,
+                EventLogger.Event.ALOGI, TAG);
         mConnectedDevices.put(
                 DeviceInfo.makeDeviceListKey(DEVICE_OUT_HEARING_AID, address),
                 new DeviceInfo(DEVICE_OUT_HEARING_AID, name, address));
@@ -2447,6 +2485,7 @@
         mAudioSystem.setDeviceConnectionState(ada,
                 AudioSystem.DEVICE_STATE_UNAVAILABLE,
                 AudioSystem.AUDIO_FORMAT_DEFAULT);
+        // always remove regardless of return code
         mConnectedDevices.remove(
                 DeviceInfo.makeDeviceListKey(DEVICE_OUT_HEARING_AID, address));
         // Remove Hearing Aid routes as well
@@ -2540,11 +2579,12 @@
             final int res = mAudioSystem.setDeviceConnectionState(ada,
                     AudioSystem.DEVICE_STATE_AVAILABLE, codec);
             if (res != AudioSystem.AUDIO_STATUS_OK) {
-                AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
+                AudioService.sDeviceLogger.enqueueAndSlog(
                         "APM failed to make available LE Audio device addr=" + address
-                                + " error=" + res).printSlog(EventLogger.Event.ALOGE, TAG));
-                // TODO: connection failed, stop here
-                // TODO: return;
+                                + " error=" + res, EventLogger.Event.ALOGE, TAG);
+                if (asDeviceConnectionFailure()) {
+                    return;
+                }
             } else {
                 AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
                         "LE Audio " + (AudioSystem.isInputDevice(device) ? "source" : "sink")
@@ -2596,8 +2636,7 @@
                 AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
                         "APM failed to make unavailable LE Audio device addr=" + address
                                 + " error=" + res).printSlog(EventLogger.Event.ALOGE, TAG));
-                // TODO:  failed to disconnect, stop here
-                // TODO: return;
+                // not taking further action: proceeding as if disconnection from APM worked
             } else {
                 AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
                         "LE Audio device addr=" + Utils.anonymizeBluetoothAddress(address)
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index fe3bbb0..ac43e86 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -63,8 +63,10 @@
 import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
 import static com.android.media.audio.Flags.absVolumeIndexFix;
 import static com.android.media.audio.Flags.alarmMinVolumeZero;
+import static com.android.media.audio.Flags.asDeviceConnectionFailure;
 import static com.android.media.audio.Flags.audioserverPermissions;
 import static com.android.media.audio.Flags.disablePrescaleAbsoluteVolume;
+import static com.android.media.audio.Flags.replaceStreamBtSco;
 import static com.android.media.audio.Flags.ringerModeAffectsAlarm;
 import static com.android.media.audio.Flags.setStreamVolumeOrder;
 import static com.android.media.audio.Flags.vgsVssSyncMuteOrder;
@@ -287,7 +289,6 @@
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.atomic.AtomicLong;
-import java.util.concurrent.atomic.AtomicReference;
 import java.util.function.BooleanSupplier;
 import java.util.stream.Collectors;
 
@@ -462,6 +463,7 @@
     private static final int MSG_CONFIGURATION_CHANGED = 54;
     private static final int MSG_BROADCAST_MASTER_MUTE = 55;
     private static final int MSG_UPDATE_CONTEXTUAL_VOLUMES = 56;
+    private static final int MSG_SCO_DEVICE_ACTIVE_UPDATE = 57;
 
     /**
      * Messages handled by the {@link SoundDoseHelper}, do not exceed
@@ -495,19 +497,60 @@
     private AudioSystemThread mAudioSystemThread;
     /** @see AudioHandler */
     private AudioHandler mAudioHandler;
-    /** @see VolumeStreamState */
-    private VolumeStreamState[] mStreamStates;
+    /**
+     *  @see VolumeStreamState
+     *  Mapping which contains for each stream type its associated {@link VolumeStreamState}
+     **/
+    private SparseArray<VolumeStreamState> mStreamStates;
 
     /*package*/ int getVssVolumeForDevice(int stream, int device) {
-        return mStreamStates[stream].getIndex(device);
+        final VolumeStreamState streamState = mStreamStates.get(stream);
+        return streamState != null ? streamState.getIndex(device) : -1;
     }
 
-    /*package*/ VolumeStreamState getVssVolumeForStream(int stream) {
-        return mStreamStates[stream];
+    /**
+     * Returns the {@link VolumeStreamState} corresponding to the passed stream type. This can be
+     * {@code null} since not all possible stream types have a valid {@link VolumeStreamState} (e.g.
+     * {@link AudioSystem#STREAM_BLUETOOTH_SCO}) is deprecated and will return a {@code null} stream
+     * state).
+     *
+     * @param stream the stream type for querying the stream state
+     *
+     * @return the {@link VolumeStreamState} corresponding to the passed stream type or {@code null}
+     */
+    @Nullable
+    /*package*/ VolumeStreamState getVssForStream(int stream) {
+        return mStreamStates.get(stream);
+    }
+
+    /**
+     * Returns the {@link VolumeStreamState} corresponding to the passed stream type. In case
+     * there is no associated stream state for the given stream type we return the default stream
+     * state for {@link AudioSystem#STREAM_MUSIC} (or throw an {@link IllegalArgumentException} in
+     * the ramp up phase of the replaceStreamBtSco flag to ensure that this case will never happen).
+     *
+     * @param stream the stream type for querying the stream state
+     *
+     * @return the {@link VolumeStreamState} corresponding to the passed stream type
+     */
+    @NonNull
+    /*package*/ VolumeStreamState getVssForStreamOrDefault(int stream) {
+        VolumeStreamState streamState = mStreamStates.get(stream);
+        if (streamState == null) {
+            if (replaceStreamBtSco()) {
+                throw new IllegalArgumentException("No VolumeStreamState for stream " + stream);
+            } else {
+                Log.e(TAG, "No VolumeStreamState for stream " + stream
+                        + ". Returning default state for STREAM_MUSIC", new Exception());
+                streamState = mStreamStates.get(AudioSystem.STREAM_MUSIC);
+            }
+        }
+        return streamState;
     }
 
     /*package*/ int getMaxVssVolumeForStream(int stream) {
-        return mStreamStates[stream].getMaxIndex();
+        final VolumeStreamState streamState = mStreamStates.get(stream);
+        return streamState != null ? streamState.getMaxIndex() : -1;
     }
 
     private SettingsObserver mSettingsObserver;
@@ -549,13 +592,13 @@
         0   // STREAM_ASSISTANT
     };
 
-    /* mStreamVolumeAlias[] indicates for each stream if it uses the volume settings
+    /* sStreamVolumeAlias[] indicates for each stream if it uses the volume settings
      * of another stream: This avoids multiplying the volume settings for hidden
      * stream types that follow other stream behavior for volume settings
      * NOTE: do not create loops in aliases!
      * Some streams alias to different streams according to device category (phone or tablet) or
      * use case (in call vs off call...). See updateStreamVolumeAlias() for more details.
-     *  mStreamVolumeAlias contains STREAM_VOLUME_ALIAS_VOICE aliases for a voice capable device
+     *  sStreamVolumeAlias contains STREAM_VOLUME_ALIAS_VOICE aliases for a voice capable device
      *  (phone), STREAM_VOLUME_ALIAS_TELEVISION for a television or set-top box and
      *  STREAM_VOLUME_ALIAS_DEFAULT for other devices (e.g. tablets).*/
     private final int[] STREAM_VOLUME_ALIAS_VOICE = new int[] {
@@ -620,12 +663,12 @@
         AudioSystem.STREAM_MUSIC,           // STREAM_ACCESSIBILITY
         AudioSystem.STREAM_MUSIC            // STREAM_ASSISTANT
     };
-    protected static int[] mStreamVolumeAlias;
+    protected static SparseIntArray sStreamVolumeAlias;
     private static final int UNSET_INDEX = -1;
 
     /**
      * Map AudioSystem.STREAM_* constants to app ops.  This should be used
-     * after mapping through mStreamVolumeAlias.
+     * after mapping through sStreamVolumeAlias.
      */
     private static final int[] STREAM_VOLUME_OPS = new int[] {
         AppOpsManager.OP_AUDIO_VOICE_VOLUME,            // STREAM_VOICE_CALL
@@ -716,6 +759,8 @@
      * @see System#MUTE_STREAMS_AFFECTED */
     private int mUserMutableStreams;
 
+    private final AtomicBoolean mScoDeviceActive = new AtomicBoolean(false);
+
     @NonNull
     private SoundEffectsHelper mSfxHelper;
 
@@ -1413,7 +1458,7 @@
         mRecordMonitor = new RecordingActivityMonitor(mContext);
         mRecordMonitor.registerRecordingCallback(mVoiceRecordingActivityMonitor, true);
 
-        // must be called before readPersistedSettings() which needs a valid mStreamVolumeAlias[]
+        // must be called before readPersistedSettings() which needs a valid sStreamVolumeAlias[]
         // array initialized by updateStreamVolumeAlias()
         updateStreamVolumeAlias(false /*updateVolumes*/, TAG);
         readPersistedSettings();
@@ -1470,7 +1515,10 @@
         int numStreamTypes = AudioSystem.getNumStreamTypes();
         synchronized (VolumeStreamState.class) {
             for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
-                VolumeStreamState streamState = mStreamStates[streamType];
+                final VolumeStreamState streamState = getVssForStream(streamType);
+                if (streamState == null) {
+                    continue;
+                }
                 int groupId = getVolumeGroupForStreamType(streamType);
                 if (groupId != AudioVolumeGroup.DEFAULT_VOLUME_GROUP
                         && sVolumeGroupStates.indexOfKey(groupId) >= 0) {
@@ -2077,9 +2125,12 @@
         // keep track of any error during stream volume initialization
         int status = AudioSystem.AUDIO_STATUS_OK;
         for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
-            VolumeStreamState streamState = mStreamStates[streamType];
+            VolumeStreamState streamState = getVssForStream(streamType);
+            if (streamState == null) {
+                continue;
+            }
             final int res = AudioSystem.initStreamVolume(
-                    streamType, streamState.mIndexMin / 10, streamState.mIndexMax / 10);
+                    streamType, MIN_STREAM_VOLUME[streamType], MAX_STREAM_VOLUME[streamType]);
             if (res != AudioSystem.AUDIO_STATUS_OK) {
                 status = res;
                 Log.e(TAG, "Failed to initStreamVolume (" + res + ") for stream " + streamType);
@@ -2237,11 +2288,14 @@
             synchronized (VolumeStreamState.class) {
                 int numStreamTypes = AudioSystem.getNumStreamTypes();
                 for (int streamType = 0; streamType < numStreamTypes; streamType++) {
-                    mStreamStates[streamType]
-                            .setAllIndexes(mStreamStates[mStreamVolumeAlias[streamType]], TAG);
-                    // apply stream volume
-                    if (!mStreamStates[streamType].mIsMuted) {
-                        mStreamStates[streamType].applyAllVolumes();
+                    int streamAlias = sStreamVolumeAlias.get(streamType, /*valueIfKeyNotFound=*/-1);
+                    final VolumeStreamState streamState = getVssForStream(streamType);
+                    if (streamAlias != -1 && streamState != null) {
+                        streamState.setAllIndexes(getVssForStream(streamAlias), TAG);
+                        // apply stream volume
+                        if (!streamState.mIsMuted) {
+                            streamState.applyAllVolumes();
+                        }
                     }
                 }
             }
@@ -2332,15 +2386,25 @@
      * @param caller caller of this method
      */
     private void updateVolumeStates(int device, int streamType, String caller) {
+        if (replaceStreamBtSco() && streamType == AudioSystem.STREAM_BLUETOOTH_SCO) {
+            return;
+        }
+
         // Handle device volume aliasing of SPEAKER_SAFE.
         if (device == AudioSystem.DEVICE_OUT_SPEAKER_SAFE) {
             device = AudioSystem.DEVICE_OUT_SPEAKER;
         }
-        if (!mStreamStates[streamType].hasIndexForDevice(device)) {
+
+        final VolumeStreamState streamState = getVssForStream(streamType);
+        if (streamState == null) {
+            // nothing to update
+            return;
+        }
+
+        if (!streamState.hasIndexForDevice(device)) {
             // set the default value, if device is affected by a full/fix/abs volume rule, it
             // will taken into account in checkFixedVolumeDevices()
-            mStreamStates[streamType].setIndex(
-                    mStreamStates[mStreamVolumeAlias[streamType]]
+            streamState.setIndex(getVssForStreamOrDefault(sStreamVolumeAlias.get(streamType))
                             .getIndex(AudioSystem.DEVICE_OUT_DEFAULT),
                     device, caller, true /*hasModifyAudioSettings*/);
         }
@@ -2353,11 +2417,11 @@
         for (AudioDeviceAttributes deviceAttributes : devicesForAttributes) {
             if (deviceAttributes.getType() == AudioDeviceInfo.convertInternalDeviceToDeviceType(
                     device)) {
-                mStreamStates[streamType].checkFixedVolumeDevices();
+                streamState.checkFixedVolumeDevices();
 
                 // Unmute streams if required and device is full volume
                 if (isStreamMute(streamType) && mFullVolumeDevices.contains(device)) {
-                    mStreamStates[streamType].mute(false, "updateVolumeStates(" + caller);
+                    streamState.mute(false, "updateVolumeStates(" + caller);
                 }
             }
         }
@@ -2367,23 +2431,30 @@
     {
         int numStreamTypes = AudioSystem.getNumStreamTypes();
         for (int streamType = 0; streamType < numStreamTypes; streamType++) {
-            mStreamStates[streamType].checkFixedVolumeDevices();
+            final VolumeStreamState vss = getVssForStream(streamType);
+            if (vss != null) {
+                vss.checkFixedVolumeDevices();
+            }
         }
     }
 
     private void checkAllFixedVolumeDevices(int streamType) {
-        mStreamStates[streamType].checkFixedVolumeDevices();
+        final VolumeStreamState vss = getVssForStream(streamType);
+        if (vss == null) {
+            return;
+        }
+        vss.checkFixedVolumeDevices();
     }
 
     private void checkMuteAffectedStreams() {
         // any stream with a min level > 0 is not muteable by definition
         // STREAM_VOICE_CALL and STREAM_BLUETOOTH_SCO can be muted by applications
         // that has the the MODIFY_PHONE_STATE permission.
-        for (int i = 0; i < mStreamStates.length; i++) {
-            final VolumeStreamState vss = mStreamStates[i];
-            if (vss.mIndexMin > 0 &&
-                (vss.mStreamType != AudioSystem.STREAM_VOICE_CALL &&
-                vss.mStreamType != AudioSystem.STREAM_BLUETOOTH_SCO)) {
+        for (int i = 0; i < mStreamStates.size(); i++) {
+            final VolumeStreamState vss = mStreamStates.valueAt(i);
+            if (vss != null && vss.mIndexMin > 0
+                    && (vss.mStreamType != AudioSystem.STREAM_VOICE_CALL
+                    && vss.mStreamType != AudioSystem.STREAM_BLUETOOTH_SCO)) {
                 mMuteAffectedStreams &= ~(1 << vss.mStreamType);
             }
         }
@@ -2392,11 +2463,15 @@
 
     private void createStreamStates() {
         int numStreamTypes = AudioSystem.getNumStreamTypes();
-        VolumeStreamState[] streams = mStreamStates = new VolumeStreamState[numStreamTypes];
+        mStreamStates = new SparseArray<>(numStreamTypes);
 
         for (int i = 0; i < numStreamTypes; i++) {
-            streams[i] =
-                    new VolumeStreamState(System.VOLUME_SETTINGS_INT[mStreamVolumeAlias[i]], i);
+            final int streamAlias = sStreamVolumeAlias.get(i, /*valueIfKeyNotFound=*/-1);
+            // a negative sStreamVolumeAlias value means the stream state type is not supported
+            if (streamAlias >= 0) {
+                mStreamStates.set(i,
+                        new VolumeStreamState(System.VOLUME_SETTINGS_INT[streamAlias], i));
+            }
         }
 
         checkAllFixedVolumeDevices();
@@ -2414,24 +2489,25 @@
      * For other volume groups not linked to any streams, default music stream index is considered.
      */
     private void updateDefaultVolumes() {
-        for (int stream = 0; stream < mStreamStates.length; stream++) {
-            int streamVolumeAlias = mStreamVolumeAlias[stream];
+        for (int stream = 0; stream < mStreamStates.size(); stream++) {
+            int streamType = mStreamStates.keyAt(stream);
+            int streamVolumeAlias = sStreamVolumeAlias.get(streamType, /*valueIfKeyNotFound=*/-1);
             if (mUseVolumeGroupAliases) {
-                if (AudioSystem.DEFAULT_STREAM_VOLUME[stream] != UNSET_INDEX) {
+                if (AudioSystem.DEFAULT_STREAM_VOLUME[streamType] != UNSET_INDEX) {
                     // Already initialized through default property based mecanism.
                     continue;
                 }
                 streamVolumeAlias = AudioSystem.STREAM_MUSIC;
-                int defaultAliasVolume = getUiDefaultRescaledIndex(streamVolumeAlias, stream);
-                if ((defaultAliasVolume >= MIN_STREAM_VOLUME[stream])
-                        && (defaultAliasVolume <= MAX_STREAM_VOLUME[stream])) {
-                    AudioSystem.DEFAULT_STREAM_VOLUME[stream] = defaultAliasVolume;
+                int defaultAliasVolume = getUiDefaultRescaledIndex(streamVolumeAlias, streamType);
+                if ((defaultAliasVolume >= MIN_STREAM_VOLUME[streamType])
+                        && (defaultAliasVolume <= MAX_STREAM_VOLUME[streamType])) {
+                    AudioSystem.DEFAULT_STREAM_VOLUME[streamType] = defaultAliasVolume;
                     continue;
                 }
             }
-            if (stream != streamVolumeAlias) {
-                AudioSystem.DEFAULT_STREAM_VOLUME[stream] =
-                        getUiDefaultRescaledIndex(streamVolumeAlias, stream);
+            if (streamVolumeAlias >= 0 && streamType != streamVolumeAlias) {
+                AudioSystem.DEFAULT_STREAM_VOLUME[streamType] =
+                        getUiDefaultRescaledIndex(streamVolumeAlias, streamType);
             }
         }
     }
@@ -2441,18 +2517,49 @@
                 srcStream, dstStream) + 5) / 10;
     }
 
+    private static int replaceBtScoStreamWithVoiceCall(int streamType, String caller) {
+        if (replaceStreamBtSco() && streamType == AudioSystem.STREAM_BLUETOOTH_SCO) {
+            if (DEBUG_VOL) {
+                Log.d(TAG,
+                        "Deprecating STREAM_BLUETOOTH_SCO, using STREAM_VOICE_CALL instead for "
+                                + "caller: " + caller);
+            }
+            streamType = AudioSystem.STREAM_VOICE_CALL;
+        }
+        return streamType;
+    }
+
+    private boolean isStreamBluetoothSco(int streamType) {
+        if (replaceStreamBtSco()) {
+            if (streamType == AudioSystem.STREAM_BLUETOOTH_SCO) {
+                // this should not happen, throwing exception
+                throw new IllegalArgumentException("STREAM_BLUETOOTH_SCO is deprecated");
+            }
+            return streamType == AudioSystem.STREAM_VOICE_CALL && mScoDeviceActive.get();
+        } else {
+            return streamType == AudioSystem.STREAM_BLUETOOTH_SCO;
+        }
+    }
+
     private void dumpStreamStates(PrintWriter pw) {
         pw.println("\nStream volumes (device: index)");
         int numStreamTypes = AudioSystem.getNumStreamTypes();
         for (int i = 0; i < numStreamTypes; i++) {
+            if (replaceStreamBtSco() && i == AudioSystem.STREAM_BLUETOOTH_SCO) {
+                continue;
+            }
             StringBuilder alias = new StringBuilder();
-            if (mStreamVolumeAlias[i] != i) {
+            final int streamAlias = sStreamVolumeAlias.get(i, /*valueIfKeyNotFound*/-1);
+            if (streamAlias != i && streamAlias != -1) {
                 alias.append(" (aliased to: ")
-                        .append(AudioSystem.STREAM_NAMES[mStreamVolumeAlias[i]])
+                        .append(AudioSystem.STREAM_NAMES[streamAlias])
                         .append(")");
             }
             pw.println("- " + AudioSystem.STREAM_NAMES[i] + alias + ":");
-            mStreamStates[i].dump(pw);
+            final VolumeStreamState vss = getVssForStream(i);
+            if (vss != null) {
+                vss.dump(pw);
+            }
             pw.println("");
         }
         pw.print("\n- mute affected streams = 0x");
@@ -2461,6 +2568,13 @@
         pw.println(Integer.toHexString(mUserMutableStreams));
     }
 
+    private void initStreamVolumeAlias(int[] streamVolumeAlias) {
+        sStreamVolumeAlias = new SparseIntArray(streamVolumeAlias.length);
+        for (int i = 0; i < streamVolumeAlias.length; ++i) {
+            sStreamVolumeAlias.put(i, streamVolumeAlias[i]);
+        }
+    }
+
     private void updateStreamVolumeAlias(boolean updateVolumes, String caller) {
         int dtmfStreamAlias;
         final int a11yStreamAlias = sIndependentA11yVolume ?
@@ -2470,24 +2584,24 @@
                 AudioSystem.STREAM_ASSISTANT : AudioSystem.STREAM_MUSIC;
 
         if (mIsSingleVolume) {
-            mStreamVolumeAlias = STREAM_VOLUME_ALIAS_TELEVISION.clone();
+            initStreamVolumeAlias(STREAM_VOLUME_ALIAS_TELEVISION);
             dtmfStreamAlias = AudioSystem.STREAM_MUSIC;
         } else if (mUseVolumeGroupAliases) {
-            mStreamVolumeAlias = STREAM_VOLUME_ALIAS_NONE.clone();
+            initStreamVolumeAlias(STREAM_VOLUME_ALIAS_NONE);
             dtmfStreamAlias = AudioSystem.STREAM_DTMF;
         } else {
             switch (mPlatformType) {
                 case AudioSystem.PLATFORM_VOICE:
-                    mStreamVolumeAlias = STREAM_VOLUME_ALIAS_VOICE.clone();
+                    initStreamVolumeAlias(STREAM_VOLUME_ALIAS_VOICE);
                     dtmfStreamAlias = AudioSystem.STREAM_RING;
                     break;
                 default:
-                    mStreamVolumeAlias = STREAM_VOLUME_ALIAS_DEFAULT.clone();
+                    initStreamVolumeAlias(STREAM_VOLUME_ALIAS_DEFAULT);
                     dtmfStreamAlias = AudioSystem.STREAM_MUSIC;
             }
             if (!mNotifAliasRing) {
-                mStreamVolumeAlias[AudioSystem.STREAM_NOTIFICATION] =
-                        AudioSystem.STREAM_NOTIFICATION;
+                sStreamVolumeAlias.put(AudioSystem.STREAM_NOTIFICATION,
+                        AudioSystem.STREAM_NOTIFICATION);
             }
         }
 
@@ -2502,26 +2616,32 @@
             }
         }
 
-        mStreamVolumeAlias[AudioSystem.STREAM_DTMF] = dtmfStreamAlias;
-        mStreamVolumeAlias[AudioSystem.STREAM_ACCESSIBILITY] = a11yStreamAlias;
-        mStreamVolumeAlias[AudioSystem.STREAM_ASSISTANT] = assistantStreamAlias;
+        sStreamVolumeAlias.put(AudioSystem.STREAM_DTMF, dtmfStreamAlias);
+        sStreamVolumeAlias.put(AudioSystem.STREAM_ACCESSIBILITY, a11yStreamAlias);
+        sStreamVolumeAlias.put(AudioSystem.STREAM_ASSISTANT, assistantStreamAlias);
+
+        if (replaceStreamBtSco()) {
+            // we do not support STREAM_BLUETOOTH_SCO, this will lead to having
+            // mStreanStates.get(STREAM_BLUETOOTH_SCO) == null
+            sStreamVolumeAlias.delete(AudioSystem.STREAM_BLUETOOTH_SCO);
+        }
 
         if (updateVolumes && mStreamStates != null) {
             updateDefaultVolumes();
 
             synchronized (mSettingsLock) {
                 synchronized (VolumeStreamState.class) {
-                    mStreamStates[AudioSystem.STREAM_DTMF]
-                            .setAllIndexes(mStreamStates[dtmfStreamAlias], caller);
-                    mStreamStates[AudioSystem.STREAM_ACCESSIBILITY].setSettingName(
+                    getVssForStreamOrDefault(AudioSystem.STREAM_DTMF)
+                            .setAllIndexes(getVssForStreamOrDefault(dtmfStreamAlias), caller);
+                    getVssForStreamOrDefault(AudioSystem.STREAM_ACCESSIBILITY).setSettingName(
                             System.VOLUME_SETTINGS_INT[a11yStreamAlias]);
-                    mStreamStates[AudioSystem.STREAM_ACCESSIBILITY].setAllIndexes(
-                            mStreamStates[a11yStreamAlias], caller);
+                    getVssForStreamOrDefault(AudioSystem.STREAM_ACCESSIBILITY).setAllIndexes(
+                            getVssForStreamOrDefault(a11yStreamAlias), caller);
                 }
             }
             if (sIndependentA11yVolume) {
                 // restore the a11y values from the settings
-                mStreamStates[AudioSystem.STREAM_ACCESSIBILITY].readSettings();
+                getVssForStreamOrDefault(AudioSystem.STREAM_ACCESSIBILITY).readSettings();
             }
 
             // apply stream mute states according to new value of mRingerModeAffectedStreams
@@ -2531,13 +2651,13 @@
                     SENDMSG_QUEUE,
                     0,
                     0,
-                    mStreamStates[AudioSystem.STREAM_DTMF], 0);
+                    getVssForStreamOrDefault(AudioSystem.STREAM_DTMF), 0);
             sendMsg(mAudioHandler,
                     MSG_SET_ALL_VOLUMES,
                     SENDMSG_QUEUE,
                     0,
                     0,
-                    mStreamStates[AudioSystem.STREAM_ACCESSIBILITY], 0);
+                    getVssForStreamOrDefault(AudioSystem.STREAM_ACCESSIBILITY), 0);
         }
         dispatchStreamAliasingUpdate();
     }
@@ -3014,7 +3134,8 @@
     }
 
     private int getIndexRange(int streamType) {
-        return (mStreamStates[streamType].getMaxIndex() - mStreamStates[streamType].getMinIndex());
+        return (getVssForStreamOrDefault(streamType).getMaxIndex() - getVssForStreamOrDefault(
+                streamType).getMinIndex());
     }
 
     private int rescaleIndex(VolumeInfo volumeInfo, int dstStream) {
@@ -3022,11 +3143,12 @@
                 || volumeInfo.getMinVolumeIndex() == VolumeInfo.INDEX_NOT_SET
                 || volumeInfo.getMaxVolumeIndex() == VolumeInfo.INDEX_NOT_SET) {
             Log.e(TAG, "rescaleIndex: volumeInfo has invalid index or range");
-            return mStreamStates[dstStream].getMinIndex();
+            return getVssForStreamOrDefault(dstStream).getMinIndex();
         }
         return rescaleIndex(volumeInfo.getVolumeIndex(),
                 volumeInfo.getMinVolumeIndex(), volumeInfo.getMaxVolumeIndex(),
-                mStreamStates[dstStream].getMinIndex(), mStreamStates[dstStream].getMaxIndex());
+                getVssForStreamOrDefault(dstStream).getMinIndex(),
+                getVssForStreamOrDefault(dstStream).getMaxIndex());
     }
 
     private int rescaleIndex(int index, int srcStream, VolumeInfo dstVolumeInfo) {
@@ -3037,14 +3159,17 @@
             return index;
         }
         return rescaleIndex(index,
-                mStreamStates[srcStream].getMinIndex(), mStreamStates[srcStream].getMaxIndex(),
+                getVssForStreamOrDefault(srcStream).getMinIndex(),
+                getVssForStreamOrDefault(srcStream).getMaxIndex(),
                 dstMin, dstMax);
     }
 
     private int rescaleIndex(int index, int srcStream, int dstStream) {
         return rescaleIndex(index,
-                mStreamStates[srcStream].getMinIndex(), mStreamStates[srcStream].getMaxIndex(),
-                mStreamStates[dstStream].getMinIndex(), mStreamStates[dstStream].getMaxIndex());
+                getVssForStreamOrDefault(srcStream).getMinIndex(),
+                getVssForStreamOrDefault(srcStream).getMaxIndex(),
+                getVssForStreamOrDefault(dstStream).getMinIndex(),
+                getVssForStreamOrDefault(dstStream).getMaxIndex());
     }
 
     private int rescaleIndex(int index, int srcMin, int srcMax, int dstMin, int dstMax) {
@@ -3065,7 +3190,7 @@
             return 0;
         }
 
-        return ((step * dstRange + srcRange / 2) / srcRange);
+        return (step * dstRange + srcRange / 2) / srcRange;
     }
 
     ///////////////////////////////////////////////////////////////////////////
@@ -3539,7 +3664,7 @@
             return;
         }
 
-        final int streamType;
+        int streamType;
         synchronized (mForceControlStreamLock) {
             // Request lock in case mVolumeControlStream is changed by other thread.
             if (mUserSelectedVolumeControlStream) { // implies mVolumeControlStream != -1
@@ -3564,8 +3689,15 @@
 
         final boolean isMute = isMuteAdjust(direction);
 
+        streamType = replaceBtScoStreamWithVoiceCall(streamType, "adjustSuggestedStreamVolume");
+
         ensureValidStreamType(streamType);
-        final int resolvedStream = mStreamVolumeAlias[streamType];
+        final int resolvedStream = sStreamVolumeAlias.get(streamType, /*valueIfKeyNotFound=*/-1);
+        if (resolvedStream == -1) {
+            Log.e(TAG, "adjustSuggestedStreamVolume: no stream vol alias for stream type "
+                    + streamType);
+            return;
+        }
 
         // Play sounds on STREAM_RING only.
         if ((flags & AudioManager.FLAG_PLAY_SOUND) != 0 &&
@@ -3641,6 +3773,8 @@
         if (mUseFixedVolume) {
             return;
         }
+        streamType = replaceBtScoStreamWithVoiceCall(streamType, "adjustStreamVolume");
+
         if (DEBUG_VOL) Log.d(TAG, "adjustStreamVolume() stream=" + streamType + ", dir=" + direction
                 + ", flags=" + flags + ", caller=" + caller);
 
@@ -3657,8 +3791,9 @@
         // that the calling app have the MODIFY_PHONE_STATE permission.
         if (isMuteAdjust &&
             (streamType == AudioSystem.STREAM_VOICE_CALL ||
-                streamType == AudioSystem.STREAM_BLUETOOTH_SCO) &&
-                mContext.checkPermission(MODIFY_PHONE_STATE, pid, uid)
+                // TODO: when replaceStreamBtSco flag is rolled out remove next condition
+                isStreamBluetoothSco(streamType))
+                && mContext.checkPermission(MODIFY_PHONE_STATE, pid, uid)
                     != PackageManager.PERMISSION_GRANTED) {
             Log.w(TAG, "MODIFY_PHONE_STATE Permission Denial: adjustStreamVolume from pid="
                     + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid());
@@ -3679,9 +3814,13 @@
         // use stream type alias here so that streams with same alias have the same behavior,
         // including with regard to silent mode control (e.g the use of STREAM_RING below and in
         // checkForRingerModeChange() in place of STREAM_RING or STREAM_NOTIFICATION)
-        int streamTypeAlias = mStreamVolumeAlias[streamType];
+        int streamTypeAlias = sStreamVolumeAlias.get(streamType, /*valueIfKeyNotFound=*/-1);
+        if (streamTypeAlias == -1) {
+            Log.e(TAG,
+                    "adjustStreamVolume: no stream vol alias for stream type " + streamType);
+        }
 
-        VolumeStreamState streamState = mStreamStates[streamTypeAlias];
+        VolumeStreamState streamState = getVssForStreamOrDefault(streamTypeAlias);
 
         final int device = getDeviceForStream(streamTypeAlias);
 
@@ -3726,7 +3865,8 @@
             }
         } else {
             // convert one UI step (+/-1) into a number of internal units on the stream alias
-            step = rescaleStep(10, streamType, streamTypeAlias);
+            step = rescaleStep((int) (10 * streamState.getIndexStepFactor()), streamType,
+                    streamTypeAlias);
         }
 
         // If either the client forces allowing ringer modes for this adjustment,
@@ -3766,7 +3906,7 @@
         if (!volumeAdjustmentAllowedByDnd(streamTypeAlias, flags)) {
             adjustVolume = false;
         }
-        int oldIndex = mStreamStates[streamType].getIndex(device);
+        int oldIndex = getVssForStreamOrDefault(streamType).getIndex(device);
 
         // Check if the volume adjustment should be handled by an absolute volume controller instead
         if (isAbsoluteVolumeDevice(device) && (flags & AudioManager.FLAG_ABSOLUTE_VOLUME) == 0) {
@@ -3822,7 +3962,7 @@
                         0);
             }
 
-            int newIndex = mStreamStates[streamType].getIndex(device);
+            int newIndex = getVssForStreamOrDefault(streamType).getIndex(device);
 
             int streamToDriveAbsVol = absVolumeIndexFix() ? getBluetoothContextualVolumeStream() :
                     AudioSystem.STREAM_MUSIC;
@@ -3849,7 +3989,7 @@
                             + newIndex + " stream=" + streamType);
                 }
                 mDeviceBroker.postSetLeAudioVolumeIndex(newIndex,
-                    mStreamStates[streamType].getMaxIndex(), streamType);
+                        getVssForStreamOrDefault(streamType).getMaxIndex(), streamType);
             }
 
             // Check if volume update should be send to Hearing Aid.
@@ -3865,8 +4005,7 @@
             }
         }
 
-        final int newIndex = mStreamStates[streamType].getIndex(device);
-
+        final int newIndex = getVssForStreamOrDefault(streamType).getIndex(device);
         if (adjustVolume) {
             synchronized (mHdmiClientLock) {
                 if (mHdmiManager != null) {
@@ -3948,22 +4087,22 @@
         synchronized (mSettingsLock) {
             synchronized (VolumeStreamState.class) {
                 List<Integer> streamsToMute = new ArrayList<>();
-                for (int stream = 0; stream < mStreamStates.length; stream++) {
-                    VolumeStreamState vss = mStreamStates[stream];
-                    if (streamAlias == mStreamVolumeAlias[stream] && vss.isMutable()) {
-                        if (!(mCameraSoundForced
-                                && (vss.getStreamType()
+                for (int streamIdx = 0; streamIdx < mStreamStates.size(); streamIdx++) {
+                    final VolumeStreamState vss = mStreamStates.valueAt(streamIdx);
+                    if (vss != null && streamAlias == sStreamVolumeAlias.get(vss.getStreamType())
+                            && vss.isMutable()) {
+                        if (!(mCameraSoundForced && (vss.getStreamType()
                                 == AudioSystem.STREAM_SYSTEM_ENFORCED))) {
                             boolean changed = vss.mute(state, /* apply= */ false,
                                     "muteAliasStreams");
                             if (changed) {
-                                streamsToMute.add(stream);
+                                streamsToMute.add(vss.getStreamType());
                             }
                         }
                     }
                 }
                 streamsToMute.forEach(streamToMute -> {
-                    mStreamStates[streamToMute].doMute();
+                    getVssForStreamOrDefault(streamToMute).doMute();
                     broadcastMuteSetting(streamToMute, state);
                 });
             }
@@ -3973,8 +4112,14 @@
     private void broadcastMuteSetting(int streamType, boolean isMuted) {
         // Stream mute changed, fire the intent.
         Intent intent = new Intent(AudioManager.STREAM_MUTE_CHANGED_ACTION);
-        intent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, streamType);
         intent.putExtra(AudioManager.EXTRA_STREAM_VOLUME_MUTED, isMuted);
+        if (replaceStreamBtSco() && isStreamBluetoothSco(streamType)) {
+            intent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE,
+                    AudioSystem.STREAM_BLUETOOTH_SCO);
+            // in this case broadcast for both sco and voice_call streams the mute status
+            sendBroadcastToAll(intent, null /* options */);
+        }
+        intent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, streamType);
         sendBroadcastToAll(intent, null /* options */);
     }
 
@@ -3985,7 +4130,7 @@
         // vss.updateVolumeGroupIndex
         synchronized (mSettingsLock) {
             synchronized (VolumeStreamState.class) {
-                final VolumeStreamState streamState = mStreamStates[streamAlias];
+                final VolumeStreamState streamState = getVssForStreamOrDefault(streamAlias);
                 // if unmuting causes a change, it was muted
                 wasMuted = streamState.mute(false, "onUnmuteStreamOnSingleVolDevice");
                 if (wasMuted) {
@@ -4083,7 +4228,11 @@
      */
     /*package*/ void onSetStreamVolume(int streamType, int index, int flags, int device,
             String caller, boolean hasModifyAudioSettings, boolean canChangeMute) {
-        final int stream = mStreamVolumeAlias[streamType];
+        final int stream = sStreamVolumeAlias.get(streamType, /*valueIfKeyNotFound=*/-1);
+        if (stream == -1) {
+            Log.e(TAG, "onSetStreamVolume: no stream vol alias for stream type " + stream);
+            return;
+        }
         // setting volume on ui sounds stream type also controls silent mode
         if (((flags & AudioManager.FLAG_ALLOW_RINGER_MODES) != 0) ||
                 (stream == getUiSoundsStreamType())) {
@@ -4093,7 +4242,7 @@
         setStreamVolumeInt(stream, index, device, false, caller, hasModifyAudioSettings);
         // setting non-zero volume for a muted stream unmutes the stream and vice versa
         // except for BT SCO stream where only explicit mute is allowed to comply to BT requirements
-        if ((streamType != AudioSystem.STREAM_BLUETOOTH_SCO) && canChangeMute) {
+        if (!isStreamBluetoothSco(streamType) && canChangeMute) {
             // As adjustStreamVolume with muteAdjust flags mute/unmutes stream and aliased streams.
             muteAliasStreams(stream, index == 0);
         }
@@ -4158,7 +4307,8 @@
         super.getVolumeGroupVolumeIndex_enforcePermission();
         synchronized (VolumeStreamState.class) {
             if (sVolumeGroupStates.indexOfKey(groupId) < 0) {
-                throw new IllegalArgumentException("No volume group for id " + groupId);
+                Log.e(TAG, "No volume group for id " + groupId);
+                return 0;
             }
             VolumeGroupState vgs = sVolumeGroupStates.get(groupId);
             // Return 0 when muted, not min index since for e.g. Voice Call, it has a non zero
@@ -4174,7 +4324,8 @@
         super.getVolumeGroupMaxVolumeIndex_enforcePermission();
         synchronized (VolumeStreamState.class) {
             if (sVolumeGroupStates.indexOfKey(groupId) < 0) {
-                throw new IllegalArgumentException("No volume group for id " + groupId);
+                Log.e(TAG, "No volume group for id " + groupId);
+                return 0;
             }
             VolumeGroupState vgs = sVolumeGroupStates.get(groupId);
             return vgs.getMaxIndex();
@@ -4188,7 +4339,8 @@
         super.getVolumeGroupMinVolumeIndex_enforcePermission();
         synchronized (VolumeStreamState.class) {
             if (sVolumeGroupStates.indexOfKey(groupId) < 0) {
-                throw new IllegalArgumentException("No volume group for id " + groupId);
+                Log.e(TAG, "No volume group for id " + groupId);
+                return 0;
             }
             VolumeGroupState vgs = sVolumeGroupStates.get(groupId);
             return vgs.getMinIndex();
@@ -4224,26 +4376,30 @@
         // that can interfere with the sending of the VOLUME_CHANGED_ACTION intent
         mAudioSystem.clearRoutingCache();
 
+        int streamType = replaceBtScoStreamWithVoiceCall(vi.getStreamType(), "setDeviceVolume");
+
+        final VolumeStreamState vss = getVssForStream(streamType);
+
         // log the current device that will be used when evaluating the sending of the
         // VOLUME_CHANGED_ACTION intent to see if the current device is the one being modified
-        final int currDev = getDeviceForStream(vi.getStreamType());
+        final int currDev = getDeviceForStream(streamType);
 
-        final boolean skipping = (currDev == ada.getInternalType());
+        final boolean skipping = (currDev == ada.getInternalType()) || (vss == null);
 
-        AudioService.sVolumeLogger.enqueue(new DeviceVolumeEvent(vi.getStreamType(), index, ada,
+        AudioService.sVolumeLogger.enqueue(new DeviceVolumeEvent(streamType, index, ada,
                 currDev, callingPackage, skipping));
 
         if (skipping) {
-            // setDeviceVolume was called on a device currently being used
+            // setDeviceVolume was called on a device currently being used or stream state is null
             return;
         }
 
         // TODO handle unmuting of current audio device
         // if a stream is not muted but the VolumeInfo is for muting, set the volume index
         // for the device to min volume
-        if (vi.hasMuteCommand() && vi.isMuted() && !isStreamMute(vi.getStreamType())) {
-            setStreamVolumeWithAttributionInt(vi.getStreamType(),
-                    mStreamStates[vi.getStreamType()].getMinIndex(),
+        if (vi.hasMuteCommand() && vi.isMuted() && !isStreamMute(streamType)) {
+            setStreamVolumeWithAttributionInt(streamType,
+                    vss.getMinIndex(),
                     /*flags*/ 0,
                     ada, callingPackage, null,
                     //TODO handle unmuting of current audio device
@@ -4257,22 +4413,22 @@
         if (vi.getMinVolumeIndex() == VolumeInfo.INDEX_NOT_SET
                 || vi.getMaxVolumeIndex() == VolumeInfo.INDEX_NOT_SET) {
             // assume index meant to be in stream type range, validate
-            if ((index * 10) < mStreamStates[vi.getStreamType()].getMinIndex()
-                    || (index * 10) > mStreamStates[vi.getStreamType()].getMaxIndex()) {
+            if ((index * 10) < vss.getMinIndex()
+                    || (index * 10) > vss.getMaxIndex()) {
                 throw new IllegalArgumentException("invalid volume index " + index
                         + " not between min/max for stream " + vi.getStreamType());
             }
         } else {
             // check if index needs to be rescaled
-            final int min = (mStreamStates[vi.getStreamType()].getMinIndex() + 5) / 10;
-            final int max = (mStreamStates[vi.getStreamType()].getMaxIndex() + 5) / 10;
+            final int min = (vss.getMinIndex() + 5) / 10;
+            final int max = (vss.getMaxIndex() + 5) / 10;
             if (vi.getMinVolumeIndex() != min || vi.getMaxVolumeIndex() != max) {
                 index = rescaleIndex(index,
                         /*srcMin*/ vi.getMinVolumeIndex(), /*srcMax*/ vi.getMaxVolumeIndex(),
                         /*dstMin*/ min, /*dstMax*/ max);
             }
         }
-        setStreamVolumeWithAttributionInt(vi.getStreamType(), index, /*flags*/ 0,
+        setStreamVolumeWithAttributionInt(streamType, index, /*flags*/ 0,
                 ada, callingPackage, null,
                 false /*canChangeMuteAndUpdateController*/);
     }
@@ -4382,6 +4538,9 @@
             @Nullable AudioDeviceAttributes ada,
             String callingPackage, String attributionTag,
             boolean canChangeMuteAndUpdateController) {
+        streamType = replaceBtScoStreamWithVoiceCall(streamType,
+                "setStreamVolumeWithAttributionInt");
+
         if ((streamType == AudioManager.STREAM_ACCESSIBILITY) && !canChangeAccessibilityVolume()) {
             Log.w(TAG, "Trying to call setStreamVolume() for a11y without"
                     + " CHANGE_ACCESSIBILITY_VOLUME  callingPackage=" + callingPackage);
@@ -4389,7 +4548,7 @@
         }
         if ((streamType == AudioManager.STREAM_VOICE_CALL) && (index == 0)
                 && (mContext.checkCallingOrSelfPermission(MODIFY_PHONE_STATE)
-                    != PackageManager.PERMISSION_GRANTED)) {
+                    != PackageManager.PERMISSION_GRANTED) && !isStreamBluetoothSco(streamType)) {
             Log.w(TAG, "Trying to call setStreamVolume() for STREAM_VOICE_CALL and index 0 without"
                     + " MODIFY_PHONE_STATE  callingPackage=" + callingPackage);
             return;
@@ -4610,6 +4769,8 @@
     private void dumpFlags(PrintWriter pw) {
 
         pw.println("\nFun with Flags:");
+        pw.println("\tcom.android.media.audio.as_device_connection_failure:"
+                + asDeviceConnectionFailure());
         pw.println("\tandroid.media.audio.autoPublicVolumeApiHardening:"
                 + autoPublicVolumeApiHardening());
         pw.println("\tandroid.media.audio.Flags.automaticBtDeviceType:"
@@ -4632,6 +4793,8 @@
                 + vgsVssSyncMuteOrder());
         pw.println("\tcom.android.media.audio.absVolumeIndexFix:"
                 + absVolumeIndexFix());
+        pw.println("\tcom.android.media.audio.replaceStreamBtSco:"
+                + replaceStreamBtSco());
     }
 
     private void dumpAudioMode(PrintWriter pw) {
@@ -4695,7 +4858,7 @@
 
         if (AudioSystem.isLeAudioDeviceType(device)) {
             mDeviceBroker.postSetLeAudioVolumeIndex(index * 10,
-                    mStreamStates[streamType].getMaxIndex(), streamType);
+                    getVssForStreamOrDefault(streamType).getMaxIndex(), streamType);
         } else if (device == AudioSystem.DEVICE_OUT_HEARING_AID) {
             mDeviceBroker.postSetHearingAidVolumeIndex(index * 10, streamType);
         } else {
@@ -4720,12 +4883,17 @@
         if (mUseFixedVolume) {
             return;
         }
+        streamType = replaceBtScoStreamWithVoiceCall(streamType, "setStreamVolume");
 
         ensureValidStreamType(streamType);
-        int streamTypeAlias = mStreamVolumeAlias[streamType];
-        VolumeStreamState streamState = mStreamStates[streamTypeAlias];
+        int streamTypeAlias = sStreamVolumeAlias.get(streamType, /*valueIfKeyNotFound*/-1);
+        if (streamTypeAlias == -1) {
+            Log.e(TAG, "setStreamVolume: no stream vol alias for stream type " + streamType);
+            return;
+        }
+        final VolumeStreamState streamState = getVssForStreamOrDefault(streamTypeAlias);
 
-        if ((streamType == AudioManager.STREAM_VOICE_CALL)
+        if (!replaceStreamBtSco() && (streamType == AudioManager.STREAM_VOICE_CALL)
                 && isInCommunication() && mDeviceBroker.isBluetoothScoActive()) {
             Log.i(TAG, "setStreamVolume for STREAM_VOICE_CALL, switching to STREAM_BLUETOOTH_SCO");
             streamType = AudioManager.STREAM_BLUETOOTH_SCO;
@@ -4789,7 +4957,7 @@
                         // ada is non-null when called from setDeviceVolume,
                         // which shouldn't update the mute state
                         canChangeMuteAndUpdateController /*canChangeMute*/);
-                index = mStreamStates[streamType].getIndex(device);
+                index = getVssForStreamOrDefault(streamType).getIndex(device);
             }
         }
 
@@ -4817,8 +4985,8 @@
                 Log.d(TAG, "setStreamVolume postSetLeAudioVolumeIndex index="
                         + index + " stream=" + streamType);
             }
-            mDeviceBroker.postSetLeAudioVolumeIndex(index, mStreamStates[streamType].getMaxIndex(),
-                    streamType);
+            mDeviceBroker.postSetLeAudioVolumeIndex(index,
+                    getVssForStreamOrDefault(streamType).getMaxIndex(), streamType);
         }
 
         if (device == AudioSystem.DEVICE_OUT_HEARING_AID
@@ -4848,7 +5016,7 @@
                         // ada is non-null when called from setDeviceVolume,
                         // which shouldn't update the mute state
                         canChangeMuteAndUpdateController /*canChangeMute*/);
-                index = mStreamStates[streamType].getIndex(device);
+                index = getVssForStreamOrDefault(streamType).getIndex(device);
             }
         }
 
@@ -4919,6 +5087,9 @@
                 != PackageManager.PERMISSION_GRANTED) {
             return;
         }
+
+        streamType = replaceBtScoStreamWithVoiceCall(streamType, "forceVolumeControlStream");
+
         if (DEBUG_VOL) { Log.d(TAG, String.format("forceVolumeControlStream(%d)", streamType)); }
         synchronized(mForceControlStreamLock) {
             if (mVolumeControlStream != -1 && streamType != -1) {
@@ -5028,7 +5199,7 @@
     // UI update and Broadcast Intent
     protected void sendVolumeUpdate(int streamType, int oldIndex, int index, int flags, int device)
     {
-        streamType = mStreamVolumeAlias[streamType];
+        streamType = sStreamVolumeAlias.get(streamType, /*valueIfKeyNotFound=*/-1);
 
         if (streamType == AudioSystem.STREAM_MUSIC && isFullVolumeDevice(device)) {
             flags &= ~AudioManager.FLAG_SHOW_UI;
@@ -5076,7 +5247,7 @@
         if (isFullVolumeDevice(device)) {
             return;
         }
-        VolumeStreamState streamState = mStreamStates[streamType];
+        final VolumeStreamState streamState = getVssForStreamOrDefault(streamType);
 
         if (streamState.setIndex(index, device, caller, hasModifyAudioSettings) || force) {
             // Post message to set system volume (it in turn will post a message
@@ -5096,9 +5267,11 @@
         if (streamType == AudioManager.USE_DEFAULT_STREAM_TYPE) {
             streamType = getActiveStreamType(streamType);
         }
+        streamType = replaceBtScoStreamWithVoiceCall(streamType, "isStreamMute");
+
         synchronized (VolumeStreamState.class) {
             ensureValidStreamType(streamType);
-            return mStreamStates[streamType].mIsMuted;
+            return getVssForStreamOrDefault(streamType).mIsMuted;
         }
     }
 
@@ -5197,7 +5370,7 @@
             if (applyRequired) {
                 // Assumes only STREAM_MUSIC going through DEVICE_OUT_REMOTE_SUBMIX
                 checkAllFixedVolumeDevices(AudioSystem.STREAM_MUSIC);
-                mStreamStates[AudioSystem.STREAM_MUSIC].applyAllVolumes();
+                getVssForStreamOrDefault(AudioSystem.STREAM_MUSIC).applyAllVolumes();
             }
         }
     }
@@ -5269,6 +5442,8 @@
 
     /** @see AudioManager#getStreamVolume(int) */
     public int getStreamVolume(int streamType) {
+        streamType = replaceBtScoStreamWithVoiceCall(streamType, "getStreamVolume");
+
         ensureValidStreamType(streamType);
         int device = getDeviceForStream(streamType);
         return getStreamVolume(streamType, device);
@@ -5276,15 +5451,16 @@
 
     private int getStreamVolume(int streamType, int device) {
         synchronized (VolumeStreamState.class) {
-            int index = mStreamStates[streamType].getIndex(device);
+            final VolumeStreamState vss = getVssForStreamOrDefault(streamType);
+            int index = vss.getIndex(device);
 
             // by convention getStreamVolume() returns 0 when a stream is muted.
-            if (mStreamStates[streamType].mIsMuted) {
+            if (vss.mIsMuted) {
                 index = 0;
             }
-            if (index != 0 && (mStreamVolumeAlias[streamType] == AudioSystem.STREAM_MUSIC) &&
-                    isFixedVolumeDevice(device)) {
-                index = mStreamStates[streamType].getMaxIndex();
+            if (index != 0 && (sStreamVolumeAlias.get(streamType) == AudioSystem.STREAM_MUSIC)
+                    && isFixedVolumeDevice(device)) {
+                index = vss.getMaxIndex();
             }
             return (index + 5) / 10;
         }
@@ -5307,20 +5483,27 @@
             return getDefaultVolumeInfo();
         }
 
-        int streamType = vi.getStreamType();
+        int streamType = replaceBtScoStreamWithVoiceCall(vi.getStreamType(), "getStreamMaxVolume");
         final VolumeInfo.Builder vib = new VolumeInfo.Builder(vi);
-        vib.setMinVolumeIndex((mStreamStates[streamType].mIndexMin + 5) / 10);
-        vib.setMaxVolumeIndex((mStreamStates[streamType].mIndexMax + 5) / 10);
+        final VolumeStreamState vss = getVssForStream(streamType);
+        if (vss == null) {
+            Log.w(TAG,
+                    "getDeviceVolume unsupported stream type " + streamType + ". Return default");
+            return getDefaultVolumeInfo();
+        }
+
+        vib.setMinVolumeIndex((vss.mIndexMin + 5) / 10);
+        vib.setMaxVolumeIndex((vss.mIndexMax + 5) / 10);
         synchronized (VolumeStreamState.class) {
             final int index;
             if (isFixedVolumeDevice(ada.getInternalType())) {
-                index = (mStreamStates[streamType].mIndexMax + 5) / 10;
+                index = (vss.mIndexMax + 5) / 10;
             } else {
-                index = (mStreamStates[streamType].getIndex(ada.getInternalType()) + 5) / 10;
+                index = (vss.getIndex(ada.getInternalType()) + 5) / 10;
             }
             vib.setVolumeIndex(index);
             // only set as a mute command if stream muted
-            if (mStreamStates[streamType].mIsMuted) {
+            if (vss.mIsMuted) {
                 vib.setMuted(true);
             }
             return vib.build();
@@ -5329,20 +5512,22 @@
 
     /** @see AudioManager#getStreamMaxVolume(int) */
     public int getStreamMaxVolume(int streamType) {
+        streamType = replaceBtScoStreamWithVoiceCall(streamType, "getStreamMaxVolume");
         ensureValidStreamType(streamType);
-        return (mStreamStates[streamType].getMaxIndex() + 5) / 10;
+        return (getVssForStreamOrDefault(streamType).getMaxIndex() + 5) / 10;
     }
 
     /** @see AudioManager#getStreamMinVolumeInt(int)
      * Part of service interface, check permissions here */
     public int getStreamMinVolume(int streamType) {
+        streamType = replaceBtScoStreamWithVoiceCall(streamType, "getStreamMinVolume");
         ensureValidStreamType(streamType);
         final boolean isPrivileged =
                 Binder.getCallingUid() == Process.SYSTEM_UID
                  || callingHasAudioSettingsPermission()
                  || (mContext.checkCallingPermission(MODIFY_AUDIO_ROUTING)
                         == PackageManager.PERMISSION_GRANTED);
-        return (mStreamStates[streamType].getMinIndex(isPrivileged) + 5) / 10;
+        return (getVssForStreamOrDefault(streamType).getMinIndex(isPrivileged) + 5) / 10;
     }
 
     @android.annotation.EnforcePermission(QUERY_AUDIO_STATE)
@@ -5350,9 +5535,12 @@
     public int getLastAudibleStreamVolume(int streamType) {
         super.getLastAudibleStreamVolume_enforcePermission();
 
+        streamType = replaceBtScoStreamWithVoiceCall(streamType, "getLastAudibleStreamVolume");
+
         ensureValidStreamType(streamType);
+
         int device = getDeviceForStream(streamType);
-        return (mStreamStates[streamType].getIndex(device) + 5) / 10;
+        return (getVssForStreamOrDefault(streamType).getIndex(device) + 5) / 10;
     }
 
     /**
@@ -5421,10 +5609,11 @@
             return new ArrayList<>(Arrays.stream(AudioManager.getPublicStreamTypes())
                     .boxed().toList());
         }
-        ArrayList<Integer> res = new ArrayList(1);
-        for (int stream : mStreamVolumeAlias) {
-            if (!res.contains(stream)) {
-                res.add(stream);
+        ArrayList<Integer> res = new ArrayList<>(1);
+        for (int streamIdx = 0; streamIdx < sStreamVolumeAlias.size(); ++streamIdx) {
+            final int streamAlias = sStreamVolumeAlias.valueAt(streamIdx);
+            if (!res.contains(streamAlias)) {
+                res.add(streamAlias);
             }
         }
         return res;
@@ -5439,10 +5628,13 @@
     public @AudioManager.PublicStreamTypes
     int getStreamTypeAlias(@AudioManager.PublicStreamTypes int sourceStreamType) {
         super.getStreamTypeAlias_enforcePermission();
+
+        sourceStreamType = replaceBtScoStreamWithVoiceCall(sourceStreamType, "getStreamTypeAlias");
+
         // verify parameters
         ensureValidStreamType(sourceStreamType);
 
-        return mStreamVolumeAlias[sourceStreamType];
+        return sStreamVolumeAlias.get(sourceStreamType, /*valueIfKeyNotFound=*/-1);
     }
 
     /**
@@ -5464,7 +5656,7 @@
      */
     public int getUiSoundsStreamType() {
         return mUseVolumeGroupAliases ? STREAM_VOLUME_ALIAS_VOICE[AudioSystem.STREAM_SYSTEM]
-                : mStreamVolumeAlias[AudioSystem.STREAM_SYSTEM];
+                : sStreamVolumeAlias.get(AudioSystem.STREAM_SYSTEM);
     }
 
     /**
@@ -5476,7 +5668,7 @@
         return mUseVolumeGroupAliases
                 ? STREAM_VOLUME_ALIAS_VOICE[aliasStreamType]
                         == STREAM_VOLUME_ALIAS_VOICE[AudioSystem.STREAM_SYSTEM]
-                : aliasStreamType == mStreamVolumeAlias[AudioSystem.STREAM_SYSTEM];
+                : aliasStreamType == sStreamVolumeAlias.get(AudioSystem.STREAM_SYSTEM);
     }
 
     /** @see AudioManager#setMicrophoneMute(boolean) */
@@ -5770,6 +5962,10 @@
                 forceUse, eventSource, 0);
 
         for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
+            final VolumeStreamState vss = getVssForStream(streamType);
+            if (vss == null) {
+                continue;
+            }
             final boolean isMuted = isStreamMutedByRingerOrZenMode(streamType);
             final boolean muteAllowedBySco =
                     !((shouldRingSco || shouldRingBle) && streamType == AudioSystem.STREAM_RING);
@@ -5780,10 +5976,9 @@
             if (!shouldMute) {
                 // unmute
                 // ring and notifications volume should never be 0 when not silenced
-                if (mStreamVolumeAlias[streamType] == AudioSystem.STREAM_RING
-                        || mStreamVolumeAlias[streamType] == AudioSystem.STREAM_NOTIFICATION) {
+                if (sStreamVolumeAlias.get(streamType) == AudioSystem.STREAM_RING
+                        || sStreamVolumeAlias.get(streamType) == AudioSystem.STREAM_NOTIFICATION) {
                     synchronized (VolumeStreamState.class) {
-                        final VolumeStreamState vss = mStreamStates[streamType];
                         for (int i = 0; i < vss.mIndexMap.size(); i++) {
                             int device = vss.mIndexMap.keyAt(i);
                             int value = vss.mIndexMap.valueAt(i);
@@ -5798,20 +5993,20 @@
                               SENDMSG_QUEUE,
                               device,
                               0,
-                              mStreamStates[streamType],
+                              vss,
                               PERSIST_DELAY);
                     }
                 }
                 sRingerAndZenModeMutedStreams &= ~(1 << streamType);
                 sMuteLogger.enqueue(new AudioServiceEvents.RingerZenMutedStreamsEvent(
                         sRingerAndZenModeMutedStreams, "muteRingerModeStreams"));
-                mStreamStates[streamType].mute(false, "muteRingerModeStreams");
+                vss.mute(false, "muteRingerModeStreams");
             } else {
                 // mute
                 sRingerAndZenModeMutedStreams |= (1 << streamType);
                 sMuteLogger.enqueue(new AudioServiceEvents.RingerZenMutedStreamsEvent(
                         sRingerAndZenModeMutedStreams, "muteRingerModeStreams"));
-                mStreamStates[streamType].mute(true, "muteRingerModeStreams");
+                vss.mute(true, "muteRingerModeStreams");
             }
         }
     }
@@ -6234,15 +6429,19 @@
 
                 final int streamType = getActiveStreamType(AudioManager.USE_DEFAULT_STREAM_TYPE);
                 final int device = getDeviceForStream(streamType);
-                final int streamAlias = mStreamVolumeAlias[streamType];
+                final int streamAlias = sStreamVolumeAlias.get(streamType, /*valueIfKeyNotFound=*/
+                        -1);
+                if (streamAlias == -1) {
+                    Log.e(TAG,
+                            "onUpdateAudioMode: no stream vol alias for stream type " + streamType);
+                }
 
                 if (DEBUG_MODE) {
                     Log.v(TAG, "onUpdateAudioMode: streamType=" + streamType
                             + ", streamAlias=" + streamAlias);
                 }
 
-                final int index = mStreamStates[streamAlias].getIndex(device);
-                final int maxIndex = mStreamStates[streamAlias].getMaxIndex();
+                final int index = getVssForStreamOrDefault(streamAlias).getIndex(device);
                 setStreamVolumeInt(streamAlias, index, device, true,
                         requesterPackage, true /*hasModifyAudioSettings*/);
 
@@ -6547,9 +6746,13 @@
         // restore volume settings
         int numStreamTypes = AudioSystem.getNumStreamTypes();
         for (int streamType = 0; streamType < numStreamTypes; streamType++) {
-            VolumeStreamState streamState = mStreamStates[streamType];
+            final VolumeStreamState streamState = getVssForStream(streamType);
 
-            if (userSwitch && mStreamVolumeAlias[streamType] == AudioSystem.STREAM_MUSIC) {
+            if (streamState == null) {
+                continue;
+            }
+
+            if (userSwitch && sStreamVolumeAlias.get(streamType) == AudioSystem.STREAM_MUSIC) {
                 continue;
             }
 
@@ -7024,14 +7227,17 @@
 
     @Override
     public boolean isStreamAffectedByRingerMode(int streamType) {
+        streamType = replaceBtScoStreamWithVoiceCall(streamType, "isStreamAffectedByRingerMode");
         return (mRingerModeAffectedStreams & (1 << streamType)) != 0;
     }
 
     public boolean isStreamAffectedByCurrentZen(int streamType) {
+        streamType = replaceBtScoStreamWithVoiceCall(streamType, "isStreamAffectedByCurrentZen");
         return (mZenModeAffectedStreams & (1 << streamType)) != 0;
     }
 
     private boolean isStreamMutedByRingerOrZenMode(int streamType) {
+        streamType = replaceBtScoStreamWithVoiceCall(streamType, "isStreamMutedByRingerOrZenMode");
         return (sRingerAndZenModeMutedStreams & (1 << streamType)) != 0;
     }
 
@@ -7114,7 +7320,7 @@
         } else {
             ringerModeAffectedStreams |= (1 << AudioSystem.STREAM_SYSTEM_ENFORCED);
         }
-        if (mStreamVolumeAlias[AudioSystem.STREAM_DTMF] == AudioSystem.STREAM_RING) {
+        if (sStreamVolumeAlias.get(AudioSystem.STREAM_DTMF) == AudioSystem.STREAM_RING) {
             ringerModeAffectedStreams |= (1 << AudioSystem.STREAM_DTMF);
         } else {
             ringerModeAffectedStreams &= ~(1 << AudioSystem.STREAM_DTMF);
@@ -7146,6 +7352,7 @@
 
     @Override
     public boolean isStreamAffectedByMute(int streamType) {
+        streamType = replaceBtScoStreamWithVoiceCall(streamType, "isStreamAffectedByMute");
         return (mMuteAffectedStreams & (1 << streamType)) != 0;
     }
 
@@ -7169,7 +7376,7 @@
     }
 
     private void ensureValidStreamType(int streamType) {
-        if (streamType < 0 || streamType >= mStreamStates.length) {
+        if (streamType < 0 || streamType >= AudioSystem.getNumStreamTypes()) {
             throw new IllegalArgumentException("Bad stream type " + streamType);
         }
     }
@@ -7221,11 +7428,15 @@
         case AudioSystem.PLATFORM_VOICE:
             if (isInCommunication()
                     || mAudioSystem.isStreamActive(AudioManager.STREAM_VOICE_CALL, 0)) {
-                if (mDeviceBroker.isBluetoothScoActive()) {
-                    // Log.v(TAG, "getActiveStreamType: Forcing STREAM_BLUETOOTH_SCO...");
+                if (!replaceStreamBtSco() && mDeviceBroker.isBluetoothScoActive()) {
+                    if (DEBUG_VOL) {
+                        Log.v(TAG, "getActiveStreamType: Forcing STREAM_BLUETOOTH_SCO...");
+                    }
                     return AudioSystem.STREAM_BLUETOOTH_SCO;
                 } else {
-                    // Log.v(TAG, "getActiveStreamType: Forcing STREAM_VOICE_CALL...");
+                    if (DEBUG_VOL) {
+                        Log.v(TAG, "getActiveStreamType: Forcing STREAM_VOICE_CALL...");
+                    }
                     return AudioSystem.STREAM_VOICE_CALL;
                 }
             } else if (suggestedStreamType == AudioManager.USE_DEFAULT_STREAM_TYPE) {
@@ -7261,7 +7472,7 @@
             }
         default:
             if (isInCommunication()) {
-                if (mDeviceBroker.isBluetoothScoActive()) {
+                if (!replaceStreamBtSco() && mDeviceBroker.isBluetoothScoActive()) {
                     if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Forcing STREAM_BLUETOOTH_SCO");
                     return AudioSystem.STREAM_BLUETOOTH_SCO;
                 } else {
@@ -7295,6 +7506,10 @@
             }
             break;
         }
+
+        suggestedStreamType = replaceBtScoStreamWithVoiceCall(suggestedStreamType,
+                "getActiveStreamType");
+
         if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Returning suggested type "
                 + suggestedStreamType);
         return suggestedStreamType;
@@ -7426,9 +7641,10 @@
                 ? MIN_STREAM_VOLUME[AudioSystem.STREAM_ALARM]
                 : Math.min(idx + 1, MAX_STREAM_VOLUME[AudioSystem.STREAM_ALARM]);
         // update the VolumeStreamState for STREAM_ALARM and its aliases
-        for (int stream : mStreamVolumeAlias) {
-            if (mStreamVolumeAlias[stream] == AudioSystem.STREAM_ALARM) {
-                mStreamStates[stream].updateNoPermMinIndex(safeIndex);
+        for (int streamIdx = 0; streamIdx < sStreamVolumeAlias.size(); ++streamIdx) {
+            final int streamAlias = sStreamVolumeAlias.valueAt(streamIdx);
+            if (streamAlias == AudioSystem.STREAM_ALARM) {
+                getVssForStreamOrDefault(streamAlias).updateNoPermMinIndex(safeIndex);
             }
         }
     }
@@ -7442,6 +7658,7 @@
      */
     @VisibleForTesting
     public int getDeviceForStream(int stream) {
+        stream = replaceBtScoStreamWithVoiceCall(stream, "getDeviceForStream");
         return selectOneAudioDevice(getDeviceSetForStream(stream));
     }
 
@@ -7503,6 +7720,8 @@
     @Override
     @Deprecated
     public int getDeviceMaskForStream(int streamType) {
+        streamType = replaceBtScoStreamWithVoiceCall(streamType, "getDeviceMaskForStream");
+
         ensureValidStreamType(streamType);
         // no permission required
         final long token = Binder.clearCallingIdentity();
@@ -7537,23 +7756,24 @@
      */
     @NonNull
     public Set<Integer> getDeviceSetForStream(int stream) {
+        stream = replaceBtScoStreamWithVoiceCall(stream, "getDeviceSetForStream");
         ensureValidStreamType(stream);
         synchronized (VolumeStreamState.class) {
-            return mStreamStates[stream].observeDevicesForStream_syncVSS(true);
+            return getVssForStreamOrDefault(stream).observeDevicesForStream_syncVSS(true);
         }
     }
 
     private void onObserveDevicesForAllStreams(int skipStream) {
         synchronized (mSettingsLock) {
             synchronized (VolumeStreamState.class) {
-                for (int stream = 0; stream < mStreamStates.length; stream++) {
-                    if (stream != skipStream) {
+                for (int stream = 0; stream < mStreamStates.size(); stream++) {
+                    final VolumeStreamState vss = mStreamStates.valueAt(stream);
+                    if (vss != null && vss.getStreamType() != skipStream) {
                         Set<Integer> deviceSet =
-                                mStreamStates[stream].observeDevicesForStream_syncVSS(
-                                        false /*checkOthers*/);
+                                vss.observeDevicesForStream_syncVSS(false /*checkOthers*/);
                         for (Integer device : deviceSet) {
                             // Update volume states for devices routed for the stream
-                            updateVolumeStates(device, stream,
+                            updateVolumeStates(device, vss.getStreamType(),
                                     "AudioService#onObserveDevicesForAllStreams");
                         }
                     }
@@ -7577,6 +7797,19 @@
                 0 /*delay*/);
     }
 
+    /*package*/ void postScoDeviceActive(boolean scoDeviceActive) {
+        sendMsg(mAudioHandler,
+                MSG_SCO_DEVICE_ACTIVE_UPDATE,
+                SENDMSG_QUEUE, scoDeviceActive ? 1 : 0 /*arg1*/, 0 /*arg2*/, null /*obj*/,
+                0 /*delay*/);
+    }
+
+    private void onUpdateScoDeviceActive(boolean scoDeviceActive) {
+        if (mScoDeviceActive.compareAndSet(!scoDeviceActive, scoDeviceActive)) {
+            getVssForStreamOrDefault(AudioSystem.STREAM_VOICE_CALL).updateIndexFactors();
+        }
+    }
+
     /**
      * @see AudioDeviceVolumeManager#setDeviceAbsoluteMultiVolumeBehavior
      *
@@ -7968,7 +8201,7 @@
     /** only public for mocking/spying, do not call outside of AudioService */
     @VisibleForTesting
     public void setMusicMute(boolean mute) {
-        mStreamStates[AudioSystem.STREAM_MUSIC].muteInternally(mute);
+        getVssForStreamOrDefault(AudioSystem.STREAM_MUSIC).muteInternally(mute);
     }
 
     private static final Set<Integer> DEVICE_MEDIA_UNMUTED_ON_PLUG_SET;
@@ -7999,8 +8232,8 @@
         if (mNm.getZenMode() != Settings.Global.ZEN_MODE_NO_INTERRUPTIONS
                 && !isStreamMutedByRingerOrZenMode(AudioSystem.STREAM_MUSIC)
                 && DEVICE_MEDIA_UNMUTED_ON_PLUG_SET.contains(newDevice)
-                && mStreamStates[AudioSystem.STREAM_MUSIC].mIsMuted
-                && mStreamStates[AudioSystem.STREAM_MUSIC].getIndex(newDevice) != 0
+                && getVssForStreamOrDefault(AudioSystem.STREAM_MUSIC).mIsMuted
+                && getVssForStreamOrDefault(AudioSystem.STREAM_MUSIC).getIndex(newDevice) != 0
                 && getDeviceSetForStreamDirect(AudioSystem.STREAM_MUSIC).contains(newDevice)) {
             if (DEBUG_VOL) {
                 Log.i(TAG, String.format("onAccessoryPlugMediaUnmute unmuting device=%d [%s]",
@@ -8009,7 +8242,8 @@
             // Locking mSettingsLock to avoid inversion when calling vss.mute -> vss.doMute ->
             // vss.updateVolumeGroupIndex
             synchronized (mSettingsLock) {
-                mStreamStates[AudioSystem.STREAM_MUSIC].mute(false, "onAccessoryPlugMediaUnmute");
+                getVssForStreamOrDefault(AudioSystem.STREAM_MUSIC).mute(false,
+                        "onAccessoryPlugMediaUnmute");
             }
         }
     }
@@ -8031,21 +8265,37 @@
     private static final SparseArray<VolumeGroupState> sVolumeGroupStates = new SparseArray<>();
 
     private void initVolumeGroupStates() {
+        int btScoGroupId = -1;
+        VolumeGroupState voiceCallGroup = null;
         for (final AudioVolumeGroup avg : getAudioVolumeGroups()) {
             try {
-                // if no valid attributes, this volume group is not controllable, throw exception
-                ensureValidAttributes(avg);
-                sVolumeGroupStates.append(avg.getId(), new VolumeGroupState(avg));
+                if (ensureValidVolumeGroup(avg)) {
+                    final VolumeGroupState vgs = new VolumeGroupState(avg);
+                    sVolumeGroupStates.append(avg.getId(), vgs);
+                    if (vgs.isVoiceCall()) {
+                        voiceCallGroup = vgs;
+                    }
+                } else {
+                    // invalid volume group will be reported for bt sco group with no other
+                    // legacy stream type, we try to replace it in sVolumeGroupStates with the
+                    // voice call volume group
+                    btScoGroupId = avg.getId();
+                }
             } catch (IllegalArgumentException e) {
                 // Volume Groups without attributes are not controllable through set/get volume
                 // using attributes. Do not append them.
                 if (DEBUG_VOL) {
                     Log.d(TAG, "volume group " + avg.name() + " for internal policy needs");
                 }
-                continue;
             }
         }
 
+        if (replaceStreamBtSco() && btScoGroupId >= 0 && voiceCallGroup != null) {
+            // the bt sco group is deprecated, storing the voice call group instead
+            // to keep the code backwards compatible when calling the volume group APIs
+            sVolumeGroupStates.append(btScoGroupId, voiceCallGroup);
+        }
+
         // need mSettingsLock for vgs.applyAllVolumes -> vss.setIndex which grabs this lock after
         // VSS.class. Locking order needs to be preserved
         synchronized (mSettingsLock) {
@@ -8056,13 +8306,30 @@
         }
     }
 
-    private void ensureValidAttributes(AudioVolumeGroup avg) {
+    /**
+     * Returns false if the legacy stream types only contains the deprecated
+     * {@link AudioSystem#STREAM_BLUETOOTH_SCO}.
+     *
+     * @throws IllegalArgumentException if it has more than one non-default {@link AudioAttributes}
+     *
+     * @param avg the volume group to check
+     */
+    private boolean ensureValidVolumeGroup(AudioVolumeGroup avg) {
         boolean hasAtLeastOneValidAudioAttributes = avg.getAudioAttributes().stream()
                 .anyMatch(aa -> !aa.equals(AudioProductStrategy.getDefaultAttributes()));
         if (!hasAtLeastOneValidAudioAttributes) {
             throw new IllegalArgumentException("Volume Group " + avg.name()
                     + " has no valid audio attributes");
         }
+        if (replaceStreamBtSco()) {
+            // if there are multiple legacy stream types associated we can omit stream bt sco
+            // otherwise this is not a valid volume group
+            if (avg.getLegacyStreamTypes().length == 1
+                    && avg.getLegacyStreamTypes()[0] == AudioSystem.STREAM_BLUETOOTH_SCO) {
+                return false;
+            }
+        }
+        return true;
     }
 
     private void readVolumeGroupsSettings(boolean userSwitch) {
@@ -8169,8 +8436,14 @@
                         break;
                     }
                 }
-                mIndexMin = MIN_STREAM_VOLUME[mPublicStreamType];
-                mIndexMax = MAX_STREAM_VOLUME[mPublicStreamType];
+
+                if (replaceStreamBtSco()) {
+                    mIndexMin = getVssForStreamOrDefault(mPublicStreamType).getMinIndex() / 10;
+                    mIndexMax = getVssForStreamOrDefault(mPublicStreamType).getMaxIndex() / 10;
+                } else {
+                    mIndexMin = MIN_STREAM_VOLUME[mPublicStreamType];
+                    mIndexMax = MAX_STREAM_VOLUME[mPublicStreamType];
+                }
             } else if (!avg.getAudioAttributes().isEmpty()) {
                 mIndexMin = AudioSystem.getMinVolumeIndexForAttributes(mAudioAttributes);
                 mIndexMax = AudioSystem.getMaxVolumeIndexForAttributes(mAudioAttributes);
@@ -8203,7 +8476,7 @@
          */
         private boolean isVssMuteBijective(int stream) {
             return isStreamAffectedByMute(stream)
-                    && (getMinIndex() == (mStreamStates[stream].mIndexMin + 5) / 10)
+                    && (getMinIndex() == (getVssForStreamOrDefault(stream).getMinIndex() + 5) / 10)
                     && (getMinIndex() == 0 || isCallStream(stream));
         }
 
@@ -8248,6 +8521,9 @@
                         }
                         return;
                     }
+
+                    float stepFactor = getVssForStreamOrDefault(
+                            mPublicStreamType).getIndexStepFactor();
                     switch (direction) {
                         case AudioManager.ADJUST_TOGGLE_MUTE: {
                             // Note: If muted by volume 0, unmute will restore volume 0.
@@ -8268,7 +8544,8 @@
                             break;
                         case AudioManager.ADJUST_RAISE:
                             // As for stream, RAISE during mute will increment the index
-                            setVolumeIndex(Math.min(previousIndex + 1, mIndexMax),  device, flags);
+                            setVolumeIndex(Math.min((int) ((previousIndex + 1) * stepFactor),
+                                    mIndexMax), device, flags);
                             break;
                         case AudioManager.ADJUST_LOWER:
                             // For stream, ADJUST_LOWER on a muted VSS is a no-op
@@ -8277,7 +8554,8 @@
                             if (isMuted() && previousIndex != 0) {
                                 mute(false);
                             } else {
-                                int newIndex = Math.max(previousIndex - 1, mIndexMin);
+                                int newIndex = Math.max((int) ((previousIndex - 1) * stepFactor),
+                                        mIndexMin);
                                 setVolumeIndex(newIndex, device, flags);
                             }
                             break;
@@ -8341,11 +8619,22 @@
             // This allows RX path muting by the audio HAL only when explicitly muted but not when
             // index is just set to 0 to repect BT requirements
             if (mHasValidStreamType && isVssMuteBijective(mPublicStreamType)
-                    && mStreamStates[mPublicStreamType].isFullyMuted()) {
+                    && getVssForStreamOrDefault(mPublicStreamType).isFullyMuted()) {
                 index = 0;
-            } else if (mPublicStreamType == AudioSystem.STREAM_BLUETOOTH_SCO && index == 0) {
+            } else if (isStreamBluetoothSco(mPublicStreamType) && index == 0) {
                 index = 1;
             }
+
+            if (replaceStreamBtSco()) {
+                index = (int) (mIndexMin + (index - mIndexMin)
+                        / getVssForStreamOrDefault(mPublicStreamType).getIndexStepFactor());
+            }
+
+            if (DEBUG_VOL) {
+                Log.d(TAG, "setVolumeIndexInt(" + mAudioVolumeGroup.getId() + ", " + index + ", "
+                        + device + ")");
+            }
+
             // Set the volume index
             mAudioSystem.setVolumeIndexForAttributes(mAudioAttributes, index, device);
         }
@@ -8371,13 +8660,17 @@
         }
 
         private boolean isValidStream(int stream) {
-            return (stream != AudioSystem.STREAM_DEFAULT) && (stream < mStreamStates.length);
+            return (stream != AudioSystem.STREAM_DEFAULT) && getVssForStream(stream) != null;
         }
 
         public boolean isMusic() {
             return mHasValidStreamType && mPublicStreamType == AudioSystem.STREAM_MUSIC;
         }
 
+        public boolean isVoiceCall() {
+            return mHasValidStreamType && mPublicStreamType == AudioSystem.STREAM_VOICE_CALL;
+        }
+
         public void applyAllVolumes(boolean userSwitch) {
             String caller = "from vgs";
             synchronized (AudioService.VolumeStreamState.class) {
@@ -8389,10 +8682,10 @@
                     if (device != AudioSystem.DEVICE_OUT_DEFAULT) {
                         for (int stream : getLegacyStreamTypes()) {
                             if (isValidStream(stream)) {
-                                boolean streamMuted = mStreamStates[stream].mIsMuted;
+                                final VolumeStreamState vss = getVssForStreamOrDefault(stream);
+                                boolean streamMuted = vss.mIsMuted;
                                 int deviceForStream = getDeviceForStream(stream);
-                                int indexForStream =
-                                        (mStreamStates[stream].getIndex(deviceForStream) + 5) / 10;
+                                int indexForStream = (vss.getIndex(deviceForStream) + 5) / 10;
                                 if (device == deviceForStream) {
                                     if (indexForStream == index && (isMuted() == streamMuted)
                                             && isVssMuteBijective(stream)) {
@@ -8402,19 +8695,17 @@
                                     if (vgsVssSyncMuteOrder()) {
                                         if ((isMuted() != streamMuted) && isVssMuteBijective(
                                                 stream)) {
-                                            mStreamStates[stream].mute(isMuted(),
-                                                    "VGS.applyAllVolumes#1");
+                                            vss.mute(isMuted(), "VGS.applyAllVolumes#1");
                                         }
                                     }
                                     if (indexForStream != index) {
-                                        mStreamStates[stream].setIndex(index * 10, device, caller,
-                                                true /*hasModifyAudioSettings*/);
+                                        vss.setIndex(index * 10, device,
+                                                caller, true /*hasModifyAudioSettings*/);
                                     }
                                     if (!vgsVssSyncMuteOrder()) {
                                         if ((isMuted() != streamMuted) && isVssMuteBijective(
                                                 stream)) {
-                                            mStreamStates[stream].mute(isMuted(),
-                                                    "VGS.applyAllVolumes#1");
+                                            vss.mute(isMuted(), "VGS.applyAllVolumes#1");
                                         }
                                     }
                                 }
@@ -8438,11 +8729,12 @@
                 boolean forceDeviceSync = userSwitch && (mIndexMap.indexOfKey(deviceForVolume) < 0);
                 for (int stream : getLegacyStreamTypes()) {
                     if (isValidStream(stream)) {
-                        boolean streamMuted = mStreamStates[stream].mIsMuted;
-                        int defaultStreamIndex = (mStreamStates[stream].getIndex(
-                                        AudioSystem.DEVICE_OUT_DEFAULT) + 5) / 10;
+                        final VolumeStreamState vss = getVssForStreamOrDefault(stream);
+                        boolean streamMuted = vss.mIsMuted;
+                        int defaultStreamIndex = (vss.getIndex(AudioSystem.DEVICE_OUT_DEFAULT) + 5)
+                                / 10;
                         if (forceDeviceSync) {
-                            mStreamStates[stream].setIndex(index * 10, deviceForVolume, caller,
+                            vss.setIndex(index * 10, deviceForVolume, caller,
                                     true /*hasModifyAudioSettings*/);
                         }
                         if (defaultStreamIndex == index && (isMuted() == streamMuted)
@@ -8451,12 +8743,11 @@
                             continue;
                         }
                         if (defaultStreamIndex != index) {
-                            mStreamStates[stream].setIndex(
-                                    index * 10, AudioSystem.DEVICE_OUT_DEFAULT, caller,
+                            vss.setIndex(index * 10, AudioSystem.DEVICE_OUT_DEFAULT, caller,
                                     true /*hasModifyAudioSettings*/);
                         }
                         if ((isMuted() != streamMuted) && isVssMuteBijective(stream)) {
-                            mStreamStates[stream].mute(isMuted(), "VGS.applyAllVolumes#2");
+                            vss.mute(isMuted(), "VGS.applyAllVolumes#2");
                         }
                     }
                 }
@@ -8611,7 +8902,6 @@
         }
     }
 
-
     // NOTE: Locking order for synchronized objects related to volume or ringer mode management:
     //  1 mScoclient OR mSafeMediaVolumeState
     //  2   mSetModeLock
@@ -8625,6 +8915,17 @@
         private int mIndexMinNoPerm;
         private int mIndexMax;
 
+        /**
+         * Variable used to determine the size of an incremental step when calling the
+         * adjustStreamVolume methods with raise/lower adjustments. This can change dynamically
+         * for some streams.
+         *
+         * <p>STREAM_VOICE_CALL has a different step value when is streaming on a SCO device.
+         * Internally we are using the same volume range but through the step factor we force the
+         * number of UI volume steps.
+         */
+        private float mIndexStepFactor = 1.f;
+
         private boolean mIsMuted = false;
         private boolean mIsMutedInternally = false;
         private String mVolumeIndexSettingName;
@@ -8666,10 +8967,10 @@
 
             mStreamType = streamType;
             mIndexMin = MIN_STREAM_VOLUME[streamType] * 10;
-            mIndexMinNoPerm = mIndexMin; // may be overwritten later in updateNoPermMinIndex()
             mIndexMax = MAX_STREAM_VOLUME[streamType] * 10;
+
             final int status = AudioSystem.initStreamVolume(
-                    streamType, mIndexMin / 10, mIndexMax / 10);
+                    streamType, MIN_STREAM_VOLUME[streamType], MAX_STREAM_VOLUME[streamType]);
             if (status != AudioSystem.AUDIO_STATUS_OK) {
                 sLifecycleLogger.enqueue(new EventLogger.StringEvent(
                          "VSS() stream:" + streamType + " initStreamVolume=" + status)
@@ -8678,6 +8979,9 @@
                         "VSS()" /*obj*/, 2 * INDICATE_SYSTEM_READY_RETRY_DELAY_MS);
             }
 
+            updateIndexFactors();
+            mIndexMinNoPerm = mIndexMin; // may be overwritten later in updateNoPermMinIndex()
+
             readSettings();
             mVolumeChanged = new Intent(AudioManager.VOLUME_CHANGED_ACTION);
             mVolumeChanged.putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, mStreamType);
@@ -8701,6 +9005,38 @@
             mStreamDevicesChangedOptions = streamDevicesChangedOptions.toBundle();
         }
 
+        public void updateIndexFactors() {
+            if (!replaceStreamBtSco()) {
+                return;
+            }
+
+            synchronized (this) {
+                if (mStreamType == AudioSystem.STREAM_VOICE_CALL) {
+                    if (MAX_STREAM_VOLUME[AudioSystem.STREAM_BLUETOOTH_SCO]
+                            > MAX_STREAM_VOLUME[mStreamType]) {
+                        mIndexMax = MAX_STREAM_VOLUME[AudioSystem.STREAM_BLUETOOTH_SCO] * 10;
+                    }
+
+                    // SCO devices have a different min index
+                    if (isStreamBluetoothSco(mStreamType)) {
+                        mIndexMin = MIN_STREAM_VOLUME[AudioSystem.STREAM_BLUETOOTH_SCO] * 10;
+                        mIndexStepFactor = 1.f;
+                    } else {
+                        mIndexMin = MIN_STREAM_VOLUME[AudioSystem.STREAM_VOICE_CALL] * 10;
+                        mIndexStepFactor = (float) (mIndexMax - mIndexMin) / (float) (
+                                MAX_STREAM_VOLUME[mStreamType] * 10
+                                        - MIN_STREAM_VOLUME[mStreamType] * 10);
+                    }
+
+                    if (mVolumeGroupState != null) {
+                        mVolumeGroupState.mIndexMin = mIndexMin;
+                    }
+
+                    mIndexMinNoPerm = mIndexMin;
+                }
+            }
+        }
+
         /**
          * Associate a {@link volumeGroupState} on the {@link VolumeStreamState}.
          * <p> It helps to synchronize the index, mute attributes on the maching
@@ -8713,6 +9049,11 @@
                 mVolumeGroupState.setSettingName(mVolumeIndexSettingName);
             }
         }
+
+        public float getIndexStepFactor() {
+            return mIndexStepFactor;
+        }
+
         /**
          * Update the minimum index that can be used without MODIFY_AUDIO_SETTINGS permission
          * @param index minimum index expressed in "UI units", i.e. no 10x factor
@@ -8754,7 +9095,7 @@
                 postObserveDevicesForAllStreams(mStreamType);
             }
             // log base stream changes to the event log
-            if (mStreamVolumeAlias[mStreamType] == mStreamType) {
+            if (sStreamVolumeAlias.get(mStreamType, /*valueIfKeyNotFound=*/-1) == mStreamType) {
                 EventLogTags.writeStreamDevicesChanged(mStreamType, prevDevices, devices);
             }
             // send STREAM_DEVICES_CHANGED_ACTION on the message handler so it is scheduled after
@@ -8871,15 +9212,20 @@
             }
         }
 
+        @GuardedBy("VolumeStreamState.class")
         private void setStreamVolumeIndex(int index, int device) {
             // Only set audio policy BT SCO stream volume to 0 when the stream is actually muted.
             // This allows RX path muting by the audio HAL only when explicitly muted but not when
             // index is just set to 0 to repect BT requirements
-            if (mStreamType == AudioSystem.STREAM_BLUETOOTH_SCO && index == 0
-                    && !isFullyMuted()) {
+            if (isStreamBluetoothSco(mStreamType) && index == 0 && !isFullyMuted()) {
                 index = 1;
             }
 
+            if (replaceStreamBtSco()) {
+                index = (int) (mIndexMin + (index * 10 - mIndexMin) / getIndexStepFactor() + 5)
+                        / 10;
+            }
+
             if (DEBUG_VOL) {
                 Log.d(TAG, "setStreamVolumeIndexAS(" + mStreamType + ", " + index + ", " + device
                         + ")");
@@ -8888,6 +9234,7 @@
         }
 
         // must be called while synchronized VolumeStreamState.class
+        @GuardedBy("VolumeStreamState.class")
         /*package*/ void applyDeviceVolume_syncVSS(int device) {
             int index;
             if (isFullyMuted()) {
@@ -9000,10 +9347,11 @@
                     isCurrentDevice = (device == getDeviceForStream(mStreamType));
                     final int numStreamTypes = AudioSystem.getNumStreamTypes();
                     for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
-                        final VolumeStreamState aliasStreamState = mStreamStates[streamType];
-                        if (streamType != mStreamType &&
-                                mStreamVolumeAlias[streamType] == mStreamType &&
-                                (changed || !aliasStreamState.hasIndexForDevice(device))) {
+                        final VolumeStreamState aliasStreamState = getVssForStream(streamType);
+                        if (aliasStreamState != null && streamType != mStreamType
+                                && sStreamVolumeAlias.get(streamType, /*valueIfKeyNotFound*/-1)
+                                == mStreamType && (changed || !aliasStreamState.hasIndexForDevice(
+                                device))) {
                             final int scaledIndex =
                                     rescaleIndex(aliasIndex, mStreamType, streamType);
                             boolean changedAlias = aliasStreamState.setIndex(scaledIndex, device,
@@ -9038,7 +9386,7 @@
                 oldIndex = (oldIndex + 5) / 10;
                 index = (index + 5) / 10;
                 // log base stream changes to the event log
-                if (mStreamVolumeAlias[mStreamType] == mStreamType) {
+                if (sStreamVolumeAlias.get(mStreamType, /*valueIfKeyNotFound=*/-1) == mStreamType) {
                     if (caller == null) {
                         Log.w(TAG, "No caller for volume_changed event", new Throwable());
                     }
@@ -9050,22 +9398,47 @@
                 if ((index != oldIndex) && isCurrentDevice) {
                     // for single volume devices, only send the volume change broadcast
                     // on the alias stream
-                    if (!mIsSingleVolume || (mStreamVolumeAlias[mStreamType] == mStreamType)) {
+                    final int streamAlias = sStreamVolumeAlias.get(
+                            mStreamType, /*valueIfKeyNotFound=*/-1);
+                    if (!mIsSingleVolume || streamAlias == mStreamType) {
                         mVolumeChanged.putExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, index);
                         mVolumeChanged.putExtra(AudioManager.EXTRA_PREV_VOLUME_STREAM_VALUE,
                                 oldIndex);
+                        int extraStreamType = mStreamType;
+                        // TODO: remove this when deprecating STREAM_BLUETOOTH_SCO
+                        if (isStreamBluetoothSco(mStreamType)) {
+                            mVolumeChanged.putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE,
+                                    AudioSystem.STREAM_BLUETOOTH_SCO);
+                            extraStreamType = AudioSystem.STREAM_BLUETOOTH_SCO;
+                        } else {
+                            mVolumeChanged.putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE,
+                                    mStreamType);
+                        }
                         mVolumeChanged.putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE_ALIAS,
-                                mStreamVolumeAlias[mStreamType]);
-                        if (mStreamType == mStreamVolumeAlias[mStreamType]) {
+                                streamAlias);
+
+                        if (mStreamType == streamAlias) {
                             String aliasStreamIndexesString = "";
                             if (!aliasStreamIndexes.isEmpty()) {
                                 aliasStreamIndexesString =
                                         " aliased streams: " + aliasStreamIndexes;
                             }
                             AudioService.sVolumeLogger.enqueue(new VolChangedBroadcastEvent(
-                                    mStreamType, aliasStreamIndexesString, index, oldIndex));
+                                    extraStreamType, aliasStreamIndexesString, index, oldIndex));
+                            if (extraStreamType != mStreamType) {
+                                AudioService.sVolumeLogger.enqueue(new VolChangedBroadcastEvent(
+                                        mStreamType, aliasStreamIndexesString, index, oldIndex));
+                            }
                         }
                         sendBroadcastToAll(mVolumeChanged, mVolumeChangedOptions);
+                        if (extraStreamType != mStreamType) {
+                            // send multiple intents in case we merged voice call and bt sco streams
+                            mVolumeChanged.putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE,
+                                    mStreamType);
+                            // do not use the options in thid case which could discard
+                            // the previous intent
+                            sendBroadcastToAll(mVolumeChanged, null);
+                        }
                     }
                 }
             }
@@ -9091,8 +9464,8 @@
                     index = mIndexMap.get(AudioSystem.DEVICE_OUT_DEFAULT);
                 }
                 final VolumeInfo vi = new VolumeInfo.Builder(mStreamType)
-                        .setMinVolumeIndex(mIndexMin)
-                        .setMaxVolumeIndex(mIndexMax)
+                        .setMinVolumeIndex(getMinIndex())
+                        .setMaxVolumeIndex(getMaxIndex())
                         .setVolumeIndex(index)
                         .setMuted(isFullyMuted())
                         .build();
@@ -9136,7 +9509,7 @@
         // must be sync'd on mSettingsLock before VolumeStreamState.class
         @GuardedBy("VolumeStreamState.class")
         public void setAllIndexes(VolumeStreamState srcStream, String caller) {
-            if (mStreamType == srcStream.mStreamType) {
+            if (srcStream == null || mStreamType == srcStream.mStreamType) {
                 return;
             }
             int srcStreamType = srcStream.getStreamType();
@@ -9281,7 +9654,7 @@
         public void doMute() {
             synchronized (VolumeStreamState.class) {
                 // If associated to volume group, update group cache
-                updateVolumeGroupIndex(getDeviceForStream(mStreamType), /* forceMuteState= */ true);
+                updateVolumeGroupIndex(getDeviceForStream(mStreamType), /* forceMuteState= */true);
 
                 // Set the new mute volume. This propagates the values to
                 // the audio system, otherwise the volume won't be changed
@@ -9302,7 +9675,7 @@
         public void checkFixedVolumeDevices() {
             synchronized (VolumeStreamState.class) {
                 // ignore settings for fixed volume devices: volume should always be at max or 0
-                if (mStreamVolumeAlias[mStreamType] == AudioSystem.STREAM_MUSIC) {
+                if (sStreamVolumeAlias.get(mStreamType) == AudioSystem.STREAM_MUSIC) {
                     for (int i = 0; i < mIndexMap.size(); i++) {
                         int device = mIndexMap.keyAt(i);
                         int index = mIndexMap.valueAt(i);
@@ -9448,7 +9821,11 @@
     }
 
     private void onSetVolumeIndexOnDevice(@NonNull DeviceVolumeUpdate update) {
-        final VolumeStreamState streamState = mStreamStates[update.mStreamType];
+        final VolumeStreamState streamState = getVssForStream(update.mStreamType);
+        if (streamState == null) {
+            Log.w(TAG, "Invalid onSetVolumeIndexOnDevice for stream type " + update.mStreamType);
+            return;
+        }
         if (update.hasVolumeIndex()) {
             int index = update.getVolumeIndex();
             if (mSoundDoseHelper.checkSafeMediaVolume(update.mStreamType, index, update.mDevice)) {
@@ -9479,8 +9856,10 @@
             // Apply change to all streams using this one as alias
             int numStreamTypes = AudioSystem.getNumStreamTypes();
             for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
-                if (streamType != streamState.mStreamType &&
-                        mStreamVolumeAlias[streamType] == streamState.mStreamType) {
+                final VolumeStreamState vss = getVssForStream(streamType);
+                if (vss != null && streamType != streamState.mStreamType
+                        && sStreamVolumeAlias.get(streamType, /*valueIfKeyNotFound=*/-1)
+                                == streamState.mStreamType) {
                     // Make sure volume is also maxed out on A2DP device for aliased stream
                     // that may have a different device selected
                     int streamDevice = getDeviceForStream(streamType);
@@ -9488,9 +9867,9 @@
                             && (isAbsoluteVolumeDevice(device)
                                 || isA2dpAbsoluteVolumeDevice(device)
                                 || AudioSystem.isLeAudioDeviceType(device))) {
-                        mStreamStates[streamType].applyDeviceVolume_syncVSS(device);
+                        vss.applyDeviceVolume_syncVSS(device);
                     }
-                    mStreamStates[streamType].applyDeviceVolume_syncVSS(streamDevice);
+                    vss.applyDeviceVolume_syncVSS(streamDevice);
                 }
             }
         }
@@ -9524,9 +9903,11 @@
             // Apply change to all streams using this one as alias
             int numStreamTypes = AudioSystem.getNumStreamTypes();
             for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
-                if (streamType != streamState.mStreamType &&
-                        mStreamVolumeAlias[streamType] == streamState.mStreamType) {
-                    mStreamStates[streamType].applyAllVolumes();
+                final VolumeStreamState vss = getVssForStream(streamType);
+                if (vss != null && streamType != streamState.mStreamType
+                        && sStreamVolumeAlias.get(streamType, /*valueIfKeyNotFound=*/-1)
+                                == streamState.mStreamType) {
+                    vss.applyAllVolumes();
                 }
             }
         }
@@ -9845,6 +10226,10 @@
                     onUpdateContextualVolumes();
                     break;
 
+                case MSG_SCO_DEVICE_ACTIVE_UPDATE:
+                    onUpdateScoDeviceActive(msg.arg1 != 0);
+                    break;
+
                 case MusicFxHelper.MSG_EFFECT_CLIENT_GONE:
                     mMusicFxHelper.handleMessage(msg);
                     break;
@@ -9972,7 +10357,7 @@
         }
         sendMsg(mAudioHandler, MSG_SET_DEVICE_VOLUME, SENDMSG_QUEUE,
                     AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, 0,
-                    mStreamStates[AudioSystem.STREAM_MUSIC], 0);
+                    getVssForStreamOrDefault(AudioSystem.STREAM_MUSIC), 0);
     }
 
     /**
@@ -10092,7 +10477,7 @@
                         SENDMSG_QUEUE,
                         0,
                         0,
-                        mStreamStates[AudioSystem.STREAM_MUSIC], 0);
+                        getVssForStreamOrDefault(AudioSystem.STREAM_MUSIC), 0);
             } else if (action.equals(Intent.ACTION_USER_BACKGROUND)) {
                 // Disable audio recording for the background user/profile
                 int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
@@ -11259,13 +11644,15 @@
                 if (cameraSoundForcedChanged) {
                     if (!mIsSingleVolume) {
                         synchronized (VolumeStreamState.class) {
-                            VolumeStreamState s = mStreamStates[AudioSystem.STREAM_SYSTEM_ENFORCED];
+                            final VolumeStreamState s = getVssForStreamOrDefault(
+                                    AudioSystem.STREAM_SYSTEM_ENFORCED);
                             if (cameraSoundForced) {
                                 s.setAllIndexesToMax();
                                 mRingerModeAffectedStreams &=
                                         ~(1 << AudioSystem.STREAM_SYSTEM_ENFORCED);
                             } else {
-                                s.setAllIndexes(mStreamStates[AudioSystem.STREAM_SYSTEM], TAG);
+                                s.setAllIndexes(getVssForStreamOrDefault(AudioSystem.STREAM_SYSTEM),
+                                        TAG);
                                 mRingerModeAffectedStreams |=
                                         (1 << AudioSystem.STREAM_SYSTEM_ENFORCED);
                             }
@@ -11282,7 +11669,7 @@
                             SENDMSG_QUEUE,
                             0,
                             0,
-                            mStreamStates[AudioSystem.STREAM_SYSTEM_ENFORCED], 0);
+                            getVssForStreamOrDefault(AudioSystem.STREAM_SYSTEM_ENFORCED), 0);
 
                 }
             }
@@ -12258,6 +12645,11 @@
             if (mController == null)
                 return;
             try {
+                // TODO: remove this when deprecating STREAM_BLUETOOTH_SCO
+                if (isStreamBluetoothSco(streamType)) {
+                    // TODO: notify both sco and voice_call about volume changes
+                    streamType = AudioSystem.STREAM_BLUETOOTH_SCO;
+                }
                 mController.volumeChanged(streamType, flags);
             } catch (RemoteException e) {
                 Log.w(TAG, "Error calling volumeChanged", e);
diff --git a/services/core/java/com/android/server/audio/SoundDoseHelper.java b/services/core/java/com/android/server/audio/SoundDoseHelper.java
index ded93e6..dc79ab2 100644
--- a/services/core/java/com/android/server/audio/SoundDoseHelper.java
+++ b/services/core/java/com/android/server/audio/SoundDoseHelper.java
@@ -633,7 +633,7 @@
     }
 
     /*package*/ void enforceSafeMediaVolume(String caller) {
-        AudioService.VolumeStreamState streamState = mAudioService.getVssVolumeForStream(
+        AudioService.VolumeStreamState streamState = mAudioService.getVssForStreamOrDefault(
                 AudioSystem.STREAM_MUSIC);
 
         for (int i = 0; i < mSafeMediaVolumeDevices.size(); ++i)  {
@@ -665,7 +665,7 @@
     @GuardedBy("mSafeMediaVolumeStateLock")
     private boolean checkSafeMediaVolume_l(int streamType, int index, int device) {
         return (mSafeMediaVolumeState == SAFE_MEDIA_VOLUME_ACTIVE)
-                    && (AudioService.mStreamVolumeAlias[streamType] == AudioSystem.STREAM_MUSIC)
+                    && (AudioService.sStreamVolumeAlias.get(streamType) == AudioSystem.STREAM_MUSIC)
                     && safeDevicesContains(device)
                     && (index > safeMediaVolumeIndex(device));
     }
@@ -908,7 +908,7 @@
                 return;
             }
 
-            if (AudioService.mStreamVolumeAlias[streamType] == AudioSystem.STREAM_MUSIC
+            if (AudioService.sStreamVolumeAlias.get(streamType) == AudioSystem.STREAM_MUSIC
                     && safeDevicesContains(device)) {
                 float attenuationDb = -AudioSystem.getStreamVolumeDB(AudioSystem.STREAM_MUSIC,
                         (newIndex + 5) / 10, device);
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index 8e8a037..fe73bfe 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -21,6 +21,7 @@
 import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
 import static android.hardware.biometrics.BiometricManager.Authenticators;
 import static android.hardware.biometrics.BiometricManager.BIOMETRIC_NO_AUTHENTICATION;
+import static android.hardware.biometrics.SensorProperties.STRENGTH_STRONG;
 
 import static com.android.server.biometrics.BiometricServiceStateProto.STATE_AUTH_IDLE;
 
@@ -41,6 +42,7 @@
 import android.hardware.biometrics.BiometricAuthenticator;
 import android.hardware.biometrics.BiometricConstants;
 import android.hardware.biometrics.BiometricPrompt;
+import android.hardware.biometrics.BiometricStateListener;
 import android.hardware.biometrics.Flags;
 import android.hardware.biometrics.IBiometricAuthenticator;
 import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
@@ -54,8 +56,12 @@
 import android.hardware.biometrics.PromptInfo;
 import android.hardware.biometrics.SensorPropertiesInternal;
 import android.hardware.camera2.CameraManager;
+import android.hardware.face.FaceManager;
+import android.hardware.face.FaceSensorPropertiesInternal;
+import android.hardware.face.IFaceAuthenticatorsRegisteredCallback;
 import android.hardware.fingerprint.FingerprintManager;
 import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
+import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback;
 import android.hardware.security.keymint.HardwareAuthenticatorType;
 import android.net.Uri;
 import android.os.Binder;
@@ -234,6 +240,8 @@
         private static final boolean DEFAULT_APP_ENABLED = true;
         private static final boolean DEFAULT_ALWAYS_REQUIRE_CONFIRMATION = false;
         private static final boolean DEFAULT_MANDATORY_BIOMETRICS_STATUS = false;
+        private static final boolean DEFAULT_MANDATORY_BIOMETRICS_REQUIREMENTS_SATISFIED_STATUS =
+                true;
 
         // Some devices that shipped before S already have face-specific settings. Instead of
         // migrating, which is complicated, let's just keep using the existing settings.
@@ -256,14 +264,23 @@
                 Settings.Secure.getUriFor(Settings.Secure.BIOMETRIC_APP_ENABLED);
         private final Uri MANDATORY_BIOMETRICS_ENABLED =
                 Settings.Secure.getUriFor(Settings.Secure.MANDATORY_BIOMETRICS);
+        private final Uri MANDATORY_BIOMETRICS_REQUIREMENTS_SATISFIED = Settings.Secure.getUriFor(
+                Settings.Secure.MANDATORY_BIOMETRICS_REQUIREMENTS_SATISFIED);
 
         private final ContentResolver mContentResolver;
         private final List<BiometricService.EnabledOnKeyguardCallback> mCallbacks;
+        private final UserManager mUserManager;
 
         private final Map<Integer, Boolean> mBiometricEnabledOnKeyguard = new HashMap<>();
         private final Map<Integer, Boolean> mBiometricEnabledForApps = new HashMap<>();
         private final Map<Integer, Boolean> mFaceAlwaysRequireConfirmation = new HashMap<>();
         private final Map<Integer, Boolean> mMandatoryBiometricsEnabled = new HashMap<>();
+        private final Map<Integer, Boolean> mMandatoryBiometricsRequirementsSatisfied =
+                new HashMap<>();
+        private final Map<Integer, Boolean> mFingerprintEnrolledForUser =
+                new HashMap<>();
+        private final Map<Integer, Boolean> mFaceEnrolledForUser =
+                new HashMap<>();
 
         /**
          * Creates a content observer.
@@ -275,6 +292,7 @@
             super(handler);
             mContentResolver = context.getContentResolver();
             mCallbacks = callbacks;
+            mUserManager = context.getSystemService(UserManager.class);
 
             final boolean hasFingerprint = context.getPackageManager()
                     .hasSystemFeature(PackageManager.FEATURE_FINGERPRINT);
@@ -285,10 +303,8 @@
             mUseLegacyFaceOnlySettings =
                     Build.VERSION.DEVICE_INITIAL_SDK_INT <= Build.VERSION_CODES.Q
                     && hasFace && !hasFingerprint;
-            mMandatoryBiometricsEnabled.put(context.getUserId(), Settings.Secure.getIntForUser(
-                    mContentResolver, Settings.Secure.MANDATORY_BIOMETRICS,
-                    DEFAULT_MANDATORY_BIOMETRICS_STATUS ? 1 : 0, context.getUserId()) != 0);
 
+            addBiometricListenersForMandatoryBiometrics(context);
             updateContentObserver();
         }
 
@@ -322,6 +338,10 @@
                     false /* notifyForDescendants */,
                     this /* observer */,
                     UserHandle.USER_ALL);
+            mContentResolver.registerContentObserver(MANDATORY_BIOMETRICS_REQUIREMENTS_SATISFIED,
+                    false /* notifyForDescendants */,
+                    this /* observer */,
+                    UserHandle.USER_ALL);
         }
 
         @Override
@@ -365,11 +385,9 @@
                         DEFAULT_APP_ENABLED ? 1 : 0 /* default */,
                         userId) != 0);
             } else if (MANDATORY_BIOMETRICS_ENABLED.equals(uri)) {
-                mMandatoryBiometricsEnabled.put(userId, Settings.Secure.getIntForUser(
-                        mContentResolver,
-                        Settings.Secure.MANDATORY_BIOMETRICS,
-                        DEFAULT_MANDATORY_BIOMETRICS_STATUS ? 1 : 0 /* default */,
-                        userId) != 0);
+                updateMandatoryBiometricsForAllProfiles();
+            } else if (MANDATORY_BIOMETRICS_REQUIREMENTS_SATISFIED.equals(uri)) {
+                updateMandatoryBiometricsRequirementsForAllProfiles();
             }
         }
 
@@ -411,9 +429,21 @@
             }
         }
 
-        public boolean getMandatoryBiometricsEnabledForUser(int userId) {
+        public boolean getMandatoryBiometricsEnabledAndRequirementsSatisfiedForUser(int userId) {
+            if (!mMandatoryBiometricsEnabled.containsKey(userId)) {
+                updateMandatoryBiometricsForAllProfiles();
+            }
+            if (!mMandatoryBiometricsRequirementsSatisfied.containsKey(userId)) {
+                updateMandatoryBiometricsRequirementsForAllProfiles();
+            }
             return mMandatoryBiometricsEnabled.getOrDefault(userId,
-                    DEFAULT_MANDATORY_BIOMETRICS_STATUS);
+                    DEFAULT_MANDATORY_BIOMETRICS_STATUS)
+                    && mMandatoryBiometricsRequirementsSatisfied.getOrDefault(userId,
+                    DEFAULT_MANDATORY_BIOMETRICS_REQUIREMENTS_SATISFIED_STATUS)
+                    && mBiometricEnabledForApps.getOrDefault(userId, DEFAULT_APP_ENABLED)
+                    && getEnabledForApps(userId)
+                    && (mFingerprintEnrolledForUser.getOrDefault(userId, false /* default */)
+                    || mFaceEnrolledForUser.getOrDefault(userId, false /* default */));
         }
 
         void notifyEnabledOnKeyguardCallbacks(int userId) {
@@ -424,6 +454,101 @@
                         userId);
             }
         }
+
+        private void updateMandatoryBiometricsForAllProfiles() {
+            final int mainUserId = mUserManager.getMainUser().getIdentifier();
+            for (UserHandle userHandle: mUserManager.getUserProfiles()) {
+                mMandatoryBiometricsEnabled.put(userHandle.getIdentifier(),
+                        Settings.Secure.getIntForUser(
+                                mContentResolver, Settings.Secure.MANDATORY_BIOMETRICS,
+                                DEFAULT_MANDATORY_BIOMETRICS_STATUS ? 1 : 0,
+                                mainUserId) != 0);
+            }
+        }
+
+        private void updateMandatoryBiometricsRequirementsForAllProfiles() {
+            final int mainUserId = mUserManager.getMainUser().getIdentifier();
+            for (UserHandle userHandle: mUserManager.getUserProfiles()) {
+                mMandatoryBiometricsRequirementsSatisfied.put(userHandle.getIdentifier(),
+                        Settings.Secure.getIntForUser(mContentResolver,
+                                Settings.Secure.MANDATORY_BIOMETRICS_REQUIREMENTS_SATISFIED,
+                                DEFAULT_MANDATORY_BIOMETRICS_REQUIREMENTS_SATISFIED_STATUS ? 1 : 0,
+                                mainUserId) != 0);
+            }
+        }
+
+        private void addBiometricListenersForMandatoryBiometrics(Context context) {
+            final FingerprintManager fingerprintManager = context.getSystemService(
+                    FingerprintManager.class);
+            final FaceManager faceManager = context.getSystemService(FaceManager.class);
+            if (fingerprintManager != null) {
+                fingerprintManager.addAuthenticatorsRegisteredCallback(
+                        new IFingerprintAuthenticatorsRegisteredCallback.Stub() {
+                            @Override
+                            public void onAllAuthenticatorsRegistered(
+                                    List<FingerprintSensorPropertiesInternal> list) {
+                                if (list == null || list.isEmpty()) {
+                                    Slog.d(TAG, "No fingerprint authenticators registered.");
+                                    return;
+                                }
+                                final FingerprintSensorPropertiesInternal
+                                        fingerprintSensorProperties = list.get(0);
+                                if (fingerprintSensorProperties.sensorStrength
+                                        == STRENGTH_STRONG) {
+                                    fingerprintManager.registerBiometricStateListener(
+                                            new BiometricStateListener() {
+                                                @Override
+                                                public void onEnrollmentsChanged(
+                                                        int userId,
+                                                        int sensorId,
+                                                        boolean hasEnrollments
+                                                ) {
+                                                    if (sensorId == fingerprintSensorProperties
+                                                            .sensorId) {
+                                                        mFingerprintEnrolledForUser.put(userId,
+                                                                hasEnrollments);
+                                                    }
+                                                }
+                                            });
+                                }
+                            }
+                        });
+            }
+            if (faceManager != null) {
+                faceManager.addAuthenticatorsRegisteredCallback(
+                        new IFaceAuthenticatorsRegisteredCallback.Stub() {
+                            @Override
+                            public void onAllAuthenticatorsRegistered(
+                                    List<FaceSensorPropertiesInternal> list) {
+                                if (list == null || list.isEmpty()) {
+                                    Slog.d(TAG, "No face authenticators registered.");
+                                    return;
+                                }
+                                final FaceSensorPropertiesInternal
+                                        faceSensorPropertiesInternal = list.get(0);
+                                if (faceSensorPropertiesInternal.sensorStrength
+                                        == STRENGTH_STRONG) {
+                                    faceManager.registerBiometricStateListener(
+                                            new BiometricStateListener() {
+                                                @Override
+                                                public void onEnrollmentsChanged(
+                                                        int userId,
+                                                        int sensorId,
+                                                        boolean hasEnrollments
+                                                ) {
+                                                    if (sensorId
+                                                            == faceSensorPropertiesInternal
+                                                            .sensorId) {
+                                                        mFaceEnrolledForUser.put(userId,
+                                                                hasEnrollments);
+                                                    }
+                                                }
+                                            });
+                                }
+                            }
+                        });
+            }
+        }
     }
 
     final class EnabledOnKeyguardCallback implements IBinder.DeathRecipient {
diff --git a/services/core/java/com/android/server/biometrics/PreAuthInfo.java b/services/core/java/com/android/server/biometrics/PreAuthInfo.java
index b9e6563..0bd22f3 100644
--- a/services/core/java/com/android/server/biometrics/PreAuthInfo.java
+++ b/services/core/java/com/android/server/biometrics/PreAuthInfo.java
@@ -112,8 +112,8 @@
                 == BiometricManager.Authenticators.MANDATORY_BIOMETRICS;
 
         if (dropCredentialFallback(promptInfo.getAuthenticators(),
-                settingObserver.getMandatoryBiometricsEnabledForUser(userId),
-                trustManager)) {
+                settingObserver.getMandatoryBiometricsEnabledAndRequirementsSatisfiedForUser(
+                        userId), trustManager)) {
             promptInfo.setAuthenticators(BiometricManager.Authenticators.BIOMETRIC_STRONG);
             promptInfo.setNegativeButtonText(context.getString(R.string.cancel));
         }
diff --git a/services/core/java/com/android/server/biometrics/biometrics.aconfig b/services/core/java/com/android/server/biometrics/biometrics.aconfig
index 92fd9cb..b2e95aa 100644
--- a/services/core/java/com/android/server/biometrics/biometrics.aconfig
+++ b/services/core/java/com/android/server/biometrics/biometrics.aconfig
@@ -14,3 +14,13 @@
   description: "This flag controls whether virtual HAL is used for testing instead of TestHal "
   bug: "294254230"
 }
+
+flag {
+  name: "notify_fingerprints_loe"
+  namespace: "biometrics_framework"
+  description: "This flag controls whether a notification should be sent to notify user when loss of enrollment happens"
+  bug: "351036558"
+  metadata {
+      purpose: PURPOSE_BUGFIX
+  }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricNotificationUtils.java b/services/core/java/com/android/server/biometrics/sensors/BiometricNotificationUtils.java
index 53e6bdb..27f9cc8 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BiometricNotificationUtils.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BiometricNotificationUtils.java
@@ -151,6 +151,43 @@
     }
 
     /**
+     * Shows a fingerprint notification for loss of enrollment
+     */
+    public static void showFingerprintLoeNotification(@NonNull Context context) {
+        Slog.d(TAG, "Showing fingerprint LOE notification");
+
+        final String name =
+                context.getString(R.string.device_unlock_notification_name);
+        final String title = context.getString(R.string.fingerprint_dangling_notification_title);
+        final String content = context.getString(R.string.fingerprint_loe_notification_msg);
+
+        // Create "Set up" notification action button.
+        final Intent setupIntent =
+                new Intent(BiometricDanglingReceiver.ACTION_FINGERPRINT_RE_ENROLL_LAUNCH);
+        final PendingIntent setupPendingIntent = PendingIntent.getBroadcastAsUser(context, 0,
+                setupIntent, PendingIntent.FLAG_IMMUTABLE, UserHandle.CURRENT);
+        final String setupText =
+                context.getString(R.string.biometric_dangling_notification_action_set_up);
+        final Notification.Action setupAction = new Notification.Action.Builder(
+                null, setupText, setupPendingIntent).build();
+
+        // Create "Not now" notification action button.
+        final Intent notNowIntent =
+                new Intent(BiometricDanglingReceiver.ACTION_FINGERPRINT_RE_ENROLL_DISMISS);
+        final PendingIntent notNowPendingIntent = PendingIntent.getBroadcastAsUser(context, 0,
+                notNowIntent, PendingIntent.FLAG_IMMUTABLE, UserHandle.CURRENT);
+        final String notNowText = context.getString(
+                R.string.biometric_dangling_notification_action_not_now);
+        final Notification.Action notNowAction = new Notification.Action.Builder(
+                null, notNowText, notNowPendingIntent).build();
+
+        showNotificationHelper(context, name, title, content, setupPendingIntent, setupAction,
+                notNowAction, Notification.CATEGORY_SYSTEM, FINGERPRINT_RE_ENROLL_CHANNEL,
+                FINGERPRINT_RE_ENROLL_NOTIFICATION_TAG, Notification.VISIBILITY_SECRET, false,
+                Notification.FLAG_NO_CLEAR);
+    }
+
+    /**
      * Shows a fingerprint bad calibration notification.
      */
     public static void showBadCalibrationNotification(@NonNull Context context) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricUserState.java b/services/core/java/com/android/server/biometrics/sensors/BiometricUserState.java
index 7fb27b6..63678aa 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BiometricUserState.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BiometricUserState.java
@@ -57,6 +57,7 @@
     protected boolean mInvalidationInProgress;
     protected final Context mContext;
     protected final File mFile;
+    private boolean mIsInvalidBiometricState = false;
 
     private final Runnable mWriteStateRunnable = this::doWriteStateInternal;
 
@@ -102,7 +103,7 @@
             serializer.endDocument();
             destination.finishWrite(out);
         } catch (Throwable t) {
-            Slog.wtf(TAG, "Failed to write settings, restoring backup", t);
+            Slog.e(TAG, "Failed to write settings, restoring backup", t);
             destination.failWrite(out);
             throw new IllegalStateException("Failed to write to file: " + mFile.toString(), t);
         } finally {
@@ -192,6 +193,29 @@
         }
     }
 
+    /**
+     * Return true if the biometric file is correctly read. Otherwise return false.
+     */
+    public boolean isInvalidBiometricState() {
+        return mIsInvalidBiometricState;
+    }
+
+    /**
+     * Delete the file of the biometric state.
+     */
+    public void deleteBiometricFile() {
+        synchronized (this) {
+            if (!mFile.exists()) {
+                return;
+            }
+            if (mFile.delete()) {
+                Slog.i(TAG, mFile + " is deleted successfully");
+            } else {
+                Slog.i(TAG, "Failed to delete " + mFile);
+            }
+        }
+    }
+
     private boolean isUnique(String name) {
         for (T identifier : mBiometrics) {
             if (identifier.getName().equals(name)) {
@@ -218,7 +242,8 @@
         try {
             in = new FileInputStream(mFile);
         } catch (FileNotFoundException fnfe) {
-            Slog.i(TAG, "No fingerprint state");
+            Slog.i(TAG, "No fingerprint state", fnfe);
+            mIsInvalidBiometricState = true;
             return;
         }
         try {
diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricUtils.java b/services/core/java/com/android/server/biometrics/sensors/BiometricUtils.java
index ebe4679..0b4f640 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BiometricUtils.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BiometricUtils.java
@@ -33,4 +33,14 @@
     CharSequence getUniqueName(Context context, int userId);
     void setInvalidationInProgress(Context context, int userId, boolean inProgress);
     boolean isInvalidationInProgress(Context context, int userId);
+
+    /**
+     * Return true if the biometric file is correctly read. Otherwise return false.
+     */
+    boolean hasValidBiometricUserState(Context context, int userId);
+
+    /**
+     * Delete the file of the biometric state.
+     */
+    void deleteStateForUser(int userId);
 }
\ No newline at end of file
diff --git a/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java
index 69ad152..77e27ba 100644
--- a/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java
@@ -25,6 +25,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.biometrics.BiometricsProto;
+import com.android.server.biometrics.Flags;
 import com.android.server.biometrics.log.BiometricContext;
 import com.android.server.biometrics.log.BiometricLogger;
 
@@ -62,7 +63,7 @@
     }
 
     private final ArrayList<UserTemplate> mUnknownHALTemplates = new ArrayList<>();
-    private final BiometricUtils<S> mBiometricUtils;
+    protected final BiometricUtils<S> mBiometricUtils;
     private final Map<Integer, Long> mAuthenticatorIds;
     private final boolean mHasEnrollmentsBeforeStarting;
     private BaseClientMonitor mCurrentTask;
@@ -105,6 +106,11 @@
                     startCleanupUnknownHalTemplates();
                 }
             }
+
+            if (mBiometricUtils.hasValidBiometricUserState(getContext(), getTargetUserId())
+                    && Flags.notifyFingerprintsLoe()) {
+                handleInvalidBiometricState();
+            }
         }
     };
 
@@ -248,4 +254,8 @@
     public ArrayList<UserTemplate> getUnknownHALTemplates() {
         return mUnknownHALTemplates;
     }
+
+    protected void handleInvalidBiometricState() {}
+
+    protected abstract int getModality();
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceUtils.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceUtils.java
index c574478..79285cb 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceUtils.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceUtils.java
@@ -124,6 +124,22 @@
         return getStateForUser(context, userId).isInvalidationInProgress();
     }
 
+    @Override
+    public boolean hasValidBiometricUserState(Context context, int userId) {
+        return getStateForUser(context, userId).isInvalidBiometricState();
+    }
+
+    @Override
+    public void deleteStateForUser(int userId) {
+        synchronized (this) {
+            FaceUserState state = mUserStates.get(userId);
+            if (state != null) {
+                state.deleteBiometricFile();
+                mUserStates.delete(userId);
+            }
+        }
+    }
+
     private FaceUserState getStateForUser(Context ctx, int userId) {
         synchronized (this) {
             FaceUserState state = mUserStates.get(userId);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
index ef7abdd..6cce722 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
@@ -227,6 +227,9 @@
                 onError(BiometricFaceConstants.FACE_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */);
                 mCallback.onClientFinished(this, false /* success */);
             }
+        } else {
+            Slog.e(TAG, "Cancellation signal is null");
+            mCallback.onClientFinished(this, false /* success */);
         }
     }
 
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInternalCleanupClient.java
index e75c6ab..964bf6c 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInternalCleanupClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInternalCleanupClient.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.content.Context;
 import android.hardware.biometrics.BiometricAuthenticator;
+import android.hardware.biometrics.BiometricsProtoEnums;
 import android.hardware.biometrics.face.IFace;
 import android.hardware.face.Face;
 import android.os.IBinder;
@@ -77,4 +78,9 @@
         FaceUtils.getInstance(getSensorId()).addBiometricForUser(
                 getContext(), getTargetUserId(), (Face) identifier);
     }
+
+    @Override
+    protected int getModality() {
+        return BiometricsProtoEnums.MODALITY_FACE;
+    }
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUtils.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUtils.java
index 0062d31..b8c06c7 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUtils.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUtils.java
@@ -140,6 +140,22 @@
         return getStateForUser(context, userId).isInvalidationInProgress();
     }
 
+    @Override
+    public boolean hasValidBiometricUserState(Context context, int userId) {
+        return getStateForUser(context, userId).isInvalidBiometricState();
+    }
+
+    @Override
+    public void deleteStateForUser(int userId) {
+        synchronized (this) {
+            FingerprintUserState state = mUserStates.get(userId);
+            if (state != null) {
+                state.deleteBiometricFile();
+                mUserStates.delete(userId);
+            }
+        }
+    }
+
     private FingerprintUserState getStateForUser(Context ctx, int userId) {
         synchronized (this) {
             FingerprintUserState state = mUserStates.get(userId);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
index d04afdb..dee4b4f 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
@@ -359,7 +359,8 @@
                 mCallback.onClientFinished(this, false /* success */);
             }
         } else {
-            Slog.e(TAG, "cancellation signal was null");
+            Slog.e(TAG, "Cancellation signal was null");
+            mCallback.onClientFinished(this, false /* success */);
         }
     }
 
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClient.java
index 5edc2ca..1fc5179 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClient.java
@@ -22,9 +22,11 @@
 import android.hardware.biometrics.BiometricsProtoEnums;
 import android.hardware.fingerprint.Fingerprint;
 import android.os.IBinder;
+import android.util.Slog;
 
 import com.android.server.biometrics.log.BiometricContext;
 import com.android.server.biometrics.log.BiometricLogger;
+import com.android.server.biometrics.sensors.BiometricNotificationUtils;
 import com.android.server.biometrics.sensors.BiometricUtils;
 import com.android.server.biometrics.sensors.InternalCleanupClient;
 import com.android.server.biometrics.sensors.InternalEnumerateClient;
@@ -42,6 +44,8 @@
 public class FingerprintInternalCleanupClient
         extends InternalCleanupClient<Fingerprint, AidlSession> {
 
+    private static final String TAG = "FingerprintInternalCleanupClient";
+
     public FingerprintInternalCleanupClient(@NonNull Context context,
             @NonNull Supplier<AidlSession> lazyDaemon,
             int userId, @NonNull String owner, int sensorId,
@@ -80,4 +84,16 @@
         FingerprintUtils.getInstance(getSensorId()).addBiometricForUser(
                 getContext(), getTargetUserId(), (Fingerprint) identifier);
     }
+
+    @Override
+    public void handleInvalidBiometricState() {
+        Slog.d(TAG, "Invalid fingerprint user state: delete the state.");
+        mBiometricUtils.deleteStateForUser(getTargetUserId());
+        BiometricNotificationUtils.showFingerprintLoeNotification(getContext());
+    }
+
+    @Override
+    protected int getModality() {
+        return BiometricsProtoEnums.MODALITY_FINGERPRINT;
+    }
 }
diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java
index 73aa14b..78f7187 100644
--- a/services/core/java/com/android/server/clipboard/ClipboardService.java
+++ b/services/core/java/com/android/server/clipboard/ClipboardService.java
@@ -684,7 +684,8 @@
                 if (clipboard == null) {
                     return null;
                 }
-                showAccessNotificationLocked(pkg, intendingUid, intendingUserId, clipboard);
+                showAccessNotificationLocked(
+                        pkg, intendingUid, intendingUserId, clipboard, deviceId);
                 notifyTextClassifierLocked(clipboard, pkg, intendingUid);
                 if (clipboard.primaryClip != null) {
                     scheduleAutoClear(userId, intendingUid, intendingDeviceId);
@@ -1438,7 +1439,7 @@
      */
     @GuardedBy("mLock")
     private void showAccessNotificationLocked(String callingPackage, int uid, @UserIdInt int userId,
-            Clipboard clipboard) {
+            Clipboard clipboard, int accessDeviceId) {
         if (clipboard.primaryClip == null) {
             return;
         }
@@ -1477,7 +1478,7 @@
             return;
         }
 
-        final ArraySet<Context> toastContexts = getToastContexts(clipboard);
+        final ArraySet<Context> toastContexts = getToastContexts(clipboard, accessDeviceId);
         Binder.withCleanCallingIdentity(() -> {
             try {
                 CharSequence callingAppLabel = mPm.getApplicationLabel(
@@ -1516,40 +1517,55 @@
      * If the clipboard is for a VirtualDevice, we attempt to return the single DisplayContext for
      * the focused VirtualDisplay for that device, but might need to return the contexts for
      * multiple displays if the VirtualDevice has several but none of them were focused.
+     *
+     * If the clipboard is NOT for a VirtualDevice, but it's being accessed from a VirtualDevice,
+     * this means that the clipboard is shared between the default and that device. In this case we
+     * need to show a toast in both places.
      */
-    private ArraySet<Context> getToastContexts(Clipboard clipboard) throws IllegalStateException {
+    private ArraySet<Context> getToastContexts(Clipboard clipboard, int accessDeviceId)
+            throws IllegalStateException {
         ArraySet<Context> contexts = new ArraySet<>();
-
-        if (mVdmInternal != null && clipboard.deviceId != DEVICE_ID_DEFAULT) {
-            DisplayManager displayManager = getContext().getSystemService(DisplayManager.class);
-
-            int topFocusedDisplayId = mWm.getTopFocusedDisplayId();
-            ArraySet<Integer> displayIds = mVdmInternal.getDisplayIdsForDevice(clipboard.deviceId);
-
-            if (displayIds.contains(topFocusedDisplayId)) {
-                Display display = displayManager.getDisplay(topFocusedDisplayId);
-                if (display != null) {
-                    contexts.add(getContext().createDisplayContext(display));
-                    return contexts;
-                }
-            }
-
-            for (int i = 0; i < displayIds.size(); i++) {
-                Display display = displayManager.getDisplay(displayIds.valueAt(i));
-                if (display != null) {
-                    contexts.add(getContext().createDisplayContext(display));
-                }
-            }
-            if (!contexts.isEmpty()) {
-                return contexts;
-            }
-            Slog.e(TAG, "getToastContexts Couldn't find any VirtualDisplays for VirtualDevice "
-                    + clipboard.deviceId);
-            // Since we couldn't find any VirtualDisplays to use at all, just fall through to using
-            // the default display below.
+        if (clipboard.deviceId == DEVICE_ID_DEFAULT || accessDeviceId == DEVICE_ID_DEFAULT) {
+            // Always show the toast on the default display when the default clipboard is accessed -
+            // also when the clipboard is shared with a virtual device and accessed from there.
+            // Same when any clipboard is accessed from the default device.
+            contexts.add(getContext());
         }
 
-        contexts.add(getContext());
+        if ((accessDeviceId == DEVICE_ID_DEFAULT && clipboard.deviceId == DEVICE_ID_DEFAULT)
+                || mVdmInternal == null) {
+            // No virtual devices involved.
+            return contexts;
+        }
+
+        // At this point the clipboard is either accessed from a virtual device, or it is a virtual
+        // device clipboard, so show a toast on the relevant virtual display(s).
+        DisplayManager displayManager = getContext().getSystemService(DisplayManager.class);
+        ArraySet<Integer> displayIds = mVdmInternal.getDisplayIdsForDevice(accessDeviceId);
+        int topFocusedDisplayId = mWm.getTopFocusedDisplayId();
+
+        if (displayIds.contains(topFocusedDisplayId)) {
+            Display display = displayManager.getDisplay(topFocusedDisplayId);
+            if (display != null) {
+                contexts.add(getContext().createDisplayContext(display));
+                return contexts;
+            }
+        }
+
+        for (int i = 0; i < displayIds.size(); i++) {
+            Display display = displayManager.getDisplay(displayIds.valueAt(i));
+            if (display != null) {
+                contexts.add(getContext().createDisplayContext(display));
+            }
+        }
+        if (contexts.isEmpty()) {
+            Slog.e(TAG, "getToastContexts Couldn't find any VirtualDisplays for VirtualDevice "
+                    + accessDeviceId);
+            // Since we couldn't find any VirtualDisplays to use at all, just fall through to using
+            // the default display below.
+            contexts.add(getContext());
+        }
+
         return contexts;
     }
 
diff --git a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
index 619aecf..38e6d82 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
@@ -50,8 +50,7 @@
 import android.hardware.devicestate.DeviceStateManagerInternal;
 import android.hardware.devicestate.IDeviceStateManager;
 import android.hardware.devicestate.IDeviceStateManagerCallback;
-import android.hardware.devicestate.feature.flags.FeatureFlags;
-import android.hardware.devicestate.feature.flags.FeatureFlagsImpl;
+import android.hardware.devicestate.feature.flags.Flags;
 import android.os.Binder;
 import android.os.Handler;
 import android.os.IBinder;
@@ -190,9 +189,6 @@
     @Nullable
     private OverrideRequest mRearDisplayPendingOverrideRequest;
 
-    @NonNull
-    private final FeatureFlags mFlags;
-
     @VisibleForTesting
     interface SystemPropertySetter {
         void setDebugTracingDeviceStateProperty(String value);
@@ -253,7 +249,6 @@
             @NonNull SystemPropertySetter systemPropertySetter) {
         super(context);
         mSystemPropertySetter = systemPropertySetter;
-        mFlags = new FeatureFlagsImpl();
         // We use the DisplayThread because this service indirectly drives
         // display (on/off) and window (position) events through its callbacks.
         DisplayThread displayThread = DisplayThread.get();
@@ -279,7 +274,7 @@
         publishBinderService(Context.DEVICE_STATE_SERVICE, mBinderService);
         publishLocalService(DeviceStateManagerInternal.class, new LocalService());
 
-        if (!mFlags.deviceStatePropertyMigration()) {
+        if (!Flags.deviceStatePropertyMigration()) {
             synchronized (mLock) {
                 readStatesAvailableForRequestFromApps();
                 mFoldedDeviceStates = readFoldedStates();
@@ -848,7 +843,7 @@
             OverrideRequest request = new OverrideRequest(token, callingPid, callingUid,
                     deviceState.get(), flags, OVERRIDE_REQUEST_TYPE_EMULATED_STATE);
 
-            if (mFlags.deviceStatePropertyMigration()) {
+            if (Flags.deviceStatePropertyMigration()) {
                 // If we don't have the CONTROL_DEVICE_STATE permission, we want to show the overlay
                 if (!hasControlDeviceStatePermission && deviceState.get().hasProperty(
                         PROPERTY_FEATURE_REAR_DISPLAY)) {
@@ -988,16 +983,16 @@
      * @param callingPid Process ID that is requesting this state change
      * @param state state that is being requested.
      */
-    private void assertCanRequestDeviceState(int callingPid, int callingUid, int state) {
+    private void enforceRequestDeviceStatePermitted(int callingPid, int callingUid, int state) {
         final boolean isTopApp = isTopApp(callingPid);
         final boolean isForegroundApp = isForegroundApp(callingPid, callingUid);
         final boolean isStateAvailableForAppRequests = isStateAvailableForAppRequests(state);
 
-        final boolean canRequestState = isTopApp
+        final boolean isAllowedToRequestState = isTopApp
                 && isForegroundApp
                 && isStateAvailableForAppRequests;
 
-        if (!canRequestState) {
+        if (!isAllowedToRequestState) {
             getContext().enforceCallingOrSelfPermission(CONTROL_DEVICE_STATE,
                     "Permission required to request device state, "
                             + "or the call must come from the top app "
@@ -1006,19 +1001,29 @@
     }
 
     /**
-     * Checks if the process can control the device state. If the calling process ID is
-     * not the top app, then check if this process holds the CONTROL_DEVICE_STATE permission.
+     * Checks if the process can cancel a device state request. If the calling process ID is not
+     * both the top app and foregrounded nor does the process ID and userID match the IDs that made
+     * the device state request, then check if this process holds the CONTROL_DEVICE_STATE
+     * permission.
      *
      * @param callingPid Process ID that is requesting this state change
      * @param callingUid UID that is requesting this state change
      */
-    private void assertCanControlDeviceState(int callingPid, int callingUid) {
+    private void enforceCancelDeviceStatePermitted(int callingPid, int callingUid) {
         final boolean isTopApp = isTopApp(callingPid);
         final boolean isForegroundApp = isForegroundApp(callingPid, callingUid);
 
-        final boolean canControlState = isTopApp && isForegroundApp;
+        boolean isAllowedToControlState = isTopApp && isForegroundApp;
 
-        if (!canControlState) {
+        if (Flags.deviceStateRequesterCancelState()) {
+            synchronized (mLock) {
+                isAllowedToControlState =
+                        isAllowedToControlState || doCallingIdsMatchOverrideRequestIdsLocked(
+                                callingPid, callingUid);
+            }
+        }
+
+        if (!isAllowedToControlState) {
             getContext().enforceCallingOrSelfPermission(CONTROL_DEVICE_STATE,
                     "Permission required to request device state, "
                             + "or the call must come from the top app.");
@@ -1052,9 +1057,19 @@
         return topApp != null && topApp.getPid() == callingPid;
     }
 
+    /**
+     * Returns if the provided {@code callingPid} and {@code callingUid} match the same id's that
+     * requested the current device state override.
+     */
+    @GuardedBy("mLock")
+    private boolean doCallingIdsMatchOverrideRequestIdsLocked(int callingPid, int callingUid) {
+        OverrideRequest request = mActiveOverride.orElse(null);
+        return request != null && request.getPid() == callingPid && request.getUid() == callingUid;
+    }
+
     private boolean isStateAvailableForAppRequests(int state) {
         synchronized (mLock) {
-            if (mFlags.deviceStatePropertyMigration()) {
+            if (Flags.deviceStatePropertyMigration()) {
                 Optional<DeviceState> deviceState =  getStateLocked(state);
                 return deviceState.isPresent() && deviceState.get().hasProperty(
                         PROPERTY_POLICY_AVAILABLE_FOR_APP_REQUEST);
@@ -1122,7 +1137,7 @@
      */
     @GuardedBy("mLock")
     private boolean isDeviceOpeningLocked(int newBaseState) {
-        if (mFlags.deviceStatePropertyMigration()) {
+        if (Flags.deviceStatePropertyMigration()) {
             final DeviceState currentBaseState = mBaseState.orElse(INVALID_DEVICE_STATE);
             final DeviceState newDeviceBaseState = getStateLocked(newBaseState).orElse(
                     INVALID_DEVICE_STATE);
@@ -1293,7 +1308,7 @@
             // Allow top processes to request a device state change
             // If the calling process ID is not the top app, then we check if this process
             // holds a permission to CONTROL_DEVICE_STATE
-            assertCanRequestDeviceState(callingPid, callingUid, state);
+            enforceRequestDeviceStatePermitted(callingPid, callingUid, state);
 
             if (token == null) {
                 throw new IllegalArgumentException("Request token must not be null.");
@@ -1318,7 +1333,7 @@
             // Allow top processes to cancel a device state change
             // If the calling process ID is not the top app, then we check if this process
             // holds a permission to CONTROL_DEVICE_STATE
-            assertCanControlDeviceState(callingPid, callingUid);
+            enforceCancelDeviceStatePermitted(callingPid, callingUid);
 
             final long callingIdentity = Binder.clearCallingIdentity();
             try {
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index 7b5cff7..226bdf5 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -579,6 +579,14 @@
         return mCurrentBrightnessMapper.getMode();
     }
 
+    /**
+     * @return The preset for this mapping strategy. Presets are used on devices that allow users
+     * to choose from a set of predefined options in display auto-brightness settings.
+     */
+    public int getPreset() {
+        return mCurrentBrightnessMapper.getPreset();
+    }
+
     public boolean isInIdleMode() {
         return mCurrentBrightnessMapper.getMode() == AUTO_BRIGHTNESS_MODE_IDLE;
     }
diff --git a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
index 8405e0a..b0507fb 100644
--- a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
+++ b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
@@ -140,10 +140,10 @@
             builder.setShortTermModelLowerLuxMultiplier(SHORT_TERM_MODEL_THRESHOLD_RATIO);
             builder.setShortTermModelUpperLuxMultiplier(SHORT_TERM_MODEL_THRESHOLD_RATIO);
             return new PhysicalMappingStrategy(builder.build(), nitsRange, brightnessRange,
-                    autoBrightnessAdjustmentMaxGamma, mode, displayWhiteBalanceController);
+                    autoBrightnessAdjustmentMaxGamma, mode, preset, displayWhiteBalanceController);
         } else if (isValidMapping(luxLevels, brightnessLevels)) {
             return new SimpleMappingStrategy(luxLevels, brightnessLevels,
-                    autoBrightnessAdjustmentMaxGamma, shortTermModelTimeout, mode);
+                    autoBrightnessAdjustmentMaxGamma, shortTermModelTimeout, mode, preset);
         } else {
             return null;
         }
@@ -394,6 +394,12 @@
     abstract int getMode();
 
     /**
+     * @return The preset for this mapping strategy. Presets are used on devices that allow users
+     * to choose from a set of predefined options in display auto-brightness settings.
+     */
+    abstract int getPreset();
+
+    /**
      * Check if the short term model should be reset given the anchor lux the last
      * brightness change was made at and the current ambient lux.
      */
@@ -598,6 +604,8 @@
         @AutomaticBrightnessController.AutomaticBrightnessMode
         private final int mMode;
 
+        private final int mPreset;
+
         private Spline mSpline;
         private float mMaxGamma;
         private float mAutoBrightnessAdjustment;
@@ -606,7 +614,8 @@
         private long mShortTermModelTimeout;
 
         private SimpleMappingStrategy(float[] lux, float[] brightness, float maxGamma,
-                long timeout, @AutomaticBrightnessController.AutomaticBrightnessMode int mode) {
+                long timeout, @AutomaticBrightnessController.AutomaticBrightnessMode int mode,
+                int preset) {
             Preconditions.checkArgument(lux.length != 0 && brightness.length != 0,
                     "Lux and brightness arrays must not be empty!");
             Preconditions.checkArgument(lux.length == brightness.length,
@@ -633,6 +642,7 @@
             computeSpline();
             mShortTermModelTimeout = timeout;
             mMode = mode;
+            mPreset = preset;
         }
 
         @Override
@@ -766,6 +776,11 @@
         }
 
         @Override
+        int getPreset() {
+            return mPreset;
+        }
+
+        @Override
         float getUserLux() {
             return mUserLux;
         }
@@ -837,6 +852,8 @@
         @AutomaticBrightnessController.AutomaticBrightnessMode
         private final int mMode;
 
+        private final int mPreset;
+
         // Previous short-term models and the times that they were computed stored for debugging
         // purposes
         private List<Spline> mPreviousBrightnessSplines = new ArrayList<>();
@@ -846,7 +863,7 @@
 
         public PhysicalMappingStrategy(BrightnessConfiguration config, float[] nits,
                 float[] brightness, float maxGamma,
-                @AutomaticBrightnessController.AutomaticBrightnessMode int mode,
+                @AutomaticBrightnessController.AutomaticBrightnessMode int mode, int preset,
                 @Nullable DisplayWhiteBalanceController displayWhiteBalanceController) {
 
             Preconditions.checkArgument(nits.length != 0 && brightness.length != 0,
@@ -860,6 +877,7 @@
                     PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX, "brightness");
 
             mMode = mode;
+            mPreset = preset;
             mMaxGamma = maxGamma;
             mAutoBrightnessAdjustment = 0;
             mUserLux = INVALID_LUX;
@@ -1073,6 +1091,11 @@
         }
 
         @Override
+        int getPreset() {
+            return mPreset;
+        }
+
+        @Override
         float getUserLux() {
             return mUserLux;
         }
diff --git a/services/core/java/com/android/server/display/BrightnessRangeController.java b/services/core/java/com/android/server/display/BrightnessRangeController.java
index 515e704..8a3e392 100644
--- a/services/core/java/com/android/server/display/BrightnessRangeController.java
+++ b/services/core/java/com/android/server/display/BrightnessRangeController.java
@@ -60,7 +60,7 @@
         mModeChangeCallback = modeChangeCallback;
         mHdrClamper = hdrClamper;
         mNormalBrightnessModeController = normalBrightnessModeController;
-        mUseHdrClamper = flags.isHdrClamperEnabled();
+        mUseHdrClamper = flags.isHdrClamperEnabled() && !flags.useNewHdrBrightnessModifier();
         mUseNbmController = flags.isNbmControllerEnabled();
         if (mUseNbmController) {
             mNormalBrightnessModeController.resetNbmData(
diff --git a/services/core/java/com/android/server/display/DisplayBrightnessState.java b/services/core/java/com/android/server/display/DisplayBrightnessState.java
index 222c5a8..dc611fc 100644
--- a/services/core/java/com/android/server/display/DisplayBrightnessState.java
+++ b/services/core/java/com/android/server/display/DisplayBrightnessState.java
@@ -332,6 +332,16 @@
         }
 
         /**
+         * Sets the {@link BrightnessReason} using the int-based reason enum. This is a convenience
+         * function so we don't have to type out the constructor syntax everywhere.
+         *
+         * @param brightnessReason The int-based brightness enum.
+         */
+        public Builder setBrightnessReason(int brightnessReason) {
+            return setBrightnessReason(new BrightnessReason(brightnessReason));
+        }
+
+        /**
          * Gets the {@link com.android.server.display.brightness.strategy.DisplayBrightnessStrategy}
          * name
          */
diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
index ed6ed60..cc115f1 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
@@ -588,22 +588,43 @@
  *         <minorVersion>0</minorVersion>
  *     </usiVersion>
  *     <evenDimmer enabled="true">
- *       <transitionPoint>0.1</transitionPoint>
- *
- *       <nits>0.2</nits>
- *       <nits>2.0</nits>
- *       <nits>500.0</nits>
- *       <nits>1000.0</nits>
- *
- *       <backlight>0</backlight>
- *       <backlight>0.0001</backlight>
- *       <backlight>0.5</backlight>
- *       <backlight>1.0</backlight>
- *
- *       <brightness>0</brightness>
- *       <brightness>0.1</brightness>
- *       <brightness>0.5</brightness>
- *       <brightness>1.0</brightness>
+ *         <transitionPoint>0.1</transitionPoint>
+ *         <brightnessMapping>
+ *             <brightnessPoint>
+ *                 <nits>0.2</nits>
+ *                 <backlight>0</backlight>
+ *                 <brightness>0</brightness>
+ *                 </brightnessPoint>
+ *             <brightnessPoint>
+ *                 <nits>2.0</nits>
+ *                 <backlight>0.01</backlight>
+ *                 <brightness>0.002</brightness>
+ *             </brightnessPoint>
+ *             <brightnessPoint>
+ *                 <nits>500.0</nits>
+ *                 <backlight>0.5</backlight>
+ *                 <brightness>0.5</brightness>
+ *             </brightnessPoint>
+ *             <brightnessPoint>
+ *                 <nits>1000</nits>
+ *                 <backlight>1.0</backlight>
+ *                 <brightness>1.0</brightness>
+ *             </brightnessPoint>
+ *         </brightnessMapping>
+ *         <luxToMinimumNitsMap>
+ *             <point>
+ *                 <value>10</value>
+ *                 <nits>0.3</nits>
+ *             </point>
+ *             <point>
+ *                 <value>50</value>
+ *                 <nits>0.7</nits>
+ *             </point>
+ *             <point>
+ *                 <value>100</value>
+ *                 <nits>1.0</nits>
+ *             </point>
+ *         </luxToMinimumNitsMap>
  *     </evenDimmer>
  *     <screenBrightnessCapForWearBedtimeMode>0.1</screenBrightnessCapForWearBedtimeMode>
  *     <idleScreenRefreshRateTimeout>
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 2cec869..9e905ab 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -722,6 +722,7 @@
             if (userSwitching) {
                 mCurrentUserId = newUserId;
             }
+            mDisplayModeDirector.onSwitchUser();
             mLogicalDisplayMapper.forEachLocked(logicalDisplay -> {
                 if (logicalDisplay.getDisplayInfoLocked().type != Display.TYPE_INTERNAL) {
                     return;
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 1177be2..53d6768 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -17,6 +17,7 @@
 package com.android.server.display;
 
 import static android.hardware.display.DisplayManagerInternal.DisplayPowerRequest.POLICY_DOZE;
+import static android.hardware.display.DisplayManagerInternal.DisplayPowerRequest.POLICY_OFF;
 
 import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DEFAULT;
 import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DOZE;
@@ -702,6 +703,17 @@
     private void handleOnSwitchUser(@UserIdInt int newUserId, int userSerial, float newBrightness) {
         Slog.i(mTag, "Switching user newUserId=" + newUserId + " userSerial=" + userSerial
                 + " newBrightness=" + newBrightness);
+
+        if (mAutomaticBrightnessController != null) {
+            int autoBrightnessPreset = Settings.System.getIntForUser(mContext.getContentResolver(),
+                    Settings.System.SCREEN_BRIGHTNESS_FOR_ALS,
+                    Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_NORMAL,
+                    UserHandle.USER_CURRENT);
+            if (autoBrightnessPreset != mAutomaticBrightnessController.getPreset()) {
+                setUpAutoBrightness(mContext, mHandler);
+            }
+        }
+
         handleBrightnessModeChange();
         if (mBrightnessTracker != null) {
             mBrightnessTracker.onSwitchUser(newUserId);
@@ -714,6 +726,7 @@
         if (mAutomaticBrightnessController != null) {
             mAutomaticBrightnessController.resetShortTermModel();
         }
+        mBrightnessClamperController.onUserSwitch();
         sendUpdatePowerState();
     }
 
@@ -790,15 +803,27 @@
     @Override
     public void overrideDozeScreenState(int displayState, @Display.StateReason int reason) {
         Slog.i(TAG, "New offload doze override: " + Display.stateToString(displayState));
-        mHandler.postAtTime(() -> {
-            if (mDisplayOffloadSession == null
-                    || !(DisplayOffloadSession.isSupportedOffloadState(displayState)
-                            || displayState == Display.STATE_UNKNOWN)) {
-                return;
+        if (mDisplayOffloadSession != null
+                && DisplayOffloadSession.isSupportedOffloadState(displayState)
+                && displayState != Display.STATE_UNKNOWN) {
+            if (mFlags.isOffloadDozeOverrideHoldsWakelockEnabled()) {
+                boolean acquired = mWakelockController.acquireWakelock(
+                        WakelockController.WAKE_LOCK_OVERRIDE_DOZE_SCREEN_STATE);
+                if (!acquired) {
+                    Slog.i(TAG, "A request to override the doze screen state is already "
+                            + "under process");
+                    return;
+                }
             }
-            mDisplayStateController.overrideDozeScreenState(displayState, reason);
-            updatePowerState();
-        }, mClock.uptimeMillis());
+            mHandler.postAtTime(() -> {
+                mDisplayStateController.overrideDozeScreenState(displayState, reason);
+                updatePowerState();
+                if (mFlags.isOffloadDozeOverrideHoldsWakelockEnabled()) {
+                    mWakelockController.releaseWakelock(
+                            WakelockController.WAKE_LOCK_OVERRIDE_DOZE_SCREEN_STATE);
+                }
+            }, mClock.uptimeMillis());
+        }
     }
 
     @Override
@@ -1009,7 +1034,7 @@
         if (mFlags.areAutoBrightnessModesEnabled()) {
             mContext.getContentResolver().registerContentObserver(
                     Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_FOR_ALS),
-                    /* notifyForDescendants= */ false, mSettingsObserver, UserHandle.USER_CURRENT);
+                    /* notifyForDescendants= */ false, mSettingsObserver, UserHandle.USER_ALL);
         }
         handleBrightnessModeChange();
     }
@@ -1325,30 +1350,6 @@
             initialize(readyToUpdateDisplayState() ? state : Display.STATE_UNKNOWN);
         }
 
-        if (mFlags.isOffloadDozeOverrideHoldsWakelockEnabled()) {
-            // Sometimes, a display-state change can come without an associated PowerRequest,
-            // as with DisplayOffload.  For those cases, we have to make sure to also mark the
-            // display as "not ready" so that we can inform power-manager when the state-change is
-            // complete.
-            if (mPowerState.getScreenState() != state) {
-                final boolean wasReady;
-                synchronized (mLock) {
-                    wasReady = mDisplayReadyLocked;
-                    mDisplayReadyLocked = false;
-                    mustNotify = true;
-                }
-
-                if (wasReady) {
-                    // If we went from ready to not-ready from the state-change (instead of a
-                    // PowerRequest) there's a good chance that nothing is keeping PowerManager
-                    // from suspending. Grab the unfinished business suspend blocker to keep the
-                    // device awake until the display-state change goes into effect.
-                    mWakelockController.acquireWakelock(
-                            WakelockController.WAKE_LOCK_UNFINISHED_BUSINESS);
-                }
-            }
-        }
-
         // Animate the screen state change unless already animating.
         // The transition may be deferred, so after this point we will use the
         // actual state instead of the desired one.
@@ -1381,8 +1382,8 @@
             if (mScreenOffBrightnessSensorController != null) {
                 mScreenOffBrightnessSensorController
                         .setLightSensorEnabled(displayBrightnessState.getShouldUseAutoBrightness()
-                        && mIsEnabled && (state == Display.STATE_OFF
-                        || (state == Display.STATE_DOZE && !allowAutoBrightnessWhileDozing))
+                        && mIsEnabled && (mPowerRequest.policy == POLICY_OFF
+                        || (mPowerRequest.policy == POLICY_DOZE && !allowAutoBrightnessWhileDozing))
                         && mLeadDisplayId == Layout.NO_LEAD_DISPLAY);
             }
         }
@@ -1755,6 +1756,7 @@
         mTempBrightnessEvent.setPhysicalDisplayId(mUniqueDisplayId);
         mTempBrightnessEvent.setPhysicalDisplayName(mPhysicalDisplayName);
         mTempBrightnessEvent.setDisplayState(state);
+        mTempBrightnessEvent.setDisplayStateReason(stateAndReason.second);
         mTempBrightnessEvent.setDisplayPolicy(mPowerRequest.policy);
         mTempBrightnessEvent.setReason(mBrightnessReason);
         mTempBrightnessEvent.setHbmMax(hbmMax);
diff --git a/services/core/java/com/android/server/display/ExternalDisplayStatsService.java b/services/core/java/com/android/server/display/ExternalDisplayStatsService.java
index f6f23d9..608fb35 100644
--- a/services/core/java/com/android/server/display/ExternalDisplayStatsService.java
+++ b/services/core/java/com/android/server/display/ExternalDisplayStatsService.java
@@ -518,18 +518,24 @@
     private void logExternalDisplayIdleStarted() {
         synchronized (mExternalDisplayStates) {
             for (var i = 0; i < mExternalDisplayStates.size(); i++) {
-                mInjector.writeLog(FrameworkStatsLog.EXTERNAL_DISPLAY_STATE_CHANGED,
-                        KEYGUARD, i + 1, mIsExternalDisplayUsedForAudio);
-                if (DEBUG) {
-                    final int displayId = mExternalDisplayStates.keyAt(i);
-                    final int state = mExternalDisplayStates.get(displayId, DISCONNECTED_STATE);
-                    Slog.d(TAG, "logExternalDisplayIdleStarted"
-                                        + " displayId=" + displayId
-                                        + " currentState=" + state
-                                        + " countOfExternalDisplays=" + (i + 1)
-                                        + " state=" + KEYGUARD
-                                        + " mIsExternalDisplayUsedForAudio="
-                                        + mIsExternalDisplayUsedForAudio);
+                final int displayId = mExternalDisplayStates.keyAt(i);
+                final int state = mExternalDisplayStates.get(displayId, DISCONNECTED_STATE);
+                // Don't try to stop "connected" session by keyguard event.
+                // There is no purpose to measure how long keyguard is shown while external
+                // display is connected but not used for mirroring or extended display.
+                // Therefore there no need to log this event.
+                if (state != DISCONNECTED_STATE && state != CONNECTED_STATE) {
+                    mInjector.writeLog(FrameworkStatsLog.EXTERNAL_DISPLAY_STATE_CHANGED,
+                            KEYGUARD, i + 1, mIsExternalDisplayUsedForAudio);
+                    if (DEBUG) {
+                        Slog.d(TAG, "logExternalDisplayIdleStarted"
+                                            + " displayId=" + displayId
+                                            + " currentState=" + state
+                                            + " countOfExternalDisplays=" + (i + 1)
+                                            + " state=" + KEYGUARD
+                                            + " mIsExternalDisplayUsedForAudio="
+                                            + mIsExternalDisplayUsedForAudio);
+                    }
                 }
             }
         }
@@ -540,7 +546,11 @@
             for (var i = 0; i < mExternalDisplayStates.size(); i++) {
                 final int displayId = mExternalDisplayStates.keyAt(i);
                 final int state = mExternalDisplayStates.get(displayId, DISCONNECTED_STATE);
-                if (state == DISCONNECTED_STATE) {
+                // No need to restart "connected" session after keyguard is stopped.
+                // This is because the connection is continuous even if keyguard is shown.
+                // In case in the future keyguard needs to be measured also while display
+                // is not used, then a 'keyguard finished' event needs to be emitted in this case.
+                if (state == DISCONNECTED_STATE || state == CONNECTED_STATE) {
                     return;
                 }
                 mInjector.writeLog(FrameworkStatsLog.EXTERNAL_DISPLAY_STATE_CHANGED,
diff --git a/services/core/java/com/android/server/display/WakelockController.java b/services/core/java/com/android/server/display/WakelockController.java
index 7bc7971..5b0229c 100644
--- a/services/core/java/com/android/server/display/WakelockController.java
+++ b/services/core/java/com/android/server/display/WakelockController.java
@@ -20,6 +20,7 @@
 import android.hardware.display.DisplayManagerInternal;
 import android.util.Slog;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.display.utils.DebugUtils;
 
@@ -37,7 +38,8 @@
     public static final int WAKE_LOCK_PROXIMITY_NEGATIVE = 2;
     public static final int WAKE_LOCK_PROXIMITY_DEBOUNCE = 3;
     public static final int WAKE_LOCK_STATE_CHANGED = 4;
-    public static final int WAKE_LOCK_UNFINISHED_BUSINESS = 5;
+    public static final int WAKE_LOCK_OVERRIDE_DOZE_SCREEN_STATE = 5;
+    public static final int WAKE_LOCK_UNFINISHED_BUSINESS = 6;
 
     @VisibleForTesting
     static final int WAKE_LOCK_MAX = WAKE_LOCK_UNFINISHED_BUSINESS;
@@ -53,18 +55,23 @@
             WAKE_LOCK_PROXIMITY_NEGATIVE,
             WAKE_LOCK_PROXIMITY_DEBOUNCE,
             WAKE_LOCK_STATE_CHANGED,
+            WAKE_LOCK_OVERRIDE_DOZE_SCREEN_STATE,
             WAKE_LOCK_UNFINISHED_BUSINESS
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface WAKE_LOCK_TYPE {
     }
 
+    private final Object mLock = new Object();
+
     // Asynchronous callbacks into the power manager service.
     // Only invoked from the handler thread while no locks are held.
     private final DisplayManagerInternal.DisplayPowerCallbacks mDisplayPowerCallbacks;
 
     // Identifiers for suspend blocker acquisition requests
     private final String mSuspendBlockerIdUnfinishedBusiness;
+    @GuardedBy("mLock")
+    private final String mSuspendBlockerOverrideDozeScreenState;
     private final String mSuspendBlockerIdOnStateChanged;
     private final String mSuspendBlockerIdProxPositive;
     private final String mSuspendBlockerIdProxNegative;
@@ -73,6 +80,10 @@
     // True if we have unfinished business and are holding a suspend-blocker.
     private boolean mUnfinishedBusiness;
 
+    // True if we have are holding a suspend-blocker to override the doze screen state.
+    @GuardedBy("mLock")
+    private boolean mIsOverrideDozeScreenStateAcquired;
+
     // True if we have have debounced the proximity change impact and are holding a suspend-blocker.
     private boolean mHasProximityDebounced;
 
@@ -108,6 +119,7 @@
         mTag = TAG + "[" + mDisplayId + "]";
         mDisplayPowerCallbacks = callbacks;
         mSuspendBlockerIdUnfinishedBusiness = "[" + displayId + "]unfinished business";
+        mSuspendBlockerOverrideDozeScreenState =  "[" + displayId + "]override doze screen state";
         mSuspendBlockerIdOnStateChanged = "[" + displayId + "]on state changed";
         mSuspendBlockerIdProxPositive = "[" + displayId + "]prox positive";
         mSuspendBlockerIdProxNegative = "[" + displayId + "]prox negative";
@@ -154,6 +166,10 @@
                 return acquireProxDebounceSuspendBlocker();
             case WAKE_LOCK_STATE_CHANGED:
                 return acquireStateChangedSuspendBlocker();
+            case WAKE_LOCK_OVERRIDE_DOZE_SCREEN_STATE:
+                synchronized (mLock) {
+                    return acquireOverrideDozeScreenStateSuspendBlockerLocked();
+                }
             case WAKE_LOCK_UNFINISHED_BUSINESS:
                 return acquireUnfinishedBusinessSuspendBlocker();
             default:
@@ -171,6 +187,10 @@
                 return releaseProxDebounceSuspendBlocker();
             case WAKE_LOCK_STATE_CHANGED:
                 return releaseStateChangedSuspendBlocker();
+            case WAKE_LOCK_OVERRIDE_DOZE_SCREEN_STATE:
+                synchronized (mLock) {
+                    return releaseOverrideDozeScreenStateSuspendBlockerLocked();
+                }
             case WAKE_LOCK_UNFINISHED_BUSINESS:
                 return releaseUnfinishedBusinessSuspendBlocker();
             default:
@@ -220,6 +240,42 @@
     }
 
     /**
+     * Acquires the suspend blocker to override the doze screen state and notifies the
+     * PowerManagerService about the changes. Note that this utility is syncronized because a
+     * request to override the doze screen state can come from a non-power thread.
+     */
+    @GuardedBy("mLock")
+    private boolean acquireOverrideDozeScreenStateSuspendBlockerLocked() {
+        // Grab a wake lock if we have unfinished business.
+        if (!mIsOverrideDozeScreenStateAcquired) {
+            if (DEBUG) {
+                Slog.d(mTag, "Acquiring suspend blocker to override the doze screen state...");
+            }
+            mDisplayPowerCallbacks.acquireSuspendBlocker(mSuspendBlockerOverrideDozeScreenState);
+            mIsOverrideDozeScreenStateAcquired = true;
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Releases the override doze screen state suspend blocker and notifies the PowerManagerService
+     * about the changes.
+     */
+    @GuardedBy("mLock")
+    private boolean releaseOverrideDozeScreenStateSuspendBlockerLocked() {
+        if (mIsOverrideDozeScreenStateAcquired) {
+            if (DEBUG) {
+                Slog.d(mTag, "Finished overriding doze screen state...");
+            }
+            mDisplayPowerCallbacks.releaseSuspendBlocker(mSuspendBlockerOverrideDozeScreenState);
+            mIsOverrideDozeScreenStateAcquired = false;
+            return true;
+        }
+        return false;
+    }
+
+    /**
      * Acquires the unfinished business wakelock and notifies the PowerManagerService about the
      * changes.
      */
@@ -366,6 +422,7 @@
         pw.println("  mOnStateChangePending=" + isOnStateChangedPending());
         pw.println("  mOnProximityPositiveMessages=" + isProximityPositiveAcquired());
         pw.println("  mOnProximityNegativeMessages=" + isProximityNegativeAcquired());
+        pw.println("  mIsOverrideDozeScreenStateAcquired=" + isOverrideDozeScreenStateAcquired());
     }
 
     @VisibleForTesting
@@ -394,6 +451,13 @@
     }
 
     @VisibleForTesting
+    String getSuspendBlockerOverrideDozeScreenState() {
+        synchronized (mLock) {
+            return mSuspendBlockerOverrideDozeScreenState;
+        }
+    }
+
+    @VisibleForTesting
     boolean hasUnfinishedBusiness() {
         return mUnfinishedBusiness;
     }
@@ -417,4 +481,11 @@
     boolean hasProximitySensorDebounced() {
         return mHasProximityDebounced;
     }
+
+    @VisibleForTesting
+    boolean isOverrideDozeScreenStateAcquired() {
+        synchronized (mLock) {
+            return mIsOverrideDozeScreenStateAcquired;
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/display/brightness/BrightnessEvent.java b/services/core/java/com/android/server/display/brightness/BrightnessEvent.java
index 5cc603c..ad57ebf 100644
--- a/services/core/java/com/android/server/display/brightness/BrightnessEvent.java
+++ b/services/core/java/com/android/server/display/brightness/BrightnessEvent.java
@@ -52,6 +52,8 @@
     private String mPhysicalDisplayId;
     private String mPhysicalDisplayName;
     private int mDisplayState;
+    @Display.StateReason
+    private int mDisplayStateReason;
     private int mDisplayPolicy;
     private long mTime;
     private float mLux;
@@ -96,6 +98,7 @@
         mPhysicalDisplayId = that.getPhysicalDisplayId();
         mPhysicalDisplayName = that.getPhysicalDisplayName();
         mDisplayState = that.mDisplayState;
+        mDisplayStateReason = that.mDisplayStateReason;
         mDisplayPolicy = that.mDisplayPolicy;
         mTime = that.getTime();
         // Lux values
@@ -133,6 +136,7 @@
         mPhysicalDisplayId = "";
         mPhysicalDisplayName = "";
         mDisplayState = Display.STATE_UNKNOWN;
+        mDisplayStateReason = Display.STATE_REASON_UNKNOWN;
         mDisplayPolicy = POLICY_OFF;
         // Lux values
         mLux = INVALID_LUX;
@@ -176,6 +180,7 @@
                 && mPhysicalDisplayId.equals(that.mPhysicalDisplayId)
                 && mPhysicalDisplayName.equals(that.mPhysicalDisplayName)
                 && mDisplayState == that.mDisplayState
+                && mDisplayStateReason == that.mDisplayStateReason
                 && mDisplayPolicy == that.mDisplayPolicy
                 && Float.floatToRawIntBits(mLux) == Float.floatToRawIntBits(that.mLux)
                 && Float.floatToRawIntBits(mPreThresholdLux)
@@ -221,6 +226,7 @@
                 + ", reason=" + mReason.toString(mAdjustmentFlags)
                 + ", strat=" + mDisplayBrightnessStrategyName
                 + ", state=" + Display.stateToString(mDisplayState)
+                + ", stateReason=" + Display.stateReasonToString(mDisplayStateReason)
                 + ", policy=" + policyToString(mDisplayPolicy)
                 + ", flags=" + flagsToString()
                 // Autobrightness
@@ -293,6 +299,10 @@
         mDisplayState = state;
     }
 
+    public void setDisplayStateReason(@Display.StateReason int reason) {
+        mDisplayStateReason = reason;
+    }
+
     public void setDisplayPolicy(int policy) {
         mDisplayPolicy = policy;
     }
diff --git a/services/core/java/com/android/server/display/brightness/BrightnessReason.java b/services/core/java/com/android/server/display/brightness/BrightnessReason.java
index 9bf10a7..9a0ee03 100644
--- a/services/core/java/com/android/server/display/brightness/BrightnessReason.java
+++ b/services/core/java/com/android/server/display/brightness/BrightnessReason.java
@@ -16,6 +16,7 @@
 
 package com.android.server.display.brightness;
 
+import android.annotation.Nullable;
 import android.util.Slog;
 
 import java.util.Objects;
@@ -66,6 +67,16 @@
     // Any number of MODIFIER_*
     private int mModifier;
 
+    // Tag used to identify the source of the brightness (usually a specific activity/window).
+    private CharSequence mTag;
+
+    public BrightnessReason() {
+    }
+
+    public BrightnessReason(int reason) {
+        setReason(reason);
+    }
+
     /**
      * A utility to clone a BrightnessReason from another BrightnessReason event
      *
@@ -74,6 +85,7 @@
     public void set(BrightnessReason other) {
         setReason(other == null ? REASON_UNKNOWN : other.mReason);
         setModifier(other == null ? 0 : other.mModifier);
+        setTag(other == null ? null : other.mTag);
     }
 
     /**
@@ -85,19 +97,20 @@
         setModifier(modifier | this.mModifier);
     }
 
-
     @Override
     public boolean equals(Object obj) {
         if (!(obj instanceof BrightnessReason)) {
             return false;
         }
         BrightnessReason other = (BrightnessReason) obj;
-        return other.mReason == mReason && other.mModifier == mModifier;
+        return other.mReason == mReason
+                && other.mModifier == mModifier
+                && Objects.equals(other.mTag != null ? other.mTag.toString() : null, mTag);
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(mReason, mModifier);
+        return Objects.hash(mReason, mModifier, mTag);
     }
 
     @Override
@@ -115,6 +128,11 @@
     public String toString(int adjustments) {
         final StringBuilder sb = new StringBuilder();
         sb.append(reasonToString(mReason));
+
+        if (mTag != null) {
+            sb.append("(").append(mTag).append(")");
+        }
+
         sb.append(" [");
         if ((adjustments & ADJUSTMENT_AUTO_TEMP) != 0) {
             sb.append(" temp_adj");
@@ -149,8 +167,23 @@
         return sb.toString();
     }
 
+    public void setTag(@Nullable CharSequence tag) {
+        mTag = tag;
+    }
+
     /**
-     * A utility to set the reason of the BrightnessReason object
+     * Gets the tag to identify who requested the brightness.
+     */
+    @Nullable public CharSequence getTag() {
+        return mTag;
+    }
+
+    public int getReason() {
+        return mReason;
+    }
+
+    /**
+     * Sets the reason of the BrightnessReason object
      *
      * @param reason The value to which the reason is to be updated.
      */
@@ -162,16 +195,12 @@
         }
     }
 
-    public int getReason() {
-        return mReason;
-    }
-
     public int getModifier() {
         return mModifier;
     }
 
     /**
-     * A utility to set the modified of the current BrightnessReason object
+     * Sets the modifier bitflags of the current BrightnessReason object
      *
      * @param modifier The value to which the modifier is to be updated
      */
diff --git a/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java b/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java
index c632e77..e157b05 100644
--- a/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java
+++ b/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java
@@ -359,11 +359,11 @@
     public void setUpAutoBrightness(AutomaticBrightnessController automaticBrightnessController,
             SensorManager sensorManager,
             DisplayDeviceConfig displayDeviceConfig, Handler handler,
-            BrightnessMappingStrategy brightnessMappingStrategy, boolean isEnabled,
+            BrightnessMappingStrategy brightnessMappingStrategy, boolean isDisplayEnabled,
             int leadDisplayId) {
         setAutomaticBrightnessController(automaticBrightnessController);
         setUpAutoBrightnessFallbackStrategy(sensorManager, displayDeviceConfig, handler,
-                brightnessMappingStrategy, isEnabled, leadDisplayId);
+                brightnessMappingStrategy, isDisplayEnabled, leadDisplayId);
     }
 
     /**
@@ -534,14 +534,14 @@
 
     private void setUpAutoBrightnessFallbackStrategy(SensorManager sensorManager,
             DisplayDeviceConfig displayDeviceConfig, Handler handler,
-            BrightnessMappingStrategy brightnessMappingStrategy, boolean isEnabled,
+            BrightnessMappingStrategy brightnessMappingStrategy, boolean isDisplayEnabled,
             int leadDisplayId) {
         AutoBrightnessFallbackStrategy autoBrightnessFallbackStrategy =
                 getAutoBrightnessFallbackStrategy();
         if (autoBrightnessFallbackStrategy != null) {
             autoBrightnessFallbackStrategy.setupAutoBrightnessFallbackSensor(
                     sensorManager, displayDeviceConfig, handler, brightnessMappingStrategy,
-                    isEnabled, leadDisplayId);
+                    isDisplayEnabled, leadDisplayId);
         }
     }
 
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 9324fc1..59fffe7 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
@@ -71,6 +71,7 @@
 
     private final List<DisplayDeviceDataListener> mDisplayDeviceDataListeners = new ArrayList<>();
     private final List<StatefulModifier> mStatefulModifiers = new ArrayList<>();
+    private final List<UserSwitchListener> mUserSwitchListeners = new ArrayList<>();
     private ModifiersAggregatedState mModifiersAggregatedState = new ModifiersAggregatedState();
 
     private final DeviceConfig.OnPropertiesChangedListener mOnPropertiesChangedListener;
@@ -127,6 +128,9 @@
             if (m instanceof StatefulModifier s) {
                 mStatefulModifiers.add(s);
             }
+            if (m instanceof UserSwitchListener l) {
+                mUserSwitchListeners.add(l);
+            }
         });
         mOnPropertiesChangedListener =
                 properties -> mClampers.forEach(BrightnessClamper::onDeviceConfigChanged);
@@ -209,6 +213,13 @@
     }
 
     /**
+     * Called when the user switches.
+     */
+    public void onUserSwitch() {
+        mUserSwitchListeners.forEach(listener -> listener.onSwitchUser());
+    }
+
+    /**
      * Used to dump ClampersController state.
      */
     public void dump(PrintWriter writer) {
@@ -336,7 +347,7 @@
                         data.mDisplayDeviceConfig));
             }
             if (flags.useNewHdrBrightnessModifier()) {
-                modifiers.add(new HdrBrightnessModifier(handler, listener, data));
+                modifiers.add(new HdrBrightnessModifier(handler, context, listener, data));
             }
             return modifiers;
         }
@@ -466,6 +477,13 @@
     }
 
     /**
+     * A clamper/modifier should implement this interface if it reads user-specific settings
+     */
+    interface UserSwitchListener {
+        void onSwitchUser();
+    }
+
+    /**
      * StatefulModifiers contribute to AggregatedState, that is used to decide if brightness
      * adjustement is needed
      */
diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessLowLuxModifier.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessLowLuxModifier.java
index 951980a..c3596c3 100644
--- a/services/core/java/com/android/server/display/brightness/clamper/BrightnessLowLuxModifier.java
+++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessLowLuxModifier.java
@@ -41,7 +41,8 @@
  * Class used to prevent the screen brightness dipping below a certain value, based on current
  * lux conditions and user preferred minimum.
  */
-public class BrightnessLowLuxModifier extends BrightnessModifier {
+public class BrightnessLowLuxModifier extends BrightnessModifier implements
+        BrightnessClamperController.UserSwitchListener {
 
     // To enable these logs, run:
     // 'adb shell setprop persist.log.tag.BrightnessLowLuxModifier DEBUG && adb reboot'
@@ -81,10 +82,9 @@
      */
     @VisibleForTesting
     public void recalculateLowerBound() {
-        int userId = UserHandle.USER_CURRENT;
         float settingNitsLowerBound = Settings.Secure.getFloatForUser(
                 mContentResolver, Settings.Secure.EVEN_DIMMER_MIN_NITS,
-                /* def= */ MIN_NITS_DEFAULT, userId);
+                /* def= */ MIN_NITS_DEFAULT, UserHandle.USER_CURRENT);
 
         boolean isActive = isSettingEnabled()
                 && mAmbientLux != BrightnessMappingStrategy.INVALID_LUX;
@@ -190,6 +190,11 @@
     }
 
     @Override
+    public void onSwitchUser() {
+        recalculateLowerBound();
+    }
+
+    @Override
     public void dump(PrintWriter pw) {
         pw.println("BrightnessLowLuxModifier:");
         pw.println("  mIsActive=" + mIsActive);
@@ -221,10 +226,10 @@
             super(handler);
             mContentResolver.registerContentObserver(
                     Settings.Secure.getUriFor(Settings.Secure.EVEN_DIMMER_MIN_NITS),
-                    false, this);
+                    false, this, UserHandle.USER_ALL);
             mContentResolver.registerContentObserver(
                     Settings.Secure.getUriFor(Settings.Secure.EVEN_DIMMER_ACTIVATED),
-                    false, this);
+                    false, this, UserHandle.USER_ALL);
         }
 
         @Override
diff --git a/services/core/java/com/android/server/display/brightness/clamper/HdrBrightnessModifier.java b/services/core/java/com/android/server/display/brightness/clamper/HdrBrightnessModifier.java
index 5e44cc3..4ab4336 100644
--- a/services/core/java/com/android/server/display/brightness/clamper/HdrBrightnessModifier.java
+++ b/services/core/java/com/android/server/display/brightness/clamper/HdrBrightnessModifier.java
@@ -21,16 +21,22 @@
 
 import android.annotation.Nullable;
 import android.annotation.SuppressLint;
+import android.content.Context;
+import android.database.ContentObserver;
 import android.hardware.display.DisplayManagerInternal;
+import android.net.Uri;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.PowerManager;
+import android.os.UserHandle;
+import android.provider.Settings;
 import android.view.SurfaceControlHdrLayerInfoListener;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.display.BrightnessSynchronizer;
 import com.android.server.display.DisplayBrightnessState;
 import com.android.server.display.DisplayDeviceConfig;
+import com.android.server.display.brightness.BrightnessReason;
 import com.android.server.display.config.HdrBrightnessData;
 
 import java.io.PrintWriter;
@@ -43,6 +49,11 @@
     static final float DEFAULT_MAX_HDR_SDR_RATIO = 1.0f;
     private static final float DEFAULT_HDR_LAYER_SIZE = -1.0f;
 
+    private final Uri mLowPowerModeSetting = Settings.Global.getUriFor(
+            Settings.Global.LOW_POWER_MODE);
+
+    private final ContentObserver mContentObserver;
+
     private final SurfaceControlHdrLayerInfoListener mHdrListener =
             new SurfaceControlHdrLayerInfoListener() {
                 @Override
@@ -51,7 +62,8 @@
                     boolean hdrLayerPresent = numberOfHdrLayers > 0;
                     mHandler.post(() -> HdrBrightnessModifier.this.onHdrInfoChanged(
                             hdrLayerPresent ? (float) (maxW * maxH) : DEFAULT_HDR_LAYER_SIZE,
-                            hdrLayerPresent ? maxDesiredHdrSdrRatio : DEFAULT_MAX_HDR_SDR_RATIO));
+                            hdrLayerPresent ? Math.max(maxDesiredHdrSdrRatio,
+                                    DEFAULT_MAX_HDR_SDR_RATIO) : DEFAULT_MAX_HDR_SDR_RATIO));
                 }
             };
 
@@ -61,6 +73,7 @@
     private final Runnable mDebouncer;
 
     private IBinder mRegisteredDisplayToken;
+    private boolean mContentObserverRegistered = false;
 
     private DisplayDeviceConfig mDisplayDeviceConfig;
     @Nullable
@@ -72,6 +85,8 @@
 
     private float mAmbientLux = INVALID_LUX;
 
+    private boolean mLowPowerMode = false;
+
     private Mode mMode = Mode.NO_HDR;
     // The maximum brightness allowed for current lux
     private float mMaxBrightness = PowerManager.BRIGHTNESS_MAX;
@@ -80,17 +95,17 @@
     private float mTransitionRate = CUSTOM_ANIMATION_RATE_NOT_SET;
     private float mPendingTransitionRate = CUSTOM_ANIMATION_RATE_NOT_SET;
 
-    HdrBrightnessModifier(Handler handler,
+    HdrBrightnessModifier(Handler handler, Context context,
             BrightnessClamperController.ClamperChangeListener clamperChangeListener,
             BrightnessClamperController.DisplayDeviceData displayData) {
-        this(new Handler(handler.getLooper()), clamperChangeListener, new Injector(), displayData);
+        this(new Handler(handler.getLooper()), clamperChangeListener,
+                new Injector(context), displayData);
     }
 
     @VisibleForTesting
     HdrBrightnessModifier(Handler handler,
             BrightnessClamperController.ClamperChangeListener clamperChangeListener,
-            Injector injector,
-            BrightnessClamperController.DisplayDeviceData displayData) {
+            Injector injector, BrightnessClamperController.DisplayDeviceData displayData) {
         mHandler = handler;
         mClamperChangeListener = clamperChangeListener;
         mInjector = injector;
@@ -99,7 +114,13 @@
             mMaxBrightness = mPendingMaxBrightness;
             mClamperChangeListener.onChanged();
         };
-        onDisplayChanged(displayData);
+        mContentObserver = new ContentObserver(mHandler) {
+            @Override
+            public void onChange(boolean selfChange) {
+                onLowPowerModeChange();
+            }
+        };
+        mHandler.post(() -> onDisplayChanged(displayData));
     }
 
     // Called in DisplayControllerHandler
@@ -120,6 +141,8 @@
 
         stateBuilder.setHdrBrightness(hdrBrightness);
         stateBuilder.setCustomAnimationRate(mTransitionRate);
+        stateBuilder.getBrightnessReason().addModifier(BrightnessReason.MODIFIER_HDR);
+
         // transition rate applied, reset
         mTransitionRate = CUSTOM_ANIMATION_RATE_NOT_SET;
     }
@@ -132,12 +155,14 @@
         pw.println("  mMaxDesiredHdrRatio=" + mMaxDesiredHdrRatio);
         pw.println("  mHdrLayerSize=" + mHdrLayerSize);
         pw.println("  mAmbientLux=" + mAmbientLux);
+        pw.println("  mLowPowerMode=" + mLowPowerMode);
         pw.println("  mMode=" + mMode);
         pw.println("  mMaxBrightness=" + mMaxBrightness);
         pw.println("  mPendingMaxBrightness=" + mPendingMaxBrightness);
         pw.println("  mTransitionRate=" + mTransitionRate);
         pw.println("  mPendingTransitionRate=" + mPendingTransitionRate);
         pw.println("  mHdrListener registered=" + (mRegisteredDisplayToken != null));
+        pw.println("  mContentObserverRegistered=" + mContentObserverRegistered);
     }
 
     // Called in DisplayControllerHandler
@@ -168,10 +193,36 @@
         }
     }
 
+    // Called in DisplayControllerHandler
     @Override
     public void onDisplayChanged(BrightnessClamperController.DisplayDeviceData displayData) {
-        mHandler.post(() -> onDisplayChanged(displayData.mDisplayToken, displayData.mWidth,
-                displayData.mHeight, displayData.mDisplayDeviceConfig));
+        mDisplayDeviceConfig = displayData.mDisplayDeviceConfig;
+        mScreenSize = (float) displayData.mWidth * displayData.mHeight;
+        HdrBrightnessData data = mDisplayDeviceConfig.getHdrBrightnessData();
+        if (data == null) {
+            unregisterHdrListener();
+        } else {
+            registerHdrListener(displayData.mDisplayToken);
+        }
+        if (data == null || data.allowInLowPowerMode) {
+            unregisterContentObserver();
+        } else {
+            registerContentObserver();
+        }
+
+        Mode newMode = recalculateMode(data);
+        // mode changed, or mode was HDR  and HdrBrightnessData changed
+        boolean needToNotifyChange = mMode != newMode
+                || (mMode != HdrBrightnessModifier.Mode.NO_HDR && data != mHdrBrightnessData);
+        mMode = newMode;
+        mHdrBrightnessData = data;
+        mMaxBrightness = findBrightnessLimit(mHdrBrightnessData, mAmbientLux);
+
+        if (needToNotifyChange) {
+            // data changed, reset custom transition rate
+            mTransitionRate = CUSTOM_ANIMATION_RATE_NOT_SET;
+            mClamperChangeListener.onChanged();
+        }
     }
 
     // Called in DisplayControllerHandler, when any modifier state changes
@@ -215,49 +266,19 @@
     }
 
     // Called in DisplayControllerHandler
-    private void onDisplayChanged(IBinder displayToken, int width, int height,
-            DisplayDeviceConfig config) {
-        mDisplayDeviceConfig = config;
-        mScreenSize = (float) width * height;
-        HdrBrightnessData data = config.getHdrBrightnessData();
-        if (data == null) {
-            unregisterHdrListener();
-        } else {
-            registerHdrListener(displayToken);
-        }
-        recalculate(data, mMaxDesiredHdrRatio);
-    }
-
-    // Called in DisplayControllerHandler
-    private void recalculate(@Nullable HdrBrightnessData data, float maxDesiredHdrRatio) {
-        Mode newMode = recalculateMode(data);
-        // if HDR mode changed, notify changed
-        boolean needToNotifyChange = mMode != newMode;
-        // If HDR mode is active, we need to check if other HDR params are changed
-        if (mMode != HdrBrightnessModifier.Mode.NO_HDR) {
-            if (!BrightnessSynchronizer.floatEquals(mMaxDesiredHdrRatio, maxDesiredHdrRatio)
-                    || data != mHdrBrightnessData) {
-                needToNotifyChange = true;
-            }
-        }
-
-        mMode = newMode;
-        mHdrBrightnessData = data;
-        mMaxDesiredHdrRatio = maxDesiredHdrRatio;
-
-        if (needToNotifyChange) {
-            // data or hdr layer changed, reset custom transition rate
-            mTransitionRate = CUSTOM_ANIMATION_RATE_NOT_SET;
-            mClamperChangeListener.onChanged();
-        }
-    }
-
-    // Called in DisplayControllerHandler
     private Mode recalculateMode(@Nullable HdrBrightnessData data) {
         // no config
         if (data == null) {
             return Mode.NO_HDR;
         }
+        // no HDR layer present
+        if (mHdrLayerSize == DEFAULT_HDR_LAYER_SIZE) {
+            return Mode.NO_HDR;
+        }
+        // low power mode and not allowed in low power mode
+        if (!data.allowInLowPowerMode && mLowPowerMode) {
+            return Mode.NO_HDR;
+        }
         // HDR layer < minHdr % for Nbm
         if (mHdrLayerSize < mScreenSize * data.minimumHdrPercentOfScreenForNbm) {
             return Mode.NO_HDR;
@@ -270,6 +291,16 @@
         return Mode.HBM_HDR;
     }
 
+    private void onLowPowerModeChange() {
+        mLowPowerMode = mInjector.isLowPowerMode();
+        Mode newMode = recalculateMode(mHdrBrightnessData);
+        if (newMode != mMode) {
+            mMode = newMode;
+            mTransitionRate = CUSTOM_ANIMATION_RATE_NOT_SET;
+            mClamperChangeListener.onChanged();
+        }
+    }
+
     private float getMaxBrightness(Mode mode, float maxBrightness, HdrBrightnessData data) {
         if (mode == Mode.NBM_HDR) {
             return Math.min(data.hbmTransitionPoint, maxBrightness);
@@ -281,7 +312,13 @@
     }
 
     // Called in DisplayControllerHandler
-    private float findBrightnessLimit(HdrBrightnessData data, float ambientLux) {
+    private float findBrightnessLimit(@Nullable HdrBrightnessData data, float ambientLux) {
+        if (data == null) {
+            return PowerManager.BRIGHTNESS_MAX;
+        }
+        if (ambientLux == INVALID_LUX) {
+            return PowerManager.BRIGHTNESS_MAX;
+        }
         float foundAmbientBoundary = Float.MAX_VALUE;
         float foundMaxBrightness = PowerManager.BRIGHTNESS_MAX;
         for (Map.Entry<Float, Float> brightnessPoint :
@@ -299,7 +336,17 @@
     // Called in DisplayControllerHandler
     private void onHdrInfoChanged(float hdrLayerSize, float maxDesiredHdrSdrRatio) {
         mHdrLayerSize = hdrLayerSize;
-        recalculate(mHdrBrightnessData, maxDesiredHdrSdrRatio);
+        Mode newMode = recalculateMode(mHdrBrightnessData);
+        // mode changed, or mode was HDR  and maxDesiredHdrRatio changed
+        boolean needToNotifyChange = mMode != newMode
+                || (mMode != HdrBrightnessModifier.Mode.NO_HDR
+                && !BrightnessSynchronizer.floatEquals(mMaxDesiredHdrRatio, maxDesiredHdrSdrRatio));
+        mMode = newMode;
+        mMaxDesiredHdrRatio = maxDesiredHdrSdrRatio;
+        if (needToNotifyChange) {
+            mTransitionRate = CUSTOM_ANIMATION_RATE_NOT_SET;
+            mClamperChangeListener.onChanged();
+        }
     }
 
     // Called in DisplayControllerHandler
@@ -323,12 +370,36 @@
         }
     }
 
+    // Called in DisplayControllerHandler
+    private void registerContentObserver() {
+        if (!mContentObserverRegistered) {
+            mInjector.registerContentObserver(mContentObserver, mLowPowerModeSetting);
+            mContentObserverRegistered = true;
+            mLowPowerMode = mInjector.isLowPowerMode();
+        }
+    }
+
+    // Called in DisplayControllerHandler
+    private void unregisterContentObserver() {
+        if (mContentObserverRegistered) {
+            mInjector.unregisterContentObserver(mContentObserver);
+            mContentObserverRegistered = false;
+            mLowPowerMode = false;
+        }
+    }
+
     private enum Mode {
         NO_HDR, NBM_HDR, HBM_HDR
     }
 
     @SuppressLint("MissingPermission")
     static class Injector {
+        private final Context mContext;
+
+        Injector(Context context) {
+            mContext = context;
+        }
+
         void registerHdrListener(SurfaceControlHdrLayerInfoListener listener, IBinder token) {
             listener.register(token);
         }
@@ -336,5 +407,19 @@
         void unregisterHdrListener(SurfaceControlHdrLayerInfoListener listener, IBinder token) {
             listener.unregister(token);
         }
+
+        void registerContentObserver(ContentObserver observer, Uri uri) {
+            mContext.getContentResolver().registerContentObserver(uri, false,
+                    observer, UserHandle.USER_ALL);
+        }
+
+        void unregisterContentObserver(ContentObserver observer) {
+            mContext.getContentResolver().unregisterContentObserver(observer);
+        }
+
+        boolean isLowPowerMode() {
+            return Settings.Global.getInt(
+                    mContext.getContentResolver(), Settings.Global.LOW_POWER_MODE, 0) != 0;
+        }
     }
 }
diff --git a/services/core/java/com/android/server/display/brightness/clamper/LightSensorController.java b/services/core/java/com/android/server/display/brightness/clamper/LightSensorController.java
index d89dd28..b219cb1 100644
--- a/services/core/java/com/android/server/display/brightness/clamper/LightSensorController.java
+++ b/services/core/java/com/android/server/display/brightness/clamper/LightSensorController.java
@@ -62,6 +62,9 @@
     private final SensorEventListener mLightSensorEventListener = new SensorEventListener() {
         @Override
         public void onSensorChanged(SensorEvent event) {
+            if (event.sensor != mRegisteredLightSensor) {
+                return;
+            }
             long now = mInjector.getTime();
             mAmbientFilter.addValue(TimeUnit.NANOSECONDS.toMillis(event.timestamp),
                     event.values[0]);
@@ -95,15 +98,13 @@
         if (mRegisteredLightSensor == mLightSensor) {
             return;
         }
+        if (mLightSensor != null) {
+            mSensorManager.registerListener(mLightSensorEventListener,
+                    mLightSensor, mLightSensorRate * 1000, mHandler);
+        }
         if (mRegisteredLightSensor != null) {
             stop();
         }
-        if (mLightSensor == null) {
-            return;
-        }
-
-        mSensorManager.registerListener(mLightSensorEventListener,
-                mLightSensor, mLightSensorRate * 1000, mHandler);
         mRegisteredLightSensor = mLightSensor;
 
         if (DEBUG) {
@@ -115,7 +116,7 @@
         if (mRegisteredLightSensor == null) {
             return;
         }
-        mSensorManager.unregisterListener(mLightSensorEventListener);
+        mSensorManager.unregisterListener(mLightSensorEventListener, mRegisteredLightSensor);
         mRegisteredLightSensor = null;
         mAmbientFilter.clear();
         mLightSensorListener.onAmbientLuxChange(INVALID_LUX);
diff --git a/services/core/java/com/android/server/display/brightness/strategy/AutoBrightnessFallbackStrategy.java b/services/core/java/com/android/server/display/brightness/strategy/AutoBrightnessFallbackStrategy.java
index d8b95ec..1db9bbe 100644
--- a/services/core/java/com/android/server/display/brightness/strategy/AutoBrightnessFallbackStrategy.java
+++ b/services/core/java/com/android/server/display/brightness/strategy/AutoBrightnessFallbackStrategy.java
@@ -16,6 +16,9 @@
 
 package com.android.server.display.brightness.strategy;
 
+import static android.hardware.display.DisplayManagerInternal.DisplayPowerRequest.POLICY_DOZE;
+import static android.hardware.display.DisplayManagerInternal.DisplayPowerRequest.POLICY_OFF;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.hardware.Sensor;
@@ -23,7 +26,6 @@
 import android.os.Handler;
 import android.os.SystemClock;
 import android.util.IndentingPrintWriter;
-import android.view.Display;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.display.BrightnessMappingStrategy;
@@ -53,7 +55,7 @@
     Sensor mScreenOffBrightnessSensor;
 
     // Indicates if the associated LogicalDisplay is enabled or not.
-    private boolean mIsEnabled;
+    private boolean mIsDisplayEnabled;
 
     // Represents if the associated display is a lead display or not. If not, the variable
     // represents the lead display ID
@@ -97,7 +99,7 @@
     public void dump(PrintWriter writer) {
         writer.println("AutoBrightnessFallbackStrategy:");
         writer.println("  mLeadDisplayId=" + mLeadDisplayId);
-        writer.println("  mIsEnabled=" + mIsEnabled);
+        writer.println("  mIsDisplayEnabled=" + mIsDisplayEnabled);
         if (mScreenOffBrightnessSensorController != null) {
             IndentingPrintWriter ipw = new IndentingPrintWriter(writer, " ");
             mScreenOffBrightnessSensorController.dump(ipw);
@@ -108,11 +110,10 @@
     public void strategySelectionPostProcessor(
             StrategySelectionNotifyRequest strategySelectionNotifyRequest) {
         if (mScreenOffBrightnessSensorController != null) {
-            int targetDisplayState = strategySelectionNotifyRequest.getTargetDisplayState();
+            int policy = strategySelectionNotifyRequest.getDisplayPowerRequest().policy;
             mScreenOffBrightnessSensorController.setLightSensorEnabled(
-                    strategySelectionNotifyRequest.isAutoBrightnessEnabled() && mIsEnabled
-                            && (targetDisplayState == Display.STATE_OFF
-                            || (targetDisplayState == Display.STATE_DOZE
+                    strategySelectionNotifyRequest.isAutoBrightnessEnabled() && mIsDisplayEnabled
+                            && (policy == POLICY_OFF || (policy == POLICY_DOZE
                             && !strategySelectionNotifyRequest
                             .isAllowAutoBrightnessWhileDozingConfig()))
                             && mLeadDisplayId == Layout.NO_LEAD_DISPLAY);
@@ -132,9 +133,9 @@
      */
     public void setupAutoBrightnessFallbackSensor(SensorManager sensorManager,
             DisplayDeviceConfig displayDeviceConfig, Handler handler,
-            BrightnessMappingStrategy brightnessMappingStrategy, boolean isEnabled,
+            BrightnessMappingStrategy brightnessMappingStrategy, boolean isDisplayEnabled,
             int leadDisplayId) {
-        mIsEnabled = isEnabled;
+        mIsDisplayEnabled = isDisplayEnabled;
         mLeadDisplayId = leadDisplayId;
         if (mScreenOffBrightnessSensorController != null) {
             mScreenOffBrightnessSensorController.stop();
diff --git a/services/core/java/com/android/server/display/brightness/strategy/OverrideBrightnessStrategy.java b/services/core/java/com/android/server/display/brightness/strategy/OverrideBrightnessStrategy.java
index 40a495c..3fc15d1 100644
--- a/services/core/java/com/android/server/display/brightness/strategy/OverrideBrightnessStrategy.java
+++ b/services/core/java/com/android/server/display/brightness/strategy/OverrideBrightnessStrategy.java
@@ -16,9 +16,10 @@
 
 package com.android.server.display.brightness.strategy;
 
+import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
+
 import com.android.server.display.DisplayBrightnessState;
 import com.android.server.display.brightness.BrightnessReason;
-import com.android.server.display.brightness.BrightnessUtils;
 import com.android.server.display.brightness.StrategyExecutionRequest;
 import com.android.server.display.brightness.StrategySelectionNotifyRequest;
 
@@ -33,9 +34,14 @@
             StrategyExecutionRequest strategyExecutionRequest) {
         // Todo(b/241308599): Introduce a validator class and add validations before setting
         // the brightness
-        return BrightnessUtils.constructDisplayBrightnessState(BrightnessReason.REASON_OVERRIDE,
-                strategyExecutionRequest.getDisplayPowerRequest().screenBrightnessOverride,
-                getName());
+        DisplayPowerRequest dpr = strategyExecutionRequest.getDisplayPowerRequest();
+        BrightnessReason reason = new BrightnessReason(BrightnessReason.REASON_OVERRIDE);
+        reason.setTag(dpr.screenBrightnessOverrideTag);
+        return new DisplayBrightnessState.Builder()
+                .setBrightness(dpr.screenBrightnessOverride)
+                .setBrightnessReason(reason)
+                .setDisplayBrightnessStrategyName(getName())
+                .build();
     }
 
     @Override
diff --git a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
index d610f08..5e471c8 100644
--- a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
@@ -121,6 +121,7 @@
     private static final int MSG_HIGH_BRIGHTNESS_THRESHOLDS_CHANGED = 6;
     private static final int MSG_REFRESH_RATE_IN_HBM_SUNLIGHT_CHANGED = 7;
     private static final int MSG_REFRESH_RATE_IN_HBM_HDR_CHANGED = 8;
+    private static final int MSG_SWITCH_USER = 9;
 
     private final Object mLock = new Object();
     private final Context mContext;
@@ -564,6 +565,13 @@
     }
 
     /**
+     * Called when the user switches.
+     */
+    public void onSwitchUser() {
+        mHandler.obtainMessage(MSG_SWITCH_USER).sendToTarget();
+    }
+
+    /**
      * Print the object's state and debug information into the given stream.
      *
      * @param pw The stream to dump information to.
@@ -789,6 +797,13 @@
                     mHbmObserver.onDeviceConfigRefreshRateInHbmHdrChanged(refreshRateInHbmHdr);
                     break;
                 }
+
+                case MSG_SWITCH_USER: {
+                    synchronized (mLock) {
+                        mSettingsObserver.updateRefreshRateSettingLocked();
+                        mSettingsObserver.updateModeSwitchingTypeSettingLocked();
+                    }
+                }
             }
         }
     }
@@ -1012,10 +1027,10 @@
             final ContentResolver cr = mContext.getContentResolver();
             mInjector.registerPeakRefreshRateObserver(cr, this);
             mInjector.registerMinRefreshRateObserver(cr, this);
-            cr.registerContentObserver(mLowPowerModeSetting, false /*notifyDescendants*/, this,
-                    UserHandle.USER_SYSTEM);
-            cr.registerContentObserver(mMatchContentFrameRateSetting, false /*notifyDescendants*/,
-                    this);
+            cr.registerContentObserver(mLowPowerModeSetting, /* notifyDescendants= */ false, this,
+                    UserHandle.USER_ALL);
+            cr.registerContentObserver(mMatchContentFrameRateSetting,
+                    /* notifyDescendants= */ false, this, UserHandle.USER_ALL);
             mInjector.registerDisplayListener(mDisplayListener, mHandler);
 
             float deviceConfigDefaultPeakRefresh =
@@ -1156,14 +1171,15 @@
             float highestRefreshRate = getMaxRefreshRateLocked(displayId);
 
             float minRefreshRate = Settings.System.getFloatForUser(cr,
-                    Settings.System.MIN_REFRESH_RATE, 0f, cr.getUserId());
+                    Settings.System.MIN_REFRESH_RATE, 0f, UserHandle.USER_CURRENT);
             if (Float.isInfinite(minRefreshRate)) {
                 // Infinity means that we want the highest possible refresh rate
                 minRefreshRate = highestRefreshRate;
             }
 
             float peakRefreshRate = Settings.System.getFloatForUser(cr,
-                    Settings.System.PEAK_REFRESH_RATE, mDefaultPeakRefreshRate, cr.getUserId());
+                    Settings.System.PEAK_REFRESH_RATE, mDefaultPeakRefreshRate,
+                    UserHandle.USER_CURRENT);
             if (Float.isInfinite(peakRefreshRate)) {
                 // Infinity means that we want the highest possible refresh rate
                 peakRefreshRate = highestRefreshRate;
@@ -1234,9 +1250,9 @@
 
         private void updateModeSwitchingTypeSettingLocked() {
             final ContentResolver cr = mContext.getContentResolver();
-            int switchingType = Settings.Secure.getIntForUser(
-                    cr, Settings.Secure.MATCH_CONTENT_FRAME_RATE, mModeSwitchingType /*default*/,
-                    cr.getUserId());
+            int switchingType = Settings.Secure.getIntForUser(cr,
+                    Settings.Secure.MATCH_CONTENT_FRAME_RATE, /* default= */ mModeSwitchingType,
+                    UserHandle.USER_CURRENT);
             if (switchingType != mModeSwitchingType) {
                 mModeSwitchingType = switchingType;
                 notifyDesiredDisplayModeSpecsChangedLocked();
@@ -3033,14 +3049,14 @@
         public void registerPeakRefreshRateObserver(@NonNull ContentResolver cr,
                 @NonNull ContentObserver observer) {
             cr.registerContentObserver(PEAK_REFRESH_RATE_URI, false /*notifyDescendants*/,
-                    observer, UserHandle.USER_SYSTEM);
+                    observer, UserHandle.USER_ALL);
         }
 
         @Override
         public void registerMinRefreshRateObserver(@NonNull ContentResolver cr,
                 @NonNull ContentObserver observer) {
             cr.registerContentObserver(MIN_REFRESH_RATE_URI, false /*notifyDescendants*/,
-                    observer, UserHandle.USER_SYSTEM);
+                    observer, UserHandle.USER_ALL);
         }
 
         @Override
diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java
index 886857c..a3b77e8 100644
--- a/services/core/java/com/android/server/dreams/DreamManagerService.java
+++ b/services/core/java/com/android/server/dreams/DreamManagerService.java
@@ -543,19 +543,20 @@
     }
 
     private void startDozingInternal(IBinder token, int screenState,
-            @Display.StateReason int reason, int screenBrightness) {
-        if (DEBUG) {
-            Slog.d(TAG, "Dream requested to start dozing: " + token
-                    + ", screenState=" + screenState
-                    + ", screenBrightness=" + screenBrightness);
-        }
+            @Display.StateReason int reason, float screenBrightnessFloat, int screenBrightnessInt) {
+        Slog.d(TAG, "Dream requested to start dozing: " + token
+                + ", screenState=" + Display.stateToString(screenState)
+                + ", reason=" + Display.stateReasonToString(reason)
+                + ", screenBrightnessFloat=" + screenBrightnessFloat
+                + ", screenBrightnessInt=" + screenBrightnessInt);
 
         synchronized (mLock) {
             if (mCurrentDream != null && mCurrentDream.token == token && mCurrentDream.canDoze) {
                 mCurrentDream.dozeScreenState = screenState;
-                mCurrentDream.dozeScreenBrightness = screenBrightness;
+                mCurrentDream.dozeScreenBrightness = screenBrightnessInt;
+                mCurrentDream.dozeScreenBrightnessFloat = screenBrightnessFloat;
                 mPowerManagerInternal.setDozeOverrideFromDreamManager(
-                        screenState, reason, screenBrightness);
+                        screenState, reason, screenBrightnessFloat, screenBrightnessInt);
                 if (!mCurrentDream.isDozing) {
                     mCurrentDream.isDozing = true;
                     mDozeWakeLock.acquire();
@@ -576,6 +577,7 @@
                 mPowerManagerInternal.setDozeOverrideFromDreamManager(
                         Display.STATE_UNKNOWN,
                         Display.STATE_REASON_DREAM_MANAGER,
+                        PowerManager.BRIGHTNESS_INVALID_FLOAT,
                         PowerManager.BRIGHTNESS_DEFAULT);
             }
         }
@@ -1096,7 +1098,7 @@
         @Override // Binder call
         public void startDozing(
                 IBinder token, int screenState, @Display.StateReason int reason,
-                int screenBrightness) {
+                float screenBrightnessFloat, int screeBrightnessInt) {
             // Requires no permission, called by Dream from an arbitrary process.
             if (token == null) {
                 throw new IllegalArgumentException("token must not be null");
@@ -1104,7 +1106,8 @@
 
             final long ident = Binder.clearCallingIdentity();
             try {
-                startDozingInternal(token, screenState, reason, screenBrightness);
+                startDozingInternal(token, screenState, reason, screenBrightnessFloat,
+                        screeBrightnessInt);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
@@ -1113,7 +1116,7 @@
         @Override // Binder call
         public void startDozingOneway(
                 IBinder token, int screenState, @Display.StateReason int reason,
-                int screenBrightness) {
+                float screenBrightnessFloat, int screeBrightnessInt) {
             // Requires no permission, called by Dream from an arbitrary process.
             if (token == null) {
                 throw new IllegalArgumentException("token must not be null");
@@ -1121,7 +1124,8 @@
 
             final long ident = Binder.clearCallingIdentity();
             try {
-                startDozingInternal(token, screenState, reason, screenBrightness);
+                startDozingInternal(token, screenState, reason, screenBrightnessFloat,
+                        screeBrightnessInt);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
@@ -1278,6 +1282,7 @@
         public boolean isWaking = false;
         public int dozeScreenState = Display.STATE_UNKNOWN;
         public int dozeScreenBrightness = PowerManager.BRIGHTNESS_DEFAULT;
+        public float dozeScreenBrightnessFloat = PowerManager.BRIGHTNESS_INVALID_FLOAT;
 
         DreamRecord(ComponentName name, int userId, boolean isPreview, boolean canDoze) {
             this.name = name;
@@ -1298,6 +1303,7 @@
                     + ", isWaking=" + isWaking
                     + ", dozeScreenState=" + dozeScreenState
                     + ", dozeScreenBrightness=" + dozeScreenBrightness
+                    + ", dozeScreenBrightnessFloat=" + dozeScreenBrightnessFloat
                     + '}';
         }
     }
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java b/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java
index 3c3bdd5..7746276 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java
@@ -769,6 +769,7 @@
      * @return true if the UI Broadcast type is valid
      */
     private static boolean isValidUiBroadcastType(int value) {
+        value = value & 0xFF;
         return ((value == 0x00)
                 || (value == 0x01)
                 || (value == 0x10)
diff --git a/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java b/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java
index 3c3cfe6..256905d 100644
--- a/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java
+++ b/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java
@@ -54,7 +54,9 @@
 
     // The maximum number of times we send <Give Device Power Status> before we give up.
     // We wait up to RESPONSE_TIMEOUT_MS * LOOP_COUNTER_MAX = 20 seconds.
-    private static final int LOOP_COUNTER_MAX = 10;
+    // Every 3 timeouts we send a <Text View On> in case the TV missed it and ignored it.
+    @VisibleForTesting
+    static final int LOOP_COUNTER_MAX = 10;
 
     private final int mTargetAddress;
     private final boolean mIsCec20;
@@ -181,6 +183,7 @@
         if (cmd.getOpcode() == Constants.MESSAGE_REPORT_POWER_STATUS) {
             int status = cmd.getParams()[0];
             if (status == HdmiControlManager.POWER_STATUS_ON) {
+                HdmiLogger.debug("TV's power status is on. Action finished successfully");
                 // If the device is still the active source, send the <Active Source> message
                 // again.
                 maySendActiveSource();
@@ -199,6 +202,12 @@
         switch (state) {
             case STATE_WAITING_FOR_REPORT_POWER_STATUS:
                 if (mPowerStatusCounter++ < LOOP_COUNTER_MAX) {
+                    if (mPowerStatusCounter % 3 == 0) {
+                        HdmiLogger.debug("Retry sending <Text View On> in case the TV "
+                                + "missed the message.");
+                        sendCommand(HdmiCecMessageBuilder.buildTextViewOn(getSourceAddress(),
+                                mTargetAddress));
+                    }
                     queryDevicePowerStatus();
                     addTimer(mState, HdmiConfig.TIMEOUT_MS);
                 } else {
diff --git a/services/core/java/com/android/server/hdmi/RequestActiveSourceAction.java b/services/core/java/com/android/server/hdmi/RequestActiveSourceAction.java
index 539a00d..a33d70a 100644
--- a/services/core/java/com/android/server/hdmi/RequestActiveSourceAction.java
+++ b/services/core/java/com/android/server/hdmi/RequestActiveSourceAction.java
@@ -19,6 +19,7 @@
 import android.hardware.hdmi.HdmiControlManager;
 import android.hardware.hdmi.IHdmiControlCallback;
 import android.util.Slog;
+import com.android.internal.annotations.VisibleForTesting;
 
 /**
  * Feature action that sends <Request Active Source> message and waits for <Active Source> on TV
@@ -39,6 +40,10 @@
     // Number of retries <Request Active Source> is sent if no device answers this message.
     private static final int MAX_SEND_RETRY_COUNT = 1;
 
+    // Timeout to wait for the LauncherX API call to be completed.
+    @VisibleForTesting
+    protected static final int TIMEOUT_WAIT_FOR_LAUNCHERX_API_CALL_MS = 10000;
+
     private int mSendRetryCount = 0;
 
 
@@ -55,7 +60,7 @@
         // We wait for default timeout to allow the message triggered by the LauncherX API call to
         // be sent by the TV and another default timeout in case the message has to be answered
         // (e.g. TV sent a <Set Stream Path> or <Routing Change>).
-        addTimer(mState, HdmiConfig.TIMEOUT_MS * 2);
+        addTimer(mState, TIMEOUT_WAIT_FOR_LAUNCHERX_API_CALL_MS);
         return true;
     }
 
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index bb2efa1..a06ad14 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -1254,14 +1254,15 @@
     /**
      * Start drag and drop.
      *
-     * @param fromChannel The input channel that is currently receiving a touch gesture that should
-     *                    be turned into the drag pointer.
-     * @param dragAndDropChannel The input channel associated with the system drag window.
+     * @param fromChannelToken The token of the input channel that is currently receiving a touch
+     *                        gesture that should be turned into the drag pointer.
+     * @param dragAndDropChannelToken The token of the input channel associated with the system drag
+     *                               window.
      * @return true if drag and drop was successfully started, false otherwise.
      */
-    public boolean startDragAndDrop(@NonNull InputChannel fromChannel,
-            @NonNull InputChannel dragAndDropChannel) {
-        return mNative.transferTouchGesture(fromChannel.getToken(), dragAndDropChannel.getToken(),
+    public boolean startDragAndDrop(@NonNull IBinder fromChannelToken,
+            @NonNull IBinder dragAndDropChannelToken) {
+        return mNative.transferTouchGesture(fromChannelToken, dragAndDropChannelToken,
                 true /* isDragDrop */);
     }
 
@@ -1355,8 +1356,7 @@
             int patternRepeatIndex = -1;
             int amplitudeCount = -1;
 
-            if (effect instanceof VibrationEffect.Composed) {
-                VibrationEffect.Composed composed = (VibrationEffect.Composed) effect;
+            if (effect instanceof VibrationEffect.Composed composed) {
                 int segmentCount = composed.getSegments().size();
                 pattern = new long[segmentCount];
                 amplitudes = new int[segmentCount];
@@ -1381,6 +1381,8 @@
                     }
                     pattern[amplitudeCount++] = segment.getDuration();
                 }
+            } else {
+                Slog.w(TAG, "Input devices don't support effect " + effect);
             }
 
             if (amplitudeCount < 0) {
diff --git a/services/core/java/com/android/server/input/KeyboardLayoutManager.java b/services/core/java/com/android/server/input/KeyboardLayoutManager.java
index 4993412..1d1a178 100644
--- a/services/core/java/com/android/server/input/KeyboardLayoutManager.java
+++ b/services/core/java/com/android/server/input/KeyboardLayoutManager.java
@@ -22,6 +22,8 @@
 import static android.hardware.input.KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD;
 import static android.hardware.input.KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_DEFAULT;
 
+import static com.android.hardware.input.Flags.keyboardLayoutManagerMultiUserImeSetup;
+
 import android.annotation.AnyThread;
 import android.annotation.MainThread;
 import android.annotation.NonNull;
@@ -1066,9 +1068,15 @@
             for (InputMethodInfo imeInfo :
                     inputMethodManagerInternal.getEnabledInputMethodListAsUser(
                             userId)) {
-                for (InputMethodSubtype imeSubtype :
-                        inputMethodManager.getEnabledInputMethodSubtypeList(
-                                imeInfo, true /* allowsImplicitlyEnabledSubtypes */)) {
+                final List<InputMethodSubtype> imeSubtypes;
+                if (keyboardLayoutManagerMultiUserImeSetup()) {
+                    imeSubtypes = inputMethodManagerInternal.getEnabledInputMethodSubtypeListAsUser(
+                            imeInfo.getId(), true /* allowsImplicitlyEnabledSubtypes */, userId);
+                } else {
+                    imeSubtypes = inputMethodManager.getEnabledInputMethodSubtypeList(imeInfo,
+                            true /* allowsImplicitlyEnabledSubtypes */);
+                }
+                for (InputMethodSubtype imeSubtype : imeSubtypes) {
                     if (!imeSubtype.isSuitableForPhysicalKeyboardLayoutMapping()) {
                         continue;
                     }
diff --git a/services/core/java/com/android/server/inputmethod/AdditionalSubtypeMapRepository.java b/services/core/java/com/android/server/inputmethod/AdditionalSubtypeMapRepository.java
index 99f4747..b08f917 100644
--- a/services/core/java/com/android/server/inputmethod/AdditionalSubtypeMapRepository.java
+++ b/services/core/java/com/android/server/inputmethod/AdditionalSubtypeMapRepository.java
@@ -38,10 +38,11 @@
 final class AdditionalSubtypeMapRepository {
     private static final String TAG = "AdditionalSubtypeMapRepository";
 
-    // TODO(b/352594784): Should we user other lock primitives?
-    @GuardedBy("sPerUserMap")
+    private static final Object sMutationLock = new Object();
+
     @NonNull
-    private static final SparseArray<AdditionalSubtypeMap> sPerUserMap = new SparseArray<>();
+    private static volatile ImmutableSparseArray<AdditionalSubtypeMap> sPerUserMap =
+            ImmutableSparseArray.empty();
 
     record WriteTask(@UserIdInt int userId, @NonNull AdditionalSubtypeMap subtypeMap,
                      @NonNull InputMethodMap inputMethodMap) {
@@ -198,7 +199,7 @@
     /**
      * Returns {@link AdditionalSubtypeMap} for the given user.
      *
-     * <p>This method is expected be called after {@link #ensureInitializedAndGet(int)}. Otherwise
+     * <p>This method is expected be called after {@link #initializeIfNecessary(int)}. Otherwise
      * {@link AdditionalSubtypeMap#EMPTY_MAP} will be returned.</p>
      *
      * @param userId the user to be queried about
@@ -207,10 +208,7 @@
     @AnyThread
     @NonNull
     static AdditionalSubtypeMap get(@UserIdInt int userId) {
-        final AdditionalSubtypeMap map;
-        synchronized (sPerUserMap) {
-            map = sPerUserMap.get(userId);
-        }
+        final AdditionalSubtypeMap map = sPerUserMap.get(userId);
         if (map == null) {
             Slog.e(TAG, "get(userId=" + userId + ") is called before loadInitialDataAndGet()."
                     + " Returning an empty map");
@@ -220,28 +218,24 @@
     }
 
     /**
-     * Ensures that {@link AdditionalSubtypeMap} is initialized for the given user. Load it from
-     * the persistent storage if {@link #putAndSave(int, AdditionalSubtypeMap, InputMethodMap)} has
-     * not been called yet.
+     * Ensures that {@link AdditionalSubtypeMap} is initialized for the given user.
      *
      * @param userId the user to be initialized
-     * @return {@link AdditionalSubtypeMap} that is associated with the given user. If
-     *         {@link #putAndSave(int, AdditionalSubtypeMap, InputMethodMap)} is already called
-     *         then the given {@link AdditionalSubtypeMap}.
      */
     @AnyThread
     @NonNull
-    static AdditionalSubtypeMap ensureInitializedAndGet(@UserIdInt int userId) {
-        final var map = AdditionalSubtypeUtils.load(userId);
-        synchronized (sPerUserMap) {
-            final AdditionalSubtypeMap previous = sPerUserMap.get(userId);
-            // If putAndSave() has already been called, then use it.
-            if (previous != null) {
-                return previous;
-            }
-            sPerUserMap.put(userId, map);
+    static void initializeIfNecessary(@UserIdInt int userId) {
+        if (sPerUserMap.contains(userId)) {
+            // Fast-pass. If putAndSave() is already called, then do nothing.
+            return;
         }
-        return map;
+        final var map = AdditionalSubtypeUtils.load(userId);
+        synchronized (sMutationLock) {
+            // Check the condition again.
+            if (!sPerUserMap.contains(userId)) {
+                sPerUserMap = sPerUserMap.cloneWithPutOrSelf(userId, map);
+            }
+        }
     }
 
     /**
@@ -255,12 +249,8 @@
     @AnyThread
     static void putAndSave(@UserIdInt int userId, @NonNull AdditionalSubtypeMap map,
             @NonNull InputMethodMap inputMethodMap) {
-        synchronized (sPerUserMap) {
-            final AdditionalSubtypeMap previous = sPerUserMap.get(userId);
-            if (previous == map) {
-                return;
-            }
-            sPerUserMap.put(userId, map);
+        synchronized (sMutationLock) {
+            sPerUserMap = sPerUserMap.cloneWithPutOrSelf(userId, map);
             sWriter.scheduleWriteTask(userId, map, inputMethodMap);
         }
     }
@@ -277,9 +267,9 @@
 
     @AnyThread
     static void remove(@UserIdInt int userId) {
-        synchronized (sPerUserMap) {
+        synchronized (sMutationLock) {
             sWriter.onUserRemoved(userId);
-            sPerUserMap.remove(userId);
+            sPerUserMap = sPerUserMap.cloneWithRemoveOrSelf(userId);
         }
     }
 }
diff --git a/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java b/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java
index 7c93c8b..5c939bc 100644
--- a/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java
+++ b/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java
@@ -202,7 +202,7 @@
                 break;
             case STATE_HIDE_IME_EXPLICIT:
                 if (Flags.refactorInsetsController()) {
-                    setImeVisibilityOnFocusedWindowClient(false, userId);
+                    setImeVisibilityOnFocusedWindowClient(false, userId, statsToken);
                 } else {
                     mService.hideCurrentInputLocked(windowToken, statsToken,
                             0 /* flags */, null /* resultReceiver */, reason, userId);
@@ -210,7 +210,7 @@
                 break;
             case STATE_HIDE_IME_NOT_ALWAYS:
                 if (Flags.refactorInsetsController()) {
-                    setImeVisibilityOnFocusedWindowClient(false, userId);
+                    setImeVisibilityOnFocusedWindowClient(false, userId, statsToken);
                 } else {
                     mService.hideCurrentInputLocked(windowToken, statsToken,
                             InputMethodManager.HIDE_NOT_ALWAYS, null /* resultReceiver */, reason,
@@ -221,7 +221,7 @@
                 if (Flags.refactorInsetsController()) {
                     // This can be triggered by IMMS#startInputOrWindowGainedFocus. We need to
                     // set the requestedVisibleTypes in InsetsController first, before applying it.
-                    setImeVisibilityOnFocusedWindowClient(true, userId);
+                    setImeVisibilityOnFocusedWindowClient(true, userId, statsToken);
                 } else {
                     mService.showCurrentInputLocked(windowToken, statsToken,
                             InputMethodManager.SHOW_IMPLICIT, MotionEvent.TOOL_TYPE_UNKNOWN,
@@ -278,14 +278,17 @@
     }
 
     @GuardedBy("ImfLock.class")
-    private void setImeVisibilityOnFocusedWindowClient(boolean visibility, @UserIdInt int userId) {
+    private void setImeVisibilityOnFocusedWindowClient(boolean visibility, @UserIdInt int userId,
+            @NonNull ImeTracker.Token statsToken) {
         final var userData = mService.getUserData(userId);
         if (userData.mImeBindingState != null
                 && userData.mImeBindingState.mFocusedWindowClient != null
                 && userData.mImeBindingState.mFocusedWindowClient.mClient != null) {
-            userData.mImeBindingState.mFocusedWindowClient.mClient.setImeVisibility(visibility);
+            userData.mImeBindingState.mFocusedWindowClient.mClient.setImeVisibility(visibility,
+                    statsToken);
         } else {
-            // TODO(b/329229469): ImeTracker?
+            ImeTracker.forLogging().onFailed(statsToken,
+                    ImeTracker.PHASE_SERVER_SET_VISIBILITY_ON_FOCUSED_WINDOW);
         }
     }
 }
diff --git a/services/core/java/com/android/server/inputmethod/IInputMethodClientInvoker.java b/services/core/java/com/android/server/inputmethod/IInputMethodClientInvoker.java
index eada288..650ea60 100644
--- a/services/core/java/com/android/server/inputmethod/IInputMethodClientInvoker.java
+++ b/services/core/java/com/android/server/inputmethod/IInputMethodClientInvoker.java
@@ -25,6 +25,7 @@
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.util.Slog;
+import android.view.inputmethod.ImeTracker;
 
 import com.android.internal.inputmethod.IInputMethodClient;
 import com.android.internal.inputmethod.InputBindResult;
@@ -250,18 +251,18 @@
     }
 
     @AnyThread
-    void setImeVisibility(boolean visible) {
+    void setImeVisibility(boolean visible, @Nullable ImeTracker.Token statsToken) {
         if (mIsProxy) {
-            setImeVisibilityInternal(visible);
+            setImeVisibilityInternal(visible, statsToken);
         } else {
-            mHandler.post(() -> setImeVisibilityInternal(visible));
+            mHandler.post(() -> setImeVisibilityInternal(visible, statsToken));
         }
     }
 
     @AnyThread
-    private void setImeVisibilityInternal(boolean visible) {
+    private void setImeVisibilityInternal(boolean visible, @Nullable ImeTracker.Token statsToken) {
         try {
-            mTarget.setImeVisibility(visible);
+            mTarget.setImeVisibility(visible, statsToken);
         } catch (RemoteException e) {
             logRemoteException(e);
         }
diff --git a/services/core/java/com/android/server/inputmethod/IInputMethodManagerImpl.java b/services/core/java/com/android/server/inputmethod/IInputMethodManagerImpl.java
index a7280e6..58e3452 100644
--- a/services/core/java/com/android/server/inputmethod/IInputMethodManagerImpl.java
+++ b/services/core/java/com/android/server/inputmethod/IInputMethodManagerImpl.java
@@ -71,7 +71,20 @@
     @Retention(SOURCE)
     @Target({METHOD})
     @interface PermissionVerified {
+        /**
+         * The name of the permission that is verified, if precisely one permission is required.
+         * If more than one permission is required, specify either {@link #allOf()} instead.
+         *
+         * <p>If specified, {@link #allOf()} must both be {@code null}.</p>
+         */
         String value() default "";
+
+        /**
+         * Specifies a list of permission names that are all required.
+         *
+         * <p>If specified, {@link #value()} must both be {@code null}.</p>
+         */
+        String[] allOf() default {};
     }
 
     @BinderThread
@@ -132,13 +145,17 @@
 
         void showInputMethodPickerFromClient(IInputMethodClient client, int auxiliarySubtypeMode);
 
-        @PermissionVerified(Manifest.permission.WRITE_SECURE_SETTINGS)
+        @PermissionVerified(allOf = {
+                Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+                Manifest.permission.WRITE_SECURE_SETTINGS})
         void showInputMethodPickerFromSystem(int auxiliarySubtypeMode, int displayId);
 
         @PermissionVerified(Manifest.permission.TEST_INPUT_METHOD)
         boolean isInputMethodPickerShownForTest();
 
-        @PermissionVerified(Manifest.permission.WRITE_SECURE_SETTINGS)
+        @PermissionVerified(allOf = {
+                Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+                Manifest.permission.WRITE_SECURE_SETTINGS})
         void onImeSwitchButtonClickFromSystem(int displayId);
 
         InputMethodSubtype getCurrentInputMethodSubtype(@UserIdInt int userId);
@@ -153,8 +170,10 @@
 
         void reportPerceptibleAsync(IBinder windowToken, boolean perceptible);
 
-        @PermissionVerified(Manifest.permission.INTERNAL_SYSTEM_WINDOW)
-        void removeImeSurface();
+        @PermissionVerified(allOf = {
+                Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+                Manifest.permission.INTERNAL_SYSTEM_WINDOW})
+        void removeImeSurface(int displayId);
 
         void removeImeSurfaceFromWindowAsync(IBinder windowToken);
 
@@ -330,13 +349,14 @@
         mCallback.showInputMethodPickerFromClient(client, auxiliarySubtypeMode);
     }
 
-    @EnforcePermission(Manifest.permission.WRITE_SECURE_SETTINGS)
+    @EnforcePermission(allOf = {
+            Manifest.permission.WRITE_SECURE_SETTINGS,
+            Manifest.permission.INTERACT_ACROSS_USERS_FULL})
     @Override
     public void showInputMethodPickerFromSystem(int auxiliarySubtypeMode, int displayId) {
         super.showInputMethodPickerFromSystem_enforcePermission();
 
         mCallback.showInputMethodPickerFromSystem(auxiliarySubtypeMode, displayId);
-
     }
 
     @EnforcePermission(Manifest.permission.TEST_INPUT_METHOD)
@@ -347,7 +367,9 @@
         return mCallback.isInputMethodPickerShownForTest();
     }
 
-    @EnforcePermission(Manifest.permission.WRITE_SECURE_SETTINGS)
+    @EnforcePermission(allOf = {
+            Manifest.permission.WRITE_SECURE_SETTINGS,
+            Manifest.permission.INTERACT_ACROSS_USERS_FULL})
     @Override
     public void onImeSwitchButtonClickFromSystem(int displayId) {
         super.onImeSwitchButtonClickFromSystem_enforcePermission();
@@ -382,12 +404,14 @@
         mCallback.reportPerceptibleAsync(windowToken, perceptible);
     }
 
-    @EnforcePermission(Manifest.permission.INTERNAL_SYSTEM_WINDOW)
+    @EnforcePermission(allOf = {
+            Manifest.permission.INTERNAL_SYSTEM_WINDOW,
+            Manifest.permission.INTERACT_ACROSS_USERS_FULL})
     @Override
-    public void removeImeSurface() {
+    public void removeImeSurface(int displayId) {
         super.removeImeSurface_enforcePermission();
 
-        mCallback.removeImeSurface();
+        mCallback.removeImeSurface(displayId);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java b/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java
index 7ebf595..42a99de 100644
--- a/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java
+++ b/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java
@@ -36,8 +36,10 @@
 import static com.android.server.inputmethod.InputMethodManagerService.computeImeDisplayIdForTarget;
 
 import android.accessibilityservice.AccessibilityService;
+import android.annotation.AnyThread;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.UserIdInt;
 import android.content.res.Configuration;
 import android.os.Binder;
@@ -83,6 +85,7 @@
      * A map used to track the requested IME target window and its state. The key represents the
      * token of the window and the value is the corresponding IME window state.
      */
+    @GuardedBy("ImfLock.class")
     private final WeakHashMap<IBinder, ImeTargetWindowState> mRequestWindowStateMap =
             new WeakHashMap<>();
 
@@ -93,6 +96,7 @@
      * @see InputMethodManager#HIDE_IMPLICIT_ONLY that system will not hide IME when the value is
      * {@code true}.
      */
+    @GuardedBy("ImfLock.class")
     boolean mRequestedShowExplicitly;
 
     /**
@@ -101,25 +105,39 @@
      * @see InputMethodManager#SHOW_FORCED
      * @see InputMethodManager#HIDE_NOT_ALWAYS
      */
+    @GuardedBy("ImfLock.class")
     boolean mShowForced;
 
     /**
      * Set if we last told the input method to show itself.
      */
+    @GuardedBy("ImfLock.class")
     private boolean mInputShown;
 
     /**
      * Set if we called
      * {@link com.android.server.wm.ImeTargetVisibilityPolicy#showImeScreenshot(IBinder, int)}.
      */
+    @GuardedBy("ImfLock.class")
     private boolean mRequestedImeScreenshot;
 
-    /** The window token of the current visible IME layering target overlay. */
-    private IBinder mCurVisibleImeLayeringOverlay;
+    /** Whether there is a visible IME layering target overlay. */
+    @GuardedBy("ImfLock.class")
+    private boolean mHasVisibleImeLayeringOverlay;
 
     /** The window token of the current visible IME input target. */
+    @GuardedBy("ImfLock.class")
     private IBinder mCurVisibleImeInputTarget;
 
+    /**
+     * The last window token that we confirmed that IME started talking to.  This is always updated
+     * upon reports from the input method.  If the window state is already changed before the report
+     * is handled, this field just keeps the last value.
+     */
+    @GuardedBy("ImfLock.class")
+    @Nullable
+    private IBinder mLastImeTargetWindow;
+
     /** Represent the invalid IME visibility state */
     public static final int STATE_INVALID = -1;
 
@@ -200,28 +218,37 @@
         mPolicy = imePolicy;
         mWindowManagerInternal.setInputMethodTargetChangeListener(new ImeTargetChangeListener() {
             @Override
-            public void onImeTargetOverlayVisibilityChanged(IBinder overlayWindowToken,
+            public void onImeTargetOverlayVisibilityChanged(@NonNull IBinder overlayWindowToken,
                     @WindowManager.LayoutParams.WindowType int windowType, boolean visible,
                     boolean removed) {
-                mCurVisibleImeLayeringOverlay =
-                        // Ignoring the starting window since it's ok to cover the IME target
-                        // window in temporary without affecting the IME visibility.
-                        (visible && !removed && windowType != TYPE_APPLICATION_STARTING)
-                                ? overlayWindowToken : null;
+                // Ignoring the starting window since it's ok to cover the IME target
+                // window in temporary without affecting the IME visibility.
+                final boolean hasOverlay = visible && !removed
+                        && windowType != TYPE_APPLICATION_STARTING;
+                synchronized (ImfLock.class) {
+                    mHasVisibleImeLayeringOverlay = hasOverlay;
+                }
             }
 
             @Override
             public void onImeInputTargetVisibilityChanged(IBinder imeInputTarget,
                     boolean visibleRequested, boolean removed) {
-                if (mCurVisibleImeInputTarget == imeInputTarget && (!visibleRequested || removed)
-                        && mCurVisibleImeLayeringOverlay != null) {
-                    final int reason = SoftInputShowHideReason.HIDE_WHEN_INPUT_TARGET_INVISIBLE;
-                    final var statsToken = ImeTracker.forLogging().onStart(ImeTracker.TYPE_HIDE,
-                            ImeTracker.ORIGIN_SERVER, reason, false /* fromUser */);
-                    mService.onApplyImeVisibilityFromComputer(imeInputTarget, statsToken,
-                            new ImeVisibilityResult(STATE_HIDE_IME_EXPLICIT, reason));
+                final boolean visibleAndNotRemoved = visibleRequested && !removed;
+                synchronized (ImfLock.class) {
+                    if (visibleAndNotRemoved) {
+                        mCurVisibleImeInputTarget = imeInputTarget;
+                        return;
+                    }
+                    if (mHasVisibleImeLayeringOverlay
+                            && mCurVisibleImeInputTarget == imeInputTarget) {
+                        final int reason = SoftInputShowHideReason.HIDE_WHEN_INPUT_TARGET_INVISIBLE;
+                        final var statsToken = ImeTracker.forLogging().onStart(ImeTracker.TYPE_HIDE,
+                                ImeTracker.ORIGIN_SERVER, reason, false /* fromUser */);
+                        mService.onApplyImeVisibilityFromComputerLocked(imeInputTarget, statsToken,
+                                new ImeVisibilityResult(STATE_HIDE_IME_EXPLICIT, reason));
+                    }
+                    mCurVisibleImeInputTarget = null;
                 }
-                mCurVisibleImeInputTarget = (visibleRequested && !removed) ? imeInputTarget : null;
             }
         });
     }
@@ -232,6 +259,7 @@
      * @param statsToken The token tracking the current IME request.
      * @return {@code true} when the show request can proceed.
      */
+    @GuardedBy("ImfLock.class")
     boolean onImeShowFlags(@NonNull ImeTracker.Token statsToken,
             @InputMethodManager.ShowFlags int showFlags) {
         if (mPolicy.mA11yRequestingNoSoftKeyboard || mPolicy.mImeHiddenByDisplayPolicy) {
@@ -258,6 +286,7 @@
      * @param statsToken The token tracking the current IME request.
      * @return {@code true} when the hide request can proceed.
      */
+    @GuardedBy("ImfLock.class")
     boolean canHideIme(@NonNull ImeTracker.Token statsToken,
             @InputMethodManager.HideFlags int hideFlags) {
         if ((hideFlags & InputMethodManager.HIDE_IMPLICIT_ONLY) != 0
@@ -279,6 +308,7 @@
      * Returns the show flags for IME. This translates from {@link InputMethodManager.ShowFlags}
      * to {@link InputMethod.ShowFlags}.
      */
+    @GuardedBy("ImfLock.class")
     @InputMethod.ShowFlags
     int getShowFlagsForInputMethodServiceOnly() {
         int flags = 0;
@@ -294,6 +324,7 @@
      * Returns the show flags for IMM. This translates from {@link InputMethod.ShowFlags}
      * to {@link InputMethodManager.ShowFlags}.
      */
+    @GuardedBy("ImfLock.class")
     @InputMethodManager.ShowFlags
     int getShowFlags() {
         int flags = 0;
@@ -305,12 +336,14 @@
         return flags;
     }
 
+    @GuardedBy("ImfLock.class")
     void clearImeShowFlags() {
         mRequestedShowExplicitly = false;
         mShowForced = false;
         mInputShown = false;
     }
 
+    @GuardedBy("ImfLock.class")
     int computeImeDisplayId(@NonNull ImeTargetWindowState state, int displayId) {
         final int displayToShowIme = computeImeDisplayIdForTarget(displayId, mImeDisplayValidator);
         state.setImeDisplayId(displayToShowIme);
@@ -328,6 +361,7 @@
      *                            visibility state, it could be {@link #STATE_SHOW_IME} or
      *                            {@link #STATE_HIDE_IME}.
      */
+    @GuardedBy("ImfLock.class")
     void requestImeVisibility(IBinder windowToken, boolean showIme) {
         ImeTargetWindowState state = getOrCreateWindowState(windowToken);
         if (!mPolicy.mPendingA11yRequestingHideKeyboard) {
@@ -343,6 +377,7 @@
         setWindowStateInner(windowToken, state);
     }
 
+    @GuardedBy("ImfLock.class")
     ImeTargetWindowState getOrCreateWindowState(IBinder windowToken) {
         ImeTargetWindowState state = mRequestWindowStateMap.get(windowToken);
         if (state == null) {
@@ -351,11 +386,13 @@
         return state;
     }
 
+    @GuardedBy("ImfLock.class")
     ImeTargetWindowState getWindowStateOrNull(IBinder windowToken) {
         ImeTargetWindowState state = mRequestWindowStateMap.get(windowToken);
         return state;
     }
 
+    @GuardedBy("ImfLock.class")
     void setWindowState(IBinder windowToken, @NonNull ImeTargetWindowState newState) {
         final ImeTargetWindowState state = mRequestWindowStateMap.get(windowToken);
         if (state != null && newState.hasEditorFocused()
@@ -367,6 +404,7 @@
         setWindowStateInner(windowToken, newState);
     }
 
+    @GuardedBy("ImfLock.class")
     private void setWindowStateInner(IBinder windowToken, @NonNull ImeTargetWindowState newState) {
         if (DEBUG) Slog.d(TAG, "setWindowStateInner, windowToken=" + windowToken
                 + ", state=" + newState);
@@ -391,6 +429,7 @@
         }
     }
 
+    @GuardedBy("ImfLock.class")
     ImeVisibilityResult computeState(ImeTargetWindowState state, boolean allowVisible) {
         // TODO: Output the request IME visibility state according to the requested window state
         final int softInputVisibility = state.mSoftInputModeState & SOFT_INPUT_MASK_STATE;
@@ -452,8 +491,7 @@
                 break;
             case WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED:
                 // Do nothing but preserving the last IME requested visibility state.
-                final ImeTargetWindowState lastState =
-                        getWindowStateOrNull(mService.mLastImeTargetWindow);
+                final ImeTargetWindowState lastState = getWindowStateOrNull(mLastImeTargetWindow);
                 if (lastState != null) {
                     state.setRequestedImeVisible(lastState.mRequestedImeVisible);
                 }
@@ -540,7 +578,7 @@
         return null;
     }
 
-    @VisibleForTesting
+    @GuardedBy("ImfLock.class")
     ImeVisibilityResult onInteractiveChanged(IBinder windowToken, boolean interactive) {
         final ImeTargetWindowState state = getWindowStateOrNull(windowToken);
         if (state != null && state.isRequestedImeVisible() && mInputShown && !interactive) {
@@ -568,6 +606,7 @@
         return userData.mImeBindingState.mFocusedWindow;
     }
 
+    @GuardedBy("ImfLock.class")
     IBinder getWindowTokenFrom(ImeTargetWindowState windowState) {
         for (IBinder windowToken : mRequestWindowStateMap.keySet()) {
             final ImeTargetWindowState state = mRequestWindowStateMap.get(windowToken);
@@ -578,6 +617,7 @@
         return null;
     }
 
+    @GuardedBy("ImfLock.class")
     boolean shouldRestoreImeVisibility(@NonNull ImeTargetWindowState state) {
         final int softInputMode = state.getSoftInputModeState();
         switch (softInputMode & WindowManager.LayoutParams.SOFT_INPUT_MASK_STATE) {
@@ -591,14 +631,28 @@
         return mWindowManagerInternal.shouldRestoreImeVisibility(getWindowTokenFrom(state));
     }
 
+    @GuardedBy("ImfLock.class")
     boolean isInputShown() {
         return mInputShown;
     }
 
+    @GuardedBy("ImfLock.class")
     void setInputShown(boolean inputShown) {
         mInputShown = inputShown;
     }
 
+    @GuardedBy("ImfLock.class")
+    @Nullable
+    IBinder getLastImeTargetWindow() {
+        return mLastImeTargetWindow;
+    }
+
+    @GuardedBy("ImfLock.class")
+    void setLastImeTargetWindow(@Nullable IBinder imeTargetWindow) {
+        mLastImeTargetWindow = imeTargetWindow;
+    }
+
+    @GuardedBy("ImfLock.class")
     void dumpDebug(ProtoOutputStream proto, long fieldId) {
         proto.write(SHOW_EXPLICITLY_REQUESTED, mRequestedShowExplicitly);
         proto.write(SHOW_FORCED, mShowForced);
@@ -607,12 +661,14 @@
         proto.write(INPUT_SHOWN, mInputShown);
     }
 
+    @GuardedBy("ImfLock.class")
     void dump(@NonNull PrintWriter pw, @NonNull String prefix) {
         final Printer p = new PrintWriterPrinter(pw);
         p.println(prefix + "mRequestedShowExplicitly=" + mRequestedShowExplicitly
                 + " mShowForced=" + mShowForced);
         p.println(prefix + "mImeHiddenByDisplayPolicy=" + mPolicy.isImeHiddenByDisplayPolicy());
         p.println(prefix + "mInputShown=" + mInputShown);
+        p.println(prefix + "mLastImeTargetWindow=" + mLastImeTargetWindow);
     }
 
     /**
@@ -629,12 +685,14 @@
          *
          * This prevents the IME from showing when it otherwise may have shown.
          */
+        @GuardedBy("ImfLock.class")
         private boolean mImeHiddenByDisplayPolicy;
 
         /**
          * Set when the accessibility service requests to hide IME by
          * {@link AccessibilityService.SoftKeyboardController#setShowMode}
          */
+        @GuardedBy("ImfLock.class")
         private boolean mA11yRequestingNoSoftKeyboard;
 
         /**
@@ -643,16 +701,20 @@
          * {@link android.provider.Settings.Secure#ACCESSIBILITY_SOFT_KEYBOARD_MODE} without
          * changing the requested IME visible state.
          */
+        @GuardedBy("ImfLock.class")
         private boolean mPendingA11yRequestingHideKeyboard;
 
+        @GuardedBy("ImfLock.class")
         void setImeHiddenByDisplayPolicy(boolean hideIme) {
             mImeHiddenByDisplayPolicy = hideIme;
         }
 
+        @GuardedBy("ImfLock.class")
         boolean isImeHiddenByDisplayPolicy() {
             return mImeHiddenByDisplayPolicy;
         }
 
+        @GuardedBy("ImfLock.class")
         void setA11yRequestNoSoftKeyboard(int keyboardShowMode) {
             mA11yRequestingNoSoftKeyboard =
                     (keyboardShowMode & AccessibilityService.SHOW_MODE_MASK) == SHOW_MODE_HIDDEN;
@@ -661,11 +723,13 @@
             }
         }
 
+        @GuardedBy("ImfLock.class")
         boolean isA11yRequestNoSoftKeyboard() {
             return mA11yRequestingNoSoftKeyboard;
         }
     }
 
+    @GuardedBy("ImfLock.class")
     ImeVisibilityPolicy getImePolicy() {
         return mPolicy;
     }
@@ -721,63 +785,78 @@
         /**
          * Set if the client has asked for the input method to be shown.
          */
+        @GuardedBy("ImfLock.class")
         private boolean mRequestedImeVisible;
 
         /**
          * A identifier for knowing the requester of {@link InputMethodManager#showSoftInput} or
          * {@link InputMethodManager#hideSoftInputFromWindow}.
          */
+        @GuardedBy("ImfLock.class")
         private IBinder mRequestImeToken;
 
         /**
          * The IME target display id for which the latest startInput was called.
          */
+        @GuardedBy("ImfLock.class")
         private int mImeDisplayId = DEFAULT_DISPLAY;
 
+        @AnyThread
         boolean hasImeFocusChanged() {
             return mImeFocusChanged;
         }
 
+        @AnyThread
         boolean hasEditorFocused() {
             return mHasFocusedEditor;
         }
 
+        @AnyThread
         boolean isStartInputByGainFocus() {
             return mIsStartInputByGainFocus;
         }
 
+        @AnyThread
         int getSoftInputModeState() {
             return mSoftInputModeState;
         }
 
+        @AnyThread
         int getWindowFlags() {
             return mWindowFlags;
         }
 
+        @AnyThread
         int getToolType() {
             return mToolType;
         }
 
+        @GuardedBy("ImfLock.class")
         private void setImeDisplayId(int imeDisplayId) {
             mImeDisplayId = imeDisplayId;
         }
 
+        @GuardedBy("ImfLock.class")
         int getImeDisplayId() {
             return mImeDisplayId;
         }
 
+        @GuardedBy("ImfLock.class")
         private void setRequestedImeVisible(boolean requestedImeVisible) {
             mRequestedImeVisible = requestedImeVisible;
         }
 
+        @GuardedBy("ImfLock.class")
         boolean isRequestedImeVisible() {
             return mRequestedImeVisible;
         }
 
+        @GuardedBy("ImfLock.class")
         void setRequestImeToken(IBinder token) {
             mRequestImeToken = token;
         }
 
+        @GuardedBy("ImfLock.class")
         IBinder getRequestImeToken() {
             return mRequestImeToken;
         }
diff --git a/services/core/java/com/android/server/inputmethod/ImmutableSparseArray.java b/services/core/java/com/android/server/inputmethod/ImmutableSparseArray.java
new file mode 100644
index 0000000..382aa8a
--- /dev/null
+++ b/services/core/java/com/android/server/inputmethod/ImmutableSparseArray.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.inputmethod;
+
+
+import android.annotation.AnyThread;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.SparseArray;
+
+import java.util.function.Consumer;
+
+/**
+ * A holder object to expose {@link SparseArray} to multiple threads in a thread-safe manner through
+ * "final Field Semantics" defined in JLS 17.5, with only exposing thread-safe methods such as
+ * {@link SparseArray#get(int)} and {@link SparseArray#size()} from {@link SparseArray}, and with
+ * adding clone-with-update style methods {@link #cloneWithPutOrSelf(int, Object)} and
+ * {@link #cloneWithRemoveOrSelf(int)} instead of exposing mutation methods.
+ *
+ * @param <E> Type of the element
+ */
+final class ImmutableSparseArray<E> {
+    @NonNull
+    private final SparseArray<E> mArray;
+
+    private static final ImmutableSparseArray<Object> EMPTY =
+            new ImmutableSparseArray<>(new SparseArray<>());
+
+    /**
+     * Returns an empty {@link ImmutableSparseArray} instance.
+     *
+     * @return An empty {@link ImmutableSparseArray} instance.
+     * @param <T> Type of the element
+     */
+    @SuppressWarnings("unchecked")
+    @AnyThread
+    @NonNull
+    static <T> ImmutableSparseArray<T> empty() {
+        return (ImmutableSparseArray<T>) EMPTY;
+    }
+
+    private ImmutableSparseArray(@NonNull SparseArray<E> array) {
+        mArray = array;
+    }
+
+    /**
+     * @return the size of this array
+     */
+    @AnyThread
+    int size() {
+        return mArray.size();
+    }
+
+    /**
+     * Returns the key of the specified index.
+     *
+     * @return the key of the specified index
+     * @throws ArrayIndexOutOfBoundsException when the index is out of range
+     */
+    @AnyThread
+    int keyAt(int index) {
+        return mArray.keyAt(index);
+    }
+
+    /**
+     * Returns the value of the specified index.
+     *
+     * @return the value of the specified index
+     * @throws ArrayIndexOutOfBoundsException when the index is out of range
+     */
+    @AnyThread
+    @Nullable
+    public E valueAt(int index) {
+        return mArray.valueAt(index);
+    }
+
+    /**
+     * Returns the index of the specified key.
+     *
+     * @return the index of the specified key if exists. Otherwise {@code -1}
+     */
+    @AnyThread
+    int indexOfKey(int key) {
+        return mArray.indexOfKey(key);
+    }
+
+    /**
+     * Returns {@code true} if the given {@code key} exists.
+     *
+     * @param key the key to be queried
+     * @return    {@code true} if the given {@code key} exists
+     */
+    @AnyThread
+    boolean contains(int key) {
+        return mArray.contains(key);
+    }
+
+    /**
+     * Returns the value associated with the {@code key}.
+     *
+     * @param key the key to be queried
+     * @return    the value associated with the {@code key} if exists. Otherwise {@code null}
+     */
+    @AnyThread
+    @Nullable
+    E get(int key) {
+        return mArray.get(key);
+    }
+
+    /**
+     * Run {@link Consumer} for each value.
+     *
+     * @param consumer {@link Consumer} to be called back
+     */
+    @AnyThread
+    void forEach(@NonNull Consumer<E> consumer) {
+        final int size = mArray.size();
+        for (int i = 0; i < size; ++i) {
+            consumer.accept(mArray.valueAt(i));
+        }
+    }
+
+    /**
+     * Returns an instance of {@link ImmutableSparseArray} that has the given key and value on top
+     * of items cloned from this instance.
+     *
+     * @param key   the key to be added
+     * @param value the value to be added
+     * @return      the same {@link ImmutableSparseArray} instance if there is actually no update.
+     *              Otherwise, a new instance of {@link ImmutableSparseArray}
+     */
+    @AnyThread
+    @NonNull
+    ImmutableSparseArray<E> cloneWithPutOrSelf(int key, @Nullable E value) {
+        final var prevKeyIndex = mArray.indexOfKey(key);
+        if (prevKeyIndex >= 0) {
+            final var prevValue = mArray.valueAt(prevKeyIndex);
+            if (prevValue == value) {
+                return this;
+            }
+        }
+        final var clone = mArray.clone();
+        clone.put(key, value);
+        return new ImmutableSparseArray<>(clone);
+    }
+
+    /**
+     * Returns an instance of {@link ImmutableSparseArray} that does not have the given key on top
+     * of items cloned from this instance.
+     *
+     * @param key the key to be removed
+     * @return    the same {@link ImmutableSparseArray} instance if there is actually no update.
+     *            Otherwise, a new instance of {@link ImmutableSparseArray}
+     */
+    @AnyThread
+    @NonNull
+    ImmutableSparseArray<E> cloneWithRemoveOrSelf(int key) {
+        final int index = indexOfKey(key);
+        if (index < 0) {
+            return this;
+        }
+        if (mArray.size() == 1) {
+            return empty();
+        }
+        final var clone = mArray.clone();
+        clone.remove(key);
+        return new ImmutableSparseArray<>(clone);
+    }
+}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
index 9837ab1..03cbab5 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
@@ -463,7 +463,7 @@
                     // should now try to restart the service for us.
                     mLastBindTime = SystemClock.uptimeMillis();
                     clearCurMethodAndSessions();
-                    mService.clearInputShownLocked();
+                    mService.mVisibilityStateComputer.setInputShown(false);
                     mService.unbindCurrentClientLocked(UnbindReason.DISCONNECT_IME, mUserId);
                 }
             }
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
index c82e5be..dba0465 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
@@ -27,6 +27,7 @@
 import android.os.IBinder;
 import android.view.inputmethod.InlineSuggestionsRequest;
 import android.view.inputmethod.InputMethodInfo;
+import android.view.inputmethod.InputMethodSubtype;
 
 import com.android.internal.inputmethod.IAccessibilityInputMethodSession;
 import com.android.internal.inputmethod.InlineSuggestionsRequestCallback;
@@ -74,13 +75,13 @@
     public abstract void setInteractive(boolean interactive);
 
     /**
-     * Hides the input methods for all the users, if visible.
+     * Hides the input method for the specified {@code originatingDisplayId}, if visible.
      *
      * @param reason               the reason for hiding the current input method
      * @param originatingDisplayId the display ID the request is originated
      */
     @ImfLockFree
-    public abstract void hideAllInputMethods(@SoftInputShowHideReason int reason,
+    public abstract void hideInputMethod(@SoftInputShowHideReason int reason,
             int originatingDisplayId);
 
     /**
@@ -89,6 +90,8 @@
      * @param userId the user ID to be queried
      * @return a list of {@link InputMethodInfo}. VR-only IMEs are already excluded
      */
+    @ImfLockFree
+    @NonNull
     public abstract List<InputMethodInfo> getInputMethodListAsUser(@UserIdInt int userId);
 
     /**
@@ -97,9 +100,24 @@
      * @param userId the user ID to be queried
      * @return a list of {@link InputMethodInfo} that are enabled for {@code userId}
      */
+    @ImfLockFree
+    @NonNull
     public abstract List<InputMethodInfo> getEnabledInputMethodListAsUser(@UserIdInt int userId);
 
     /**
+     * Returns the list of installed input methods that are enabled for the specified user.
+     *
+     * @param imiId                           IME ID to be queried about
+     * @param allowsImplicitlyEnabledSubtypes {@code true} to return the implicitly enabled subtypes
+     * @param userId                          the user ID to be queried about
+     * @return a list of {@link InputMethodSubtype} that are enabled for {@code userId}
+     */
+    @ImfLockFree
+    @NonNull
+    public abstract List<InputMethodSubtype> getEnabledInputMethodSubtypeListAsUser(
+            String imiId, boolean allowsImplicitlyEnabledSubtypes, @UserIdInt int userId);
+
+    /**
      * Called by the Autofill Frameworks to request an {@link InlineSuggestionsRequest} from
      * the input method.
      *
@@ -297,21 +315,33 @@
 
                 @ImfLockFree
                 @Override
-                public void hideAllInputMethods(@SoftInputShowHideReason int reason,
+                public void hideInputMethod(@SoftInputShowHideReason int reason,
                         int originatingDisplayId) {
                 }
 
+                @ImfLockFree
+                @NonNull
                 @Override
                 public List<InputMethodInfo> getInputMethodListAsUser(@UserIdInt int userId) {
                     return Collections.emptyList();
                 }
 
+                @ImfLockFree
+                @NonNull
                 @Override
                 public List<InputMethodInfo> getEnabledInputMethodListAsUser(
                         @UserIdInt int userId) {
                     return Collections.emptyList();
                 }
 
+                @ImfLockFree
+                @NonNull
+                @Override
+                public List<InputMethodSubtype> getEnabledInputMethodSubtypeListAsUser(String imiId,
+                        boolean allowsImplicitlyEnabledSubtypes, int userId) {
+                    return Collections.emptyList();
+                }
+
                 @Override
                 public void onCreateInlineSuggestionsRequest(@UserIdInt int userId,
                         InlineSuggestionsRequestInfo requestInfo,
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index a9e9dcf..76380b7 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -81,6 +81,7 @@
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
 import android.content.pm.UserInfo;
+import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.hardware.input.InputManager;
 import android.inputmethodservice.InputMethodService;
@@ -170,6 +171,7 @@
 import com.android.internal.inputmethod.UnbindReason;
 import com.android.internal.os.TransferPipe;
 import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.CollectionUtils;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.Preconditions;
 import com.android.server.AccessibilityManagerInternal;
@@ -252,12 +254,9 @@
     private @interface MultiUserUnawareField {
     }
 
-    private static final int MSG_SHOW_IM_SUBTYPE_PICKER = 1;
-
-    private static final int MSG_HIDE_ALL_INPUT_METHODS = 1035;
+    private static final int MSG_HIDE_INPUT_METHOD = 1035;
     private static final int MSG_REMOVE_IME_SURFACE = 1060;
     private static final int MSG_REMOVE_IME_SURFACE_FROM_WINDOW = 1061;
-    private static final int MSG_UPDATE_IME_WINDOW_STATUS = 1070;
 
     private static final int MSG_RESET_HANDWRITING = 1090;
     private static final int MSG_START_HANDWRITING = 1100;
@@ -270,7 +269,6 @@
 
     private static final int MSG_HARD_KEYBOARD_SWITCH_CHANGED = 4000;
 
-    private static final int MSG_SYSTEM_UNLOCK_USER = 5000;
     private static final int MSG_DISPATCH_ON_INPUT_METHOD_LIST_UPDATED = 5010;
 
     private static final int MSG_NOTIFY_IME_UID_TO_AUDIO_SERVICE = 7000;
@@ -305,6 +303,28 @@
     private final String[] mNonPreemptibleInputMethods;
 
     /**
+     * Whether the new Input Method Switcher menu is enabled.
+     *
+     * @see #shouldEnableNewInputMethodSwitcherMenu
+     */
+    @SharedByAllUsersField
+    private final boolean mNewInputMethodSwitcherMenuEnabled;
+
+    /**
+     * Returns {@code true} if the new Input Method Switcher menu is enabled. This will be
+     * {@code false} for watches and small screen devices.
+     *
+     * @param context the context to check the device configuration for.
+     */
+    private static boolean shouldEnableNewInputMethodSwitcherMenu(@NonNull Context context) {
+        final boolean isWatch = context.getPackageManager()
+                .hasSystemFeature(PackageManager.FEATURE_WATCH);
+        final boolean isSmallScreen = (context.getResources().getConfiguration().screenLayout
+                & Configuration.SCREENLAYOUT_SIZE_MASK) == Configuration.SCREENLAYOUT_SIZE_SMALL;
+        return Flags.imeSwitcherRevamp() && !isWatch && !isSmallScreen;
+    }
+
+    /**
      * See {@link #shouldEnableConcurrentMultiUserMode(Context)} about when set to be {@code true}.
      */
     @SharedByAllUsersField
@@ -339,6 +359,35 @@
         return mConcurrentMultiUserModeEnabled ? callingProcessUserId : mCurrentUserId;
     }
 
+    /**
+     * Figures out the target IME user ID associated with the given {@code displayId}.
+     *
+     * @param displayId the display ID to be queried about
+     * @return User ID to be used for this {@code displayId}.
+     */
+    @GuardedBy("ImfLock.class")
+    @UserIdInt
+    private int resolveImeUserIdFromDisplayIdLocked(int displayId) {
+        return mConcurrentMultiUserModeEnabled
+                ? mUserManagerInternal.getUserAssignedToDisplay(displayId) : mCurrentUserId;
+    }
+
+    /**
+     * Figures out the target IME user ID associated with the given {@code windowToken}.
+     *
+     * @param windowToken the Window token to be queried about
+     * @return User ID to be used for this {@code displayId}.
+     */
+    @GuardedBy("ImfLock.class")
+    @UserIdInt
+    private int resolveImeUserIdFromWindowLocked(@NonNull IBinder windowToken) {
+        if (mConcurrentMultiUserModeEnabled) {
+            final int displayId = mWindowManagerInternal.getDisplayIdForWindow(windowToken);
+            return mUserManagerInternal.getUserAssignedToDisplay(displayId);
+        }
+        return mCurrentUserId;
+    }
+
     final Context mContext;
     final Resources mRes;
     private final Handler mHandler;
@@ -372,7 +421,7 @@
     @GuardedBy("ImfLock.class")
     @MultiUserUnawareField
     @NonNull
-    private final ImeVisibilityStateComputer mVisibilityStateComputer;
+    final ImeVisibilityStateComputer mVisibilityStateComputer;
 
     @GuardedBy("ImfLock.class")
     @SharedByAllUsersField
@@ -423,6 +472,9 @@
         IInputMethodSession mSession;
         InputChannel mChannel;
 
+        @UserIdInt
+        final int mUserId;
+
         @Override
         public String toString() {
             return "SessionState{uid=" + mClient.mUid + " pid=" + mClient.mPid
@@ -431,15 +483,17 @@
                     + " session=" + Integer.toHexString(
                     System.identityHashCode(mSession))
                     + " channel=" + mChannel
+                    + " userId=" + mUserId
                     + "}";
         }
 
         SessionState(ClientState client, IInputMethodInvoker method,
-                IInputMethodSession session, InputChannel channel) {
+                IInputMethodSession session, InputChannel channel, @UserIdInt int userId) {
             mClient = client;
             mMethod = method;
             mSession = session;
             mChannel = channel;
+            mUserId = userId;
         }
     }
 
@@ -495,14 +549,6 @@
     }
 
     /**
-     * The last window token that we confirmed that IME started talking to.  This is always updated
-     * upon reports from the input method.  If the window state is already changed before the report
-     * is handled, this field just keeps the last value.
-     */
-    @MultiUserUnawareField
-    IBinder mLastImeTargetWindow;
-
-    /**
      * Map of window perceptible states indexed by their associated window tokens.
      *
      * The value {@code true} indicates that IME has not been mostly hidden via
@@ -569,13 +615,12 @@
 
     @GuardedBy("ImfLock.class")
     private void onSecureSettingsChangedLocked(@NonNull String key, @UserIdInt int userId) {
-        if (!mConcurrentMultiUserModeEnabled && userId != mCurrentUserId) {
-            return;
-        }
         switch (key) {
             case Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD: {
-                if (!Flags.imeSwitcherRevamp()) {
-                    mMenuController.updateKeyboardFromSettingsLocked();
+                if (!mNewInputMethodSwitcherMenuEnabled) {
+                    if (userId == mCurrentUserId) {
+                        mMenuController.updateKeyboardFromSettingsLocked(userId);
+                    }
                 }
                 break;
             }
@@ -641,14 +686,14 @@
                         }
                     }
                 }
-                if (Flags.imeSwitcherRevamp()) {
+                if (mNewInputMethodSwitcherMenuEnabled) {
                     synchronized (ImfLock.class) {
                         final var bindingController = getInputMethodBindingController(senderUserId);
                         mMenuControllerNew.hide(bindingController.getCurTokenDisplayId(),
                                 senderUserId);
                     }
                 } else {
-                    mMenuController.hideInputMethodMenu();
+                    mMenuController.hideInputMethodMenu(senderUserId);
                 }
             } else {
                 Slog.w(TAG, "Unexpected intent " + intent);
@@ -662,6 +707,7 @@
      * <p>Note: For historical reasons, {@link Intent#ACTION_LOCALE_CHANGED} has been sent to all
      * the users.</p>
      */
+    @WorkerThread
     void onActionLocaleChanged(@NonNull LocaleList prevLocales, @NonNull LocaleList newLocales) {
         if (DEBUG) {
             Slog.d(TAG, "onActionLocaleChanged prev=" + prevLocales + " new=" + newLocales);
@@ -671,19 +717,23 @@
                 return;
             }
             for (int userId : mUserManagerInternal.getUserIds()) {
-                final InputMethodSettings settings = queryInputMethodServicesInternal(
-                        mContext,
-                        userId,
-                        AdditionalSubtypeMapRepository.get(userId),
-                        DirectBootAwareness.AUTO);
-                InputMethodSettingsRepository.put(userId, settings);
-
-                if (mConcurrentMultiUserModeEnabled || userId == mCurrentUserId) {
-                    postInputMethodSettingUpdatedLocked(true /* resetDefaultEnabledIme */, userId);
-                    // If the locale is changed, needs to reset the default ime
-                    resetDefaultImeLocked(mContext, userId);
-                    updateFromSettingsLocked(true, userId);
+                // Does InputMethodInfo really have data dependency on system locale?
+                // TODO(b/356679261): Check if we really need to update RawInputMethodInfo here.
+                {
+                    final var userData = getUserData(userId);
+                    final var additionalSubtypeMap = AdditionalSubtypeMapRepository.get(userId);
+                    final var rawMethodMap = queryRawInputMethodServiceMap(mContext, userId);
+                    userData.mRawInputMethodMap.set(rawMethodMap);
+                    final var methodMap = rawMethodMap.toInputMethodMap(additionalSubtypeMap,
+                            DirectBootAwareness.AUTO,
+                            mUserManagerInternal.isUserUnlockingOrUnlocked(userId));
+                    final var settings = InputMethodSettings.create(methodMap, userId);
+                    InputMethodSettingsRepository.put(userId, settings);
                 }
+                postInputMethodSettingUpdatedLocked(true /* resetDefaultEnabledIme */, userId);
+                // If the locale is changed, needs to reset the default ime
+                resetDefaultImeLocked(mContext, userId);
+                updateFromSettingsLocked(true, userId);
             }
         }
     }
@@ -753,18 +803,13 @@
 
         private void onFinishPackageChangesInternal() {
             final int userId = getChangingUserId();
+            final var userData = getUserData(userId);
 
             // Instantiating InputMethodInfo requires disk I/O.
             // Do them before acquiring the lock to minimize the chances of ANR (b/340221861).
-            final var newMethodMapWithoutAdditionalSubtypes =
-                    queryInputMethodServicesInternal(mContext, userId,
-                            AdditionalSubtypeMap.EMPTY_MAP, DirectBootAwareness.AUTO)
-                            .getMethodMap();
+            userData.mRawInputMethodMap.set(queryRawInputMethodServiceMap(mContext, userId));
 
             synchronized (ImfLock.class) {
-                final boolean isCurrentUser = (userId == mCurrentUserId);
-                final AdditionalSubtypeMap additionalSubtypeMap =
-                        AdditionalSubtypeMapRepository.get(userId);
                 final InputMethodSettings settings = InputMethodSettingsRepository.get(userId);
 
                 InputMethodInfo curIm = null;
@@ -785,14 +830,7 @@
                     int change = isPackageDisappearing(imi.getPackageName());
                     if (change == PACKAGE_PERMANENT_CHANGE) {
                         Slog.i(TAG, "Input method uninstalled, disabling: " + imi.getComponent());
-                        if (isCurrentUser) {
-                            setInputMethodEnabledLocked(imi.getId(), false, userId);
-                        } else {
-                            settings.buildAndPutEnabledInputMethodsStrRemovingId(
-                                    new StringBuilder(),
-                                    settings.getEnabledInputMethodsAndSubtypeList(),
-                                    imi.getId());
-                        }
+                        setInputMethodEnabledLocked(imi.getId(), false, userId);
                     } else if (change == PACKAGE_UPDATING) {
                         Slog.i(TAG, "Input method reinstalling, clearing additional subtypes: "
                                 + imi.getComponent());
@@ -801,6 +839,7 @@
                 }
 
                 // Clear additional subtypes as a batch operation.
+                final var additionalSubtypeMap = AdditionalSubtypeMapRepository.get(userId);
                 final AdditionalSubtypeMap newAdditionalSubtypeMap =
                         additionalSubtypeMap.cloneWithRemoveOrSelf(imesToClearAdditionalSubtypes);
                 final boolean additionalSubtypeChanged =
@@ -810,8 +849,10 @@
                             settings.getMethodMap());
                 }
 
-                final var newMethodMap = newMethodMapWithoutAdditionalSubtypes
-                        .applyAdditionalSubtypes(newAdditionalSubtypeMap);
+                final var newMethodMap = userData.mRawInputMethodMap.get().toInputMethodMap(
+                        newAdditionalSubtypeMap,
+                        DirectBootAwareness.AUTO,
+                        mUserManagerInternal.isUserUnlockingOrUnlocked(userId));
 
                 if (InputMethodMap.areSame(settings.getMethodMap(), newMethodMap)) {
                     // No update in the actual IME map.
@@ -821,9 +862,6 @@
                 final InputMethodSettings newSettings =
                         InputMethodSettings.create(newMethodMap, userId);
                 InputMethodSettingsRepository.put(userId, newSettings);
-                if (!isCurrentUser) {
-                    return;
-                }
                 postInputMethodSettingUpdatedLocked(false /* resetDefaultEnabledIme */, userId);
 
                 boolean changed = false;
@@ -955,18 +993,14 @@
         @NonNull
         private static InputMethodManagerService createServiceForProduction(
                 @NonNull Context context) {
-            // TODO(b/196206770): Disallow I/O on this thread. Currently it's needed for loading
-            // additional subtypes in switchUserOnHandlerLocked().
             final ServiceThread thread = new ServiceThread(HANDLER_THREAD_NAME,
-                    Process.THREAD_PRIORITY_FOREGROUND, true /* allowIo */);
+                    Process.THREAD_PRIORITY_FOREGROUND, false /* allowIo */);
             thread.start();
 
             final ServiceThread ioThread = new ServiceThread(PACKAGE_MONITOR_THREAD_NAME,
                     Process.THREAD_PRIORITY_FOREGROUND, true /* allowIo */);
             ioThread.start();
 
-            SecureSettingsWrapper.setContentResolver(context.getContentResolver());
-
             return new InputMethodManagerService(context,
                     shouldEnableConcurrentMultiUserMode(context), thread.getLooper(),
                     Handler.createAsync(ioThread.getLooper()),
@@ -1025,7 +1059,6 @@
         public void onUserRemoved(UserInfo user) {
             // Called directly from UserManagerService. Do not block the calling thread.
             final int userId = user.id;
-            SecureSettingsWrapper.onUserRemoved(userId);
             AdditionalSubtypeMapRepository.remove(userId);
             InputMethodSettingsRepository.remove(userId);
             mService.mUserDataRepository.remove(userId);
@@ -1033,10 +1066,26 @@
 
         @Override
         public void onUserUnlocking(@NonNull TargetUser user) {
-            // Called on ActivityManager thread.
-            SecureSettingsWrapper.onUserUnlocking(user.getUserIdentifier());
-            mService.mHandler.obtainMessage(MSG_SYSTEM_UNLOCK_USER, user.getUserIdentifier(), 0)
-                    .sendToTarget();
+            // Called on ActivityManager thread. Do not block the calling thread.
+            final int userId = user.getUserIdentifier();
+            SecureSettingsWrapper.onUserUnlocking(userId);
+            mService.mIoHandler.post(() -> {
+                final var userData = mService.getUserData(userId);
+                final var methodMap = userData.mRawInputMethodMap.get().toInputMethodMap(
+                        AdditionalSubtypeMapRepository.get(userId), DirectBootAwareness.AUTO, true);
+                final var newSettings = InputMethodSettings.create(methodMap, userId);
+                InputMethodSettingsRepository.put(userId, newSettings);
+                synchronized (ImfLock.class) {
+                    if (!mService.mSystemReady) {
+                        return;
+                    }
+                    // We need to rebuild IMEs.
+                    mService.postInputMethodSettingUpdatedLocked(false /* resetDefaultEnabledIme */,
+                            userId);
+                    mService.updateInputMethodsFromSettingsLocked(true /* enabledChanged */,
+                            userId);
+                }
+            });
         }
 
         @Override
@@ -1046,10 +1095,8 @@
             SecureSettingsWrapper.onUserStarting(userId);
             mService.mIoHandler.post(() -> {
                 synchronized (ImfLock.class) {
-                    if (mService.mConcurrentMultiUserModeEnabled) {
-                        if (mService.mCurrentUserId != userId && mService.mSystemReady) {
-                            mService.initializeVisibleBackgroundUserLocked(userId);
-                        }
+                    if (mService.mSystemReady) {
+                        mService.onUserReadyLocked(userId);
                     }
                 }
             });
@@ -1065,19 +1112,22 @@
 
                 for (int userId : userIds) {
                     Slog.d(TAG, "Start initialization for user=" + userId);
-                    final var additionalSubtypeMap =
-                            AdditionalSubtypeMapRepository.ensureInitializedAndGet(userId);
-                    final var settings = InputMethodManagerService.queryInputMethodServicesInternal(
-                            context, userId, additionalSubtypeMap,
-                            DirectBootAwareness.AUTO).getMethodMap();
-                    InputMethodSettingsRepository.put(userId,
-                            InputMethodSettings.create(settings, userId));
+                    final var userData = mService.getUserData(userId);
+
+                    AdditionalSubtypeMapRepository.initializeIfNecessary(userId);
+                    final var additionalSubtypeMap = AdditionalSubtypeMapRepository.get(userId);
+                    final var rawMethodMap = queryRawInputMethodServiceMap(context, userId);
+                    userData.mRawInputMethodMap.set(rawMethodMap);
+                    final var methodMap = rawMethodMap.toInputMethodMap(additionalSubtypeMap,
+                            DirectBootAwareness.AUTO,
+                            userManagerInternal.isUserUnlockingOrUnlocked(userId));
+                    final var settings = InputMethodSettings.create(methodMap, userId);
+                    InputMethodSettingsRepository.put(userId, settings);
 
                     final int profileParentId = userManagerInternal.getProfileParentId(userId);
                     final boolean value =
                             InputMethodDrawsNavBarResourceMonitor.evaluate(context,
                                     profileParentId);
-                    final var userData = mService.getUserData(userId);
                     userData.mImeDrawsNavBar.set(value);
 
                     userData.mBackgroundLoadLatch.countDown();
@@ -1085,22 +1135,21 @@
                 }
             });
         }
-    }
 
-    void onUnlockUser(@UserIdInt int userId) {
-        synchronized (ImfLock.class) {
-            if (DEBUG) {
-                Slog.d(TAG, "onUnlockUser: userId=" + userId + " curUserId=" + mCurrentUserId);
-            }
-            if (!mSystemReady) {
-                return;
-            }
-            final InputMethodSettings newSettings = queryInputMethodServicesInternal(mContext,
-                    userId, AdditionalSubtypeMapRepository.get(userId), DirectBootAwareness.AUTO);
-            InputMethodSettingsRepository.put(userId, newSettings);
-            // We need to rebuild IMEs.
-            postInputMethodSettingUpdatedLocked(false /* resetDefaultEnabledIme */, userId);
-            updateInputMethodsFromSettingsLocked(true /* enabledChanged */, userId);
+        @Override
+        public void onUserStopped(@NonNull TargetUser user) {
+            final int userId = user.getUserIdentifier();
+            // Called on ActivityManager thread.
+            SecureSettingsWrapper.onUserStopped(userId);
+            mService.mIoHandler.post(() -> {
+                final var userData = mService.getUserData(userId);
+                final var additionalSubtypeMap = AdditionalSubtypeMapRepository.get(userId);
+                final var rawMethodMap = userData.mRawInputMethodMap.get();
+                final var methodMap = rawMethodMap.toInputMethodMap(additionalSubtypeMap,
+                        DirectBootAwareness.AUTO, false /* userUnlocked */);
+                InputMethodSettingsRepository.put(userId,
+                        InputMethodSettings.create(methodMap, userId));
+            });
         }
     }
 
@@ -1112,7 +1161,7 @@
                 mUserSwitchHandlerTask.mClientToBeReset = clientToBeReset;
                 return;
             }
-            mHandler.removeCallbacks(mUserSwitchHandlerTask);
+            mIoHandler.removeCallbacks(mUserSwitchHandlerTask);
         }
         // Hide soft input before user switch task since switch task may block main handler a while
         // and delayed the hideCurrentInputLocked().
@@ -1122,7 +1171,7 @@
         final UserSwitchHandlerTask task = new UserSwitchHandlerTask(this, userId,
                 clientToBeReset);
         mUserSwitchHandlerTask = task;
-        mHandler.post(task);
+        mIoHandler.post(task);
     }
 
     @VisibleForTesting
@@ -1136,10 +1185,11 @@
             mConcurrentMultiUserModeEnabled = concurrentMultiUserModeEnabled;
             mContext = context;
             mRes = context.getResources();
+            SecureSettingsWrapper.onStart(mContext);
 
             mHandler = Handler.createAsync(uiLooper, this);
             mIoHandler = ioHandler;
-            SystemLocaleWrapper.onStart(context, this::onActionLocaleChanged, mHandler);
+            SystemLocaleWrapper.onStart(context, this::onActionLocaleChanged, mIoHandler);
             mImeTrackerService = new ImeTrackerService(mHandler);
             mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);
             mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
@@ -1150,6 +1200,7 @@
             mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
 
             mSlotIme = mContext.getString(com.android.internal.R.string.status_bar_ime);
+            mNewInputMethodSwitcherMenuEnabled = shouldEnableNewInputMethodSwitcherMenu(mContext);
 
             mShowOngoingImeSwitcherForPhones = false;
 
@@ -1162,7 +1213,7 @@
                             : bindingControllerFactory);
 
             mMenuController = new InputMethodMenuController(this);
-            mMenuControllerNew = Flags.imeSwitcherRevamp()
+            mMenuControllerNew = mNewInputMethodSwitcherMenuEnabled
                     ? new InputMethodMenuControllerNew() : null;
             mVisibilityStateComputer = new ImeVisibilityStateComputer(this);
             mVisibilityApplier = new DefaultImeVisibilityApplier(this);
@@ -1182,12 +1233,6 @@
         }
     }
 
-    @GuardedBy("ImfLock.class")
-    @UserIdInt
-    int getCurrentImeUserIdLocked() {
-        return mCurrentUserId;
-    }
-
     private final class InkWindowInitializer implements Runnable {
         public void run() {
             synchronized (ImfLock.class) {
@@ -1199,12 +1244,12 @@
         }
     }
 
-    private void onUpdateEditorToolType(int toolType) {
-        synchronized (ImfLock.class) {
-            IInputMethodInvoker curMethod = getCurMethodLocked();
-            if (curMethod != null) {
-                curMethod.updateEditorToolType(toolType);
-            }
+    @GuardedBy("ImfLock.class")
+    private void onUpdateEditorToolTypeLocked(@MotionEvent.ToolType int toolType,
+            @UserIdInt int userId) {
+        final var curMethod = getInputMethodBindingController(userId).getCurMethod();
+        if (curMethod != null) {
+            curMethod.updateEditorToolType(toolType);
         }
     }
 
@@ -1403,38 +1448,40 @@
                         UserHandle.ALL, broadcastFilterForAllUsers, null, null,
                         Context.RECEIVER_EXPORTED);
 
-                final String defaultImiId = SecureSettingsWrapper.getString(
-                        Settings.Secure.DEFAULT_INPUT_METHOD, null, currentUserId);
-                final boolean imeSelectedOnBoot = !TextUtils.isEmpty(defaultImiId);
-                final var settings = InputMethodSettingsRepository.get(currentUserId);
-                postInputMethodSettingUpdatedLocked(
-                        !imeSelectedOnBoot /* resetDefaultEnabledIme */, currentUserId);
-                updateFromSettingsLocked(true, currentUserId);
-                InputMethodUtils.setNonSelectedSystemImesDisabledUntilUsed(
-                        getPackageManagerForUser(mContext, currentUserId),
-                        settings.getEnabledInputMethodList());
-
                 AdditionalSubtypeMapRepository.startWriterThread();
 
-                if (mConcurrentMultiUserModeEnabled) {
-                    for (int userId : mUserManagerInternal.getUserIds()) {
-                        if (userId != mCurrentUserId) {
-                            initializeVisibleBackgroundUserLocked(userId);
-                        }
-                    }
+                for (int userId : mUserManagerInternal.getUserIds()) {
+                    onUserReadyLocked(userId);
                 }
             }
         }
     }
 
+    @GuardedBy("ImfLock.class")
+    void onUserReadyLocked(@UserIdInt int userId) {
+        if (!mUserManagerInternal.isUserRunning(userId)) {
+            return;
+        }
+
+        final String defaultImiId = SecureSettingsWrapper.getString(
+                Settings.Secure.DEFAULT_INPUT_METHOD, null, userId);
+        final boolean imeSelectedOnBoot = !TextUtils.isEmpty(defaultImiId);
+        final var settings = InputMethodSettingsRepository.get(userId);
+        postInputMethodSettingUpdatedLocked(!imeSelectedOnBoot /* resetDefaultEnabledIme */,
+                userId);
+        updateFromSettingsLocked(true, userId);
+        InputMethodUtils.setNonSelectedSystemImesDisabledUntilUsed(
+                getPackageManagerForUser(mContext, userId), settings.getEnabledInputMethodList());
+    }
+
     void registerImeRequestedChangedListener() {
         mWindowManagerInternal.setOnImeRequestedChangedListener(
-                (windowToken, imeVisible) -> {
+                (windowToken, imeVisible, statsToken) -> {
                     if (Flags.refactorInsetsController()) {
                         if (imeVisible) {
-                            showCurrentInputInternal(windowToken);
+                            showCurrentInputInternal(windowToken, statsToken);
                         } else {
-                            hideCurrentInputInternal(windowToken);
+                            hideCurrentInputInternal(windowToken, statsToken);
                         }
                     }
                 });
@@ -1469,9 +1516,8 @@
             mContext.enforceCallingOrSelfPermission(
                     Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
         }
-        synchronized (ImfLock.class) {
-            return queryDefaultInputMethodForUserIdLocked(userId);
-        }
+        final InputMethodSettings settings = InputMethodSettingsRepository.get(userId);
+        return settings.getMethodMap().get(settings.getSelectedInputMethod());
     }
 
     @BinderThread
@@ -1483,20 +1529,16 @@
             mContext.enforceCallingOrSelfPermission(
                     Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
         }
-        synchronized (ImfLock.class) {
-            final int[] resolvedUserIds = InputMethodUtils.resolveUserId(userId,
-                    mCurrentUserId, null);
-            if (resolvedUserIds.length != 1) {
-                return InputMethodInfoSafeList.empty();
-            }
-            final int callingUid = Binder.getCallingUid();
-            final long ident = Binder.clearCallingIdentity();
-            try {
-                return InputMethodInfoSafeList.create(getInputMethodListLocked(
-                        resolvedUserIds[0], directBootAwareness, callingUid));
-            } finally {
-                Binder.restoreCallingIdentity(ident);
-            }
+        if (!mUserManagerInternal.exists(userId)) {
+            return InputMethodInfoSafeList.empty();
+        }
+        final int callingUid = Binder.getCallingUid();
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            return InputMethodInfoSafeList.create(getInputMethodListInternal(
+                    userId, directBootAwareness, callingUid));
+        } finally {
+            Binder.restoreCallingIdentity(ident);
         }
     }
 
@@ -1508,20 +1550,16 @@
             mContext.enforceCallingOrSelfPermission(
                     Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
         }
-        synchronized (ImfLock.class) {
-            final int[] resolvedUserIds = InputMethodUtils.resolveUserId(userId,
-                    mCurrentUserId, null);
-            if (resolvedUserIds.length != 1) {
-                return InputMethodInfoSafeList.empty();
-            }
-            final int callingUid = Binder.getCallingUid();
-            final long ident = Binder.clearCallingIdentity();
-            try {
-                return InputMethodInfoSafeList.create(
-                        getEnabledInputMethodListLocked(resolvedUserIds[0], callingUid));
-            } finally {
-                Binder.restoreCallingIdentity(ident);
-            }
+        if (!mUserManagerInternal.exists(userId)) {
+            return InputMethodInfoSafeList.empty();
+        }
+        final int callingUid = Binder.getCallingUid();
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            return InputMethodInfoSafeList.create(
+                    getEnabledInputMethodListInternal(userId, callingUid));
+        } finally {
+            Binder.restoreCallingIdentity(ident);
         }
     }
 
@@ -1534,20 +1572,15 @@
             mContext.enforceCallingOrSelfPermission(
                     Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
         }
-        synchronized (ImfLock.class) {
-            final int[] resolvedUserIds = InputMethodUtils.resolveUserId(userId,
-                    mCurrentUserId, null);
-            if (resolvedUserIds.length != 1) {
-                return Collections.emptyList();
-            }
-            final int callingUid = Binder.getCallingUid();
-            final long ident = Binder.clearCallingIdentity();
-            try {
-                return getInputMethodListLocked(
-                        resolvedUserIds[0], directBootAwareness, callingUid);
-            } finally {
-                Binder.restoreCallingIdentity(ident);
-            }
+        if (!mUserManagerInternal.exists(userId)) {
+            return Collections.emptyList();
+        }
+        final int callingUid = Binder.getCallingUid();
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            return getInputMethodListInternal(userId, directBootAwareness, callingUid);
+        } finally {
+            Binder.restoreCallingIdentity(ident);
         }
     }
 
@@ -1559,19 +1592,15 @@
             mContext.enforceCallingOrSelfPermission(
                     Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
         }
-        synchronized (ImfLock.class) {
-            final int[] resolvedUserIds = InputMethodUtils.resolveUserId(userId,
-                    mCurrentUserId, null);
-            if (resolvedUserIds.length != 1) {
-                return Collections.emptyList();
-            }
-            final int callingUid = Binder.getCallingUid();
-            final long ident = Binder.clearCallingIdentity();
-            try {
-                return getEnabledInputMethodListLocked(resolvedUserIds[0], callingUid);
-            } finally {
-                Binder.restoreCallingIdentity(ident);
-            }
+        if (!mUserManagerInternal.exists(userId)) {
+            return Collections.emptyList();
+        }
+        final int callingUid = Binder.getCallingUid();
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            return getEnabledInputMethodListInternal(userId, callingUid);
+        } finally {
+            Binder.restoreCallingIdentity(ident);
         }
     }
 
@@ -1615,18 +1644,13 @@
         return true;
     }
 
-    @GuardedBy("ImfLock.class")
-    private List<InputMethodInfo> getInputMethodListLocked(@UserIdInt int userId,
+    private List<InputMethodInfo> getInputMethodListInternal(@UserIdInt int userId,
             @DirectBootAwareness int directBootAwareness, int callingUid) {
-        final InputMethodSettings settings;
-        if (directBootAwareness == DirectBootAwareness.AUTO) {
-            settings = InputMethodSettingsRepository.get(userId);
-        } else {
-            final AdditionalSubtypeMap additionalSubtypeMap =
-                    AdditionalSubtypeMapRepository.get(userId);
-            settings = queryInputMethodServicesInternal(mContext, userId, additionalSubtypeMap,
-                    directBootAwareness);
-        }
+        final var userData = getUserData(userId);
+        final var methodMap = userData.mRawInputMethodMap.get().toInputMethodMap(
+                AdditionalSubtypeMapRepository.get(userId), directBootAwareness,
+                mUserManagerInternal.isUserUnlockingOrUnlocked(userId));
+        final var settings = InputMethodSettings.create(methodMap, userId);
         // Create a copy.
         final ArrayList<InputMethodInfo> methodList = new ArrayList<>(settings.getMethodList());
         // filter caller's access to input methods
@@ -1635,8 +1659,7 @@
         return methodList;
     }
 
-    @GuardedBy("ImfLock.class")
-    private List<InputMethodInfo> getEnabledInputMethodListLocked(@UserIdInt int userId,
+    private List<InputMethodInfo> getEnabledInputMethodListInternal(@UserIdInt int userId,
             int callingUid) {
         final InputMethodSettings settings = InputMethodSettingsRepository.get(userId);
         final ArrayList<InputMethodInfo> methodList = settings.getEnabledInputMethodList();
@@ -1663,20 +1686,17 @@
                     Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
         }
 
-        synchronized (ImfLock.class) {
-            final int callingUid = Binder.getCallingUid();
-            final long ident = Binder.clearCallingIdentity();
-            try {
-                return getEnabledInputMethodSubtypeListLocked(imiId,
-                        allowsImplicitlyEnabledSubtypes, userId, callingUid);
-            } finally {
-                Binder.restoreCallingIdentity(ident);
-            }
+        final int callingUid = Binder.getCallingUid();
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            return getEnabledInputMethodSubtypeListInternal(imiId,
+                    allowsImplicitlyEnabledSubtypes, userId, callingUid);
+        } finally {
+            Binder.restoreCallingIdentity(ident);
         }
     }
 
-    @GuardedBy("ImfLock.class")
-    private List<InputMethodSubtype> getEnabledInputMethodSubtypeListLocked(String imiId,
+    private List<InputMethodSubtype> getEnabledInputMethodSubtypeListInternal(String imiId,
             boolean allowsImplicitlyEnabledSubtypes, @UserIdInt int userId, int callingUid) {
         final InputMethodSettings settings = InputMethodSettingsRepository.get(userId);
         final InputMethodInfo imi = settings.getMethodMap().get(imiId);
@@ -1804,10 +1824,10 @@
                     ImeTracker.PHASE_SERVER_WAIT_IME);
             userData.mCurStatsToken = null;
             // TODO: Make mMenuController multi-user aware
-            if (Flags.imeSwitcherRevamp()) {
+            if (mNewInputMethodSwitcherMenuEnabled) {
                 mMenuControllerNew.hide(bindingController.getCurTokenDisplayId(), userId);
             } else {
-                mMenuController.hideInputMethodMenuLocked();
+                mMenuController.hideInputMethodMenuLocked(userId);
             }
         }
     }
@@ -1839,24 +1859,6 @@
         }
     }
 
-    @VisibleForTesting
-    void setAttachedClientForTesting(@NonNull ClientState cs) {
-        synchronized (ImfLock.class) {
-            getUserData(mCurrentUserId).mCurClient = cs;
-        }
-    }
-
-    @GuardedBy("ImfLock.class")
-    void clearInputShownLocked() {
-        mVisibilityStateComputer.setInputShown(false);
-    }
-
-    @GuardedBy("ImfLock.class")
-    @Override
-    public boolean isInputShownLocked() {
-        return mVisibilityStateComputer.isInputShown();
-    }
-
     @GuardedBy("ImfLock.class")
     private boolean isShowRequestedForCurrentWindow(@UserIdInt int userId) {
         final var userData = getUserData(userId);
@@ -1910,7 +1912,12 @@
         if (Flags.refactorInsetsController()) {
             if (isShowRequestedForCurrentWindow(userId) && userData.mImeBindingState != null
                     && userData.mImeBindingState.mFocusedWindow != null) {
-                showCurrentInputInternal(userData.mImeBindingState.mFocusedWindow);
+                // Re-use current statsToken, if it exists.
+                final var statsToken = userData.mCurStatsToken != null ? userData.mCurStatsToken
+                        : createStatsTokenForFocusedClient(true /* show */,
+                                SoftInputShowHideReason.ATTACH_NEW_INPUT, userId);
+                userData.mCurStatsToken = null;
+                showCurrentInputInternal(userData.mImeBindingState.mFocusedWindow, statsToken);
             }
         } else {
             if (isShowRequestedForCurrentWindow(userId)) {
@@ -2351,7 +2358,7 @@
                     if (userData.mCurClient != null) {
                         clearClientSessionLocked(userData.mCurClient);
                         userData.mCurClient.mCurSession = new SessionState(
-                                userData.mCurClient, method, session, channel);
+                                userData.mCurClient, method, session, channel, userId);
                         InputBindResult res = attachNewInputLocked(
                                 StartInputReason.SESSION_CREATED_BY_IME, true, userId);
                         attachNewAccessibilityLocked(StartInputReason.SESSION_CREATED_BY_IME, true,
@@ -2492,9 +2499,7 @@
                     sessionState.mSession.finishSession();
                 } catch (RemoteException e) {
                     Slog.w(TAG, "Session failed to close due to remote exception", e);
-                    // TODO(b/350386877): Propagate userId from the caller or infer it from
-                    //  sessionState
-                    final int userId = mCurrentUserId;
+                    final int userId = sessionState.mUserId;
                     final var bindingController = getInputMethodBindingController(userId);
                     updateSystemUiLocked(0 /* vis */, bindingController.getBackDisposition(),
                             userId);
@@ -2625,7 +2630,7 @@
         if (!mShowOngoingImeSwitcherForPhones) return false;
         // When the IME switcher dialog is shown, the IME switcher button should be hidden.
         // TODO(b/305849394): Make mMenuController multi-user aware.
-        final boolean switcherMenuShowing = Flags.imeSwitcherRevamp()
+        final boolean switcherMenuShowing = mNewInputMethodSwitcherMenuEnabled
                 ? mMenuControllerNew.isShowing()
                 : mMenuController.getSwitchingDialogLocked() != null;
         if (switcherMenuShowing) {
@@ -2645,7 +2650,8 @@
                 || (visibility & InputMethodService.IME_INVISIBLE) != 0) {
             return false;
         }
-        if (mWindowManagerInternal.isHardKeyboardAvailable() && !Flags.imeSwitcherRevamp()) {
+        if (mWindowManagerInternal.isHardKeyboardAvailable()
+                && !mNewInputMethodSwitcherMenuEnabled) {
             // When physical keyboard is attached, we show the ime switcher (or notification if
             // NavBar is not available) because SHOW_IME_WITH_HARD_KEYBOARD settings currently
             // exists in the IME switcher dialog.  Might be OK to remove this condition once
@@ -2656,7 +2662,7 @@
         }
 
         final InputMethodSettings settings = InputMethodSettingsRepository.get(userId);
-        if (Flags.imeSwitcherRevamp()) {
+        if (mNewInputMethodSwitcherMenuEnabled) {
             // The IME switcher button should be shown when the current IME specified a
             // language settings activity.
             final var curImi = settings.getMethodMap().get(settings.getSelectedInputMethod());
@@ -2773,22 +2779,20 @@
             }
             final IBinder targetWindow = mImeTargetWindowMap.get(startInputToken);
             if (targetWindow != null) {
-                mWindowManagerInternal.updateInputMethodTargetWindow(token, targetWindow);
+                mWindowManagerInternal.updateInputMethodTargetWindow(targetWindow);
             }
-            mLastImeTargetWindow = targetWindow;
+            mVisibilityStateComputer.setLastImeTargetWindow(targetWindow);
         }
     }
 
-    private void updateImeWindowStatus(boolean disableImeIcon) {
-        synchronized (ImfLock.class) {
-            // TODO(b/350386877): Propagate userId from the caller.
-            final int userId = mCurrentUserId;
-            if (disableImeIcon) {
-                final var bindingController = getInputMethodBindingController(userId);
-                updateSystemUiLocked(0, bindingController.getBackDisposition(), userId);
-            } else {
-                updateSystemUiLocked(userId);
-            }
+    @GuardedBy("ImfLock.class")
+    private void updateImeWindowStatusLocked(boolean disableImeIcon, int displayId) {
+        final int userId = resolveImeUserIdFromDisplayIdLocked(displayId);
+        if (disableImeIcon) {
+            final var bindingController = getInputMethodBindingController(userId);
+            updateSystemUiLocked(0, bindingController.getBackDisposition(), userId);
+        } else {
+            updateSystemUiLocked(userId);
         }
     }
 
@@ -2834,7 +2838,7 @@
             }
             final var curId = bindingController.getCurId();
             // TODO(b/305849394): Make mMenuController multi-user aware.
-            final boolean switcherMenuShowing = Flags.imeSwitcherRevamp()
+            final boolean switcherMenuShowing = mNewInputMethodSwitcherMenuEnabled
                     ? mMenuControllerNew.isShowing()
                     : mMenuController.getSwitchingDialogLocked() != null;
             if (switcherMenuShowing
@@ -2845,8 +2849,8 @@
             }
             final boolean needsToShowImeSwitcher = shouldShowImeSwitcherLocked(vis, userId);
             if (mStatusBarManagerInternal != null) {
-                mStatusBarManagerInternal.setImeWindowStatus(curTokenDisplayId,
-                        curToken, vis, backDisposition, needsToShowImeSwitcher);
+                mStatusBarManagerInternal.setImeWindowStatus(curTokenDisplayId, vis,
+                        backDisposition, needsToShowImeSwitcher);
             }
         } finally {
             Binder.restoreCallingIdentity(ident);
@@ -2856,65 +2860,11 @@
     @GuardedBy("ImfLock.class")
     void updateFromSettingsLocked(boolean enabledMayChange, @UserIdInt int userId) {
         updateInputMethodsFromSettingsLocked(enabledMayChange, userId);
-        if (!Flags.imeSwitcherRevamp()) {
-            mMenuController.updateKeyboardFromSettingsLocked();
+        if (!mNewInputMethodSwitcherMenuEnabled) {
+            mMenuController.updateKeyboardFromSettingsLocked(userId);
         }
     }
 
-    /**
-     * This initialization logic is used when and only when {@link #mConcurrentMultiUserModeEnabled}
-     * is set to {@code true}.
-     *
-     * <p>There remain several yet-to-be-implemented features. For the canonical and desired
-     * behaviors always refer to single-user code paths such as
-     * {@link #updateInputMethodsFromSettingsLocked(boolean, int)}.</p>
-     *
-     * <p>Here are examples of missing features.</p>
-     * <ul>
-     *     <li>Profiles are not supported.</li>
-     *     <li>
-     *         {@link PackageManager#COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED} is not updated.
-     *     </li>
-     *     <li>{@link InputMethodBindingController#getDeviceIdToShowIme()} is ignored.</li>
-     *     <li>and so on.</li>
-     * </ul>
-     */
-    @GuardedBy("ImfLock.class")
-    void initializeVisibleBackgroundUserLocked(@UserIdInt int userId) {
-        final var settings = InputMethodSettingsRepository.get(userId);
-
-        // Until we figure out what makes most sense, we enable all the pre-installed IMEs in
-        // concurrent multi-user IME mode.
-        String enabledImeIdsStr = settings.getEnabledInputMethodsStr();
-        for (var imi : settings.getMethodList()) {
-            if (!imi.isSystem()) {
-                continue;
-            }
-            enabledImeIdsStr = InputMethodUtils.concatEnabledImeIds(enabledImeIdsStr, imi.getId());
-        }
-        if (!TextUtils.equals(settings.getEnabledInputMethodsStr(), enabledImeIdsStr)) {
-            settings.putEnabledInputMethodsStr(enabledImeIdsStr);
-        }
-
-        // Also update the currently-selected IME.
-        String id = settings.getSelectedInputMethod();
-        if (TextUtils.isEmpty(id)) {
-            final InputMethodInfo imi = InputMethodInfoUtils.getMostApplicableDefaultIME(
-                    settings.getEnabledInputMethodList());
-            if (imi != null) {
-                id = imi.getId();
-                settings.putSelectedInputMethod(id);
-            }
-        }
-        final var userData = getUserData(userId);
-        final var bindingController = userData.mBindingController;
-        bindingController.setSelectedMethodId(id);
-
-        // Also re-initialize controllers.
-        userData.mSwitchingController.resetCircularListLocked(mContext, settings);
-        userData.mHardwareKeyboardShortcutController.update(settings);
-    }
-
     @GuardedBy("ImfLock.class")
     void updateInputMethodsFromSettingsLocked(boolean enabledMayChange, @UserIdInt int userId) {
         final InputMethodSettings settings = InputMethodSettingsRepository.get(userId);
@@ -3093,66 +3043,92 @@
         }
     }
 
+    @GuardedBy("ImfLock.class")
+    private void sendResultReceiverFailureLocked(@Nullable ResultReceiver resultReceiver) {
+        if (resultReceiver == null) {
+            return;
+        }
+        final boolean isInputShown = mVisibilityStateComputer.isInputShown();
+        resultReceiver.send(isInputShown
+                ? InputMethodManager.RESULT_UNCHANGED_SHOWN
+                : InputMethodManager.RESULT_UNCHANGED_HIDDEN, null);
+    }
+
     @Override
     public boolean showSoftInput(IInputMethodClient client, IBinder windowToken,
             @NonNull ImeTracker.Token statsToken, @InputMethodManager.ShowFlags int flags,
             int lastClickToolType, ResultReceiver resultReceiver,
             @SoftInputShowHideReason int reason) {
         Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMMS.showSoftInput");
-        final int uid = Binder.getCallingUid();
-        final int callingUserId = UserHandle.getUserId(uid);
         ImeTracing.getInstance().triggerManagerServiceDump(
                 "InputMethodManagerService#showSoftInput", mDumper);
         synchronized (ImfLock.class) {
-            final int userId = resolveImeUserIdLocked(callingUserId);
-            if (!canInteractWithImeLocked(uid, client, "showSoftInput", statsToken,
-                    userId)) {
-                ImeTracker.forLogging().onFailed(
-                        statsToken, ImeTracker.PHASE_SERVER_CLIENT_FOCUSED);
-                Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
-                return false;
+            final boolean result = showSoftInputLocked(client, windowToken, statsToken, flags,
+                    lastClickToolType, resultReceiver, reason);
+            // When ZeroJankProxy is enabled, the app has already received "true" as the return
+            // value, and expect "resultReceiver" to be notified later. See b/327751155.
+            if (!result && Flags.useZeroJankProxy()) {
+                sendResultReceiverFailureLocked(resultReceiver);
             }
-            final long ident = Binder.clearCallingIdentity();
-            final var userData = getUserData(userId);
-            try {
-                if (DEBUG) Slog.v(TAG, "Client requesting input be shown");
-                if (Flags.refactorInsetsController()) {
-                    boolean wasVisible = isInputShownLocked();
-                    if (userData.mImeBindingState != null
-                            && userData.mImeBindingState.mFocusedWindowClient != null
-                            && userData.mImeBindingState.mFocusedWindowClient.mClient != null) {
-                        userData.mImeBindingState.mFocusedWindowClient.mClient
-                                .setImeVisibility(true);
-                        if (resultReceiver != null) {
-                            resultReceiver.send(
-                                    wasVisible ? InputMethodManager.RESULT_UNCHANGED_SHOWN
-                                            : InputMethodManager.RESULT_SHOWN, null);
-                        }
-                        return true;
-                    }
-                    return false;
-                } else {
-                    return showCurrentInputLocked(windowToken, statsToken, flags, lastClickToolType,
-                            resultReceiver, reason, userId);
-                }
-            } finally {
-                Binder.restoreCallingIdentity(ident);
-                Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
-            }
+            return result;  // ignored when ZeroJankProxy is enabled.
         }
     }
 
-    boolean showCurrentInputInternal(IBinder windowToken) {
+    @GuardedBy("ImfLock.class")
+    private boolean showSoftInputLocked(IInputMethodClient client, IBinder windowToken,
+            @NonNull ImeTracker.Token statsToken, @InputMethodManager.ShowFlags int flags,
+            int lastClickToolType, ResultReceiver resultReceiver,
+            @SoftInputShowHideReason int reason) {
+        final int uid = Binder.getCallingUid();
+        final int callingUserId = UserHandle.getUserId(uid);
+        final int userId = resolveImeUserIdLocked(callingUserId);
+        if (!canInteractWithImeLocked(uid, client, "showSoftInput", statsToken,
+                userId)) {
+            ImeTracker.forLogging().onFailed(
+                    statsToken, ImeTracker.PHASE_SERVER_CLIENT_FOCUSED);
+            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
+            return false;
+        }
+        final var userData = getUserData(userId);
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            if (DEBUG) Slog.v(TAG, "Client requesting input be shown");
+            if (Flags.refactorInsetsController()) {
+                boolean wasVisible = mVisibilityStateComputer.isInputShown();
+                if (userData.mImeBindingState != null
+                        && userData.mImeBindingState.mFocusedWindowClient != null
+                        && userData.mImeBindingState.mFocusedWindowClient.mClient != null) {
+                    userData.mImeBindingState.mFocusedWindowClient.mClient
+                            .setImeVisibility(true, statsToken);
+                    if (resultReceiver != null) {
+                        resultReceiver.send(
+                                wasVisible ? InputMethodManager.RESULT_UNCHANGED_SHOWN
+                                        : InputMethodManager.RESULT_SHOWN, null);
+                    }
+                    return true;
+                }
+                return false;
+            } else {
+                return showCurrentInputLocked(windowToken, statsToken, flags, lastClickToolType,
+                        resultReceiver, reason, userId);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
+        }
+    }
+
+    // TODO(b/353463205) check callers to see if we can make statsToken @NonNull
+    boolean showCurrentInputInternal(IBinder windowToken, @Nullable ImeTracker.Token statsToken) {
         Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMMS.showCurrentInputInternal");
         ImeTracing.getInstance().triggerManagerServiceDump(
                 "InputMethodManagerService#showSoftInput", mDumper);
         synchronized (ImfLock.class) {
-            // TODO(b/305849394): Infer userId from windowToken
-            final int userId = mCurrentUserId;
+            final int userId = resolveImeUserIdFromWindowLocked(windowToken);
             final long ident = Binder.clearCallingIdentity();
             try {
                 if (DEBUG) Slog.v(TAG, "Client requesting input be shown");
-                return showCurrentInputLocked(windowToken, null /* statsToken */, 0 /* flags */,
+                return showCurrentInputLocked(windowToken, statsToken, 0 /* flags */,
                         0 /* lastClickTooType */, null /* resultReceiver */,
                         SoftInputShowHideReason.SHOW_SOFT_INPUT, userId);
             } finally {
@@ -3162,17 +3138,17 @@
         }
     }
 
-    boolean hideCurrentInputInternal(IBinder windowToken) {
+    // TODO(b/353463205) check callers to see if we can make statsToken @NonNull
+    boolean hideCurrentInputInternal(IBinder windowToken, @Nullable ImeTracker.Token statsToken) {
         Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMMS.hideCurrentInputInternal");
         ImeTracing.getInstance().triggerManagerServiceDump(
                 "InputMethodManagerService#hideSoftInput", mDumper);
         synchronized (ImfLock.class) {
-            // TODO(b/305849394): Infer userId from windowToken
-            final int userId = mCurrentUserId;
+            final int userId = resolveImeUserIdFromWindowLocked(windowToken);
             final long ident = Binder.clearCallingIdentity();
             try {
                 if (DEBUG) Slog.v(TAG, "Client requesting input be hidden");
-                return hideCurrentInputLocked(windowToken, null /* statsToken */, 0 /* flags */,
+                return hideCurrentInputLocked(windowToken, statsToken, 0 /* flags */,
                         null /* resultReceiver */, SoftInputShowHideReason.HIDE_SOFT_INPUT,
                         userId);
             } finally {
@@ -3430,14 +3406,14 @@
             Objects.requireNonNull(windowToken, "windowToken must not be null");
             synchronized (ImfLock.class) {
                 Boolean windowPerceptible = mFocusedWindowPerceptible.get(windowToken);
-                final int userId = mCurrentUserId;
+                final int userId = resolveImeUserIdFromWindowLocked(windowToken);
                 final var userData = getUserData(userId);
                 if (userData.mImeBindingState.mFocusedWindow != windowToken
                         || (windowPerceptible != null && windowPerceptible == perceptible)) {
                     return;
                 }
                 mFocusedWindowPerceptible.put(windowToken, windowPerceptible);
-                updateSystemUiLocked(mCurrentUserId);
+                updateSystemUiLocked(userId);
             }
         });
     }
@@ -3488,9 +3464,9 @@
             userData.mCurStatsToken = null;
 
             if (Flags.useHandwritingListenerForTooltype()) {
-                maybeReportToolType();
+                maybeReportToolType(userId);
             } else if (lastClickToolType != MotionEvent.TOOL_TYPE_UNKNOWN) {
-                onUpdateEditorToolType(lastClickToolType);
+                onUpdateEditorToolTypeLocked(lastClickToolType, userId);
             }
             mVisibilityApplier.performShowIme(windowToken, statsToken,
                     mVisibilityStateComputer.getShowFlagsForInputMethodServiceOnly(),
@@ -3505,7 +3481,8 @@
     }
 
     @GuardedBy("ImfLock.class")
-    private void maybeReportToolType() {
+    private void maybeReportToolType(@UserIdInt int userId) {
+        // TODO(b/356638981): This needs to be compatible with visible background users.
         int lastDeviceId = mInputManagerInternal.getLastUsedInputDeviceId();
         final InputManager im = mContext.getSystemService(InputManager.class);
         if (im == null) {
@@ -3524,57 +3501,71 @@
             // other toolTypes are irrelevant and reported as unknown.
             toolType = MotionEvent.TOOL_TYPE_UNKNOWN;
         }
-        onUpdateEditorToolType(toolType);
+        onUpdateEditorToolTypeLocked(toolType, userId);
     }
 
     @Override
     public boolean hideSoftInput(IInputMethodClient client, IBinder windowToken,
             @NonNull ImeTracker.Token statsToken, @InputMethodManager.HideFlags int flags,
             ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {
-        final int uid = Binder.getCallingUid();
-        final int callingUserId = UserHandle.getUserId(uid);
         ImeTracing.getInstance().triggerManagerServiceDump(
                 "InputMethodManagerService#hideSoftInput", mDumper);
         synchronized (ImfLock.class) {
-            final int userId = resolveImeUserIdLocked(callingUserId);
-            if (!canInteractWithImeLocked(uid, client, "hideSoftInput", statsToken, userId)) {
-                if (isInputShownLocked()) {
-                    ImeTracker.forLogging().onFailed(
-                            statsToken, ImeTracker.PHASE_SERVER_CLIENT_FOCUSED);
-                } else {
-                    ImeTracker.forLogging().onCancelled(statsToken,
-                            ImeTracker.PHASE_SERVER_CLIENT_FOCUSED);
+            final boolean result = hideSoftInputLocked(client, windowToken, statsToken, flags,
+                    resultReceiver, reason);
+            // When ZeroJankProxy is enabled, the app has already received "true" as the return
+            // value, and expect "resultReceiver" to be notified later. See b/327751155.
+            if (!result && Flags.useZeroJankProxy()) {
+                sendResultReceiverFailureLocked(resultReceiver);
+            }
+            return result;  // ignored when ZeroJankProxy is enabled.
+        }
+    }
+
+    @GuardedBy("ImfLock.class")
+    private boolean hideSoftInputLocked(IInputMethodClient client, IBinder windowToken,
+            @NonNull ImeTracker.Token statsToken, @InputMethodManager.HideFlags int flags,
+            ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {
+        final int uid = Binder.getCallingUid();
+        final int callingUserId = UserHandle.getUserId(uid);
+        final int userId = resolveImeUserIdLocked(callingUserId);
+        if (!canInteractWithImeLocked(uid, client, "hideSoftInput", statsToken, userId)) {
+            if (mVisibilityStateComputer.isInputShown()) {
+                ImeTracker.forLogging().onFailed(
+                        statsToken, ImeTracker.PHASE_SERVER_CLIENT_FOCUSED);
+            } else {
+                ImeTracker.forLogging().onCancelled(statsToken,
+                        ImeTracker.PHASE_SERVER_CLIENT_FOCUSED);
+            }
+            return false;
+        }
+        final var userData = getUserData(userId);
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMMS.hideSoftInput");
+            if (DEBUG) Slog.v(TAG, "Client requesting input be hidden");
+            if (Flags.refactorInsetsController()) {
+                if (userData.mImeBindingState != null
+                        && userData.mImeBindingState.mFocusedWindowClient != null
+                        && userData.mImeBindingState.mFocusedWindowClient.mClient != null) {
+                    boolean wasVisible = mVisibilityStateComputer.isInputShown();
+                    // TODO add windowToken to interface
+                    userData.mImeBindingState.mFocusedWindowClient.mClient
+                            .setImeVisibility(false, statsToken);
+                    if (resultReceiver != null) {
+                        resultReceiver.send(wasVisible ? InputMethodManager.RESULT_HIDDEN
+                                : InputMethodManager.RESULT_UNCHANGED_HIDDEN, null);
+                    }
+                    return true;
                 }
                 return false;
+            } else {
+                return InputMethodManagerService.this.hideCurrentInputLocked(
+                        windowToken, statsToken, flags, resultReceiver, reason, userId);
             }
-            final long ident = Binder.clearCallingIdentity();
-            final var userData = getUserData(userId);
-            try {
-                Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMMS.hideSoftInput");
-                if (DEBUG) Slog.v(TAG, "Client requesting input be hidden");
-                if (Flags.refactorInsetsController()) {
-                    if (userData.mImeBindingState != null
-                            && userData.mImeBindingState.mFocusedWindowClient != null
-                            && userData.mImeBindingState.mFocusedWindowClient.mClient != null) {
-                        boolean wasVisible = isInputShownLocked();
-                        // TODO add windowToken to interface
-                        userData.mImeBindingState.mFocusedWindowClient.mClient
-                                .setImeVisibility(false);
-                        if (resultReceiver != null) {
-                            resultReceiver.send(wasVisible ? InputMethodManager.RESULT_HIDDEN
-                                    : InputMethodManager.RESULT_UNCHANGED_HIDDEN, null);
-                        }
-                        return true;
-                    }
-                    return false;
-                } else {
-                    return InputMethodManagerService.this.hideCurrentInputLocked(windowToken,
-                            statsToken, flags, resultReceiver, reason, userId);
-                }
-            } finally {
-                Binder.restoreCallingIdentity(ident);
-                Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
-            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
         }
     }
 
@@ -3619,7 +3610,7 @@
         // TODO(b/246309664): Clean up IMMS#mImeWindowVis
         IInputMethodInvoker curMethod = bindingController.getCurMethod();
         final boolean shouldHideSoftInput = curMethod != null
-                && (isInputShownLocked()
+                && (mVisibilityStateComputer.isInputShown()
                 || (bindingController.getImeWindowVis() & InputMethodService.IME_ACTIVE) != 0);
 
         mVisibilityStateComputer.requestImeVisibility(windowToken, false);
@@ -3967,10 +3958,9 @@
     }
 
     @GuardedBy("ImfLock.class")
-    private boolean canShowInputMethodPickerLocked(IInputMethodClient client) {
+    private boolean canShowInputMethodPickerLocked(IInputMethodClient client,
+            @UserIdInt int userId) {
         final int uid = Binder.getCallingUid();
-        // TODO(b/305849394): Get userId from callers.
-        final int userId = mCurrentUserId;
         final var userData = getUserData(userId);
         if (userData.mImeBindingState.mFocusedWindowClient != null && client != null
                 && userData.mImeBindingState.mFocusedWindowClient.mClient.asBinder()
@@ -3991,31 +3981,44 @@
     @Override
     public void showInputMethodPickerFromClient(IInputMethodClient client,
             int auxiliarySubtypeMode) {
+        if (mConcurrentMultiUserModeEnabled) {
+            Slog.w(TAG, "showInputMethodPickerFromClient is not enabled on automotive");
+            return;
+        }
         final int callingUserId = UserHandle.getCallingUserId();
         synchronized (ImfLock.class) {
-            if (!canShowInputMethodPickerLocked(client)) {
+            final int userId = resolveImeUserIdLocked(callingUserId);
+            if (!canShowInputMethodPickerLocked(client, userId)) {
                 Slog.w(TAG, "Ignoring showInputMethodPickerFromClient of uid "
                         + Binder.getCallingUid() + ": " + client);
                 return;
             }
-            final int userId = resolveImeUserIdLocked(callingUserId);
             final var userData = getUserData(userId);
             // Always call subtype picker, because subtype picker is a superset of input method
             // picker.
             final int displayId = (userData.mCurClient != null)
                     ? userData.mCurClient.mSelfReportedDisplayId : DEFAULT_DISPLAY;
-            mHandler.obtainMessage(MSG_SHOW_IM_SUBTYPE_PICKER, auxiliarySubtypeMode, displayId)
-                    .sendToTarget();
+            mHandler.post(() -> {
+                synchronized (ImfLock.class) {
+                    showInputMethodPickerLocked(auxiliarySubtypeMode, displayId, userId);
+                }
+            });
         }
     }
 
-    @IInputMethodManagerImpl.PermissionVerified(Manifest.permission.WRITE_SECURE_SETTINGS)
+    @IInputMethodManagerImpl.PermissionVerified(allOf = {
+            Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+            Manifest.permission.WRITE_SECURE_SETTINGS})
     @Override
     public void showInputMethodPickerFromSystem(int auxiliarySubtypeMode, int displayId) {
         // Always call subtype picker, because subtype picker is a superset of input method
         // picker.
-        mHandler.obtainMessage(MSG_SHOW_IM_SUBTYPE_PICKER, auxiliarySubtypeMode, displayId)
-                .sendToTarget();
+        mHandler.post(() -> {
+            synchronized (ImfLock.class) {
+                final int userId = resolveImeUserIdFromDisplayIdLocked(displayId);
+                showInputMethodPickerLocked(auxiliarySubtypeMode, displayId, userId);
+            }
+        });
     }
 
     /**
@@ -4024,7 +4027,7 @@
     @IInputMethodManagerImpl.PermissionVerified(Manifest.permission.TEST_INPUT_METHOD)
     public boolean isInputMethodPickerShownForTest() {
         synchronized (ImfLock.class) {
-            return Flags.imeSwitcherRevamp()
+            return mNewInputMethodSwitcherMenuEnabled
                     ? mMenuControllerNew.isShowing()
                     : mMenuController.isisInputMethodPickerShownForTestLocked();
         }
@@ -4099,11 +4102,13 @@
         }
     }
 
-    @IInputMethodManagerImpl.PermissionVerified(Manifest.permission.WRITE_SECURE_SETTINGS)
+    @IInputMethodManagerImpl.PermissionVerified(allOf = {
+            Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+            Manifest.permission.WRITE_SECURE_SETTINGS})
     @Override
     public void onImeSwitchButtonClickFromSystem(int displayId) {
         synchronized (ImfLock.class) {
-            final int userId = mCurrentUserId;
+            final int userId = resolveImeUserIdFromDisplayIdLocked(displayId);
             final var userData = getUserData(userId);
             final var bindingController = userData.mBindingController;
             final var curToken = bindingController.getCurToken();
@@ -4344,6 +4349,7 @@
                         + subtype.getLocale() + ", " + subtype.getMode());
             }
         }
+        final var userData = getUserData(userId);
         synchronized (ImfLock.class) {
             if (!mSystemReady) {
                 return;
@@ -4359,9 +4365,10 @@
                         settings.getMethodMap());
                 final long ident = Binder.clearCallingIdentity();
                 try {
-                    final InputMethodSettings newSettings = queryInputMethodServicesInternal(
-                            mContext, userId, AdditionalSubtypeMapRepository.get(userId),
-                            DirectBootAwareness.AUTO);
+                    final var methodMap = userData.mRawInputMethodMap.get().toInputMethodMap(
+                            AdditionalSubtypeMapRepository.get(userId), DirectBootAwareness.AUTO,
+                            mUserManagerInternal.isUserUnlockingOrUnlocked(userId));
+                    final var newSettings = InputMethodSettings.create(methodMap, userId);
                     InputMethodSettingsRepository.put(userId, newSettings);
                     if (isCurrentUser) {
                         postInputMethodSettingUpdatedLocked(false /* resetDefaultEnabledIme */,
@@ -4441,9 +4448,11 @@
         });
     }
 
-    @IInputMethodManagerImpl.PermissionVerified(Manifest.permission.INTERNAL_SYSTEM_WINDOW)
+    @IInputMethodManagerImpl.PermissionVerified(allOf = {
+            Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+            Manifest.permission.INTERNAL_SYSTEM_WINDOW})
     @Override
-    public void removeImeSurface() {
+    public void removeImeSurface(int displayId) {
         mHandler.obtainMessage(MSG_REMOVE_IME_SURFACE).sendToTarget();
     }
 
@@ -4712,8 +4721,8 @@
             proto.write(CUR_SEQ, bindingController.getSequenceNumber());
             proto.write(CUR_CLIENT, Objects.toString(userData.mCurClient));
             userData.mImeBindingState.dumpDebug(proto, mWindowManagerInternal);
-            proto.write(LAST_IME_TARGET_WINDOW_NAME,
-                    mWindowManagerInternal.getWindowName(mLastImeTargetWindow));
+            proto.write(LAST_IME_TARGET_WINDOW_NAME, mWindowManagerInternal.getWindowName(
+                    mVisibilityStateComputer.getLastImeTargetWindow()));
             proto.write(CUR_FOCUSED_WINDOW_SOFT_INPUT_MODE, InputMethodDebug.softInputModeToString(
                     userData.mImeBindingState.mFocusedWindowSoftInputMode));
             if (userData.mCurEditorInfo != null) {
@@ -4730,7 +4739,7 @@
             proto.write(IS_INTERACTIVE, mIsInteractive);
             proto.write(BACK_DISPOSITION, bindingController.getBackDisposition());
             proto.write(IME_WINDOW_VISIBILITY, bindingController.getImeWindowVis());
-            if (!Flags.imeSwitcherRevamp()) {
+            if (!mNewInputMethodSwitcherMenuEnabled) {
                 proto.write(SHOW_IME_WITH_HARD_KEYBOARD,
                         mMenuController.getShowImeWithHardKeyboard());
             }
@@ -4878,17 +4887,17 @@
                 final long ident = Binder.clearCallingIdentity();
                 try {
                     if (Flags.refactorInsetsController()) {
-                        userData.mCurClient.mClient.setImeVisibility(false);
+                        userData.mCurClient.mClient.setImeVisibility(false, statsToken);
                         // TODO we will loose the flags here
                         if (userData.mImeBindingState != null
                                 && userData.mImeBindingState.mFocusedWindowClient != null
                                 && userData.mImeBindingState.mFocusedWindowClient.mClient != null) {
                             userData.mImeBindingState.mFocusedWindowClient.mClient
-                                    .setImeVisibility(false);
+                                    .setImeVisibility(false, statsToken);
                         }
                     } else {
-                        hideCurrentInputLocked(mLastImeTargetWindow, statsToken, flags,
-                                null /* resultReceiver */, reason, userId);
+                        hideCurrentInputLocked(mVisibilityStateComputer.getLastImeTargetWindow(),
+                                statsToken, flags, null /* resultReceiver */, reason, userId);
                     }
                 } finally {
                     Binder.restoreCallingIdentity(ident);
@@ -4917,18 +4926,18 @@
                 final long ident = Binder.clearCallingIdentity();
                 try {
                     if (Flags.refactorInsetsController()) {
-                        userData.mCurClient.mClient.setImeVisibility(false);
+                        userData.mCurClient.mClient.setImeVisibility(false, statsToken);
                         // TODO we will loose the flags here
                         if (userData.mImeBindingState != null
                                 && userData.mImeBindingState.mFocusedWindowClient != null
                                 && userData.mImeBindingState.mFocusedWindowClient.mClient != null) {
                             userData.mImeBindingState.mFocusedWindowClient.mClient
-                                    .setImeVisibility(true);
+                                    .setImeVisibility(true, statsToken);
                         }
                     } else {
-                        showCurrentInputLocked(mLastImeTargetWindow, statsToken, flags,
-                                MotionEvent.TOOL_TYPE_UNKNOWN, null /* resultReceiver */, reason,
-                                userId);
+                        showCurrentInputLocked(mVisibilityStateComputer.getLastImeTargetWindow(),
+                                statsToken, flags, MotionEvent.TOOL_TYPE_UNKNOWN,
+                                null /* resultReceiver */, reason, userId);
                     }
                 } finally {
                     Binder.restoreCallingIdentity(ident);
@@ -4945,14 +4954,12 @@
         return mVisibilityApplier;
     }
 
-    void onApplyImeVisibilityFromComputer(IBinder windowToken, @NonNull ImeTracker.Token statsToken,
-            @NonNull ImeVisibilityResult result) {
-        synchronized (ImfLock.class) {
-            // TODO(b/305849394): Infer userId from windowToken
-            final int userId = mCurrentUserId;
-            mVisibilityApplier.applyImeVisibility(windowToken, statsToken, result.getState(),
-                    result.getReason(), userId);
-        }
+    @GuardedBy("ImfLock.class")
+    void onApplyImeVisibilityFromComputerLocked(IBinder windowToken,
+            @NonNull ImeTracker.Token statsToken, @NonNull ImeVisibilityResult result) {
+        final int userId = resolveImeUserIdFromWindowLocked(windowToken);
+        mVisibilityApplier.applyImeVisibility(windowToken, statsToken, result.getState(),
+                result.getReason(), userId);
     }
 
     @GuardedBy("ImfLock.class")
@@ -5008,104 +5015,97 @@
         userData.mEnabledAccessibilitySessions = accessibilitySessions;
     }
 
+    @GuardedBy("ImfLock.class")
+    private void showInputMethodPickerLocked(int auxiliarySubtypeMode, int displayId,
+            @UserIdInt int userId) {
+        final boolean showAuxSubtypes;
+        switch (auxiliarySubtypeMode) {
+            // This is undocumented so far, but IMM#showInputMethodPicker() has been
+            // implemented so that auxiliary subtypes will be excluded when the soft
+            // keyboard is invisible.
+            case InputMethodManager.SHOW_IM_PICKER_MODE_AUTO ->
+                    showAuxSubtypes = mVisibilityStateComputer.isInputShown();
+            case InputMethodManager.SHOW_IM_PICKER_MODE_INCLUDE_AUXILIARY_SUBTYPES ->
+                    showAuxSubtypes = true;
+            case InputMethodManager.SHOW_IM_PICKER_MODE_EXCLUDE_AUXILIARY_SUBTYPES ->
+                    showAuxSubtypes = false;
+            default -> {
+                Slog.e(TAG, "Unknown subtype picker mode=" + auxiliarySubtypeMode);
+                return;
+            }
+        }
+        final InputMethodSettings settings = InputMethodSettingsRepository.get(userId);
+        final boolean isScreenLocked = mWindowManagerInternal.isKeyguardLocked()
+                && mWindowManagerInternal.isKeyguardSecure(userId);
+        final String lastInputMethodId = settings.getSelectedInputMethod();
+        int lastInputMethodSubtypeId = settings.getSelectedInputMethodSubtypeId(lastInputMethodId);
+
+        final List<ImeSubtypeListItem> imList = InputMethodSubtypeSwitchingController
+                .getSortedInputMethodAndSubtypeList(
+                        showAuxSubtypes, isScreenLocked, true /* forImeMenu */,
+                        mContext, settings);
+        if (imList.isEmpty()) {
+            Slog.w(TAG, "Show switching menu failed, imList is empty,"
+                    + " showAuxSubtypes: " + showAuxSubtypes
+                    + " isScreenLocked: " + isScreenLocked
+                    + " userId: " + userId);
+            return;
+        }
+
+        if (mNewInputMethodSwitcherMenuEnabled) {
+            if (DEBUG) {
+                Slog.v(TAG, "Show IME switcher menu,"
+                        + " showAuxSubtypes=" + showAuxSubtypes
+                        + " displayId=" + displayId
+                        + " preferredInputMethodId=" + lastInputMethodId
+                        + " preferredInputMethodSubtypeId=" + lastInputMethodSubtypeId);
+            }
+
+            final var itemsAndIndex = getInputMethodPickerItems(imList,
+                    lastInputMethodId, lastInputMethodSubtypeId, userId);
+            final var menuItems = itemsAndIndex.first;
+            final int selectedIndex = itemsAndIndex.second;
+
+            if (selectedIndex == -1) {
+                Slog.w(TAG, "Switching menu shown with no item selected"
+                        + ", IME id: " + lastInputMethodId
+                        + ", subtype index: " + lastInputMethodSubtypeId);
+            }
+
+            mMenuControllerNew.show(menuItems, selectedIndex, displayId, userId);
+        } else {
+            mMenuController.showInputMethodMenuLocked(showAuxSubtypes, displayId,
+                    lastInputMethodId, lastInputMethodSubtypeId, imList, userId);
+        }
+    }
+
     @SuppressWarnings("unchecked")
     @UiThread
     @Override
     public boolean handleMessage(Message msg) {
         switch (msg.what) {
-            case MSG_SHOW_IM_SUBTYPE_PICKER:
-                final boolean showAuxSubtypes;
-                final int displayId = msg.arg2;
-                switch (msg.arg1) {
-                    case InputMethodManager.SHOW_IM_PICKER_MODE_AUTO:
-                        // This is undocumented so far, but IMM#showInputMethodPicker() has been
-                        // implemented so that auxiliary subtypes will be excluded when the soft
-                        // keyboard is invisible.
-                        synchronized (ImfLock.class) {
-                            showAuxSubtypes = isInputShownLocked();
-                        }
-                        break;
-                    case InputMethodManager.SHOW_IM_PICKER_MODE_INCLUDE_AUXILIARY_SUBTYPES:
-                        showAuxSubtypes = true;
-                        break;
-                    case InputMethodManager.SHOW_IM_PICKER_MODE_EXCLUDE_AUXILIARY_SUBTYPES:
-                        showAuxSubtypes = false;
-                        break;
-                    default:
-                        Slog.e(TAG, "Unknown subtype picker mode = " + msg.arg1);
-                        return false;
-                }
+            case MSG_HIDE_INPUT_METHOD: {
+                @SoftInputShowHideReason final int reason = msg.arg1;
+                final int originatingDisplayId = msg.arg2;
                 synchronized (ImfLock.class) {
-                    final InputMethodSettings settings =
-                            InputMethodSettingsRepository.get(mCurrentUserId);
-                    final int userId = settings.getUserId();
-                    final boolean isScreenLocked = mWindowManagerInternal.isKeyguardLocked()
-                            && mWindowManagerInternal.isKeyguardSecure(userId);
-                    final String lastInputMethodId = settings.getSelectedInputMethod();
-                    int lastInputMethodSubtypeId =
-                            settings.getSelectedInputMethodSubtypeId(lastInputMethodId);
-
-                    final List<ImeSubtypeListItem> imList = InputMethodSubtypeSwitchingController
-                            .getSortedInputMethodAndSubtypeList(
-                                    showAuxSubtypes, isScreenLocked, true /* forImeMenu */,
-                                    mContext, settings);
-                    if (imList.isEmpty()) {
-                        Slog.w(TAG, "Show switching menu failed, imList is empty,"
-                                + " showAuxSubtypes: " + showAuxSubtypes
-                                + " isScreenLocked: " + isScreenLocked
-                                + " userId: " + userId);
-                        return false;
-                    }
-
-                    if (Flags.imeSwitcherRevamp()) {
-                        if (DEBUG) {
-                            Slog.v(TAG, "Show IME switcher menu,"
-                                    + " showAuxSubtypes=" + showAuxSubtypes
-                                    + " displayId=" + displayId
-                                    + " preferredInputMethodId=" + lastInputMethodId
-                                    + " preferredInputMethodSubtypeId=" + lastInputMethodSubtypeId);
-                        }
-
-                        final var itemsAndIndex = getInputMethodPickerItems(imList,
-                                lastInputMethodId, lastInputMethodSubtypeId, userId);
-                        final var menuItems = itemsAndIndex.first;
-                        final int selectedIndex = itemsAndIndex.second;
-
-                        if (selectedIndex == -1) {
-                            Slog.w(TAG, "Switching menu shown with no item selected"
-                                    + ", IME id: " + lastInputMethodId
-                                    + ", subtype index: " + lastInputMethodSubtypeId);
-                        }
-
-                        mMenuControllerNew.show(menuItems, selectedIndex, displayId, userId);
-                    } else {
-                        mMenuController.showInputMethodMenuLocked(showAuxSubtypes, displayId,
-                                lastInputMethodId, lastInputMethodSubtypeId, imList);
-                    }
-                }
-                return true;
-
-            // ---------------------------------------------------------
-
-            case MSG_HIDE_ALL_INPUT_METHODS:
-                synchronized (ImfLock.class) {
-                    // TODO(b/305849394): Needs to figure out what to do where for background users.
-                    final int userId = mCurrentUserId;
+                    final int userId = resolveImeUserIdFromDisplayIdLocked(originatingDisplayId);
                     final var userData = getUserData(userId);
                     if (Flags.refactorInsetsController()) {
                         if (userData.mImeBindingState != null
                                 && userData.mImeBindingState.mFocusedWindowClient != null
                                 && userData.mImeBindingState.mFocusedWindowClient.mClient != null) {
                             userData.mImeBindingState.mFocusedWindowClient.mClient
-                                    .setImeVisibility(false);
+                                    .setImeVisibility(false,
+                                            null /* TODO(b329229469) check statsToken */);
                         }
                     } else {
-                        @SoftInputShowHideReason final int reason = (int) msg.obj;
+
                         hideCurrentInputLocked(userData.mImeBindingState.mFocusedWindow,
                                 0 /* flags */, reason, userId);
                     }
                 }
                 return true;
+            }
             case MSG_REMOVE_IME_SURFACE: {
                 synchronized (ImfLock.class) {
                     // TODO(b/305849394): Needs to figure out what to do where for background users.
@@ -5125,8 +5125,7 @@
             case MSG_REMOVE_IME_SURFACE_FROM_WINDOW: {
                 IBinder windowToken = (IBinder) msg.obj;
                 synchronized (ImfLock.class) {
-                    // TODO(b/305849394): Infer userId from windowToken.
-                    final int userId = mCurrentUserId;
+                    final int userId = resolveImeUserIdFromWindowLocked(windowToken);
                     final var userData = getUserData(userId);
                     try {
                         if (windowToken == userData.mImeBindingState.mFocusedWindow
@@ -5139,10 +5138,6 @@
                 }
                 return true;
             }
-            case MSG_UPDATE_IME_WINDOW_STATUS: {
-                updateImeWindowStatus(msg.arg1 == 1);
-                return true;
-            }
 
             // ---------------------------------------------------------
 
@@ -5152,18 +5147,13 @@
 
             // --------------------------------------------------------------
             case MSG_HARD_KEYBOARD_SWITCH_CHANGED:
-                if (!Flags.imeSwitcherRevamp()) {
+                if (!mNewInputMethodSwitcherMenuEnabled) {
                     mMenuController.handleHardKeyboardStatusChange(msg.arg1 == 1);
                 }
                 synchronized (ImfLock.class) {
                     sendOnNavButtonFlagsChangedToAllImesLocked();
                 }
                 return true;
-            case MSG_SYSTEM_UNLOCK_USER: {
-                final int userId = msg.arg1;
-                onUnlockUser(userId);
-                return true;
-            }
             case MSG_DISPATCH_ON_INPUT_METHOD_LIST_UPDATED: {
                 final int userId = msg.arg1;
                 final List<InputMethodInfo> imes = (List<InputMethodInfo>) msg.obj;
@@ -5317,31 +5307,15 @@
     }
 
     @NonNull
-    static InputMethodSettings queryInputMethodServicesInternal(Context context,
-            @UserIdInt int userId, @NonNull AdditionalSubtypeMap additionalSubtypeMap,
-            @DirectBootAwareness int directBootAwareness) {
+    static RawInputMethodMap queryRawInputMethodServiceMap(Context context, @UserIdInt int userId) {
         final Context userAwareContext = context.getUserId() == userId
                 ? context
                 : context.createContextAsUser(UserHandle.of(userId), 0 /* flags */);
 
-        final int directBootAwarenessFlags;
-        switch (directBootAwareness) {
-            case DirectBootAwareness.ANY:
-                directBootAwarenessFlags = PackageManager.MATCH_DIRECT_BOOT_AWARE
-                        | PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
-                break;
-            case DirectBootAwareness.AUTO:
-                directBootAwarenessFlags = PackageManager.MATCH_DIRECT_BOOT_AUTO;
-                break;
-            default:
-                directBootAwarenessFlags = PackageManager.MATCH_DIRECT_BOOT_AUTO;
-                Slog.e(TAG, "Unknown directBootAwareness=" + directBootAwareness
-                        + ". Falling back to DirectBootAwareness.AUTO");
-                break;
-        }
         final int flags = PackageManager.GET_META_DATA
                 | PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS
-                | directBootAwarenessFlags;
+                | PackageManager.MATCH_DIRECT_BOOT_AWARE
+                | PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
 
         // Beware that package visibility filtering will be enforced based on the effective calling
         // identity (Binder.getCallingUid()), but our use case always expect Binder.getCallingUid()
@@ -5357,14 +5331,11 @@
         final List<String> enabledInputMethodList =
                 InputMethodUtils.getEnabledInputMethodIdsForFiltering(context, userId);
 
-        final InputMethodMap methodMap = filterInputMethodServices(
-                additionalSubtypeMap, enabledInputMethodList, userAwareContext, services);
-        return InputMethodSettings.create(methodMap, userId);
+        return filterInputMethodServices(enabledInputMethodList, userAwareContext, services);
     }
 
     @NonNull
-    static InputMethodMap filterInputMethodServices(
-            @NonNull AdditionalSubtypeMap additionalSubtypeMap,
+    static RawInputMethodMap filterInputMethodServices(
             List<String> enabledInputMethodList, Context userAwareContext,
             List<ResolveInfo> services) {
         final ArrayMap<String, Integer> imiPackageCount = new ArrayMap<>();
@@ -5385,7 +5356,7 @@
 
             try {
                 final InputMethodInfo imi = new InputMethodInfo(userAwareContext, ri,
-                        additionalSubtypeMap.get(imeId));
+                        Collections.emptyList());
                 if (imi.isVrOnly()) {
                     continue;  // Skip VR-only IME, which isn't supported for now.
                 }
@@ -5408,7 +5379,7 @@
                 Slog.wtf(TAG, "Unable to load input method " + imeId, e);
             }
         }
-        return InputMethodMap.of(methodMap);
+        return RawInputMethodMap.of(methodMap);
     }
 
     @GuardedBy("ImfLock.class")
@@ -5712,38 +5683,15 @@
         }
     }
 
-    /**
-     * Returns the default {@link InputMethodInfo} for the specific userId.
-     *
-     * @param userId user ID to query
-     */
-    @GuardedBy("ImfLock.class")
-    private InputMethodInfo queryDefaultInputMethodForUserIdLocked(@UserIdInt int userId) {
-        final InputMethodSettings settings = InputMethodSettingsRepository.get(userId);
-        return settings.getMethodMap().get(settings.getSelectedInputMethod());
-    }
-
     @GuardedBy("ImfLock.class")
     private boolean switchToInputMethodLocked(@NonNull String imeId, int subtypeId,
             @UserIdInt int userId) {
-        final InputMethodSettings settings = InputMethodSettingsRepository.get(userId);
-        if (mConcurrentMultiUserModeEnabled || userId == mCurrentUserId) {
-            if (!settings.getMethodMap().containsKey(imeId)
-                    || !settings.getEnabledInputMethodList()
-                    .contains(settings.getMethodMap().get(imeId))) {
-                return false; // IME is not found or not enabled.
-            }
-            setInputMethodLocked(imeId, subtypeId, userId);
-            return true;
-        }
-        if (!settings.getMethodMap().containsKey(imeId)
-                || !settings.getEnabledInputMethodList().contains(
-                settings.getMethodMap().get(imeId))) {
+        final var settings = InputMethodSettingsRepository.get(userId);
+        final var enabledImes = settings.getEnabledInputMethodList();
+        if (!CollectionUtils.any(enabledImes, imi -> imi.getId().equals(imeId))) {
             return false; // IME is not found or not enabled.
         }
-        settings.putSelectedInputMethod(imeId);
-        // For non-current user, only reset subtypeId (instead of setting the given one).
-        settings.putSelectedSubtype(NOT_A_SUBTYPE_ID);
+        setInputMethodLocked(imeId, subtypeId, userId);
         return true;
     }
 
@@ -5853,25 +5801,34 @@
 
         @ImfLockFree
         @Override
-        public void hideAllInputMethods(@SoftInputShowHideReason int reason,
+        public void hideInputMethod(@SoftInputShowHideReason int reason,
                 int originatingDisplayId) {
-            mHandler.removeMessages(MSG_HIDE_ALL_INPUT_METHODS);
-            mHandler.obtainMessage(MSG_HIDE_ALL_INPUT_METHODS, reason).sendToTarget();
+            mHandler.removeMessages(MSG_HIDE_INPUT_METHOD);
+            mHandler.obtainMessage(MSG_HIDE_INPUT_METHOD, reason, originatingDisplayId)
+                    .sendToTarget();
         }
 
+        @ImfLockFree
+        @NonNull
         @Override
         public List<InputMethodInfo> getInputMethodListAsUser(@UserIdInt int userId) {
-            synchronized (ImfLock.class) {
-                return getInputMethodListLocked(userId, DirectBootAwareness.AUTO,
-                        Process.SYSTEM_UID);
-            }
+            return getInputMethodListInternal(userId, DirectBootAwareness.AUTO, Process.SYSTEM_UID);
         }
 
+        @ImfLockFree
+        @NonNull
         @Override
         public List<InputMethodInfo> getEnabledInputMethodListAsUser(@UserIdInt int userId) {
-            synchronized (ImfLock.class) {
-                return getEnabledInputMethodListLocked(userId, Process.SYSTEM_UID);
-            }
+            return getEnabledInputMethodListInternal(userId, Process.SYSTEM_UID);
+        }
+
+        @ImfLockFree
+        @NonNull
+        @Override
+        public List<InputMethodSubtype> getEnabledInputMethodSubtypeListAsUser(
+                String imiId, boolean allowsImplicitlyEnabledSubtypes, @UserIdInt int userId) {
+            return getEnabledInputMethodSubtypeListInternal(imiId, allowsImplicitlyEnabledSubtypes,
+                    userId, Process.SYSTEM_UID);
         }
 
         @Override
@@ -5902,22 +5859,7 @@
                 if (!settings.getMethodMap().containsKey(imeId)) {
                     return false; // IME is not found.
                 }
-                if (userId == mCurrentUserId) {
-                    setInputMethodEnabledLocked(imeId, enabled, userId);
-                    return true;
-                }
-                if (enabled) {
-                    final String enabledImeIdsStr = settings.getEnabledInputMethodsStr();
-                    final String newEnabledImeIdsStr = InputMethodUtils.concatEnabledImeIds(
-                            enabledImeIdsStr, imeId);
-                    if (!TextUtils.equals(enabledImeIdsStr, newEnabledImeIdsStr)) {
-                        settings.putEnabledInputMethodsStr(newEnabledImeIdsStr);
-                    }
-                } else {
-                    settings.buildAndPutEnabledInputMethodsStrRemovingId(
-                            new StringBuilder(),
-                            settings.getEnabledInputMethodsAndSubtypeList(), imeId);
-                }
+                setInputMethodEnabledLocked(imeId, enabled, userId);
                 return true;
             }
         }
@@ -5965,8 +5907,7 @@
         @Override
         public void reportImeControl(@Nullable IBinder windowToken) {
             synchronized (ImfLock.class) {
-                // TODO(b/305849394): Need to infer userId or get userId from callers.
-                final int userId = mCurrentUserId;
+                final int userId = resolveImeUserIdFromWindowLocked(windowToken);
                 final var userData = getUserData(userId);
                 if (userData.mImeBindingState.mFocusedWindow != windowToken) {
                     // A perceptible value was set for the focused window, but it is no longer in
@@ -5981,18 +5922,18 @@
         @Override
         public void onImeParentChanged(int displayId) {
             synchronized (ImfLock.class) {
-                // TODO(b/305849394): Need to infer userId or get userId from callers.
-                final int userId = mCurrentUserId;
+                final int userId = resolveImeUserIdFromDisplayIdLocked(displayId);
                 final var userData = getUserData(userId);
                 // Hide the IME method menu only when the IME surface parent is changed by the
                 // input target changed, in case seeing the dialog dismiss flickering during
                 // the next focused window starting the input connection.
-                if (mLastImeTargetWindow != userData.mImeBindingState.mFocusedWindow) {
-                    if (Flags.imeSwitcherRevamp()) {
+                if (mVisibilityStateComputer.getLastImeTargetWindow()
+                        != userData.mImeBindingState.mFocusedWindow) {
+                    if (mNewInputMethodSwitcherMenuEnabled) {
                         final var bindingController = getInputMethodBindingController(userId);
                         mMenuControllerNew.hide(bindingController.getCurTokenDisplayId(), userId);
                     } else {
-                        mMenuController.hideInputMethodMenuLocked();
+                        mMenuController.hideInputMethodMenuLocked(userId);
                     }
                 }
             }
@@ -6007,8 +5948,11 @@
         @ImfLockFree
         @Override
         public void updateImeWindowStatus(boolean disableImeIcon, int displayId) {
-            mHandler.obtainMessage(MSG_UPDATE_IME_WINDOW_STATUS, disableImeIcon ? 1 : 0, 0)
-                    .sendToTarget();
+            mHandler.post(() -> {
+                synchronized (ImfLock.class) {
+                    updateImeWindowStatusLocked(disableImeIcon, displayId);
+                }
+            });
         }
 
         @Override
@@ -6106,8 +6050,8 @@
         public void onSwitchKeyboardLayoutShortcut(int direction, int displayId,
                 IBinder targetWindowToken) {
             synchronized (ImfLock.class) {
-                // TODO(b/305849394): Infer userId from displayId
-                switchKeyboardLayoutLocked(direction, getUserData(mCurrentUserId));
+                final int userId = resolveImeUserIdFromDisplayIdLocked(displayId);
+                switchKeyboardLayoutLocked(direction, getUserData(userId));
             }
         }
     }
@@ -6341,9 +6285,12 @@
                     };
             mUserDataRepository.forAllUserData(userDataDump);
 
-            if (Flags.imeSwitcherRevamp()) {
+            if (mNewInputMethodSwitcherMenuEnabled) {
                 p.println("  menuControllerNew:");
                 mMenuControllerNew.dump(p, "  ");
+            } else {
+                p.println("  menuController:");
+                mMenuController.dump(p, "  ");
             }
             p.println("  mCurToken=" + bindingController.getCurToken());
             p.println("  mCurTokenDisplayId=" + bindingController.getCurTokenDisplayId());
@@ -6617,28 +6564,29 @@
                     break;
             }
         }
+        final int[] userIds;
         synchronized (ImfLock.class) {
-            final int[] userIds = InputMethodUtils.resolveUserId(userIdToBeResolved,
-                    mCurrentUserId, shellCommand.getErrPrintWriter());
-            try (PrintWriter pr = shellCommand.getOutPrintWriter()) {
-                for (int userId : userIds) {
-                    final List<InputMethodInfo> methods = all
-                            ? getInputMethodListLocked(
-                                    userId, DirectBootAwareness.AUTO, Process.SHELL_UID)
-                            : getEnabledInputMethodListLocked(userId, Process.SHELL_UID);
-                    if (userIds.length > 1) {
-                        pr.print("User #");
-                        pr.print(userId);
+            userIds = InputMethodUtils.resolveUserId(userIdToBeResolved, mCurrentUserId,
+                    shellCommand.getErrPrintWriter());
+        }
+        try (PrintWriter pr = shellCommand.getOutPrintWriter()) {
+            for (int userId : userIds) {
+                final List<InputMethodInfo> methods = all
+                        ? getInputMethodListInternal(
+                                userId, DirectBootAwareness.AUTO, Process.SHELL_UID)
+                        : getEnabledInputMethodListInternal(userId, Process.SHELL_UID);
+                if (userIds.length > 1) {
+                    pr.print("User #");
+                    pr.print(userId);
+                    pr.println(":");
+                }
+                for (InputMethodInfo info : methods) {
+                    if (brief) {
+                        pr.println(info.getId());
+                    } else {
+                        pr.print(info.getId());
                         pr.println(":");
-                    }
-                    for (InputMethodInfo info : methods) {
-                        if (brief) {
-                            pr.println(info.getId());
-                        } else {
-                            pr.print(info.getId());
-                            pr.println(":");
-                            info.dump(pr::println, "  ");
-                        }
+                        info.dump(pr::println, "  ");
                     }
                 }
             }
@@ -6721,36 +6669,8 @@
     private boolean handleShellCommandEnableDisableInputMethodInternalLocked(
             @UserIdInt int userId, String imeId, boolean enabled, PrintWriter out,
             PrintWriter error) {
-        boolean failedToEnableUnknownIme = false;
-        boolean previouslyEnabled = false;
         final InputMethodSettings settings = InputMethodSettingsRepository.get(userId);
-        if (userId == mCurrentUserId) {
-            if (enabled && !settings.getMethodMap().containsKey(imeId)) {
-                failedToEnableUnknownIme = true;
-            } else {
-                previouslyEnabled = setInputMethodEnabledLocked(imeId, enabled, userId);
-            }
-        } else {
-            if (enabled) {
-                if (!settings.getMethodMap().containsKey(imeId)) {
-                    failedToEnableUnknownIme = true;
-                } else {
-                    final String enabledImeIdsStr = settings.getEnabledInputMethodsStr();
-                    final String newEnabledImeIdsStr = InputMethodUtils.concatEnabledImeIds(
-                            enabledImeIdsStr, imeId);
-                    previouslyEnabled = TextUtils.equals(enabledImeIdsStr, newEnabledImeIdsStr);
-                    if (!previouslyEnabled) {
-                        settings.putEnabledInputMethodsStr(newEnabledImeIdsStr);
-                    }
-                }
-            } else {
-                previouslyEnabled =
-                        settings.buildAndPutEnabledInputMethodsStrRemovingId(
-                                new StringBuilder(),
-                                settings.getEnabledInputMethodsAndSubtypeList(), imeId);
-            }
-        }
-        if (failedToEnableUnknownIme) {
+        if (enabled && !settings.getMethodMap().containsKey(imeId)) {
             error.print("Unknown input method ");
             error.print(imeId);
             error.println(" cannot be enabled for user #" + userId);
@@ -6759,6 +6679,8 @@
                     + " failed due to its unrecognized IME ID.");
             return false;
         }
+
+        final boolean previouslyEnabled = setInputMethodEnabledLocked(imeId, enabled, userId);
         out.print("Input method ");
         out.print(imeId);
         out.print(": ");
@@ -6839,66 +6761,47 @@
                     final String nextIme;
                     final List<InputMethodInfo> nextEnabledImes;
                     final InputMethodSettings settings = InputMethodSettingsRepository.get(userId);
-                    if (userId == mCurrentUserId) {
-                        final var userData = getUserData(userId);
-                        if (Flags.refactorInsetsController()) {
-                            if (userData.mImeBindingState != null
-                                    && userData.mImeBindingState.mFocusedWindowClient != null
-                                    && userData.mImeBindingState.mFocusedWindowClient.mClient
-                                    != null) {
-                                userData.mImeBindingState.mFocusedWindowClient.mClient
-                                        .setImeVisibility(false);
-                            } else {
-                                // TODO(b329229469): ImeTracker?
-                            }
+                    final var userData = getUserData(userId);
+                    if (Flags.refactorInsetsController()) {
+                        if (userData.mImeBindingState != null
+                                && userData.mImeBindingState.mFocusedWindowClient != null
+                                && userData.mImeBindingState.mFocusedWindowClient.mClient
+                                != null) {
+                            userData.mImeBindingState.mFocusedWindowClient.mClient
+                                    .setImeVisibility(false,
+                                    null /* TODO(b329229469) initialize statsToken here? */);
                         } else {
-                            hideCurrentInputLocked(userData.mImeBindingState.mFocusedWindow,
-                                    0 /* flags */,
-                                    SoftInputShowHideReason.HIDE_RESET_SHELL_COMMAND, userId);
+                            // TODO(b329229469): ImeTracker?
                         }
-                        final var bindingController = userData.mBindingController;
-                        bindingController.unbindCurrentMethod();
-
-                        // Enable default IMEs, disable others
-                        var toDisable = settings.getEnabledInputMethodList();
-                        var defaultEnabled = InputMethodInfoUtils.getDefaultEnabledImes(
-                                mContext, settings.getMethodList());
-                        toDisable.removeAll(defaultEnabled);
-                        for (InputMethodInfo info : toDisable) {
-                            setInputMethodEnabledLocked(info.getId(), false, userId);
-                        }
-                        for (InputMethodInfo info : defaultEnabled) {
-                            setInputMethodEnabledLocked(info.getId(), true, userId);
-                        }
-                        // Choose new default IME, reset to none if no IME available.
-                        if (!chooseNewDefaultIMELocked(userId)) {
-                            resetSelectedInputMethodAndSubtypeLocked(null, userId);
-                        }
-                        updateInputMethodsFromSettingsLocked(true /* enabledMayChange */, userId);
-                        InputMethodUtils.setNonSelectedSystemImesDisabledUntilUsed(
-                                getPackageManagerForUser(mContext, settings.getUserId()),
-                                settings.getEnabledInputMethodList());
-                        nextIme = settings.getSelectedInputMethod();
-                        nextEnabledImes = settings.getEnabledInputMethodList();
                     } else {
-                        nextEnabledImes = InputMethodInfoUtils.getDefaultEnabledImes(mContext,
-                                settings.getMethodList());
-                        nextIme = InputMethodInfoUtils.getMostApplicableDefaultIME(
-                                nextEnabledImes).getId();
-
-                        // Reset enabled IMEs.
-                        final String[] nextEnabledImeIds = new String[nextEnabledImes.size()];
-                        for (int i = 0; i < nextEnabledImeIds.length; ++i) {
-                            nextEnabledImeIds[i] = nextEnabledImes.get(i).getId();
-                        }
-                        settings.putEnabledInputMethodsStr(InputMethodUtils.concatEnabledImeIds(
-                                "", nextEnabledImeIds));
-
-                        // Reset selected IME.
-                        settings.putSelectedInputMethod(nextIme);
-                        settings.putSelectedDefaultDeviceInputMethod(null);
-                        settings.putSelectedSubtype(NOT_A_SUBTYPE_ID);
+                        hideCurrentInputLocked(userData.mImeBindingState.mFocusedWindow,
+                                0 /* flags */,
+                                SoftInputShowHideReason.HIDE_RESET_SHELL_COMMAND, userId);
                     }
+                    final var bindingController = userData.mBindingController;
+                    bindingController.unbindCurrentMethod();
+
+                    // Enable default IMEs, disable others
+                    var toDisable = settings.getEnabledInputMethodList();
+                    var defaultEnabled = InputMethodInfoUtils.getDefaultEnabledImes(
+                            mContext, settings.getMethodList());
+                    toDisable.removeAll(defaultEnabled);
+                    for (InputMethodInfo info : toDisable) {
+                        setInputMethodEnabledLocked(info.getId(), false, userId);
+                    }
+                    for (InputMethodInfo info : defaultEnabled) {
+                        setInputMethodEnabledLocked(info.getId(), true, userId);
+                    }
+                    // Choose new default IME, reset to none if no IME available.
+                    if (!chooseNewDefaultIMELocked(userId)) {
+                        resetSelectedInputMethodAndSubtypeLocked(null, userId);
+                    }
+                    updateInputMethodsFromSettingsLocked(true /* enabledMayChange */, userId);
+                    InputMethodUtils.setNonSelectedSystemImesDisabledUntilUsed(
+                            getPackageManagerForUser(mContext, settings.getUserId()),
+                            settings.getEnabledInputMethodList());
+                    nextIme = settings.getSelectedInputMethod();
+                    nextEnabledImes = settings.getEnabledInputMethodList();
                     out.println("Reset current and enabled IMEs for user #" + userId);
                     out.println("  Selected: " + nextIme);
                     nextEnabledImes.forEach(ime -> out.println("   Enabled: " + ime.getId()));
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java b/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java
index 06f73f3..f16a5a0 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java
@@ -21,6 +21,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.UserIdInt;
 import android.app.AlertDialog;
 import android.content.Context;
 import android.content.DialogInterface;
@@ -28,6 +29,7 @@
 import android.graphics.drawable.Drawable;
 import android.provider.Settings;
 import android.text.TextUtils;
+import android.util.Printer;
 import android.util.Slog;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -58,6 +60,7 @@
     private AlertDialog.Builder mDialogBuilder;
     private AlertDialog mSwitchingDialog;
     private View mSwitchingDialogTitleView;
+    private List<ImeSubtypeListItem> mImList;
     private InputMethodInfo[] mIms;
     private int[] mSubtypeIds;
 
@@ -75,13 +78,12 @@
     @GuardedBy("ImfLock.class")
     void showInputMethodMenuLocked(boolean showAuxSubtypes, int displayId,
             String preferredInputMethodId, int preferredInputMethodSubtypeId,
-            @NonNull List<ImeSubtypeListItem> imList) {
+            @NonNull List<ImeSubtypeListItem> imList, @UserIdInt int userId) {
         if (DEBUG) Slog.v(TAG, "Show switching menu. showAuxSubtypes=" + showAuxSubtypes);
 
-        final int userId = mService.getCurrentImeUserIdLocked();
         final var bindingController = mService.getInputMethodBindingController(userId);
 
-        hideInputMethodMenuLocked();
+        hideInputMethodMenuLocked(userId);
 
         if (preferredInputMethodSubtypeId == NOT_A_SUBTYPE_ID) {
             final InputMethodSubtype currentSubtype =
@@ -97,6 +99,7 @@
 
         // Find out which item should be checked by default.
         final int size = imList.size();
+        mImList = imList;
         mIms = new InputMethodInfo[size];
         mSubtypeIds = new int[size];
         // No items are checked by default. When we have a list of explicitly enabled subtypes,
@@ -128,7 +131,7 @@
         }
         final Context dialogWindowContext = mDialogWindowContext.get(displayId);
         mDialogBuilder = new AlertDialog.Builder(dialogWindowContext);
-        mDialogBuilder.setOnCancelListener(dialog -> hideInputMethodMenu());
+        mDialogBuilder.setOnCancelListener(dialog -> hideInputMethodMenu(userId));
 
         final Context dialogContext = mDialogBuilder.getContext();
         final TypedArray a = dialogContext.obtainStyledAttributes(null,
@@ -159,7 +162,7 @@
                     isChecked, userId);
             // Ensure that the input method dialog is dismissed when changing
             // the hardware keyboard state.
-            hideInputMethodMenu();
+            hideInputMethodMenu(userId);
         });
 
         // Fill the list items with onClick listener, which takes care of IME (and subtype)
@@ -182,7 +185,7 @@
                     }
                     mService.setInputMethodLocked(im.getId(), subtypeId, userId);
                 }
-                hideInputMethodMenuLocked();
+                hideInputMethodMenuLocked(userId);
             }
         };
         mDialogBuilder.setSingleChoiceItems(adapter, checkedItem, choiceListener);
@@ -206,10 +209,10 @@
         mSwitchingDialog.show();
     }
 
-    void updateKeyboardFromSettingsLocked() {
+    void updateKeyboardFromSettingsLocked(@UserIdInt int userId) {
         mShowImeWithHardKeyboard =
                 SecureSettingsWrapper.getBoolean(Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD,
-                        false, mService.getCurrentImeUserIdLocked());
+                        false, userId);
         if (mSwitchingDialog != null && mSwitchingDialogTitleView != null
                 && mSwitchingDialog.isShowing()) {
             final Switch hardKeySwitch = mSwitchingDialogTitleView.findViewById(
@@ -220,18 +223,22 @@
 
     /**
      * Hides the input method switcher menu.
+     *
+     * @param userId user ID for this operation
      */
-    void hideInputMethodMenu() {
+    void hideInputMethodMenu(@UserIdInt int userId) {
         synchronized (ImfLock.class) {
-            hideInputMethodMenuLocked();
+            hideInputMethodMenuLocked(userId);
         }
     }
 
     /**
      * Hides the input method switcher menu, synchronised version of {@link #hideInputMethodMenu}.
+     *
+     * @param userId user ID for this operation
      */
     @GuardedBy("ImfLock.class")
-    void hideInputMethodMenuLocked() {
+    void hideInputMethodMenuLocked(@UserIdInt int userId) {
         if (DEBUG) Slog.v(TAG, "Hide switching menu");
 
         if (mSwitchingDialog != null) {
@@ -239,12 +246,12 @@
             mSwitchingDialog = null;
             mSwitchingDialogTitleView = null;
 
-            // TODO(b/305849394): Make InputMethodMenuController multi-user aware
-            final int userId = mService.getCurrentImeUserIdLocked();
             mService.updateSystemUiLocked(userId);
             mService.sendOnNavButtonFlagsChangedToAllImesLocked();
             mDialogBuilder = null;
+            mImList = null;
             mIms = null;
+            mSubtypeIds = null;
         }
     }
 
@@ -277,6 +284,15 @@
         }
     }
 
+    void dump(@NonNull Printer pw, @NonNull String prefix) {
+        final boolean showing = isisInputMethodPickerShownForTestLocked();
+        pw.println(prefix + "  isShowing: " + showing);
+
+        if (showing) {
+            pw.println(prefix + "  imList: " + mImList);
+        }
+    }
+
     private static class ImeSubtypeListAdapter extends ArrayAdapter<ImeSubtypeListItem> {
         private final LayoutInflater mInflater;
         private final int mTextViewResourceId;
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodMenuControllerNew.java b/services/core/java/com/android/server/inputmethod/InputMethodMenuControllerNew.java
index 045414b..35fae18 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodMenuControllerNew.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodMenuControllerNew.java
@@ -140,6 +140,8 @@
         // Indicate that the list can be scrolled.
         recyclerView.setScrollIndicators(
                 hasLanguageSettingsButton ? View.SCROLL_INDICATOR_BOTTOM : 0);
+        // Request focus to enable rotary scrolling on watches.
+        recyclerView.requestFocus();
 
         builder.setOnCancelListener(dialog -> hide(displayId, userId));
         mMenuItems = items;
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodSettingsRepository.java b/services/core/java/com/android/server/inputmethod/InputMethodSettingsRepository.java
index 1b84036..4f5af63 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodSettingsRepository.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodSettingsRepository.java
@@ -19,15 +19,13 @@
 import android.annotation.AnyThread;
 import android.annotation.NonNull;
 import android.annotation.UserIdInt;
-import android.util.SparseArray;
-
-import com.android.internal.annotations.GuardedBy;
 
 final class InputMethodSettingsRepository {
-    // TODO(b/352594784): Should we user other lock primitives?
-    @GuardedBy("sPerUserMap")
+    private static final Object sMutationLock = new Object();
+
     @NonNull
-    private static final SparseArray<InputMethodSettings> sPerUserMap = new SparseArray<>();
+    private static volatile ImmutableSparseArray<InputMethodSettings> sPerUserMap =
+            ImmutableSparseArray.empty();
 
     /**
      * Not intended to be instantiated.
@@ -38,10 +36,7 @@
     @NonNull
     @AnyThread
     static InputMethodSettings get(@UserIdInt int userId) {
-        final InputMethodSettings obj;
-        synchronized (sPerUserMap) {
-            obj = sPerUserMap.get(userId);
-        }
+        final InputMethodSettings obj = sPerUserMap.get(userId);
         if (obj != null) {
             return obj;
         }
@@ -50,15 +45,15 @@
 
     @AnyThread
     static void put(@UserIdInt int userId, @NonNull InputMethodSettings obj) {
-        synchronized (sPerUserMap) {
-            sPerUserMap.put(userId, obj);
+        synchronized (sMutationLock) {
+            sPerUserMap = sPerUserMap.cloneWithPutOrSelf(userId, obj);
         }
     }
 
     @AnyThread
     static void remove(@UserIdInt int userId) {
-        synchronized (sPerUserMap) {
-            sPerUserMap.remove(userId);
+        synchronized (sMutationLock) {
+            sPerUserMap = sPerUserMap.cloneWithRemoveOrSelf(userId);
         }
     }
 }
diff --git a/services/core/java/com/android/server/inputmethod/RawInputMethodMap.java b/services/core/java/com/android/server/inputmethod/RawInputMethodMap.java
new file mode 100644
index 0000000..4e39a3f
--- /dev/null
+++ b/services/core/java/com/android/server/inputmethod/RawInputMethodMap.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.inputmethod;
+
+import android.annotation.AnyThread;
+import android.annotation.NonNull;
+import android.util.ArrayMap;
+import android.util.Slog;
+import android.view.inputmethod.InputMethodInfo;
+
+import com.android.internal.inputmethod.DirectBootAwareness;
+
+import java.util.List;
+
+/**
+ * This is quite similar to {@link InputMethodMap} with two major differences.
+ *
+ * <ul>
+ *     <li>Additional {@link android.view.inputmethod.InputMethodSubtype} is not included.</li>
+ *     <li>Always include direct-boot unaware {@link android.inputmethodservice.InputMethodService}.
+ *     </li>
+ * </ul>
+ *
+ * <p>As seen in {@link #toInputMethodMap(AdditionalSubtypeMap, int, boolean)}, you can consider
+ * this is a prototype data where you can always derive {@link InputMethodMap} with
+ * {@link AdditionalSubtypeMap} and a boolean information whether
+ * {@link com.android.server.pm.UserManagerInternal#isUserUnlockingOrUnlocked(int)} returns
+ * {@code true} or not.</p>
+ */
+final class RawInputMethodMap {
+    static final String TAG = "RawInputMethodMap";
+
+    private static final ArrayMap<String, InputMethodInfo> EMPTY_MAP = new ArrayMap<>();
+
+    private final ArrayMap<String, InputMethodInfo> mMap;
+
+    static RawInputMethodMap emptyMap() {
+        return new RawInputMethodMap(EMPTY_MAP);
+    }
+
+    static RawInputMethodMap of(@NonNull ArrayMap<String, InputMethodInfo> map) {
+        return new RawInputMethodMap(map);
+    }
+
+    private RawInputMethodMap(@NonNull ArrayMap<String, InputMethodInfo> map) {
+        mMap = map.isEmpty() ? EMPTY_MAP : new ArrayMap<>(map);
+    }
+
+    @AnyThread
+    @NonNull
+    List<InputMethodInfo> values() {
+        return List.copyOf(mMap.values());
+    }
+
+    @NonNull
+    InputMethodMap toInputMethodMap(@NonNull AdditionalSubtypeMap additionalSubtypeMap,
+            @DirectBootAwareness int directBootAwareness, boolean userUnlocked) {
+        final int size = mMap.size();
+        final var newMap = new ArrayMap<String, InputMethodInfo>(size);
+
+        final boolean requireDirectBootAwareFlag;
+        switch (directBootAwareness) {
+            case DirectBootAwareness.ANY -> requireDirectBootAwareFlag = false;
+            case DirectBootAwareness.AUTO -> requireDirectBootAwareFlag = !userUnlocked;
+            default -> {
+                requireDirectBootAwareFlag = !userUnlocked;
+                Slog.e(TAG, "Unknown directBootAwareness=" + directBootAwareness
+                        + ". Falling back to DirectBootAwareness.AUTO");
+            }
+        }
+
+        boolean updated = false;
+        for (int i = 0; i < size; ++i) {
+            final var imeId = mMap.keyAt(i);
+            final var imi = mMap.valueAt(i);
+            if (requireDirectBootAwareFlag && !imi.getServiceInfo().directBootAware) {
+                updated = true;
+                continue;
+            }
+            final var newAdditionalSubtypes = additionalSubtypeMap.get(imeId);
+            if (newAdditionalSubtypes == null || newAdditionalSubtypes.isEmpty()) {
+                newMap.put(imi.getId(), imi);
+            } else {
+                updated = true;
+                newMap.put(imi.getId(), new InputMethodInfo(imi, newAdditionalSubtypes));
+            }
+        }
+        // If newMap is semantically the same as mMap, we can reuse mMap and discard newMap.
+        return InputMethodMap.of(updated ? newMap : mMap);
+    }
+}
diff --git a/services/core/java/com/android/server/inputmethod/SecureSettingsWrapper.java b/services/core/java/com/android/server/inputmethod/SecureSettingsWrapper.java
index e7cff20..476888e 100644
--- a/services/core/java/com/android/server/inputmethod/SecureSettingsWrapper.java
+++ b/services/core/java/com/android/server/inputmethod/SecureSettingsWrapper.java
@@ -20,11 +20,13 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
+import android.app.ActivityManagerInternal;
 import android.content.ContentResolver;
+import android.content.Context;
+import android.content.pm.UserInfo;
 import android.provider.Settings;
 import android.util.ArrayMap;
 import android.util.ArraySet;
-import android.util.SparseArray;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.server.LocalServices;
@@ -38,6 +40,13 @@
  * to the persistent value when the user storage is unlocked.</p>
  */
 final class SecureSettingsWrapper {
+
+    private static final Object sMutationLock = new Object();
+
+    @NonNull
+    private static volatile ImmutableSparseArray<ReaderWriter> sUserMap =
+            ImmutableSparseArray.empty();
+
     @Nullable
     private static volatile ContentResolver sContentResolver = null;
 
@@ -61,8 +70,8 @@
      */
     @AnyThread
     static void endTestMode() {
-        synchronized (sUserMap) {
-            sUserMap.clear();
+        synchronized (sMutationLock) {
+            sUserMap = ImmutableSparseArray.empty();
         }
         sTestMode = false;
     }
@@ -243,10 +252,6 @@
         }
     }
 
-    @GuardedBy("sUserMap")
-    @NonNull
-    private static final SparseArray<ReaderWriter> sUserMap = new SparseArray<>();
-
     private static final ReaderWriter NOOP = new ReaderWriter() {
         @Override
         public void putString(String key, String str) {
@@ -282,15 +287,15 @@
     private static ReaderWriter putOrGet(@UserIdInt int userId,
             @NonNull ReaderWriter readerWriter) {
         final boolean isUnlockedUserImpl = readerWriter instanceof UnlockedUserImpl;
-        synchronized (sUserMap) {
+        synchronized (sMutationLock) {
             final ReaderWriter current = sUserMap.get(userId);
             if (current == null) {
-                sUserMap.put(userId, readerWriter);
+                sUserMap = sUserMap.cloneWithPutOrSelf(userId, readerWriter);
                 return readerWriter;
             }
             // Upgrading from CopyOnWriteImpl to DirectImpl is allowed.
             if (current instanceof LockedUserImpl && isUnlockedUserImpl) {
-                sUserMap.put(userId, readerWriter);
+                sUserMap = sUserMap.cloneWithPutOrSelf(userId, readerWriter);
                 return readerWriter;
             }
             return current;
@@ -300,11 +305,9 @@
     @NonNull
     @AnyThread
     private static ReaderWriter get(@UserIdInt int userId) {
-        synchronized (sUserMap) {
-            final ReaderWriter readerWriter = sUserMap.get(userId);
-            if (readerWriter != null) {
-                return readerWriter;
-            }
+        final ReaderWriter readerWriter = sUserMap.get(userId);
+        if (readerWriter != null) {
+            return readerWriter;
         }
         if (sTestMode) {
             return putOrGet(userId, new FakeReaderWriterImpl());
@@ -318,13 +321,30 @@
     }
 
     /**
-     * Called when the system is starting.
+     * Called when {@link InputMethodManagerService} is starting.
      *
-     * @param contentResolver the {@link ContentResolver} to be used
+     * @param context the {@link Context} to be used.
      */
     @AnyThread
-    static void setContentResolver(@NonNull ContentResolver contentResolver) {
-        sContentResolver = contentResolver;
+    static void onStart(@NonNull Context context) {
+        sContentResolver = context.getContentResolver();
+
+        final int userId = LocalServices.getService(ActivityManagerInternal.class)
+                .getCurrentUserId();
+        final UserManagerInternal userManagerInternal =
+                LocalServices.getService(UserManagerInternal.class);
+        putOrGet(userId, createImpl(userManagerInternal, userId));
+
+        userManagerInternal.addUserLifecycleListener(
+                new UserManagerInternal.UserLifecycleListener() {
+                    @Override
+                    public void onUserRemoved(UserInfo user) {
+                        synchronized (sMutationLock) {
+                            sUserMap = sUserMap.cloneWithRemoveOrSelf(user.id);
+                        }
+                    }
+                }
+        );
     }
 
     /**
@@ -357,14 +377,19 @@
     }
 
     /**
-     * Called when a user is being removed.
+     * Called when a user is stopped, which changes the user storage to the locked state again.
      *
-     * @param userId the ID of the user whose storage is being removed.
+     * @param userId the ID of the user whose storage is being locked again.
      */
     @AnyThread
-    static void onUserRemoved(@UserIdInt int userId) {
-        synchronized (sUserMap) {
-            sUserMap.remove(userId);
+    static void onUserStopped(@UserIdInt int userId) {
+        final LockedUserImpl lockedUserImpl = new LockedUserImpl(userId, sContentResolver);
+        synchronized (sMutationLock) {
+            final ReaderWriter current = sUserMap.get(userId);
+            if (current == null || current instanceof LockedUserImpl) {
+                return;
+            }
+            sUserMap = sUserMap.cloneWithPutOrSelf(userId, lockedUserImpl);
         }
     }
 
diff --git a/services/core/java/com/android/server/inputmethod/UserData.java b/services/core/java/com/android/server/inputmethod/UserData.java
index be57321..4fb55e1 100644
--- a/services/core/java/com/android/server/inputmethod/UserData.java
+++ b/services/core/java/com/android/server/inputmethod/UserData.java
@@ -30,6 +30,7 @@
 
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
 
 /** Placeholder for all IMMS user specific fields */
 final class UserData {
@@ -43,6 +44,17 @@
     @NonNull
     final CountDownLatch mBackgroundLoadLatch = new CountDownLatch(1);
 
+    /**
+     * Contains non-null {@link RawInputMethodMap}, which represents the latest collections of
+     * {@link android.view.inputmethod.InputMethodInfo} for both direct-boot aware and unaware IMEs
+     * before taking {@link AdditionalSubtypeMap} into account.
+     *
+     * <p>See {@link RawInputMethodMap} for details on when to use this.</p>
+     */
+    @NonNull
+    final AtomicReference<RawInputMethodMap> mRawInputMethodMap =
+            new AtomicReference<>(RawInputMethodMap.emptyMap());
+
     @NonNull
     final InputMethodBindingController mBindingController;
 
diff --git a/services/core/java/com/android/server/inputmethod/UserDataRepository.java b/services/core/java/com/android/server/inputmethod/UserDataRepository.java
index 6f831cc..e3524b1 100644
--- a/services/core/java/com/android/server/inputmethod/UserDataRepository.java
+++ b/services/core/java/com/android/server/inputmethod/UserDataRepository.java
@@ -19,51 +19,39 @@
 import android.annotation.AnyThread;
 import android.annotation.NonNull;
 import android.annotation.UserIdInt;
-import android.util.SparseArray;
 
-import com.android.internal.annotations.GuardedBy;
-
-import java.util.concurrent.locks.ReentrantReadWriteLock;
 import java.util.function.Consumer;
 import java.util.function.IntFunction;
 
 final class UserDataRepository {
 
-    private final ReentrantReadWriteLock mUserDataLock = new ReentrantReadWriteLock();
+    private final Object mMutationLock = new Object();
 
-    @GuardedBy("mUserDataLock")
-    private final SparseArray<UserData> mUserData = new SparseArray<>();
+    @NonNull
+    private volatile ImmutableSparseArray<UserData> mUserData = ImmutableSparseArray.empty();
 
     private final IntFunction<InputMethodBindingController> mBindingControllerFactory;
 
     @AnyThread
     @NonNull
     UserData getOrCreate(@UserIdInt int userId) {
-        mUserDataLock.writeLock().lock();
-        try {
-            UserData userData = mUserData.get(userId);
-            if (userData == null) {
-                userData = new UserData(userId, mBindingControllerFactory.apply(userId));
-                mUserData.put(userId, userData);
-            }
+        // Do optimistic read first for optimization.
+        final var userData = mUserData.get(userId);
+        if (userData != null) {
             return userData;
-        } finally {
-            mUserDataLock.writeLock().unlock();
+        }
+        // Note that the below line can be called concurrently. Here we assume that
+        // instantiating UserData for the same user multiple times would have no side effect.
+        final var newUserData = new UserData(userId, mBindingControllerFactory.apply(userId));
+        synchronized (mMutationLock) {
+            mUserData = mUserData.cloneWithPutOrSelf(userId, newUserData);
+            return newUserData;
         }
     }
 
     @AnyThread
     void forAllUserData(Consumer<UserData> consumer) {
-        final SparseArray<UserData> copiedArray;
-        mUserDataLock.readLock().lock();
-        try {
-            copiedArray = mUserData.clone();
-        } finally {
-            mUserDataLock.readLock().unlock();
-        }
-        for (int i = 0; i < copiedArray.size(); i++) {
-            consumer.accept(copiedArray.valueAt(i));
-        }
+        mUserData.forEach(consumer);
     }
 
     UserDataRepository(
@@ -73,11 +61,8 @@
 
     @AnyThread
     void remove(@UserIdInt int userId) {
-        mUserDataLock.writeLock().lock();
-        try {
-            mUserData.remove(userId);
-        } finally {
-            mUserDataLock.writeLock().unlock();
+        synchronized (mMutationLock) {
+            mUserData = mUserData.cloneWithRemoveOrSelf(userId);
         }
     }
 }
diff --git a/services/core/java/com/android/server/inputmethod/ZeroJankProxy.java b/services/core/java/com/android/server/inputmethod/ZeroJankProxy.java
index 770e12d..c940a9c 100644
--- a/services/core/java/com/android/server/inputmethod/ZeroJankProxy.java
+++ b/services/core/java/com/android/server/inputmethod/ZeroJankProxy.java
@@ -86,8 +86,6 @@
     interface Callback extends IInputMethodManagerImpl.Callback {
         @GuardedBy("ImfLock.class")
         ClientState getClientStateLocked(IInputMethodClient client);
-        @GuardedBy("ImfLock.class")
-        boolean isInputShownLocked();
     }
 
     private final Callback mInner;
@@ -178,19 +176,8 @@
             @Nullable ImeTracker.Token statsToken, @InputMethodManager.ShowFlags int flags,
             @MotionEvent.ToolType int lastClickToolType, ResultReceiver resultReceiver,
             @SoftInputShowHideReason int reason) {
-        offload(
-                () -> {
-                    if (!mInner.showSoftInput(
-                            client,
-                            windowToken,
-                            statsToken,
-                            flags,
-                            lastClickToolType,
-                            resultReceiver,
-                            reason)) {
-                        sendResultReceiverFailure(resultReceiver);
-                    }
-                });
+        offload(() -> mInner.showSoftInput(
+                client, windowToken, statsToken, flags, lastClickToolType, resultReceiver, reason));
         return true;
     }
 
@@ -198,30 +185,11 @@
     public boolean hideSoftInput(IInputMethodClient client, IBinder windowToken,
             @Nullable ImeTracker.Token statsToken, @InputMethodManager.HideFlags int flags,
             ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {
-        offload(
-                () -> {
-                    if (!mInner.hideSoftInput(
-                            client, windowToken, statsToken, flags, resultReceiver, reason)) {
-                        sendResultReceiverFailure(resultReceiver);
-                    }
-                });
+        offload(() -> mInner.hideSoftInput(
+                client, windowToken, statsToken, flags, resultReceiver, reason));
         return true;
     }
 
-    private void sendResultReceiverFailure(@Nullable ResultReceiver resultReceiver) {
-        if (resultReceiver == null) {
-            return;
-        }
-        final boolean isInputShown;
-        synchronized (ImfLock.class) {
-            isInputShown = mInner.isInputShownLocked();
-        }
-        resultReceiver.send(isInputShown
-                        ? InputMethodManager.RESULT_UNCHANGED_SHOWN
-                        : InputMethodManager.RESULT_UNCHANGED_HIDDEN,
-                null);
-    }
-
     @Override
     @IInputMethodManagerImpl.PermissionVerified(Manifest.permission.TEST_INPUT_METHOD)
     public void hideSoftInputFromServerForTest() {
@@ -284,7 +252,9 @@
         offload(() -> mInner.showInputMethodPickerFromClient(client, auxiliarySubtypeMode));
     }
 
-    @IInputMethodManagerImpl.PermissionVerified(Manifest.permission.WRITE_SECURE_SETTINGS)
+    @IInputMethodManagerImpl.PermissionVerified(allOf = {
+            Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+            Manifest.permission.WRITE_SECURE_SETTINGS})
     @Override
     public void showInputMethodPickerFromSystem(int auxiliarySubtypeMode, int displayId) {
         mInner.showInputMethodPickerFromSystem(auxiliarySubtypeMode, displayId);
@@ -296,7 +266,9 @@
         return mInner.isInputMethodPickerShownForTest();
     }
 
-    @IInputMethodManagerImpl.PermissionVerified(Manifest.permission.WRITE_SECURE_SETTINGS)
+    @IInputMethodManagerImpl.PermissionVerified(allOf = {
+            Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+            Manifest.permission.WRITE_SECURE_SETTINGS})
     @Override
     public void onImeSwitchButtonClickFromSystem(int displayId) {
         mInner.onImeSwitchButtonClickFromSystem(displayId);
@@ -330,10 +302,12 @@
         mInner.reportPerceptibleAsync(windowToken, perceptible);
     }
 
-    @IInputMethodManagerImpl.PermissionVerified(Manifest.permission.INTERNAL_SYSTEM_WINDOW)
+    @IInputMethodManagerImpl.PermissionVerified(allOf = {
+            Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+            Manifest.permission.INTERNAL_SYSTEM_WINDOW})
     @Override
-    public void removeImeSurface() {
-        mInner.removeImeSurface();
+    public void removeImeSurface(int displayId) {
+        mInner.removeImeSurface(displayId);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/locales/LocaleManagerService.java b/services/core/java/com/android/server/locales/LocaleManagerService.java
index 3d0b079..741513c 100644
--- a/services/core/java/com/android/server/locales/LocaleManagerService.java
+++ b/services/core/java/com/android/server/locales/LocaleManagerService.java
@@ -616,9 +616,10 @@
             LocaleConfig resLocaleConfig = null;
             try {
                 resLocaleConfig = LocaleConfig.fromContextIgnoringOverride(
-                        mContext.createPackageContext(appPackageName, 0));
+                        mContext.createPackageContextAsUser(appPackageName, /* flags= */ 0,
+                                UserHandle.of(userId)));
             } catch (PackageManager.NameNotFoundException e) {
-                Slog.e(TAG, "Unknown package name " + appPackageName);
+                Slog.e(TAG, "Unknown package name " + appPackageName + " for user " + userId);
                 return;
             }
             final File file = getXmlFileNameForUser(appPackageName, userId);
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 7de1045..3f4a9bb 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubService.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
@@ -297,7 +297,10 @@
         }
 
         public boolean isExpired() {
-            return mTimestamp + ContextHubTransactionManager.RELIABLE_MESSAGE_TIMEOUT.toNanos()
+            return mTimestamp
+                            + ContextHubTransactionManager
+                                    .RELIABLE_MESSAGE_DUPLICATE_DETECTION_TIMEOUT
+                                    .toNanos()
                     < SystemClock.elapsedRealtimeNanos();
         }
     }
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 e6d330f8..2a0b1af 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManager.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManager.java
@@ -56,6 +56,9 @@
 
     public static final Duration RELIABLE_MESSAGE_TIMEOUT = Duration.ofSeconds(1);
 
+    public static final Duration RELIABLE_MESSAGE_DUPLICATE_DETECTION_TIMEOUT =
+            RELIABLE_MESSAGE_TIMEOUT.multipliedBy(3);
+
     private static final int MAX_PENDING_REQUESTS = 10000;
 
     private static final int RELIABLE_MESSAGE_MAX_NUM_RETRY = 3;
@@ -714,9 +717,13 @@
         }
         mReliableMessageHostEndpointIdActiveSet.remove(transaction.getHostEndpointId());
 
-        Log.d(TAG, "Successfully completed reliable message transaction with "
-                + "message sequence number: " + transaction.getMessageSequenceNumber()
-                + " and result: " + result);
+    Log.d(
+        TAG,
+        "Successfully completed reliable message transaction with "
+            + "message sequence number = "
+            + transaction.getMessageSequenceNumber()
+            + " and result = "
+            + result);
     }
 
     /**
@@ -729,15 +736,20 @@
         int numCompletedStartCalls = transaction.getNumCompletedStartCalls();
         @ContextHubTransaction.Result int result = transaction.onTransact();
         if (result == ContextHubTransaction.RESULT_SUCCESS) {
-            Log.d(TAG, "Successfully "
-                    + (numCompletedStartCalls == 0 ? "started" : "retried")
-                    + " reliable message transaction with message sequence number: "
-                    + transaction.getMessageSequenceNumber());
+      Log.d(
+          TAG,
+          "Successfully "
+              + (numCompletedStartCalls == 0 ? "started" : "retried")
+              + " reliable message transaction with message sequence number = "
+              + transaction.getMessageSequenceNumber());
         } else {
-            Log.w(TAG, "Could not start reliable message transaction with "
-                    + "message sequence number: "
-                    + transaction.getMessageSequenceNumber()
-                    + ", result: " + result);
+      Log.w(
+          TAG,
+          "Could not start reliable message transaction with "
+              + "message sequence number = "
+              + transaction.getMessageSequenceNumber()
+              + ", result = "
+              + result);
         }
 
         transaction.setNextRetryTime(now + RELIABLE_MESSAGE_RETRY_WAIT_TIME.toNanos());
diff --git a/services/core/java/com/android/server/location/injector/SystemEmergencyHelper.java b/services/core/java/com/android/server/location/injector/SystemEmergencyHelper.java
index 5df0de8..df45a6e 100644
--- a/services/core/java/com/android/server/location/injector/SystemEmergencyHelper.java
+++ b/services/core/java/com/android/server/location/injector/SystemEmergencyHelper.java
@@ -77,7 +77,7 @@
                         mIsInEmergencyCall = mTelephonyManager.isEmergencyNumber(
                                 intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER));
                         dispatchEmergencyStateChanged();
-                    } catch (IllegalStateException e) {
+                    } catch (IllegalStateException | UnsupportedOperationException e) {
                         Log.w(TAG, "Failed to call TelephonyManager.isEmergencyNumber().", e);
                     }
                 }
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index d9f3622..1070f2f 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -292,7 +292,7 @@
                         == PackageManager.PERMISSION_GRANTED;
         final boolean hasModifyAudioRoutingPermission =
                 checkCallerHasModifyAudioRoutingPermission(pid, uid);
-
+        boolean hasMediaContentControlPermission = checkMediaContentControlPermission(uid, pid);
         boolean hasMediaRoutingControlPermission =
                 checkMediaRoutingControlPermission(uid, pid, packageName);
 
@@ -307,6 +307,7 @@
                         userId,
                         hasConfigureWifiDisplayPermission,
                         hasModifyAudioRoutingPermission,
+                        hasMediaContentControlPermission,
                         hasMediaRoutingControlPermission);
             }
         } finally {
@@ -327,6 +328,12 @@
         }
     }
 
+    @RequiresPermission(
+            anyOf = {
+                Manifest.permission.MEDIA_ROUTING_CONTROL,
+                Manifest.permission.MEDIA_CONTENT_CONTROL
+            },
+            conditional = true)
     public void updateScanningState(
             @NonNull IMediaRouter2 router, @ScanningState int scanningState) {
         Objects.requireNonNull(router, "router must not be null");
@@ -1133,6 +1140,7 @@
             int userId,
             boolean hasConfigureWifiDisplayPermission,
             boolean hasModifyAudioRoutingPermission,
+            boolean hasMediaContentControlPermission,
             boolean hasMediaRoutingControlPermission) {
         final IBinder binder = router.asBinder();
         if (mAllRouterRecords.get(binder) != null) {
@@ -1151,6 +1159,7 @@
                         packageName,
                         hasConfigureWifiDisplayPermission,
                         hasModifyAudioRoutingPermission,
+                        hasMediaContentControlPermission,
                         hasMediaRoutingControlPermission);
         try {
             binder.linkToDeath(routerRecord, 0);
@@ -1213,6 +1222,12 @@
         disposeUserIfNeededLocked(userRecord); // since router removed from user
     }
 
+    @RequiresPermission(
+            anyOf = {
+                Manifest.permission.MEDIA_ROUTING_CONTROL,
+                Manifest.permission.MEDIA_CONTENT_CONTROL
+            },
+            conditional = true)
     @GuardedBy("mLock")
     private void updateScanningStateLocked(
             @NonNull IMediaRouter2 router, @ScanningState int scanningState) {
@@ -1223,7 +1238,11 @@
             return;
         }
 
+        boolean enableScanViaMediaContentControl =
+                Flags.enableFullScanWithMediaContentControl()
+                        && routerRecord.mHasMediaContentControlPermission;
         if (scanningState == SCANNING_STATE_SCANNING_FULL
+                && !enableScanViaMediaContentControl
                 && !routerRecord.mHasMediaRoutingControl) {
             throw new SecurityException("Screen off scan requires MEDIA_ROUTING_CONTROL");
         }
@@ -1676,7 +1695,11 @@
             return;
         }
 
+        boolean enableScanViaMediaContentControl =
+                Flags.enableFullScanWithMediaContentControl()
+                        && managerRecord.mHasMediaContentControl;
         if (!managerRecord.mHasMediaRoutingControl
+                && !enableScanViaMediaContentControl
                 && scanningState == SCANNING_STATE_SCANNING_FULL) {
             throw new SecurityException("Screen off scan requires MEDIA_ROUTING_CONTROL");
         }
@@ -2067,9 +2090,10 @@
         public final int mPid;
         public final boolean mHasConfigureWifiDisplayPermission;
         public final boolean mHasModifyAudioRoutingPermission;
+        public final boolean mHasMediaContentControlPermission;
+        public final boolean mHasMediaRoutingControl;
         public final AtomicBoolean mHasBluetoothRoutingPermission;
         public final int mRouterId;
-        public final boolean mHasMediaRoutingControl;
         public @ScanningState int mScanningState = SCANNING_STATE_NOT_SCANNING;
 
         public RouteDiscoveryPreference mDiscoveryPreference;
@@ -2083,6 +2107,7 @@
                 String packageName,
                 boolean hasConfigureWifiDisplayPermission,
                 boolean hasModifyAudioRoutingPermission,
+                boolean hasMediaContentControlPermission,
                 boolean hasMediaRoutingControl) {
             mUserRecord = userRecord;
             mPackageName = packageName;
@@ -2093,9 +2118,10 @@
             mPid = pid;
             mHasConfigureWifiDisplayPermission = hasConfigureWifiDisplayPermission;
             mHasModifyAudioRoutingPermission = hasModifyAudioRoutingPermission;
+            mHasMediaContentControlPermission = hasMediaContentControlPermission;
+            mHasMediaRoutingControl = hasMediaRoutingControl;
             mHasBluetoothRoutingPermission =
                     new AtomicBoolean(checkCallerHasBluetoothPermissions(mPid, mUid));
-            mHasMediaRoutingControl = hasMediaRoutingControl;
             mRouterId = mNextRouterOrManagerId.getAndIncrement();
         }
 
diff --git a/services/core/java/com/android/server/media/MediaRouterService.java b/services/core/java/com/android/server/media/MediaRouterService.java
index 1188a07..363b8e4 100644
--- a/services/core/java/com/android/server/media/MediaRouterService.java
+++ b/services/core/java/com/android/server/media/MediaRouterService.java
@@ -443,6 +443,12 @@
 
     // Binder call
     @Override
+    @RequiresPermission(
+            anyOf = {
+                Manifest.permission.MEDIA_ROUTING_CONTROL,
+                Manifest.permission.MEDIA_CONTENT_CONTROL
+            },
+            conditional = true)
     public void updateScanningStateWithRouter2(
             IMediaRouter2 router, @ScanningState int scanningState) {
         mService2.updateScanningState(router, scanningState);
diff --git a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
index 803b125..621c090 100644
--- a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
+++ b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
@@ -702,7 +702,7 @@
         }
     }
 
-    private final class BinderService extends IMediaProjectionManager.Stub {
+    final class BinderService extends IMediaProjectionManager.Stub {
 
         BinderService(Context context) {
             super(PermissionEnforcer.fromContext(context));
@@ -891,6 +891,13 @@
         @Override
         public void requestConsentForInvalidProjection(@NonNull IMediaProjection projection) {
             requestConsentForInvalidProjection_enforcePermission();
+
+            if (android.companion.virtualdevice.flags.Flags.mediaProjectionKeyguardRestrictions()
+                    && mKeyguardManager.isKeyguardLocked()) {
+                Slog.v(TAG, "Reusing token: Won't request consent while the keyguard is locked");
+                return;
+            }
+
             synchronized (mLock) {
                 if (!isCurrentProjection(projection)) {
                     Slog.v(TAG, "Reusing token: Won't request consent again for a token that "
diff --git a/services/core/java/com/android/server/net/Android.bp b/services/core/java/com/android/server/net/Android.bp
index 3ac2d23..68dc781 100644
--- a/services/core/java/com/android/server/net/Android.bp
+++ b/services/core/java/com/android/server/net/Android.bp
@@ -9,3 +9,10 @@
     name: "net_flags_lib",
     aconfig_declarations: "net_flags",
 }
+
+java_aconfig_library {
+    name: "net_flags_host_lib",
+    aconfig_declarations: "net_flags",
+    host_supported: true,
+    mode: "test",
+}
diff --git a/services/core/java/com/android/server/net/watchlist/OWNERS b/services/core/java/com/android/server/net/watchlist/OWNERS
index d0c4e55..eef1e46 100644
--- a/services/core/java/com/android/server/net/watchlist/OWNERS
+++ b/services/core/java/com/android/server/net/watchlist/OWNERS
@@ -1,2 +1 @@
-alanstokes@google.com
 simonjw@google.com
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index 1a8e44b..1fdb57c 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -950,7 +950,7 @@
                 || isPackageOrComponentAllowed(component.getPackageName(), userId))) {
             return false;
         }
-        return componentHasBindPermission(component, userId);
+        return isValidService(component, userId);
     }
 
     private boolean componentHasBindPermission(ComponentName component, int userId) {
@@ -1302,11 +1302,12 @@
                     if (TextUtils.equals(getPackageName(approvedPackageOrComponent), packageName)) {
                         final ComponentName component = ComponentName.unflattenFromString(
                                 approvedPackageOrComponent);
-                        if (component != null && !componentHasBindPermission(component, userId)) {
+                        if (component != null && !isValidService(component, userId)) {
                             approved.removeAt(j);
                             if (DEBUG) {
                                 Slog.v(TAG, "Removing " + approvedPackageOrComponent
-                                        + " from approved list; no bind permission found "
+                                        + " from approved list; no bind permission or "
+                                        + "service interface filter found "
                                         + mConfig.bindPermission);
                             }
                         }
@@ -1325,6 +1326,11 @@
         }
     }
 
+    protected boolean isValidService(ComponentName component, int userId) {
+        return componentHasBindPermission(component, userId) && queryPackageForServices(
+                component.getPackageName(), userId).contains(component);
+    }
+
     protected boolean isValidEntry(String packageOrComponent, int userId) {
         return hasMatchingServices(packageOrComponent, userId);
     }
diff --git a/services/core/java/com/android/server/notification/NotificationAttentionHelper.java b/services/core/java/com/android/server/notification/NotificationAttentionHelper.java
index 5a9cf03..bd551fb 100644
--- a/services/core/java/com/android/server/notification/NotificationAttentionHelper.java
+++ b/services/core/java/com/android/server/notification/NotificationAttentionHelper.java
@@ -230,9 +230,17 @@
                     mFlagResolver.getIntValue(NotificationFlags.NOTIF_VOLUME1),
                     mFlagResolver.getIntValue(NotificationFlags.NOTIF_VOLUME2),
                     mFlagResolver.getIntValue(NotificationFlags.NOTIF_COOLDOWN_COUNTER_RESET),
-                    record -> mPackageManager.checkPermission(
+                    record -> {
+                        final String category = record.getNotification().category;
+                        if (Notification.CATEGORY_ALARM.equals(category)
+                                || Notification.CATEGORY_CAR_EMERGENCY.equals(category)
+                                || Notification.CATEGORY_CAR_WARNING.equals(category)) {
+                            return true;
+                        }
+                        return mPackageManager.checkPermission(
                             permission.RECEIVE_EMERGENCY_BROADCAST,
-                            record.getSbn().getPackageName()) == PERMISSION_GRANTED);
+                            record.getSbn().getPackageName()) == PERMISSION_GRANTED;
+                    });
 
             return new StrategyAvalanche(
                     mFlagResolver.getIntValue(NotificationFlags.NOTIF_COOLDOWN_T1),
@@ -248,9 +256,17 @@
                     mFlagResolver.getIntValue(NotificationFlags.NOTIF_VOLUME1),
                     mFlagResolver.getIntValue(NotificationFlags.NOTIF_VOLUME2),
                     mFlagResolver.getIntValue(NotificationFlags.NOTIF_COOLDOWN_COUNTER_RESET),
-                    record -> mPackageManager.checkPermission(
+                    record -> {
+                        final String category = record.getNotification().category;
+                        if (Notification.CATEGORY_ALARM.equals(category)
+                                || Notification.CATEGORY_CAR_EMERGENCY.equals(category)
+                                || Notification.CATEGORY_CAR_WARNING.equals(category)) {
+                            return true;
+                        }
+                        return mPackageManager.checkPermission(
                             permission.RECEIVE_EMERGENCY_BROADCAST,
-                            record.getSbn().getPackageName()) == PERMISSION_GRANTED);
+                            record.getSbn().getPackageName()) == PERMISSION_GRANTED;
+                    });
         }
     }
 
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 016abff..4179edd 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -1315,10 +1315,24 @@
                         nv.rank, nv.count);
 
                 StatusBarNotification sbn = r.getSbn();
-                cancelNotification(callingUid, callingPid, sbn.getPackageName(), sbn.getTag(),
-                        sbn.getId(), FLAG_AUTO_CANCEL,
-                        FLAG_FOREGROUND_SERVICE | FLAG_USER_INITIATED_JOB | FLAG_BUBBLE,
-                        false, r.getUserId(), REASON_CLICK, nv.rank, nv.count, null);
+                // Notifications should be cancelled on click if they have been lifetime extended,
+                // regardless of presence or absence of FLAG_AUTO_CANCEL.
+                if (lifetimeExtensionRefactor()
+                        && (sbn.getNotification().flags
+                        & FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY) != 0) {
+                    cancelNotification(callingUid, callingPid, sbn.getPackageName(), sbn.getTag(),
+                            sbn.getId(), FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY,
+                            FLAG_FOREGROUND_SERVICE | FLAG_USER_INITIATED_JOB
+                                    | FLAG_BUBBLE,
+                            false, r.getUserId(), REASON_CLICK, nv.rank, nv.count, null);
+
+                } else {
+                    // Otherwise, only FLAG_AUTO_CANCEL notifications should be canceled on click.
+                    cancelNotification(callingUid, callingPid, sbn.getPackageName(), sbn.getTag(),
+                            sbn.getId(), FLAG_AUTO_CANCEL,
+                            FLAG_FOREGROUND_SERVICE | FLAG_USER_INITIATED_JOB | FLAG_BUBBLE,
+                            false, r.getUserId(), REASON_CLICK, nv.rank, nv.count, null);
+                }
                 nv.recycle();
                 reportUserInteraction(r);
                 mAssistants.notifyAssistantNotificationClicked(r);
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index b12a917..95d8bb9 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -2811,8 +2811,9 @@
     private final class H extends Handler {
         private static final int MSG_DISPATCH = 1;
         private static final int MSG_METRICS = 2;
-        private static final int MSG_RINGER_AUDIO = 5;
         private static final int MSG_APPLY_EFFECTS = 6;
+        private static final int MSG_AUDIO_APPLIED_TO_RINGER = 7;
+        private static final int MSG_AUDIO_NOT_APPLIED_TO_RINGER = 8;
 
         private static final long METRICS_PERIOD_MS = 6 * 60 * 60 * 1000;
 
@@ -2831,8 +2832,13 @@
         }
 
         private void postUpdateRingerAndAudio(boolean shouldApplyToRinger) {
-            removeMessages(MSG_RINGER_AUDIO);
-            sendMessage(obtainMessage(MSG_RINGER_AUDIO, shouldApplyToRinger));
+            if (shouldApplyToRinger) {
+                removeMessages(MSG_AUDIO_APPLIED_TO_RINGER);
+                sendEmptyMessage(MSG_AUDIO_APPLIED_TO_RINGER);
+            } else {
+                removeMessages(MSG_AUDIO_NOT_APPLIED_TO_RINGER);
+                sendEmptyMessage(MSG_AUDIO_NOT_APPLIED_TO_RINGER);
+            }
         }
 
         private void postApplyDeviceEffects(@ConfigChangeOrigin int origin) {
@@ -2849,9 +2855,11 @@
                 case MSG_METRICS:
                     mMetrics.emit();
                     break;
-                case MSG_RINGER_AUDIO:
-                    boolean shouldApplyToRinger = (boolean) msg.obj;
-                    updateRingerAndAudio(shouldApplyToRinger);
+                case MSG_AUDIO_APPLIED_TO_RINGER:
+                    updateRingerAndAudio(/* shouldApplyToRinger= */ true);
+                    break;
+                case MSG_AUDIO_NOT_APPLIED_TO_RINGER:
+                    updateRingerAndAudio(/* shouldApplyToRinger= */ false);
                     break;
                 case MSG_APPLY_EFFECTS:
                     @ConfigChangeOrigin int origin = msg.arg1;
diff --git a/services/core/java/com/android/server/pm/BackgroundUserSoundNotifier.java b/services/core/java/com/android/server/pm/BackgroundUserSoundNotifier.java
index 15e758c..e3061a7 100644
--- a/services/core/java/com/android/server/pm/BackgroundUserSoundNotifier.java
+++ b/services/core/java/com/android/server/pm/BackgroundUserSoundNotifier.java
@@ -233,19 +233,25 @@
         final Notification.Action switchUser = new Notification.Action.Builder(null,
                 fgContext.getString(R.string.bg_user_sound_notification_button_switch_user),
                 switchIntent).build();
-        return new Notification.Builder(mSystemUserContext, BUSN_CHANNEL_ID)
+        Notification.Builder notificationBuilder = new Notification.Builder(mSystemUserContext,
+                BUSN_CHANNEL_ID)
                 .setSmallIcon(icon)
                 .setTicker(title)
+                .setCategory(Notification.CATEGORY_REMINDER)
                 .setWhen(0)
                 .setOngoing(true)
                 .setColor(fgContext.getColor(R.color.system_notification_accent_color))
                 .setContentTitle(title)
                 .setContentIntent(muteIntent)
                 .setAutoCancel(true)
-                .setActions(mute, switchUser)
-                .setContentText(fgContext.getString(R.string.bg_user_sound_notification_message))
-                .setVisibility(Notification.VISIBILITY_PUBLIC)
-                .build();
+                .setVisibility(Notification.VISIBILITY_PUBLIC);
+        if (mUserManager.isUserSwitcherEnabled() && (mUserManager.getUserSwitchability(
+                UserHandle.of(fgContext.getUserId())) == UserManager.SWITCHABILITY_STATUS_OK)) {
+            notificationBuilder.setActions(mute, switchUser);
+        } else {
+            notificationBuilder.setActions(mute);
+        }
+        return notificationBuilder.build();
     }
 }
 
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 8d3f07e..a0d5ea8 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -501,9 +501,9 @@
             mPm.setUpCustomResolverActivity(pkg, pkgSetting);
         }
 
-        // When upgrading a package, pkgSetting is copied from oldPkgSetting. Clear the app
-        // metadata file path for the new package.
-        if (oldPkgSetting != null) {
+        // When upgrading a package, clear the app metadata file path for the new package.
+        if (oldPkgSetting != null
+                && oldPkgSetting.getLastUpdateTime() < pkgSetting.getLastUpdateTime()) {
             pkgSetting.setAppMetadataFilePath(null);
             pkgSetting.setAppMetadataSource(APP_METADATA_SOURCE_UNKNOWN);
         }
@@ -3264,6 +3264,7 @@
     /**
      * Tries to restore the disabled system package after an update has been deleted.
      */
+    @GuardedBy("mPm.mInstallLock")
     public void restoreDisabledSystemPackageLIF(DeletePackageAction action,
             @NonNull int[] allUserHandles, boolean writeSettings) throws SystemDeleteException {
         final PackageSetting deletedPs = action.mDeletingPs;
@@ -3282,10 +3283,21 @@
         }
         // Install the system package
         if (DEBUG_REMOVE) Slog.d(TAG, "Re-installing system package: " + disabledPs);
-        try (PackageManagerTracedLock installLock = mPm.mInstallLock.acquireLock()) {
+        try {
             final int[] origUsers = outInfo == null ? null : outInfo.mOrigUsers;
-            installPackageFromSystemLIF(disabledPs.getPathString(), allUserHandles,
-                    origUsers, writeSettings);
+            try (PackageManagerTracedLock installLock = mPm.mInstallLock.acquireLock()) {
+                installPackageFromSystemLIF(disabledPs.getPathString(), allUserHandles,
+                        origUsers, writeSettings);
+            }
+            if (origUsers != null) {
+                mPm.commitPackageStateMutation(null, mutator -> {
+                    for (int userId : origUsers) {
+                        mutator.forPackage(disabledPs.getPackageName())
+                                .userState(userId)
+                                .setOverlayPaths(deletedPs.getOverlayPaths(userId));
+                    }
+                });
+            }
         } catch (PackageManagerException e) {
             Slog.w(TAG, "Failed to restore system package:" + deletedPs.getPackageName() + ": "
                     + e.getMessage());
@@ -3813,13 +3825,13 @@
                 // This also has the (beneficial) side effect where if a package disappears from an
                 // APEX, leaving only a /data copy, it will lose its apexModuleName.
                 //
-                // This must be done before scanSystemPackageLI as that will throw in the case of a
+                // This must be done before scanPackageForInitLI as that will throw in the case of a
                 // system -> data package.
                 disabledPkgSetting.setApexModuleName(activeApexInfo.apexModuleName);
             }
         }
 
-        final Pair<ScanResult, Boolean> scanResultPair = scanSystemPackageLI(
+        final Pair<ScanResult, Boolean> scanResultPair = scanPackageForInitLI(
                 parsedPackage, parseFlags, scanFlags, user);
         final ScanResult scanResult = scanResultPair.first;
         boolean shouldHideSystemApp = scanResultPair.second;
@@ -4054,7 +4066,7 @@
         }
     }
 
-    private Pair<ScanResult, Boolean> scanSystemPackageLI(ParsedPackage parsedPackage,
+    private Pair<ScanResult, Boolean> scanPackageForInitLI(ParsedPackage parsedPackage,
             @ParsingPackageUtils.ParseFlags int parseFlags,
             @PackageManagerService.ScanFlags int scanFlags,
             @Nullable UserHandle user) throws PackageManagerException {
@@ -4167,7 +4179,7 @@
                         ParsingPackageUtils.getSigningDetails(input, parsedPackage,
                                 false /*skipVerify*/);
                 if (result.isError()) {
-                    throw new PrepareFailure("Failed collect during scanSystemPackageLI",
+                    throw new PrepareFailure("Failed collect during scanPackageForInitLI",
                             result.getException());
                 }
                 disabledPkgSetting.setSigningDetails(result.getResult());
diff --git a/services/core/java/com/android/server/pm/PackageArchiver.java b/services/core/java/com/android/server/pm/PackageArchiver.java
index 6b7b702..5e45b4c 100644
--- a/services/core/java/com/android/server/pm/PackageArchiver.java
+++ b/services/core/java/com/android/server/pm/PackageArchiver.java
@@ -869,18 +869,25 @@
     private int createDraftSession(String packageName, String installerPackage,
             String callerPackageName,
             IntentSender statusReceiver, int userId) throws IOException {
+        Computer snapshot = mPm.snapshotComputer();
         PackageInstaller.SessionParams sessionParams = new PackageInstaller.SessionParams(
                 PackageInstaller.SessionParams.MODE_FULL_INSTALL);
         sessionParams.setAppPackageName(packageName);
         sessionParams.setAppLabel(
                 mContext.getString(com.android.internal.R.string.unarchival_session_app_label));
-        sessionParams.setAppIcon(
-                getArchivedAppIcon(packageName, UserHandle.of(userId), callerPackageName));
+        // The draft session's app icon is based on the current launcher's icon overlay appops mode
+        String launcherPackageName = getCurrentLauncherPackageName(userId);
+        int launcherUid = launcherPackageName != null
+                ? snapshot.getPackageUid(launcherPackageName, 0, userId)
+                : Process.SYSTEM_UID;
+        sessionParams.setAppIcon(getArchivedAppIcon(packageName, UserHandle.of(userId),
+                isOverlayEnabled(launcherUid,
+                        launcherPackageName == null ? callerPackageName : launcherPackageName)));
         // To make sure SessionInfo::isUnarchival returns true for draft sessions,
         // INSTALL_UNARCHIVE is also set.
         sessionParams.installFlags = (INSTALL_UNARCHIVE_DRAFT | INSTALL_UNARCHIVE);
 
-        int installerUid = mPm.snapshotComputer().getPackageUid(installerPackage, 0, userId);
+        int installerUid = snapshot.getPackageUid(installerPackage, 0, userId);
         // Handles case of repeated unarchival calls for the same package.
         int existingSessionId = mPm.mInstallerService.getExistingDraftSessionId(installerUid,
                 sessionParams,
@@ -926,12 +933,27 @@
     /**
      * Returns the icon of an archived app. This is the icon of the main activity of the app.
      *
-     * <p> The icon is returned without any treatment/overlay. In the rare case the app had multiple
-     * launcher activities, only one of the icons is returned arbitrarily.
+     * <p> In the rare case the app had multiple launcher activities, only one of the icons is
+     * returned arbitrarily.
+     *
+     * <p> By default, the icon will be overlay'd with a cloud icon on top. A launcher app can
+     * disable the cloud overlay via the
+     * {@link LauncherApps.ArchiveCompatibilityParams#setEnableIconOverlay(boolean)} API.
+     * The default launcher's cloud overlay mode determines the cloud overlay status returned by
+     * any other callers. That is, if the current launcher has the cloud overlay disabled, any other
+     * app that fetches the app icon will also get an icon that has the cloud overlay disabled.
+     * This is to prevent style mismatch caused by icons that are fetched by different callers.
      */
     @Nullable
     public Bitmap getArchivedAppIcon(@NonNull String packageName, @NonNull UserHandle user,
             String callingPackageName) {
+        return getArchivedAppIcon(packageName, user,
+                isOverlayEnabled(Binder.getCallingUid(), callingPackageName));
+    }
+
+    @Nullable
+    private Bitmap getArchivedAppIcon(@NonNull String packageName, @NonNull UserHandle user,
+            boolean isOverlayEnabled) {
         Objects.requireNonNull(packageName);
         Objects.requireNonNull(user);
 
@@ -955,14 +977,19 @@
         // In the rare case the archived app defined more than two launcher activities, we choose
         // the first one arbitrarily.
         Bitmap icon = decodeIcon(archiveState.getActivityInfos().get(0));
-        if (icon != null && getAppOpsManager().checkOp(
-                AppOpsManager.OP_ARCHIVE_ICON_OVERLAY, callingUid, callingPackageName)
-                == MODE_ALLOWED) {
+
+        if (icon != null && isOverlayEnabled) {
             icon = includeCloudOverlay(icon);
         }
         return icon;
     }
 
+    private boolean isOverlayEnabled(int callingUid, String packageName) {
+        return getAppOpsManager().checkOp(
+                AppOpsManager.OP_ARCHIVE_ICON_OVERLAY, callingUid, packageName)
+                == MODE_ALLOWED;
+    }
+
     /**
      * This method first checks the ArchiveState for the provided userId and then tries to fallback
      * to other users if the current user is not archived.
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index a1dffc6..9757582 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -1452,6 +1452,13 @@
             }
 
             if (!ArrayUtils.isEmpty(after.splitNames)) {
+                if (beforeSplitNames.length != beforeSplitRevisionCodes.length) {
+                    throw new PackageManagerException(INSTALL_FAILED_VERSION_DOWNGRADE,
+                            "Current split names and the split revision codes are not 1:1 mapping."
+                                    + "This indicates that the package info data has been"
+                                    + " corrupted.");
+                }
+
                 for (int i = 0; i < after.splitNames.length; i++) {
                     final String splitName = after.splitNames[i];
                     final int j = ArrayUtils.indexOf(beforeSplitNames, splitName);
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 9177e2b..b7dfd8d 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -4489,10 +4489,24 @@
         String splitName = parser.getAttributeValue(null, ATTR_NAME);
         int splitRevision = parser.getAttributeInt(null, ATTR_VERSION, -1);
         if (splitName != null && splitRevision >= 0) {
+            final int beforeSplitNamesLength = outPs.getSplitNames().length;
+            // If the split name already exists in the outPs#getSplitNames, don't add it
+            // into the array and update its revision code below
             outPs.setSplitNames(ArrayUtils.appendElement(String.class,
                     outPs.getSplitNames(), splitName));
-            outPs.setSplitRevisionCodes(ArrayUtils.appendInt(
-                    outPs.getSplitRevisionCodes(), splitRevision));
+
+            // If the same split name has already been added before, update the latest
+            // revision code
+            final int afterSplitNamesLength = outPs.getSplitNames().length;
+            if (beforeSplitNamesLength == afterSplitNamesLength) {
+                final int index = ArrayUtils.indexOf(outPs.getSplitNames(), splitName);
+                final int[] splitRevisionCodes = outPs.getSplitRevisionCodes();
+                splitRevisionCodes[index] = splitRevision;
+                outPs.setSplitRevisionCodes(splitRevisionCodes);
+            } else {
+                outPs.setSplitRevisionCodes(ArrayUtils.appendInt(
+                        outPs.getSplitRevisionCodes(), splitRevision, /* allowDuplicates= */ true));
+            }
         }
 
         XmlUtils.skipCurrentTag(parser);
diff --git a/services/core/java/com/android/server/pm/TEST_MAPPING b/services/core/java/com/android/server/pm/TEST_MAPPING
index e12b70f..c40608d 100644
--- a/services/core/java/com/android/server/pm/TEST_MAPPING
+++ b/services/core/java/com/android/server/pm/TEST_MAPPING
@@ -160,6 +160,9 @@
           "exclude-annotation": "androidx.test.filters.FlakyTest"
         }
       ]
+    },
+    {
+      "name": "CtsUpdateOwnershipEnforcementTestCases"
     }
   ],
   "imports": [
diff --git a/services/core/java/com/android/server/pm/UserManagerInternal.java b/services/core/java/com/android/server/pm/UserManagerInternal.java
index 0e7ce2e..14b0fc8 100644
--- a/services/core/java/com/android/server/pm/UserManagerInternal.java
+++ b/services/core/java/com/android/server/pm/UserManagerInternal.java
@@ -497,6 +497,17 @@
     public abstract boolean isUserVisible(@UserIdInt int userId, int displayId);
 
     /**
+     * Checks if the given user is a visible background full user, which is a full background user
+     * assigned to secondary displays on the devices that have
+     * {@link UserManager#isVisibleBackgroundUsersEnabled()
+     * config_multiuserVisibleBackgroundUsers enabled} (for example, passenger users on
+     * automotive builds, using the display associated with their seats).
+     *
+     * @see UserManager#isUserVisible()
+     */
+    public abstract boolean isVisibleBackgroundFullUser(@UserIdInt int userId);
+
+    /**
      * Returns the main display id assigned to the user, or {@code Display.INVALID_DISPLAY} if the
      * user is not assigned to any main display.
      *
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index dde9943..c902fb2 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -7927,6 +7927,17 @@
         }
 
         @Override
+        public boolean isVisibleBackgroundFullUser(@UserIdInt int userId) {
+            if (!UserManager.isVisibleBackgroundUsersEnabled()) {
+                return false;
+            }
+            boolean isForeground = userId == getCurrentUserId();
+            boolean isProfile = isProfileUnchecked(userId);
+            boolean isVisible = isUserVisible(userId);
+            return isVisible && !isForeground && !isProfile;
+        }
+
+        @Override
         public int getMainDisplayAssignedToUser(@UserIdInt int userId) {
             return mUserVisibilityMediator.getMainDisplayAssignedToUser(userId);
         }
diff --git a/services/core/java/com/android/server/pm/UserVisibilityMediator.java b/services/core/java/com/android/server/pm/UserVisibilityMediator.java
index 613ebd3..46207c1 100644
--- a/services/core/java/com/android/server/pm/UserVisibilityMediator.java
+++ b/services/core/java/com/android/server/pm/UserVisibilityMediator.java
@@ -579,11 +579,11 @@
                         + " to user %d on start", userId, displayId, userAssignedToDisplay);
                 return false;
             }
-            // Then if was assigned extra
-            userAssignedToDisplay = mExtraDisplaysAssignedToUsers.get(userId, USER_NULL);
+            // Then if the display was assigned before
+            userAssignedToDisplay = mExtraDisplaysAssignedToUsers.get(displayId, USER_NULL);
             if (userAssignedToDisplay != USER_NULL) {
                 Slogf.w(TAG, "assignUserToExtraDisplay(%d, %d): failed because user %d was already "
-                        + "assigned that extra display", userId, displayId, userAssignedToDisplay);
+                        + "assigned to extra display", userId, displayId, userAssignedToDisplay);
                 return false;
             }
             if (DBG) {
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index 8be20b0..aaa38a3 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -149,6 +149,13 @@
         CONTACTS_PERMISSIONS.add(Manifest.permission.GET_ACCOUNTS);
     }
 
+    private static final Set<String> CALL_LOG_PERMISSIONS = new ArraySet<>();
+    static {
+        CALL_LOG_PERMISSIONS.add(Manifest.permission.READ_CALL_LOG);
+        CALL_LOG_PERMISSIONS.add(Manifest.permission.WRITE_CALL_LOG);
+    }
+
+
     private static final Set<String> ALWAYS_LOCATION_PERMISSIONS = new ArraySet<>();
     static {
         ALWAYS_LOCATION_PERMISSIONS.add(Manifest.permission.ACCESS_FINE_LOCATION);
@@ -753,7 +760,7 @@
         String contactsProviderPackage =
                 getDefaultProviderAuthorityPackage(ContactsContract.AUTHORITY, userId);
         grantSystemFixedPermissionsToSystemPackage(pm, contactsProviderPackage, userId,
-                CONTACTS_PERMISSIONS, PHONE_PERMISSIONS);
+                CONTACTS_PERMISSIONS, PHONE_PERMISSIONS, CALL_LOG_PERMISSIONS);
         grantPermissionsToSystemPackage(pm, contactsProviderPackage, userId, STORAGE_PERMISSIONS);
 
         // Device provisioning
diff --git a/services/core/java/com/android/server/policy/ModifierShortcutManager.java b/services/core/java/com/android/server/policy/ModifierShortcutManager.java
index fde23b7..cefecbc 100644
--- a/services/core/java/com/android/server/policy/ModifierShortcutManager.java
+++ b/services/core/java/com/android/server/policy/ModifierShortcutManager.java
@@ -16,6 +16,9 @@
 
 package com.android.server.policy;
 
+import static com.android.server.flags.Flags.modifierShortcutManagerMultiuser;
+
+import android.annotation.Nullable;
 import android.annotation.SuppressLint;
 import android.app.role.RoleManager;
 import android.content.ActivityNotFoundException;
@@ -31,6 +34,7 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.text.TextUtils;
+import android.util.IndentingPrintWriter;
 import android.util.Log;
 import android.util.LongSparseArray;
 import android.util.Slog;
@@ -52,6 +56,7 @@
 import org.xmlpull.v1.XmlPullParserException;
 
 import java.io.IOException;
+import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
@@ -81,6 +86,10 @@
     private final SparseArray<String> mRoleShortcuts = new SparseArray<String>();
     private final SparseArray<String> mShiftRoleShortcuts = new SparseArray<String>();
     private final Map<String, Intent> mRoleIntents = new HashMap<String, Intent>();
+    private final SparseArray<ComponentName> mComponentShortcuts = new SparseArray<>();
+    private final SparseArray<ComponentName> mShiftComponentShortcuts = new SparseArray<>();
+    private final Map<ComponentName, Intent> mComponentIntents =
+            new HashMap<ComponentName, Intent>();
 
     private LongSparseArray<IShortcutService> mShortcutKeyServices = new LongSparseArray<>();
 
@@ -114,23 +123,31 @@
 
     private final Context mContext;
     private final Handler mHandler;
-    private final RoleManager mRoleManager;
-    private final PackageManager mPackageManager;
     private boolean mSearchKeyShortcutPending = false;
     private boolean mConsumeSearchKeyUp = true;
+    private UserHandle mCurrentUser;
 
-    ModifierShortcutManager(Context context, Handler handler) {
+    ModifierShortcutManager(Context context, Handler handler, UserHandle currentUser) {
         mContext = context;
         mHandler = handler;
-        mPackageManager = mContext.getPackageManager();
-        mRoleManager = mContext.getSystemService(RoleManager.class);
-        mRoleManager.addOnRoleHoldersChangedListenerAsUser(mContext.getMainExecutor(),
+        RoleManager rm = mContext.getSystemService(RoleManager.class);
+        rm.addOnRoleHoldersChangedListenerAsUser(mContext.getMainExecutor(),
                 (String roleName, UserHandle user) -> {
                     mRoleIntents.remove(roleName);
                 }, UserHandle.ALL);
+        mCurrentUser = currentUser;
         loadShortcuts();
     }
 
+    void setCurrentUser(UserHandle newUser) {
+        mCurrentUser = newUser;
+
+        // Role based shortcuts may resolve to different apps for different users
+        // so clear the cache.
+        mRoleIntents.clear();
+        mComponentIntents.clear();
+    }
+
     /**
      * Gets the shortcut intent for a given keycode+modifier. Make sure you
      * strip whatever modifier is used for invoking shortcuts (for example,
@@ -146,9 +163,11 @@
      * to invoke the shortcut.
      * @return The intent that matches the shortcut, or null if not found.
      */
+    @Nullable
     private Intent getIntent(KeyCharacterMap kcm, int keyCode, int metaState) {
         // If a modifier key other than shift is also pressed, skip it.
-        final boolean isShiftOn = KeyEvent.metaStateHasModifiers(metaState, KeyEvent.META_SHIFT_ON);
+        final boolean isShiftOn = KeyEvent.metaStateHasModifiers(
+                metaState, KeyEvent.META_SHIFT_ON);
         if (!isShiftOn && !KeyEvent.metaStateHasNoModifiers(metaState)) {
             return null;
         }
@@ -160,37 +179,54 @@
 
         // First try the exact keycode (with modifiers).
         int shortcutChar = kcm.get(keyCode, metaState);
-        if (shortcutChar != 0) {
+        if (shortcutChar == 0) {
+            return null;
+        }
+        shortcutIntent = shortcutMap.get(shortcutChar);
+
+        if (shortcutIntent == null) {
+            // Next try the primary character on that key.
+            shortcutChar = Character.toLowerCase(kcm.getDisplayLabel(keyCode));
+            if (shortcutChar == 0) {
+                return null;
+            }
             shortcutIntent = shortcutMap.get(shortcutChar);
         }
 
-        // Next try the primary character on that key.
         if (shortcutIntent == null) {
-            shortcutChar = Character.toLowerCase(kcm.getDisplayLabel(keyCode));
-            if (shortcutChar != 0) {
-                shortcutIntent = shortcutMap.get(shortcutChar);
-
-                if (shortcutIntent == null) {
-                    // Check for role based shortcut
-                    String role = isShiftOn ? mShiftRoleShortcuts.get(shortcutChar)
-                            : mRoleShortcuts.get(shortcutChar);
-                    if (role != null) {
-                        shortcutIntent = getRoleLaunchIntent(role);
-                    }
-                }
+            // Next check for role based shortcut with primary character.
+            String role = isShiftOn ? mShiftRoleShortcuts.get(shortcutChar)
+                    : mRoleShortcuts.get(shortcutChar);
+            if (role != null) {
+                shortcutIntent = getRoleLaunchIntent(role);
             }
         }
 
+        if (modifierShortcutManagerMultiuser()) {
+            if (shortcutIntent == null) {
+                // Next check component based shortcuts with primary character.
+                ComponentName component = isShiftOn
+                        ? mShiftComponentShortcuts.get(shortcutChar)
+                        : mComponentShortcuts.get(shortcutChar);
+                if (component != null) {
+                    shortcutIntent = resolveComponentNameIntent(component);
+                }
+            }
+        }
         return shortcutIntent;
     }
 
     private Intent getRoleLaunchIntent(String role) {
         Intent intent = mRoleIntents.get(role);
         if (intent == null) {
-            if (mRoleManager.isRoleAvailable(role)) {
-                String rolePackage = mRoleManager.getDefaultApplication(role);
+            Context context = modifierShortcutManagerMultiuser()
+                    ? mContext.createContextAsUser(mCurrentUser, 0) : mContext;
+            RoleManager rm = context.getSystemService(RoleManager.class);
+            PackageManager pm = context.getPackageManager();
+            if (rm.isRoleAvailable(role)) {
+                String rolePackage = rm.getDefaultApplication(role);
                 if (rolePackage != null) {
-                    intent = mPackageManager.getLaunchIntentForPackage(rolePackage);
+                    intent = pm.getLaunchIntentForPackage(rolePackage);
                     if (intent != null) {
                         intent.putExtra(EXTRA_ROLE, role);
                         mRoleIntents.put(role, intent);
@@ -248,31 +284,17 @@
                                 + " className=" + className + " shortcutChar=" + shortcutChar);
                         continue;
                     }
-                    ComponentName componentName = new ComponentName(packageName, className);
-                    try {
-                        mPackageManager.getActivityInfo(componentName,
-                                PackageManager.MATCH_DIRECT_BOOT_AWARE
-                                        | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
-                                        | PackageManager.MATCH_UNINSTALLED_PACKAGES);
-                    } catch (PackageManager.NameNotFoundException e) {
-                        String[] packages = mPackageManager.canonicalToCurrentPackageNames(
-                                new String[] { packageName });
-                        componentName = new ComponentName(packages[0], className);
-                        try {
-                            mPackageManager.getActivityInfo(componentName,
-                                    PackageManager.MATCH_DIRECT_BOOT_AWARE
-                                            | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
-                                            | PackageManager.MATCH_UNINSTALLED_PACKAGES);
-                        } catch (PackageManager.NameNotFoundException e1) {
-                            Log.w(TAG, "Unable to add bookmark: " + packageName
-                                    + "/" + className + " not found.");
-                            continue;
+                    if (modifierShortcutManagerMultiuser()) {
+                        ComponentName componentName = new ComponentName(packageName, className);
+                        if (isShiftShortcut) {
+                            mShiftComponentShortcuts.put(shortcutChar, componentName);
+                        } else {
+                            mComponentShortcuts.put(shortcutChar, componentName);
                         }
+                        continue;
+                    } else {
+                        intent = resolveComponentNameIntent(packageName, className);
                     }
-
-                    intent = new Intent(Intent.ACTION_MAIN);
-                    intent.addCategory(Intent.CATEGORY_LAUNCHER);
-                    intent.setComponent(componentName);
                 } else if (categoryName != null) {
                     if (roleName != null) {
                         Log.w(TAG, "Cannot specify role bookmark when category is present for"
@@ -310,6 +332,50 @@
         }
     }
 
+    @Nullable
+    private Intent resolveComponentNameIntent(ComponentName componentName) {
+        Intent intent = mComponentIntents.get(componentName);
+        if (intent == null) {
+            intent = resolveComponentNameIntent(
+                    componentName.getPackageName(), componentName.getClassName());
+            if (intent != null) {
+                mComponentIntents.put(componentName, intent);
+            }
+        }
+        return intent;
+    }
+
+    @Nullable
+    private Intent resolveComponentNameIntent(String packageName, String className) {
+        Context context = modifierShortcutManagerMultiuser()
+                ? mContext.createContextAsUser(mCurrentUser, 0) : mContext;
+        PackageManager pm = context.getPackageManager();
+        int flags = PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
+        if (!modifierShortcutManagerMultiuser()) {
+            flags |= PackageManager.MATCH_DIRECT_BOOT_AWARE
+                    | PackageManager.MATCH_UNINSTALLED_PACKAGES;
+        }
+        ComponentName componentName = new ComponentName(packageName, className);
+        try {
+            pm.getActivityInfo(componentName, flags);
+        } catch (PackageManager.NameNotFoundException e) {
+            String[] packages = pm.canonicalToCurrentPackageNames(
+                    new String[] { packageName });
+            componentName = new ComponentName(packages[0], className);
+            try {
+                pm.getActivityInfo(componentName, flags);
+            } catch (PackageManager.NameNotFoundException e1) {
+                Log.w(TAG, "Unable to add bookmark: " + packageName
+                        + "/" + className + " not found.");
+                return null;
+            }
+        }
+        Intent intent = new Intent(Intent.ACTION_MAIN);
+        intent.addCategory(Intent.CATEGORY_LAUNCHER);
+        intent.setComponent(componentName);
+        return intent;
+    }
+
     void registerShortcutKey(long shortcutCode, IShortcutService shortcutService)
             throws RemoteException {
         IShortcutService service = mShortcutKeyServices.get(shortcutCode);
@@ -396,7 +462,11 @@
             if (intent != null) {
                 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                 try {
-                    mContext.startActivityAsUser(intent, UserHandle.CURRENT);
+                    if (modifierShortcutManagerMultiuser()) {
+                        mContext.startActivityAsUser(intent, mCurrentUser);
+                    } else {
+                        mContext.startActivityAsUser(intent, UserHandle.CURRENT);
+                    }
                 } catch (ActivityNotFoundException ex) {
                     Slog.w(TAG, "Dropping application launch key because "
                             + "the activity to which it is registered was not found: "
@@ -414,7 +484,11 @@
         if (shortcutIntent != null) {
             shortcutIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
             try {
-                mContext.startActivityAsUser(shortcutIntent, UserHandle.CURRENT);
+                if (modifierShortcutManagerMultiuser()) {
+                    mContext.startActivityAsUser(shortcutIntent, mCurrentUser);
+                } else {
+                    mContext.startActivityAsUser(shortcutIntent, UserHandle.CURRENT);
+                }
             } catch (ActivityNotFoundException ex) {
                 Slog.w(TAG, "Dropping shortcut key combination because "
                         + "the activity to which it is registered was not found: "
@@ -523,6 +597,30 @@
             }
         }
 
+        if (modifierShortcutManagerMultiuser()) {
+            for (int i = 0; i < mComponentShortcuts.size(); i++) {
+                ComponentName component = mComponentShortcuts.valueAt(i);
+                KeyboardShortcutInfo info = shortcutInfoFromIntent(
+                        (char) (mComponentShortcuts.keyAt(i)),
+                        resolveComponentNameIntent(component),
+                        false);
+                if (info != null) {
+                    shortcuts.add(info);
+                }
+            }
+
+            for (int i = 0; i < mShiftComponentShortcuts.size(); i++) {
+                ComponentName component = mShiftComponentShortcuts.valueAt(i);
+                KeyboardShortcutInfo info = shortcutInfoFromIntent(
+                        (char) (mShiftComponentShortcuts.keyAt(i)),
+                        resolveComponentNameIntent(component),
+                        true);
+                if (info != null) {
+                    shortcuts.add(info);
+                }
+            }
+        }
+
         return new KeyboardShortcutGroup(
                 mContext.getString(R.string.keyboard_shortcut_group_applications),
                 shortcuts);
@@ -545,23 +643,26 @@
 
         CharSequence label;
         Icon icon;
+        Context context = modifierShortcutManagerMultiuser()
+                ? mContext.createContextAsUser(mCurrentUser, 0) : mContext;
+        PackageManager pm = context.getPackageManager();
         ActivityInfo resolvedActivity = intent.resolveActivityInfo(
-                mPackageManager, PackageManager.MATCH_DEFAULT_ONLY);
+                pm, PackageManager.MATCH_DEFAULT_ONLY);
         if (resolvedActivity == null) {
             return null;
         }
         boolean isResolver = com.android.internal.app.ResolverActivity.class.getName().equals(
                 resolvedActivity.name);
         if (isResolver) {
-            label = getIntentCategoryLabel(mContext,
+            label = getIntentCategoryLabel(context,
                     intent.getSelector().getCategories().iterator().next());
             if (label == null) {
                 return null;
             }
-            icon = Icon.createWithResource(mContext, R.drawable.sym_def_app_icon);
+            icon = Icon.createWithResource(context, R.drawable.sym_def_app_icon);
 
         } else {
-            label = resolvedActivity.loadLabel(mPackageManager);
+            label = resolvedActivity.loadLabel(pm);
             icon = Icon.createWithResource(
                     resolvedActivity.packageName, resolvedActivity.getIconResource());
         }
@@ -607,4 +708,60 @@
         return context.getString(resid);
     };
 
+    void dump(String prefix, PrintWriter pw) {
+        IndentingPrintWriter ipw = new IndentingPrintWriter(pw,  "  ", prefix);
+        ipw.println("ModifierShortcutManager shortcuts:");
+
+        ipw.increaseIndent();
+        ipw.println("Roles");
+        ipw.increaseIndent();
+        for (int i = 0; i <  mRoleShortcuts.size(); i++) {
+            String role = mRoleShortcuts.valueAt(i);
+            char shortcutChar = (char) mRoleShortcuts.keyAt(i);
+            Intent intent = getRoleLaunchIntent(role);
+            ipw.println(shortcutChar + " " + role + " " + intent);
+        }
+
+        for (int i = 0; i <  mShiftRoleShortcuts.size(); i++) {
+            String role = mShiftRoleShortcuts.valueAt(i);
+            char shortcutChar = (char) mShiftRoleShortcuts.keyAt(i);
+            Intent intent = getRoleLaunchIntent(role);
+            ipw.println("SHIFT+" + shortcutChar + " " + role + " " + intent);
+        }
+
+        ipw.decreaseIndent();
+        ipw.println("Selectors");
+        ipw.increaseIndent();
+        for (int i = 0; i <  mIntentShortcuts.size(); i++) {
+            char shortcutChar = (char) mIntentShortcuts.keyAt(i);
+            Intent intent = mIntentShortcuts.valueAt(i);
+            ipw.println(shortcutChar + " " + intent);
+        }
+
+        for (int i = 0; i <  mShiftShortcuts.size(); i++) {
+            char shortcutChar = (char) mShiftShortcuts.keyAt(i);
+            Intent intent = mShiftShortcuts.valueAt(i);
+            ipw.println("SHIFT+" + shortcutChar + " " + intent);
+
+        }
+
+        if (modifierShortcutManagerMultiuser()) {
+            ipw.decreaseIndent();
+            ipw.println("ComponentNames");
+            ipw.increaseIndent();
+            for (int i = 0; i < mComponentShortcuts.size(); i++) {
+                char shortcutChar = (char) mComponentShortcuts.keyAt(i);
+                ComponentName component = mComponentShortcuts.valueAt(i);
+                Intent intent = resolveComponentNameIntent(component);
+                ipw.println(shortcutChar + " " + component + " " + intent);
+            }
+
+            for (int i = 0; i < mShiftComponentShortcuts.size(); i++) {
+                char shortcutChar = (char) mShiftComponentShortcuts.keyAt(i);
+                ComponentName component = mShiftComponentShortcuts.valueAt(i);
+                Intent intent = resolveComponentNameIntent(component);
+                ipw.println("SHIFT+" + shortcutChar + " " + component + " " + intent);
+            }
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index e89a412..21d6c64 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -76,6 +76,8 @@
 import static android.view.contentprotection.flags.Flags.createAccessibilityOverlayAppOpEnabled;
 
 import static com.android.hardware.input.Flags.emojiAndScreenshotKeycodesAvailable;
+import static com.android.hardware.input.Flags.modifierShortcutDump;
+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;
@@ -1157,8 +1159,7 @@
                     break;
                 case SHORT_PRESS_POWER_CLOSE_IME_OR_GO_HOME: {
                     if (mDismissImeOnBackKeyPressed) {
-                        // TODO(b/308479256): Check if hiding "all" IMEs is OK or not.
-                        InputMethodManagerInternal.get().hideAllInputMethods(
+                        InputMethodManagerInternal.get().hideInputMethod(
                                 SoftInputShowHideReason.HIDE_POWER_BUTTON_GO_HOME, displayId);
                     } else {
                         shortPressPowerGoHome();
@@ -2281,7 +2282,8 @@
         mWakeGestureListener = new MyWakeGestureListener(mContext, mHandler);
         mSettingsObserver = new SettingsObserver(mHandler);
         mSettingsObserver.observe();
-        mModifierShortcutManager = new ModifierShortcutManager(mContext, mHandler);
+        mModifierShortcutManager = new ModifierShortcutManager(
+                mContext, mHandler, UserHandle.of(mCurrentUserId));
         mUiMode = mContext.getResources().getInteger(
                 com.android.internal.R.integer.config_defaultUiModeType);
         mHomeIntent =  new Intent(Intent.ACTION_MAIN, null);
@@ -2402,7 +2404,7 @@
                     PowerManager.GO_TO_SLEEP_REASON_TIMEOUT);
         }
 
-        mWindowManagerInternal.registerAppTransitionListener(new AppTransitionListener() {
+        final var transitionListener = new AppTransitionListener(DEFAULT_DISPLAY) {
             @Override
             public int onAppTransitionStartingLocked(long statusBarAnimationStartTime,
                     long statusBarAnimationDuration) {
@@ -2436,7 +2438,8 @@
                     mLockAfterDreamingTransitionFinished = false;
                 }
             }
-        });
+        };
+        mWindowManagerInternal.registerAppTransitionListener(transitionListener);
 
         mKeyguardDrawnTimeout = mContext.getResources().getInteger(
                 com.android.internal.R.integer.config_keyguardDrawnTimeout);
@@ -6510,6 +6513,9 @@
         if (statusBar != null) {
             statusBar.setCurrentUser(newUserId);
         }
+        if (modifierShortcutManagerMultiuser()) {
+            mModifierShortcutManager.setCurrentUser(UserHandle.of(newUserId));
+        }
     }
 
     @Override
@@ -6663,6 +6669,9 @@
 
         pw.print(prefix); pw.println("Looper state:");
         mHandler.getLooper().dump(new PrintWriterPrinter(pw), prefix + "  ");
+        if (modifierShortcutDump()) {
+            mModifierShortcutManager.dump(prefix, pw);
+        }
     }
 
     private static String endcallBehaviorToString(int behavior) {
diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java
index aa5f5a24..b28da55b 100644
--- a/services/core/java/com/android/server/power/Notifier.java
+++ b/services/core/java/com/android/server/power/Notifier.java
@@ -375,9 +375,9 @@
             final boolean unimportantForLogging = newOwnerUid == Process.SYSTEM_UID
                     && (newFlags & PowerManager.UNIMPORTANT_FOR_LOGGING) != 0;
             try {
-                mBatteryStats.noteChangeWakelockFromSource(workSource, ownerPid, tag, historyTag,
-                        monitorType, newWorkSource, newOwnerPid, newTag, newHistoryTag,
-                        newMonitorType, unimportantForLogging);
+                notifyWakelockChanging(workSource, ownerPid, tag,
+                            historyTag, monitorType, newWorkSource, newOwnerPid, newTag,
+                            newHistoryTag, newMonitorType, unimportantForLogging);
             } catch (RemoteException ex) {
                 // Ignore
             }
@@ -1127,6 +1127,29 @@
         mWakeLockLog.onWakeLockReleased(tag, ownerUid, currentTime);
     }
 
+    @SuppressLint("AndroidFrameworkRequiresPermission")
+    private void notifyWakelockChanging(WorkSource workSource, int ownerPid, String tag,
+            String historyTag, int monitorType, WorkSource newWorkSource, int newOwnerPid,
+            String newTag, String newHistoryTag, int newMonitorType, boolean unimportantForLogging)
+            throws RemoteException {
+        if (!mFlags.improveWakelockLatency()) {
+            mBatteryStats.noteChangeWakelockFromSource(workSource, ownerPid, tag,
+                    historyTag, monitorType, newWorkSource, newOwnerPid, newTag,
+                    newHistoryTag, newMonitorType, unimportantForLogging);
+        } else {
+            mHandler.post(() -> {
+                try {
+                    mBatteryStats.noteChangeWakelockFromSource(workSource, ownerPid, tag,
+                            historyTag, monitorType, newWorkSource, newOwnerPid, newTag,
+                            newHistoryTag, newMonitorType, unimportantForLogging);
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Failed to notify the wakelock changing from source via "
+                            + "Notifier." + e.getLocalizedMessage());
+                }
+            });
+        }
+    }
+
     private final class NotifierHandler extends Handler {
 
         public NotifierHandler(Looper looper) {
diff --git a/services/core/java/com/android/server/power/PowerGroup.java b/services/core/java/com/android/server/power/PowerGroup.java
index 77bdc45..72594b3 100644
--- a/services/core/java/com/android/server/power/PowerGroup.java
+++ b/services/core/java/com/android/server/power/PowerGroup.java
@@ -430,8 +430,8 @@
         return mDisplayPowerRequest.policy;
     }
 
-    boolean updateLocked(float screenBrightnessOverride, boolean useProximitySensor,
-            boolean boostScreenBrightness, int dozeScreenState,
+    boolean updateLocked(float screenBrightnessOverride, CharSequence overrideTag,
+            boolean useProximitySensor, boolean boostScreenBrightness, int dozeScreenState,
             @Display.StateReason int dozeScreenStateReason,
             float dozeScreenBrightness, boolean overrideDrawWakeLock,
             PowerSaveState powerSaverState, boolean quiescent,
@@ -441,6 +441,7 @@
         mDisplayPowerRequest.policy = getDesiredScreenPolicyLocked(quiescent, dozeAfterScreenOff,
                 bootCompleted, screenBrightnessBoostInProgress, brightWhenDozing);
         mDisplayPowerRequest.screenBrightnessOverride = screenBrightnessOverride;
+        mDisplayPowerRequest.screenBrightnessOverrideTag = overrideTag;
         mDisplayPowerRequest.useProximitySensor = useProximitySensor;
         mDisplayPowerRequest.boostScreenBrightness = boostScreenBrightness;
 
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 6fe1ccd..699c9b5 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -34,6 +34,9 @@
 import static com.android.internal.util.LatencyTracker.ACTION_TURN_ON_SCREEN;
 import static com.android.server.deviceidle.Flags.disableWakelocksInLightIdle;
 import static com.android.server.display.DisplayDeviceConfig.INVALID_BRIGHTNESS_IN_CONFIG;
+import static com.android.server.display.brightness.BrightnessUtils.isValidBrightnessValue;
+import static com.android.server.power.ScreenTimeoutOverridePolicy.RELEASE_REASON_UNKNOWN;
+import static com.android.server.power.ScreenTimeoutOverridePolicy.RELEASE_REASON_WAKE_LOCK_DEATH;
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
@@ -629,6 +632,9 @@
     private float mScreenBrightnessOverrideFromWindowManager =
             PowerManager.BRIGHTNESS_INVALID_FLOAT;
 
+    // Tag identifying the window/activity that requested the brightness override.
+    private CharSequence mScreenBrightnessOverrideFromWmTag = null;
+
     // The window manager has determined the user to be inactive via other means.
     // Set this to false to disable.
     private boolean mUserInactiveOverrideFromWindowManager;
@@ -647,11 +653,16 @@
 
     private int mDozeScreenStateOverrideReasonFromDreamManager = Display.STATE_REASON_UNKNOWN;
 
-    // The screen brightness to use while dozing.
+    // The screen brightness between 1 and 255 to use while dozing.
     private int mDozeScreenBrightnessOverrideFromDreamManager = PowerManager.BRIGHTNESS_DEFAULT;
 
+    /**
+     * The screen brightness between {@link PowerManager#BRIGHTNESS_MIN} and
+     * {@link PowerManager.BRIGHTNESS_MAX} to use while dozing.
+     */
     private float mDozeScreenBrightnessOverrideFromDreamManagerFloat =
             PowerManager.BRIGHTNESS_INVALID_FLOAT;
+
     // Keep display state when dozing.
     private boolean mDrawWakeLockOverrideFromSidekick;
 
@@ -1816,7 +1827,7 @@
                 return;
             }
 
-            removeWakeLockLocked(wakeLock, index);
+            removeWakeLockDeathLocked(wakeLock, index);
         }
     }
 
@@ -1848,6 +1859,12 @@
     }
 
     @GuardedBy("mLock")
+    private void removeWakeLockDeathLocked(WakeLock wakeLock, int index) {
+        removeWakeLockNoUpdateLocked(wakeLock, index, RELEASE_REASON_WAKE_LOCK_DEATH);
+        updatePowerStateLocked();
+    }
+
+    @GuardedBy("mLock")
     private void applyWakeLockFlagsOnReleaseLocked(WakeLock wakeLock) {
         if ((wakeLock.mFlags & PowerManager.ON_AFTER_RELEASE) != 0
                 && isScreenLock(wakeLock)) {
@@ -2002,7 +2019,7 @@
 
     @GuardedBy("mLock")
     private void notifyWakeLockReleasedLocked(WakeLock wakeLock) {
-        notifyWakeLockReleasedLocked(wakeLock, ScreenTimeoutOverridePolicy.RELEASE_REASON_UNKNOWN);
+        notifyWakeLockReleasedLocked(wakeLock, RELEASE_REASON_UNKNOWN);
     }
 
     @GuardedBy("mLock")
@@ -3623,16 +3640,18 @@
 
                 // Determine appropriate screen brightness.
                 final float screenBrightnessOverride;
+                CharSequence overrideTag = null;
                 if (!mBootCompleted) {
                     // Keep the brightness steady during boot. This requires the
                     // bootloader brightness and the default brightness to be identical.
                     screenBrightnessOverride = mScreenBrightnessDefault;
                 } else if (isValidBrightness(mScreenBrightnessOverrideFromWindowManager)) {
                     screenBrightnessOverride = mScreenBrightnessOverrideFromWindowManager;
+                    overrideTag = mScreenBrightnessOverrideFromWmTag;
                 } else {
                     screenBrightnessOverride = PowerManager.BRIGHTNESS_INVALID_FLOAT;
                 }
-                boolean ready = powerGroup.updateLocked(screenBrightnessOverride,
+                boolean ready = powerGroup.updateLocked(screenBrightnessOverride, overrideTag,
                         shouldUseProximitySensorLocked(), shouldBoostScreenBrightness(),
                         mDozeScreenStateOverrideFromDreamManager,
                         mDozeScreenStateOverrideReasonFromDreamManager,
@@ -4417,11 +4436,13 @@
         }
     }
 
-    private void setScreenBrightnessOverrideFromWindowManagerInternal(float brightness) {
+    private void setScreenBrightnessOverrideFromWindowManagerInternal(
+            float brightness, CharSequence tag) {
         synchronized (mLock) {
             if (!BrightnessSynchronizer.floatEquals(mScreenBrightnessOverrideFromWindowManager,
                     brightness)) {
                 mScreenBrightnessOverrideFromWindowManager = brightness;
+                mScreenBrightnessOverrideFromWmTag = tag;
                 mDirty |= DIRTY_SETTINGS;
                 updatePowerStateLocked();
             }
@@ -4448,15 +4469,21 @@
     }
 
     private void setDozeOverrideFromDreamManagerInternal(
-            int screenState, @Display.StateReason int reason, int screenBrightness) {
+            int screenState, @Display.StateReason int reason, float screenBrightnessFloat,
+            int screenBrightnessInt) {
         synchronized (mLock) {
             if (mDozeScreenStateOverrideFromDreamManager != screenState
-                    || mDozeScreenBrightnessOverrideFromDreamManager != screenBrightness) {
+                    || mDozeScreenBrightnessOverrideFromDreamManager != screenBrightnessInt
+                    || !BrightnessSynchronizer.floatEquals(
+                    mDozeScreenBrightnessOverrideFromDreamManagerFloat,
+                    screenBrightnessFloat)) {
                 mDozeScreenStateOverrideFromDreamManager = screenState;
                 mDozeScreenStateOverrideReasonFromDreamManager = reason;
-                mDozeScreenBrightnessOverrideFromDreamManager = screenBrightness;
+                mDozeScreenBrightnessOverrideFromDreamManager = screenBrightnessInt;
                 mDozeScreenBrightnessOverrideFromDreamManagerFloat =
-                        BrightnessSynchronizer.brightnessIntToFloat(mDozeScreenBrightnessOverrideFromDreamManager);
+                        isValidBrightnessValue(screenBrightnessFloat)
+                                ? screenBrightnessFloat
+                                : BrightnessSynchronizer.brightnessIntToFloat(screenBrightnessInt);
                 mDirty |= DIRTY_SETTINGS;
                 updatePowerStateLocked();
             }
@@ -4760,6 +4787,8 @@
             pw.println("  mStayOnWhilePluggedInSetting=" + mStayOnWhilePluggedInSetting);
             pw.println("  mScreenBrightnessOverrideFromWindowManager="
                     + mScreenBrightnessOverrideFromWindowManager);
+            pw.println("  mScreenBrightnessOverrideFromWmTag="
+                    + mScreenBrightnessOverrideFromWmTag);
             pw.println("  mUserActivityTimeoutOverrideFromWindowManager="
                     + mUserActivityTimeoutOverrideFromWindowManager);
             pw.println("  mUserInactiveOverrideFromWindowManager="
@@ -7074,17 +7103,19 @@
     @VisibleForTesting
     final class LocalService extends PowerManagerInternal {
         @Override
-        public void setScreenBrightnessOverrideFromWindowManager(float screenBrightness) {
+        public void setScreenBrightnessOverrideFromWindowManager(
+                float screenBrightness, CharSequence tag) {
             if (screenBrightness < PowerManager.BRIGHTNESS_MIN
                     || screenBrightness > PowerManager.BRIGHTNESS_MAX) {
                 screenBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;
+                tag = null;
             }
-            setScreenBrightnessOverrideFromWindowManagerInternal(screenBrightness);
+            setScreenBrightnessOverrideFromWindowManagerInternal(screenBrightness, tag);
         }
 
         @Override
         public void setDozeOverrideFromDreamManager(
-                int screenState, int reason, int screenBrightness) {
+                int screenState, int reason, float screenBrightnessFloat, int screenBrightnessInt) {
             switch (screenState) {
                 case Display.STATE_UNKNOWN:
                 case Display.STATE_OFF:
@@ -7097,11 +7128,17 @@
                     screenState = Display.STATE_UNKNOWN;
                     break;
             }
-            if (screenBrightness < PowerManager.BRIGHTNESS_DEFAULT
-                    || screenBrightness > PowerManager.BRIGHTNESS_ON) {
-                screenBrightness = PowerManager.BRIGHTNESS_DEFAULT;
+            if (screenBrightnessInt < PowerManager.BRIGHTNESS_DEFAULT
+                    || screenBrightnessInt > PowerManager.BRIGHTNESS_ON) {
+                screenBrightnessInt = PowerManager.BRIGHTNESS_DEFAULT;
             }
-            setDozeOverrideFromDreamManagerInternal(screenState, reason, screenBrightness);
+            if (screenBrightnessFloat != PowerManager.BRIGHTNESS_OFF_FLOAT
+                    && (screenBrightnessFloat < PowerManager.BRIGHTNESS_MIN
+                    || screenBrightnessFloat > PowerManager.BRIGHTNESS_MAX)) {
+                screenBrightnessFloat = PowerManager.BRIGHTNESS_INVALID_FLOAT;
+            }
+            setDozeOverrideFromDreamManagerInternal(screenState, reason, screenBrightnessFloat,
+                    screenBrightnessInt);
         }
 
         @Override
diff --git a/services/core/java/com/android/server/power/ScreenTimeoutOverridePolicy.java b/services/core/java/com/android/server/power/ScreenTimeoutOverridePolicy.java
index dcb3c39..8e08ce9 100644
--- a/services/core/java/com/android/server/power/ScreenTimeoutOverridePolicy.java
+++ b/services/core/java/com/android/server/power/ScreenTimeoutOverridePolicy.java
@@ -83,6 +83,11 @@
     public static final int RELEASE_REASON_USER_ACTIVITY_ACCESSIBILITY = 7;
 
     /**
+     * Release reason code: Release because wakelock dies.
+     */
+    public static final int RELEASE_REASON_WAKE_LOCK_DEATH = 8;
+
+    /**
      * @hide
      */
     @IntDef(prefix = { "RELEASE_REASON_" }, value = {
@@ -93,7 +98,8 @@
             RELEASE_REASON_USER_ACTIVITY_OTHER,
             RELEASE_REASON_USER_ACTIVITY_BUTTON,
             RELEASE_REASON_USER_ACTIVITY_TOUCH,
-            RELEASE_REASON_USER_ACTIVITY_ACCESSIBILITY
+            RELEASE_REASON_USER_ACTIVITY_ACCESSIBILITY,
+            RELEASE_REASON_WAKE_LOCK_DEATH
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface ReleaseReason{}
diff --git a/services/core/java/com/android/server/power/WakefulnessSessionObserver.java b/services/core/java/com/android/server/power/WakefulnessSessionObserver.java
index 3546565..c6b2602 100644
--- a/services/core/java/com/android/server/power/WakefulnessSessionObserver.java
+++ b/services/core/java/com/android/server/power/WakefulnessSessionObserver.java
@@ -31,6 +31,7 @@
 import static com.android.server.power.ScreenTimeoutOverridePolicy.RELEASE_REASON_USER_ACTIVITY_BUTTON;
 import static com.android.server.power.ScreenTimeoutOverridePolicy.RELEASE_REASON_USER_ACTIVITY_OTHER;
 import static com.android.server.power.ScreenTimeoutOverridePolicy.RELEASE_REASON_USER_ACTIVITY_TOUCH;
+import static com.android.server.power.ScreenTimeoutOverridePolicy.RELEASE_REASON_WAKE_LOCK_DEATH;
 
 import android.annotation.IntDef;
 import android.app.ActivityManager;
@@ -574,10 +575,13 @@
                         case RELEASE_REASON_USER_ACTIVITY_ACCESSIBILITY:
                             outcome = OVERRIDE_OUTCOME_CANCEL_USER_INTERACTION;
                             break;
-                        case RELEASE_REASON_SCREEN_LOCK:
-                        case RELEASE_REASON_NON_INTERACTIVE:
+                        case RELEASE_REASON_WAKE_LOCK_DEATH:
                             outcome = OVERRIDE_OUTCOME_CANCEL_CLIENT_DISCONNECT;
                             break;
+                        case RELEASE_REASON_NON_INTERACTIVE:
+                        case RELEASE_REASON_SCREEN_LOCK:
+                            outcome = OVERRIDE_OUTCOME_CANCEL_OTHER;
+                            break;
                         default:
                             outcome = OVERRIDE_OUTCOME_UNKNOWN;
                     }
diff --git a/services/core/java/com/android/server/power/hint/TEST_MAPPING b/services/core/java/com/android/server/power/hint/TEST_MAPPING
index 34c25c6..5450700 100644
--- a/services/core/java/com/android/server/power/hint/TEST_MAPPING
+++ b/services/core/java/com/android/server/power/hint/TEST_MAPPING
@@ -1,27 +1,18 @@
 {
   "presubmit": [
     {
-      "name": "FrameworksServicesTests",
+      "name": "PerformanceHintTests",
       "options": [
-        {
-          "include-filter": "com.android.server.power.hint"
-        },
-        {
-          "exclude-annotation": "androidx.test.filters.FlakyTest"
-        }
+        {"exclude-annotation": "androidx.test.filters.FlakyTest"},
+        {"exclude-annotation": "org.junit.Ignore"}
       ]
-    }
-  ],
-  "postsubmit": [
+    },
     {
       "name": "CtsStatsdAtomHostTestCases",
       "options": [
+        {"include-filter": "android.cts.statsdatom.performancehintmanager"},
         {"exclude-annotation": "androidx.test.filters.FlakyTest"},
-        {"exclude-annotation": "org.junit.Ignore"},
-        {"include-filter": "android.cts.statsdatom.performancehintmanager"}
-      ],
-      "file_patterns": [
-        "(/|^)HintManagerService.java"
+        {"exclude-annotation": "org.junit.Ignore"}
       ]
     }
   ]
diff --git a/services/core/java/com/android/server/power/stats/AmbientDisplayPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/AmbientDisplayPowerStatsProcessor.java
new file mode 100644
index 0000000..a42929f
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/AmbientDisplayPowerStatsProcessor.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.power.stats;
+
+import android.os.BatteryConsumer;
+import android.os.PersistableBundle;
+
+import com.android.internal.os.PowerStats;
+
+public class AmbientDisplayPowerStatsProcessor extends PowerStatsProcessor {
+    private final PowerStatsLayout mStatsLayout;
+    private final PowerStats.Descriptor mDescriptor;
+    private final long[] mTmpDeviceStats;
+    private PowerStats.Descriptor mScreenPowerStatsDescriptor;
+    private ScreenPowerStatsLayout mScreenPowerStatsLayout;
+    private long[] mTmpScreenStats;
+
+    public AmbientDisplayPowerStatsProcessor() {
+        mStatsLayout = new PowerStatsLayout();
+        mStatsLayout.addDeviceSectionPowerEstimate();
+        PersistableBundle extras = new PersistableBundle();
+        mStatsLayout.toExtras(extras);
+        mDescriptor = new PowerStats.Descriptor(BatteryConsumer.POWER_COMPONENT_AMBIENT_DISPLAY,
+                mStatsLayout.getDeviceStatsArrayLength(), null, 0, 0, extras);
+        mTmpDeviceStats = new long[mDescriptor.statsArrayLength];
+    }
+
+    @Override
+    void finish(PowerComponentAggregatedPowerStats stats, long timestampMs) {
+        stats.setPowerStatsDescriptor(mDescriptor);
+
+        PowerComponentAggregatedPowerStats screenStats =
+                stats.getAggregatedPowerStats().getPowerComponentStats(
+                        BatteryConsumer.POWER_COMPONENT_SCREEN);
+        if (screenStats == null) {
+            return;
+        }
+
+        if (mScreenPowerStatsDescriptor == null) {
+            mScreenPowerStatsDescriptor = screenStats.getPowerStatsDescriptor();
+            if (mScreenPowerStatsDescriptor == null) {
+                return;
+            }
+
+            mScreenPowerStatsLayout = new ScreenPowerStatsLayout(mScreenPowerStatsDescriptor);
+            mTmpScreenStats = new long[mScreenPowerStatsDescriptor.statsArrayLength];
+        }
+
+        MultiStateStats.States[] deviceStateConfig = screenStats.getConfig().getDeviceStateConfig();
+
+        // Ambient display power estimates have already been calculated by the screen power stats
+        // processor. All that remains to be done is copy the estimates over.
+        MultiStateStats.States.forEachTrackedStateCombination(deviceStateConfig,
+                states -> {
+                    screenStats.getDeviceStats(mTmpScreenStats, states);
+                    double power =
+                            mScreenPowerStatsLayout.getScreenDozePowerEstimate(mTmpScreenStats);
+                    mStatsLayout.setDevicePowerEstimate(mTmpDeviceStats, power);
+                    stats.setDeviceStats(states, mTmpDeviceStats);
+                });
+    }
+}
diff --git a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
index 1b7bf89..c878f14 100644
--- a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
+++ b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
@@ -296,6 +296,7 @@
     private final LongSparseArray<SamplingTimer> mKernelMemoryStats = new LongSparseArray<>();
     private int[] mCpuPowerBracketMap;
     private final CpuPowerStatsCollector mCpuPowerStatsCollector;
+    private final ScreenPowerStatsCollector mScreenPowerStatsCollector;
     private final MobileRadioPowerStatsCollector mMobileRadioPowerStatsCollector;
     private final WifiPowerStatsCollector mWifiPowerStatsCollector;
     private final BluetoothPowerStatsCollector mBluetoothPowerStatsCollector;
@@ -303,6 +304,54 @@
     private final GnssPowerStatsCollector mGnssPowerStatsCollector;
     private final CustomEnergyConsumerPowerStatsCollector mCustomEnergyConsumerPowerStatsCollector;
     private final SparseBooleanArray mPowerStatsCollectorEnabled = new SparseBooleanArray();
+    private ScreenPowerStatsCollector.ScreenUsageTimeRetriever mScreenUsageTimeRetriever =
+            new ScreenPowerStatsCollector.ScreenUsageTimeRetriever() {
+
+                @Override
+                public long getScreenOnTimeMs(int display) {
+                    synchronized (BatteryStatsImpl.this) {
+                        return getDisplayScreenOnTime(display,
+                                mClock.elapsedRealtime() * 1000) / 1000;
+                    }
+                }
+
+                @Override
+                public long getBrightnessLevelTimeMs(int display, int brightnessLevel) {
+                    synchronized (BatteryStatsImpl.this) {
+                        return getDisplayScreenBrightnessTime(display, brightnessLevel,
+                                mClock.elapsedRealtime() * 1000) / 1000;
+                    }
+                }
+
+                @Override
+                public long getScreenDozeTimeMs(int display) {
+                    synchronized (BatteryStatsImpl.this) {
+                        return getDisplayScreenDozeTime(display,
+                                mClock.elapsedRealtime() * 1000) / 1000;
+                    }
+                }
+
+                @Override
+                public void retrieveTopActivityTimes(Callback callback) {
+                    synchronized (BatteryStatsImpl.this) {
+                        long elapsedTimeUs = mClock.elapsedRealtime() * 1000;
+                        for (int i = mUidStats.size() - 1; i >= 0; i--) {
+                            Uid uid = mUidStats.valueAt(i);
+                            long topStateTime = uid.getProcessStateTime(Uid.PROCESS_STATE_TOP,
+                                    elapsedTimeUs, STATS_SINCE_CHARGED) / 1000;
+                            Timer timer = uid.getForegroundActivityTimer();
+                            if (timer == null) {
+                                callback.onUidTopActivityTime(uid.mUid, topStateTime);
+                            } else {
+                                long topActivityTime = timer.getTotalTimeLocked(elapsedTimeUs,
+                                        STATS_SINCE_CHARGED) / 1000;
+                                callback.onUidTopActivityTime(uid.mUid, Math.min(topStateTime,
+                                        topActivityTime));
+                            }
+                        }
+                    }
+                }
+            };
     private final WifiPowerStatsCollector.WifiStatsRetriever mWifiStatsRetriever =
             new WifiPowerStatsCollector.WifiStatsRetriever() {
                 @Override
@@ -1966,8 +2015,9 @@
     }
 
     private class PowerStatsCollectorInjector implements CpuPowerStatsCollector.Injector,
-            MobileRadioPowerStatsCollector.Injector, WifiPowerStatsCollector.Injector,
-            BluetoothPowerStatsCollector.Injector, EnergyConsumerPowerStatsCollector.Injector {
+            ScreenPowerStatsCollector.Injector, MobileRadioPowerStatsCollector.Injector,
+            WifiPowerStatsCollector.Injector, BluetoothPowerStatsCollector.Injector,
+            EnergyConsumerPowerStatsCollector.Injector {
         private PackageManager mPackageManager;
         private PowerStatsCollector.ConsumedEnergyRetriever mConsumedEnergyRetriever;
         private NetworkStatsManager mNetworkStatsManager;
@@ -2039,6 +2089,16 @@
         }
 
         @Override
+        public ScreenPowerStatsCollector.ScreenUsageTimeRetriever getScreenUsageTimeRetriever() {
+            return mScreenUsageTimeRetriever;
+        }
+
+        @Override
+        public int getDisplayCount() {
+            return BatteryStatsImpl.this.getDisplayCount();
+        }
+
+        @Override
         public Supplier<NetworkStats> getMobileNetworkStatsSupplier() {
             return () -> readMobileNetworkStatsLocked(mNetworkStatsManager);
         }
@@ -5371,8 +5431,6 @@
         }
     }
 
-    int mSensorNesting;
-
     @GuardedBy("this")
     public void noteStartSensorLocked(int uid, int sensor) {
         noteStartSensorLocked(uid, sensor, mClock.elapsedRealtime(), mClock.uptimeMillis());
@@ -5381,11 +5439,8 @@
     @GuardedBy("this")
     public void noteStartSensorLocked(int uid, int sensor, long elapsedRealtimeMs, long uptimeMs) {
         uid = mapUid(uid);
-        if (mSensorNesting == 0) {
-            mHistory.recordStateStartEvent(elapsedRealtimeMs, uptimeMs,
-                    HistoryItem.STATE_SENSOR_ON_FLAG);
-        }
-        mSensorNesting++;
+        mHistory.recordStateStartEvent(elapsedRealtimeMs, uptimeMs,
+                HistoryItem.STATE_SENSOR_ON_FLAG, uid, "sensor:0x" + Integer.toHexString(sensor));
         getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
                 .noteStartSensor(sensor, elapsedRealtimeMs);
     }
@@ -5398,11 +5453,8 @@
     @GuardedBy("this")
     public void noteStopSensorLocked(int uid, int sensor, long elapsedRealtimeMs, long uptimeMs) {
         uid = mapUid(uid);
-        mSensorNesting--;
-        if (mSensorNesting == 0) {
-            mHistory.recordStateStopEvent(elapsedRealtimeMs, uptimeMs,
-                    HistoryItem.STATE_SENSOR_ON_FLAG);
-        }
+        mHistory.recordStateStopEvent(elapsedRealtimeMs, uptimeMs,
+                HistoryItem.STATE_SENSOR_ON_FLAG, uid, "sensor:0x" + Integer.toHexString(sensor));
         getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
                 .noteStopSensor(sensor, elapsedRealtimeMs);
     }
@@ -5736,13 +5788,17 @@
         maybeUpdateOverallScreenBrightness(overallBin, elapsedRealtimeMs, uptimeMs);
 
         if (shouldScheduleSync) {
-            final int numDisplays = mPerDisplayBatteryStats.length;
-            final int[] displayStates = new int[numDisplays];
-            for (int i = 0; i < numDisplays; i++) {
-                displayStates[i] = mPerDisplayBatteryStats[i].screenState;
+            if (mPowerStatsCollectorEnabled.get(BatteryConsumer.POWER_COMPONENT_SCREEN)) {
+                mScreenPowerStatsCollector.schedule();
+            } else {
+                final int numDisplays = mPerDisplayBatteryStats.length;
+                final int[] displayStates = new int[numDisplays];
+                for (int i = 0; i < numDisplays; i++) {
+                    displayStates[i] = mPerDisplayBatteryStats[i].screenState;
+                }
+                mExternalSync.scheduleSyncDueToScreenStateChange(externalUpdateFlag,
+                        batteryRunning, batteryScreenOffRunning, state, displayStates);
             }
-            mExternalSync.scheduleSyncDueToScreenStateChange(externalUpdateFlag,
-                    batteryRunning, batteryScreenOffRunning, state, displayStates);
         }
     }
 
@@ -11290,6 +11346,9 @@
         mCpuPowerStatsCollector = new CpuPowerStatsCollector(mPowerStatsCollectorInjector);
         mCpuPowerStatsCollector.addConsumer(this::recordPowerStats);
 
+        mScreenPowerStatsCollector = new ScreenPowerStatsCollector(mPowerStatsCollectorInjector);
+        mScreenPowerStatsCollector.addConsumer(this::recordPowerStats);
+
         mMobileRadioPowerStatsCollector = new MobileRadioPowerStatsCollector(
                 mPowerStatsCollectorInjector, this::onMobileRadioPowerStatsRetrieved);
         mMobileRadioPowerStatsCollector.addConsumer(this::recordPowerStats);
@@ -14750,6 +14809,10 @@
                 mPowerStatsCollectorEnabled.get(BatteryConsumer.POWER_COMPONENT_CPU));
         mCpuPowerStatsCollector.schedule();
 
+        mScreenPowerStatsCollector.setEnabled(
+                mPowerStatsCollectorEnabled.get(BatteryConsumer.POWER_COMPONENT_SCREEN));
+        mScreenPowerStatsCollector.schedule();
+
         mMobileRadioPowerStatsCollector.setEnabled(
                 mPowerStatsCollectorEnabled.get(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO));
         mMobileRadioPowerStatsCollector.schedule();
@@ -14786,6 +14849,8 @@
         switch (powerComponent) {
             case BatteryConsumer.POWER_COMPONENT_CPU:
                 return mCpuPowerStatsCollector;
+            case BatteryConsumer.POWER_COMPONENT_SCREEN:
+                return mScreenPowerStatsCollector;
             case BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO:
                 return mMobileRadioPowerStatsCollector;
             case BatteryConsumer.POWER_COMPONENT_WIFI:
@@ -16328,12 +16393,18 @@
      * Callers will need to wait for the collection to complete on the handler thread.
      */
     public void schedulePowerStatsSampleCollection() {
+        if (!mSystemReady) {
+            return;
+        }
+
         mCpuPowerStatsCollector.forceSchedule();
+        mScreenPowerStatsCollector.forceSchedule();
         mMobileRadioPowerStatsCollector.forceSchedule();
         mWifiPowerStatsCollector.forceSchedule();
         mBluetoothPowerStatsCollector.forceSchedule();
         mCameraPowerStatsCollector.forceSchedule();
         mGnssPowerStatsCollector.forceSchedule();
+        mCustomEnergyConsumerPowerStatsCollector.forceSchedule();
     }
 
     /**
@@ -16351,11 +16422,13 @@
      */
     public void dumpStatsSample(PrintWriter pw) {
         mCpuPowerStatsCollector.collectAndDump(pw);
+        mScreenPowerStatsCollector.collectAndDump(pw);
         mMobileRadioPowerStatsCollector.collectAndDump(pw);
         mWifiPowerStatsCollector.collectAndDump(pw);
         mBluetoothPowerStatsCollector.collectAndDump(pw);
         mCameraPowerStatsCollector.collectAndDump(pw);
         mGnssPowerStatsCollector.collectAndDump(pw);
+        mCustomEnergyConsumerPowerStatsCollector.collectAndDump(pw);
     }
 
     private final Runnable mWriteAsyncRunnable = () -> {
diff --git a/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java b/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java
index ac68966..b308f38 100644
--- a/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java
+++ b/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java
@@ -93,8 +93,10 @@
                 if (!mPowerStatsExporterEnabled.get(BatteryConsumer.POWER_COMPONENT_BLUETOOTH)) {
                     mPowerCalculators.add(new BluetoothPowerCalculator(mPowerProfile));
                 }
-                mPowerCalculators.add(new SensorPowerCalculator(
-                        mContext.getSystemService(SensorManager.class)));
+                if (!mPowerStatsExporterEnabled.get(BatteryConsumer.POWER_COMPONENT_SENSORS)) {
+                    mPowerCalculators.add(new SensorPowerCalculator(
+                            mContext.getSystemService(SensorManager.class)));
+                }
                 if (!mPowerStatsExporterEnabled.get(BatteryConsumer.POWER_COMPONENT_GNSS)) {
                     mPowerCalculators.add(new GnssPowerCalculator(mPowerProfile));
                 }
@@ -110,8 +112,13 @@
                 if (!mPowerStatsExporterEnabled.get(BatteryConsumer.POWER_COMPONENT_VIDEO)) {
                     mPowerCalculators.add(new VideoPowerCalculator(mPowerProfile));
                 }
-                mPowerCalculators.add(new ScreenPowerCalculator(mPowerProfile));
-                mPowerCalculators.add(new AmbientDisplayPowerCalculator(mPowerProfile));
+                if (!mPowerStatsExporterEnabled.get(BatteryConsumer.POWER_COMPONENT_SCREEN)) {
+                    mPowerCalculators.add(new ScreenPowerCalculator(mPowerProfile));
+                }
+                if (!mPowerStatsExporterEnabled.get(
+                        BatteryConsumer.POWER_COMPONENT_AMBIENT_DISPLAY)) {
+                    mPowerCalculators.add(new AmbientDisplayPowerCalculator(mPowerProfile));
+                }
                 mPowerCalculators.add(new IdlePowerCalculator(mPowerProfile));
                 if (!mPowerStatsExporterEnabled.get(BatteryConsumer.POWER_COMPONENT_ANY)) {
                     mPowerCalculators.add(new CustomEnergyConsumerPowerCalculator(mPowerProfile));
diff --git a/services/core/java/com/android/server/power/stats/CustomEnergyConsumerPowerStatsCollector.java b/services/core/java/com/android/server/power/stats/CustomEnergyConsumerPowerStatsCollector.java
index 1191901..4bfe442 100644
--- a/services/core/java/com/android/server/power/stats/CustomEnergyConsumerPowerStatsCollector.java
+++ b/services/core/java/com/android/server/power/stats/CustomEnergyConsumerPowerStatsCollector.java
@@ -19,6 +19,7 @@
 import android.hardware.power.stats.EnergyConsumerType;
 import android.os.BatteryConsumer;
 
+import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -65,4 +66,22 @@
         }
         return success;
     }
+
+    @Override
+    public boolean forceSchedule() {
+        ensureInitialized();
+        boolean success = false;
+        for (int i = 0; i < mCollectors.size(); i++) {
+            success |= mCollectors.get(i).forceSchedule();
+        }
+        return success;
+    }
+
+    @Override
+    public void collectAndDump(PrintWriter pw) {
+        ensureInitialized();
+        for (int i = 0; i < mCollectors.size(); i++) {
+            mCollectors.get(i).collectAndDump(pw);
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/power/stats/EnergyConsumerPowerStatsCollector.java b/services/core/java/com/android/server/power/stats/EnergyConsumerPowerStatsCollector.java
index cace941..79fbe8e 100644
--- a/services/core/java/com/android/server/power/stats/EnergyConsumerPowerStatsCollector.java
+++ b/services/core/java/com/android/server/power/stats/EnergyConsumerPowerStatsCollector.java
@@ -163,10 +163,7 @@
 
         mLayout.setConsumedEnergy(mPowerStats.stats, 0, uJtoUc(energyDelta, averageVoltage));
 
-        for (int i = mPowerStats.uidStats.size() - 1; i >= 0; i--) {
-            mLayout.setUidConsumedEnergy(mPowerStats.uidStats.valueAt(i), 0, 0);
-        }
-
+        mPowerStats.uidStats.clear();
         if (energy != null) {
             for (int i = energy.length - 1; i >= 0; i--) {
                 EnergyConsumerAttribution[] perUid = energy[i].attribution;
@@ -176,9 +173,12 @@
 
                 for (EnergyConsumerAttribution attribution : perUid) {
                     int uid = mUidResolver.mapUid(attribution.uid);
-                    long lastEnergy = mLastConsumerEnergyPerUid.get(uid);
-                    long deltaEnergy = attribution.energyUWs - lastEnergy;
+                    long lastEnergy = mLastConsumerEnergyPerUid.get(uid, ENERGY_UNSPECIFIED);
                     mLastConsumerEnergyPerUid.put(uid, attribution.energyUWs);
+                    if (lastEnergy == ENERGY_UNSPECIFIED) {
+                        continue;
+                    }
+                    long deltaEnergy = attribution.energyUWs - lastEnergy;
                     if (deltaEnergy <= 0) {
                         continue;
                     }
@@ -189,7 +189,8 @@
                     }
 
                     mLayout.setUidConsumedEnergy(uidStats, 0,
-                            mLayout.getUidConsumedEnergy(uidStats, 0) + deltaEnergy);
+                            mLayout.getUidConsumedEnergy(uidStats, 0)
+                                    + uJtoUc(deltaEnergy, averageVoltage));
                 }
             }
         }
diff --git a/services/core/java/com/android/server/power/stats/GnssPowerCalculator.java b/services/core/java/com/android/server/power/stats/GnssPowerCalculator.java
index ab22e3e..1003a81 100644
--- a/services/core/java/com/android/server/power/stats/GnssPowerCalculator.java
+++ b/services/core/java/com/android/server/power/stats/GnssPowerCalculator.java
@@ -126,7 +126,7 @@
         long totalTime = 0;
         double totalPower = 0;
         for (int i = 0; i < GnssSignalQuality.NUM_GNSS_SIGNAL_QUALITY_LEVELS; i++) {
-            long timePerLevel = stats.getGpsSignalQualityTime(i, rawRealtimeUs, statsType);
+            long timePerLevel = stats.getGpsSignalQualityTime(i, rawRealtimeUs, statsType) / 1000;
             totalTime += timePerLevel;
             totalPower += mAveragePowerPerSignalQuality[i] * timePerLevel;
         }
diff --git a/services/core/java/com/android/server/power/stats/PowerComponentAggregatedPowerStats.java b/services/core/java/com/android/server/power/stats/PowerComponentAggregatedPowerStats.java
index 8384b2b..6820197 100644
--- a/services/core/java/com/android/server/power/stats/PowerComponentAggregatedPowerStats.java
+++ b/services/core/java/com/android/server/power/stats/PowerComponentAggregatedPowerStats.java
@@ -163,15 +163,14 @@
         }
     }
 
-    void setDeviceStats(@AggregatedPowerStatsConfig.TrackedState int[] states, long[] values) {
+    void setDeviceStats(int[] states, long[] values) {
         if (mDeviceStats == null) {
             createDeviceStats(0);
         }
         mDeviceStats.setStats(states, values);
     }
 
-    void setUidStats(int uid, @AggregatedPowerStatsConfig.TrackedState int[] states,
-            long[] values) {
+    void setUidStats(int uid, int[] states, long[] values) {
         UidStats uidStats = getUidStats(uid);
         uidStats.stats.setStats(states, values);
     }
diff --git a/services/core/java/com/android/server/power/stats/PowerStatsCollector.java b/services/core/java/com/android/server/power/stats/PowerStatsCollector.java
index d9f6c1f..f5b0005 100644
--- a/services/core/java/com/android/server/power/stats/PowerStatsCollector.java
+++ b/services/core/java/com/android/server/power/stats/PowerStatsCollector.java
@@ -196,12 +196,11 @@
         }
 
         IndentingPrintWriter out = new IndentingPrintWriter(pw);
-        out.print(getClass().getSimpleName());
         if (!isEnabled()) {
+            out.print(getClass().getSimpleName());
             out.println(": disabled");
             return;
         }
-        out.println();
 
         ArrayList<PowerStats> collected = new ArrayList<>();
         Consumer<PowerStats> consumer = collected::add;
@@ -215,11 +214,9 @@
             removeConsumer(consumer);
         }
 
-        out.increaseIndent();
         for (PowerStats stats : collected) {
             stats.dump(out);
         }
-        out.decreaseIndent();
     }
 
     private void awaitCompletion() {
diff --git a/services/core/java/com/android/server/power/stats/PowerStatsExporter.java b/services/core/java/com/android/server/power/stats/PowerStatsExporter.java
index 0f13492..bd75faa 100644
--- a/services/core/java/com/android/server/power/stats/PowerStatsExporter.java
+++ b/services/core/java/com/android/server/power/stats/PowerStatsExporter.java
@@ -59,58 +59,61 @@
      */
     public void exportAggregatedPowerStats(BatteryUsageStats.Builder batteryUsageStatsBuilder,
             long monotonicStartTime, long monotonicEndTime) {
-        boolean hasStoredSpans = false;
-        long maxEndTime = monotonicStartTime;
-        List<PowerStatsSpan.Metadata> spans = mPowerStatsStore.getTableOfContents();
-        for (int i = spans.size() - 1; i >= 0; i--) {
-            PowerStatsSpan.Metadata metadata = spans.get(i);
-            if (!metadata.getSections().contains(AggregatedPowerStatsSection.TYPE)) {
-                continue;
-            }
-
-            List<PowerStatsSpan.TimeFrame> timeFrames = metadata.getTimeFrames();
-            long spanMinTime = Long.MAX_VALUE;
-            long spanMaxTime = Long.MIN_VALUE;
-            for (int j = 0; j < timeFrames.size(); j++) {
-                PowerStatsSpan.TimeFrame timeFrame = timeFrames.get(j);
-                long startMonotonicTime = timeFrame.startMonotonicTime;
-                long endMonotonicTime = startMonotonicTime + timeFrame.duration;
-                if (startMonotonicTime < spanMinTime) {
-                    spanMinTime = startMonotonicTime;
+        synchronized (this) {
+            boolean hasStoredSpans = false;
+            long maxEndTime = monotonicStartTime;
+            List<PowerStatsSpan.Metadata> spans = mPowerStatsStore.getTableOfContents();
+            for (int i = spans.size() - 1; i >= 0; i--) {
+                PowerStatsSpan.Metadata metadata = spans.get(i);
+                if (!metadata.getSections().contains(AggregatedPowerStatsSection.TYPE)) {
+                    continue;
                 }
-                if (endMonotonicTime > spanMaxTime) {
-                    spanMaxTime = endMonotonicTime;
+
+                List<PowerStatsSpan.TimeFrame> timeFrames = metadata.getTimeFrames();
+                long spanMinTime = Long.MAX_VALUE;
+                long spanMaxTime = Long.MIN_VALUE;
+                for (int j = 0; j < timeFrames.size(); j++) {
+                    PowerStatsSpan.TimeFrame timeFrame = timeFrames.get(j);
+                    long startMonotonicTime = timeFrame.startMonotonicTime;
+                    long endMonotonicTime = startMonotonicTime + timeFrame.duration;
+                    if (startMonotonicTime < spanMinTime) {
+                        spanMinTime = startMonotonicTime;
+                    }
+                    if (endMonotonicTime > spanMaxTime) {
+                        spanMaxTime = endMonotonicTime;
+                    }
+                }
+
+                if (!(spanMinTime >= monotonicStartTime && spanMaxTime < monotonicEndTime)) {
+                    continue;
+                }
+
+                if (spanMaxTime > maxEndTime) {
+                    maxEndTime = spanMaxTime;
+                }
+
+                PowerStatsSpan span = mPowerStatsStore.loadPowerStatsSpan(metadata.getId(),
+                        AggregatedPowerStatsSection.TYPE);
+                if (span == null) {
+                    Slog.e(TAG, "Could not read PowerStatsStore section " + metadata);
+                    continue;
+                }
+                List<PowerStatsSpan.Section> sections = span.getSections();
+                for (int k = 0; k < sections.size(); k++) {
+                    hasStoredSpans = true;
+                    PowerStatsSpan.Section section = sections.get(k);
+                    populateBatteryUsageStatsBuilder(batteryUsageStatsBuilder,
+                            ((AggregatedPowerStatsSection) section).getAggregatedPowerStats());
                 }
             }
 
-            if (!(spanMinTime >= monotonicStartTime && spanMaxTime < monotonicEndTime)) {
-                continue;
+            if (!hasStoredSpans
+                    || maxEndTime < monotonicEndTime - mBatterySessionTimeSpanSlackMillis) {
+                mPowerStatsAggregator.aggregatePowerStats(maxEndTime, monotonicEndTime,
+                        stats -> populateBatteryUsageStatsBuilder(batteryUsageStatsBuilder, stats));
             }
-
-            if (spanMaxTime > maxEndTime) {
-                maxEndTime = spanMaxTime;
-            }
-
-            PowerStatsSpan span = mPowerStatsStore.loadPowerStatsSpan(metadata.getId(),
-                    AggregatedPowerStatsSection.TYPE);
-            if (span == null) {
-                Slog.e(TAG, "Could not read PowerStatsStore section " + metadata);
-                continue;
-            }
-            List<PowerStatsSpan.Section> sections = span.getSections();
-            for (int k = 0; k < sections.size(); k++) {
-                hasStoredSpans = true;
-                PowerStatsSpan.Section section = sections.get(k);
-                populateBatteryUsageStatsBuilder(batteryUsageStatsBuilder,
-                        ((AggregatedPowerStatsSection) section).getAggregatedPowerStats());
-            }
+            mPowerStatsAggregator.reset();
         }
-
-        if (!hasStoredSpans || maxEndTime < monotonicEndTime - mBatterySessionTimeSpanSlackMillis) {
-            mPowerStatsAggregator.aggregatePowerStats(maxEndTime, monotonicEndTime,
-                    stats -> populateBatteryUsageStatsBuilder(batteryUsageStatsBuilder, stats));
-        }
-        mPowerStatsAggregator.reset();
     }
 
     private void populateBatteryUsageStatsBuilder(
@@ -213,6 +216,8 @@
             PowerStatsLayout layout) {
         AggregatedPowerStatsConfig.PowerComponent powerComponent = powerComponentStats.getConfig();
         int powerComponentId = powerComponent.getPowerComponentId();
+        boolean isCustomComponent =
+                powerComponentId >= BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID;
         PowerStats.Descriptor descriptor = powerComponentStats.getPowerStatsDescriptor();
         long[] uidStats = new long[descriptor.uidStatsArrayLength];
 
@@ -220,7 +225,7 @@
         boolean breakDownByProcState = batteryUsageStatsBuilder.isProcessStateDataNeeded()
                 && powerComponent
                 .getUidStateConfig()[AggregatedPowerStatsConfig.STATE_PROCESS_STATE].isTracked()
-                && powerComponentId < BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID;
+                && !isCustomComponent;
 
         ArrayList<Integer> uids = new ArrayList<>();
         powerComponentStats.collectUids(uids);
@@ -234,7 +239,7 @@
             }
 
             for (int powerState = 0; powerState < BatteryConsumer.POWER_STATE_COUNT; powerState++) {
-                if (batteryUsageStatsBuilder.isPowerStateDataNeeded()) {
+                if (batteryUsageStatsBuilder.isPowerStateDataNeeded() && !isCustomComponent) {
                     if (powerState == BatteryConsumer.POWER_STATE_UNSPECIFIED) {
                         continue;
                     }
@@ -307,7 +312,10 @@
                 }
             }
             if (powerComponentId >= BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID) {
-                builder.addConsumedPowerForCustomComponent(powerComponentId, powerAllProcStates);
+                if (batteryUsageStatsBuilder.isSupportedCustomPowerComponent(powerComponentId)) {
+                    builder.addConsumedPowerForCustomComponent(powerComponentId,
+                            powerAllProcStates);
+                }
             } else {
                 builder.addConsumedPower(powerComponentId, powerAllProcStates,
                         BatteryConsumer.POWER_MODEL_UNDEFINED);
@@ -319,7 +327,9 @@
                 batteryUsageStatsBuilder.getAggregateBatteryConsumerBuilder(
                         BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS);
         if (powerComponentId >= BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID) {
-            allAppsScope.addConsumedPowerForCustomComponent(powerComponentId, powerAllApps);
+            if (batteryUsageStatsBuilder.isSupportedCustomPowerComponent(powerComponentId)) {
+                allAppsScope.addConsumedPowerForCustomComponent(powerComponentId, powerAllApps);
+            }
         } else {
             BatteryConsumer.Key key = allAppsScope.getKey(powerComponentId,
                     BatteryConsumer.PROCESS_STATE_ANY, screenState, powerState);
diff --git a/services/core/java/com/android/server/power/stats/PowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/PowerStatsProcessor.java
index dfc8daa..c81c7ff 100644
--- a/services/core/java/com/android/server/power/stats/PowerStatsProcessor.java
+++ b/services/core/java/com/android/server/power/stats/PowerStatsProcessor.java
@@ -220,8 +220,7 @@
         }
 
         @Nullable
-        public DeviceStateEstimation getDeviceStateEstimate(
-                @AggregatedPowerStatsConfig.TrackedState int[] stateValues) {
+        public DeviceStateEstimation getDeviceStateEstimate(int[] stateValues) {
             String label = concatLabels(mConfig.getDeviceStateConfig(), stateValues);
             for (int i = 0; i < deviceStateEstimations.size(); i++) {
                 DeviceStateEstimation deviceStateEstimation = this.deviceStateEstimations.get(i);
@@ -233,8 +232,7 @@
         }
 
         public CombinedDeviceStateEstimate getCombinedDeviceStateEstimate(
-                MultiStateStats.States[] deviceStates,
-                @AggregatedPowerStatsConfig.TrackedState int[] stateValues) {
+                MultiStateStats.States[] deviceStates, int[] stateValues) {
             String label = concatLabels(deviceStates, stateValues);
             for (int i = 0; i < combinedDeviceStateEstimations.size(); i++) {
                 CombinedDeviceStateEstimate cdse = combinedDeviceStateEstimations.get(i);
@@ -259,8 +257,8 @@
             for (int i = deviceStateEstimations.size() - 1; i >= 0; i--) {
                 deviceStateEstimations.get(i).intermediates = null;
             }
-            for (int i = deviceStateEstimations.size() - 1; i >= 0; i--) {
-                deviceStateEstimations.get(i).intermediates = null;
+            for (int i = combinedDeviceStateEstimations.size() - 1; i >= 0; i--) {
+                combinedDeviceStateEstimations.get(i).intermediates = null;
             }
             for (int i = uidStateEstimates.size() - 1; i >= 0; i--) {
                 UidStateEstimate uidStateEstimate = uidStateEstimates.get(i);
@@ -275,12 +273,10 @@
 
     protected static class DeviceStateEstimation {
         public final String id;
-        @AggregatedPowerStatsConfig.TrackedState
         public final int[] stateValues;
         public Object intermediates;
 
-        public DeviceStateEstimation(MultiStateStats.States[] config,
-                @AggregatedPowerStatsConfig.TrackedState int[] stateValues) {
+        public DeviceStateEstimation(MultiStateStats.States[] config, int[] stateValues) {
             id = concatLabels(config, stateValues);
             this.stateValues = stateValues;
         }
@@ -288,11 +284,12 @@
 
     protected static class CombinedDeviceStateEstimate {
         public final String id;
+        public final int[] stateValues;
         public List<DeviceStateEstimation> deviceStateEstimations = new ArrayList<>();
         public Object intermediates;
 
-        public CombinedDeviceStateEstimate(MultiStateStats.States[] config,
-                @AggregatedPowerStatsConfig.TrackedState int[] stateValues) {
+        public CombinedDeviceStateEstimate(MultiStateStats.States[] config, int[] stateValues) {
+            this.stateValues = Arrays.copyOf(stateValues, stateValues.length);
             id = concatLabels(config, stateValues);
         }
     }
@@ -310,19 +307,16 @@
     }
 
     protected static class UidStateProportionalEstimate {
-        @AggregatedPowerStatsConfig.TrackedState
         public final int[] stateValues;
         public Object intermediates;
 
-        protected UidStateProportionalEstimate(
-                @AggregatedPowerStatsConfig.TrackedState int[] stateValues) {
+        protected UidStateProportionalEstimate(int[] stateValues) {
             this.stateValues = stateValues;
         }
     }
 
     @NonNull
-    private static String concatLabels(MultiStateStats.States[] config,
-            @AggregatedPowerStatsConfig.TrackedState int[] stateValues) {
+    private static String concatLabels(MultiStateStats.States[] config, int[] stateValues) {
         List<String> labels = new ArrayList<>();
         for (int state = 0; state < config.length; state++) {
             if (config[state] != null && config[state].isTracked()) {
@@ -334,7 +328,6 @@
         return labels.toString();
     }
 
-    @AggregatedPowerStatsConfig.TrackedState
     private static int[][] getAllTrackedStateCombinations(MultiStateStats.States[] states) {
         List<int[]> combinations = new ArrayList<>();
         MultiStateStats.States.forEachTrackedStateCombination(states, stateValues -> {
diff --git a/services/core/java/com/android/server/power/stats/ScreenPowerStatsCollector.java b/services/core/java/com/android/server/power/stats/ScreenPowerStatsCollector.java
new file mode 100644
index 0000000..291f289
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/ScreenPowerStatsCollector.java
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.power.stats;
+
+import android.hardware.power.stats.EnergyConsumerType;
+import android.os.BatteryConsumer;
+import android.os.BatteryStats;
+import android.os.Handler;
+import android.os.PersistableBundle;
+import android.util.Slog;
+import android.util.SparseLongArray;
+
+import com.android.internal.os.Clock;
+import com.android.internal.os.PowerStats;
+
+import java.util.Arrays;
+import java.util.function.IntSupplier;
+
+public class ScreenPowerStatsCollector extends PowerStatsCollector {
+    private static final String TAG = "ScreenPowerStatsCollector";
+
+    interface ScreenUsageTimeRetriever {
+        interface Callback {
+            void onUidTopActivityTime(int uid, long topActivityTimeMs);
+        }
+
+        void retrieveTopActivityTimes(Callback callback);
+
+        long getScreenOnTimeMs(int display);
+        long getBrightnessLevelTimeMs(int display, int brightnessLevel);
+        long getScreenDozeTimeMs(int display);
+    }
+
+    interface Injector {
+        Handler getHandler();
+        Clock getClock();
+        PowerStatsUidResolver getUidResolver();
+        long getPowerStatsCollectionThrottlePeriod(String powerComponentName);
+        ConsumedEnergyRetriever getConsumedEnergyRetriever();
+        IntSupplier getVoltageSupplier();
+        ScreenUsageTimeRetriever getScreenUsageTimeRetriever();
+        int getDisplayCount();
+    }
+
+    private static final long ENERGY_UNSPECIFIED = -1;
+
+    private final Injector mInjector;
+    private boolean mIsInitialized;
+    private ScreenPowerStatsLayout mLayout;
+    private int mDisplayCount;
+    private PowerStats mPowerStats;
+    private ConsumedEnergyRetriever mConsumedEnergyRetriever;
+    private IntSupplier mVoltageSupplier;
+    private ScreenUsageTimeRetriever mScreenUsageTimeRetriever;
+    private int[] mEnergyConsumerIds = new int[0];
+    private long[] mLastConsumedEnergyUws;
+    private int mLastVoltageMv;
+    private boolean mFirstSample = true;
+    private long[] mLastScreenOnTime;
+    private long[][] mLastBrightnessLevelTime;
+    private long[] mLastDozeTime;
+    private final SparseLongArray mLastTopActivityTime = new SparseLongArray();
+    private long mLastCollectionTime;
+
+    ScreenPowerStatsCollector(Injector injector) {
+        super(injector.getHandler(),
+                injector.getPowerStatsCollectionThrottlePeriod(
+                        BatteryConsumer.powerComponentIdToString(
+                                BatteryConsumer.POWER_COMPONENT_SCREEN)),
+                injector.getUidResolver(), injector.getClock());
+        mInjector = injector;
+    }
+
+    private boolean ensureInitialized() {
+        if (mIsInitialized) {
+            return true;
+        }
+
+        if (!isEnabled()) {
+            return false;
+        }
+
+        mDisplayCount = mInjector.getDisplayCount();
+        mConsumedEnergyRetriever = mInjector.getConsumedEnergyRetriever();
+        mVoltageSupplier = mInjector.getVoltageSupplier();
+        mScreenUsageTimeRetriever = mInjector.getScreenUsageTimeRetriever();
+        mEnergyConsumerIds = mConsumedEnergyRetriever.getEnergyConsumerIds(
+                EnergyConsumerType.DISPLAY);
+        mLastConsumedEnergyUws = new long[mEnergyConsumerIds.length];
+        Arrays.fill(mLastConsumedEnergyUws, ENERGY_UNSPECIFIED);
+
+        mLayout = new ScreenPowerStatsLayout();
+        mLayout.addDeviceScreenUsageDurationSection(mInjector.getDisplayCount());
+        mLayout.addDeviceSectionEnergyConsumers(mEnergyConsumerIds.length);
+        mLayout.addDeviceSectionUsageDuration();
+        mLayout.addDeviceSectionPowerEstimate();
+        mLayout.addUidTopActivitiyDuration();
+        mLayout.addUidSectionPowerEstimate();
+
+        PersistableBundle extras = new PersistableBundle();
+        mLayout.toExtras(extras);
+        PowerStats.Descriptor powerStatsDescriptor = new PowerStats.Descriptor(
+                BatteryConsumer.POWER_COMPONENT_SCREEN, mLayout.getDeviceStatsArrayLength(),
+                null, 0, mLayout.getUidStatsArrayLength(),
+                extras);
+
+        mLastScreenOnTime = new long[mDisplayCount];
+        mLastBrightnessLevelTime = new long[mDisplayCount][BatteryStats.NUM_SCREEN_BRIGHTNESS_BINS];
+        mLastDozeTime = new long[mDisplayCount];
+
+        mPowerStats = new PowerStats(powerStatsDescriptor);
+
+        mIsInitialized = true;
+        return true;
+    }
+
+    @Override
+    protected PowerStats collectStats() {
+        if (!ensureInitialized()) {
+            return null;
+        }
+
+        if (mEnergyConsumerIds.length != 0) {
+            collectEnergyConsumers();
+        }
+
+        for (int display = 0; display < mDisplayCount; display++) {
+            long screenOnTimeMs = mScreenUsageTimeRetriever.getScreenOnTimeMs(display);
+            if (!mFirstSample) {
+                mLayout.setScreenOnDuration(mPowerStats.stats, display,
+                        screenOnTimeMs - mLastScreenOnTime[display]);
+            }
+            mLastScreenOnTime[display] = screenOnTimeMs;
+
+            for (int level = 0; level < BatteryStats.NUM_SCREEN_BRIGHTNESS_BINS; level++) {
+                long brightnessLevelTimeMs =
+                        mScreenUsageTimeRetriever.getBrightnessLevelTimeMs(display, level);
+                if (!mFirstSample) {
+                    mLayout.setBrightnessLevelDuration(mPowerStats.stats, display, level,
+                            brightnessLevelTimeMs - mLastBrightnessLevelTime[display][level]);
+                }
+                mLastBrightnessLevelTime[display][level] = brightnessLevelTimeMs;
+            }
+            long screenDozeTimeMs = mScreenUsageTimeRetriever.getScreenDozeTimeMs(display);
+            if (!mFirstSample) {
+                mLayout.setScreenDozeDuration(mPowerStats.stats, display,
+                        screenDozeTimeMs - mLastDozeTime[display]);
+            }
+            mLastDozeTime[display] = screenDozeTimeMs;
+        }
+
+        mPowerStats.uidStats.clear();
+
+        mScreenUsageTimeRetriever.retrieveTopActivityTimes((uid, topActivityTimeMs) -> {
+            long topActivityDuration = topActivityTimeMs - mLastTopActivityTime.get(uid);
+            if (topActivityDuration == 0) {
+                return;
+            }
+            mLastTopActivityTime.put(uid, topActivityTimeMs);
+
+            int mappedUid = mUidResolver.mapUid(uid);
+            long[] uidStats = mPowerStats.uidStats.get(mappedUid);
+            if (uidStats == null) {
+                uidStats = new long[mLayout.getUidStatsArrayLength()];
+                mPowerStats.uidStats.put(mappedUid, uidStats);
+            }
+
+            mLayout.setUidTopActivityDuration(uidStats,
+                    mLayout.getUidTopActivityDuration(uidStats) + topActivityDuration);
+        });
+
+        long elapsedRealtime = mClock.elapsedRealtime();
+        mPowerStats.durationMs = elapsedRealtime - mLastCollectionTime;
+        mLastCollectionTime = elapsedRealtime;
+
+        mFirstSample = false;
+
+        return mPowerStats;
+    }
+
+    private void collectEnergyConsumers() {
+        int voltageMv = mVoltageSupplier.getAsInt();
+        if (voltageMv <= 0) {
+            Slog.wtf(TAG, "Unexpected battery voltage (" + voltageMv
+                    + " mV) when querying energy consumers");
+            return;
+        }
+
+        int averageVoltage = mLastVoltageMv != 0 ? (mLastVoltageMv + voltageMv) / 2 : voltageMv;
+        mLastVoltageMv = voltageMv;
+
+        long[] energyUws = mConsumedEnergyRetriever.getConsumedEnergyUws(mEnergyConsumerIds);
+        if (energyUws == null) {
+            return;
+        }
+
+        for (int i = energyUws.length - 1; i >= 0; i--) {
+            long energyDelta = mLastConsumedEnergyUws[i] != ENERGY_UNSPECIFIED
+                    ? energyUws[i] - mLastConsumedEnergyUws[i] : 0;
+            if (energyDelta < 0) {
+                // Likely, restart of powerstats HAL
+                energyDelta = 0;
+            }
+            mLayout.setConsumedEnergy(mPowerStats.stats, i, uJtoUc(energyDelta, averageVoltage));
+            mLastConsumedEnergyUws[i] = energyUws[i];
+        }
+    }
+
+    @Override
+    protected void onUidRemoved(int uid) {
+        mLastTopActivityTime.delete(uid);
+    }
+}
diff --git a/services/core/java/com/android/server/power/stats/ScreenPowerStatsLayout.java b/services/core/java/com/android/server/power/stats/ScreenPowerStatsLayout.java
new file mode 100644
index 0000000..f134aa8
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/ScreenPowerStatsLayout.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.power.stats;
+
+import android.annotation.NonNull;
+import android.os.BatteryStats;
+import android.os.PersistableBundle;
+
+import com.android.internal.os.PowerStats;
+
+/**
+ * Captures the positions and lengths of sections of the stats array, such as time-in-state,
+ * power usage estimates etc.
+ */
+public class ScreenPowerStatsLayout extends PowerStatsLayout {
+    private static final String EXTRA_DEVICE_SCREEN_COUNT = "dsc";
+    private static final String EXTRA_DEVICE_SCREEN_ON_DURATION_POSITION = "dsd";
+    private static final String EXTRA_DEVICE_BRIGHTNESS_DURATION_POSITIONS = "dbd";
+    private static final String EXTRA_DEVICE_DOZE_DURATION_POSITION = "ddd";
+    private static final String EXTRA_DEVICE_DOZE_POWER_POSITION = "ddp";
+    private static final String EXTRA_UID_FOREGROUND_DURATION = "uf";
+
+    private int mDisplayCount;
+    private int mDeviceScreenOnDurationPosition;
+    private int[] mDeviceBrightnessDurationPositions;
+    private int mDeviceScreenDozeDurationPosition;
+    private int mDeviceScreenDozePowerPosition;
+    private int mUidTopActivityTimePosition;
+
+    ScreenPowerStatsLayout() {
+    }
+
+    ScreenPowerStatsLayout(@NonNull PowerStats.Descriptor descriptor) {
+        super(descriptor);
+    }
+
+    void addDeviceScreenUsageDurationSection(int displayCount) {
+        mDisplayCount = displayCount;
+        mDeviceScreenOnDurationPosition = addDeviceSection(displayCount, "on");
+        mDeviceBrightnessDurationPositions = new int[BatteryStats.NUM_SCREEN_BRIGHTNESS_BINS];
+        for (int level = 0; level < BatteryStats.NUM_SCREEN_BRIGHTNESS_BINS; level++) {
+            mDeviceBrightnessDurationPositions[level] =
+                    addDeviceSection(displayCount, BatteryStats.SCREEN_BRIGHTNESS_NAMES[level]);
+        }
+        mDeviceScreenDozeDurationPosition = addDeviceSection(displayCount, "doze");
+    }
+
+    @Override
+    public void addDeviceSectionPowerEstimate() {
+        super.addDeviceSectionPowerEstimate();
+        // Used by AmbientDisplayPowerStatsProcessor
+        mDeviceScreenDozePowerPosition = addDeviceSection(1, "doze-power", FLAG_HIDDEN);
+    }
+
+    public int getDisplayCount() {
+        return mDisplayCount;
+    }
+
+    /**
+     * Stores screen-on time for the specified display.
+     */
+    public void setScreenOnDuration(long[] stats, int display, long durationMs) {
+        stats[mDeviceScreenOnDurationPosition + display] = durationMs;
+    }
+
+    /**
+     * Returns screen-on time for the specified display.
+     */
+    public long getScreenOnDuration(long[] stats, int display) {
+        return stats[mDeviceScreenOnDurationPosition + display];
+    }
+
+    /**
+     * Stores time at the specified brightness level for the specified display.
+     */
+    public void setBrightnessLevelDuration(long[] stats, int display, int brightnessLevel,
+            long durationMs) {
+        stats[mDeviceBrightnessDurationPositions[brightnessLevel] + display] = durationMs;
+    }
+
+    /**
+     * Returns time at the specified brightness level for the specified display.
+     */
+    public long getBrightnessLevelDuration(long[] stats, int display, int brightnessLevel) {
+        return stats[mDeviceBrightnessDurationPositions[brightnessLevel] + display];
+    }
+
+    /**
+     * Stores time in the doze (ambient) state for the specified display.
+     */
+    public void setScreenDozeDuration(long[] stats, int display, long durationMs) {
+        stats[mDeviceScreenDozeDurationPosition + display] = durationMs;
+    }
+
+    /**
+     * Retrieves time in the doze (ambient) state for the specified display.
+     */
+    public long getScreenDozeDuration(long[] stats, int display) {
+        return stats[mDeviceScreenDozeDurationPosition + display];
+    }
+
+    /**
+     * Stores estimated power in the doze (ambient) state.
+     */
+    public void setScreenDozePowerEstimate(long[] stats, double power) {
+        stats[mDeviceScreenDozePowerPosition] = (long) (power * MILLI_TO_NANO_MULTIPLIER);
+    }
+
+    /**
+     * Retrieves estimated power in the doze (ambient) state.
+     */
+    public double getScreenDozePowerEstimate(long[] stats) {
+        return stats[mDeviceScreenDozePowerPosition] / MILLI_TO_NANO_MULTIPLIER;
+    }
+
+    void addUidTopActivitiyDuration() {
+        mUidTopActivityTimePosition = addUidSection(1, "top");
+    }
+
+    /**
+     * Stores time the UID spent in the TOP state.
+     */
+    public void setUidTopActivityDuration(long[] stats, long durationMs) {
+        stats[mUidTopActivityTimePosition] = durationMs;
+    }
+
+    /**
+     * Returns time the UID spent in the TOP state.
+     */
+    public long getUidTopActivityDuration(long[] stats) {
+        return stats[mUidTopActivityTimePosition];
+    }
+
+    @Override
+    public void toExtras(PersistableBundle extras) {
+        super.toExtras(extras);
+        extras.putInt(EXTRA_DEVICE_SCREEN_COUNT, mDisplayCount);
+        extras.putInt(EXTRA_DEVICE_SCREEN_ON_DURATION_POSITION, mDeviceScreenOnDurationPosition);
+        extras.putIntArray(EXTRA_DEVICE_BRIGHTNESS_DURATION_POSITIONS,
+                mDeviceBrightnessDurationPositions);
+        extras.putInt(EXTRA_DEVICE_DOZE_DURATION_POSITION, mDeviceScreenDozeDurationPosition);
+        extras.putInt(EXTRA_DEVICE_DOZE_POWER_POSITION, mDeviceScreenDozePowerPosition);
+        extras.putInt(EXTRA_UID_FOREGROUND_DURATION, mUidTopActivityTimePosition);
+    }
+
+    @Override
+    public void fromExtras(PersistableBundle extras) {
+        super.fromExtras(extras);
+        mDisplayCount = extras.getInt(EXTRA_DEVICE_SCREEN_COUNT, 1);
+        mDeviceScreenOnDurationPosition = extras.getInt(EXTRA_DEVICE_SCREEN_ON_DURATION_POSITION);
+        mDeviceBrightnessDurationPositions = extras.getIntArray(
+                EXTRA_DEVICE_BRIGHTNESS_DURATION_POSITIONS);
+        mDeviceScreenDozeDurationPosition = extras.getInt(EXTRA_DEVICE_DOZE_DURATION_POSITION);
+        mDeviceScreenDozePowerPosition = extras.getInt(EXTRA_DEVICE_DOZE_POWER_POSITION);
+        mUidTopActivityTimePosition = extras.getInt(EXTRA_UID_FOREGROUND_DURATION);
+    }
+}
diff --git a/services/core/java/com/android/server/power/stats/ScreenPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/ScreenPowerStatsProcessor.java
new file mode 100644
index 0000000..908c751
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/ScreenPowerStatsProcessor.java
@@ -0,0 +1,239 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.power.stats;
+
+import static android.os.BatteryConsumer.PROCESS_STATE_ANY;
+
+import static com.android.internal.os.PowerProfile.POWER_GROUP_DISPLAY_AMBIENT;
+import static com.android.internal.os.PowerProfile.POWER_GROUP_DISPLAY_SCREEN_FULL;
+import static com.android.internal.os.PowerProfile.POWER_GROUP_DISPLAY_SCREEN_ON;
+import static com.android.server.power.stats.AggregatedPowerStatsConfig.SCREEN_STATE_ON;
+import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_POWER;
+import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_PROCESS_STATE;
+import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_SCREEN;
+
+import android.os.BatteryStats;
+import android.util.Slog;
+
+import com.android.internal.os.PowerProfile;
+import com.android.internal.os.PowerStats;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class ScreenPowerStatsProcessor extends PowerStatsProcessor {
+    private static final String TAG = "ScreenPowerStatsProcessor";
+    private final int mDisplayCount;
+    private final UsageBasedPowerEstimator[] mScreenOnPowerEstimators;
+    private final UsageBasedPowerEstimator[] mScreenDozePowerEstimators;
+    private final UsageBasedPowerEstimator[][] mScreenBrightnessLevelPowerEstimators;
+    private PowerStats.Descriptor mLastUsedDescriptor;
+    private ScreenPowerStatsLayout mStatsLayout;
+    private PowerEstimationPlan mPlan;
+    private long[] mTmpDeviceStatsArray;
+    private long[] mTmpUidStatsArray;
+
+    private static class Intermediates {
+        public double power;
+    }
+
+    public ScreenPowerStatsProcessor(PowerProfile powerProfile) {
+        mDisplayCount = powerProfile.getNumDisplays();
+        mScreenOnPowerEstimators = new UsageBasedPowerEstimator[mDisplayCount];
+        mScreenDozePowerEstimators = new UsageBasedPowerEstimator[mDisplayCount];
+        mScreenBrightnessLevelPowerEstimators = new UsageBasedPowerEstimator[mDisplayCount][];
+        for (int display = 0; display < mDisplayCount; display++) {
+            mScreenOnPowerEstimators[display] = new UsageBasedPowerEstimator(
+                    powerProfile.getAveragePowerForOrdinal(POWER_GROUP_DISPLAY_SCREEN_ON, display));
+
+            double averagePowerFullBrightness = powerProfile.getAveragePowerForOrdinal(
+                    POWER_GROUP_DISPLAY_SCREEN_FULL, display);
+            mScreenBrightnessLevelPowerEstimators[display] =
+                    new UsageBasedPowerEstimator[BatteryStats.NUM_SCREEN_BRIGHTNESS_BINS];
+            for (int bin = 0; bin < BatteryStats.NUM_SCREEN_BRIGHTNESS_BINS; bin++) {
+                // For example, if the number of bins is 3, the corresponding averages
+                // are calculated as 0.5 * full, 1.5 * full, 2.5 * full
+                final double binPowerMah = averagePowerFullBrightness * (bin + 0.5)
+                        / BatteryStats.NUM_SCREEN_BRIGHTNESS_BINS;
+                mScreenBrightnessLevelPowerEstimators[display][bin] =
+                        new UsageBasedPowerEstimator(binPowerMah);
+            }
+
+            mScreenDozePowerEstimators[display] = new UsageBasedPowerEstimator(
+                    powerProfile.getAveragePowerForOrdinal(POWER_GROUP_DISPLAY_AMBIENT, display));
+        }
+    }
+
+    private boolean unpackPowerStatsDescriptor(PowerStats.Descriptor descriptor) {
+        if (descriptor == null) {
+            return false;
+        }
+
+        if (descriptor.equals(mLastUsedDescriptor)) {
+            return true;
+        }
+
+        mLastUsedDescriptor = descriptor;
+        mStatsLayout = new ScreenPowerStatsLayout(descriptor);
+        if (mStatsLayout.getDisplayCount() != mDisplayCount) {
+            Slog.e(TAG, "Incompatible number of displays: " + mStatsLayout.getDisplayCount()
+                    + ", expected: " + mDisplayCount);
+            return false;
+        }
+
+        mTmpDeviceStatsArray = new long[descriptor.statsArrayLength];
+        mTmpUidStatsArray = new long[descriptor.uidStatsArrayLength];
+        return true;
+    }
+
+    @Override
+    void finish(PowerComponentAggregatedPowerStats stats, long timestampMs) {
+        if (!unpackPowerStatsDescriptor(stats.getPowerStatsDescriptor())) {
+            return;
+        }
+
+        if (mPlan == null) {
+            mPlan = new PowerEstimationPlan(stats.getConfig());
+        }
+
+        computeDevicePowerEstimates(stats);
+        combineDeviceStateEstimates();
+
+        List<Integer> uids = new ArrayList<>();
+        stats.collectUids(uids);
+
+        if (!uids.isEmpty()) {
+            computeUidPowerEstimates(stats, uids);
+        }
+        mPlan.resetIntermediates();
+    }
+
+    private void computeDevicePowerEstimates(PowerComponentAggregatedPowerStats stats) {
+        for (int i = mPlan.deviceStateEstimations.size() - 1; i >= 0; i--) {
+            DeviceStateEstimation estimation = mPlan.deviceStateEstimations.get(i);
+            if (!stats.getDeviceStats(mTmpDeviceStatsArray, estimation.stateValues)) {
+                continue;
+            }
+
+            if (estimation.stateValues[STATE_SCREEN] == SCREEN_STATE_ON) {
+                double power;
+                if (mStatsLayout.getEnergyConsumerCount() > 0) {
+                    power = uCtoMah(mStatsLayout.getConsumedEnergy(mTmpDeviceStatsArray, 0));
+                } else {
+                    power = 0;
+                    for (int display = 0; display < mStatsLayout.getDisplayCount(); display++) {
+                        power += computeDisplayPower(mTmpDeviceStatsArray, display);
+                    }
+                }
+                mStatsLayout.setDevicePowerEstimate(mTmpDeviceStatsArray, power);
+                Intermediates intermediates = new Intermediates();
+                intermediates.power = power;
+                estimation.intermediates = intermediates;
+            } else {
+                double power = 0;
+                if (mStatsLayout.getEnergyConsumerCount() > 0) {
+                    power = uCtoMah(mStatsLayout.getConsumedEnergy(mTmpDeviceStatsArray, 0));
+                } else {
+                    for (int display = 0; display < mStatsLayout.getDisplayCount(); display++) {
+                        power += mScreenDozePowerEstimators[display].calculatePower(
+                                mStatsLayout.getScreenDozeDuration(mTmpDeviceStatsArray, display));
+                    }
+                }
+                mStatsLayout.setScreenDozePowerEstimate(mTmpDeviceStatsArray, power);
+            }
+
+            stats.setDeviceStats(estimation.stateValues, mTmpDeviceStatsArray);
+        }
+    }
+
+    private double computeDisplayPower(long[] stats, int display) {
+        double power = mScreenOnPowerEstimators[display]
+                .calculatePower(mStatsLayout.getScreenOnDuration(stats, display));
+        for (int bin = 0; bin < BatteryStats.NUM_SCREEN_BRIGHTNESS_BINS; bin++) {
+            power += mScreenBrightnessLevelPowerEstimators[display][bin]
+                    .calculatePower(mStatsLayout.getBrightnessLevelDuration(stats, display, bin));
+        }
+        return power;
+    }
+
+    /**
+     * Combine power estimates before distributing them proportionally to UIDs.
+     */
+    private void combineDeviceStateEstimates() {
+        for (int i = mPlan.combinedDeviceStateEstimations.size() - 1; i >= 0; i--) {
+            CombinedDeviceStateEstimate cdse = mPlan.combinedDeviceStateEstimations.get(i);
+            List<DeviceStateEstimation> deviceStateEstimations = cdse.deviceStateEstimations;
+            double power = 0;
+            for (int j = deviceStateEstimations.size() - 1; j >= 0; j--) {
+                DeviceStateEstimation dse = deviceStateEstimations.get(j);
+                Intermediates intermediates = (Intermediates) dse.intermediates;
+                if (intermediates != null) {
+                    power += intermediates.power;
+                }
+            }
+            if (power != 0) {
+                Intermediates cdseIntermediates = new Intermediates();
+                cdseIntermediates.power = power;
+                cdse.intermediates = cdseIntermediates;
+            }
+        }
+    }
+
+    private void computeUidPowerEstimates(PowerComponentAggregatedPowerStats stats,
+            List<Integer> uids) {
+        int[] uidStateValues = new int[stats.getConfig().getUidStateConfig().length];
+        uidStateValues[STATE_SCREEN] = SCREEN_STATE_ON;
+        uidStateValues[STATE_PROCESS_STATE] = PROCESS_STATE_ANY;
+
+        for (int i = mPlan.uidStateEstimates.size() - 1; i >= 0; i--) {
+            UidStateEstimate uidStateEstimate = mPlan.uidStateEstimates.get(i);
+            Intermediates intermediates =
+                    (Intermediates) uidStateEstimate.combinedDeviceStateEstimate.intermediates;
+            int[] deviceStateValues = uidStateEstimate.combinedDeviceStateEstimate
+                    .stateValues;
+            if (deviceStateValues[STATE_SCREEN] != SCREEN_STATE_ON
+                    || intermediates == null) {
+                continue;
+            }
+
+            uidStateValues[STATE_POWER] = deviceStateValues[STATE_POWER];
+
+            long totalTopActivityDuration = 0;
+            for (int j = uids.size() - 1; j >= 0; j--) {
+                int uid = uids.get(j);
+                if (stats.getUidStats(mTmpUidStatsArray, uid, uidStateValues)) {
+                    totalTopActivityDuration +=
+                            mStatsLayout.getUidTopActivityDuration(mTmpUidStatsArray);
+                }
+            }
+
+            if (totalTopActivityDuration == 0) {
+                return;
+            }
+
+            for (int j = uids.size() - 1; j >= 0; j--) {
+                int uid = uids.get(j);
+                if (stats.getUidStats(mTmpUidStatsArray, uid, uidStateValues)) {
+                    long duration = mStatsLayout.getUidTopActivityDuration(mTmpUidStatsArray);
+                    double power = intermediates.power * duration / totalTopActivityDuration;
+                    mStatsLayout.setUidPowerEstimate(mTmpUidStatsArray, power);
+                    stats.setUidStats(uid, uidStateValues, mTmpUidStatsArray);
+                }
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/power/stats/SensorPowerStatsLayout.java b/services/core/java/com/android/server/power/stats/SensorPowerStatsLayout.java
new file mode 100644
index 0000000..e66cd39
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/SensorPowerStatsLayout.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.power.stats;
+
+import android.os.PersistableBundle;
+import android.util.Slog;
+import android.util.SparseIntArray;
+
+public class SensorPowerStatsLayout extends PowerStatsLayout {
+    private static final String TAG = "SensorPowerStatsLayout";
+    private static final String EXTRA_DEVICE_SENSOR_HANDLES = "dsh";
+    private static final String EXTRA_UID_SENSOR_POSITIONS = "usp";
+
+    private final SparseIntArray mSensorPositions = new SparseIntArray();
+
+    void addUidSensorSection(int handle, String label) {
+        mSensorPositions.put(handle, addUidSection(1, label, FLAG_OPTIONAL));
+    }
+
+    /**
+     * Returns the position in the uid stats array of the duration element corresponding
+     * to the specified sensor identified by its handle.
+     */
+    public int getUidSensorDurationPosition(int handle) {
+        return mSensorPositions.get(handle, UNSUPPORTED);
+    }
+
+    /**
+     * Adds the specified duration to the accumulated timer for the specified sensor.
+     */
+    public void addUidSensorDuration(long[] stats, int handle, long durationMs) {
+        int position = mSensorPositions.get(handle, UNSUPPORTED);
+        if (position == UNSUPPORTED) {
+            Slog.e(TAG, "Unknown sensor: " + handle);
+            return;
+        }
+        stats[position] += durationMs;
+    }
+
+    @Override
+    public void toExtras(PersistableBundle extras) {
+        super.toExtras(extras);
+
+        int[] handlers = new int[mSensorPositions.size()];
+        int[] uidDurationPositions = new int[mSensorPositions.size()];
+
+        for (int i = 0; i < mSensorPositions.size(); i++) {
+            handlers[i] = mSensorPositions.keyAt(i);
+            uidDurationPositions[i] = mSensorPositions.valueAt(i);
+        }
+
+        extras.putIntArray(EXTRA_DEVICE_SENSOR_HANDLES, handlers);
+        extras.putIntArray(EXTRA_UID_SENSOR_POSITIONS, uidDurationPositions);
+    }
+
+    @Override
+    public void fromExtras(PersistableBundle extras) {
+        super.fromExtras(extras);
+
+        int[] handlers = extras.getIntArray(EXTRA_DEVICE_SENSOR_HANDLES);
+        int[] uidDurationPositions = extras.getIntArray(EXTRA_UID_SENSOR_POSITIONS);
+
+        for (int i = 0; i < handlers.length; i++) {
+            mSensorPositions.put(handlers[i], uidDurationPositions[i]);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/power/stats/SensorPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/SensorPowerStatsProcessor.java
new file mode 100644
index 0000000..5bd3288
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/SensorPowerStatsProcessor.java
@@ -0,0 +1,312 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.power.stats;
+
+import android.hardware.Sensor;
+import android.hardware.SensorManager;
+import android.os.BatteryConsumer;
+import android.os.BatteryStats;
+import android.os.PersistableBundle;
+import android.util.Slog;
+import android.util.SparseArray;
+
+import com.android.internal.os.PowerStats;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.List;
+import java.util.function.Supplier;
+
+public class SensorPowerStatsProcessor extends PowerStatsProcessor {
+    private static final String TAG = "SensorPowerStatsProcessor";
+    private static final String ANDROID_SENSOR_TYPE_PREFIX = "android.sensor.";
+
+    private static final double MILLIS_IN_HOUR = 1000.0 * 60 * 60;
+    private static final String SENSOR_EVENT_TAG_PREFIX = "sensor:0x";
+    private final Supplier<SensorManager> mSensorManagerSupplier;
+
+    private static final long INITIAL_TIMESTAMP = -1;
+    private SensorManager mSensorManager;
+    private SensorPowerStatsLayout mStatsLayout;
+    private PowerStats mPowerStats;
+    private boolean mIsInitialized;
+    private PowerStats.Descriptor mDescriptor;
+    private long mLastUpdateTimestamp;
+    private PowerEstimationPlan mPlan;
+
+    private static class SensorState {
+        public int sensorHandle;
+        public boolean stateOn;
+        public int uid;
+        public long startTime = INITIAL_TIMESTAMP;
+    }
+
+    private static class Intermediates {
+        public double power;
+    }
+
+    private final SparseArray<SensorState> mSensorStates = new SparseArray<>();
+    private long[] mTmpDeviceStatsArray;
+    private long[] mTmpUidStatsArray;
+
+    public SensorPowerStatsProcessor(Supplier<SensorManager> sensorManagerSupplier) {
+        mSensorManagerSupplier = sensorManagerSupplier;
+    }
+
+    private boolean ensureInitialized() {
+        if (mIsInitialized) {
+            return true;
+        }
+
+        mSensorManager = mSensorManagerSupplier.get();
+        if (mSensorManager == null) {
+            return false;
+        }
+
+        mStatsLayout = new SensorPowerStatsLayout();
+        List<Sensor> sensorList = new ArrayList<>(mSensorManager.getSensorList(Sensor.TYPE_ALL));
+        sensorList.sort(Comparator.comparingInt(Sensor::getId));
+        for (int i = 0; i < sensorList.size(); i++) {
+            Sensor sensor = sensorList.get(i);
+            String label = makeLabel(sensor, sensorList);
+            mStatsLayout.addUidSensorSection(sensor.getHandle(), label);
+        }
+        mStatsLayout.addUidSectionPowerEstimate();
+        mStatsLayout.addDeviceSectionPowerEstimate();
+
+        PersistableBundle extras = new PersistableBundle();
+        mStatsLayout.toExtras(extras);
+        mDescriptor = new PowerStats.Descriptor(
+                BatteryConsumer.POWER_COMPONENT_SENSORS, mStatsLayout.getDeviceStatsArrayLength(),
+                null, 0, mStatsLayout.getUidStatsArrayLength(),
+                extras);
+
+        mPowerStats = new PowerStats(mDescriptor);
+        mTmpUidStatsArray = new long[mDescriptor.uidStatsArrayLength];
+        mTmpDeviceStatsArray = new long[mDescriptor.statsArrayLength];
+
+        mIsInitialized = true;
+        return true;
+    }
+
+    private String makeLabel(Sensor sensor, List<Sensor> sensorList) {
+        int type = sensor.getType();
+        String label = sensor.getStringType();
+
+        boolean isSingleton = true;
+        for (int i = sensorList.size() - 1; i >= 0; i--) {
+            Sensor s = sensorList.get(i);
+            if (s == sensor) {
+                continue;
+            }
+            if (s.getType() == type) {
+                isSingleton = false;
+                break;
+            }
+        }
+        if (!isSingleton) {
+            StringBuilder sb = new StringBuilder(label).append('.');
+            if (sensor.getId() > 0) { // 0 and -1 are reserved
+                sb.append(sensor.getId());
+            } else {
+                sb.append(sensor.getName());
+            }
+            label = sb.toString();
+        }
+        if (label.startsWith(ANDROID_SENSOR_TYPE_PREFIX)) {
+            label = label.substring(ANDROID_SENSOR_TYPE_PREFIX.length());
+        }
+        return label.replace(' ', '_');
+    }
+
+    @Override
+    void start(PowerComponentAggregatedPowerStats stats, long timestampMs) {
+        if (!ensureInitialized()) {
+            return;
+        }
+
+        // Establish a baseline at the beginning of an accumulation pass
+        mLastUpdateTimestamp = timestampMs;
+        flushPowerStats(stats, timestampMs);
+    }
+
+    @Override
+    void noteStateChange(PowerComponentAggregatedPowerStats stats, BatteryStats.HistoryItem item) {
+        if (!mIsInitialized) {
+            return;
+        }
+
+        if (item.eventTag == null || item.eventTag.string == null
+                || !item.eventTag.string.startsWith(SENSOR_EVENT_TAG_PREFIX)) {
+            return;
+        }
+
+        int sensorHandle;
+        try {
+            sensorHandle = Integer.parseInt(item.eventTag.string, SENSOR_EVENT_TAG_PREFIX.length(),
+                    item.eventTag.string.length(), 16);
+        } catch (NumberFormatException e) {
+            Slog.wtf(TAG, "Bad format of event tag: " + item.eventTag.string);
+            return;
+        }
+
+        SensorState sensor = mSensorStates.get(sensorHandle);
+        if (sensor == null) {
+            sensor = new SensorState();
+            sensor.sensorHandle = sensorHandle;
+            mSensorStates.put(sensorHandle, sensor);
+        }
+
+        int uid = item.eventTag.uid;
+        boolean sensorOn = (item.states & BatteryStats.HistoryItem.STATE_SENSOR_ON_FLAG) != 0;
+        if (sensorOn) {
+            if (!sensor.stateOn) {
+                sensor.stateOn = true;
+                sensor.uid = uid;
+                sensor.startTime = item.time;
+            } else if (sensor.uid != uid) {
+                recordUsageDuration(sensor, item.time);
+                sensor.uid = uid;
+            }
+        } else {
+            if (sensor.stateOn) {
+                recordUsageDuration(sensor, item.time);
+                sensor.stateOn = false;
+            }
+        }
+    }
+
+    @Override
+    void finish(PowerComponentAggregatedPowerStats stats, long timestampMs) {
+        if (!mIsInitialized) {
+            return;
+        }
+
+        for (int i = mSensorStates.size() - 1; i >= 0; i--) {
+            SensorState sensor = mSensorStates.valueAt(i);
+            if (sensor.stateOn) {
+                recordUsageDuration(sensor, timestampMs);
+            }
+        }
+        flushPowerStats(stats, timestampMs);
+
+        if (mPlan == null) {
+            mPlan = new PowerEstimationPlan(stats.getConfig());
+        }
+
+        List<Integer> uids = new ArrayList<>();
+        stats.collectUids(uids);
+
+        computeUidPowerEstimates(stats, uids);
+        computeDevicePowerEstimates(stats);
+
+        mPlan.resetIntermediates();
+    }
+
+    protected void recordUsageDuration(SensorState sensorState, long time) {
+        long durationMs = Math.max(0, time - sensorState.startTime);
+        if (durationMs > 0) {
+            long[] uidStats = mPowerStats.uidStats.get(sensorState.uid);
+            if (uidStats == null) {
+                uidStats = new long[mDescriptor.uidStatsArrayLength];
+                mPowerStats.uidStats.put(sensorState.uid, uidStats);
+            }
+            mStatsLayout.addUidSensorDuration(uidStats, sensorState.sensorHandle, durationMs);
+        }
+        sensorState.startTime = time;
+    }
+
+    private void flushPowerStats(PowerComponentAggregatedPowerStats stats, long timestamp) {
+        mPowerStats.durationMs = timestamp - mLastUpdateTimestamp;
+        stats.addPowerStats(mPowerStats, timestamp);
+
+        Arrays.fill(mPowerStats.stats, 0);
+        mPowerStats.uidStats.clear();
+        mLastUpdateTimestamp = timestamp;
+    }
+
+    private void computeUidPowerEstimates(PowerComponentAggregatedPowerStats stats,
+            List<Integer> uids) {
+        List<Sensor> sensorList = mSensorManager.getSensorList(Sensor.TYPE_ALL);
+        int[] uidSensorDurationPositions = new int[sensorList.size()];
+        double[] sensorPower = new double[sensorList.size()];
+        for (int i = sensorList.size() - 1; i >= 0; i--) {
+            Sensor sensor = sensorList.get(i);
+            uidSensorDurationPositions[i] =
+                    mStatsLayout.getUidSensorDurationPosition(sensor.getHandle());
+            sensorPower[i] = sensor.getPower() / MILLIS_IN_HOUR;
+        }
+
+        for (int i = mPlan.uidStateEstimates.size() - 1; i >= 0; i--) {
+            UidStateEstimate uidStateEstimate = mPlan.uidStateEstimates.get(i);
+            List<UidStateProportionalEstimate> proportionalEstimates =
+                    uidStateEstimate.proportionalEstimates;
+            for (int j = proportionalEstimates.size() - 1; j >= 0; j--) {
+                UidStateProportionalEstimate proportionalEstimate = proportionalEstimates.get(j);
+                for (int k = uids.size() - 1; k >= 0; k--) {
+                    int uid = uids.get(k);
+                    if (!stats.getUidStats(mTmpUidStatsArray, uid,
+                            proportionalEstimate.stateValues)) {
+                        continue;
+                    }
+                    double power = 0;
+                    for (int m = 0; m < uidSensorDurationPositions.length; m++) {
+                        int position = uidSensorDurationPositions[m];
+                        if (position == PowerStatsLayout.UNSUPPORTED
+                                || mTmpUidStatsArray[position] == 0) {
+                            continue;
+                        }
+                        power += sensorPower[m] * mTmpUidStatsArray[position];
+                    }
+                    if (power == 0) {
+                        continue;
+                    }
+
+                    mStatsLayout.setUidPowerEstimate(mTmpUidStatsArray, power);
+                    stats.setUidStats(uid, proportionalEstimate.stateValues, mTmpUidStatsArray);
+
+                    Intermediates intermediates = (Intermediates) uidStateEstimate
+                            .combinedDeviceStateEstimate.intermediates;
+                    if (intermediates == null) {
+                        intermediates = new Intermediates();
+                        uidStateEstimate.combinedDeviceStateEstimate.intermediates = intermediates;
+                    }
+                    intermediates.power += power;
+                }
+            }
+        }
+    }
+
+    private void computeDevicePowerEstimates(PowerComponentAggregatedPowerStats stats) {
+        for (int i = mPlan.combinedDeviceStateEstimations.size() - 1; i >= 0; i--) {
+            CombinedDeviceStateEstimate estimation =
+                    mPlan.combinedDeviceStateEstimations.get(i);
+            if (estimation.intermediates == null) {
+                continue;
+            }
+
+            if (!stats.getDeviceStats(mTmpDeviceStatsArray, estimation.stateValues)) {
+                continue;
+            }
+
+            mStatsLayout.setDevicePowerEstimate(mTmpDeviceStatsArray,
+                    ((Intermediates) estimation.intermediates).power);
+            stats.setDeviceStats(estimation.stateValues, mTmpDeviceStatsArray);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java b/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java
index 52ef87c..06a2565 100644
--- a/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java
+++ b/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java
@@ -130,6 +130,7 @@
 
 import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.KeepForWeakReference;
 import com.android.internal.camera.flags.Flags;
 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
 import com.android.internal.os.BackgroundThread;
@@ -1944,8 +1945,12 @@
     }
 
     private class CallStateHelper {
-        private OutgoingEmergencyStateCallback mEmergencyStateCallback;
-        private CallStateCallback mCallStateCallback;
+        // TelephonyCallback instances are only weakly referenced when registered, so we need
+        // to ensure these fields are kept during optimization to preserve lifecycle semantics.
+        @KeepForWeakReference
+        private final OutgoingEmergencyStateCallback mEmergencyStateCallback;
+        @KeepForWeakReference
+        private final CallStateCallback mCallStateCallback;
 
         private boolean mIsInEmergencyCall;
         private boolean mMicUnmutedForEmergencyCall;
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index bca81f52..331a594 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -64,6 +64,7 @@
 import static com.android.internal.util.FrameworkStatsLog.TIME_ZONE_DETECTOR_STATE__DETECTION_MODE__UNKNOWN;
 import static com.android.server.am.MemoryStatUtil.readMemoryStatFromFilesystem;
 import static com.android.server.stats.Flags.addMobileBytesTransferByProcStatePuller;
+import static com.android.server.stats.Flags.applyNetworkStatsPollRateLimit;
 import static com.android.server.stats.pull.IonMemoryUtil.readProcessSystemIonHeapSizesFromDebugfs;
 import static com.android.server.stats.pull.IonMemoryUtil.readSystemIonHeapSizeFromDebugfs;
 import static com.android.server.stats.pull.ProcfsMemoryUtil.getProcessCmdlines;
@@ -298,6 +299,13 @@
      */
     private static final long NETSTATS_UID_DEFAULT_BUCKET_DURATION_MS = HOURS.toMillis(2);
 
+    /**
+     * Polling NetworkStats is a heavy operation and it should be done sparingly. Atom pulls may
+     * happen in bursts, but these should be infrequent. The poll rate limit ensures that data is
+     * sufficiently fresh (i.e. not stale) while reducing system load during atom pull bursts.
+     */
+    private static final long NETSTATS_POLL_RATE_LIMIT_MS = 15000;
+
     private static final int CPU_TIME_PER_THREAD_FREQ_MAX_NUM_FREQUENCIES = 8;
     private static final int OP_FLAGS_PULLED = OP_FLAG_SELF | OP_FLAG_TRUSTED_PROXIED;
     private static final String COMMON_PERMISSION_PREFIX = "android.permission.";
@@ -415,6 +423,9 @@
     @GuardedBy("mDataBytesTransferLock")
     private final ArrayList<NetworkStatsExt> mNetworkStatsBaselines = new ArrayList<>();
 
+    @GuardedBy("mDataBytesTransferLock")
+    private long mLastNetworkStatsPollTime = -NETSTATS_POLL_RATE_LIMIT_MS;
+
     // Listener for monitoring subscriptions changed event.
     private StatsSubscriptionsListener mStatsSubscriptionsListener;
     // List that stores SubInfo of subscriptions that ever appeared since boot.
@@ -1063,24 +1074,26 @@
         // Initialize NetworkStats baselines.
         synchronized (mDataBytesTransferLock) {
             mNetworkStatsBaselines.addAll(
-                    collectNetworkStatsSnapshotForAtom(FrameworkStatsLog.WIFI_BYTES_TRANSFER));
+                    collectNetworkStatsSnapshotForAtomLocked(
+                            FrameworkStatsLog.WIFI_BYTES_TRANSFER));
             mNetworkStatsBaselines.addAll(
-                    collectNetworkStatsSnapshotForAtom(
+                    collectNetworkStatsSnapshotForAtomLocked(
                             FrameworkStatsLog.WIFI_BYTES_TRANSFER_BY_FG_BG));
             mNetworkStatsBaselines.addAll(
-                    collectNetworkStatsSnapshotForAtom(FrameworkStatsLog.MOBILE_BYTES_TRANSFER));
-            mNetworkStatsBaselines.addAll(collectNetworkStatsSnapshotForAtom(
+                    collectNetworkStatsSnapshotForAtomLocked(
+                            FrameworkStatsLog.MOBILE_BYTES_TRANSFER));
+            mNetworkStatsBaselines.addAll(collectNetworkStatsSnapshotForAtomLocked(
                     FrameworkStatsLog.MOBILE_BYTES_TRANSFER_BY_FG_BG));
-            mNetworkStatsBaselines.addAll(collectNetworkStatsSnapshotForAtom(
+            mNetworkStatsBaselines.addAll(collectNetworkStatsSnapshotForAtomLocked(
                     FrameworkStatsLog.BYTES_TRANSFER_BY_TAG_AND_METERED));
             mNetworkStatsBaselines.addAll(
-                    collectNetworkStatsSnapshotForAtom(
+                    collectNetworkStatsSnapshotForAtomLocked(
                             FrameworkStatsLog.DATA_USAGE_BYTES_TRANSFER));
             mNetworkStatsBaselines.addAll(
-                    collectNetworkStatsSnapshotForAtom(
+                    collectNetworkStatsSnapshotForAtomLocked(
                             FrameworkStatsLog.OEM_MANAGED_BYTES_TRANSFER));
             if (canQueryTypeProxy) {
-                mNetworkStatsBaselines.addAll(collectNetworkStatsSnapshotForAtom(
+                mNetworkStatsBaselines.addAll(collectNetworkStatsSnapshotForAtomLocked(
                         FrameworkStatsLog.PROXY_BYTES_TRANSFER_BY_FG_BG));
             }
         }
@@ -1243,12 +1256,14 @@
         );
     }
 
+    @GuardedBy("mDataBytesTransferLock")
     @NonNull
-    private List<NetworkStatsExt> collectNetworkStatsSnapshotForAtom(int atomTag) {
+    private List<NetworkStatsExt> collectNetworkStatsSnapshotForAtomLocked(int atomTag) {
         List<NetworkStatsExt> ret = new ArrayList<>();
         switch (atomTag) {
             case FrameworkStatsLog.WIFI_BYTES_TRANSFER: {
-                final NetworkStats stats = getUidNetworkStatsSnapshotForTransport(TRANSPORT_WIFI);
+                final NetworkStats stats = getUidNetworkStatsSnapshotForTransportLocked(
+                        TRANSPORT_WIFI);
                 if (stats != null) {
                     ret.add(new NetworkStatsExt(sliceNetworkStatsByUid(stats),
                             new int[]{TRANSPORT_WIFI}, /*slicedByFgbg=*/false));
@@ -1256,7 +1271,8 @@
                 break;
             }
             case FrameworkStatsLog.WIFI_BYTES_TRANSFER_BY_FG_BG: {
-                final NetworkStats stats = getUidNetworkStatsSnapshotForTransport(TRANSPORT_WIFI);
+                final NetworkStats stats = getUidNetworkStatsSnapshotForTransportLocked(
+                        TRANSPORT_WIFI);
                 if (stats != null) {
                     ret.add(new NetworkStatsExt(sliceNetworkStatsByUidAndFgbg(stats),
                             new int[]{TRANSPORT_WIFI}, /*slicedByFgbg=*/true));
@@ -1265,7 +1281,7 @@
             }
             case FrameworkStatsLog.MOBILE_BYTES_TRANSFER: {
                 final NetworkStats stats =
-                        getUidNetworkStatsSnapshotForTransport(TRANSPORT_CELLULAR);
+                        getUidNetworkStatsSnapshotForTransportLocked(TRANSPORT_CELLULAR);
                 if (stats != null) {
                     ret.add(new NetworkStatsExt(sliceNetworkStatsByUid(stats),
                             new int[]{TRANSPORT_CELLULAR}, /*slicedByFgbg=*/false));
@@ -1274,7 +1290,7 @@
             }
             case FrameworkStatsLog.MOBILE_BYTES_TRANSFER_BY_FG_BG: {
                 final NetworkStats stats =
-                        getUidNetworkStatsSnapshotForTransport(TRANSPORT_CELLULAR);
+                        getUidNetworkStatsSnapshotForTransportLocked(TRANSPORT_CELLULAR);
                 if (stats != null) {
                     ret.add(new NetworkStatsExt(sliceNetworkStatsByUidAndFgbg(stats),
                             new int[]{TRANSPORT_CELLULAR}, /*slicedByFgbg=*/true));
@@ -1282,10 +1298,10 @@
                 break;
             }
             case FrameworkStatsLog.PROXY_BYTES_TRANSFER_BY_FG_BG: {
-                final NetworkStats stats = getUidNetworkStatsSnapshotForTemplate(
+                final NetworkStats stats = getUidNetworkStatsSnapshotForTemplateLocked(
                         new NetworkTemplate.Builder(MATCH_PROXY).build(),  /*includeTags=*/false);
                 if (stats != null) {
-                    ret.add(new NetworkStatsExt(sliceNetworkStatsByUidTagAndMetered(stats),
+                    ret.add(new NetworkStatsExt(sliceNetworkStatsByUidAndFgbg(stats),
                             new int[]{TRANSPORT_BLUETOOTH},
                             /*slicedByFgbg=*/true, /*slicedByTag=*/false,
                             /*slicedByMetered=*/false, TelephonyManager.NETWORK_TYPE_UNKNOWN,
@@ -1294,9 +1310,9 @@
                 break;
             }
             case FrameworkStatsLog.BYTES_TRANSFER_BY_TAG_AND_METERED: {
-                final NetworkStats wifiStats = getUidNetworkStatsSnapshotForTemplate(
+                final NetworkStats wifiStats = getUidNetworkStatsSnapshotForTemplateLocked(
                         new NetworkTemplate.Builder(MATCH_WIFI).build(), /*includeTags=*/true);
-                final NetworkStats cellularStats = getUidNetworkStatsSnapshotForTemplate(
+                final NetworkStats cellularStats = getUidNetworkStatsSnapshotForTemplateLocked(
                         new NetworkTemplate.Builder(MATCH_MOBILE)
                                 .setMeteredness(METERED_YES).build(), /*includeTags=*/true);
                 if (wifiStats != null && cellularStats != null) {
@@ -1311,12 +1327,12 @@
             }
             case FrameworkStatsLog.DATA_USAGE_BYTES_TRANSFER: {
                 for (final SubInfo subInfo : mHistoricalSubs) {
-                    ret.addAll(getDataUsageBytesTransferSnapshotForSub(subInfo));
+                    ret.addAll(getDataUsageBytesTransferSnapshotForSubLocked(subInfo));
                 }
                 break;
             }
             case FrameworkStatsLog.OEM_MANAGED_BYTES_TRANSFER: {
-                ret.addAll(getDataUsageBytesTransferSnapshotForOemManaged());
+                ret.addAll(getDataUsageBytesTransferSnapshotForOemManagedLocked());
                 break;
             }
             default:
@@ -1325,8 +1341,9 @@
         return ret;
     }
 
+    @GuardedBy("mDataBytesTransferLock")
     private int pullDataBytesTransferLocked(int atomTag, @NonNull List<StatsEvent> pulledData) {
-        final List<NetworkStatsExt> current = collectNetworkStatsSnapshotForAtom(atomTag);
+        final List<NetworkStatsExt> current = collectNetworkStatsSnapshotForAtomLocked(atomTag);
 
         if (current == null) {
             Slog.e(TAG, "current snapshot is null for " + atomTag + ", return.");
@@ -1459,8 +1476,9 @@
         }
     }
 
+    @GuardedBy("mDataBytesTransferLock")
     @NonNull
-    private List<NetworkStatsExt> getDataUsageBytesTransferSnapshotForOemManaged() {
+    private List<NetworkStatsExt> getDataUsageBytesTransferSnapshotForOemManagedLocked() {
         final List<Pair<Integer, Integer>> matchRulesAndTransports = List.of(
                 new Pair(MATCH_ETHERNET, TRANSPORT_ETHERNET),
                 new Pair(MATCH_MOBILE, TRANSPORT_CELLULAR),
@@ -1479,7 +1497,8 @@
                 // Thus, specifying networks through their identifiers are not needed.
                 final NetworkTemplate template = new NetworkTemplate.Builder(matchRule)
                         .setOemManaged(oemManaged).build();
-                final NetworkStats stats = getUidNetworkStatsSnapshotForTemplate(template, false);
+                final NetworkStats stats = getUidNetworkStatsSnapshotForTemplateLocked(
+                        template, false);
                 final Integer transport = ruleAndTransport.second;
                 if (stats != null) {
                     ret.add(new NetworkStatsExt(sliceNetworkStatsByUidAndFgbg(stats),
@@ -1496,8 +1515,9 @@
     /**
      * Create a snapshot of NetworkStats for a given transport.
      */
+    @GuardedBy("mDataBytesTransferLock")
     @Nullable
-    private NetworkStats getUidNetworkStatsSnapshotForTransport(int transport) {
+    private NetworkStats getUidNetworkStatsSnapshotForTransportLocked(int transport) {
         NetworkTemplate template = null;
         switch (transport) {
             case TRANSPORT_CELLULAR:
@@ -1510,7 +1530,7 @@
             default:
                 Log.wtf(TAG, "Unexpected transport.");
         }
-        return getUidNetworkStatsSnapshotForTemplate(template, /*includeTags=*/false);
+        return getUidNetworkStatsSnapshotForTemplateLocked(template, /*includeTags=*/false);
     }
 
     /**
@@ -1534,8 +1554,9 @@
      * Note that this should be only used to calculate diff since the snapshot might contains
      * some traffic before boot.
      */
+    @GuardedBy("mDataBytesTransferLock")
     @Nullable
-    private NetworkStats getUidNetworkStatsSnapshotForTemplate(
+    private NetworkStats getUidNetworkStatsSnapshotForTemplateLocked(
             @NonNull NetworkTemplate template, boolean includeTags) {
         final long elapsedMillisSinceBoot = SystemClock.elapsedRealtime();
         final long currentTimeInMillis = MICROSECONDS.toMillis(SystemClock.currentTimeMicro());
@@ -1547,13 +1568,19 @@
         final long startTime = currentTimeInMillis - elapsedMillisSinceBoot - bucketDuration;
         final long endTime = currentTimeInMillis + bucketDuration;
 
-        // TODO (b/156313635): This is short-term hack to allow perfd gets updated networkStats
-        //  history when query in every second in order to show realtime statistics. However,
-        //  this is not a good long-term solution since NetworkStatsService will make frequent
-        //  I/O and also block main thread when polling.
-        //  Consider making perfd queries NetworkStatsService directly.
-        if (template.getMatchRule() == MATCH_WIFI && template.getSubscriberIds().isEmpty()) {
-            getNetworkStatsManager().forceUpdate();
+        // NetworkStatsManager#forceUpdate updates stats for all networks
+        if (applyNetworkStatsPollRateLimit()) {
+            // The new way: rate-limit force-polling for all NetworkStats queries
+            if (elapsedMillisSinceBoot - mLastNetworkStatsPollTime >= NETSTATS_POLL_RATE_LIMIT_MS) {
+                mLastNetworkStatsPollTime = elapsedMillisSinceBoot;
+                getNetworkStatsManager().forceUpdate();
+            }
+        } else {
+            // The old way: force-poll only on WiFi queries. Data for other queries can be stale
+            // if there was no recent poll beforehand (e.g. for WiFi or scheduled poll)
+            if (template.getMatchRule() == MATCH_WIFI && template.getSubscriberIds().isEmpty()) {
+                getNetworkStatsManager().forceUpdate();
+            }
         }
 
         final android.app.usage.NetworkStats queryNonTaggedStats =
@@ -1572,8 +1599,9 @@
         return nonTaggedStats.add(taggedStats);
     }
 
+    @GuardedBy("mDataBytesTransferLock")
     @NonNull
-    private List<NetworkStatsExt> getDataUsageBytesTransferSnapshotForSub(
+    private List<NetworkStatsExt> getDataUsageBytesTransferSnapshotForSubLocked(
             @NonNull SubInfo subInfo) {
         final List<NetworkStatsExt> ret = new ArrayList<>();
         for (final int ratType : getAllCollapsedRatTypes()) {
@@ -1583,7 +1611,7 @@
                             .setRatType(ratType)
                             .setMeteredness(METERED_YES).build();
             final NetworkStats stats =
-                    getUidNetworkStatsSnapshotForTemplate(template, /*includeTags=*/false);
+                    getUidNetworkStatsSnapshotForTemplateLocked(template, /*includeTags=*/false);
             if (stats != null) {
                 ret.add(new NetworkStatsExt(sliceNetworkStatsByFgbg(stats),
                         new int[]{TRANSPORT_CELLULAR}, /*slicedByFgbg=*/true,
@@ -5273,6 +5301,13 @@
 
         @Override
         public void onSubscriptionsChanged() {
+            synchronized (mDataBytesTransferLock) {
+                onSubscriptionsChangedLocked();
+            }
+        }
+
+        @GuardedBy("mDataBytesTransferLock")
+        private void onSubscriptionsChangedLocked() {
             final List<SubscriptionInfo> currentSubs = mSm.getCompleteActiveSubscriptionInfoList();
             for (final SubscriptionInfo sub : currentSubs) {
                 final SubInfo match = CollectionUtils.find(mHistoricalSubs,
@@ -5295,12 +5330,11 @@
                         subscriberId, sub.isOpportunistic());
                 Slog.i(TAG, "subId " + subId + " added into historical sub list");
 
-                synchronized (mDataBytesTransferLock) {
-                    mHistoricalSubs.add(subInfo);
-                    // Since getting snapshot when pulling will also include data before boot,
-                    // query stats as baseline to prevent double count is needed.
-                    mNetworkStatsBaselines.addAll(getDataUsageBytesTransferSnapshotForSub(subInfo));
-                }
+                mHistoricalSubs.add(subInfo);
+                // Since getting snapshot when pulling will also include data before boot,
+                // query stats as baseline to prevent double count is needed.
+                mNetworkStatsBaselines.addAll(
+                        getDataUsageBytesTransferSnapshotForSubLocked(subInfo));
             }
         }
     }
diff --git a/services/core/java/com/android/server/stats/stats_flags.aconfig b/services/core/java/com/android/server/stats/stats_flags.aconfig
index 6faa273..f360837 100644
--- a/services/core/java/com/android/server/stats/stats_flags.aconfig
+++ b/services/core/java/com/android/server/stats/stats_flags.aconfig
@@ -8,3 +8,11 @@
     bug: "309512867"
     is_fixed_read_only: true
 }
+
+flag {
+    name: "apply_network_stats_poll_rate_limit"
+    namespace: "statsd"
+    description: "Apply a rate limit for polling network stats when pulling relevant atoms"
+    bug: "352495181"
+    is_fixed_read_only: true
+}
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
index e4f60ec..a4a29a0 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
@@ -54,15 +54,13 @@
      * Used by InputMethodManagerService to notify the IME status.
      *
      * @param displayId The display to which the IME is bound to.
-     * @param token The IME token.
      * @param vis Bit flags about the IME visibility.
      *            (e.g. {@link android.inputmethodservice.InputMethodService#IME_ACTIVE})
      * @param backDisposition Bit flags about the IME back disposition.
      *         (e.g. {@link android.inputmethodservice.InputMethodService#BACK_DISPOSITION_DEFAULT})
      * @param showImeSwitcher {@code true} when the IME switcher button should be shown.
      */
-    void setImeWindowStatus(int displayId, IBinder token, int vis,
-            int backDisposition, boolean showImeSwitcher);
+    void setImeWindowStatus(int displayId, int vis, int backDisposition, boolean showImeSwitcher);
 
     /**
      * See {@link android.app.StatusBarManager#setIcon(String, int, int, String)}.
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 85c8900..c3601b3c 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -534,9 +534,9 @@
         }
 
         @Override
-        public void setImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition,
+        public void setImeWindowStatus(int displayId, int vis, int backDisposition,
                 boolean showImeSwitcher) {
-            StatusBarManagerService.this.setImeWindowStatus(displayId, token, vis, backDisposition,
+            StatusBarManagerService.this.setImeWindowStatus(displayId, vis, backDisposition,
                     showImeSwitcher);
         }
 
@@ -1351,25 +1351,24 @@
     }
 
     @Override
-    public void setImeWindowStatus(int displayId, final IBinder token, final int vis,
-            final int backDisposition, final boolean showImeSwitcher) {
+    public void setImeWindowStatus(int displayId, final int vis, final int backDisposition,
+            final boolean showImeSwitcher) {
         enforceStatusBar();
 
         if (SPEW) {
-            Slog.d(TAG, "swetImeWindowStatus vis=" + vis + " backDisposition=" + backDisposition);
+            Slog.d(TAG, "setImeWindowStatus vis=" + vis + " backDisposition=" + backDisposition);
         }
 
         synchronized(mLock) {
             // In case of IME change, we need to call up setImeWindowStatus() regardless of
             // mImeWindowVis because mImeWindowVis may not have been set to false when the
             // previous IME was destroyed.
-            getUiState(displayId).setImeWindowState(vis, backDisposition, showImeSwitcher, token);
+            getUiState(displayId).setImeWindowState(vis, backDisposition, showImeSwitcher);
 
             mHandler.post(() -> {
                 if (mBar == null) return;
                 try {
-                    mBar.setImeWindowStatus(
-                            displayId, token, vis, backDisposition, showImeSwitcher);
+                    mBar.setImeWindowStatus(displayId, vis, backDisposition, showImeSwitcher);
                 } catch (RemoteException ex) { }
             });
         }
@@ -1422,7 +1421,6 @@
         private int mImeWindowVis = 0;
         private int mImeBackDisposition = 0;
         private boolean mShowImeSwitcher = false;
-        private IBinder mImeToken = null;
         private LetterboxDetails[] mLetterboxDetails = new LetterboxDetails[0];
 
         private void setBarAttributes(@Appearance int appearance,
@@ -1465,11 +1463,10 @@
         }
 
         private void setImeWindowState(final int vis, final int backDisposition,
-                final boolean showImeSwitcher, final IBinder token) {
+                final boolean showImeSwitcher) {
             mImeWindowVis = vis;
             mImeBackDisposition = backDisposition;
             mShowImeSwitcher = showImeSwitcher;
-            mImeToken = token;
         }
     }
 
@@ -1563,7 +1560,7 @@
             return new RegisterStatusBarResult(icons, gatherDisableActionsLocked(mCurrentUserId, 1),
                     state.mAppearance, state.mAppearanceRegions, state.mImeWindowVis,
                     state.mImeBackDisposition, state.mShowImeSwitcher,
-                    gatherDisableActionsLocked(mCurrentUserId, 2), state.mImeToken,
+                    gatherDisableActionsLocked(mCurrentUserId, 2),
                     state.mNavbarColorManagedByIme, state.mBehavior, state.mRequestedVisibleTypes,
                     state.mPackageName, state.mTransientBarTypes, state.mLetterboxDetails);
         }
@@ -1890,8 +1887,7 @@
         enforceStatusBarService();
         final long token = Binder.clearCallingIdentity();
         try {
-            // TODO(b/308479256): Check if hiding "all" IMEs is OK or not.
-            InputMethodManagerInternal.get().hideAllInputMethods(
+            InputMethodManagerInternal.get().hideInputMethod(
                     SoftInputShowHideReason.HIDE_BUBBLES, displayId);
         } finally {
             Binder.restoreCallingIdentity(token);
diff --git a/services/core/java/com/android/server/timezonedetector/location/ZoneInfoDbTimeZoneProviderEventPreProcessor.java b/services/core/java/com/android/server/timezonedetector/location/ZoneInfoDbTimeZoneProviderEventPreProcessor.java
index aa2b74e..58c3ba5 100644
--- a/services/core/java/com/android/server/timezonedetector/location/ZoneInfoDbTimeZoneProviderEventPreProcessor.java
+++ b/services/core/java/com/android/server/timezonedetector/location/ZoneInfoDbTimeZoneProviderEventPreProcessor.java
@@ -56,12 +56,17 @@
         // enables immediate failover to a secondary provider, one that might provide valid IDs for
         // the same location, which should provide better behavior than just ignoring the event.
         if (hasInvalidZones(event)) {
-            TimeZoneProviderStatus providerStatus = new TimeZoneProviderStatus.Builder(
-                    event.getTimeZoneProviderStatus())
-                    .setTimeZoneResolutionOperationStatus(OPERATION_STATUS_FAILED)
-                    .build();
-            return TimeZoneProviderEvent.createUncertainEvent(
-                    event.getCreationElapsedMillis(), providerStatus);
+            TimeZoneProviderStatus providerStatus = event.getTimeZoneProviderStatus();
+            TimeZoneProviderStatus.Builder providerStatusBuilder;
+            if (providerStatus != null) {
+                providerStatusBuilder = new TimeZoneProviderStatus.Builder(providerStatus);
+            } else {
+                providerStatusBuilder = new TimeZoneProviderStatus.Builder();
+            }
+            return TimeZoneProviderEvent.createUncertainEvent(event.getCreationElapsedMillis(),
+                    providerStatusBuilder
+                            .setTimeZoneResolutionOperationStatus(OPERATION_STATUS_FAILED)
+                            .build());
         }
 
         return event;
diff --git a/services/core/java/com/android/server/vibrator/AbstractComposedVibratorStep.java b/services/core/java/com/android/server/vibrator/AbstractComposedVibratorStep.java
new file mode 100644
index 0000000..b263159
--- /dev/null
+++ b/services/core/java/com/android/server/vibrator/AbstractComposedVibratorStep.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.vibrator;
+
+import android.os.SystemClock;
+import android.os.VibrationEffect;
+
+import java.util.List;
+
+/**
+ * Represent a step on a single vibrator that plays one or more segments from a
+ * {@link VibrationEffect.Composed} effect.
+ */
+abstract class AbstractComposedVibratorStep extends AbstractVibratorStep {
+    public final VibrationEffect.Composed effect;
+    public final int segmentIndex;
+
+    /**
+     * @param conductor          The {@link VibrationStepConductor} for these steps.
+     * @param startTime          The time to schedule this step in the conductor.
+     * @param controller         The vibrator that is playing the effect.
+     * @param effect             The effect being played in this step.
+     * @param index              The index of the next segment to be played by this step
+     * @param pendingVibratorOffDeadline The time the vibrator is expected to complete any
+     *                           previous vibration and turn off. This is used to allow this step to
+     *                           be triggered when the completion callback is received, and can
+     *                           be used to play effects back-to-back.
+     */
+    AbstractComposedVibratorStep(VibrationStepConductor conductor, long startTime,
+            VibratorController controller, VibrationEffect.Composed effect, int index,
+            long pendingVibratorOffDeadline) {
+        super(conductor, startTime, controller, pendingVibratorOffDeadline);
+        this.effect = effect;
+        this.segmentIndex = index;
+    }
+
+    /**
+     * Return the {@link VibrationStepConductor#nextVibrateStep} with start and off timings
+     * calculated from {@link #getVibratorOnDuration()} based on the current
+     * {@link SystemClock#uptimeMillis()} and jumping all played segments from the effect.
+     */
+    protected List<Step> nextSteps(int segmentsPlayed) {
+        // Schedule next steps to run right away.
+        long nextStartTime = SystemClock.uptimeMillis();
+        if (mVibratorOnResult > 0) {
+            // Vibrator was turned on by this step, with mVibratorOnResult as the duration.
+            // Schedule next steps for right after the vibration finishes.
+            nextStartTime += mVibratorOnResult;
+        }
+        return nextSteps(nextStartTime, segmentsPlayed);
+    }
+
+    /**
+     * Return the {@link VibrationStepConductor#nextVibrateStep} with given start time,
+     * which might be calculated independently, and jumping all played segments from the effect.
+     *
+     * <p>This should be used when the vibrator on/off state is not responsible for the step
+     * execution timing, e.g. while playing the vibrator amplitudes.
+     */
+    protected List<Step> nextSteps(long nextStartTime, int segmentsPlayed) {
+        int nextSegmentIndex = segmentIndex + segmentsPlayed;
+        int effectSize = effect.getSegments().size();
+        int repeatIndex = effect.getRepeatIndex();
+        if (nextSegmentIndex >= effectSize && repeatIndex >= 0) {
+            // Count the loops that were played.
+            int loopSize = effectSize - repeatIndex;
+            int loopSegmentsPlayed = nextSegmentIndex - repeatIndex;
+            getVibration().stats.reportRepetition(loopSegmentsPlayed / loopSize);
+            nextSegmentIndex = repeatIndex + ((nextSegmentIndex - effectSize) % loopSize);
+        }
+        Step nextStep = conductor.nextVibrateStep(nextStartTime, controller, effect,
+                nextSegmentIndex, mPendingVibratorOffDeadline);
+        return List.of(nextStep);
+    }
+}
diff --git a/services/core/java/com/android/server/vibrator/AbstractVibratorStep.java b/services/core/java/com/android/server/vibrator/AbstractVibratorStep.java
index 90b6f95..42203b1 100644
--- a/services/core/java/com/android/server/vibrator/AbstractVibratorStep.java
+++ b/services/core/java/com/android/server/vibrator/AbstractVibratorStep.java
@@ -16,21 +16,16 @@
 
 package com.android.server.vibrator;
 
+import android.annotation.NonNull;
 import android.os.SystemClock;
-import android.os.VibrationEffect;
 import android.util.Slog;
 
 import java.util.Arrays;
 import java.util.List;
 
-/**
- * Represent a step on a single vibrator that plays one or more segments from a
- * {@link VibrationEffect.Composed} effect.
- */
+/** Represent a step on a single vibrator that plays a command on {@link VibratorController}. */
 abstract class AbstractVibratorStep extends Step {
     public final VibratorController controller;
-    public final VibrationEffect.Composed effect;
-    public final int segmentIndex;
 
     long mVibratorOnResult;
     long mPendingVibratorOffDeadline;
@@ -41,20 +36,15 @@
      * @param startTime          The time to schedule this step in the
      *                           {@link VibrationStepConductor}.
      * @param controller         The vibrator that is playing the effect.
-     * @param effect             The effect being played in this step.
-     * @param index              The index of the next segment to be played by this step
      * @param pendingVibratorOffDeadline The time the vibrator is expected to complete any
      *                           previous vibration and turn off. This is used to allow this step to
      *                           be triggered when the completion callback is received, and can
      *                           be used to play effects back-to-back.
      */
     AbstractVibratorStep(VibrationStepConductor conductor, long startTime,
-            VibratorController controller, VibrationEffect.Composed effect, int index,
-            long pendingVibratorOffDeadline) {
+            VibratorController controller, long pendingVibratorOffDeadline) {
         super(conductor, startTime);
         this.controller = controller;
-        this.effect = effect;
-        this.segmentIndex = index;
         mPendingVibratorOffDeadline = pendingVibratorOffDeadline;
     }
 
@@ -88,6 +78,7 @@
         return shouldAcceptCallback;
     }
 
+    @NonNull
     @Override
     public List<Step> cancel() {
         return Arrays.asList(new CompleteEffectVibratorStep(conductor, SystemClock.uptimeMillis(),
@@ -138,43 +129,4 @@
         controller.setAmplitude(amplitude);
         getVibration().stats.reportSetAmplitude();
     }
-
-    /**
-     * Return the {@link VibrationStepConductor#nextVibrateStep} with start and off timings
-     * calculated from {@link #getVibratorOnDuration()} based on the current
-     * {@link SystemClock#uptimeMillis()} and jumping all played segments from the effect.
-     */
-    protected List<Step> nextSteps(int segmentsPlayed) {
-        // Schedule next steps to run right away.
-        long nextStartTime = SystemClock.uptimeMillis();
-        if (mVibratorOnResult > 0) {
-            // Vibrator was turned on by this step, with mVibratorOnResult as the duration.
-            // Schedule next steps for right after the vibration finishes.
-            nextStartTime += mVibratorOnResult;
-        }
-        return nextSteps(nextStartTime, segmentsPlayed);
-    }
-
-    /**
-     * Return the {@link VibrationStepConductor#nextVibrateStep} with given start time,
-     * which might be calculated independently, and jumping all played segments from the effect.
-     *
-     * <p>This should be used when the vibrator on/off state is not responsible for the step
-     * execution timing, e.g. while playing the vibrator amplitudes.
-     */
-    protected List<Step> nextSteps(long nextStartTime, int segmentsPlayed) {
-        int nextSegmentIndex = segmentIndex + segmentsPlayed;
-        int effectSize = effect.getSegments().size();
-        int repeatIndex = effect.getRepeatIndex();
-        if (nextSegmentIndex >= effectSize && repeatIndex >= 0) {
-            // Count the loops that were played.
-            int loopSize = effectSize - repeatIndex;
-            int loopSegmentsPlayed = nextSegmentIndex - repeatIndex;
-            getVibration().stats.reportRepetition(loopSegmentsPlayed / loopSize);
-            nextSegmentIndex = repeatIndex + ((nextSegmentIndex - effectSize) % loopSize);
-        }
-        Step nextStep = conductor.nextVibrateStep(nextStartTime, controller, effect,
-                nextSegmentIndex, mPendingVibratorOffDeadline);
-        return nextStep == null ? VibrationStepConductor.EMPTY_STEP_LIST : Arrays.asList(nextStep);
-    }
 }
diff --git a/services/core/java/com/android/server/vibrator/CompleteEffectVibratorStep.java b/services/core/java/com/android/server/vibrator/CompleteEffectVibratorStep.java
index 48dd992..7f9c349 100644
--- a/services/core/java/com/android/server/vibrator/CompleteEffectVibratorStep.java
+++ b/services/core/java/com/android/server/vibrator/CompleteEffectVibratorStep.java
@@ -16,6 +16,7 @@
 
 package com.android.server.vibrator;
 
+import android.annotation.NonNull;
 import android.os.SystemClock;
 import android.os.Trace;
 import android.os.VibrationEffect;
@@ -35,8 +36,7 @@
 
     CompleteEffectVibratorStep(VibrationStepConductor conductor, long startTime, boolean cancelled,
             VibratorController controller, long pendingVibratorOffDeadline) {
-        super(conductor, startTime, controller, /* effect= */ null, /* index= */ -1,
-                pendingVibratorOffDeadline);
+        super(conductor, startTime, controller, pendingVibratorOffDeadline);
         mCancelled = cancelled;
     }
 
@@ -47,6 +47,7 @@
         return mCancelled;
     }
 
+    @NonNull
     @Override
     public List<Step> cancel() {
         if (mCancelled) {
@@ -57,6 +58,7 @@
         return super.cancel();
     }
 
+    @NonNull
     @Override
     public List<Step> play() {
         Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "CompleteEffectVibratorStep");
diff --git a/services/core/java/com/android/server/vibrator/ComposePrimitivesVibratorStep.java b/services/core/java/com/android/server/vibrator/ComposePrimitivesVibratorStep.java
index 940bd08..e495af5 100644
--- a/services/core/java/com/android/server/vibrator/ComposePrimitivesVibratorStep.java
+++ b/services/core/java/com/android/server/vibrator/ComposePrimitivesVibratorStep.java
@@ -16,6 +16,7 @@
 
 package com.android.server.vibrator;
 
+import android.annotation.NonNull;
 import android.os.Trace;
 import android.os.VibrationEffect;
 import android.os.vibrator.PrimitiveSegment;
@@ -31,7 +32,7 @@
  * <p>This step will use the maximum supported number of consecutive segments of type
  * {@link PrimitiveSegment} starting at the current index.
  */
-final class ComposePrimitivesVibratorStep extends AbstractVibratorStep {
+final class ComposePrimitivesVibratorStep extends AbstractComposedVibratorStep {
     /**
      * Default limit to the number of primitives in a composition, if none is defined by the HAL,
      * to prevent repeating effects from generating an infinite list.
@@ -47,6 +48,7 @@
                 index, pendingVibratorOffDeadline);
     }
 
+    @NonNull
     @Override
     public List<Step> play() {
         Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "ComposePrimitivesStep");
diff --git a/services/core/java/com/android/server/vibrator/ComposePwleVibratorStep.java b/services/core/java/com/android/server/vibrator/ComposePwleVibratorStep.java
index 5d572be6..e8952fa 100644
--- a/services/core/java/com/android/server/vibrator/ComposePwleVibratorStep.java
+++ b/services/core/java/com/android/server/vibrator/ComposePwleVibratorStep.java
@@ -16,6 +16,7 @@
 
 package com.android.server.vibrator;
 
+import android.annotation.NonNull;
 import android.os.Trace;
 import android.os.VibrationEffect;
 import android.os.vibrator.RampSegment;
@@ -31,7 +32,7 @@
  * <p>This step will use the maximum supported number of consecutive segments of type
  * {@link RampSegment}, starting at the current index.
  */
-final class ComposePwleVibratorStep extends AbstractVibratorStep {
+final class ComposePwleVibratorStep extends AbstractComposedVibratorStep {
     /**
      * Default limit to the number of PWLE segments, if none is defined by the HAL, to prevent
      * repeating effects from generating an infinite list.
@@ -47,6 +48,7 @@
                 index, pendingVibratorOffDeadline);
     }
 
+    @NonNull
     @Override
     public List<Step> play() {
         Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "ComposePwleStep");
diff --git a/services/core/java/com/android/server/vibrator/DeviceAdapter.java b/services/core/java/com/android/server/vibrator/DeviceAdapter.java
index 98309cd..bd4fc07 100644
--- a/services/core/java/com/android/server/vibrator/DeviceAdapter.java
+++ b/services/core/java/com/android/server/vibrator/DeviceAdapter.java
@@ -21,7 +21,6 @@
 import android.os.VibrationEffect;
 import android.os.VibratorInfo;
 import android.os.vibrator.VibrationEffectSegment;
-import android.util.Slog;
 import android.util.SparseArray;
 
 import java.util.ArrayList;
@@ -82,9 +81,8 @@
     @NonNull
     @Override
     public VibrationEffect adaptToVibrator(int vibratorId, @NonNull VibrationEffect effect) {
-        if (!(effect instanceof VibrationEffect.Composed)) {
+        if (!(effect instanceof VibrationEffect.Composed composed)) {
             // Segments adapters can only apply to Composed effects.
-            Slog.wtf(TAG, "Error adapting unsupported vibration effect: " + effect);
             return effect;
         }
 
@@ -95,7 +93,6 @@
         }
 
         VibratorInfo info = controller.getVibratorInfo();
-        VibrationEffect.Composed composed = (VibrationEffect.Composed) effect;
         List<VibrationEffectSegment> newSegments = new ArrayList<>(composed.getSegments());
         int newRepeatIndex = composed.getRepeatIndex();
 
diff --git a/services/core/java/com/android/server/vibrator/FinishSequentialEffectStep.java b/services/core/java/com/android/server/vibrator/FinishSequentialEffectStep.java
index c9683d9..6456371 100644
--- a/services/core/java/com/android/server/vibrator/FinishSequentialEffectStep.java
+++ b/services/core/java/com/android/server/vibrator/FinishSequentialEffectStep.java
@@ -16,6 +16,7 @@
 
 package com.android.server.vibrator;
 
+import android.annotation.NonNull;
 import android.os.Trace;
 import android.util.Slog;
 
@@ -43,6 +44,7 @@
         return true;
     }
 
+    @NonNull
     @Override
     public List<Step> play() {
         Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "FinishSequentialEffectStep");
@@ -61,6 +63,7 @@
         }
     }
 
+    @NonNull
     @Override
     public List<Step> cancel() {
         cancelImmediately();
diff --git a/services/core/java/com/android/server/vibrator/HalVibration.java b/services/core/java/com/android/server/vibrator/HalVibration.java
index f9bad59..46bd7af 100644
--- a/services/core/java/com/android/server/vibrator/HalVibration.java
+++ b/services/core/java/com/android/server/vibrator/HalVibration.java
@@ -108,23 +108,6 @@
     }
 
     /**
-     * Resolves the default vibration amplitude of {@link #getEffectToPlay()} and each fallback.
-     *
-     * @param defaultAmplitude An integer in [1,255] representing the device default amplitude to
-     *                        replace the {@link VibrationEffect#DEFAULT_AMPLITUDE}.
-     */
-    public void resolveEffects(int defaultAmplitude) {
-        CombinedVibration newEffect =
-                mEffectToPlay.transform(VibrationEffect::resolve, defaultAmplitude);
-        if (!Objects.equals(mEffectToPlay, newEffect)) {
-            mEffectToPlay = newEffect;
-        }
-        for (int i = 0; i < mFallbacks.size(); i++) {
-            mFallbacks.setValueAt(i, mFallbacks.valueAt(i).resolve(defaultAmplitude));
-        }
-    }
-
-    /**
      * Scales the {@link #getEffectToPlay()} and each fallback effect based on the vibration usage.
      */
     public void scaleEffects(VibrationScaler scaler) {
diff --git a/services/core/java/com/android/server/vibrator/HapticFeedbackVibrationProvider.java b/services/core/java/com/android/server/vibrator/HapticFeedbackVibrationProvider.java
index 98a2ba0d..3f9da82 100644
--- a/services/core/java/com/android/server/vibrator/HapticFeedbackVibrationProvider.java
+++ b/services/core/java/com/android/server/vibrator/HapticFeedbackVibrationProvider.java
@@ -46,6 +46,8 @@
             VibrationAttributes.createForUsage(VibrationAttributes.USAGE_HARDWARE_FEEDBACK);
     private static final VibrationAttributes COMMUNICATION_REQUEST_VIBRATION_ATTRIBUTES =
             VibrationAttributes.createForUsage(VibrationAttributes.USAGE_COMMUNICATION_REQUEST);
+    private static final VibrationAttributes IME_FEEDBACK_VIBRATION_ATTRIBUTES =
+            VibrationAttributes.createForUsage(VibrationAttributes.USAGE_IME_FEEDBACK);
 
     private final VibratorInfo mVibratorInfo;
     private final boolean mHapticTextHandleEnabled;
@@ -219,8 +221,6 @@
         }
 
         int vibFlags = 0;
-        boolean fromIme =
-                (privFlags & HapticFeedbackConstants.PRIVATE_FLAG_APPLY_INPUT_METHOD_SETTINGS) != 0;
         boolean bypassVibrationIntensitySetting =
                 (flags & HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING) != 0;
         if (bypassVibrationIntensitySetting) {
@@ -229,9 +229,6 @@
         if (shouldBypassInterruptionPolicy(effectId)) {
             vibFlags |= VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY;
         }
-        if (shouldBypassIntensityScale(effectId, fromIme)) {
-            vibFlags |= VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_SCALE;
-        }
 
         return vibFlags == 0 ? attrs : new VibrationAttributes.Builder(attrs)
                 .setFlags(vibFlags).build();
@@ -362,22 +359,6 @@
                 /* fallbackForPredefinedEffect= */ predefinedEffectFallback);
     }
 
-    private boolean shouldBypassIntensityScale(int effectId, boolean isIme) {
-        if (!Flags.keyboardCategoryEnabled() || mKeyboardVibrationFixedAmplitude < 0 || !isIme) {
-            // Shouldn't bypass if not support keyboard category, no fixed amplitude or not an IME.
-            return false;
-        }
-        switch (effectId) {
-            case HapticFeedbackConstants.KEYBOARD_TAP:
-                return mVibratorInfo.isPrimitiveSupported(
-                        VibrationEffect.Composition.PRIMITIVE_CLICK);
-            case HapticFeedbackConstants.KEYBOARD_RELEASE:
-                return mVibratorInfo.isPrimitiveSupported(
-                        VibrationEffect.Composition.PRIMITIVE_TICK);
-        }
-        return false;
-    }
-
     private VibrationAttributes createKeyboardVibrationAttributes(
             @HapticFeedbackConstants.PrivateFlags int privFlags) {
         // Use touch attribute when the keyboard category is disable.
@@ -388,7 +369,8 @@
         if ((privFlags & HapticFeedbackConstants.PRIVATE_FLAG_APPLY_INPUT_METHOD_SETTINGS) == 0) {
             return TOUCH_VIBRATION_ATTRIBUTES;
         }
-        return new VibrationAttributes.Builder(TOUCH_VIBRATION_ATTRIBUTES)
+        return new VibrationAttributes.Builder(IME_FEEDBACK_VIBRATION_ATTRIBUTES)
+                // TODO(b/332661766): Remove CATEGORY_KEYBOARD logic
                 .setCategory(VibrationAttributes.CATEGORY_KEYBOARD)
                 .build();
     }
diff --git a/services/core/java/com/android/server/vibrator/PerformPrebakedVibratorStep.java b/services/core/java/com/android/server/vibrator/PerformPrebakedVibratorStep.java
index 8094e7c5..4b23216 100644
--- a/services/core/java/com/android/server/vibrator/PerformPrebakedVibratorStep.java
+++ b/services/core/java/com/android/server/vibrator/PerformPrebakedVibratorStep.java
@@ -16,6 +16,7 @@
 
 package com.android.server.vibrator;
 
+import android.annotation.NonNull;
 import android.os.Trace;
 import android.os.VibrationEffect;
 import android.os.vibrator.PrebakedSegment;
@@ -31,7 +32,7 @@
  * <p>This step automatically falls back by replacing the prebaked segment with
  * {@link VibrationSettings#getFallbackEffect(int)}, if available.
  */
-final class PerformPrebakedVibratorStep extends AbstractVibratorStep {
+final class PerformPrebakedVibratorStep extends AbstractComposedVibratorStep {
 
     PerformPrebakedVibratorStep(VibrationStepConductor conductor, long startTime,
             VibratorController controller, VibrationEffect.Composed effect, int index,
@@ -42,6 +43,7 @@
                 index, pendingVibratorOffDeadline);
     }
 
+    @NonNull
     @Override
     public List<Step> play() {
         Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "PerformPrebakedVibratorStep");
diff --git a/services/core/java/com/android/server/vibrator/PerformVendorEffectVibratorStep.java b/services/core/java/com/android/server/vibrator/PerformVendorEffectVibratorStep.java
new file mode 100644
index 0000000..8f36118
--- /dev/null
+++ b/services/core/java/com/android/server/vibrator/PerformVendorEffectVibratorStep.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.vibrator;
+
+import android.annotation.NonNull;
+import android.os.Trace;
+import android.os.VibrationEffect;
+
+import java.util.List;
+
+/**
+ * Represents a step to turn the vibrator on with a vendor-specific vibration from a
+ * {@link VibrationEffect.VendorEffect} effect.
+ */
+final class PerformVendorEffectVibratorStep extends AbstractVibratorStep {
+    /**
+     * Timeout to ensure vendor vibrations are not unbounded if vibrator callbacks are lost.
+     */
+    static final long VENDOR_EFFECT_MAX_DURATION_MS = 60_000; // 1 min
+
+    public final VibrationEffect.VendorEffect effect;
+
+    PerformVendorEffectVibratorStep(VibrationStepConductor conductor, long startTime,
+            VibratorController controller, VibrationEffect.VendorEffect effect,
+            long pendingVibratorOffDeadline) {
+        // This step should wait for the last vibration to finish (with the timeout) and for the
+        // intended step start time (to respect the effect delays).
+        super(conductor, Math.max(startTime, pendingVibratorOffDeadline), controller,
+                pendingVibratorOffDeadline);
+        this.effect = effect;
+    }
+
+    @NonNull
+    @Override
+    public List<Step> play() {
+        Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "PerformVendorEffectVibratorStep");
+        try {
+            long vibratorOnResult = controller.on(effect, getVibration().id);
+            vibratorOnResult = Math.min(vibratorOnResult, VENDOR_EFFECT_MAX_DURATION_MS);
+            handleVibratorOnResult(vibratorOnResult);
+            return List.of(new CompleteEffectVibratorStep(conductor, startTime,
+                    /* cancelled= */ false, controller, mPendingVibratorOffDeadline));
+        } finally {
+            Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/vibrator/RampOffVibratorStep.java b/services/core/java/com/android/server/vibrator/RampOffVibratorStep.java
index f40c994..901f9c3 100644
--- a/services/core/java/com/android/server/vibrator/RampOffVibratorStep.java
+++ b/services/core/java/com/android/server/vibrator/RampOffVibratorStep.java
@@ -16,6 +16,7 @@
 
 package com.android.server.vibrator;
 
+import android.annotation.NonNull;
 import android.os.SystemClock;
 import android.os.Trace;
 import android.util.Slog;
@@ -31,8 +32,7 @@
     RampOffVibratorStep(VibrationStepConductor conductor, long startTime, float amplitudeTarget,
             float amplitudeDelta, VibratorController controller,
             long pendingVibratorOffDeadline) {
-        super(conductor, startTime, controller, /* effect= */ null, /* index= */ -1,
-                pendingVibratorOffDeadline);
+        super(conductor, startTime, controller, pendingVibratorOffDeadline);
         mAmplitudeTarget = amplitudeTarget;
         mAmplitudeDelta = amplitudeDelta;
     }
@@ -42,12 +42,14 @@
         return true;
     }
 
+    @NonNull
     @Override
     public List<Step> cancel() {
         return Arrays.asList(new TurnOffVibratorStep(conductor, SystemClock.uptimeMillis(),
                 controller, /* isCleanUp= */ true));
     }
 
+    @NonNull
     @Override
     public List<Step> play() {
         Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "RampOffVibratorStep");
diff --git a/services/core/java/com/android/server/vibrator/SetAmplitudeVibratorStep.java b/services/core/java/com/android/server/vibrator/SetAmplitudeVibratorStep.java
index e13ec6c..8478e77 100644
--- a/services/core/java/com/android/server/vibrator/SetAmplitudeVibratorStep.java
+++ b/services/core/java/com/android/server/vibrator/SetAmplitudeVibratorStep.java
@@ -16,6 +16,7 @@
 
 package com.android.server.vibrator;
 
+import android.annotation.NonNull;
 import android.os.SystemClock;
 import android.os.Trace;
 import android.os.VibrationEffect;
@@ -32,7 +33,7 @@
  * <p>This step ignores vibration completion callbacks and control the vibrator on/off state
  * and amplitude to simulate waveforms represented by a sequence of {@link StepSegment}.
  */
-final class SetAmplitudeVibratorStep extends AbstractVibratorStep {
+final class SetAmplitudeVibratorStep extends AbstractComposedVibratorStep {
     /**
      * The repeating waveform keeps the vibrator ON all the time. Use a minimum duration to
      * prevent short patterns from turning the vibrator ON too frequently.
@@ -69,6 +70,7 @@
         return shouldAcceptCallback;
     }
 
+    @NonNull
     @Override
     public List<Step> play() {
         // TODO: consider separating the "on" steps at the start into a separate Step.
diff --git a/services/core/java/com/android/server/vibrator/StartSequentialEffectStep.java b/services/core/java/com/android/server/vibrator/StartSequentialEffectStep.java
index c197271..3ceba57 100644
--- a/services/core/java/com/android/server/vibrator/StartSequentialEffectStep.java
+++ b/services/core/java/com/android/server/vibrator/StartSequentialEffectStep.java
@@ -16,6 +16,7 @@
 
 package com.android.server.vibrator;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.hardware.vibrator.IVibratorManager;
 import android.os.CombinedVibration;
@@ -74,6 +75,7 @@
         return mVibratorsOnMaxDuration;
     }
 
+    @NonNull
     @Override
     public List<Step> play() {
         Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "StartSequentialEffectStep");
@@ -111,6 +113,7 @@
         return nextSteps;
     }
 
+    @NonNull
     @Override
     public List<Step> cancel() {
         return VibrationStepConductor.EMPTY_STEP_LIST;
@@ -173,13 +176,12 @@
         for (int i = 0; i < vibratorCount; i++) {
             steps[i] = conductor.nextVibrateStep(vibrationStartTime,
                     conductor.getVibrators().get(effectMapping.vibratorIdAt(i)),
-                    effectMapping.effectAt(i),
-                    /* segmentIndex= */ 0, /* vibratorOffTimeout= */ 0);
+                    effectMapping.effectAt(i));
         }
 
         if (steps.length == 1) {
             // No need to prepare and trigger sync effects on a single vibrator.
-            return startVibrating(steps[0], nextSteps);
+            return startVibrating(steps[0], effectMapping.effectAt(0), nextSteps);
         }
 
         // This synchronization of vibrators should be executed one at a time, even if we are
@@ -196,8 +198,8 @@
                 effectMapping.getRequiredSyncCapabilities(),
                 effectMapping.getVibratorIds());
 
-        for (AbstractVibratorStep step : steps) {
-            long duration = startVibrating(step, nextSteps);
+        for (int i = 0; i < vibratorCount; i++) {
+            long duration = startVibrating(steps[i], effectMapping.effectAt(i), nextSteps);
             if (duration < 0) {
                 // One vibrator has failed, fail this entire sync attempt.
                 hasFailed = true;
@@ -231,7 +233,12 @@
         return hasFailed ? -1 : maxDuration;
     }
 
-    private long startVibrating(AbstractVibratorStep step, List<Step> nextSteps) {
+    private long startVibrating(@Nullable AbstractVibratorStep step, VibrationEffect effect,
+            List<Step> nextSteps) {
+        if (step == null) {
+            // Failed to create a step for VibrationEffect.
+            return -1;
+        }
         nextSteps.addAll(step.play());
         long stepDuration = step.getVibratorOnDuration();
         if (stepDuration < 0) {
@@ -239,7 +246,7 @@
             return stepDuration;
         }
         // Return the longest estimation for the entire effect.
-        return Math.max(stepDuration, step.effect.getDuration());
+        return Math.max(stepDuration, effect.getDuration());
     }
 
     /**
@@ -249,28 +256,20 @@
      * play all of the effects in sync.
      */
     final class DeviceEffectMap {
-        private final SparseArray<VibrationEffect.Composed> mVibratorEffects;
+        private final SparseArray<VibrationEffect> mVibratorEffects;
         private final int[] mVibratorIds;
         private final long mRequiredSyncCapabilities;
 
         DeviceEffectMap(CombinedVibration.Mono mono) {
             SparseArray<VibratorController> vibrators = conductor.getVibrators();
             VibrationEffect effect = mono.getEffect();
-            if (effect instanceof VibrationEffect.Composed) {
-                mVibratorEffects = new SparseArray<>(vibrators.size());
-                mVibratorIds = new int[vibrators.size()];
+            mVibratorEffects = new SparseArray<>(vibrators.size());
+            mVibratorIds = new int[vibrators.size()];
 
-                VibrationEffect.Composed composedEffect = (VibrationEffect.Composed) effect;
-                for (int i = 0; i < vibrators.size(); i++) {
-                    int vibratorId = vibrators.keyAt(i);
-                    mVibratorEffects.put(vibratorId, composedEffect);
-                    mVibratorIds[i] = vibratorId;
-                }
-            } else {
-                Slog.wtf(VibrationThread.TAG,
-                        "Unable to map device vibrators to unexpected effect: " + effect);
-                mVibratorEffects = new SparseArray<>();
-                mVibratorIds = new int[0];
+            for (int i = 0; i < vibrators.size(); i++) {
+                int vibratorId = vibrators.keyAt(i);
+                mVibratorEffects.put(vibratorId, effect);
+                mVibratorIds[i] = vibratorId;
             }
             mRequiredSyncCapabilities = calculateRequiredSyncCapabilities(mVibratorEffects);
         }
@@ -282,13 +281,7 @@
             for (int i = 0; i < stereoEffects.size(); i++) {
                 int vibratorId = stereoEffects.keyAt(i);
                 if (vibrators.contains(vibratorId)) {
-                    VibrationEffect effect = stereoEffects.valueAt(i);
-                    if (effect instanceof VibrationEffect.Composed) {
-                        mVibratorEffects.put(vibratorId, (VibrationEffect.Composed) effect);
-                    } else {
-                        Slog.wtf(VibrationThread.TAG,
-                                "Unable to map device vibrators to unexpected effect: " + effect);
-                    }
+                    mVibratorEffects.put(vibratorId, stereoEffects.valueAt(i));
                 }
             }
             mVibratorIds = new int[mVibratorEffects.size()];
@@ -326,7 +319,7 @@
         }
 
         /** Return the {@link VibrationEffect} at given index. */
-        public VibrationEffect.Composed effectAt(int index) {
+        public VibrationEffect effectAt(int index) {
             return mVibratorEffects.valueAt(index);
         }
 
@@ -338,16 +331,24 @@
          * IVibratorManager.CAP_PREPARE_* and IVibratorManager.CAP_MIXED_TRIGGER_* capabilities.
          */
         private long calculateRequiredSyncCapabilities(
-                SparseArray<VibrationEffect.Composed> effects) {
+                SparseArray<VibrationEffect> effects) {
             long prepareCap = 0;
             for (int i = 0; i < effects.size(); i++) {
-                VibrationEffectSegment firstSegment = effects.valueAt(i).getSegments().get(0);
-                if (firstSegment instanceof StepSegment) {
-                    prepareCap |= IVibratorManager.CAP_PREPARE_ON;
-                } else if (firstSegment instanceof PrebakedSegment) {
+                VibrationEffect effect = effects.valueAt(i);
+                if (effect instanceof VibrationEffect.VendorEffect) {
                     prepareCap |= IVibratorManager.CAP_PREPARE_PERFORM;
-                } else if (firstSegment instanceof PrimitiveSegment) {
-                    prepareCap |= IVibratorManager.CAP_PREPARE_COMPOSE;
+                } else if (effect instanceof VibrationEffect.Composed composed) {
+                    VibrationEffectSegment firstSegment = composed.getSegments().get(0);
+                    if (firstSegment instanceof StepSegment) {
+                        prepareCap |= IVibratorManager.CAP_PREPARE_ON;
+                    } else if (firstSegment instanceof PrebakedSegment) {
+                        prepareCap |= IVibratorManager.CAP_PREPARE_PERFORM;
+                    } else if (firstSegment instanceof PrimitiveSegment) {
+                        prepareCap |= IVibratorManager.CAP_PREPARE_COMPOSE;
+                    }
+                } else {
+                    Slog.wtf(VibrationThread.TAG,
+                            "Unable to check sync capabilities to unexpected effect: " + effect);
                 }
             }
             int triggerCap = 0;
diff --git a/services/core/java/com/android/server/vibrator/TurnOffVibratorStep.java b/services/core/java/com/android/server/vibrator/TurnOffVibratorStep.java
index 065ce11..87dc269 100644
--- a/services/core/java/com/android/server/vibrator/TurnOffVibratorStep.java
+++ b/services/core/java/com/android/server/vibrator/TurnOffVibratorStep.java
@@ -16,6 +16,7 @@
 
 package com.android.server.vibrator;
 
+import android.annotation.NonNull;
 import android.os.SystemClock;
 import android.os.Trace;
 
@@ -36,7 +37,7 @@
 
     TurnOffVibratorStep(VibrationStepConductor conductor, long startTime,
             VibratorController controller, boolean isCleanUp) {
-        super(conductor, startTime, controller, /* effect= */ null, /* index= */ -1, startTime);
+        super(conductor, startTime, controller, startTime);
         mIsCleanUp = isCleanUp;
     }
 
@@ -45,6 +46,7 @@
         return mIsCleanUp;
     }
 
+    @NonNull
     @Override
     public List<Step> cancel() {
         return Arrays.asList(new TurnOffVibratorStep(conductor, SystemClock.uptimeMillis(),
@@ -56,6 +58,7 @@
         stopVibrating();
     }
 
+    @NonNull
     @Override
     public List<Step> play() {
         Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "TurnOffVibratorStep");
diff --git a/services/core/java/com/android/server/vibrator/Vibration.java b/services/core/java/com/android/server/vibrator/Vibration.java
index 5fab13b..5c567da 100644
--- a/services/core/java/com/android/server/vibrator/Vibration.java
+++ b/services/core/java/com/android/server/vibrator/Vibration.java
@@ -44,9 +44,9 @@
  */
 abstract class Vibration {
     private static final DateTimeFormatter DEBUG_TIME_FORMATTER = DateTimeFormatter.ofPattern(
-            "HH:mm:ss.SSS").withZone(ZoneId.systemDefault());
+            "HH:mm:ss.SSS");
     private static final DateTimeFormatter DEBUG_DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern(
-            "MM-dd HH:mm:ss.SSS").withZone(ZoneId.systemDefault());
+            "MM-dd HH:mm:ss.SSS");
 
     // Used to generate globally unique vibration ids.
     private static final AtomicInteger sNextVibrationId = new AtomicInteger(1); // 0 = no callback
@@ -244,12 +244,10 @@
 
         @Override
         public String toString() {
-            return "createTime: " + DEBUG_DATE_TIME_FORMATTER.format(
-                    Instant.ofEpochMilli(mCreateTime))
-                    + ", startTime: " + DEBUG_DATE_TIME_FORMATTER.format(
-                    Instant.ofEpochMilli(mStartTime))
-                    + ", endTime: " + (mEndTime == 0 ? null : DEBUG_DATE_TIME_FORMATTER.format(
-                    Instant.ofEpochMilli(mEndTime)))
+            return "createTime: " + formatTime(mCreateTime, /*includeDate=*/ true)
+                    + ", startTime: " + formatTime(mStartTime, /*includeDate=*/ true)
+                    + ", endTime: " + (mEndTime == 0 ? null : formatTime(mEndTime,
+                    /*includeDate=*/ true))
                     + ", durationMs: " + mDurationMs
                     + ", status: " + mStatus.name().toLowerCase(Locale.ROOT)
                     + ", playedEffect: " + mPlayedEffect
@@ -273,14 +271,12 @@
             boolean isExternalVibration = mPlayedEffect == null;
             String timingsStr = String.format(Locale.ROOT,
                     "%s | %8s | %20s | duration: %5dms | start: %12s | end: %12s",
-                    DEBUG_DATE_TIME_FORMATTER.format(Instant.ofEpochMilli(mCreateTime)),
+                    formatTime(mCreateTime, /*includeDate=*/ true),
                     isExternalVibration ? "external" : "effect",
                     mStatus.name().toLowerCase(Locale.ROOT),
                     mDurationMs,
-                    mStartTime == 0 ? ""
-                            : DEBUG_TIME_FORMATTER.format(Instant.ofEpochMilli(mStartTime)),
-                    mEndTime == 0 ? ""
-                            : DEBUG_TIME_FORMATTER.format(Instant.ofEpochMilli(mEndTime)));
+                    mStartTime == 0 ? "" : formatTime(mStartTime, /*includeDate=*/ false),
+                    mEndTime == 0 ? "" : formatTime(mEndTime, /*includeDate=*/ false));
             String paramStr = String.format(Locale.ROOT,
                     " | scale: %8s (adaptive=%.2f) | flags: %4s | usage: %s",
                     VibrationScaler.scaleLevelToString(mScaleLevel), mAdaptiveScale,
@@ -315,12 +311,10 @@
             pw.increaseIndent();
             pw.println("status = " + mStatus.name().toLowerCase(Locale.ROOT));
             pw.println("durationMs = " + mDurationMs);
-            pw.println("createTime = " + DEBUG_DATE_TIME_FORMATTER.format(
-                    Instant.ofEpochMilli(mCreateTime)));
-            pw.println("startTime = " + DEBUG_DATE_TIME_FORMATTER.format(
-                    Instant.ofEpochMilli(mStartTime)));
+            pw.println("createTime = " + formatTime(mCreateTime, /*includeDate=*/ true));
+            pw.println("startTime = " + formatTime(mStartTime, /*includeDate=*/ true));
             pw.println("endTime = " + (mEndTime == 0 ? null
-                    : DEBUG_DATE_TIME_FORMATTER.format(Instant.ofEpochMilli(mEndTime))));
+                    : formatTime(mEndTime, /*includeDate=*/ true)));
             pw.println("playedEffect = " + mPlayedEffect);
             pw.println("originalEffect = " + mOriginalEffect);
             pw.println("scale = " + VibrationScaler.scaleLevelToString(mScaleLevel));
@@ -399,13 +393,14 @@
 
         private void dumpEffect(
                 ProtoOutputStream proto, long fieldId, VibrationEffect effect) {
-            final long token = proto.start(fieldId);
-            VibrationEffect.Composed composed = (VibrationEffect.Composed) effect;
-            for (VibrationEffectSegment segment : composed.getSegments()) {
-                dumpEffect(proto, VibrationEffectProto.SEGMENTS, segment);
+            if (effect instanceof VibrationEffect.Composed composed) {
+                final long token = proto.start(fieldId);
+                for (VibrationEffectSegment segment : composed.getSegments()) {
+                    dumpEffect(proto, VibrationEffectProto.SEGMENTS, segment);
+                }
+                proto.write(VibrationEffectProto.REPEAT, composed.getRepeatIndex());
+                proto.end(token);
             }
-            proto.write(VibrationEffectProto.REPEAT, composed.getRepeatIndex());
-            proto.end(token);
         }
 
         private void dumpEffect(ProtoOutputStream proto, long fieldId,
@@ -458,5 +453,12 @@
             proto.write(PrimitiveSegmentProto.DELAY, segment.getDelay());
             proto.end(token);
         }
+
+        private String formatTime(long timeInMillis, boolean includeDate) {
+            return (includeDate ? DEBUG_DATE_TIME_FORMATTER : DEBUG_TIME_FORMATTER)
+                    // Ensure timezone is retrieved at formatting time
+                    .withZone(ZoneId.systemDefault())
+                    .format(Instant.ofEpochMilli(timeInMillis));
+        }
     }
 }
diff --git a/services/core/java/com/android/server/vibrator/VibrationScaler.java b/services/core/java/com/android/server/vibrator/VibrationScaler.java
index d9ca710..3933759 100644
--- a/services/core/java/com/android/server/vibrator/VibrationScaler.java
+++ b/services/core/java/com/android/server/vibrator/VibrationScaler.java
@@ -25,14 +25,12 @@
 import android.os.Vibrator;
 import android.os.vibrator.Flags;
 import android.os.vibrator.PrebakedSegment;
-import android.os.vibrator.VibrationEffectSegment;
 import android.util.IndentingPrintWriter;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.proto.ProtoOutputStream;
 
 import java.io.PrintWriter;
-import java.util.ArrayList;
 import java.util.Locale;
 
 /** Controls vibration scaling. */
@@ -136,12 +134,6 @@
      */
     @NonNull
     public VibrationEffect scale(@NonNull VibrationEffect effect, int usageHint) {
-        if (!(effect instanceof VibrationEffect.Composed)) {
-            // This only scales composed vibration effects.
-            Slog.wtf(TAG, "Error scaling unsupported vibration effect: " + effect);
-            return effect;
-        }
-
         int newEffectStrength = getEffectStrength(usageHint);
         ScaleLevel scaleLevel = mScaleLevels.get(getScaleLevel(usageHint));
         float adaptiveScale = getAdaptiveHapticsScale(usageHint);
@@ -154,26 +146,10 @@
             scaleLevel = SCALE_LEVEL_NONE;
         }
 
-        VibrationEffect.Composed composedEffect = (VibrationEffect.Composed) effect;
-        ArrayList<VibrationEffectSegment> segments =
-                new ArrayList<>(composedEffect.getSegments());
-        int segmentCount = segments.size();
-        for (int i = 0; i < segmentCount; i++) {
-            segments.set(i,
-                    segments.get(i).resolve(mDefaultVibrationAmplitude)
-                            .applyEffectStrength(newEffectStrength)
-                            .scale(scaleLevel.factor)
-                            .scaleLinearly(adaptiveScale));
-        }
-        if (segments.equals(composedEffect.getSegments())) {
-            // No segment was updated, return original effect.
-            return effect;
-        }
-        VibrationEffect.Composed scaled =
-                new VibrationEffect.Composed(segments, composedEffect.getRepeatIndex());
-        // Make sure we validate what was scaled, since we're using the constructor directly
-        scaled.validate();
-        return scaled;
+        return effect.resolve(mDefaultVibrationAmplitude)
+                .applyEffectStrength(newEffectStrength)
+                .scale(scaleLevel.factor)
+                .scaleLinearly(adaptiveScale);
     }
 
     /**
diff --git a/services/core/java/com/android/server/vibrator/VibrationSettings.java b/services/core/java/com/android/server/vibrator/VibrationSettings.java
index 0206155..fb92d60 100644
--- a/services/core/java/com/android/server/vibrator/VibrationSettings.java
+++ b/services/core/java/com/android/server/vibrator/VibrationSettings.java
@@ -21,6 +21,7 @@
 import static android.os.VibrationAttributes.USAGE_ALARM;
 import static android.os.VibrationAttributes.USAGE_COMMUNICATION_REQUEST;
 import static android.os.VibrationAttributes.USAGE_HARDWARE_FEEDBACK;
+import static android.os.VibrationAttributes.USAGE_IME_FEEDBACK;
 import static android.os.VibrationAttributes.USAGE_MEDIA;
 import static android.os.VibrationAttributes.USAGE_NOTIFICATION;
 import static android.os.VibrationAttributes.USAGE_PHYSICAL_EMULATION;
@@ -560,6 +561,7 @@
             mKeyboardVibrationOn = loadSystemSetting(
                     Settings.System.KEYBOARD_VIBRATION_ENABLED, 1, userHandle) > 0;
 
+            int keyboardIntensity = getDefaultIntensity(USAGE_IME_FEEDBACK);
             int alarmIntensity = toIntensity(
                     loadSystemSetting(Settings.System.ALARM_VIBRATION_INTENSITY, -1, userHandle),
                     getDefaultIntensity(USAGE_ALARM));
@@ -610,6 +612,12 @@
                 mCurrentVibrationIntensities.put(USAGE_TOUCH, hapticFeedbackIntensity);
             }
 
+            if (mVibrationConfig.isKeyboardVibrationSettingsSupported()) {
+                mCurrentVibrationIntensities.put(USAGE_IME_FEEDBACK, keyboardIntensity);
+            } else {
+                mCurrentVibrationIntensities.put(USAGE_IME_FEEDBACK, hapticFeedbackIntensity);
+            }
+
             // A11y is not disabled by any haptic feedback setting.
             mCurrentVibrationIntensities.put(USAGE_ACCESSIBILITY, positiveHapticFeedbackIntensity);
         }
diff --git a/services/core/java/com/android/server/vibrator/VibrationStepConductor.java b/services/core/java/com/android/server/vibrator/VibrationStepConductor.java
index f3e226e..7152844 100644
--- a/services/core/java/com/android/server/vibrator/VibrationStepConductor.java
+++ b/services/core/java/com/android/server/vibrator/VibrationStepConductor.java
@@ -21,7 +21,6 @@
 import android.os.Build;
 import android.os.CombinedVibration;
 import android.os.IBinder;
-import android.os.VibrationAttributes;
 import android.os.VibrationEffect;
 import android.os.vibrator.Flags;
 import android.os.vibrator.PrebakedSegment;
@@ -123,6 +122,24 @@
 
     @Nullable
     AbstractVibratorStep nextVibrateStep(long startTime, VibratorController controller,
+            VibrationEffect effect) {
+        if (Build.IS_DEBUGGABLE) {
+            expectIsVibrationThread(true);
+        }
+        if (effect instanceof VibrationEffect.VendorEffect vendorEffect) {
+            return new PerformVendorEffectVibratorStep(this, startTime, controller, vendorEffect,
+                    /* pendingVibratorOffDeadline= */ 0);
+        }
+        if (effect instanceof VibrationEffect.Composed composed) {
+            return nextVibrateStep(startTime, controller, composed, /* segmentIndex= */ 0,
+                    /* pendingVibratorOffDeadline= */ 0);
+        }
+        Slog.wtf(TAG, "Unable to create next step for unexpected effect: " + effect);
+        return null;
+    }
+
+    @NonNull
+    AbstractVibratorStep nextVibrateStep(long startTime, VibratorController controller,
             VibrationEffect.Composed effect, int segmentIndex, long pendingVibratorOffDeadline) {
         if (Build.IS_DEBUGGABLE) {
             expectIsVibrationThread(true);
@@ -159,16 +176,11 @@
             expectIsVibrationThread(true);
         }
 
-        if (!mVibration.callerInfo.attrs.isFlagSet(
-                VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_SCALE)) {
-            if (Flags.adaptiveHapticsEnabled()) {
-                waitForVibrationParamsIfRequired();
-            }
-            // Scale resolves the default amplitudes from the effect before scaling them.
-            mVibration.scaleEffects(mVibrationScaler);
-        } else {
-            mVibration.resolveEffects(mVibrationScaler.getDefaultVibrationAmplitude());
+        if (Flags.adaptiveHapticsEnabled()) {
+            waitForVibrationParamsIfRequired();
         }
+        // Scale resolves the default amplitudes from the effect before scaling them.
+        mVibration.scaleEffects(mVibrationScaler);
 
         mVibration.adaptToDevice(mDeviceAdapter);
         CombinedVibration.Sequential sequentialEffect = toSequential(mVibration.getEffectToPlay());
diff --git a/services/core/java/com/android/server/vibrator/VibratorControlService.java b/services/core/java/com/android/server/vibrator/VibratorControlService.java
index f82ff67..de5e662 100644
--- a/services/core/java/com/android/server/vibrator/VibratorControlService.java
+++ b/services/core/java/com/android/server/vibrator/VibratorControlService.java
@@ -41,6 +41,7 @@
 import android.os.SystemClock;
 import android.os.VibrationAttributes;
 import android.os.VibrationEffect;
+import android.os.vibrator.Flags;
 import android.util.IndentingPrintWriter;
 import android.util.IntArray;
 import android.util.Slog;
@@ -68,7 +69,7 @@
     private static final int NO_SCALE = -1;
 
     private static final DateTimeFormatter DEBUG_DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern(
-            "MM-dd HH:mm:ss.SSS").withZone(ZoneId.systemDefault());
+            "MM-dd HH:mm:ss.SSS");
 
     private final VibrationParamsRecords mVibrationParamsRecords;
     private final VibratorControllerHolder mVibratorControllerHolder;
@@ -265,9 +266,15 @@
                 return null;
             }
 
+            if (Flags.throttleVibrationParamsRequests() && mVibrationParamRequest != null
+                    && mVibrationParamRequest.usage == usage) {
+                // Reuse existing future for ongoing request with same usage.
+                return mVibrationParamRequest.future;
+            }
+
             try {
                 endOngoingRequestVibrationParamsLocked(/* wasCancelled= */ true);
-                mVibrationParamRequest = new VibrationParamRequest(uid);
+                mVibrationParamRequest = new VibrationParamRequest(uid, usage);
                 vibratorController.requestVibrationParams(vibrationType, timeoutInMillis,
                         mVibrationParamRequest.token);
                 return mVibrationParamRequest.future;
@@ -533,10 +540,12 @@
         public final CompletableFuture<Void> future = new CompletableFuture<>();
         public final IBinder token = new Binder();
         public final int uid;
+        public final @VibrationAttributes.Usage int usage;
         public final long uptimeMs;
 
-        VibrationParamRequest(int uid) {
+        VibrationParamRequest(int uid, @VibrationAttributes.Usage int usage) {
             this.uid = uid;
+            this.usage = usage;
             uptimeMs = SystemClock.uptimeMillis();
         }
 
@@ -591,7 +600,8 @@
         public void dump(IndentingPrintWriter pw) {
             String line = String.format(Locale.ROOT,
                     "%s | %6s | scale: %5s | typesMask: %6s | usages: %s",
-                    DEBUG_DATE_TIME_FORMATTER.format(Instant.ofEpochMilli(mCreateTime)),
+                    DEBUG_DATE_TIME_FORMATTER.withZone(ZoneId.systemDefault()).format(
+                            Instant.ofEpochMilli(mCreateTime)),
                     mOperation.name().toLowerCase(Locale.ROOT),
                     (mScale == NO_SCALE) ? "" : String.format(Locale.ROOT, "%.2f", mScale),
                     Long.toBinaryString(mTypesMask), createVibrationUsagesString());
diff --git a/services/core/java/com/android/server/vibrator/VibratorController.java b/services/core/java/com/android/server/vibrator/VibratorController.java
index 988e8fe..8cc157c 100644
--- a/services/core/java/com/android/server/vibrator/VibratorController.java
+++ b/services/core/java/com/android/server/vibrator/VibratorController.java
@@ -20,8 +20,10 @@
 import android.hardware.vibrator.IVibrator;
 import android.os.Binder;
 import android.os.IVibratorStateListener;
+import android.os.Parcel;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
+import android.os.VibrationEffect;
 import android.os.VibratorInfo;
 import android.os.vibrator.PrebakedSegment;
 import android.os.vibrator.PrimitiveSegment;
@@ -262,6 +264,35 @@
     }
 
     /**
+     * Plays vendor vibration effect, using {@code vibrationId} for completion callback to
+     * {@link OnVibrationCompleteListener}.
+     *
+     * <p>This will affect the state of {@link #isVibrating()}.
+     *
+     * @return The positive duration of the vibration started, if successful, zero if the vibrator
+     * do not support the input or a negative number if the operation failed.
+     */
+    public long on(VibrationEffect.VendorEffect vendorEffect, long vibrationId) {
+        synchronized (mLock) {
+            Parcel vendorData = Parcel.obtain();
+            try {
+                vendorEffect.getVendorData().writeToParcel(vendorData, /* flags= */ 0);
+                vendorData.setDataPosition(0);
+                long duration = mNativeWrapper.performVendorEffect(vendorData,
+                        vendorEffect.getEffectStrength(), vendorEffect.getLinearScale(),
+                        vibrationId);
+                if (duration > 0) {
+                    mCurrentAmplitude = -1;
+                    notifyListenerOnVibrating(true);
+                }
+                return duration;
+            } finally {
+                vendorData.recycle();
+            }
+        }
+    }
+
+    /**
      * Plays predefined vibration effect, using {@code vibrationId} for completion callback to
      * {@link OnVibrationCompleteListener}.
      *
@@ -427,6 +458,9 @@
         private static native long performEffect(long nativePtr, long effect, long strength,
                 long vibrationId);
 
+        private static native long performVendorEffect(long nativePtr, Parcel vendorData,
+                long strength, float scale, long vibrationId);
+
         private static native long performComposedEffect(long nativePtr, PrimitiveSegment[] effect,
                 long vibrationId);
 
@@ -482,6 +516,12 @@
             return performEffect(mNativePtr, effect, strength, vibrationId);
         }
 
+        /** Turns vibrator on to perform a vendor-specific effect. */
+        public long performVendorEffect(Parcel vendorData, long strength, float scale,
+                long vibrationId) {
+            return performVendorEffect(mNativePtr, vendorData, strength, scale, vibrationId);
+        }
+
         /** Turns vibrator on to perform effect composed of give primitives effect. */
         public long compose(PrimitiveSegment[] primitives, long vibrationId) {
             return performComposedEffect(mNativePtr, primitives, vibrationId);
diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
index bff175f..7610d7d 100644
--- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java
+++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
@@ -103,8 +103,7 @@
             new VibrationAttributes.Builder().build();
     private static final int ATTRIBUTES_ALL_BYPASS_FLAGS =
             VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY
-                    | VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF
-                    | VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_SCALE;
+                    | VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF;
 
     /** Fixed large duration used to note repeating vibrations to {@link IBatteryStats}. */
     private static final long BATTERY_STATS_REPEATING_VIBRATION_DURATION = 5_000;
@@ -540,6 +539,11 @@
             Slog.e(TAG, "token must not be null");
             return null;
         }
+        if (effect.hasVendorEffects()
+                && !hasPermission(android.Manifest.permission.VIBRATE_VENDOR_EFFECTS)) {
+            Slog.w(TAG, "vibrate; no permission for vendor effects");
+            return null;
+        }
         enforceUpdateAppOpsStatsPermission(uid);
         if (!isEffectValid(effect)) {
             return null;
@@ -920,8 +924,7 @@
     private VibrationStepConductor createVibrationStepConductor(HalVibration vib) {
         CompletableFuture<Void> requestVibrationParamsFuture = null;
 
-        if (Flags.adaptiveHapticsEnabled() && !vib.callerInfo.attrs.isFlagSet(
-                VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_SCALE)
+        if (Flags.adaptiveHapticsEnabled()
                 && mVibratorControlService.shouldRequestVibrationParams(
                 vib.callerInfo.attrs.getUsage())) {
             requestVibrationParamsFuture =
@@ -935,13 +938,8 @@
     }
 
     private Vibration.EndInfo startVibrationOnInputDevicesLocked(HalVibration vib) {
-        if (!vib.callerInfo.attrs.isFlagSet(
-                VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_SCALE)) {
-            // Scale resolves the default amplitudes from the effect before scaling them.
-            vib.scaleEffects(mVibrationScaler);
-        } else {
-            vib.resolveEffects(mVibrationScaler.getDefaultVibrationAmplitude());
-        }
+        // Scale resolves the default amplitudes from the effect before scaling them.
+        vib.scaleEffects(mVibrationScaler);
         mInputDeviceDelegate.vibrateIfAvailable(vib.callerInfo, vib.getEffectToPlay());
 
         return new Vibration.EndInfo(Vibration.Status.FORWARDED_TO_INPUT_DEVICES);
@@ -1304,12 +1302,13 @@
     }
 
     private void fillVibrationFallbacks(HalVibration vib, VibrationEffect effect) {
-        VibrationEffect.Composed composed = (VibrationEffect.Composed) effect;
+        if (!(effect instanceof VibrationEffect.Composed composed)) {
+            return;
+        }
         int segmentCount = composed.getSegments().size();
         for (int i = 0; i < segmentCount; i++) {
             VibrationEffectSegment segment = composed.getSegments().get(i);
-            if (segment instanceof PrebakedSegment) {
-                PrebakedSegment prebaked = (PrebakedSegment) segment;
+            if (segment instanceof PrebakedSegment prebaked) {
                 VibrationEffect fallback = mVibrationSettings.getFallbackEffect(
                         prebaked.getEffectId());
                 if (prebaked.shouldFallback() && fallback != null) {
@@ -1392,12 +1391,11 @@
 
     @Nullable
     private static PrebakedSegment extractPrebakedSegment(VibrationEffect effect) {
-        if (effect instanceof VibrationEffect.Composed) {
-            VibrationEffect.Composed composed = (VibrationEffect.Composed) effect;
+        if (effect instanceof VibrationEffect.Composed composed) {
             if (composed.getSegments().size() == 1) {
                 VibrationEffectSegment segment = composed.getSegments().get(0);
-                if (segment instanceof PrebakedSegment) {
-                    return (PrebakedSegment) segment;
+                if (segment instanceof PrebakedSegment prebaked) {
+                    return prebaked;
                 }
             }
         }
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperCropper.java b/services/core/java/com/android/server/wallpaper/WallpaperCropper.java
index f70a3ba..5d01bc3 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperCropper.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperCropper.java
@@ -601,8 +601,8 @@
                             .getDefaultDisplaySizes().get(orientation);
                     if (displayForThisOrientation == null) continue;
                     float sampleSizeForThisOrientation = Math.max(1f, Math.min(
-                            crop.width() / displayForThisOrientation.x,
-                            crop.height() / displayForThisOrientation.y));
+                            (float) crop.width() / displayForThisOrientation.x,
+                            (float) crop.height() / displayForThisOrientation.y));
                     sampleSize = Math.min(sampleSize, sampleSizeForThisOrientation);
                 }
                 // If the total crop has more width or height than either the max texture size
@@ -676,7 +676,12 @@
 
                     final Rect estimateCrop = new Rect(cropHint);
                     if (!multiCrop()) estimateCrop.scale(1f / options.inSampleSize);
-                    else estimateCrop.scale(1f / sampleSize);
+                    else {
+                        estimateCrop.left = (int) Math.floor(estimateCrop.left / sampleSize);
+                        estimateCrop.top = (int) Math.floor(estimateCrop.top / sampleSize);
+                        estimateCrop.right = (int) Math.ceil(estimateCrop.right / sampleSize);
+                        estimateCrop.bottom = (int) Math.ceil(estimateCrop.bottom / sampleSize);
+                    }
                     float hRatio = (float) wpData.mHeight / estimateCrop.height();
                     final int destHeight = (int) (estimateCrop.height() * hRatio);
                     final int destWidth = (int) (estimateCrop.width() * hRatio);
@@ -720,7 +725,10 @@
                         }
                         if (multiCrop()) {
                             Slog.v(TAG, "  cropHint=" + cropHint);
+                            Slog.v(TAG, "  estimateCrop=" + estimateCrop);
                             Slog.v(TAG, "  sampleSize=" + sampleSize);
+                            Slog.v(TAG, "  user defined crops: " + wallpaper.mCropHints);
+                            Slog.v(TAG, "  all crops: " + defaultCrops);
                         }
                         Slog.v(TAG, "  targetSize=" + safeWidth + "x" + safeHeight);
                         Slog.v(TAG, "  maxTextureSize=" + GLHelper.getMaxTextureSize());
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 72c7be3..ba2594a 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -3844,6 +3844,7 @@
             pw.print("  mPadding="); pw.println(wpSize.mPadding);
         });
         pw.print("  mCropHint="); pw.println(wallpaper.cropHint);
+        if (multiCrop()) pw.print("  mCropHints="); pw.println(wallpaper.mCropHints);
         pw.print("  mName=");  pw.println(wallpaper.name);
         pw.print("  mAllowBackup="); pw.println(wallpaper.allowBackup);
         pw.print("  mWallpaperComponent="); pw.println(wallpaper.wallpaperComponent);
diff --git a/services/core/java/com/android/server/wm/AbsAppSnapshotController.java b/services/core/java/com/android/server/wm/AbsAppSnapshotController.java
index 09de01e..68f3738 100644
--- a/services/core/java/com/android/server/wm/AbsAppSnapshotController.java
+++ b/services/core/java/com/android/server/wm/AbsAppSnapshotController.java
@@ -43,7 +43,6 @@
 import android.view.SurfaceControl;
 import android.view.ThreadedRenderer;
 import android.view.WindowInsets;
-import android.view.WindowInsetsController;
 import android.view.WindowManager;
 import android.window.ScreenCapture;
 import android.window.SnapshotDrawerUtils;
@@ -137,7 +136,6 @@
     }
 
     abstract ActivityRecord getTopActivity(TYPE source);
-    abstract WindowState getTopFullscreenWindow(TYPE source);
     abstract ActivityManager.TaskDescription getTaskDescription(TYPE source);
     /**
      * Find the window for a given task to take a snapshot. Top child of the task is usually the one
@@ -331,7 +329,7 @@
         builder.setPixelFormat(pixelFormat);
         builder.setIsTranslucent(isTranslucent);
         builder.setWindowingMode(source.getWindowingMode());
-        builder.setAppearance(getAppearance(source));
+        builder.setAppearance(mainWindow.mAttrs.insetsFlags.appearance);
 
         final Configuration taskConfig = activity.getTask().getConfiguration();
         final int displayRotation = taskConfig.windowConfiguration.getDisplayRotation();
@@ -450,7 +448,7 @@
                 mainWindow.getWindowConfiguration().getRotation(), new Point(taskWidth, taskHeight),
                 contentInsets, letterboxInsets, false /* isLowResolution */,
                 false /* isRealSnapshot */, source.getWindowingMode(),
-                getAppearance(source), false /* isTranslucent */, false /* hasImeSurface */);
+                attrs.insetsFlags.appearance, false /* isTranslucent */, false /* hasImeSurface */);
         return validateSnapshot(taskSnapshot);
     }
 
@@ -460,19 +458,6 @@
     }
 
     /**
-     * @return The {@link WindowInsetsController.Appearance} flags for the top main app window in
-     * the given {@param TYPE}.
-     */
-    @WindowInsetsController.Appearance
-    private int getAppearance(TYPE source) {
-        final WindowState topFullscreenWindow = getTopFullscreenWindow(source);
-        if (topFullscreenWindow != null) {
-            return topFullscreenWindow.mAttrs.insetsFlags.appearance;
-        }
-        return 0;
-    }
-
-    /**
      * Called when an {@link ActivityRecord} has been removed.
      */
     void onAppRemoved(ActivityRecord activity) {
diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java
index a8dcaa8..0c10551 100644
--- a/services/core/java/com/android/server/wm/ActivityClientController.java
+++ b/services/core/java/com/android/server/wm/ActivityClientController.java
@@ -68,7 +68,6 @@
 import android.app.ActivityTaskManager;
 import android.app.FullscreenRequestHandler;
 import android.app.IActivityClientController;
-import android.app.ICompatCameraControlCallback;
 import android.app.IRequestFinishCallback;
 import android.app.PictureInPictureParams;
 import android.app.PictureInPictureUiState;
@@ -1008,22 +1007,6 @@
         Binder.restoreCallingIdentity(origId);
     }
 
-    @Override
-    public void requestCompatCameraControl(IBinder token, boolean showControl,
-            boolean transformationApplied, ICompatCameraControlCallback callback) {
-        final long origId = Binder.clearCallingIdentity();
-        try {
-            synchronized (mGlobalLock) {
-                final ActivityRecord r = ActivityRecord.isInRootTaskLocked(token);
-                if (r != null) {
-                    r.updateCameraCompatState(showControl, transformationApplied, callback);
-                }
-            }
-        } finally {
-            Binder.restoreCallingIdentity(origId);
-        }
-    }
-
     /**
      * Initialize the {@link #mSetPipAspectRatioQuotaTracker} if applicable, which should happen
      * out of {@link #mGlobalLock} to avoid deadlock (AM lock is used in QuotaTrack ctor).
@@ -1124,8 +1107,8 @@
         }
 
         try {
-            mService.getLifecycleManager().scheduleTransactionItem(r.app.getThread(),
-                    EnterPipRequestedItem.obtain(r.token));
+            final EnterPipRequestedItem item = new EnterPipRequestedItem(r.token);
+            mService.getLifecycleManager().scheduleTransactionItem(r.app.getThread(), item);
             return true;
         } catch (Exception e) {
             Slog.w(TAG, "Failed to send enter pip requested item: "
@@ -1140,8 +1123,8 @@
     void onPictureInPictureUiStateChanged(@NonNull ActivityRecord r,
             PictureInPictureUiState pipState) {
         try {
-            mService.getLifecycleManager().scheduleTransactionItem(r.app.getThread(),
-                    PipStateTransactionItem.obtain(r.token, pipState));
+            final PipStateTransactionItem item = new PipStateTransactionItem(r.token, pipState);
+            mService.getLifecycleManager().scheduleTransactionItem(r.app.getThread(), item);
         } catch (Exception e) {
             Slog.w(TAG, "Failed to send pip state transaction item: "
                     + r.intent.getComponent(), e);
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index b3208bf..fb2bf39 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -4,10 +4,6 @@
 import static android.app.ActivityManager.START_SUCCESS;
 import static android.app.ActivityManager.START_TASK_TO_FRONT;
 import static android.app.ActivityManager.processStateAmToProto;
-import static android.app.CameraCompatTaskInfo.CAMERA_COMPAT_CONTROL_DISMISSED;
-import static android.app.CameraCompatTaskInfo.CAMERA_COMPAT_CONTROL_HIDDEN;
-import static android.app.CameraCompatTaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED;
-import static android.app.CameraCompatTaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED;
 import static android.app.WaitResult.INVALID_DELAY;
 import static android.app.WaitResult.LAUNCH_STATE_COLD;
 import static android.app.WaitResult.LAUNCH_STATE_HOT;
@@ -69,11 +65,6 @@
 import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__STATE__NOT_VISIBLE;
 import static com.android.internal.util.FrameworkStatsLog.APP_START_OCCURRED__PACKAGE_STOPPED_STATE__PACKAGE_STATE_NORMAL;
 import static com.android.internal.util.FrameworkStatsLog.APP_START_OCCURRED__PACKAGE_STOPPED_STATE__PACKAGE_STATE_STOPPED;
-import static com.android.internal.util.FrameworkStatsLog.CAMERA_COMPAT_CONTROL_EVENT_REPORTED__EVENT__APPEARED_APPLY_TREATMENT;
-import static com.android.internal.util.FrameworkStatsLog.CAMERA_COMPAT_CONTROL_EVENT_REPORTED__EVENT__APPEARED_REVERT_TREATMENT;
-import static com.android.internal.util.FrameworkStatsLog.CAMERA_COMPAT_CONTROL_EVENT_REPORTED__EVENT__CLICKED_APPLY_TREATMENT;
-import static com.android.internal.util.FrameworkStatsLog.CAMERA_COMPAT_CONTROL_EVENT_REPORTED__EVENT__CLICKED_DISMISS;
-import static com.android.internal.util.FrameworkStatsLog.CAMERA_COMPAT_CONTROL_EVENT_REPORTED__EVENT__CLICKED_REVERT_TREATMENT;
 import static com.android.server.am.MemoryStatUtil.MemoryStat;
 import static com.android.server.am.MemoryStatUtil.readMemoryStatFromFilesystem;
 import static com.android.server.am.ProcessList.INVALID_ADJ;
@@ -89,7 +80,6 @@
 import android.app.ActivityOptions;
 import android.app.ActivityOptions.SourceInfo;
 import android.app.ApplicationStartInfo;
-import android.app.CameraCompatTaskInfo.CameraCompatControlState;
 import android.app.WaitResult;
 import android.app.WindowConfiguration.WindowingMode;
 import android.content.ComponentName;
@@ -1622,7 +1612,8 @@
 
         int positionToLog = APP_COMPAT_STATE_CHANGED__LETTERBOX_POSITION__NOT_LETTERBOXED_POSITION;
         if (isAppCompateStateChangedToLetterboxed(state)) {
-            positionToLog = activity.mLetterboxUiController.getLetterboxPositionForLogging();
+            positionToLog = activity.mAppCompatController.getAppCompatReachabilityOverrides()
+                    .getLetterboxPositionForLogging();
         }
         FrameworkStatsLog.write(FrameworkStatsLog.APP_COMPAT_STATE_CHANGED,
                 packageUid, state, positionToLog);
@@ -1662,71 +1653,6 @@
         }
     }
 
-    /**
-     * Logs the Camera Compat Control appeared event that corresponds to the given {@code state}
-     * with the given {@code packageUid}.
-     */
-    void logCameraCompatControlAppearedEventReported(@CameraCompatControlState int state,
-            int packageUid) {
-        switch (state) {
-            case CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED:
-                logCameraCompatControlEventReported(
-                        CAMERA_COMPAT_CONTROL_EVENT_REPORTED__EVENT__APPEARED_APPLY_TREATMENT,
-                        packageUid);
-                break;
-            case CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED:
-                logCameraCompatControlEventReported(
-                        CAMERA_COMPAT_CONTROL_EVENT_REPORTED__EVENT__APPEARED_REVERT_TREATMENT,
-                        packageUid);
-                break;
-            case CAMERA_COMPAT_CONTROL_HIDDEN:
-                // Nothing to log.
-                break;
-            default:
-                Slog.w(TAG, "Unexpected state in logCameraCompatControlAppearedEventReported: "
-                        + state);
-                break;
-        }
-    }
-
-    /**
-     * Logs the Camera Compat Control clicked event that corresponds to the given {@code state}
-     * with the given {@code packageUid}.
-     */
-    void logCameraCompatControlClickedEventReported(@CameraCompatControlState int state,
-            int packageUid) {
-        switch (state) {
-            case CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED:
-                logCameraCompatControlEventReported(
-                        CAMERA_COMPAT_CONTROL_EVENT_REPORTED__EVENT__CLICKED_APPLY_TREATMENT,
-                        packageUid);
-                break;
-            case CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED:
-                logCameraCompatControlEventReported(
-                        CAMERA_COMPAT_CONTROL_EVENT_REPORTED__EVENT__CLICKED_REVERT_TREATMENT,
-                        packageUid);
-                break;
-            case CAMERA_COMPAT_CONTROL_DISMISSED:
-                logCameraCompatControlEventReported(
-                        CAMERA_COMPAT_CONTROL_EVENT_REPORTED__EVENT__CLICKED_DISMISS,
-                        packageUid);
-                break;
-            default:
-                Slog.w(TAG, "Unexpected state in logCameraCompatControlAppearedEventReported: "
-                        + state);
-                break;
-        }
-    }
-
-    private void logCameraCompatControlEventReported(int event, int packageUid) {
-        FrameworkStatsLog.write(FrameworkStatsLog.CAMERA_COMPAT_CONTROL_EVENT_REPORTED, packageUid,
-                event);
-        if (DEBUG_METRICS) {
-            Slog.i(TAG, String.format("CAMERA_COMPAT_CONTROL_EVENT_REPORTED(%s, %s)", packageUid,
-                    event));
-        }
-    }
-
     private ArtManagerInternal getArtManagerInternal() {
         if (mArtManagerInternal == null) {
             // Note that this may be null.
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 400919a..eea3ab8 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -33,12 +33,7 @@
 import static android.app.ActivityTaskManager.INVALID_TASK_ID;
 import static android.app.AppOpsManager.MODE_ALLOWED;
 import static android.app.AppOpsManager.OP_PICTURE_IN_PICTURE;
-import static android.app.CameraCompatTaskInfo.CAMERA_COMPAT_CONTROL_DISMISSED;
-import static android.app.CameraCompatTaskInfo.CAMERA_COMPAT_CONTROL_HIDDEN;
-import static android.app.CameraCompatTaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED;
-import static android.app.CameraCompatTaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED;
 import static android.app.CameraCompatTaskInfo.CAMERA_COMPAT_FREEFORM_NONE;
-import static android.app.CameraCompatTaskInfo.cameraCompatControlStateToString;
 import static android.app.WaitResult.INVALID_DELAY;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM;
@@ -53,7 +48,6 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.app.WindowConfiguration.activityTypeToString;
-import static android.app.WindowConfiguration.isFloating;
 import static android.app.admin.DevicePolicyResources.Drawables.Source.PROFILE_SWITCH_ANIMATION;
 import static android.app.admin.DevicePolicyResources.Drawables.Style.OUTLINE;
 import static android.app.admin.DevicePolicyResources.Drawables.WORK_PROFILE_ICON;
@@ -272,8 +266,6 @@
 import android.app.Activity;
 import android.app.ActivityManager.TaskDescription;
 import android.app.ActivityOptions;
-import android.app.CameraCompatTaskInfo.CameraCompatControlState;
-import android.app.ICompatCameraControlCallback;
 import android.app.IScreenCaptureObserver;
 import android.app.PendingIntent;
 import android.app.PictureInPictureParams;
@@ -336,7 +328,6 @@
 import android.service.dreams.DreamActivity;
 import android.service.voice.IVoiceInteractionSession;
 import android.util.ArraySet;
-import android.util.DisplayMetrics;
 import android.util.EventLog;
 import android.util.Log;
 import android.util.MergedConfiguration;
@@ -373,6 +364,7 @@
 
 import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.KeepForWeakReference;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.ResolverActivity;
 import com.android.internal.content.ReferrerIntent;
@@ -497,7 +489,7 @@
     final String launchedFromPackage; // always the package who started the activity.
     @Nullable
     final String launchedFromFeatureId; // always the feature in launchedFromPackage
-    private final int mLaunchSourceType; // original launch source type
+    int mLaunchSourceType; // latest launch source type
     final Intent intent;    // the original intent that generated us
     final String shortComponentName; // the short component name of the intent
     final String resolvedType; // as per original caller;
@@ -826,19 +818,6 @@
     // and therefore #isLetterboxedForFixedOrientationAndAspectRatio returns false.
     private boolean mIsEligibleForFixedOrientationLetterbox;
 
-    // State of the Camera app compat control which is used to correct stretched viewfinder
-    // in apps that don't handle all possible configurations and changes between them correctly.
-    @CameraCompatControlState
-    private int mCameraCompatControlState = CAMERA_COMPAT_CONTROL_HIDDEN;
-
-    // The callback that allows to ask the calling View to apply the treatment for stretched
-    // issues affecting camera viewfinders when the user clicks on the camera compat control.
-    @Nullable
-    private ICompatCameraControlCallback mCompatCameraControlCallback;
-
-    private final boolean mCameraCompatControlEnabled;
-    private boolean mCameraCompatControlClickedByUser;
-
     // activity is not displayed?
     // TODO: rename to mNoDisplay
     @VisibleForTesting
@@ -943,6 +922,8 @@
 
     private RemoteCallbackList<IScreenCaptureObserver> mCaptureCallbacks;
 
+    // Ensure the field is kept during optimization to preserve downstream weak refs.
+    @KeepForWeakReference
     private final ColorDisplayService.ColorTransformController mColorTransformController =
             (matrix, translation) -> mWmService.mH.post(() -> {
                 synchronized (mWmService.mGlobalLock) {
@@ -1344,10 +1325,6 @@
         }
 
         mLetterboxUiController.dump(pw, prefix);
-
-        pw.println(prefix + "mCameraCompatControlState="
-                + cameraCompatControlStateToString(mCameraCompatControlState));
-        pw.println(prefix + "mCameraCompatControlEnabled=" + mCameraCompatControlEnabled);
     }
 
     static boolean dumpActivity(FileDescriptor fd, PrintWriter pw, int index, ActivityRecord r,
@@ -1469,8 +1446,9 @@
                     + "display, activityRecord=%s, displayId=%d, config=%s", this, displayId,
                     config);
 
-            mAtmService.getLifecycleManager().scheduleTransactionItem(app.getThread(),
-                    MoveToDisplayItem.obtain(token, displayId, config, activityWindowInfo));
+            final MoveToDisplayItem item =
+                    new MoveToDisplayItem(token, displayId, config, activityWindowInfo);
+            mAtmService.getLifecycleManager().scheduleTransactionItem(app.getThread(), item);
         } catch (RemoteException e) {
             // If process died, whatever.
         }
@@ -1487,8 +1465,9 @@
             ProtoLog.v(WM_DEBUG_CONFIGURATION, "Sending new config to %s, "
                     + "config: %s", this, config);
 
-            mAtmService.getLifecycleManager().scheduleTransactionItem(app.getThread(),
-                    ActivityConfigurationChangeItem.obtain(token, config, activityWindowInfo));
+            final ActivityConfigurationChangeItem item =
+                    new ActivityConfigurationChangeItem(token, config, activityWindowInfo);
+            mAtmService.getLifecycleManager().scheduleTransactionItem(app.getThread(), item);
         } catch (RemoteException e) {
             // If process died, whatever.
         }
@@ -1508,8 +1487,9 @@
             ProtoLog.v(WM_DEBUG_STATES, "Sending position change to %s, onTop: %b",
                     this, onTop);
 
-            mAtmService.getLifecycleManager().scheduleTransactionItem(app.getThread(),
-                    TopResumedActivityChangeItem.obtain(token, onTop));
+            final TopResumedActivityChangeItem item =
+                    new TopResumedActivityChangeItem(token, onTop);
+            mAtmService.getLifecycleManager().scheduleTransactionItem(app.getThread(), item);
         } catch (RemoteException e) {
             // If process died, whatever.
             Slog.w(TAG, "Failed to send top-resumed=" + onTop + " to " + this, e);
@@ -1883,100 +1863,6 @@
         mLetterboxUiController.getLetterboxInnerBounds(outBounds);
     }
 
-    void updateCameraCompatState(boolean showControl, boolean transformationApplied,
-            ICompatCameraControlCallback callback) {
-        if (!isCameraCompatControlEnabled()) {
-            // Feature is disabled by config_isCameraCompatControlForStretchedIssuesEnabled.
-            return;
-        }
-        if (mCameraCompatControlClickedByUser && (showControl
-                || mCameraCompatControlState == CAMERA_COMPAT_CONTROL_DISMISSED)) {
-            // The user already applied treatment on this activity or dismissed control.
-            // Respecting their choice.
-            return;
-        }
-        mCompatCameraControlCallback = callback;
-        int newCameraCompatControlState = !showControl
-                ? CAMERA_COMPAT_CONTROL_HIDDEN
-                : transformationApplied
-                        ? CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED
-                        : CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED;
-        boolean changed = setCameraCompatControlState(newCameraCompatControlState);
-        if (!changed) {
-            return;
-        }
-        mTaskSupervisor.getActivityMetricsLogger().logCameraCompatControlAppearedEventReported(
-                newCameraCompatControlState, info.applicationInfo.uid);
-        if (newCameraCompatControlState == CAMERA_COMPAT_CONTROL_HIDDEN) {
-            mCameraCompatControlClickedByUser = false;
-            mCompatCameraControlCallback = null;
-        }
-        // Trigger TaskInfoChanged to update the camera compat UI.
-        getTask().dispatchTaskInfoChangedIfNeeded(true /* force */);
-        // TaskOrganizerController#onTaskInfoChanged adds pending task events to the queue waiting
-        // for the surface placement to be ready. So need to trigger surface placement to dispatch
-        // events to avoid stale state for the camera compat control.
-        getDisplayContent().setLayoutNeeded();
-        mWmService.mWindowPlacerLocked.performSurfacePlacement();
-    }
-
-    void updateCameraCompatStateFromUser(@CameraCompatControlState int state) {
-        if (!isCameraCompatControlEnabled()) {
-            // Feature is disabled by config_isCameraCompatControlForStretchedIssuesEnabled.
-            return;
-        }
-        if (state == CAMERA_COMPAT_CONTROL_HIDDEN) {
-            Slog.w(TAG, "Unexpected hidden state in updateCameraCompatState");
-            return;
-        }
-        boolean changed = setCameraCompatControlState(state);
-        mCameraCompatControlClickedByUser = true;
-        if (!changed) {
-            return;
-        }
-        mTaskSupervisor.getActivityMetricsLogger().logCameraCompatControlClickedEventReported(
-                state, info.applicationInfo.uid);
-        if (state == CAMERA_COMPAT_CONTROL_DISMISSED) {
-            mCompatCameraControlCallback = null;
-            return;
-        }
-        if (mCompatCameraControlCallback == null) {
-            Slog.w(TAG, "Callback for a camera compat control is null");
-            return;
-        }
-        try {
-            if (state == CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED) {
-                mCompatCameraControlCallback.applyCameraCompatTreatment();
-            } else {
-                mCompatCameraControlCallback.revertCameraCompatTreatment();
-            }
-        } catch (RemoteException e) {
-            Slog.e(TAG, "Unable to apply or revert camera compat treatment", e);
-        }
-    }
-
-    private boolean setCameraCompatControlState(@CameraCompatControlState int state) {
-        if (!isCameraCompatControlEnabled()) {
-            // Feature is disabled by config_isCameraCompatControlForStretchedIssuesEnabled.
-            return false;
-        }
-        if (mCameraCompatControlState != state) {
-            mCameraCompatControlState = state;
-            return true;
-        }
-        return false;
-    }
-
-    @CameraCompatControlState
-    int getCameraCompatControlState() {
-        return mCameraCompatControlState;
-    }
-
-    @VisibleForTesting
-    boolean isCameraCompatControlEnabled() {
-        return mCameraCompatControlEnabled;
-    }
-
     /**
      * @return {@code true} if bar shown within a given rectangle is allowed to be fully transparent
      *     when the current activity is displayed.
@@ -2104,8 +1990,6 @@
         // initialised.
         mLetterboxUiController = new LetterboxUiController(mWmService, this);
         mAppCompatController = new AppCompatController(mWmService, this);
-        mCameraCompatControlEnabled = mWmService.mContext.getResources()
-                .getBoolean(R.bool.config_isCameraCompatControlForStretchedIssuesEnabled);
         mResolveConfigHint = new TaskFragment.ConfigOverrideHint();
         if (mWmService.mFlags.mInsetsDecoupledConfiguration) {
             // When the stable configuration is the default behavior, override for the legacy apps
@@ -2450,6 +2334,10 @@
         return mLaunchSourceType == type;
     }
 
+    void updateLaunchSourceType(int launchFromUid, WindowProcessController caller) {
+        mLaunchSourceType = determineLaunchSourceType(launchFromUid, caller);
+    }
+
     private int determineLaunchSourceType(int launchFromUid, WindowProcessController caller) {
         if (launchFromUid == Process.SYSTEM_UID || launchFromUid == Process.ROOT_UID) {
             return LAUNCH_SOURCE_TYPE_SYSTEM;
@@ -2812,9 +2700,9 @@
         }
         try {
             mTransferringSplashScreenState = TRANSFER_SPLASH_SCREEN_ATTACH_TO_CLIENT;
-            mAtmService.getLifecycleManager().scheduleTransactionItem(app.getThread(),
-                    TransferSplashScreenViewStateItem.obtain(token, parcelable,
-                            windowAnimationLeash));
+            final TransferSplashScreenViewStateItem item =
+                    new TransferSplashScreenViewStateItem(token, parcelable, windowAnimationLeash);
+            mAtmService.getLifecycleManager().scheduleTransactionItem(app.getThread(), item);
             scheduleTransferSplashScreenTimeout();
         } catch (Exception e) {
             Slog.w(TAG, "onCopySplashScreenComplete fail: " + this);
@@ -4080,8 +3968,8 @@
 
             try {
                 if (DEBUG_SWITCH) Slog.i(TAG_SWITCH, "Destroying: " + this);
-                mAtmService.getLifecycleManager().scheduleTransactionItem(app.getThread(),
-                        DestroyActivityItem.obtain(token, finishing));
+                final DestroyActivityItem item = new DestroyActivityItem(token, finishing);
+                mAtmService.getLifecycleManager().scheduleTransactionItem(app.getThread(), item);
             } catch (Exception e) {
                 // We can just ignore exceptions here...  if the process has crashed, our death
                 // notification will clean things up.
@@ -4289,7 +4177,8 @@
     }
 
     void finishRelaunching() {
-        mLetterboxUiController.setRelaunchingAfterRequestedOrientationChanged(false);
+        mAppCompatController.getAppCompatOrientationOverrides()
+                .setRelaunchingAfterRequestedOrientationChanged(false);
         mTaskSupervisor.getActivityMetricsLogger().notifyActivityRelaunched(this);
 
         if (mPendingRelaunchCount > 0) {
@@ -4995,8 +4884,8 @@
             try {
                 final ArrayList<ResultInfo> list = new ArrayList<>();
                 list.add(new ResultInfo(resultWho, requestCode, resultCode, data, callerToken));
-                mAtmService.getLifecycleManager().scheduleTransactionItem(app.getThread(),
-                        ActivityResultItem.obtain(token, list));
+                final ActivityResultItem item = new ActivityResultItem(token, list);
+                mAtmService.getLifecycleManager().scheduleTransactionItem(app.getThread(), item);
                 return;
             } catch (Exception e) {
                 Slog.w(TAG, "Exception thrown sending result to " + this, e);
@@ -5007,9 +4896,9 @@
         if (forceSendForMediaProjection && attachedToProcess() && isState(STARTED, PAUSING, PAUSED,
                 STOPPING, STOPPED)) {
             // Build result to be returned immediately.
-            final ActivityResultItem activityResultItem = ActivityResultItem.obtain(
-                    token, List.of(new ResultInfo(resultWho, requestCode, resultCode, data,
-                            callerToken)));
+            final List<ResultInfo> infos = List.of(
+                    new ResultInfo(resultWho, requestCode, resultCode, data, callerToken));
+            final ActivityResultItem activityResultItem = new ActivityResultItem(token, infos);
             // When the activity result is delivered, the activity will transition to RESUMED.
             // Since the activity is only resumed so the result can be immediately delivered,
             // return it to its original lifecycle state.
@@ -5052,13 +4941,13 @@
     private ActivityLifecycleItem getLifecycleItemForCurrentStateForResult() {
         switch (mState) {
             case STARTED:
-                return StartActivityItem.obtain(token, null);
+                return new StartActivityItem(token, null);
             case PAUSING:
             case PAUSED:
-                return PauseActivityItem.obtain(token);
+                return new PauseActivityItem(token);
             case STOPPING:
             case STOPPED:
-                return StopActivityItem.obtain(token);
+                return new StopActivityItem(token);
             default:
                 // Do not send a result immediately if the activity is in state INITIALIZING,
                 // RESTARTING_PROCESS, FINISHING, DESTROYING, or DESTROYED.
@@ -5113,8 +5002,9 @@
                 // Making sure the client state is RESUMED after transaction completed and doing
                 // so only if activity is currently RESUMED. Otherwise, client may have extra
                 // life-cycle calls to RESUMED (and PAUSED later).
-                mAtmService.getLifecycleManager().scheduleTransactionItem(app.getThread(),
-                        NewIntentItem.obtain(token, ar, mState == RESUMED));
+                final NewIntentItem item =
+                        new NewIntentItem(token, ar, mState == RESUMED /* resume */);
+                mAtmService.getLifecycleManager().scheduleTransactionItem(app.getThread(), item);
                 unsent = false;
             } catch (RemoteException e) {
                 Slog.w(TAG, "Exception thrown sending new intent to " + this, e);
@@ -6346,9 +6236,9 @@
             EventLogTags.writeWmPauseActivity(mUserId, System.identityHashCode(this),
                     shortComponentName, "userLeaving=false", "make-active");
             try {
-                mAtmService.getLifecycleManager().scheduleTransactionItem(app.getThread(),
-                        PauseActivityItem.obtain(token, finishing, false /* userLeaving */,
-                                false /* dontReport */, mAutoEnteringPip));
+                final PauseActivityItem item = new PauseActivityItem(token, finishing,
+                        false /* userLeaving */, false /* dontReport */, mAutoEnteringPip);
+                mAtmService.getLifecycleManager().scheduleTransactionItem(app.getThread(), item);
             } catch (Exception e) {
                 Slog.w(TAG, "Exception thrown sending pause: " + intent.getComponent(), e);
             }
@@ -6360,7 +6250,7 @@
 
             try {
                 mAtmService.getLifecycleManager().scheduleTransactionItem(app.getThread(),
-                        StartActivityItem.obtain(token, takeSceneTransitionInfo()));
+                        new StartActivityItem(token, takeSceneTransitionInfo()));
             } catch (Exception e) {
                 Slog.w(TAG, "Exception thrown sending start: " + intent.getComponent(), e);
             }
@@ -6657,7 +6547,7 @@
             EventLogTags.writeWmStopActivity(
                     mUserId, System.identityHashCode(this), shortComponentName);
             mAtmService.getLifecycleManager().scheduleTransactionItem(app.getThread(),
-                    StopActivityItem.obtain(token));
+                    new StopActivityItem(token));
 
             mAtmService.mH.postDelayed(mStopTimeoutRunnable, STOP_TIMEOUT);
         } catch (Exception e) {
@@ -8179,7 +8069,8 @@
                 mLastReportedConfiguration.getMergedConfiguration())) {
             ensureActivityConfiguration(false /* ignoreVisibility */);
             if (mPendingRelaunchCount > originalRelaunchingCount) {
-                mLetterboxUiController.setRelaunchingAfterRequestedOrientationChanged(true);
+                mAppCompatController.getAppCompatOrientationOverrides()
+                        .setRelaunchingAfterRequestedOrientationChanged(true);
             }
             if (mTransitionController.inPlayingTransition(this)) {
                 mTransitionController.mValidateActivityCompat.add(this);
@@ -8395,7 +8286,8 @@
      */
     @ActivityInfo.SizeChangesSupportMode
     private int supportsSizeChanges() {
-        if (mLetterboxUiController.shouldOverrideForceNonResizeApp()) {
+        if (mAppCompatController.getAppCompatResizeOverrides()
+                .shouldOverrideForceNonResizeApp()) {
             return SIZE_CHANGES_UNSUPPORTED_OVERRIDE;
         }
 
@@ -8403,7 +8295,8 @@
             return SIZE_CHANGES_SUPPORTED_METADATA;
         }
 
-        if (mLetterboxUiController.shouldOverrideForceResizeApp()) {
+        if (mAppCompatController.getAppCompatResizeOverrides()
+                .shouldOverrideForceResizeApp()) {
             return SIZE_CHANGES_SUPPORTED_OVERRIDE;
         }
 
@@ -8648,7 +8541,14 @@
             resolvedConfig.windowConfiguration.setMaxBounds(mTmpBounds);
         }
 
-        applySizeOverrideIfNeeded(newParentConfiguration, parentWindowingMode, resolvedConfig);
+        applySizeOverrideIfNeeded(
+                mDisplayContent,
+                info.applicationInfo,
+                newParentConfiguration,
+                resolvedConfig,
+                mOptOutEdgeToEdge,
+                hasFixedRotationTransform(),
+                getCompatDisplayInsets() != null);
         mResolveConfigHint.resetTmpOverrides();
 
         logAppCompatState();
@@ -8658,100 +8558,6 @@
         return Rect.copyOrNull(mResolveConfigHint.mParentAppBoundsOverride);
     }
 
-    /**
-     * If necessary, override configuration fields related to app bounds.
-     * This will happen when the app is targeting SDK earlier than 35.
-     * The insets and configuration has decoupled since SDK level 35, to make the system
-     * compatible to existing apps, override the configuration with legacy metrics. In legacy
-     * metrics, fields such as appBounds will exclude some of the system bar areas.
-     * The override contains all potentially affected fields in Configuration, including
-     * screenWidthDp, screenHeightDp, smallestScreenWidthDp, and orientation.
-     * All overrides to those fields should be in this method.
-     *
-     * TODO: Consider integrate this with computeConfigByResolveHint()
-     */
-    private void applySizeOverrideIfNeeded(Configuration newParentConfiguration,
-            int parentWindowingMode, Configuration inOutConfig) {
-        if (mDisplayContent == null) {
-            return;
-        }
-        final Rect parentBounds = newParentConfiguration.windowConfiguration.getBounds();
-        int rotation = newParentConfiguration.windowConfiguration.getRotation();
-        if (rotation == ROTATION_UNDEFINED && !isFixedRotationTransforming()) {
-            rotation = mDisplayContent.getRotation();
-        }
-        if (!mOptOutEdgeToEdge && (!mResolveConfigHint.mUseOverrideInsetsForConfig
-                || getCompatDisplayInsets() != null
-                || (isFloating(parentWindowingMode)
-                        // Check the requested windowing mode of activity as well in case it is
-                        // switching between PiP and fullscreen.
-                        && (inOutConfig.windowConfiguration.getWindowingMode()
-                                == WINDOWING_MODE_UNDEFINED
-                                || isFloating(inOutConfig.windowConfiguration.getWindowingMode())))
-                || rotation == ROTATION_UNDEFINED)) {
-            // If the insets configuration decoupled logic is not enabled for the app, or the app
-            // already has a compat override, or the context doesn't contain enough info to
-            // calculate the override, skip the override.
-            return;
-        }
-        // Make sure the orientation related fields will be updated by the override insets, because
-        // fixed rotation has assigned the fields from display's configuration.
-        if (hasFixedRotationTransform()) {
-            inOutConfig.windowConfiguration.setAppBounds(null);
-            inOutConfig.screenWidthDp = Configuration.SCREEN_WIDTH_DP_UNDEFINED;
-            inOutConfig.screenHeightDp = Configuration.SCREEN_HEIGHT_DP_UNDEFINED;
-            inOutConfig.smallestScreenWidthDp = Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED;
-            inOutConfig.orientation = ORIENTATION_UNDEFINED;
-        }
-
-        // Override starts here.
-        final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270);
-        final int dw = rotated ? mDisplayContent.mBaseDisplayHeight
-                : mDisplayContent.mBaseDisplayWidth;
-        final int dh = rotated ? mDisplayContent.mBaseDisplayWidth
-                : mDisplayContent.mBaseDisplayHeight;
-        final Rect nonDecorInsets = mDisplayContent.getDisplayPolicy()
-                .getDecorInsetsInfo(rotation, dw, dh).mOverrideNonDecorInsets;
-        // This should be the only place override the configuration for ActivityRecord. Override
-        // the value if not calculated yet.
-        Rect outAppBounds = inOutConfig.windowConfiguration.getAppBounds();
-        if (outAppBounds == null || outAppBounds.isEmpty()) {
-            inOutConfig.windowConfiguration.setAppBounds(parentBounds);
-            outAppBounds = inOutConfig.windowConfiguration.getAppBounds();
-            outAppBounds.inset(nonDecorInsets);
-        }
-        float density = inOutConfig.densityDpi;
-        if (density == Configuration.DENSITY_DPI_UNDEFINED) {
-            density = newParentConfiguration.densityDpi;
-        }
-        density *= DisplayMetrics.DENSITY_DEFAULT_SCALE;
-        if (inOutConfig.screenWidthDp == Configuration.SCREEN_WIDTH_DP_UNDEFINED) {
-            inOutConfig.screenWidthDp = (int) (outAppBounds.width() / density + 0.5f);
-        }
-        if (inOutConfig.screenHeightDp == Configuration.SCREEN_HEIGHT_DP_UNDEFINED) {
-            inOutConfig.screenHeightDp = (int) (outAppBounds.height() / density + 0.5f);
-        }
-        if (inOutConfig.smallestScreenWidthDp
-                == Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED
-                && parentWindowingMode == WINDOWING_MODE_FULLSCREEN) {
-            // For the case of PIP transition and multi-window environment, the
-            // smallestScreenWidthDp is handled already. Override only if the app is in
-            // fullscreen.
-            final DisplayInfo info = new DisplayInfo(mDisplayContent.getDisplayInfo());
-            mDisplayContent.computeSizeRanges(info, rotated, dw, dh,
-                    mDisplayContent.getDisplayMetrics().density,
-                    inOutConfig, true /* overrideConfig */);
-        }
-
-        // It's possible that screen size will be considered in different orientation with or
-        // without considering the system bar insets. Override orientation as well.
-        if (inOutConfig.orientation == ORIENTATION_UNDEFINED) {
-            inOutConfig.orientation =
-                    (inOutConfig.screenWidthDp <= inOutConfig.screenHeightDp)
-                            ? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE;
-        }
-    }
-
     private void computeConfigByResolveHint(@NonNull Configuration resolvedConfig,
             @NonNull Configuration parentConfig) {
         task.computeConfigResourceOverrides(resolvedConfig, parentConfig, mResolveConfigHint);
@@ -8827,8 +8633,8 @@
     /**
      * Adjusts position of resolved bounds if they don't fill the parent using gravity
      * requested in the config or via an ADB command. For more context see {@link
-     * LetterboxUiController#getHorizontalPositionMultiplier(Configuration)} and
-     * {@link LetterboxUiController#getVerticalPositionMultiplier(Configuration)}
+     * AppCompatReachabilityOverrides#getHorizontalPositionMultiplier(Configuration)} and
+     * {@link AppCompatReachabilityOverrides#getVerticalPositionMultiplier(Configuration)}
      * <p>
      * Note that this is the final step that can change the resolved bounds. After this method
      * is called, the position of the bounds will be moved to app space as sandboxing if the
@@ -8857,11 +8663,13 @@
         } else {
             navBarInsets = Insets.NONE;
         }
+        final AppCompatReachabilityOverrides reachabilityOverrides =
+                mAppCompatController.getAppCompatReachabilityOverrides();
         // Horizontal position
         int offsetX = 0;
         if (parentBounds.width() != screenResolvedBoundsWidth) {
             if (screenResolvedBoundsWidth <= parentAppBoundsWidth) {
-                float positionMultiplier = mLetterboxUiController.getHorizontalPositionMultiplier(
+                float positionMultiplier = reachabilityOverrides.getHorizontalPositionMultiplier(
                         newParentConfiguration);
                 // If in immersive mode, always align to right and overlap right insets (task bar)
                 // as they are transient and hidden. This removes awkward right spacing.
@@ -8882,7 +8690,7 @@
         int offsetY = 0;
         if (parentBoundsHeight != screenResolvedBoundsHeight) {
             if (screenResolvedBoundsHeight <= parentAppBoundsHeight) {
-                float positionMultiplier = mLetterboxUiController.getVerticalPositionMultiplier(
+                float positionMultiplier = reachabilityOverrides.getVerticalPositionMultiplier(
                         newParentConfiguration);
                 // If in immersive mode, always align to bottom and overlap bottom insets (nav bar,
                 // task bar) as they are transient and hidden. This removes awkward bottom spacing.
@@ -10079,17 +9887,17 @@
         try {
             ProtoLog.i(WM_DEBUG_STATES, "Moving to %s Relaunching %s callers=%s" ,
                     (andResume ? "RESUMED" : "PAUSED"), this, Debug.getCallers(6));
-            final ClientTransactionItem callbackItem = ActivityRelaunchItem.obtain(token,
+            final ClientTransactionItem callbackItem = new ActivityRelaunchItem(token,
                     pendingResults, pendingNewIntents, configChangeFlags,
                     new MergedConfiguration(getProcessGlobalConfiguration(),
                             getMergedOverrideConfiguration()),
                     preserveWindow, getActivityWindowInfo());
             final ActivityLifecycleItem lifecycleItem;
             if (andResume) {
-                lifecycleItem = ResumeActivityItem.obtain(token, isTransitionForward(),
+                lifecycleItem = new ResumeActivityItem(token, isTransitionForward(),
                         shouldSendCompatFakeFocus());
             } else {
-                lifecycleItem = PauseActivityItem.obtain(token);
+                lifecycleItem = new PauseActivityItem(token);
             }
             mAtmService.getLifecycleManager().scheduleTransactionAndLifecycleItems(
                     app.getThread(), callbackItem, lifecycleItem);
@@ -10181,7 +9989,7 @@
         // {@link ActivityTaskManagerService.activityStopped}).
         try {
             mAtmService.getLifecycleManager().scheduleTransactionItem(app.getThread(),
-                    StopActivityItem.obtain(token));
+                    new StopActivityItem(token));
         } catch (RemoteException e) {
             Slog.w(TAG, "Exception thrown during restart " + this, e);
         }
@@ -10573,7 +10381,7 @@
                 mAppCompatController.getAppCompatOrientationOverrides()
                         .shouldIgnoreOrientationRequestLoop());
         proto.write(SHOULD_OVERRIDE_FORCE_RESIZE_APP,
-                mLetterboxUiController.shouldOverrideForceResizeApp());
+                mAppCompatController.getAppCompatResizeOverrides().shouldOverrideForceResizeApp());
         proto.write(SHOULD_ENABLE_USER_ASPECT_RATIO_SETTINGS,
                 mAppCompatController.getAppCompatAspectRatioOverrides()
                         .shouldEnableUserAspectRatioSettings());
@@ -10880,6 +10688,9 @@
                 return true;
             }
         }
+        if (mAtmService.mBackNavigationController.isStartingSurfaceShown(this)) {
+            return true;
+        }
         if (!super.isSyncFinished(group)) return false;
         if (mDisplayContent != null && mDisplayContent.mUnknownAppVisibilityController
                 .isVisibilityUnknown(this)) {
@@ -10975,12 +10786,8 @@
      * Whether we should send fake focus when the activity is resumed. This is done because some
      * game engines wait to get focus before drawing the content of the app.
      */
-    // TODO(b/263593361): Explore enabling compat fake focus for freeform.
-    // TODO(b/263592337): Explore enabling compat fake focus for fullscreen, e.g. for when
-    // covered with bubbles.
     boolean shouldSendCompatFakeFocus() {
-        return mLetterboxUiController.shouldSendFakeFocus() && inMultiWindowMode()
-                && !inPinnedWindowingMode() && !inFreeformWindowingMode();
+        return mAppCompatController.getAppCompatFocusOverrides().shouldSendFakeFocus();
     }
 
     boolean canCaptureSnapshot() {
diff --git a/services/core/java/com/android/server/wm/ActivityRefresher.java b/services/core/java/com/android/server/wm/ActivityRefresher.java
index bc82271..dcc325e 100644
--- a/services/core/java/com/android/server/wm/ActivityRefresher.java
+++ b/services/core/java/com/android/server/wm/ActivityRefresher.java
@@ -84,9 +84,9 @@
         ProtoLog.v(WM_DEBUG_STATES,
                 "Refreshing activity for freeform camera compatibility treatment, "
                         + "activityRecord=%s", activity);
-        final RefreshCallbackItem refreshCallbackItem = RefreshCallbackItem.obtain(
-                activity.token, cycleThroughStop ? ON_STOP : ON_PAUSE);
-        final ResumeActivityItem resumeActivityItem = ResumeActivityItem.obtain(
+        final RefreshCallbackItem refreshCallbackItem =
+                new RefreshCallbackItem(activity.token, cycleThroughStop ? ON_STOP : ON_PAUSE);
+        final ResumeActivityItem resumeActivityItem = new ResumeActivityItem(
                 activity.token, /* isForward */ false, /* shouldSendCompatFakeFocus */ false);
         try {
             activity.mAtmService.getLifecycleManager().scheduleTransactionAndLifecycleItems(
diff --git a/services/core/java/com/android/server/wm/ActivitySnapshotController.java b/services/core/java/com/android/server/wm/ActivitySnapshotController.java
index 48bc813..aa63393 100644
--- a/services/core/java/com/android/server/wm/ActivitySnapshotController.java
+++ b/services/core/java/com/android/server/wm/ActivitySnapshotController.java
@@ -590,10 +590,6 @@
         return activity;
     }
 
-    WindowState getTopFullscreenWindow(ActivityRecord activity) {
-        return activity.findMainWindow();
-    }
-
     @Override
     ActivityManager.TaskDescription getTaskDescription(ActivityRecord object) {
         return object.taskDescription;
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 5bfe9d7..cc195ac 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1793,6 +1793,9 @@
                     activity.destroyIfPossible("Removes redundant singleInstance");
                 }
             }
+            if (mLastStartActivityRecord != null) {
+                targetTaskTop.mLaunchSourceType = mLastStartActivityRecord.mLaunchSourceType;
+            }
             targetTaskTop.mTransitionController.collect(targetTaskTop);
             recordTransientLaunchIfNeeded(targetTaskTop);
             // Recycle the target task for this launch.
@@ -2537,9 +2540,7 @@
                 if (!mOptions.canTaskOverlayResume()) {
                     final Task task = mRootWindowContainer.anyTaskForId(
                             mOptions.getLaunchTaskId());
-                    final ActivityRecord top = task != null
-                            ? task.getTopNonFinishingActivity() : null;
-                    if (top != null && !top.isState(RESUMED)) {
+                    if (task != null && !task.canBeResumed(r)) {
 
                         // The caller specifies that we'd like to be avoided to be moved to the
                         // front, so be it!
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index a84598d..9f3bbd1 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -5522,7 +5522,7 @@
             final int procCount = procs.size();
             for (int i = 0; i < procCount; i++) {
                 final int procUid = procs.keyAt(i);
-                if (UserHandle.isApp(procUid) || !UserHandle.isSameUser(procUid, uid)) {
+                if (!UserHandle.isCore(procUid) || !UserHandle.isSameUser(procUid, uid)) {
                     // Don't use an app process or different user process for system component.
                     continue;
                 }
@@ -7175,25 +7175,6 @@
         }
 
         /**
-         * Checks if the given user is a visible background user, which is a full, background user
-         * assigned to secondary displays on the devices that have
-         * {@link UserManager#isVisibleBackgroundUsersEnabled()
-         * config_multiuserVisibleBackgroundUsers enabled} (for example, passenger users on
-         * automotive builds, using the display associated with their seats).
-         *
-         * @see UserManager#isUserVisible()
-         */
-        private boolean isVisibleBackgroundUser(int userId) {
-            if (!UserManager.isVisibleBackgroundUsersEnabled()) {
-                return false;
-            }
-            boolean isForeground = getCurrentUserId() == userId;
-            boolean isProfile = getUserManager().isProfile(userId);
-            boolean isVisible = mWindowManager.mUmInternal.isUserVisible(userId);
-            return isVisible && !isForeground && !isProfile;
-        }
-
-        /**
          * In a car environment, {@link ActivityTaskManagerService#mShowDialogs} is always set to
          * {@code false} from {@link ActivityTaskManagerService#updateShouldShowDialogsLocked}
          * because its UI mode is {@link Configuration#UI_MODE_TYPE_CAR}. Thus, error dialogs are
@@ -7208,7 +7189,7 @@
          * @see ActivityTaskManagerService#updateShouldShowDialogsLocked
          */
         private boolean shouldShowDialogsForVisibleBackgroundUserLocked(int userId) {
-            if (!isVisibleBackgroundUser(userId)) {
+            if (!mWindowManager.mUmInternal.isVisibleBackgroundFullUser(userId)) {
                 return false;
             }
             final int displayId = mWindowManager.mUmInternal.getMainDisplayAssignedToUser(userId);
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index e81b440..509a060 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -929,7 +929,7 @@
                 final boolean isTransitionForward = r.isTransitionForward();
                 final IBinder fragmentToken = r.getTaskFragment().getFragmentToken();
                 final int deviceId = getDeviceIdForDisplayId(r.getDisplayId());
-                final LaunchActivityItem launchActivityItem = LaunchActivityItem.obtain(r.token,
+                final LaunchActivityItem launchActivityItem = new LaunchActivityItem(r.token,
                         r.intent, System.identityHashCode(r), r.info,
                         procConfig, overrideConfig, deviceId,
                         r.getFilteredReferrer(r.launchedFromPackage), task.voiceInteractor,
@@ -942,12 +942,12 @@
                 // Set desired final state.
                 final ActivityLifecycleItem lifecycleItem;
                 if (andResume) {
-                    lifecycleItem = ResumeActivityItem.obtain(r.token, isTransitionForward,
+                    lifecycleItem = new ResumeActivityItem(r.token, isTransitionForward,
                             r.shouldSendCompatFakeFocus());
                 } else if (r.isVisibleRequested()) {
-                    lifecycleItem = PauseActivityItem.obtain(r.token);
+                    lifecycleItem = new PauseActivityItem(r.token);
                 } else {
-                    lifecycleItem = StopActivityItem.obtain(r.token);
+                    lifecycleItem = new StopActivityItem(r.token);
                 }
 
                 // Schedule transaction.
@@ -2801,6 +2801,13 @@
                                 targetActivity, activityOptions);
                     }
 
+                    if (callingPid > 0) {
+                        final WindowProcessController wpc = mService.mProcessMap
+                                .getProcess(callingPid);
+                        if (wpc != null) {
+                            targetActivity.updateLaunchSourceType(callingUid, wpc);
+                        }
+                    }
                     mService.getActivityStartController().postStartActivityProcessingForLastStarter(
                             task.getTopNonFinishingActivity(), ActivityManager.START_TASK_TO_FRONT,
                             task.getRootTask());
@@ -2894,6 +2901,8 @@
         ActivityRecord fillAndReturnTop(Task task, TaskInfo info) {
             info.numActivities = 0;
             info.baseActivity = null;
+            info.capturedLink = null;
+            info.capturedLinkTimestamp = 0;
             mInfo = info;
             task.forAllActivities(this);
             final ActivityRecord top = mTopRunning;
diff --git a/services/core/java/com/android/server/wm/AppCompatAspectRatioOverrides.java b/services/core/java/com/android/server/wm/AppCompatAspectRatioOverrides.java
index 05d4c82..d2f3d1d 100644
--- a/services/core/java/com/android/server/wm/AppCompatAspectRatioOverrides.java
+++ b/services/core/java/com/android/server/wm/AppCompatAspectRatioOverrides.java
@@ -36,6 +36,7 @@
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.AppCompatConfiguration.LETTERBOX_POSITION_MULTIPLIER_CENTER;
 import static com.android.server.wm.AppCompatConfiguration.MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO;
+import static com.android.server.wm.AppCompatUtils.isChangeEnabled;
 
 import android.annotation.NonNull;
 import android.content.pm.PackageManager;
@@ -49,8 +50,6 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.wm.utils.OptPropFactory;
 
-import java.util.function.Function;
-
 /**
  * Encapsulates app compat configurations and overrides related to aspect ratio.
  */
@@ -75,20 +74,20 @@
     @NonNull
     private final OptPropFactory.OptProp mAllowOrientationOverrideOptProp;
     @NonNull
-    private final Function<Boolean, Boolean> mIsDisplayFullScreenAndInPostureProvider;
+    private final AppCompatDeviceStateQuery mAppCompatDeviceStateQuery;
     @NonNull
-    private final Function<Configuration, Float> mGetHorizontalPositionMultiplierProvider;
+    private final AppCompatReachabilityOverrides mAppCompatReachabilityOverrides;
 
     AppCompatAspectRatioOverrides(@NonNull ActivityRecord activityRecord,
             @NonNull AppCompatConfiguration appCompatConfiguration,
             @NonNull OptPropFactory optPropBuilder,
-            @NonNull Function<Boolean, Boolean> isDisplayFullScreenAndInPostureProvider,
-            @NonNull Function<Configuration, Float> getHorizontalPositionMultiplierProvider) {
+            @NonNull AppCompatDeviceStateQuery appCompatDeviceStateQuery,
+            @NonNull AppCompatReachabilityOverrides appCompatReachabilityOverrides) {
         mActivityRecord = activityRecord;
         mAppCompatConfiguration = appCompatConfiguration;
+        mAppCompatDeviceStateQuery = appCompatDeviceStateQuery;
         mUserAspectRatioState = new UserAspectRatioState();
-        mIsDisplayFullScreenAndInPostureProvider = isDisplayFullScreenAndInPostureProvider;
-        mGetHorizontalPositionMultiplierProvider = getHorizontalPositionMultiplierProvider;
+        mAppCompatReachabilityOverrides = appCompatReachabilityOverrides;
         mAllowMinAspectRatioOverrideOptProp = optPropBuilder.create(
                 PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE);
         mAllowUserAspectRatioOverrideOptProp = optPropBuilder.create(
@@ -115,7 +114,7 @@
      */
     boolean shouldOverrideMinAspectRatio() {
         return mAllowMinAspectRatioOverrideOptProp.shouldEnableWithOptInOverrideAndOptOutProperty(
-                isCompatChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO));
+                isChangeEnabled(mActivityRecord, OVERRIDE_MIN_ASPECT_RATIO));
     }
 
     /**
@@ -154,7 +153,7 @@
     }
 
     boolean isSystemOverrideToFullscreenEnabled() {
-        return isCompatChangeEnabled(OVERRIDE_ANY_ORIENTATION_TO_USER)
+        return isChangeEnabled(mActivityRecord, OVERRIDE_ANY_ORIENTATION_TO_USER)
                 && !mAllowOrientationOverrideOptProp.isFalse()
                 && (mUserAspectRatioState.mUserAspectRatio == USER_MIN_ASPECT_RATIO_UNSET
                 || mUserAspectRatioState.mUserAspectRatio == USER_MIN_ASPECT_RATIO_FULLSCREEN);
@@ -244,12 +243,13 @@
     }
 
     private boolean shouldUseSplitScreenAspectRatio(@NonNull Configuration parentConfiguration) {
-        final boolean isBookMode = mIsDisplayFullScreenAndInPostureProvider
-                .apply(/* isTabletop */false);
-        final boolean isNotCenteredHorizontally = mGetHorizontalPositionMultiplierProvider.apply(
-                parentConfiguration) != LETTERBOX_POSITION_MULTIPLIER_CENTER;
-        final boolean isTabletopMode = mIsDisplayFullScreenAndInPostureProvider
-                .apply(/* isTabletop */ true);
+        final boolean isBookMode = mAppCompatDeviceStateQuery
+                .isDisplayFullScreenAndInPosture(/* isTabletop */false);
+        final boolean isNotCenteredHorizontally =
+                mAppCompatReachabilityOverrides.getHorizontalPositionMultiplier(parentConfiguration)
+                        != LETTERBOX_POSITION_MULTIPLIER_CENTER;
+        final boolean isTabletopMode = mAppCompatDeviceStateQuery
+                .isDisplayFullScreenAndInPosture(/* isTabletop */ true);
         final boolean isLandscape = isFixedOrientationLandscape(
                 mActivityRecord.getOverrideOrientation());
         final AppCompatCameraOverrides cameraOverrides =
@@ -302,10 +302,6 @@
         private int mUserAspectRatio = USER_MIN_ASPECT_RATIO_UNSET;
     }
 
-    private boolean isCompatChangeEnabled(long overrideChangeId) {
-        return mActivityRecord.info.isChangeEnabled(overrideChangeId);
-    }
-
     private Resources getResources() {
         return mActivityRecord.mWmService.mContext.getResources();
     }
diff --git a/services/core/java/com/android/server/wm/AppCompatCameraOverrides.java b/services/core/java/com/android/server/wm/AppCompatCameraOverrides.java
index 93a8663..aeaaffd 100644
--- a/services/core/java/com/android/server/wm/AppCompatCameraOverrides.java
+++ b/services/core/java/com/android/server/wm/AppCompatCameraOverrides.java
@@ -30,6 +30,7 @@
 
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.wm.AppCompatUtils.isChangeEnabled;
 
 import android.annotation.NonNull;
 import android.app.CameraCompatTaskInfo.FreeformCameraCompatMode;
@@ -99,7 +100,8 @@
     boolean shouldOverrideMinAspectRatioForCamera() {
         return isCameraActive() && mAllowMinAspectRatioOverrideOptProp
                 .shouldEnableWithOptInOverrideAndOptOutProperty(
-                        isCompatChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA));
+                        isChangeEnabled(mActivityRecord,
+                                OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA));
     }
 
     /**
@@ -115,7 +117,7 @@
      */
     boolean shouldRefreshActivityForCameraCompat() {
         return mCameraCompatAllowRefreshOptProp.shouldEnableWithOptOutOverrideAndProperty(
-                isCompatChangeEnabled(OVERRIDE_CAMERA_COMPAT_DISABLE_REFRESH));
+                isChangeEnabled(mActivityRecord, OVERRIDE_CAMERA_COMPAT_DISABLE_REFRESH));
     }
 
     /**
@@ -134,7 +136,7 @@
      */
     boolean shouldRefreshActivityViaPauseForCameraCompat() {
         return mCameraCompatEnableRefreshViaPauseOptProp.shouldEnableWithOverrideAndProperty(
-                isCompatChangeEnabled(OVERRIDE_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE));
+                isChangeEnabled(mActivityRecord, OVERRIDE_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE));
     }
 
     /**
@@ -150,7 +152,7 @@
      */
     boolean shouldForceRotateForCameraCompat() {
         return mCameraCompatAllowForceRotationOptProp.shouldEnableWithOptOutOverrideAndProperty(
-                isCompatChangeEnabled(OVERRIDE_CAMERA_COMPAT_DISABLE_FORCE_ROTATION));
+                isChangeEnabled(mActivityRecord, OVERRIDE_CAMERA_COMPAT_DISABLE_FORCE_ROTATION));
     }
 
     /**
@@ -168,7 +170,7 @@
      * </ul>
      */
     boolean shouldApplyFreeformTreatmentForCameraCompat() {
-        return Flags.cameraCompatForFreeform() && !isCompatChangeEnabled(
+        return Flags.cameraCompatForFreeform() && !isChangeEnabled(mActivityRecord,
                 OVERRIDE_CAMERA_COMPAT_DISABLE_FREEFORM_WINDOWING_TREATMENT);
     }
 
@@ -191,7 +193,7 @@
     }
 
     boolean isOverrideOrientationOnlyForCameraEnabled() {
-        return isCompatChangeEnabled(OVERRIDE_ORIENTATION_ONLY_FOR_CAMERA);
+        return isChangeEnabled(mActivityRecord, OVERRIDE_ORIENTATION_ONLY_FOR_CAMERA);
     }
 
     /**
@@ -227,10 +229,6 @@
         mAppCompatCameraOverridesState.mFreeformCameraCompatMode = freeformCameraCompatMode;
     }
 
-    private boolean isCompatChangeEnabled(long overrideChangeId) {
-        return mActivityRecord.info.isChangeEnabled(overrideChangeId);
-    }
-
     static class AppCompatCameraOverridesState {
         // Whether activity "refresh" was requested but not finished in
         // ActivityRecord#activityResumedLocked following the camera compat force rotation in
diff --git a/services/core/java/com/android/server/wm/AppCompatController.java b/services/core/java/com/android/server/wm/AppCompatController.java
index f9e2507..d38edfc 100644
--- a/services/core/java/com/android/server/wm/AppCompatController.java
+++ b/services/core/java/com/android/server/wm/AppCompatController.java
@@ -35,7 +35,11 @@
     @NonNull
     private final AppCompatAspectRatioPolicy mAppCompatAspectRatioPolicy;
     @NonNull
+    private final AppCompatReachabilityPolicy mAppCompatReachabilityPolicy;
+    @NonNull
     private final AppCompatOverrides mAppCompatOverrides;
+    @NonNull
+    private final AppCompatDeviceStateQuery mAppCompatDeviceStateQuery;
 
     AppCompatController(@NonNull WindowManagerService wmService,
                         @NonNull ActivityRecord activityRecord) {
@@ -43,13 +47,16 @@
         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,
-                wmService.mAppCompatConfiguration, optPropBuilder);
+                wmService.mAppCompatConfiguration, optPropBuilder, mAppCompatDeviceStateQuery);
         mOrientationPolicy = new AppCompatOrientationPolicy(activityRecord, mAppCompatOverrides);
         mAppCompatAspectRatioPolicy = new AppCompatAspectRatioPolicy(activityRecord,
                 mTransparentPolicy, mAppCompatOverrides);
+        mAppCompatReachabilityPolicy = new AppCompatReachabilityPolicy(mActivityRecord,
+                wmService.mAppCompatConfiguration);
     }
 
     @NonNull
@@ -87,6 +94,11 @@
         return mAppCompatOverrides.getAppCompatAspectRatioOverrides();
     }
 
+    @NonNull
+    AppCompatResizeOverrides getAppCompatResizeOverrides() {
+        return mAppCompatOverrides.getAppCompatResizeOverrides();
+    }
+
     @Nullable
     AppCompatCameraPolicy getAppCompatCameraPolicy() {
         if (mActivityRecord.mDisplayContent != null) {
@@ -94,4 +106,25 @@
         }
         return null;
     }
+
+    @NonNull
+    AppCompatReachabilityPolicy getAppCompatReachabilityPolicy() {
+        return mAppCompatReachabilityPolicy;
+    }
+
+    @NonNull
+    AppCompatFocusOverrides getAppCompatFocusOverrides() {
+        return mAppCompatOverrides.getAppCompatFocusOverrides();
+    }
+
+    @NonNull
+    AppCompatReachabilityOverrides getAppCompatReachabilityOverrides() {
+        return mAppCompatOverrides.getAppCompatReachabilityOverrides();
+    }
+
+    @NonNull
+    AppCompatDeviceStateQuery getAppCompatDeviceStateQuery() {
+        return mAppCompatDeviceStateQuery;
+    }
+
 }
diff --git a/services/core/java/com/android/server/wm/AppCompatDeviceStateQuery.java b/services/core/java/com/android/server/wm/AppCompatDeviceStateQuery.java
new file mode 100644
index 0000000..3abea24
--- /dev/null
+++ b/services/core/java/com/android/server/wm/AppCompatDeviceStateQuery.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+
+import android.annotation.NonNull;
+
+/**
+ * Provides information about the current state of the display in relation of
+ * fold/unfold and other positions.
+ */
+class AppCompatDeviceStateQuery {
+
+    @NonNull
+    final ActivityRecord mActivityRecord;
+
+    AppCompatDeviceStateQuery(@NonNull ActivityRecord activityRecord) {
+        mActivityRecord = activityRecord;
+    }
+
+    /**
+     * Check if we are in the given pose and in fullscreen mode.
+     *
+     * Note that we check the task rather than the parent as with ActivityEmbedding the parent
+     * might be a TaskFragment, and its windowing mode is always MULTI_WINDOW, even if the task is
+     * actually fullscreen. If display is still in transition e.g. unfolding, don't return true
+     * for HALF_FOLDED state or app will flicker.
+     */
+    boolean isDisplayFullScreenAndInPosture(boolean isTabletop) {
+        final Task task = mActivityRecord.getTask();
+        final DisplayContent dc = mActivityRecord.mDisplayContent;
+        return dc != null && task != null && !dc.inTransition()
+                && dc.getDisplayRotation().isDeviceInPosture(
+                    DeviceStateController.DeviceState.HALF_FOLDED, isTabletop)
+                && task.getWindowingMode() == WINDOWING_MODE_FULLSCREEN;
+    }
+
+    /**
+     * Note that we check the task rather than the parent as with ActivityEmbedding the parent might
+     * be a TaskFragment, and its windowing mode is always MULTI_WINDOW, even if the task is
+     * actually fullscreen.
+     */
+    boolean isDisplayFullScreenAndSeparatingHinge() {
+        final Task task = mActivityRecord.getTask();
+        return mActivityRecord.mDisplayContent != null && task != null
+                && mActivityRecord.mDisplayContent.getDisplayRotation().isDisplaySeparatingHinge()
+                && task.getWindowingMode() == WINDOWING_MODE_FULLSCREEN;
+    }
+}
diff --git a/services/core/java/com/android/server/wm/AppCompatFocusOverrides.java b/services/core/java/com/android/server/wm/AppCompatFocusOverrides.java
new file mode 100644
index 0000000..ab4bb14
--- /dev/null
+++ b/services/core/java/com/android/server/wm/AppCompatFocusOverrides.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static android.content.pm.ActivityInfo.OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS;
+import static android.view.WindowManager.PROPERTY_COMPAT_ENABLE_FAKE_FOCUS;
+
+import static com.android.server.wm.AppCompatUtils.isChangeEnabled;
+
+import android.annotation.NonNull;
+
+import com.android.server.wm.utils.OptPropFactory;
+
+/**
+ * Encapsulates app compat focus policy.
+ */
+class AppCompatFocusOverrides {
+
+    @NonNull
+    final ActivityRecord mActivityRecord;
+    @NonNull
+    private final OptPropFactory.OptProp mFakeFocusOptProp;
+
+    AppCompatFocusOverrides(@NonNull ActivityRecord activityRecord,
+            @NonNull AppCompatConfiguration appCompatConfiguration,
+            @NonNull OptPropFactory optPropBuilder) {
+        mActivityRecord = activityRecord;
+        mFakeFocusOptProp = optPropBuilder.create(PROPERTY_COMPAT_ENABLE_FAKE_FOCUS,
+                appCompatConfiguration::isCompatFakeFocusEnabled);
+    }
+
+    /**
+     * Whether sending compat fake focus for split screen resumed activities is enabled. Needed
+     * because some game engines wait to get focus before drawing the content of the app which isn't
+     * guaranteed by default in multi-window modes.
+     *
+     * <p>This treatment is enabled when the following conditions are met:
+     * <ul>
+     *     <li>Flag gating the treatment is enabled
+     *     <li>Component property is NOT set to false
+     *     <li>Component property is set to true or per-app override is enabled
+     * </ul>
+     */
+    boolean shouldSendFakeFocus() {
+        // TODO(b/263593361): Explore enabling compat fake focus for freeform.
+        // TODO(b/263592337): Explore enabling compat fake focus for fullscreen, e.g. for when
+        // covered with bubbles.
+        return mFakeFocusOptProp.shouldEnableWithOverrideAndProperty(
+                isChangeEnabled(mActivityRecord, OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS))
+                && mActivityRecord.inMultiWindowMode() && !mActivityRecord.inPinnedWindowingMode()
+                && !mActivityRecord.inFreeformWindowingMode();
+    }
+
+}
diff --git a/services/core/java/com/android/server/wm/AppCompatOrientationOverrides.java b/services/core/java/com/android/server/wm/AppCompatOrientationOverrides.java
index 0adf825..bd01351 100644
--- a/services/core/java/com/android/server/wm/AppCompatOrientationOverrides.java
+++ b/services/core/java/com/android/server/wm/AppCompatOrientationOverrides.java
@@ -20,14 +20,20 @@
 import static android.content.pm.ActivityInfo.OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED;
 import static android.content.pm.ActivityInfo.OVERRIDE_ENABLE_COMPAT_IGNORE_REQUESTED_ORIENTATION;
 import static android.content.pm.ActivityInfo.OVERRIDE_LANDSCAPE_ORIENTATION_TO_REVERSE_LANDSCAPE;
+import static android.content.pm.ActivityInfo.OVERRIDE_RESPECT_REQUESTED_ORIENTATION;
 import static android.content.pm.ActivityInfo.OVERRIDE_UNDEFINED_ORIENTATION_TO_NOSENSOR;
 import static android.content.pm.ActivityInfo.OVERRIDE_UNDEFINED_ORIENTATION_TO_PORTRAIT;
+import static android.content.pm.ActivityInfo.OVERRIDE_USE_DISPLAY_LANDSCAPE_NATURAL_ORIENTATION;
+import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
+import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_DISPLAY_ORIENTATION_OVERRIDE;
 import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_IGNORING_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED;
+import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE;
 import static android.view.WindowManager.PROPERTY_COMPAT_IGNORE_REQUESTED_ORIENTATION;
 
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.AppCompatUtils.asLazy;
+import static com.android.server.wm.AppCompatUtils.isChangeEnabled;
 
 import android.annotation.NonNull;
 
@@ -54,6 +60,10 @@
     private final OptPropFactory.OptProp mIgnoreRequestedOrientationOptProp;
     @NonNull
     private final OptPropFactory.OptProp mAllowIgnoringOrientationRequestWhenLoopDetectedOptProp;
+    @NonNull
+    private final OptPropFactory.OptProp mAllowOrientationOverrideOptProp;
+    @NonNull
+    private final OptPropFactory.OptProp mAllowDisplayOrientationOverrideOptProp;
 
     @NonNull
     final OrientationOverridesState mOrientationOverridesState;
@@ -74,6 +84,17 @@
         mAllowIgnoringOrientationRequestWhenLoopDetectedOptProp = optPropBuilder.create(
                 PROPERTY_COMPAT_ALLOW_IGNORING_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED,
                 isPolicyForIgnoringRequestedOrientationEnabled);
+        mAllowOrientationOverrideOptProp = optPropBuilder.create(
+                PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE);
+        mAllowDisplayOrientationOverrideOptProp = optPropBuilder.create(
+                PROPERTY_COMPAT_ALLOW_DISPLAY_ORIENTATION_OVERRIDE,
+                () -> mActivityRecord.mDisplayContent != null
+                        && mActivityRecord.getTask() != null
+                        && mActivityRecord.mDisplayContent.getIgnoreOrientationRequest()
+                        && !mActivityRecord.getTask().inMultiWindowMode()
+                        && mActivityRecord.mDisplayContent.getNaturalOrientation()
+                            == ORIENTATION_LANDSCAPE
+        );
     }
 
     boolean shouldEnableIgnoreOrientationRequest() {
@@ -81,6 +102,10 @@
                 isCompatChangeEnabled(OVERRIDE_ENABLE_COMPAT_IGNORE_REQUESTED_ORIENTATION));
     }
 
+    boolean isOverrideRespectRequestedOrientationEnabled() {
+        return isChangeEnabled(mActivityRecord, OVERRIDE_RESPECT_REQUESTED_ORIENTATION);
+    }
+
     /**
      * Whether an app is calling {@link android.app.Activity#setRequestedOrientation}
      * in a loop and orientation request should be ignored.
@@ -113,6 +138,26 @@
     }
 
     /**
+     * Whether should fix display orientation to landscape natural orientation when a task is
+     * fullscreen and the display is ignoring orientation requests.
+     *
+     * <p>This treatment is enabled when the following conditions are met:
+     * <ul>
+     *     <li>Opt-out component property isn't enabled
+     *     <li>Opt-in per-app override is enabled
+     *     <li>Task is in fullscreen.
+     *     <li>{@link DisplayContent#getIgnoreOrientationRequest} is enabled
+     *     <li>Natural orientation of the display is landscape.
+     * </ul>
+     */
+    boolean shouldUseDisplayLandscapeNaturalOrientation() {
+        return mAllowDisplayOrientationOverrideOptProp
+                .shouldEnableWithOptInOverrideAndOptOutProperty(
+                        isChangeEnabled(mActivityRecord,
+                                OVERRIDE_USE_DISPLAY_LANDSCAPE_NATURAL_ORIENTATION));
+    }
+
+    /**
      * Sets whether an activity is relaunching after the app has called {@link
      * android.app.Activity#setRequestedOrientation}.
      */
@@ -125,6 +170,10 @@
         return mOrientationOverridesState.mIsRelaunchingAfterRequestedOrientationChanged;
     }
 
+    boolean isAllowOrientationOverrideOptOut() {
+        return mAllowOrientationOverrideOptProp.isFalse();
+    }
+
     @VisibleForTesting
     int getSetOrientationRequestCounter() {
         return mOrientationOverridesState.mSetOrientationRequestCounter;
diff --git a/services/core/java/com/android/server/wm/AppCompatOrientationPolicy.java b/services/core/java/com/android/server/wm/AppCompatOrientationPolicy.java
index 17f0d97..c5506de 100644
--- a/services/core/java/com/android/server/wm/AppCompatOrientationPolicy.java
+++ b/services/core/java/com/android/server/wm/AppCompatOrientationPolicy.java
@@ -86,7 +86,8 @@
             return SCREEN_ORIENTATION_PORTRAIT;
         }
 
-        if (mAppCompatOverrides.isAllowOrientationOverrideOptOut()) {
+        if (mAppCompatOverrides.getAppCompatOrientationOverrides()
+                .isAllowOrientationOverrideOptOut()) {
             return candidate;
         }
 
diff --git a/services/core/java/com/android/server/wm/AppCompatOverrides.java b/services/core/java/com/android/server/wm/AppCompatOverrides.java
index b611ba9..80bbee3 100644
--- a/services/core/java/com/android/server/wm/AppCompatOverrides.java
+++ b/services/core/java/com/android/server/wm/AppCompatOverrides.java
@@ -16,20 +16,6 @@
 
 package com.android.server.wm;
 
-import static android.content.pm.ActivityInfo.FORCE_NON_RESIZE_APP;
-import static android.content.pm.ActivityInfo.FORCE_RESIZE_APP;
-import static android.content.pm.ActivityInfo.OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS;
-import static android.content.pm.ActivityInfo.OVERRIDE_RESPECT_REQUESTED_ORIENTATION;
-import static android.content.pm.ActivityInfo.OVERRIDE_USE_DISPLAY_LANDSCAPE_NATURAL_ORIENTATION;
-import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
-import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_DISPLAY_ORIENTATION_OVERRIDE;
-import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE;
-import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES;
-import static android.view.WindowManager.PROPERTY_COMPAT_ENABLE_FAKE_FOCUS;
-
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
-
 import android.annotation.NonNull;
 
 import com.android.server.wm.utils.OptPropFactory;
@@ -39,64 +25,35 @@
  */
 public class AppCompatOverrides {
 
-    private static final String TAG = TAG_WITH_CLASS_NAME ? "AppCompatOverrides" : TAG_ATM;
-
-    @NonNull
-    private final AppCompatConfiguration mAppCompatConfiguration;
-
-    @NonNull
-    private final ActivityRecord mActivityRecord;
-
-    @NonNull
-    private final OptPropFactory.OptProp mFakeFocusOptProp;
-    @NonNull
-    private final OptPropFactory.OptProp mAllowOrientationOverrideOptProp;
-    @NonNull
-    private final OptPropFactory.OptProp mAllowDisplayOrientationOverrideOptProp;
-    @NonNull
-    private final OptPropFactory.OptProp mAllowForceResizeOverrideOptProp;
     @NonNull
     private final AppCompatOrientationOverrides mAppCompatOrientationOverrides;
     @NonNull
     private final AppCompatCameraOverrides mAppCompatCameraOverrides;
     @NonNull
     private final AppCompatAspectRatioOverrides mAppCompatAspectRatioOverrides;
+    @NonNull
+    private final AppCompatFocusOverrides mAppCompatFocusOverrides;
+    @NonNull
+    private final AppCompatResizeOverrides mAppCompatResizeOverrides;
+    @NonNull
+    private final AppCompatReachabilityOverrides mAppCompatReachabilityOverrides;
 
     AppCompatOverrides(@NonNull ActivityRecord activityRecord,
             @NonNull AppCompatConfiguration appCompatConfiguration,
-            @NonNull OptPropFactory optPropBuilder) {
-        mAppCompatConfiguration = appCompatConfiguration;
-        mActivityRecord = activityRecord;
-
-        mAppCompatCameraOverrides = new AppCompatCameraOverrides(mActivityRecord,
-                mAppCompatConfiguration, optPropBuilder);
-        mAppCompatOrientationOverrides = new AppCompatOrientationOverrides(mActivityRecord,
-                mAppCompatConfiguration, optPropBuilder, mAppCompatCameraOverrides);
-        // TODO(b/341903757) Remove BooleanSuppliers after fixing dependency with reachability.
+            @NonNull OptPropFactory optPropBuilder,
+            @NonNull AppCompatDeviceStateQuery appCompatDeviceStateQuery) {
+        mAppCompatCameraOverrides = new AppCompatCameraOverrides(activityRecord,
+                appCompatConfiguration, optPropBuilder);
+        mAppCompatOrientationOverrides = new AppCompatOrientationOverrides(activityRecord,
+                appCompatConfiguration, optPropBuilder, mAppCompatCameraOverrides);
+        mAppCompatReachabilityOverrides = new AppCompatReachabilityOverrides(activityRecord,
+                appCompatConfiguration, appCompatDeviceStateQuery);
         mAppCompatAspectRatioOverrides = new AppCompatAspectRatioOverrides(activityRecord,
-                mAppCompatConfiguration, optPropBuilder,
-                activityRecord.mLetterboxUiController::isDisplayFullScreenAndInPosture,
-                activityRecord.mLetterboxUiController::getHorizontalPositionMultiplier);
-
-        mFakeFocusOptProp = optPropBuilder.create(PROPERTY_COMPAT_ENABLE_FAKE_FOCUS,
-                mAppCompatConfiguration::isCompatFakeFocusEnabled);
-
-
-        mAllowOrientationOverrideOptProp = optPropBuilder.create(
-                PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE);
-
-        mAllowDisplayOrientationOverrideOptProp = optPropBuilder.create(
-                PROPERTY_COMPAT_ALLOW_DISPLAY_ORIENTATION_OVERRIDE,
-                () -> mActivityRecord.mDisplayContent != null
-                        && mActivityRecord.getTask() != null
-                        && mActivityRecord.mDisplayContent.getIgnoreOrientationRequest()
-                        && !mActivityRecord.getTask().inMultiWindowMode()
-                        && mActivityRecord.mDisplayContent.getNaturalOrientation()
-                            == ORIENTATION_LANDSCAPE
-        );
-
-        mAllowForceResizeOverrideOptProp = optPropBuilder.create(
-                PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES);
+                appCompatConfiguration, optPropBuilder, appCompatDeviceStateQuery,
+                mAppCompatReachabilityOverrides);
+        mAppCompatFocusOverrides = new AppCompatFocusOverrides(activityRecord,
+                appCompatConfiguration, optPropBuilder);
+        mAppCompatResizeOverrides = new AppCompatResizeOverrides(activityRecord, optPropBuilder);
     }
 
     @NonNull
@@ -114,83 +71,18 @@
         return mAppCompatAspectRatioOverrides;
     }
 
-    /**
-     * Whether sending compat fake focus for split screen resumed activities is enabled. Needed
-     * because some game engines wait to get focus before drawing the content of the app which isn't
-     * guaranteed by default in multi-window modes.
-     *
-     * <p>This treatment is enabled when the following conditions are met:
-     * <ul>
-     *     <li>Flag gating the treatment is enabled
-     *     <li>Component property is NOT set to false
-     *     <li>Component property is set to true or per-app override is enabled
-     * </ul>
-     */
-    boolean shouldSendFakeFocus() {
-        return mFakeFocusOptProp.shouldEnableWithOverrideAndProperty(
-                isCompatChangeEnabled(OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS));
+    @NonNull
+    AppCompatFocusOverrides getAppCompatFocusOverrides() {
+        return mAppCompatFocusOverrides;
     }
 
-    boolean isAllowOrientationOverrideOptOut() {
-        return mAllowOrientationOverrideOptProp.isFalse();
+    @NonNull
+    AppCompatResizeOverrides getAppCompatResizeOverrides() {
+        return mAppCompatResizeOverrides;
     }
 
-    boolean isOverrideRespectRequestedOrientationEnabled() {
-        return isCompatChangeEnabled(OVERRIDE_RESPECT_REQUESTED_ORIENTATION);
-    }
-
-    /**
-     * Whether should fix display orientation to landscape natural orientation when a task is
-     * fullscreen and the display is ignoring orientation requests.
-     *
-     * <p>This treatment is enabled when the following conditions are met:
-     * <ul>
-     *     <li>Opt-out component property isn't enabled
-     *     <li>Opt-in per-app override is enabled
-     *     <li>Task is in fullscreen.
-     *     <li>{@link DisplayContent#getIgnoreOrientationRequest} is enabled
-     *     <li>Natural orientation of the display is landscape.
-     * </ul>
-     */
-    boolean shouldUseDisplayLandscapeNaturalOrientation() {
-        return mAllowDisplayOrientationOverrideOptProp
-                .shouldEnableWithOptInOverrideAndOptOutProperty(
-                        isCompatChangeEnabled(OVERRIDE_USE_DISPLAY_LANDSCAPE_NATURAL_ORIENTATION));
-    }
-
-    /**
-     * Whether we should apply the force resize per-app override. When this override is applied it
-     * forces the packages it is applied to to be resizable. It won't change whether the app can be
-     * put into multi-windowing mode, but allow the app to resize without going into size-compat
-     * mode when the window container resizes, such as display size change or screen rotation.
-     *
-     * <p>This method returns {@code true} when the following conditions are met:
-     * <ul>
-     *     <li>Opt-out component property isn't enabled
-     *     <li>Per-app override is enabled
-     * </ul>
-     */
-    boolean shouldOverrideForceResizeApp() {
-        return mAllowForceResizeOverrideOptProp.shouldEnableWithOptInOverrideAndOptOutProperty(
-                isCompatChangeEnabled(FORCE_RESIZE_APP));
-    }
-
-    /**
-     * Whether we should apply the force non resize per-app override. When this override is applied
-     * it forces the packages it is applied to to be non-resizable.
-     *
-     * <p>This method returns {@code true} when the following conditions are met:
-     * <ul>
-     *     <li>Opt-out component property isn't enabled
-     *     <li>Per-app override is enabled
-     * </ul>
-     */
-    boolean shouldOverrideForceNonResizeApp() {
-        return mAllowForceResizeOverrideOptProp.shouldEnableWithOptInOverrideAndOptOutProperty(
-                isCompatChangeEnabled(FORCE_NON_RESIZE_APP));
-    }
-
-    private boolean isCompatChangeEnabled(long overrideChangeId) {
-        return mActivityRecord.info.isChangeEnabled(overrideChangeId);
+    @NonNull
+    AppCompatReachabilityOverrides getAppCompatReachabilityOverrides() {
+        return mAppCompatReachabilityOverrides;
     }
 }
diff --git a/services/core/java/com/android/server/wm/AppCompatReachabilityOverrides.java b/services/core/java/com/android/server/wm/AppCompatReachabilityOverrides.java
new file mode 100644
index 0000000..b9bdc32
--- /dev/null
+++ b/services/core/java/com/android/server/wm/AppCompatReachabilityOverrides.java
@@ -0,0 +1,343 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+
+import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__LETTERBOX_POSITION__BOTTOM;
+import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__LETTERBOX_POSITION__CENTER;
+import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__LETTERBOX_POSITION__LEFT;
+import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__LETTERBOX_POSITION__RIGHT;
+import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__LETTERBOX_POSITION__TOP;
+import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__LETTERBOX_POSITION__UNKNOWN_POSITION;
+import static com.android.server.wm.AppCompatConfiguration.LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_CENTER;
+import static com.android.server.wm.AppCompatConfiguration.LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_LEFT;
+import static com.android.server.wm.AppCompatConfiguration.LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_RIGHT;
+import static com.android.server.wm.AppCompatConfiguration.LETTERBOX_VERTICAL_REACHABILITY_POSITION_BOTTOM;
+import static com.android.server.wm.AppCompatConfiguration.LETTERBOX_VERTICAL_REACHABILITY_POSITION_CENTER;
+import static com.android.server.wm.AppCompatConfiguration.LETTERBOX_VERTICAL_REACHABILITY_POSITION_TOP;
+
+import android.annotation.NonNull;
+import android.content.res.Configuration;
+import android.graphics.Rect;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.window.flags.Flags;
+
+/**
+ * Encapsulate overrides and configurations about app compat reachability.
+ */
+class AppCompatReachabilityOverrides {
+
+    @NonNull
+    private final ActivityRecord mActivityRecord;
+    @NonNull
+    private final AppCompatConfiguration mAppCompatConfiguration;
+    @NonNull
+    private final AppCompatDeviceStateQuery mAppCompatDeviceStateQuery;
+    @NonNull
+    private final ReachabilityState mReachabilityState;
+
+    AppCompatReachabilityOverrides(@NonNull ActivityRecord activityRecord,
+            @NonNull AppCompatConfiguration appCompatConfiguration,
+            @NonNull AppCompatDeviceStateQuery appCompatDeviceStateQuery) {
+        mActivityRecord = activityRecord;
+        mAppCompatConfiguration = appCompatConfiguration;
+        mAppCompatDeviceStateQuery = appCompatDeviceStateQuery;
+        mReachabilityState = new ReachabilityState();
+    }
+
+    boolean isFromDoubleTap() {
+        return mReachabilityState.isFromDoubleTap();
+    }
+
+    boolean isDoubleTapEvent() {
+        return mReachabilityState.mIsDoubleTapEvent;
+    }
+
+    void setDoubleTapEvent() {
+        mReachabilityState.mIsDoubleTapEvent = true;
+    }
+
+    /**
+     * Provides the multiplier to use when calculating the position of a letterboxed app after
+     * an horizontal reachability event (double tap). The method takes the current state of the
+     * device (e.g. device in book mode) into account.
+     * </p>
+     * @param parentConfiguration The parent {@link Configuration}.
+     * @return The value to use for calculating the letterbox horizontal position.
+     */
+    float getHorizontalPositionMultiplier(@NonNull Configuration parentConfiguration) {
+        // Don't check resolved configuration because it may not be updated yet during
+        // configuration change.
+        boolean bookModeEnabled = isFullScreenAndBookModeEnabled();
+        return isHorizontalReachabilityEnabled(parentConfiguration)
+                // Using the last global dynamic position to avoid "jumps" when moving
+                // between apps or activities.
+                ? mAppCompatConfiguration.getHorizontalMultiplierForReachability(bookModeEnabled)
+                : mAppCompatConfiguration.getLetterboxHorizontalPositionMultiplier(bookModeEnabled);
+    }
+
+    /**
+     * Provides the multiplier to use when calculating the position of a letterboxed app after
+     * a vertical reachability event (double tap). The method takes the current state of the
+     * device (e.g. device posture) into account.
+     * </p>
+     * @param parentConfiguration The parent {@link Configuration}.
+     * @return The value to use for calculating the letterbox horizontal position.
+     */
+    float getVerticalPositionMultiplier(@NonNull Configuration parentConfiguration) {
+        // Don't check resolved configuration because it may not be updated yet during
+        // configuration change.
+        boolean tabletopMode = mAppCompatDeviceStateQuery
+                .isDisplayFullScreenAndInPosture(/* isTabletop */ true);
+        return isVerticalReachabilityEnabled(parentConfiguration)
+                // Using the last global dynamic position to avoid "jumps" when moving
+                // between apps or activities.
+                ? mAppCompatConfiguration.getVerticalMultiplierForReachability(tabletopMode)
+                : mAppCompatConfiguration.getLetterboxVerticalPositionMultiplier(tabletopMode);
+    }
+
+    @VisibleForTesting
+    boolean isHorizontalReachabilityEnabled() {
+        return isHorizontalReachabilityEnabled(mActivityRecord.getParent().getConfiguration());
+    }
+
+    @VisibleForTesting
+    boolean isVerticalReachabilityEnabled() {
+        return isVerticalReachabilityEnabled(mActivityRecord.getParent().getConfiguration());
+    }
+
+    boolean isLetterboxDoubleTapEducationEnabled() {
+        return isHorizontalReachabilityEnabled() || isVerticalReachabilityEnabled();
+    }
+
+    @AppCompatConfiguration.LetterboxVerticalReachabilityPosition
+    int getLetterboxPositionForVerticalReachability() {
+        final boolean isInFullScreenTabletopMode =
+                mAppCompatDeviceStateQuery.isDisplayFullScreenAndSeparatingHinge();
+        return mAppCompatConfiguration.getLetterboxPositionForVerticalReachability(
+                isInFullScreenTabletopMode);
+    }
+
+    @AppCompatConfiguration.LetterboxHorizontalReachabilityPosition
+    int getLetterboxPositionForHorizontalReachability() {
+        final boolean isInFullScreenBookMode = isFullScreenAndBookModeEnabled();
+        return mAppCompatConfiguration.getLetterboxPositionForHorizontalReachability(
+                isInFullScreenBookMode);
+    }
+
+    int getLetterboxPositionForLogging() {
+        int positionToLog = APP_COMPAT_STATE_CHANGED__LETTERBOX_POSITION__UNKNOWN_POSITION;
+        if (isHorizontalReachabilityEnabled()) {
+            int letterboxPositionForHorizontalReachability = mAppCompatConfiguration
+                    .getLetterboxPositionForHorizontalReachability(mAppCompatDeviceStateQuery
+                            .isDisplayFullScreenAndInPosture(/* isTabletop */ false));
+            positionToLog = letterboxHorizontalReachabilityPositionToLetterboxPositionForLogging(
+                    letterboxPositionForHorizontalReachability);
+        } else if (isVerticalReachabilityEnabled()) {
+            int letterboxPositionForVerticalReachability = mAppCompatConfiguration
+                    .getLetterboxPositionForVerticalReachability(mAppCompatDeviceStateQuery
+                            .isDisplayFullScreenAndInPosture(/* isTabletop */ true));
+            positionToLog = letterboxVerticalReachabilityPositionToLetterboxPositionForLogging(
+                    letterboxPositionForVerticalReachability);
+        }
+        return positionToLog;
+    }
+
+    /**
+     * @return {@value true} if the vertical reachability should be allowed in case of
+     * thin letterboxing.
+     */
+    boolean allowVerticalReachabilityForThinLetterbox() {
+        if (!Flags.disableThinLetterboxingPolicy()) {
+            return true;
+        }
+        // When the flag is enabled we allow vertical reachability only if the
+        // app is not thin letterboxed vertically.
+        return !isVerticalThinLetterboxed();
+    }
+
+    /**
+     * @return {@value true} if the horizontal reachability should be enabled in case of
+     * thin letterboxing.
+     */
+    boolean allowHorizontalReachabilityForThinLetterbox() {
+        if (!Flags.disableThinLetterboxingPolicy()) {
+            return true;
+        }
+        // When the flag is enabled we allow horizontal reachability only if the
+        // app is not thin pillarboxed.
+        return !isHorizontalThinLetterboxed();
+    }
+
+    /**
+     * @return {@value true} if the resulting app is letterboxed in a way defined as thin.
+     */
+    boolean isVerticalThinLetterboxed() {
+        final int thinHeight = mAppCompatConfiguration.getThinLetterboxHeightPx();
+        if (thinHeight < 0) {
+            return false;
+        }
+        final Task task = mActivityRecord.getTask();
+        if (task == null) {
+            return false;
+        }
+        final int padding = Math.abs(
+                task.getBounds().height() - mActivityRecord.getBounds().height()) / 2;
+        return padding <= thinHeight;
+    }
+
+    /**
+     * @return {@value true} if the resulting app is pillarboxed in a way defined as thin.
+     */
+    boolean isHorizontalThinLetterboxed() {
+        final int thinWidth = mAppCompatConfiguration.getThinLetterboxWidthPx();
+        if (thinWidth < 0) {
+            return false;
+        }
+        final Task task = mActivityRecord.getTask();
+        if (task == null) {
+            return false;
+        }
+        final int padding = Math.abs(
+                task.getBounds().width() - mActivityRecord.getBounds().width()) / 2;
+        return padding <= thinWidth;
+    }
+
+    // Note that we check the task rather than the parent as with ActivityEmbedding the parent might
+    // be a TaskFragment, and its windowing mode is always MULTI_WINDOW, even if the task is
+    // actually fullscreen.
+    private boolean isDisplayFullScreenAndSeparatingHinge() {
+        Task task = mActivityRecord.getTask();
+        return mActivityRecord.mDisplayContent != null
+                && mActivityRecord.mDisplayContent.getDisplayRotation().isDisplaySeparatingHinge()
+                && task != null
+                && task.getWindowingMode() == WINDOWING_MODE_FULLSCREEN;
+    }
+
+    private int letterboxHorizontalReachabilityPositionToLetterboxPositionForLogging(
+            @AppCompatConfiguration.LetterboxHorizontalReachabilityPosition int position) {
+        switch (position) {
+            case LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_LEFT:
+                return APP_COMPAT_STATE_CHANGED__LETTERBOX_POSITION__LEFT;
+            case LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_CENTER:
+                return APP_COMPAT_STATE_CHANGED__LETTERBOX_POSITION__CENTER;
+            case LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_RIGHT:
+                return APP_COMPAT_STATE_CHANGED__LETTERBOX_POSITION__RIGHT;
+            default:
+                throw new AssertionError(
+                        "Unexpected letterbox horizontal reachability position type: "
+                                + position);
+        }
+    }
+
+    private int letterboxVerticalReachabilityPositionToLetterboxPositionForLogging(
+            @AppCompatConfiguration.LetterboxVerticalReachabilityPosition int position) {
+        switch (position) {
+            case LETTERBOX_VERTICAL_REACHABILITY_POSITION_TOP:
+                return APP_COMPAT_STATE_CHANGED__LETTERBOX_POSITION__TOP;
+            case LETTERBOX_VERTICAL_REACHABILITY_POSITION_CENTER:
+                return APP_COMPAT_STATE_CHANGED__LETTERBOX_POSITION__CENTER;
+            case LETTERBOX_VERTICAL_REACHABILITY_POSITION_BOTTOM:
+                return APP_COMPAT_STATE_CHANGED__LETTERBOX_POSITION__BOTTOM;
+            default:
+                throw new AssertionError(
+                        "Unexpected letterbox vertical reachability position type: "
+                                + position);
+        }
+    }
+
+    private boolean isFullScreenAndBookModeEnabled() {
+        return mAppCompatDeviceStateQuery.isDisplayFullScreenAndInPosture(/* isTabletop */ false)
+                && mAppCompatConfiguration.getIsAutomaticReachabilityInBookModeEnabled();
+    }
+
+    /**
+     * Whether horizontal reachability is enabled for an activity in the current configuration.
+     *
+     * <p>Conditions that needs to be met:
+     * <ul>
+     *   <li>Windowing mode is fullscreen.
+     *   <li>Horizontal Reachability is enabled.
+     *   <li>First top opaque activity fills parent vertically, but not horizontally.
+     * </ul>
+     */
+    private boolean isHorizontalReachabilityEnabled(@NonNull Configuration parentConfiguration) {
+        if (!allowHorizontalReachabilityForThinLetterbox()) {
+            return false;
+        }
+        final Rect parentAppBoundsOverride = mActivityRecord.getParentAppBoundsOverride();
+        final Rect parentAppBounds = parentAppBoundsOverride != null
+                ? parentAppBoundsOverride : parentConfiguration.windowConfiguration.getAppBounds();
+        // Use screen resolved bounds which uses resolved bounds or size compat bounds
+        // as activity bounds can sometimes be empty
+        final Rect opaqueActivityBounds = mActivityRecord.mAppCompatController
+                .getTransparentPolicy().getFirstOpaqueActivity()
+                .map(ActivityRecord::getScreenResolvedBounds)
+                .orElse(mActivityRecord.getScreenResolvedBounds());
+        return mAppCompatConfiguration.getIsHorizontalReachabilityEnabled()
+                && parentConfiguration.windowConfiguration.getWindowingMode()
+                == WINDOWING_MODE_FULLSCREEN
+                // Check whether the activity fills the parent vertically.
+                && parentAppBounds.height() <= opaqueActivityBounds.height()
+                && parentAppBounds.width() > opaqueActivityBounds.width();
+    }
+
+    /**
+     * Whether vertical reachability is enabled for an activity in the current configuration.
+     *
+     * <p>Conditions that needs to be met:
+     * <ul>
+     *   <li>Windowing mode is fullscreen.
+     *   <li>Vertical Reachability is enabled.
+     *   <li>First top opaque activity fills parent horizontally but not vertically.
+     * </ul>
+     */
+    private boolean isVerticalReachabilityEnabled(@NonNull Configuration parentConfiguration) {
+        if (!allowVerticalReachabilityForThinLetterbox()) {
+            return false;
+        }
+        final Rect parentAppBoundsOverride = mActivityRecord.getParentAppBoundsOverride();
+        final Rect parentAppBounds = parentAppBoundsOverride != null
+                ? parentAppBoundsOverride : parentConfiguration.windowConfiguration.getAppBounds();
+        // Use screen resolved bounds which uses resolved bounds or size compat bounds
+        // as activity bounds can sometimes be empty.
+        final Rect opaqueActivityBounds = mActivityRecord.mAppCompatController
+                .getTransparentPolicy().getFirstOpaqueActivity()
+                .map(ActivityRecord::getScreenResolvedBounds)
+                .orElse(mActivityRecord.getScreenResolvedBounds());
+        return mAppCompatConfiguration.getIsVerticalReachabilityEnabled()
+                && parentConfiguration.windowConfiguration.getWindowingMode()
+                    == WINDOWING_MODE_FULLSCREEN
+                // Check whether the activity fills the parent horizontally.
+                && parentAppBounds.width() <= opaqueActivityBounds.width()
+                && parentAppBounds.height() > opaqueActivityBounds.height();
+    }
+
+    private static class ReachabilityState {
+        // If the current event is a double tap.
+        private boolean mIsDoubleTapEvent;
+
+        boolean isFromDoubleTap() {
+            final boolean isFromDoubleTap = mIsDoubleTapEvent;
+            mIsDoubleTapEvent = false;
+            return isFromDoubleTap;
+        }
+    }
+
+}
diff --git a/services/core/java/com/android/server/wm/AppCompatReachabilityPolicy.java b/services/core/java/com/android/server/wm/AppCompatReachabilityPolicy.java
new file mode 100644
index 0000000..e4e7654
--- /dev/null
+++ b/services/core/java/com/android/server/wm/AppCompatReachabilityPolicy.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static com.android.internal.util.FrameworkStatsLog.LETTERBOX_POSITION_CHANGED__POSITION_CHANGE__BOTTOM_TO_CENTER;
+import static com.android.internal.util.FrameworkStatsLog.LETTERBOX_POSITION_CHANGED__POSITION_CHANGE__CENTER_TO_BOTTOM;
+import static com.android.internal.util.FrameworkStatsLog.LETTERBOX_POSITION_CHANGED__POSITION_CHANGE__CENTER_TO_LEFT;
+import static com.android.internal.util.FrameworkStatsLog.LETTERBOX_POSITION_CHANGED__POSITION_CHANGE__CENTER_TO_RIGHT;
+import static com.android.internal.util.FrameworkStatsLog.LETTERBOX_POSITION_CHANGED__POSITION_CHANGE__CENTER_TO_TOP;
+import static com.android.internal.util.FrameworkStatsLog.LETTERBOX_POSITION_CHANGED__POSITION_CHANGE__LEFT_TO_CENTER;
+import static com.android.internal.util.FrameworkStatsLog.LETTERBOX_POSITION_CHANGED__POSITION_CHANGE__RIGHT_TO_CENTER;
+import static com.android.internal.util.FrameworkStatsLog.LETTERBOX_POSITION_CHANGED__POSITION_CHANGE__TOP_TO_CENTER;
+import static com.android.server.wm.AppCompatConfiguration.LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_CENTER;
+import static com.android.server.wm.AppCompatConfiguration.LETTERBOX_VERTICAL_REACHABILITY_POSITION_CENTER;
+
+import android.annotation.NonNull;
+import android.graphics.Rect;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.function.Supplier;
+
+/**
+ * Encapsulate logic about app compat reachability.
+ */
+class AppCompatReachabilityPolicy {
+
+    @NonNull
+    private final ActivityRecord mActivityRecord;
+    @NonNull
+    private final AppCompatConfiguration mAppCompatConfiguration;
+
+    AppCompatReachabilityPolicy(@NonNull ActivityRecord activityRecord,
+            @NonNull AppCompatConfiguration appCompatConfiguration) {
+        mActivityRecord = activityRecord;
+        mAppCompatConfiguration = appCompatConfiguration;
+    }
+
+    @VisibleForTesting
+    void handleHorizontalDoubleTap(int x, @NonNull Supplier<Rect> innerFrameSupplier) {
+        final AppCompatReachabilityOverrides reachabilityOverrides =
+                mActivityRecord.mAppCompatController.getAppCompatReachabilityOverrides();
+        if (!reachabilityOverrides.isHorizontalReachabilityEnabled()
+                || mActivityRecord.isInTransition()) {
+            return;
+        }
+        final Rect letterboxInnerFrame = innerFrameSupplier.get();
+        if (letterboxInnerFrame.left <= x && letterboxInnerFrame.right >= x) {
+            // Only react to clicks at the sides of the letterboxed app window.
+            return;
+        }
+        final AppCompatDeviceStateQuery deviceStateQuery = mActivityRecord.mAppCompatController
+                .getAppCompatDeviceStateQuery();
+        final boolean isInFullScreenBookMode = deviceStateQuery
+                    .isDisplayFullScreenAndSeparatingHinge()
+                && mAppCompatConfiguration.getIsAutomaticReachabilityInBookModeEnabled();
+        final int letterboxPositionForHorizontalReachability = mAppCompatConfiguration
+                .getLetterboxPositionForHorizontalReachability(isInFullScreenBookMode);
+        if (letterboxInnerFrame.left > x) {
+            // Moving to the next stop on the left side of the app window: right > center > left.
+            mAppCompatConfiguration.movePositionForHorizontalReachabilityToNextLeftStop(
+                    isInFullScreenBookMode);
+            int letterboxPositionChangeForLog =
+                    letterboxPositionForHorizontalReachability
+                            == LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_CENTER
+                                ? LETTERBOX_POSITION_CHANGED__POSITION_CHANGE__CENTER_TO_LEFT
+                                : LETTERBOX_POSITION_CHANGED__POSITION_CHANGE__RIGHT_TO_CENTER;
+            logLetterboxPositionChange(letterboxPositionChangeForLog);
+            reachabilityOverrides.setDoubleTapEvent();
+        } else if (letterboxInnerFrame.right < x) {
+            // Moving to the next stop on the right side of the app window: left > center > right.
+            mAppCompatConfiguration.movePositionForHorizontalReachabilityToNextRightStop(
+                    isInFullScreenBookMode);
+            final int letterboxPositionChangeForLog =
+                    letterboxPositionForHorizontalReachability
+                            == LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_CENTER
+                                ? LETTERBOX_POSITION_CHANGED__POSITION_CHANGE__CENTER_TO_RIGHT
+                                : LETTERBOX_POSITION_CHANGED__POSITION_CHANGE__LEFT_TO_CENTER;
+            logLetterboxPositionChange(letterboxPositionChangeForLog);
+            reachabilityOverrides.setDoubleTapEvent();
+        }
+        // TODO(197549949): Add animation for transition.
+        mActivityRecord.recomputeConfiguration();
+    }
+
+    @VisibleForTesting
+    void handleVerticalDoubleTap(int y, @NonNull Supplier<Rect> innerFrameSupplier) {
+        final AppCompatReachabilityOverrides reachabilityOverrides =
+                mActivityRecord.mAppCompatController.getAppCompatReachabilityOverrides();
+        if (!reachabilityOverrides.isVerticalReachabilityEnabled()
+                || mActivityRecord.isInTransition()) {
+            return;
+        }
+        final Rect letterboxInnerFrame = innerFrameSupplier.get();
+        if (letterboxInnerFrame.top <= y && letterboxInnerFrame.bottom >= y) {
+            // Only react to clicks at the top and bottom of the letterboxed app window.
+            return;
+        }
+        final AppCompatDeviceStateQuery deviceStateQuery = mActivityRecord.mAppCompatController
+                .getAppCompatDeviceStateQuery();
+        final boolean isInFullScreenTabletopMode = deviceStateQuery
+                .isDisplayFullScreenAndSeparatingHinge();
+        final int letterboxPositionForVerticalReachability = mAppCompatConfiguration
+                .getLetterboxPositionForVerticalReachability(isInFullScreenTabletopMode);
+        if (letterboxInnerFrame.top > y) {
+            // Moving to the next stop on the top side of the app window: bottom > center > top.
+            mAppCompatConfiguration.movePositionForVerticalReachabilityToNextTopStop(
+                    isInFullScreenTabletopMode);
+            final int letterboxPositionChangeForLog =
+                    letterboxPositionForVerticalReachability
+                            == LETTERBOX_VERTICAL_REACHABILITY_POSITION_CENTER
+                                ? LETTERBOX_POSITION_CHANGED__POSITION_CHANGE__CENTER_TO_TOP
+                                : LETTERBOX_POSITION_CHANGED__POSITION_CHANGE__BOTTOM_TO_CENTER;
+            logLetterboxPositionChange(letterboxPositionChangeForLog);
+            reachabilityOverrides.setDoubleTapEvent();
+        } else if (letterboxInnerFrame.bottom < y) {
+            // Moving to the next stop on the bottom side of the app window: top > center > bottom.
+            mAppCompatConfiguration.movePositionForVerticalReachabilityToNextBottomStop(
+                    isInFullScreenTabletopMode);
+            final int letterboxPositionChangeForLog =
+                    letterboxPositionForVerticalReachability
+                            == LETTERBOX_VERTICAL_REACHABILITY_POSITION_CENTER
+                                ? LETTERBOX_POSITION_CHANGED__POSITION_CHANGE__CENTER_TO_BOTTOM
+                                : LETTERBOX_POSITION_CHANGED__POSITION_CHANGE__TOP_TO_CENTER;
+            logLetterboxPositionChange(letterboxPositionChangeForLog);
+            reachabilityOverrides.setDoubleTapEvent();
+        }
+        // TODO(197549949): Add animation for transition.
+        mActivityRecord.recomputeConfiguration();
+    }
+
+    /**
+     * Logs letterbox position changes via {@link ActivityMetricsLogger#logLetterboxPositionChange}.
+     */
+    private void logLetterboxPositionChange(int letterboxPositionChangeForLog) {
+        mActivityRecord.mTaskSupervisor.getActivityMetricsLogger()
+                .logLetterboxPositionChange(mActivityRecord, letterboxPositionChangeForLog);
+    }
+}
diff --git a/services/core/java/com/android/server/wm/AppCompatResizeOverrides.java b/services/core/java/com/android/server/wm/AppCompatResizeOverrides.java
new file mode 100644
index 0000000..60c1825
--- /dev/null
+++ b/services/core/java/com/android/server/wm/AppCompatResizeOverrides.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+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 com.android.server.wm.AppCompatUtils.isChangeEnabled;
+
+import android.annotation.NonNull;
+
+import com.android.server.wm.utils.OptPropFactory;
+
+/**
+ * Encapsulate app compat logic about resizability.
+ */
+class AppCompatResizeOverrides {
+
+    @NonNull
+    private final ActivityRecord mActivityRecord;
+
+    @NonNull
+    private final OptPropFactory.OptProp mAllowForceResizeOverrideOptProp;
+
+    AppCompatResizeOverrides(@NonNull ActivityRecord activityRecord,
+            @NonNull OptPropFactory optPropBuilder) {
+        mActivityRecord = activityRecord;
+        mAllowForceResizeOverrideOptProp = optPropBuilder.create(
+                PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES);
+    }
+
+    /**
+     * Whether we should apply the force resize per-app override. When this override is applied it
+     * forces the packages it is applied to to be resizable. It won't change whether the app can be
+     * put into multi-windowing mode, but allow the app to resize without going into size-compat
+     * mode when the window container resizes, such as display size change or screen rotation.
+     *
+     * <p>This method returns {@code true} when the following conditions are met:
+     * <ul>
+     *     <li>Opt-out component property isn't enabled
+     *     <li>Per-app override is enabled
+     * </ul>
+     */
+    boolean shouldOverrideForceResizeApp() {
+        return mAllowForceResizeOverrideOptProp.shouldEnableWithOptInOverrideAndOptOutProperty(
+                isChangeEnabled(mActivityRecord, FORCE_RESIZE_APP));
+    }
+
+    /**
+     * Whether we should apply the force non resize per-app override. When this override is applied
+     * it forces the packages it is applied to to be non-resizable.
+     *
+     * <p>This method returns {@code true} when the following conditions are met:
+     * <ul>
+     *     <li>Opt-out component property isn't enabled
+     *     <li>Per-app override is enabled
+     * </ul>
+     */
+    boolean shouldOverrideForceNonResizeApp() {
+        return mAllowForceResizeOverrideOptProp.shouldEnableWithOptInOverrideAndOptOutProperty(
+                isChangeEnabled(mActivityRecord, FORCE_NON_RESIZE_APP));
+    }
+}
diff --git a/services/core/java/com/android/server/wm/AppCompatUtils.java b/services/core/java/com/android/server/wm/AppCompatUtils.java
index 1b30a20..8c5193e 100644
--- a/services/core/java/com/android/server/wm/AppCompatUtils.java
+++ b/services/core/java/com/android/server/wm/AppCompatUtils.java
@@ -19,7 +19,13 @@
 import static android.content.res.Configuration.UI_MODE_TYPE_MASK;
 import static android.content.res.Configuration.UI_MODE_TYPE_VR_HEADSET;
 
+import static com.android.server.wm.ActivityRecord.State.RESUMED;
+
 import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.AppCompatTaskInfo;
+import android.app.CameraCompatTaskInfo;
+import android.app.TaskInfo;
 import android.content.res.Configuration;
 import android.graphics.Rect;
 
@@ -73,4 +79,82 @@
     static boolean isInVrUiMode(Configuration config) {
         return (config.uiMode & UI_MODE_TYPE_MASK) == UI_MODE_TYPE_VR_HEADSET;
     }
+
+    /**
+     * @param activityRecord The {@link ActivityRecord} for the app package.
+     * @param overrideChangeId The per-app override identifier.
+     * @return {@code true} if the per-app override is enable for the given activity.
+     */
+    static boolean isChangeEnabled(@NonNull ActivityRecord activityRecord, long overrideChangeId) {
+        return activityRecord.info.isChangeEnabled(overrideChangeId);
+    }
+
+    static void fillAppCompatTaskInfo(@NonNull Task task, @NonNull TaskInfo info,
+            @Nullable ActivityRecord top) {
+        final AppCompatTaskInfo appCompatTaskInfo = info.appCompatTaskInfo;
+        appCompatTaskInfo.topActivityLetterboxVerticalPosition = TaskInfo.PROPERTY_VALUE_UNSET;
+        appCompatTaskInfo.topActivityLetterboxHorizontalPosition = TaskInfo.PROPERTY_VALUE_UNSET;
+        appCompatTaskInfo.topActivityLetterboxWidth = TaskInfo.PROPERTY_VALUE_UNSET;
+        appCompatTaskInfo.topActivityLetterboxHeight = TaskInfo.PROPERTY_VALUE_UNSET;
+        appCompatTaskInfo.cameraCompatTaskInfo.freeformCameraCompatMode =
+                CameraCompatTaskInfo.CAMERA_COMPAT_FREEFORM_NONE;
+        if (top == null) {
+            return;
+        }
+        final AppCompatReachabilityOverrides reachabilityOverrides = top.mAppCompatController
+                .getAppCompatReachabilityOverrides();
+        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.
+        appCompatTaskInfo.topActivityInSizeCompat = isTopActivityVisible && top.inSizeCompatMode();
+        if (appCompatTaskInfo.topActivityInSizeCompat
+                && top.mWmService.mAppCompatConfiguration.isTranslucentLetterboxingEnabled()) {
+            // We hide the restart button in case of transparent activities.
+            appCompatTaskInfo.topActivityInSizeCompat = top.fillsParent();
+        }
+        // Whether the direct top activity is eligible for letterbox education.
+        appCompatTaskInfo.topActivityEligibleForLetterboxEducation = isTopActivityResumed
+                && top.isEligibleForLetterboxEducation();
+        appCompatTaskInfo.isLetterboxEducationEnabled = top.mLetterboxUiController
+                .isLetterboxEducationEnabled();
+
+        appCompatTaskInfo.isUserFullscreenOverrideEnabled = top.mAppCompatController
+                .getAppCompatAspectRatioOverrides().shouldApplyUserFullscreenOverride();
+        appCompatTaskInfo.isSystemFullscreenOverrideEnabled = top.mAppCompatController
+                .getAppCompatAspectRatioOverrides().isSystemOverrideToFullscreenEnabled();
+
+        appCompatTaskInfo.isFromLetterboxDoubleTap = reachabilityOverrides.isFromDoubleTap();
+        appCompatTaskInfo.topActivityLetterboxWidth = top.getBounds().width();
+        appCompatTaskInfo.topActivityLetterboxHeight = top.getBounds().height();
+        // We need to consider if letterboxed or pillarboxed.
+        // TODO(b/336807329) Encapsulate reachability logic
+        appCompatTaskInfo.isLetterboxDoubleTapEnabled = reachabilityOverrides
+                .isLetterboxDoubleTapEducationEnabled();
+        if (appCompatTaskInfo.isLetterboxDoubleTapEnabled) {
+            if (appCompatTaskInfo.isTopActivityPillarboxed()) {
+                if (reachabilityOverrides.allowHorizontalReachabilityForThinLetterbox()) {
+                    // Pillarboxed.
+                    appCompatTaskInfo.topActivityLetterboxHorizontalPosition =
+                            reachabilityOverrides.getLetterboxPositionForHorizontalReachability();
+                } else {
+                    appCompatTaskInfo.isLetterboxDoubleTapEnabled = false;
+                }
+            } else {
+                if (reachabilityOverrides.allowVerticalReachabilityForThinLetterbox()) {
+                    // Letterboxed.
+                    appCompatTaskInfo.topActivityLetterboxVerticalPosition =
+                            reachabilityOverrides.getLetterboxPositionForVerticalReachability();
+                } else {
+                    appCompatTaskInfo.isLetterboxDoubleTapEnabled = false;
+                }
+            }
+        }
+        appCompatTaskInfo.topActivityEligibleForUserAspectRatioButton =
+                !info.isTopActivityTransparent && !appCompatTaskInfo.topActivityInSizeCompat
+                        && top.mAppCompatController.getAppCompatAspectRatioOverrides()
+                            .shouldEnableUserAspectRatioSettings();
+        appCompatTaskInfo.topActivityBoundsLetterboxed = top.areBoundsLetterboxed();
+        appCompatTaskInfo.cameraCompatTaskInfo.freeformCameraCompatMode = top.mAppCompatController
+                .getAppCompatCameraOverrides().getFreeformCameraCompatMode();
+    }
 }
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index 78636a7..5a0cbf3 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -250,7 +250,7 @@
 
         ArraySet<ActivityRecord> tmpOpenApps = mDisplayContent.mOpeningApps;
         ArraySet<ActivityRecord> tmpCloseApps = mDisplayContent.mClosingApps;
-        if (mDisplayContent.mAtmService.mBackNavigationController.isMonitoringTransition()) {
+        if (mDisplayContent.mAtmService.mBackNavigationController.isMonitoringFinishTransition()) {
             tmpOpenApps = new ArraySet<>(mDisplayContent.mOpeningApps);
             tmpCloseApps = new ArraySet<>(mDisplayContent.mClosingApps);
             if (mDisplayContent.mAtmService.mBackNavigationController
diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java
index 8421765..924f765 100644
--- a/services/core/java/com/android/server/wm/BackNavigationController.java
+++ b/services/core/java/com/android/server/wm/BackNavigationController.java
@@ -24,6 +24,7 @@
 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 com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_BACK_PREVIEW;
@@ -47,6 +48,7 @@
 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;
 import android.view.RemoteAnimationTarget;
@@ -83,11 +85,6 @@
 
     private AnimationHandler mAnimationHandler;
 
-    /**
-     * The transition who match the back navigation targets,
-     * release animation after this transition finish.
-     */
-    private Transition mWaitTransitionFinish;
     private final ArrayList<WindowContainer> mTmpOpenApps = new ArrayList<>();
     private final ArrayList<WindowContainer> mTmpCloseApps = new ArrayList<>();
 
@@ -143,7 +140,7 @@
 
         BackNavigationInfo.Builder infoBuilder = new BackNavigationInfo.Builder();
         synchronized (wmService.mGlobalLock) {
-            if (isMonitoringTransition()) {
+            if (isMonitoringFinishTransition()) {
                 Slog.w(TAG, "Previous animation hasn't finish, status: " + mAnimationHandler);
                 // Don't start any animation for it.
                 return null;
@@ -166,15 +163,14 @@
                 return null;
             }
 
-            // Move focus to the top embedded window if possible
-            if (mWindowManagerService.moveFocusToAdjacentEmbeddedWindow(window)) {
-                window = wmService.getFocusedWindowLocked();
-                if (window == null) {
-                    Slog.e(TAG, "New focused window is null, returning null.");
-                    return null;
-                }
+            // Updating the window to the most recently used one among the embedded windows
+            // that are displayed adjacently, unless the IME is visible.
+            // When the IME is visible, the IME is displayed on top of embedded activities.
+            // In that case, the back event should still be delivered to focused activity in
+            // order to dismiss the IME.
+            if (!window.getDisplayContent().getImeContainer().isVisible()) {
+                window = mWindowManagerService.getMostRecentUsedEmbeddedWindowForBack(window);
             }
-
             if (!window.isDrawn()) {
                 ProtoLog.d(WM_DEBUG_BACK_PREVIEW,
                         "Focused window didn't have a valid surface drawn.");
@@ -309,6 +305,7 @@
                     backType = BackNavigationInfo.TYPE_CALLBACK;
                 } else if (prevTask.isActivityTypeHome()) {
                     removedWindowContainer = currentTask;
+                    prevTask = prevTask.getRootTask();
                     backType = BackNavigationInfo.TYPE_RETURN_TO_HOME;
                     final ActivityRecord ar = prevTask.getTopNonFinishingActivity();
                     mShowWallpaper = ar != null && ar.hasWallpaper();
@@ -563,10 +560,15 @@
         return !prevActivities.isEmpty();
     }
 
-    boolean isMonitoringTransition() {
+    boolean isMonitoringFinishTransition() {
         return mAnimationHandler.mComposed || mNavigationMonitor.isMonitorForRemote();
     }
 
+    boolean isMonitoringPrepareTransition(Transition transition) {
+        return mAnimationHandler.mComposed
+                && mAnimationHandler.mOpenAnimAdaptor.mPreparedOpenTransition == transition;
+    }
+
     private void scheduleAnimation(@NonNull AnimationHandler.ScheduleAnimationBuilder builder) {
         mPendingAnimation = builder.build();
         mWindowManagerService.mWindowPlacerLocked.requestTraversal();
@@ -577,7 +579,9 @@
     }
 
     private boolean isWaitBackTransition() {
-        return mAnimationHandler.mComposed && mAnimationHandler.mWaitTransition;
+        // Ignore mWaitTransition while flag is enabled.
+        return mAnimationHandler.mComposed && (Flags.migratePredictiveBackTransition()
+                || mAnimationHandler.mWaitTransition);
     }
 
     boolean isKeyguardOccluded(WindowState focusWindow) {
@@ -627,7 +631,7 @@
      */
     boolean removeIfContainsBackAnimationTargets(ArraySet<ActivityRecord> openApps,
             ArraySet<ActivityRecord> closeApps) {
-        if (!isMonitoringTransition()) {
+        if (!isMonitoringFinishTransition()) {
             return false;
         }
         mTmpCloseApps.addAll(closeApps);
@@ -653,7 +657,6 @@
                 final ActivityRecord ar = openApps.valueAt(i);
                 if (mAnimationHandler.isTarget(ar, true /* open */)) {
                     openApps.removeAt(i);
-                    mAnimationHandler.markStartingSurfaceMatch(null /* reparentTransaction */);
                 }
             }
             for (int i = closeApps.size() - 1; i >= 0; --i) {
@@ -671,6 +674,12 @@
         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.
@@ -768,8 +777,14 @@
      * open or close list.
      */
     void onTransactionReady(Transition transition, ArrayList<Transition.ChangeInfo> targets,
-            SurfaceControl.Transaction startTransaction) {
-        if (!isMonitoringTransition() || targets.isEmpty()) {
+            SurfaceControl.Transaction startTransaction,
+            SurfaceControl.Transaction finishTransaction) {
+        if (isMonitoringPrepareTransition(transition)) {
+            // Flag target matches and prepare to remove windowless surface.
+            mAnimationHandler.markStartingSurfaceMatch(startTransaction);
+            return;
+        }
+        if (!isMonitoringFinishTransition() || targets.isEmpty()) {
             return;
         }
         if (mAnimationHandler.hasTargetDetached()) {
@@ -802,42 +817,40 @@
         if (!matchAnimationTargets) {
             mNavigationMonitor.onTransitionReadyWhileNavigate(mTmpOpenApps, mTmpCloseApps);
         } else {
-            if (mWaitTransitionFinish != null) {
+            if (mAnimationHandler.mPrepareCloseTransition != null) {
                 Slog.e(TAG, "Gesture animation is applied on another transition?");
             }
-            mWaitTransitionFinish = transition;
-            // Flag target matches to defer remove the splash screen.
-            for (int i = mTmpOpenApps.size() - 1; i >= 0; --i) {
-                final WindowContainer wc = mTmpOpenApps.get(i);
-                if (mAnimationHandler.isTarget(wc, true /* open */)) {
-                    mAnimationHandler.markStartingSurfaceMatch(startTransaction);
-                    break;
-                }
+            mAnimationHandler.mPrepareCloseTransition = transition;
+            if (!Flags.migratePredictiveBackTransition()) {
+                // 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) {
-                startTransaction.merge(mAnimationHandler.mOpenAnimAdaptor.mCloseTransaction);
+                finishTransaction.merge(mAnimationHandler.mOpenAnimAdaptor.mCloseTransaction);
                 mAnimationHandler.mOpenAnimAdaptor.mCloseTransaction = null;
             }
-            // 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());
         }
         mTmpOpenApps.clear();
         mTmpCloseApps.clear();
     }
 
     boolean isMonitorTransitionTarget(WindowContainer wc) {
-        if (!isWaitBackTransition() || mWaitTransitionFinish == null) {
-            return false;
+        if ((isWaitBackTransition() && mAnimationHandler.mPrepareCloseTransition != null)
+                || (mAnimationHandler.mOpenAnimAdaptor != null
+                && mAnimationHandler.mOpenAnimAdaptor.mPreparedOpenTransition != null)) {
+            return mAnimationHandler.isTarget(wc, wc.isVisibleRequested() /* open */);
         }
-        return mAnimationHandler.isTarget(wc, wc.isVisibleRequested() /* open */);
+        return false;
     }
 
     boolean shouldPauseTouch(WindowContainer wc) {
         // Once the close transition is ready, it means the onBackInvoked callback has invoked, and
         // app is ready to trigger next transition, no matter what it will be.
-        return mAnimationHandler.mComposed && mWaitTransitionFinish == null
+        return mAnimationHandler.mComposed && mAnimationHandler.mPrepareCloseTransition == null
                 && mAnimationHandler.isTarget(wc, wc.isVisibleRequested() /* open */);
     }
 
@@ -848,7 +861,6 @@
     void clearBackAnimations(boolean cancel) {
         mAnimationHandler.clearBackAnimateTarget(cancel);
         mNavigationMonitor.stopMonitorTransition();
-        mWaitTransitionFinish = null;
     }
 
     /**
@@ -859,7 +871,13 @@
     */
     void onTransitionFinish(ArrayList<Transition.ChangeInfo> targets,
             @NonNull Transition finishedTransition) {
-        if (finishedTransition == mWaitTransitionFinish) {
+        if (isMonitoringPrepareTransition(finishedTransition)) {
+            if (mAnimationHandler.mPrepareCloseTransition == null) {
+                clearBackAnimations(true /* cancel */);
+            }
+            return;
+        }
+        if (finishedTransition == mAnimationHandler.mPrepareCloseTransition) {
             clearBackAnimations(false /* cancel */);
         }
         if (!mBackAnimationInProgress || mPendingAnimationBuilder == null) {
@@ -939,6 +957,7 @@
         // the opening target like starting window do.
         private boolean mStartingSurfaceTargetMatch;
         private ActivityRecord[] mOpenActivities;
+        Transition mPrepareCloseTransition;
 
         AnimationHandler(WindowManagerService wms) {
             mWindowManagerService = wms;
@@ -983,6 +1002,12 @@
                 @NonNull ActivityRecord[] openingActivities)  {
             if (isActivitySwitch(close, open)) {
                 mSwitchType = ACTIVITY_SWITCH;
+                if (Flags.migratePredictiveBackTransition()) {
+                    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)) {
@@ -1020,6 +1045,34 @@
             mOpenActivities = openingActivities;
         }
 
+        private Pair<WindowContainer, WindowContainer[]> promoteToTFIfNeeded(
+                WindowContainer close, WindowContainer[] open) {
+            WindowContainer replaceClose = close;
+            TaskFragment closeTF = close.asActivityRecord().getTaskFragment();
+            if (closeTF != null && !closeTF.isEmbedded()) {
+                closeTF = null;
+            }
+            final WindowContainer[] replaceOpen = new WindowContainer[open.length];
+            if (open.length >= 2) { // Promote to TaskFragment
+                for (int i = open.length - 1; i >= 0; --i) {
+                    replaceOpen[i] = open[i].asActivityRecord().getTaskFragment();
+                    replaceClose = closeTF != null ? closeTF : close;
+                }
+            } else {
+                TaskFragment openTF = open[0].asActivityRecord().getTaskFragment();
+                if (openTF != null && !openTF.isEmbedded()) {
+                    openTF = null;
+                }
+                if (closeTF != openTF) {
+                    replaceOpen[0] = openTF != null ? openTF : open[0];
+                    replaceClose = closeTF != null ? closeTF : close;
+                } else {
+                    replaceOpen[0] = open[0];
+                }
+            }
+            return new Pair<>(replaceClose, replaceOpen);
+        }
+
         private boolean composeAnimations(@NonNull WindowContainer close,
                 @NonNull WindowContainer[] open, @NonNull ActivityRecord[] openingActivities) {
             if (mComposed || mWaitTransition) {
@@ -1081,7 +1134,8 @@
         }
 
         void markWindowHasDrawn(ActivityRecord activity) {
-            if (!mComposed || mWaitTransition) {
+            if (!mComposed || mWaitTransition || mOpenAnimAdaptor.mPreparedOpenTransition == null
+                    || mOpenAnimAdaptor.mRequestedStartingSurfaceId == INVALID_TASK_ID) {
                 return;
             }
             boolean allWindowDrawn = true;
@@ -1097,6 +1151,17 @@
             }
         }
 
+        boolean isStartingSurfaceDrawn(ActivityRecord activity) {
+            // Check whether we create windowless surface to prepare open transition
+            if (!mComposed || mOpenAnimAdaptor.mPreparedOpenTransition == null) {
+                return false;
+            }
+            if (isTarget(activity, true /* open */)) {
+                return mOpenAnimAdaptor.mStartingSurface != null;
+            }
+            return false;
+        }
+
         private static boolean isAnimateTarget(@NonNull WindowContainer window,
                 @NonNull WindowContainer animationTarget, int switchType) {
             if (switchType == TASK_SWITCH) {
@@ -1110,7 +1175,9 @@
                         && window.hasChild(animationTarget));
             } else if (switchType == ACTIVITY_SWITCH) {
                 return window == animationTarget
-                        || (window.asTaskFragment() != null && window.hasChild(animationTarget));
+                        || (window.asTaskFragment() != null && window.hasChild(animationTarget))
+                        || (animationTarget.asTaskFragment() != null
+                        && animationTarget.hasChild(window));
             }
             return false;
         }
@@ -1123,8 +1190,14 @@
                         resetActivity.mDisplayContent
                                 .continueUpdateOrientationForDiffOrienLaunchingApp();
                     }
+                    final Transition finishTransition =
+                            resetActivity.mTransitionController.mFinishingTransition;
+                    final boolean inFinishTransition = finishTransition != null
+                            && (mPrepareCloseTransition == finishTransition
+                            || (mOpenAnimAdaptor != null
+                            && mOpenAnimAdaptor.mPreparedOpenTransition == finishTransition));
                     if (resetActivity.mLaunchTaskBehind) {
-                        restoreLaunchBehind(resetActivity, cancel);
+                        restoreLaunchBehind(resetActivity, cancel, inFinishTransition);
                     }
                 }
             }
@@ -1138,12 +1211,25 @@
             }
         }
 
-        void markStartingSurfaceMatch(SurfaceControl.Transaction reparentTransaction) {
+        void markStartingSurfaceMatch(SurfaceControl.Transaction startTransaction) {
             if (mStartingSurfaceTargetMatch) {
                 return;
             }
             mStartingSurfaceTargetMatch = true;
-            mOpenAnimAdaptor.reparentWindowlessSurfaceToTarget(reparentTransaction);
+
+            if (mOpenAnimAdaptor.mRequestedStartingSurfaceId == INVALID_TASK_ID) {
+                return;
+            }
+            final SurfaceControl startingSurface = mOpenAnimAdaptor.mStartingSurface;
+            if (startingSurface != null && startingSurface.isValid()) {
+                startTransaction.addTransactionCommittedListener(Runnable::run, () -> {
+                    synchronized (mWindowManagerService.mGlobalLock) {
+                        if (mOpenAnimAdaptor != null) {
+                            mOpenAnimAdaptor.cleanUpWindowlessSurface(true);
+                        }
+                    }
+                });
+            }
         }
 
         void clearBackAnimateTarget(boolean cancel) {
@@ -1151,6 +1237,7 @@
                 mComposed = false;
                 finishPresentAnimations(cancel);
             }
+            mPrepareCloseTransition = null;
             mWaitTransition = false;
             mStartingSurfaceTargetMatch = false;
             mSwitchType = UNKNOWN;
@@ -1240,6 +1327,9 @@
             // requested one during animating.
             private int mRequestedStartingSurfaceId = INVALID_TASK_ID;
             private SurfaceControl mStartingSurface;
+
+            private Transition mPreparedOpenTransition;
+
             BackWindowAnimationAdaptorWrapper(boolean isOpen, int switchType,
                     @NonNull WindowContainer... targets) {
                 mAdaptors = new BackWindowAnimationAdaptor[targets.length];
@@ -1268,6 +1358,8 @@
                     mCloseTransaction.apply();
                     mCloseTransaction = null;
                 }
+
+                mPreparedOpenTransition = null;
             }
 
             private RemoteAnimationTarget createWrapTarget() {
@@ -1280,8 +1372,7 @@
                     unionBounds.union(mAdaptors[i].mAnimationTarget.localBounds);
                 }
                 final WindowContainer wc = mAdaptors[0].mTarget;
-                final Task task = wc.asActivityRecord() != null
-                        ? wc.asActivityRecord().getTask() : wc.asTask();
+                final Task task = mAdaptors[0].getTopTask();
                 final RemoteAnimationTarget represent = mAdaptors[0].mAnimationTarget;
                 final SurfaceControl leashSurface = new SurfaceControl.Builder()
                         .setName("cross-animation-leash")
@@ -1294,7 +1385,7 @@
                 mCloseTransaction = new SurfaceControl.Transaction();
                 mCloseTransaction.reparent(leashSurface, null);
                 final SurfaceControl.Transaction pt = wc.getPendingTransaction();
-                pt.setLayer(leashSurface, wc.getParent().getLastLayer());
+                pt.setLayer(leashSurface, wc.getLastLayer());
                 for (int i = mAdaptors.length - 1; i >= 0; --i) {
                     BackWindowAnimationAdaptor adaptor = mAdaptors[i];
                     pt.reparent(adaptor.mAnimationTarget.leash, leashSurface);
@@ -1324,15 +1415,20 @@
                 }
                 final WindowContainer mainOpen = mAdaptors[0].mTarget;
                 final int switchType = mAdaptors[0].mSwitchType;
-                final Task openTask = switchType == TASK_SWITCH
-                        ? mainOpen.asTask() : switchType == ACTIVITY_SWITCH
-                        ? mainOpen.asActivityRecord().getTask() : null;
+                final Task openTask = mAdaptors[0].getTopTask();
                 if (openTask == null) {
                     return;
                 }
-                final ActivityRecord mainActivity = switchType == ACTIVITY_SWITCH
-                        ? mainOpen.asActivityRecord()
-                        : openTask.getTopNonFinishingActivity();
+                ActivityRecord mainActivity = null;
+                if (switchType == ACTIVITY_SWITCH) {
+                    mainActivity = mainOpen.asActivityRecord();
+                    if (mainActivity == null && mainOpen.asTaskFragment() != null) {
+                        mainActivity = mainOpen.asTaskFragment().getTopNonFinishingActivity();
+                    }
+                }
+                if (mainActivity == null) {
+                    mainActivity = openTask.getTopNonFinishingActivity();
+                }
                 if (mainActivity == null) {
                     return;
                 }
@@ -1364,6 +1460,8 @@
                                     synchronized (openTask.mWmService.mGlobalLock) {
                                         if (mRequestedStartingSurfaceId != INVALID_TASK_ID) {
                                             mStartingSurface = sc;
+                                            openTask.mWmService.mWindowPlacerLocked
+                                                    .requestTraversal();
                                         } else {
                                             sc.release();
                                         }
@@ -1372,28 +1470,6 @@
                             });
             }
 
-            // When back gesture has triggered and transition target matches navigation target,
-            // reparent the starting surface to the opening target as it's starting window.
-            void reparentWindowlessSurfaceToTarget(SurfaceControl.Transaction reparentTransaction) {
-                if (mRequestedStartingSurfaceId == INVALID_TASK_ID) {
-                    return;
-                }
-                // If open target matches, reparent to open activity or task
-                if (mStartingSurface != null && mStartingSurface.isValid()) {
-                    SurfaceControl.Transaction transaction = reparentTransaction != null
-                            ? reparentTransaction : mAdaptors[0].mTarget.getPendingTransaction();
-                    if (mAdaptors.length != 1) {
-                        // More than one opening window, reparent starting surface to leaf task.
-                        final WindowContainer wc = mAdaptors[0].mTarget;
-                        final Task task = wc.asActivityRecord() != null
-                                ? wc.asActivityRecord().getTask() : wc.asTask();
-                        transaction.reparent(mStartingSurface, task != null
-                                        ? task.getSurfaceControl()
-                                        : mAdaptors[0].mTarget.getSurfaceControl());
-                    }
-                }
-            }
-
             /**
              * Ask shell to clear the starting surface.
              * @param openTransitionMatch if true, shell will play the remove starting window
@@ -1431,6 +1507,22 @@
                 mSwitchType = switchType;
             }
 
+            Task getTopTask() {
+                final Task asTask = mTarget.asTask();
+                if (asTask != null) {
+                    return asTask;
+                }
+                final ActivityRecord ar = mTarget.asActivityRecord();
+                if (ar != null) {
+                    return ar.getTask();
+                }
+                final TaskFragment tf = mTarget.asTaskFragment();
+                if (tf != null) {
+                    return tf.getTask();
+                }
+                return null;
+            }
+
             @Override
             public boolean getShowWallpaper() {
                 return false;
@@ -1620,9 +1712,8 @@
                     needsLaunchBehind = snapshot == null;
                 }
                 if (needsLaunchBehind) {
-                    for (int i = visibleOpenActivities.length - 1; i >= 0; --i) {
-                        setLaunchBehind(visibleOpenActivities[i]);
-                    }
+                    openAnimationAdaptor.mPreparedOpenTransition =
+                            setLaunchBehind(visibleOpenActivities);
                 }
                 // Force update mLastSurfaceShowing for opening activity and its task.
                 if (mWindowManagerService.mRoot.mTransitionController.isShellTransitionsEnabled()) {
@@ -1678,13 +1769,22 @@
                                 // animation was canceled
                                 return;
                             }
-                            if (!triggerBack) {
-                                clearBackAnimateTarget(true /* cancel */);
+                            if (Flags.migratePredictiveBackTransition()) {
+                                if (mOpenAnimAdaptor == null
+                                        || mOpenAnimAdaptor.mPreparedOpenTransition == null) {
+                                    // no open nor close transition, this is window animation
+                                    if (!triggerBack) {
+                                        clearBackAnimateTarget(true /* cancel */);
+                                    }
+                                }
                             } else {
-                                mWaitTransition = true;
+                                if (!triggerBack) {
+                                    clearBackAnimateTarget(true /* cancel */);
+                                } else {
+                                    mWaitTransition = true;
+                                }
                             }
                         }
-                        // TODO Add timeout monitor if transition didn't happen
                     }
                 };
             }
@@ -1741,28 +1841,75 @@
         return openActivities;
     }
 
-    private static void setLaunchBehind(@NonNull ActivityRecord activity) {
-        if (!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 */);
+    private static Transition setLaunchBehind(@NonNull ActivityRecord[] activities) {
+        final boolean migrateBackTransition = Flags.migratePredictiveBackTransition();
+        final ArrayList<ActivityRecord> affects = new ArrayList<>();
+        for (int i = activities.length - 1; i >= 0; --i) {
+            final ActivityRecord activity = activities[i];
+            if (activity.mLaunchTaskBehind || activity.isVisibleRequested()) {
+                continue;
+            }
+            affects.add(activity);
+        }
+        if (affects.isEmpty()) {
+            return null;
+        }
+
+        final TransitionController tc = activities[0].mTransitionController;
+        final Transition prepareOpen = migrateBackTransition && !tc.isCollecting()
+                ? tc.createTransition(TRANSIT_PREPARE_BACK_NAVIGATION) : 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;
+            activity.mLaunchTaskBehind = true;
 
-        ProtoLog.d(WM_DEBUG_BACK_PREVIEW,
-                "Setting Activity.mLauncherTaskBehind to true. Activity=%s", activity);
-        activity.mTaskSupervisor.mStoppingActivities.remove(activity);
-        activity.getDisplayContent().ensureActivitiesVisible(null /* starting */,
-                true /* notifyClients */);
+            ProtoLog.d(WM_DEBUG_BACK_PREVIEW,
+                    "Setting Activity.mLauncherTaskBehind to true. Activity=%s", activity);
+            activity.mTaskSupervisor.mStoppingActivities.remove(activity);
+
+            if (!migrateBackTransition) {
+                activity.getDisplayContent().ensureActivitiesVisible(null /* starting */,
+                        true /* notifyClients */);
+            } else if (activity.shouldBeVisible()) {
+                activity.ensureActivityConfiguration(true /* ignoreVisibility */);
+                activity.makeVisibleIfNeeded(null /* starting */, true /* notifyToClient */);
+            }
+        }
+        boolean needTransition = false;
+        final DisplayContent dc = affects.get(0).getDisplayContent();
+        for (int i = affects.size() - 1; i >= 0; --i) {
+            final ActivityRecord activity = affects.get(i);
+            needTransition |= tc.isCollecting(activity);
+        }
+        if (prepareOpen != null) {
+            if (needTransition) {
+                tc.requestStartTransition(prepareOpen,
+                        null /*startTask */, null /* remoteTransition */,
+                        null /* displayChange */);
+                tc.setReady(dc);
+                return prepareOpen;
+            } else {
+                prepareOpen.abort();
+            }
+        }
+        return null;
     }
 
-    private static void restoreLaunchBehind(@NonNull ActivityRecord activity, boolean cancel) {
+    private static void restoreLaunchBehind(@NonNull ActivityRecord activity, boolean cancel,
+            boolean finishTransition) {
         if (!activity.isAttached()) {
             // The activity was detached from hierarchy.
             return;
@@ -1772,9 +1919,15 @@
                 "Setting Activity.mLauncherTaskBehind to false. Activity=%s",
                 activity);
         if (cancel) {
-            // Restore the launch-behind state
-            // TODO b/347168362 Change status directly during collecting for a transition.
-            activity.mTaskSupervisor.scheduleLaunchTaskBehindComplete(activity.token);
+            final boolean migrateBackTransition = Flags.migratePredictiveBackTransition();
+            if (migrateBackTransition && finishTransition) {
+                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);
+            }
             // Ignore all change
             activity.mTransitionController.mSnapshotController
                     .mActivitySnapshotController.clearOnBackPressedActivities();
@@ -1836,12 +1989,7 @@
                         && ah.mSwitchType != AnimationHandler.ACTIVITY_SWITCH)) {
                     return;
                 }
-                for (int i = mAnimationHandler.mOpenActivities.length - 1; i >= 0; --i) {
-                    final ActivityRecord preDrawActivity = mAnimationHandler.mOpenActivities[i];
-                    if (!preDrawActivity.mLaunchTaskBehind) {
-                        setLaunchBehind(preDrawActivity);
-                    }
-                }
+                setLaunchBehind(mAnimationHandler.mOpenActivities);
             }
         }
     }
@@ -1854,10 +2002,15 @@
             snapshot = task.mRootWindowContainer.mWindowManager.mTaskSnapshotController.getSnapshot(
                     task.mTaskId, task.mUserId, false /* restoreFromDisk */,
                     false /* isLowResolution */);
-        } else if (w.asActivityRecord() != null) {
-            final ActivityRecord ar = w.asActivityRecord();
-            snapshot = ar.mWmService.mSnapshotController.mActivitySnapshotController
-                    .getSnapshot(visibleOpenActivities);
+        } else {
+            ActivityRecord ar = w.asActivityRecord();
+            if (ar == null && w.asTaskFragment() != null) {
+                ar = w.asTaskFragment().getTopNonFinishingActivity();
+            }
+            if (ar != null) {
+                snapshot = ar.mWmService.mSnapshotController.mActivitySnapshotController
+                        .getSnapshot(visibleOpenActivities);
+            }
         }
 
         return isSnapshotCompatible(snapshot, visibleOpenActivities) ? snapshot : null;
diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
index 54024e9..02c8a49 100644
--- a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
+++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
@@ -20,9 +20,11 @@
 import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
 import static android.app.ActivityOptions.BackgroundActivityStartMode;
 import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED;
+import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS;
 import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_COMPAT;
 import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_DENIED;
 import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED;
+import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_IF_VISIBLE;
 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE;
@@ -226,6 +228,21 @@
         };
     }
 
+    static String balStartModeToString(@BackgroundActivityStartMode int startMode) {
+        return switch (startMode) {
+            case MODE_BACKGROUND_ACTIVITY_START_ALLOWED -> "MODE_BACKGROUND_ACTIVITY_START_ALLOWED";
+            case MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED ->
+                    "MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED";
+            case MODE_BACKGROUND_ACTIVITY_START_COMPAT -> "MODE_BACKGROUND_ACTIVITY_START_COMPAT";
+            case MODE_BACKGROUND_ACTIVITY_START_DENIED -> "MODE_BACKGROUND_ACTIVITY_START_DENIED";
+            case MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS ->
+                    "MODE_BACKGROUND_ACTIVITY_START_ALWAYS";
+            case MODE_BACKGROUND_ACTIVITY_START_ALLOW_IF_VISIBLE ->
+                    "MODE_BACKGROUND_ACTIVITY_START_ALLOW_IF_VISIBLE";
+            default -> "MODE_BACKGROUND_ACTIVITY_START_ALLOWED(" + startMode + ")";
+        };
+    }
+
     @GuardedBy("mService.mGlobalLock")
     private final HashMap<Integer, FinishedActivityEntry> mTaskIdToFinishedActivity =
             new HashMap<>();
@@ -464,10 +481,6 @@
             this.mResultForRealCaller = resultForRealCaller;
         }
 
-        public boolean isPendingIntentBalAllowedByPermission() {
-            return PendingIntentRecord.isPendingIntentBalAllowedByPermission(mCheckedOptions);
-        }
-
         public boolean callerExplicitOptInOrAutoOptIn() {
             if (mAutoOptInCaller) {
                 return !callerExplicitOptOut();
@@ -528,6 +541,8 @@
             sb.append("; balAllowedByPiCreatorWithHardening: ")
                     .append(mBalAllowedByPiCreatorWithHardening);
             sb.append("; resultIfPiCreatorAllowsBal: ").append(mResultForCaller);
+            sb.append("; callerStartMode: ").append(balStartModeToString(
+                    mCheckedOptions.getPendingIntentBackgroundActivityStartMode()));
             sb.append("; hasRealCaller: ").append(hasRealCaller());
             sb.append("; isCallForResult: ").append(mIsCallForResult);
             sb.append("; isPendingIntent: ").append(isPendingIntent());
@@ -553,6 +568,8 @@
                 }
                 sb.append("; balAllowedByPiSender: ").append(mBalAllowedByPiSender);
                 sb.append("; resultIfPiSenderAllowsBal: ").append(mResultForRealCaller);
+                sb.append("; realCallerStartMode: ").append(balStartModeToString(
+                        mCheckedOptions.getPendingIntentCreatorBackgroundActivityStartMode()));
             }
             // features
             sb.append("; balImproveRealCallerVisibilityCheck: ")
@@ -949,7 +966,8 @@
             }
         }
 
-        if (state.isPendingIntentBalAllowedByPermission()
+        if (state.mCheckedOptions.getPendingIntentBackgroundActivityStartMode()
+                == MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS
                 && hasBalPermission(state.mRealCallingUid, state.mRealCallingPid)) {
             return new BalVerdict(BAL_ALLOW_PERMISSION,
                     /*background*/ false,
diff --git a/services/core/java/com/android/server/wm/ClientLifecycleManager.java b/services/core/java/com/android/server/wm/ClientLifecycleManager.java
index bf34759..0acc661 100644
--- a/services/core/java/com/android/server/wm/ClientLifecycleManager.java
+++ b/services/core/java/com/android/server/wm/ClientLifecycleManager.java
@@ -25,7 +25,6 @@
 import android.app.servertransaction.LaunchActivityItem;
 import android.compat.annotation.ChangeId;
 import android.compat.annotation.EnabledSince;
-import android.os.Binder;
 import android.os.Build;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -81,13 +80,6 @@
             Slog.w(TAG, "Failed to deliver transaction for " + client
                             + "\ntransaction=" + transaction);
             throw e;
-        } finally {
-            if (!(client instanceof Binder)) {
-                // If client is not an instance of Binder - it's a remote call and at this point it
-                // is safe to recycle the object. All objects used for local calls will be recycled
-                // after the transaction is executed on client in ActivityThread.
-                transaction.recycle();
-            }
         }
     }
 
@@ -99,7 +91,7 @@
      */
     void scheduleTransactionItemNow(@NonNull IApplicationThread client,
             @NonNull ClientTransactionItem transactionItem) throws RemoteException {
-        final ClientTransaction clientTransaction = ClientTransaction.obtain(client);
+        final ClientTransaction clientTransaction = new ClientTransaction(client);
         clientTransaction.addTransactionItem(transactionItem);
         scheduleTransaction(clientTransaction);
     }
@@ -210,7 +202,7 @@
         }
 
         // Create new transaction if there is no existing.
-        final ClientTransaction transaction = ClientTransaction.obtain(client);
+        final ClientTransaction transaction = new ClientTransaction(client);
         mPendingTransactions.put(clientBinder, transaction);
         return transaction;
     }
diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java
index efd5202..3ebaf03 100644
--- a/services/core/java/com/android/server/wm/ConfigurationContainer.java
+++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java
@@ -22,14 +22,23 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.app.WindowConfiguration.activityTypeToString;
+import static android.app.WindowConfiguration.isFloating;
 import static android.app.WindowConfiguration.windowingModeToString;
 import static android.app.WindowConfigurationProto.WINDOWING_MODE;
 import static android.content.ConfigurationProto.WINDOW_CONFIGURATION;
+import static android.content.pm.ActivityInfo.INSETS_DECOUPLED_CONFIGURATION_ENFORCED;
+import static android.content.pm.ActivityInfo.OVERRIDE_ENABLE_INSETS_DECOUPLED_CONFIGURATION;
+import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
+import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
+import static android.content.res.Configuration.ORIENTATION_UNDEFINED;
+import static android.view.Surface.ROTATION_270;
+import static android.view.Surface.ROTATION_90;
 
 import static com.android.server.wm.ConfigurationContainerProto.FULL_CONFIGURATION;
 import static com.android.server.wm.ConfigurationContainerProto.MERGED_OVERRIDE_CONFIGURATION;
@@ -38,11 +47,14 @@
 import android.annotation.CallSuper;
 import android.annotation.NonNull;
 import android.app.WindowConfiguration;
+import android.content.pm.ApplicationInfo;
 import android.content.res.Configuration;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.os.LocaleList;
+import android.util.DisplayMetrics;
 import android.util.proto.ProtoOutputStream;
+import android.view.DisplayInfo;
 
 import com.android.internal.annotations.VisibleForTesting;
 
@@ -173,6 +185,110 @@
         mResolvedOverrideConfiguration.setTo(mRequestedOverrideConfiguration);
     }
 
+    /**
+     * If necessary, override configuration fields related to app bounds.
+     * This will happen when the app is targeting SDK earlier than 35.
+     * The insets and configuration has decoupled since SDK level 35, to make the system
+     * compatible to existing apps, override the configuration with legacy metrics. In legacy
+     * metrics, fields such as appBounds will exclude some of the system bar areas.
+     * The override contains all potentially affected fields in Configuration, including
+     * screenWidthDp, screenHeightDp, smallestScreenWidthDp, and orientation.
+     * All overrides to those fields should be in this method.
+     *
+     * TODO: Consider integrate this with computeConfigByResolveHint()
+     */
+    static void applySizeOverrideIfNeeded(DisplayContent displayContent, ApplicationInfo appInfo,
+            Configuration newParentConfiguration, Configuration inOutConfig,
+            boolean optsOutEdgeToEdge, boolean hasFixedRotationTransform,
+            boolean hasCompatDisplayInsets) {
+        if (displayContent == null) {
+            return;
+        }
+        final boolean useOverrideInsetsForConfig =
+                displayContent.mWmService.mFlags.mInsetsDecoupledConfiguration
+                        ? !appInfo.isChangeEnabled(INSETS_DECOUPLED_CONFIGURATION_ENFORCED)
+                                && !appInfo.isChangeEnabled(
+                                        OVERRIDE_ENABLE_INSETS_DECOUPLED_CONFIGURATION)
+                        : appInfo.isChangeEnabled(OVERRIDE_ENABLE_INSETS_DECOUPLED_CONFIGURATION);
+        final int parentWindowingMode =
+                newParentConfiguration.windowConfiguration.getWindowingMode();
+        final boolean isFloating = isFloating(parentWindowingMode)
+                // Check the requested windowing mode of activity as well in case it is
+                // switching between PiP and fullscreen.
+                && (inOutConfig.windowConfiguration.getWindowingMode() == WINDOWING_MODE_UNDEFINED
+                        || isFloating(inOutConfig.windowConfiguration.getWindowingMode()));
+        int rotation = newParentConfiguration.windowConfiguration.getRotation();
+        if (rotation == ROTATION_UNDEFINED && !hasFixedRotationTransform) {
+            rotation = displayContent.getRotation();
+        }
+        if (!optsOutEdgeToEdge && (!useOverrideInsetsForConfig
+                || hasCompatDisplayInsets
+                || isFloating
+                || rotation == ROTATION_UNDEFINED)) {
+            // If the insets configuration decoupled logic is not enabled for the app, or the app
+            // already has a compat override, or the context doesn't contain enough info to
+            // calculate the override, skip the override.
+            return;
+        }
+        // Make sure the orientation related fields will be updated by the override insets, because
+        // fixed rotation has assigned the fields from display's configuration.
+        if (hasFixedRotationTransform) {
+            inOutConfig.windowConfiguration.setAppBounds(null);
+            inOutConfig.screenWidthDp = Configuration.SCREEN_WIDTH_DP_UNDEFINED;
+            inOutConfig.screenHeightDp = Configuration.SCREEN_HEIGHT_DP_UNDEFINED;
+            inOutConfig.smallestScreenWidthDp = Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED;
+            inOutConfig.orientation = ORIENTATION_UNDEFINED;
+        }
+
+        // Override starts here.
+        final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270);
+        final int dw = rotated
+                ? displayContent.mBaseDisplayHeight
+                : displayContent.mBaseDisplayWidth;
+        final int dh = rotated
+                ? displayContent.mBaseDisplayWidth
+                : displayContent.mBaseDisplayHeight;
+        // This should be the only place override the configuration for ActivityRecord. Override
+        // the value if not calculated yet.
+        Rect outAppBounds = inOutConfig.windowConfiguration.getAppBounds();
+        if (outAppBounds == null || outAppBounds.isEmpty()) {
+            inOutConfig.windowConfiguration.setAppBounds(
+                    newParentConfiguration.windowConfiguration.getBounds());
+            outAppBounds = inOutConfig.windowConfiguration.getAppBounds();
+            outAppBounds.inset(displayContent.getDisplayPolicy()
+                    .getDecorInsetsInfo(rotation, dw, dh).mOverrideNonDecorInsets);
+        }
+        float density = inOutConfig.densityDpi;
+        if (density == Configuration.DENSITY_DPI_UNDEFINED) {
+            density = newParentConfiguration.densityDpi;
+        }
+        density *= DisplayMetrics.DENSITY_DEFAULT_SCALE;
+        if (inOutConfig.screenWidthDp == Configuration.SCREEN_WIDTH_DP_UNDEFINED) {
+            inOutConfig.screenWidthDp = (int) (outAppBounds.width() / density + 0.5f);
+        }
+        if (inOutConfig.screenHeightDp == Configuration.SCREEN_HEIGHT_DP_UNDEFINED) {
+            inOutConfig.screenHeightDp = (int) (outAppBounds.height() / density + 0.5f);
+        }
+        if (inOutConfig.smallestScreenWidthDp == Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED
+                && parentWindowingMode == WINDOWING_MODE_FULLSCREEN) {
+            // For the case of PIP transition and multi-window environment, the
+            // smallestScreenWidthDp is handled already. Override only if the app is in
+            // fullscreen.
+            final DisplayInfo info = new DisplayInfo(displayContent.getDisplayInfo());
+            displayContent.computeSizeRanges(info, rotated, dw, dh,
+                    displayContent.getDisplayMetrics().density,
+                    inOutConfig, true /* overrideConfig */);
+        }
+
+        // It's possible that screen size will be considered in different orientation with or
+        // without considering the system bar insets. Override orientation as well.
+        if (inOutConfig.orientation == ORIENTATION_UNDEFINED) {
+            inOutConfig.orientation = (inOutConfig.screenWidthDp <= inOutConfig.screenHeightDp)
+                    ? ORIENTATION_PORTRAIT
+                    : ORIENTATION_LANDSCAPE;
+        }
+    }
+
     /** Returns {@code true} if requested override override configuration is not empty. */
     boolean hasRequestedOverrideConfiguration() {
         return mHasOverrideConfiguration;
diff --git a/services/core/java/com/android/server/wm/DeferredDisplayUpdater.java b/services/core/java/com/android/server/wm/DeferredDisplayUpdater.java
index 3b99954..1994174 100644
--- a/services/core/java/com/android/server/wm/DeferredDisplayUpdater.java
+++ b/services/core/java/com/android/server/wm/DeferredDisplayUpdater.java
@@ -52,7 +52,7 @@
  * display change transition. In this case, we will queue all display updates until the current
  * transition's collection finishes and then apply them afterwards.
  */
-public class DeferredDisplayUpdater implements DisplayUpdater {
+class DeferredDisplayUpdater {
 
     /**
      * List of fields that could be deferred before applying to DisplayContent.
@@ -110,7 +110,7 @@
         continueScreenUnblocking();
     };
 
-    public DeferredDisplayUpdater(@NonNull DisplayContent displayContent) {
+    DeferredDisplayUpdater(@NonNull DisplayContent displayContent) {
         mDisplayContent = displayContent;
         mNonOverrideDisplayInfo.copyFrom(mDisplayContent.getDisplayInfo());
     }
@@ -122,8 +122,7 @@
      *
      * @param finishCallback is called when all pending display updates are finished
      */
-    @Override
-    public void updateDisplayInfo(@NonNull Runnable finishCallback) {
+    void updateDisplayInfo(@NonNull Runnable finishCallback) {
         // Get the latest display parameters from the DisplayManager
         final DisplayInfo displayInfo = getCurrentDisplayInfo();
 
@@ -310,9 +309,11 @@
         return !Objects.equals(first.uniqueId, second.uniqueId);
     }
 
-    @Override
-    public void onDisplayContentDisplayPropertiesPostChanged(int previousRotation, int newRotation,
-            DisplayAreaInfo newDisplayAreaInfo) {
+    /**
+     * Called after physical display has changed and after DisplayContent applied new display
+     * properties.
+     */
+    void onDisplayContentDisplayPropertiesPostChanged() {
         // Unblock immediately in case there is no transition. This is unlikely to happen.
         if (mScreenUnblocker != null && !mDisplayContent.mTransitionController.inTransition()) {
             mScreenUnblocker.sendToTarget();
@@ -320,13 +321,16 @@
         }
     }
 
-    @Override
-    public void onDisplaySwitching(boolean switching) {
+    /**
+     * Called with {@code true} when physical display is going to switch. And {@code false} when
+     * the display is turned on or the device goes to sleep.
+     */
+    void onDisplaySwitching(boolean switching) {
         mShouldWaitForTransitionWhenScreenOn = switching;
     }
 
-    @Override
-    public boolean waitForTransition(@NonNull Message screenUnblocker) {
+    /** Returns {@code true} if the transition will control when to turn on the screen. */
+    boolean waitForTransition(@NonNull Message screenUnblocker) {
         if (!Flags.waitForTransitionOnDisplaySwitch()) return false;
         if (!mShouldWaitForTransitionWhenScreenOn) {
             return false;
diff --git a/services/core/java/com/android/server/wm/DesktopModeBoundsCalculator.java b/services/core/java/com/android/server/wm/DesktopModeBoundsCalculator.java
index 3ecdff6..9996bbc 100644
--- a/services/core/java/com/android/server/wm/DesktopModeBoundsCalculator.java
+++ b/services/core/java/com/android/server/wm/DesktopModeBoundsCalculator.java
@@ -41,7 +41,7 @@
 
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.window.flags.Flags;
+import com.android.server.wm.utils.DesktopModeFlagsUtil;
 
 import java.util.function.Consumer;
 
@@ -106,7 +106,7 @@
         final TaskDisplayArea displayArea = task.getDisplayArea();
         final Rect screenBounds = displayArea.getBounds();
         final Size idealSize = calculateIdealSize(screenBounds, DESKTOP_MODE_INITIAL_BOUNDS_SCALE);
-        if (!Flags.enableWindowingDynamicInitialBounds()) {
+        if (!DesktopModeFlagsUtil.DYNAMIC_INITIAL_BOUNDS.isEnabled(activity.mWmService.mContext)) {
             return centerInScreen(idealSize, screenBounds);
         }
         // TODO(b/353457301): Replace with app compat aspect ratio method when refactoring complete.
diff --git a/services/core/java/com/android/server/wm/Dimmer.java b/services/core/java/com/android/server/wm/Dimmer.java
index 7ce9de4..2d1eb41 100644
--- a/services/core/java/com/android/server/wm/Dimmer.java
+++ b/services/core/java/com/android/server/wm/Dimmer.java
@@ -16,39 +16,194 @@
 
 package com.android.server.wm;
 
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_DIMMER;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+
 import android.annotation.NonNull;
 import android.graphics.Rect;
+import android.util.Log;
+import android.view.Surface;
 import android.view.SurfaceControl;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.protolog.ProtoLog;
 import com.android.window.flags.Flags;
 
+
 /**
  * Utility class for use by a WindowContainer implementation to add "DimLayer" support, that is
  * black layers of varying opacity at various Z-levels which create the effect of a Dim.
  */
-public abstract class Dimmer {
-
-    static final boolean DIMMER_REFACTOR = Flags.introduceSmootherDimmer();
+class Dimmer {
 
     /**
      * The {@link WindowContainer} that our Dims are bounded to. We may be dimming on behalf of the
      * host, some controller of it, or one of the hosts children.
      */
-    protected final WindowContainer mHost;
+    private final WindowContainer<?> mHost;
 
-    protected Dimmer(WindowContainer host) {
+    private static final String TAG = TAG_WITH_CLASS_NAME ? "Dimmer" : TAG_WM;
+    DimState mDimState;
+    final DimmerAnimationHelper.AnimationAdapterFactory mAnimationAdapterFactory;
+
+    /**
+     * Controls the dim behaviour
+     */
+    protected class DimState {
+        /** Related objects */
+        SurfaceControl mDimSurface;
+        final WindowContainer<?> mHostContainer;
+        // The last container to request to dim
+        private WindowContainer<?> mLastRequestedDimContainer;
+        /** Animation */
+        private final DimmerAnimationHelper mAnimationHelper;
+        boolean mSkipAnimation = false;
+        // Determines whether the dim layer should animate before destroying.
+        boolean mAnimateExit = true;
+        /** Surface visibility and bounds */
+        private boolean mIsVisible = false;
+        // TODO(b/64816140): Remove after confirming dimmer layer always matches its container.
+        final Rect mDimBounds = new Rect();
+
+        DimState() {
+            mHostContainer = mHost;
+            mAnimationHelper = new DimmerAnimationHelper(mAnimationAdapterFactory);
+            try {
+                mDimSurface = makeDimLayer();
+            } catch (Surface.OutOfResourcesException e) {
+                Log.w(TAG, "OutOfResourcesException creating dim surface");
+            }
+        }
+
+        void ensureVisible(@NonNull SurfaceControl.Transaction t) {
+            if (!mIsVisible) {
+                t.show(mDimSurface);
+                t.setAlpha(mDimSurface, 0f);
+                mIsVisible = true;
+            }
+        }
+
+        void adjustSurfaceLayout(@NonNull SurfaceControl.Transaction t) {
+            // TODO: Once we use geometry from hierarchy this falls away.
+            t.setPosition(mDimSurface, mDimBounds.left, mDimBounds.top);
+            t.setWindowCrop(mDimSurface, mDimBounds.width(), mDimBounds.height());
+        }
+
+        /**
+         * Set the parameters to prepare the dim to change its appearance
+         */
+        void prepareLookChange(float alpha, int blurRadius) {
+            mAnimationHelper.setRequestedAppearance(alpha, blurRadius);
+        }
+
+        /**
+         * Prepare the dim for the exit animation
+         */
+        void exit(@NonNull SurfaceControl.Transaction t) {
+            if (!mAnimateExit) {
+                remove(t);
+            } else {
+                mAnimationHelper.setExitParameters();
+                setReady(t);
+            }
+        }
+
+        void remove(@NonNull SurfaceControl.Transaction t) {
+            mAnimationHelper.stopCurrentAnimation(mDimSurface);
+            if (mDimSurface.isValid()) {
+                t.remove(mDimSurface);
+                ProtoLog.d(WM_DEBUG_DIMMER,
+                        "Removing dim surface %s on transaction %s", this, t);
+            } else {
+                Log.w(TAG, "Tried to remove " + mDimSurface + " multiple times\n");
+            }
+        }
+
+        @Override
+        public String toString() {
+            return "Dimmer#DimState with host=" + mHostContainer + ", surface=" + mDimSurface;
+        }
+
+        /**
+         * Set the parameters to prepare the dim to be relative parented to the dimming container
+         */
+        void prepareReparent(@NonNull WindowContainer<?> geometryParent,
+                @NonNull WindowContainer<?> relativeParent, int relativeLayer) {
+            mAnimationHelper.setRequestedRelativeParent(relativeParent, relativeLayer);
+            mAnimationHelper.setRequestedGeometryParent(geometryParent);
+        }
+
+        /**
+         * Call when all the changes have been requested to have them applied
+         * @param t The transaction in which to apply the changes
+         */
+        void setReady(@NonNull SurfaceControl.Transaction t) {
+            mAnimationHelper.applyChanges(t, this);
+        }
+
+        /**
+         * Whether anyone is currently requesting the dim
+         */
+        boolean isDimming() {
+            return mLastRequestedDimContainer != null
+                    && (mHostContainer.isVisibleRequested() || !Flags.useTasksDimOnly());
+        }
+
+        private SurfaceControl makeDimLayer() {
+            return mHost.makeChildSurface(null)
+                    .setParent(mHost.getSurfaceControl())
+                    .setColorLayer()
+                    .setName("Dim Layer for - " + mHost.getName())
+                    .setCallsite("DimLayer.makeDimLayer")
+                    .build();
+        }
+    }
+
+    protected Dimmer(@NonNull WindowContainer<?> host) {
+        this(host, new DimmerAnimationHelper.AnimationAdapterFactory());
+    }
+
+    @VisibleForTesting
+    Dimmer(@NonNull WindowContainer host,
+                 @NonNull DimmerAnimationHelper.AnimationAdapterFactory animationFactory) {
         mHost = host;
+        mAnimationAdapterFactory = animationFactory;
     }
 
-    // Constructs the correct type of dimmer
-    static Dimmer create(WindowContainer host) {
-        return DIMMER_REFACTOR ? new SmoothDimmer(host) : new LegacyDimmer(host);
+    public boolean hostIsTask() {
+        return mHost.asTask() != null;
     }
 
-    @NonNull
-    WindowContainer<?> getHost() {
-        return mHost;
+    /**
+     * Mark all dims as pending completion on the next call to {@link #updateDims}
+     *
+     * Called before iterating on mHost's children, first step of dimming.
+     * This is intended for us by the host container, to be called at the beginning of
+     * {@link WindowContainer#prepareSurfaces}. After calling this, the container should
+     * chain {@link WindowContainer#prepareSurfaces} down to its children to give them
+     * a chance to request dims to continue.
+     */
+    void resetDimStates() {
+        if (mDimState != null) {
+            mDimState.mLastRequestedDimContainer = null;
+        }
+    }
+
+    /**
+     * Set the aspect of the dim layer, and request to keep dimming.
+     * For each call to {@link WindowContainer#prepareSurfaces} the Dim state will be reset, and the
+     * child should call setAppearance again to request the Dim to continue.
+     * If multiple containers call this method, only the changes relative to the topmost will be
+     * applied.
+     * @param dimmingContainer  Container requesting the dim
+     * @param alpha      Dim amount
+     * @param blurRadius Blur amount
+     */
+    protected void adjustAppearance(@NonNull WindowContainer<?> dimmingContainer,
+                                    float alpha, int blurRadius) {
+        final DimState d = obtainDimState(dimmingContainer);
+        d.prepareLookChange(alpha, blurRadius);
     }
 
     /**
@@ -62,42 +217,17 @@
      * the child of the host should call adjustRelativeLayer and {@link Dimmer#adjustAppearance} to
      * continue dimming. Indeed, this method won't be able to keep dimming or get a new DimState
      * without also adjusting the appearance.
-     * @param container      The container which to dim above. Should be a child of the host.
+     * @param geometryParent    The container that defines the geometry of the dim
+     * @param dimmingContainer      The container which to dim above. Should be a child of the host.
      * @param relativeLayer  The position of the dim wrt the container
      */
-    protected abstract void adjustRelativeLayer(WindowContainer container, int relativeLayer);
-
-    /**
-     * Set the aspect of the dim layer, and request to keep dimming.
-     * For each call to {@link WindowContainer#prepareSurfaces} the Dim state will be reset, and the
-     * child should call setAppearance again to request the Dim to continue.
-     * If multiple containers call this method, only the changes relative to the topmost will be
-     * applied.
-     * @param container  Container requesting the dim
-     * @param alpha      Dim amount
-     * @param blurRadius Blur amount
-     */
-    protected abstract void adjustAppearance(
-            WindowContainer container, float alpha, int blurRadius);
-
-    /**
-     * Mark all dims as pending completion on the next call to {@link #updateDims}
-     *
-     * Called before iterating on mHost's children, first step of dimming.
-     * This is intended for us by the host container, to be called at the beginning of
-     * {@link WindowContainer#prepareSurfaces}. After calling this, the container should
-     * chain {@link WindowContainer#prepareSurfaces} down to it's children to give them
-     * a chance to request dims to continue.
-     */
-    abstract void resetDimStates();
-
-    /** Returns non-null bounds if the dimmer is showing. */
-    abstract Rect getDimBounds();
-
-    abstract void dontAnimateExit();
-
-    @VisibleForTesting
-    abstract SurfaceControl getDimLayer();
+    public void adjustPosition(@NonNull WindowContainer<?> geometryParent,
+                                    @NonNull WindowContainer<?> dimmingContainer,
+                                    int relativeLayer) {
+        if (mDimState != null) {
+            mDimState.prepareReparent(geometryParent, dimmingContainer, relativeLayer);
+        }
+    }
 
     /**
      * Call after invoking {@link WindowContainer#prepareSurfaces} on children as
@@ -106,5 +236,54 @@
      * @param t      A transaction in which to update the dims.
      * @return true if any Dims were updated.
      */
-    abstract boolean updateDims(SurfaceControl.Transaction t);
+    boolean updateDims(@NonNull SurfaceControl.Transaction t) {
+        if (mDimState == null) {
+            return false;
+        }
+        if (!mDimState.isDimming()) {
+            // No one is dimming, fade out and remove the dim
+            mDimState.exit(t);
+            mDimState = null;
+            return false;
+        } else {
+            // Someone is dimming, show the requested changes
+            if (!Flags.useTasksDimOnly()) {
+                mDimState.adjustSurfaceLayout(t);
+            }
+            final WindowState ws = mDimState.mLastRequestedDimContainer.asWindowState();
+            if (!mDimState.mIsVisible && ws != null && ws.mActivityRecord != null
+                    && ws.mActivityRecord.mStartingData != null) {
+                // Skip enter animation while starting window is on top of its activity
+                mDimState.mSkipAnimation = true;
+            }
+            mDimState.setReady(t);
+            return true;
+        }
+    }
+
+    @NonNull
+    private DimState obtainDimState(@NonNull WindowContainer<?> container) {
+        if (mDimState == null) {
+            mDimState = new DimState();
+        }
+        mDimState.mLastRequestedDimContainer = container;
+        return mDimState;
+    }
+
+    /** Returns non-null bounds if the dimmer is showing. */
+    @VisibleForTesting
+    SurfaceControl getDimLayer() {
+        return mDimState != null ? mDimState.mDimSurface : null;
+    }
+
+    @Deprecated
+    Rect getDimBounds() {
+        return mDimState != null ? mDimState.mDimBounds : null;
+    }
+
+    void dontAnimateExit() {
+        if (mDimState != null) {
+            mDimState.mAnimateExit = false;
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/wm/DimmerAnimationHelper.java b/services/core/java/com/android/server/wm/DimmerAnimationHelper.java
index 22fa88f..3dba57f 100644
--- a/services/core/java/com/android/server/wm/DimmerAnimationHelper.java
+++ b/services/core/java/com/android/server/wm/DimmerAnimationHelper.java
@@ -25,6 +25,8 @@
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.util.Log;
 import android.util.proto.ProtoOutputStream;
 import android.view.SurfaceControl;
@@ -46,13 +48,14 @@
     static class Change {
         private float mAlpha = -1f;
         private int mBlurRadius = -1;
-        private WindowContainer mDimmingContainer = null;
+        private WindowContainer<?> mDimmingContainer = null;
+        private WindowContainer<?> mGeometryParent = null;
         private int mRelativeLayer = -1;
         private static final float EPSILON = 0.0001f;
 
         Change() {}
 
-        Change(Change other) {
+        Change(@NonNull Change other) {
             mAlpha = other.mAlpha;
             mBlurRadius = other.mBlurRadius;
             mDimmingContainer = other.mDimmingContainer;
@@ -60,15 +63,15 @@
         }
 
         // Same alpha and blur
-        boolean hasSameVisualProperties(Change other) {
+        boolean hasSameVisualProperties(@NonNull Change other) {
             return Math.abs(mAlpha - other.mAlpha) < EPSILON && mBlurRadius == other.mBlurRadius;
         }
 
-        boolean hasSameDimmingContainer(Change other) {
+        boolean hasSameDimmingContainer(@NonNull Change other) {
             return mDimmingContainer != null && mDimmingContainer == other.mDimmingContainer;
         }
 
-        void inheritPropertiesFromAnimation(AnimationSpec anim) {
+        void inheritPropertiesFromAnimation(@NonNull AnimationSpec anim) {
             mAlpha = anim.mCurrentAlpha;
             mBlurRadius = anim.mCurrentBlur;
         }
@@ -97,11 +100,16 @@
     }
 
     // Sets a requested change without applying it immediately
-    void setRequestedRelativeParent(WindowContainer relativeParent, int relativeLayer) {
+    void setRequestedRelativeParent(@NonNull WindowContainer<?> relativeParent, int relativeLayer) {
         mRequestedProperties.mDimmingContainer = relativeParent;
         mRequestedProperties.mRelativeLayer = relativeLayer;
     }
 
+    // Sets the requested layer to reparent the dim to without applying it immediately
+    void setRequestedGeometryParent(WindowContainer<?> geometryParent) {
+        mRequestedProperties.mGeometryParent = geometryParent;
+    }
+
     // Sets a requested change without applying it immediately
     void setRequestedAppearance(float alpha, int blurRadius) {
         mRequestedProperties.mAlpha = alpha;
@@ -114,7 +122,7 @@
      * {@link Change#setRequestedRelativeParent(WindowContainer, int)}, or
      * {@link Change#setRequestedAppearance(float, int)}
      */
-    void applyChanges(SurfaceControl.Transaction t, SmoothDimmer.DimState dim) {
+    void applyChanges(@NonNull SurfaceControl.Transaction t, @NonNull Dimmer.DimState dim) {
         if (mRequestedProperties.mDimmingContainer == null) {
             Log.e(TAG, this + " does not have a dimming container. Have you forgotten to "
                     + "call adjustRelativeLayer?");
@@ -128,7 +136,9 @@
         }
 
         dim.ensureVisible(t);
-        relativeReparent(dim.mDimSurface,
+        reparent(dim.mDimSurface,
+                mRequestedProperties.mGeometryParent != mCurrentProperties.mGeometryParent
+                        ? mRequestedProperties.mGeometryParent.getSurfaceControl() : null,
                 mRequestedProperties.mDimmingContainer.getSurfaceControl(),
                 mRequestedProperties.mRelativeLayer, t);
 
@@ -160,7 +170,7 @@
     }
 
     private void startAnimation(
-            SurfaceControl.Transaction t, SmoothDimmer.DimState dim) {
+            @NonNull SurfaceControl.Transaction t, @NonNull Dimmer.DimState dim) {
         ProtoLog.v(WM_DEBUG_DIMMER, "Starting animation on %s", dim);
         mAlphaAnimationSpec = getRequestedAnimationSpec();
         mLocalAnimationAdapter = mAnimationAdapterFactory.get(mAlphaAnimationSpec,
@@ -186,7 +196,7 @@
         return mAlphaAnimationSpec != null;
     }
 
-    void stopCurrentAnimation(SurfaceControl surface) {
+    void stopCurrentAnimation(@NonNull SurfaceControl surface) {
         if (mLocalAnimationAdapter != null && isAnimating()) {
             // Save the current animation progress and cancel the animation
             mCurrentProperties.inheritPropertiesFromAnimation(mAlphaAnimationSpec);
@@ -196,6 +206,7 @@
         }
     }
 
+    @NonNull
     private AnimationSpec getRequestedAnimationSpec() {
         final float startAlpha = Math.max(mCurrentProperties.mAlpha, 0f);
         final int startBlur = Math.max(mCurrentProperties.mBlurRadius, 0);
@@ -212,18 +223,25 @@
     }
 
     /**
-     * Change the relative parent of this dim layer
+     * Change the geometry and relative parent of this dim layer
      */
-    void relativeReparent(SurfaceControl dimLayer, SurfaceControl relativeParent,
-                          int relativePosition, SurfaceControl.Transaction t) {
+    void reparent(@NonNull SurfaceControl dimLayer,
+                  @Nullable SurfaceControl newGeometryParent,
+                  @NonNull SurfaceControl relativeParent,
+                  int relativePosition,
+                  @NonNull SurfaceControl.Transaction t) {
         try {
+            if (newGeometryParent != null) {
+                t.reparent(dimLayer, newGeometryParent);
+            }
             t.setRelativeLayer(dimLayer, relativeParent, relativePosition);
         } catch (NullPointerException e) {
             Log.w(TAG, "Tried to change parent of dim " + dimLayer + " after remove", e);
         }
     }
 
-    void setAlphaBlur(SurfaceControl sc, float alpha, int blur, SurfaceControl.Transaction t) {
+    void setAlphaBlur(@NonNull SurfaceControl sc, float alpha, int blur,
+                      @NonNull SurfaceControl.Transaction t) {
         try {
             t.setAlpha(sc, alpha);
             t.setBackgroundBlurRadius(sc, blur);
@@ -232,7 +250,7 @@
         }
     }
 
-    private long getDimDuration(WindowContainer container) {
+    private long getDimDuration(@NonNull WindowContainer<?> container) {
         // Use the same duration as the animation on the WindowContainer
         AnimationAdapter animationAdapter = container.mSurfaceAnimator.getAnimation();
         final float durationScale = container.mWmService.getTransitionAnimationScaleLocked();
@@ -282,7 +300,8 @@
         }
 
         @Override
-        public void apply(SurfaceControl.Transaction t, SurfaceControl sc, long currentPlayTime) {
+        public void apply(@NonNull SurfaceControl.Transaction t, @NonNull SurfaceControl sc,
+                          long currentPlayTime) {
             if (!mStarted) {
                 // The first frame would end up in the sync transaction, and since this could be
                 // applied after the animation transaction, we avoid putting visible changes here.
diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java
index def495f..86f69cd 100644
--- a/services/core/java/com/android/server/wm/DisplayArea.java
+++ b/services/core/java/com/android/server/wm/DisplayArea.java
@@ -267,7 +267,8 @@
                 // between fullscreen and PiP would work well. Checking TaskFragment rather than
                 // Task to ensure that Activity Embedding is excluded.
                 && activity.getTaskFragment().getWindowingMode() == WINDOWING_MODE_FULLSCREEN
-                && activity.mLetterboxUiController.isOverrideRespectRequestedOrientationEnabled();
+                && activity.mAppCompatController.getAppCompatOrientationOverrides()
+                    .isOverrideRespectRequestedOrientationEnabled();
     }
 
     boolean getIgnoreOrientationRequest() {
@@ -817,7 +818,7 @@
      * DisplayArea that can be dimmed.
      */
     static class Dimmable extends DisplayArea<DisplayArea> {
-        private final Dimmer mDimmer = Dimmer.create(this);
+        private final Dimmer mDimmer = new Dimmer(this);
 
         Dimmable(WindowManagerService wms, Type type, String name, int featureId) {
             super(wms, type, name, featureId);
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 9371149..3a0de85 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -159,7 +159,6 @@
 import static com.android.server.wm.utils.DisplayInfoOverrides.copyDisplayInfoFields;
 import static com.android.server.wm.utils.RegionUtils.forEachRectReverse;
 import static com.android.server.wm.utils.RegionUtils.rectListToRegion;
-import static com.android.window.flags.Flags.deferDisplayUpdates;
 import static com.android.window.flags.Flags.explicitRefreshRateHints;
 
 import android.annotation.IntDef;
@@ -478,7 +477,7 @@
     AppCompatCameraPolicy mAppCompatCameraPolicy;
 
     DisplayFrames mDisplayFrames;
-    final DisplayUpdater mDisplayUpdater;
+    final DeferredDisplayUpdater mDisplayUpdater;
 
     private boolean mInTouchMode;
 
@@ -550,7 +549,6 @@
 
     /** Save allocating when calculating rects */
     private final Rect mTmpRect = new Rect();
-    private final Rect mTmpRect2 = new Rect();
     private final Region mTmpRegion = new Region();
 
     private final Configuration mTmpConfiguration = new Configuration();
@@ -619,13 +617,11 @@
     private static final long FIXED_ROTATION_HIDE_ANIMATION_DEBOUNCE_DELAY_MS = 250;
     private AsyncRotationController mAsyncRotationController;
 
-    final FixedRotationTransitionListener mFixedRotationTransitionListener =
-            new FixedRotationTransitionListener();
+    final FixedRotationTransitionListener mFixedRotationTransitionListener;
 
     @VisibleForTesting
     final DeviceStateController mDeviceStateController;
     final Consumer<DeviceStateController.DeviceState> mDeviceStateConsumer;
-    final PhysicalDisplaySwitchTransitionLauncher mDisplaySwitchTransitionLauncher;
     final RemoteDisplayChangeController mRemoteDisplayChangeController;
 
     /** Windows added since {@link #mCurrentFocus} was set to null. Used for ANR blaming. */
@@ -1142,11 +1138,7 @@
         mWallpaperController.resetLargestDisplay(display);
         display.getDisplayInfo(mDisplayInfo);
         display.getMetrics(mDisplayMetrics);
-        if (deferDisplayUpdates()) {
-            mDisplayUpdater = new DeferredDisplayUpdater(this);
-        } else {
-            mDisplayUpdater = new ImmediateDisplayUpdater(this);
-        }
+        mDisplayUpdater = new DeferredDisplayUpdater(this);
         mSystemGestureExclusionLimit = mWmService.mConstants.mSystemGestureExclusionLimitDp
                 * mDisplayMetrics.densityDpi / DENSITY_DEFAULT;
         isDefaultDisplay = mDisplayId == DEFAULT_DISPLAY;
@@ -1163,14 +1155,13 @@
                 TAG_WM + "/displayId:" + mDisplayId, mDisplayId);
         mHoldScreenWakeLock.setReferenceCounted(false);
 
+        mFixedRotationTransitionListener = new FixedRotationTransitionListener(mDisplayId);
         mAppTransition = new AppTransition(mWmService.mContext, mWmService, this);
         mAppTransition.registerListenerLocked(mWmService.mActivityManagerAppTransitionNotifier);
         mAppTransition.registerListenerLocked(mFixedRotationTransitionListener);
         mAppTransitionController = new AppTransitionController(mWmService, this);
         mTransitionController.registerLegacyListener(mFixedRotationTransitionListener);
         mUnknownAppVisibilityController = new UnknownAppVisibilityController(mWmService, this);
-        mDisplaySwitchTransitionLauncher = new PhysicalDisplaySwitchTransitionLauncher(this,
-                mTransitionController);
         mRemoteDisplayChangeController = new RemoteDisplayChangeController(this);
 
         final InputChannel inputChannel = mWmService.mInputManager.monitorInput(
@@ -1191,7 +1182,6 @@
 
         mDeviceStateConsumer =
                 (@NonNull DeviceStateController.DeviceState newFoldState) -> {
-                    mDisplaySwitchTransitionLauncher.foldStateChanged(newFoldState);
                     mDisplayRotation.foldStateChanged(newFoldState);
                 };
         mDeviceStateController.registerDeviceStateCallback(mDeviceStateConsumer,
@@ -2940,8 +2930,9 @@
 
         if (!handlesOrientationChangeFromDescendant(orientation)) {
             ActivityRecord topActivity = topRunningActivity(/* considerKeyguardState= */ true);
-            if (topActivity != null && topActivity.mLetterboxUiController
-                    .shouldUseDisplayLandscapeNaturalOrientation()) {
+            if (topActivity != null && topActivity.mAppCompatController
+                    .getAppCompatOrientationOverrides()
+                        .shouldUseDisplayLandscapeNaturalOrientation()) {
                 ProtoLog.v(WM_DEBUG_ORIENTATION,
                         "Display id=%d is ignoring orientation request for %d, return %d"
                         + " following a per-app override for %s",
@@ -3094,8 +3085,6 @@
                 // metrics are updated as rotation settings might depend on them
                 mWmService.mDisplayWindowSettings.applySettingsToDisplayLocked(this,
                         /* includeRotationSettings */ false);
-                mDisplayUpdater.onDisplayContentDisplayPropertiesPreChanged(mDisplayId,
-                        mInitialDisplayWidth, mInitialDisplayHeight, newWidth, newHeight);
                 mDisplayRotation.physicalDisplayChanged();
                 mDisplayPolicy.physicalDisplayChanged();
             }
@@ -3130,8 +3119,7 @@
 
             if (physicalDisplayChanged) {
                 mDisplayPolicy.physicalDisplayUpdated();
-                mDisplayUpdater.onDisplayContentDisplayPropertiesPostChanged(currentRotation,
-                        getRotation(), getDisplayAreaInfo());
+                mDisplayUpdater.onDisplayContentDisplayPropertiesPostChanged();
             }
         }
     }
@@ -3895,6 +3883,22 @@
     }
 
     /**
+     * Returns the focused window of the given Activity if the Activity is focused.
+     */
+    WindowState findFocusedWindow(ActivityRecord activityRecord) {
+        final ActivityRecord tmpApp = mFocusedApp;
+        mTmpWindow = null;
+        try {
+            mFocusedApp = activityRecord;
+            // mFindFocusedWindow will populate mTmpWindow with the new focused window when found.
+            activityRecord.forAllWindows(mFindFocusedWindow, true /* traverseTopToBottom */);
+        } finally {
+            mFocusedApp = tmpApp;
+        }
+        return mTmpWindow;
+    }
+
+    /**
      * Update the focused window and make some adjustments if the focus has changed.
      *
      * @param mode Indicates the situation we are in. Possible modes are:
@@ -5077,9 +5081,11 @@
             Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
         }
 
-        // This should be called after the insets have been dispatched to clients and we have
-        // committed finish drawing windows.
-        mInsetsStateController.getImeSourceProvider().checkAndStartShowImePostLayout();
+        if (!android.view.inputmethod.Flags.refactorInsetsController()) {
+            // This should be called after the insets have been dispatched to clients and we have
+            // committed finish drawing windows.
+            mInsetsStateController.getImeSourceProvider().checkAndStartShowImePostLayout();
+        }
 
         mLastHasContent = mTmpApplySurfaceChangesTransactionState.displayHasContent;
         if (!inTransition() && !mDisplayRotation.isRotatingSeamlessly()) {
@@ -6262,7 +6268,7 @@
         // by looping the children, so that we don't miss any root tasks after the children size
         // changed or reordered.
         final ArrayList<Task> rootTasks = new ArrayList<>();
-        forAllRootTasks(rootTask -> {
+        getDefaultTaskDisplayArea().forAllRootTasks(rootTask -> {
             for (int activityType : activityTypes) {
                 // Collect the root tasks that are currently being organized.
                 if (rootTask.mCreatedByOrganizer) {
@@ -6911,6 +6917,10 @@
         /** Whether {@link #mAnimatingRecents} is going to be the top activity. */
         private boolean mRecentsWillBeTop;
 
+        FixedRotationTransitionListener(int displayId) {
+            super(displayId);
+        }
+
         /**
          * If the recents activity has a fixed orientation which is different from the current top
          * activity, it will be rotated before being shown so we avoid a screen rotation animation
@@ -6997,7 +7007,7 @@
             // by finishing the recents animation and moving it to top. That also avoids flickering
             // due to wait for previous activity to be paused if it supports PiP that ignores the
             // effect of resume-while-pausing.
-            if (r == null || r == mAnimatingRecents || r.getDisplayId() != mDisplayId) {
+            if (r == null || r == mAnimatingRecents) {
                 return;
             }
             if (mAnimatingRecents != null && mRecentsWillBeTop) {
@@ -7160,8 +7170,8 @@
         @Override
         public void setImeInputTargetRequestedVisibility(boolean visible) {
             if (android.view.inputmethod.Flags.refactorInsetsController()) {
+                // TODO(b/329229469) we won't have the statsToken in all cases, but should still log
                 try {
-                    // TODO stats token
                     mRemoteInsetsController.setImeInputTargetRequestedVisibility(visible);
                 } catch (RemoteException e) {
                     Slog.w(TAG, "Failed to deliver setImeInputTargetRequestedVisibility", e);
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 80362a4..c3339cd 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -588,7 +588,7 @@
                     gesturesPointerEventCallbacks);
             displayContent.registerPointerEventListener(mSystemGestures);
         }
-        mAppTransitionListener = new WindowManagerInternal.AppTransitionListener() {
+        mAppTransitionListener = new WindowManagerInternal.AppTransitionListener(displayId) {
 
             private Runnable mAppTransitionPending = () -> {
                 StatusBarManagerInternal statusBar = getStatusBarManagerInternal();
diff --git a/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java b/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java
index 63fe94c..e50a089 100644
--- a/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java
@@ -44,6 +44,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.protolog.ProtoLog;
 import com.android.server.UiThread;
+import com.android.window.flags.Flags;
 
 /**
  * Controls camera compatibility treatment that handles orientation mismatch between camera
@@ -69,6 +70,9 @@
     @NonNull
     private final ActivityRefresher mActivityRefresher;
 
+    @Nullable
+    private Task mCameraTask;
+
     @ScreenOrientation
     private int mLastReportedOrientation = SCREEN_ORIENTATION_UNSET;
 
@@ -104,7 +108,7 @@
      * guaranteed to match, the rotation can cause letterboxing.
      *
      * <p>If treatment isn't applicable returns {@link SCREEN_ORIENTATION_UNSPECIFIED}. See {@link
-     * #shouldComputeCameraCompatOrientation} for conditions enabling the treatment.
+     * #isTreatmentEnabledForDisplay} for conditions enabling the treatment.
      */
     @ScreenOrientation
     int getOrientation() {
@@ -136,9 +140,9 @@
         // are aligned when they compute orientation of the preview.
         // This means that even for a landscape-only activity and a device with landscape natural
         // orientation this would return SCREEN_ORIENTATION_PORTRAIT because an assumption that
-        // natural orientation = portrait window = portait camera is the main wrong assumption
+        // natural orientation = portrait window = portrait camera is the main wrong assumption
         // that apps make when they implement camera previews so landscape windows need be
-        // rotated in the orientation oposite to the natural one even if it's portrait.
+        // rotated in the orientation opposite to the natural one even if it's portrait.
         // TODO(b/261475895): Consider allowing more rotations for "sensor" and "user" versions
         // of the portrait and landscape orientation requests.
         final int orientation = (isPortraitActivity && isNaturalDisplayOrientationPortrait)
@@ -296,6 +300,7 @@
     @Override
     public boolean onCameraOpened(@NonNull ActivityRecord cameraActivity,
             @NonNull String cameraId) {
+        mCameraTask = cameraActivity.getTask();
         // Checking whether an activity in fullscreen rather than the task as this camera
         // compat treatment doesn't cover activity embedding.
         if (cameraActivity.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) {
@@ -305,7 +310,7 @@
         }
         // Checking that the whole app is in multi-window mode as we shouldn't show toast
         // for the activity embedding case.
-        if (cameraActivity.getTask().getWindowingMode() == WINDOWING_MODE_MULTI_WINDOW
+        if (mCameraTask != null && mCameraTask.getWindowingMode() == WINDOWING_MODE_MULTI_WINDOW
                 && isTreatmentEnabledForActivity(
                 cameraActivity, /* mustBeFullscreen */ false)) {
             final PackageManager packageManager = mWmService.mContext.getPackageManager();
@@ -343,10 +348,15 @@
 
     @Override
     public boolean onCameraClosed(@NonNull String cameraId) {
-        // Top activity in the same task as the camera activity, or `null` if the task is
-        // closed.
-        final ActivityRecord topActivity = mDisplayContent.topRunningActivity(
-                /* considerKeyguardState= */ true);
+        final ActivityRecord topActivity;
+        if (Flags.cameraCompatFullscreenPickSameTaskActivity()) {
+            topActivity = mCameraTask != null ? mCameraTask.getTopActivity(
+                    /* includeFinishing= */ true, /* includeOverlays= */ false) : null;
+        } else {
+            topActivity = mDisplayContent.topRunningActivity(/* considerKeyguardState= */ true);
+        }
+
+        mCameraTask = null;
         if (topActivity == null) {
             return true;
         }
@@ -368,8 +378,6 @@
                 mDisplayContent.mDisplayId);
         // Checking whether an activity in fullscreen rather than the task as this camera compat
         // treatment doesn't cover activity embedding.
-        // TODO(b/350495350): Consider checking whether this activity is the camera activity, or
-        // whether the top activity has the same task as the one which opened camera.
         if (topActivity.getWindowingMode() != WINDOWING_MODE_FULLSCREEN) {
             return true;
         }
diff --git a/services/core/java/com/android/server/wm/DisplayUpdater.java b/services/core/java/com/android/server/wm/DisplayUpdater.java
deleted file mode 100644
index 918b180..0000000
--- a/services/core/java/com/android/server/wm/DisplayUpdater.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm;
-
-import android.annotation.NonNull;
-import android.os.Message;
-import android.view.Surface;
-import android.window.DisplayAreaInfo;
-
-/**
- * Interface for a helper class that manages updates of DisplayInfo coming from DisplayManager
- */
-interface DisplayUpdater {
-    /**
-     * Reads the latest display parameters from the display manager and returns them in a callback.
-     * If there are pending display updates, it will wait for them to finish first and only then it
-     * will call the callback with the latest display parameters.
-     *
-     * @param callback is called when all pending display updates are finished
-     */
-    void updateDisplayInfo(@NonNull Runnable callback);
-
-    /**
-     * Called when physical display has changed and before DisplayContent has applied new display
-     * properties
-     */
-    default void onDisplayContentDisplayPropertiesPreChanged(int displayId, int initialDisplayWidth,
-            int initialDisplayHeight, int newWidth, int newHeight) {
-    }
-
-    /**
-     * Called after physical display has changed and after DisplayContent applied new display
-     * properties
-     */
-    default void onDisplayContentDisplayPropertiesPostChanged(
-            @Surface.Rotation int previousRotation, @Surface.Rotation int newRotation,
-            @NonNull DisplayAreaInfo newDisplayAreaInfo) {
-    }
-
-    /**
-     * Called with {@code true} when physical display is going to switch. And {@code false} when
-     * the display is turned on or the device goes to sleep.
-     */
-    default void onDisplaySwitching(boolean switching) {
-    }
-
-    /** Returns {@code true} if the transition will control when to turn on the screen. */
-    default boolean waitForTransition(@NonNull Message screenUnBlocker) {
-        return false;
-    }
-}
diff --git a/services/core/java/com/android/server/wm/DisplayWindowPolicyControllerHelper.java b/services/core/java/com/android/server/wm/DisplayWindowPolicyControllerHelper.java
index e0d69b0..4ec318b 100644
--- a/services/core/java/com/android/server/wm/DisplayWindowPolicyControllerHelper.java
+++ b/services/core/java/com/android/server/wm/DisplayWindowPolicyControllerHelper.java
@@ -16,9 +16,12 @@
 
 package com.android.server.wm;
 
+import static android.content.pm.ActivityInfo.FLAG_CAN_DISPLAY_ON_REMOTE_DEVICES;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.WindowConfiguration;
+import android.companion.virtualdevice.flags.Flags;
 import android.content.ComponentName;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
@@ -26,6 +29,7 @@
 import android.os.UserHandle;
 import android.util.ArraySet;
 import android.util.Slog;
+import android.view.Display;
 import android.window.DisplayWindowPolicyController;
 
 import java.io.PrintWriter;
@@ -80,6 +84,9 @@
                 if (hasDisplayCategory(activities.get(i))) {
                     return false;
                 }
+                if (!launchAllowedByDisplayPolicy(activities.get(i))) {
+                    return false;
+                }
             }
             return true;
         }
@@ -95,7 +102,13 @@
         if (mDisplayWindowPolicyController == null) {
             // Missing controller means that this display has no categories for activity launch
             // restriction.
-            return !hasDisplayCategory(activityInfo);
+            if (hasDisplayCategory(activityInfo)) {
+                return false;
+            }
+            if (!launchAllowedByDisplayPolicy(activityInfo)) {
+                return false;
+            }
+            return true;
         }
         return mDisplayWindowPolicyController.canActivityBeLaunched(activityInfo, intent,
             windowingMode, launchingFromDisplayId, isNewTask);
@@ -112,6 +125,24 @@
         return false;
     }
 
+    private boolean launchAllowedByDisplayPolicy(ActivityInfo aInfo) {
+        if (!Flags.enforceRemoteDeviceOptOutOnAllVirtualDisplays()) {
+            return true;
+        }
+        int displayType = mDisplayContent.getDisplay().getType();
+        if (displayType != Display.TYPE_VIRTUAL && displayType != Display.TYPE_WIFI) {
+            return true;
+        }
+        if ((aInfo.flags & FLAG_CAN_DISPLAY_ON_REMOTE_DEVICES) == 0) {
+            Slog.d(TAG,
+                    String.format("Checking activity launch on display %d, activity requires"
+                                    + " android:canDisplayOnRemoteDevices=true",
+                            mDisplayContent.mDisplayId));
+            return false;
+        }
+        return true;
+    }
+
     /**
      * @see DisplayWindowPolicyController#keepActivityOnWindowFlagsChanged(ActivityInfo, int, int)
      */
diff --git a/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java b/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java
index 8bd8098..7135c3b 100644
--- a/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java
+++ b/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java
@@ -44,7 +44,6 @@
 import com.android.modules.utils.TypedXmlPullParser;
 import com.android.modules.utils.TypedXmlSerializer;
 import com.android.server.wm.DisplayWindowSettings.SettingsProvider;
-import com.android.window.flags.Flags;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -55,8 +54,10 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.function.Consumer;
 
 /**
  * Implementation of {@link SettingsProvider} that reads the base settings provided in a display
@@ -143,28 +144,38 @@
      * @see #DATA_DISPLAY_SETTINGS_FILE_PATH
      */
     void setOverrideSettingsForUser(@UserIdInt int userId) {
-        if (!Flags.perUserDisplayWindowSettings()) {
-            return;
-        }
         final AtomicFile settingsFile = getOverrideSettingsFileForUser(userId);
         setOverrideSettingsStorage(new AtomicFileStorage(settingsFile));
     }
 
     /**
      * Removes display override settings that are no longer associated with active displays.
-     * This is necessary because displays can be dynamically added or removed during
-     * the system's lifecycle (e.g., user switch, system server restart).
+     * <p>
+     * This cleanup process is essential due to the dynamic nature of displays, which can
+     * be added or removed during various system events such as user switching or
+     * system server restarts.
      *
-     * @param root The root window container used to obtain the currently active displays.
+     * @param wms  the WindowManagerService instance for retrieving all possible {@link DisplayInfo}
+     *             for the given logical display.
+     * @param root the root window container used to obtain the currently active displays.
      */
-    void removeStaleDisplaySettings(@NonNull RootWindowContainer root) {
-        if (!Flags.perUserDisplayWindowSettings()) {
-            return;
-        }
+    void removeStaleDisplaySettingsLocked(@NonNull WindowManagerService wms,
+            @NonNull RootWindowContainer root) {
         final Set<String> displayIdentifiers = new ArraySet<>();
+        final Consumer<DisplayInfo> addDisplayIdentifier =
+                displayInfo -> displayIdentifiers.add(mOverrideSettings.getIdentifier(displayInfo));
         root.forAllDisplays(dc -> {
-            final String identifier = mOverrideSettings.getIdentifier(dc.getDisplayInfo());
-            displayIdentifiers.add(identifier);
+            // Begin with the current display's information. Note that the display layout of the
+            // current device state might not include this display (e.g., external or virtual
+            // displays), resulting in empty possible display info.
+            addDisplayIdentifier.accept(dc.getDisplayInfo());
+
+            // Then, add all possible display information for this display if available.
+            final List<DisplayInfo> displayInfos = wms.getPossibleDisplayInfoLocked(dc.mDisplayId);
+            final int size = displayInfos.size();
+            for (int i = 0; i < size; i++) {
+                addDisplayIdentifier.accept(displayInfos.get(i));
+            }
         });
         mOverrideSettings.removeStaleDisplaySettings(displayIdentifiers);
     }
@@ -385,12 +396,9 @@
 
     @NonNull
     private static AtomicFile getOverrideSettingsFileForUser(@UserIdInt int userId) {
-        final File directory;
-        if (userId == USER_SYSTEM || !Flags.perUserDisplayWindowSettings()) {
-            directory = Environment.getDataDirectory();
-        } else {
-            directory = Environment.getDataSystemCeDirectory(userId);
-        }
+        final File directory = (userId == USER_SYSTEM)
+                ? Environment.getDataDirectory()
+                : Environment.getDataSystemCeDirectory(userId);
         final File overrideSettingsFile = new File(directory, DATA_DISPLAY_SETTINGS_FILE_PATH);
         return new AtomicFile(overrideSettingsFile, WM_DISPLAY_COMMIT_TAG);
     }
diff --git a/services/core/java/com/android/server/wm/DragDropController.java b/services/core/java/com/android/server/wm/DragDropController.java
index 6abef8b..c849a37 100644
--- a/services/core/java/com/android/server/wm/DragDropController.java
+++ b/services/core/java/com/android/server/wm/DragDropController.java
@@ -16,7 +16,6 @@
 
 package com.android.server.wm;
 
-import static android.content.ClipDescription.EXTRA_HIDE_DRAG_SOURCE_TASK_ID;
 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
 import static android.view.View.DRAG_FLAG_GLOBAL;
 import static android.view.View.DRAG_FLAG_GLOBAL_SAME_APPLICATION;
@@ -228,7 +227,7 @@
                         final Display display = displayContent.getDisplay();
                         touchFocusTransferredFuture = mCallback.get().registerInputChannel(
                                 mDragState, display, mService.mInputManager,
-                                callingWin.mInputChannel);
+                                callingWin.mInputChannelToken);
                     } else {
                         // Skip surface logic for a drag triggered by an AccessibilityAction
                         mDragState.broadcastDragStartedLocked(touchX, touchY);
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index ba74f50..59435b8 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -456,10 +456,6 @@
         }
     }
 
-    InputChannel getInputChannel() {
-        return mInputInterceptor == null ? null : mInputInterceptor.mClientChannel;
-    }
-
     InputWindowHandle getInputWindowHandle() {
         return mInputInterceptor == null ? null : mInputInterceptor.mDragWindowHandle;
     }
diff --git a/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java b/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
index 3123018..63af5c6 100644
--- a/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
+++ b/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
@@ -240,7 +240,7 @@
             }
             r.setVisibility(true);
         }
-        if (r != starting) {
+        if (r != starting && mNotifyClients) {
             mTaskFragment.mTaskSupervisor.startSpecificActivity(r, andResume,
                     true /* checkConfig */);
         }
diff --git a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
index 91c61b1..3a5f9b7 100644
--- a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
@@ -89,6 +89,7 @@
 
     @Override
     void onPostLayout() {
+        boolean wasServerVisible = mServerVisible;
         super.onPostLayout();
 
         if (android.view.inputmethod.Flags.refactorInsetsController()) {
@@ -101,11 +102,35 @@
             // again, so that the control with leash can be eventually dispatched
             if (!mGivenInsetsReady && mServerVisible && !givenInsetsPending) {
                 mGivenInsetsReady = true;
+                ImeTracker.forLogging().onProgress(mStatsToken,
+                        ImeTracker.PHASE_WM_POST_LAYOUT_NOTIFY_CONTROLS_CHANGED);
                 mStateController.notifyControlChanged(mControlTarget);
+                setImeShowing(true);
+            } else if (wasServerVisible && mServerVisible && mGivenInsetsReady
+                    && givenInsetsPending) {
+                // If the server visibility didn't change (still visible), and mGivenInsetsReady
+                // is set, we won't call into notifyControlChanged. Therefore, we can reset the
+                // statsToken, if available.
+                ImeTracker.forLogging().onCancelled(mStatsToken,
+                        ImeTracker.PHASE_WM_POST_LAYOUT_NOTIFY_CONTROLS_CHANGED);
+                mStatsToken = null;
+            } else if (wasServerVisible && !mServerVisible) {
+                setImeShowing(false);
             }
         }
     }
 
+    @Nullable
+    ImeTracker.Token getAndClearStatsToken() {
+        if (android.view.inputmethod.Flags.refactorInsetsController()) {
+            ImeTracker.Token statsToken = mStatsToken;
+            mStatsToken = null;
+            return statsToken;
+        } else {
+            return null;
+        }
+    }
+
     @Override
     protected boolean isLeashReadyForDispatching() {
         if (android.view.inputmethod.Flags.refactorInsetsController()) {
@@ -142,6 +167,14 @@
             }
             control.setSkipAnimationOnce(startingData != null && startingData.hasImeSurface());
         }
+        if (android.view.inputmethod.Flags.refactorInsetsController()) {
+            if (control != null && control.getLeash() != null) {
+                ImeTracker.Token statsToken = getAndClearStatsToken();
+                ImeTracker.forLogging().onProgress(statsToken,
+                        ImeTracker.PHASE_WM_GET_CONTROL_WITH_LEASH);
+                control.setImeStatsToken(statsToken);
+            }
+        }
         return control;
     }
 
@@ -168,7 +201,7 @@
             if (android.view.inputmethod.Flags.refactorInsetsController()) {
                 if (!serverVisible && !mFrozen) {
                     mGivenInsetsReady = false;
-                    updateControlForTarget(mControlTarget, true /* force */);
+                    updateControlForTarget(mControlTarget, true /* force */, null /* statsToken */);
                 }
             }
         }
@@ -214,22 +247,29 @@
     }
 
     @Override
-    void updateControlForTarget(@Nullable InsetsControlTarget target, boolean force) {
+    void updateControlForTarget(@Nullable InsetsControlTarget target, boolean force,
+            @NonNull ImeTracker.Token statsToken) {
         if (target != null && target.getWindow() != null) {
             // ime control target could be a different window.
             // Refer WindowState#getImeControlTarget().
             target = target.getWindow().getImeControlTarget();
         }
-        super.updateControlForTarget(target, force);
+        // TODO(b/329229469) make sure that the statsToken of all callers is non-null (currently
+        //  not the case)
+        super.updateControlForTarget(target, force, statsToken);
         if (Flags.refactorInsetsController()) {
+            // TODO(b/353463205) investigate if we should fail the statsToken, or if it's only
+            //  temporary null.
             if (target != null) {
-                invokeOnImeRequestedChangedListener(target.getWindow());
+                invokeOnImeRequestedChangedListener(target.getWindow(), statsToken);
             }
         }
     }
 
+    // TODO(b/353463205) change statsToken to be NonNull, after the flag is permanently enabled
     @Override
-    protected boolean updateClientVisibility(InsetsControlTarget caller) {
+    protected boolean updateClientVisibility(InsetsControlTarget caller,
+            @Nullable ImeTracker.Token statsToken) {
         InsetsControlTarget controlTarget = getControlTarget();
         if (caller != controlTarget) {
             if (Flags.refactorInsetsController()) {
@@ -240,6 +280,8 @@
                     // its new requested visibility for the IME
                     boolean imeVisible = caller.isRequestedVisible(WindowInsets.Type.ime());
                     if (controlTarget != null) {
+                        ImeTracker.forLogging().onProgress(statsToken,
+                                ImeTracker.PHASE_WM_SET_REMOTE_TARGET_IME_VISIBILITY);
                         controlTarget.setImeInputTargetRequestedVisibility(imeVisible);
                     } else {
                         // In case of a virtual display that cannot show the IME, the
@@ -249,17 +291,24 @@
                         controlTarget = mDisplayContent.getImeHostOrFallback(caller.getWindow());
 
                         if (controlTarget != caller) {
+                            ImeTracker.forLogging().onProgress(statsToken,
+                                    ImeTracker.PHASE_WM_SET_REMOTE_TARGET_IME_VISIBILITY);
                             controlTarget.setImeInputTargetRequestedVisibility(imeVisible);
+                        } else {
+                            ImeTracker.forLogging().onFailed(statsToken,
+                                    ImeTracker.PHASE_WM_SET_REMOTE_TARGET_IME_VISIBILITY);
                         }
                     }
 
                     WindowState windowState = caller.getWindow();
-                    invokeOnImeRequestedChangedListener(windowState);
+                    invokeOnImeRequestedChangedListener(windowState, statsToken);
+                } else {
+                    // TODO(b/353463205) add ImeTracker?
                 }
             }
             return false;
         }
-        boolean changed = super.updateClientVisibility(caller);
+        boolean changed = super.updateClientVisibility(caller, statsToken);
         if (!Flags.refactorInsetsController()) {
             if (changed && caller.isRequestedVisible(mSource.getType())) {
                 reportImeDrawnForOrganizerIfNeeded(caller);
@@ -273,7 +322,11 @@
                 WindowState windowState = caller.getWindow() != null ? caller.getWindow()
                         : ((mDisplayContent.getImeInputTarget() != null)
                                 ? mDisplayContent.getImeInputTarget().getWindowState() : null);
-                invokeOnImeRequestedChangedListener(windowState);
+                invokeOnImeRequestedChangedListener(windowState, statsToken);
+            } else {
+                // TODO(b/329229469) change phase and check cancelled / failed
+                ImeTracker.forLogging().onCancelled(statsToken,
+                        ImeTracker.PHASE_CLIENT_REPORT_REQUESTED_VISIBLE_TYPES);
             }
         }
         return changed;
@@ -288,22 +341,34 @@
                 // know about the new requestedVisibleTypes for the IME.
                 if (imeControlTarget != null) {
                     imeControlTarget.setImeInputTargetRequestedVisibility(
-                            (targetWin.getRequestedVisibleTypes()
-                                    & WindowInsets.Type.ime()) != 0);
+                            (targetWin.getRequestedVisibleTypes() & WindowInsets.Type.ime()) != 0);
                 }
             }
         }
     }
 
-    private void invokeOnImeRequestedChangedListener(WindowState windowState) {
+    // TODO(b/353463205) check callers to see if we can make statsToken @NonNull
+    private void invokeOnImeRequestedChangedListener(WindowState windowState,
+            @Nullable ImeTracker.Token statsToken) {
         final var imeListener = mDisplayContent.mWmService.mOnImeRequestedChangedListener;
         if (imeListener != null) {
             if (windowState != null) {
+                ImeTracker.forLogging().onProgress(statsToken,
+                        ImeTracker.PHASE_WM_POSTING_CHANGED_IME_VISIBILITY);
                 mDisplayContent.mWmService.mH.post(() -> {
+                    ImeTracker.forLogging().onProgress(statsToken,
+                            ImeTracker.PHASE_WM_INVOKING_IME_REQUESTED_LISTENER);
                     imeListener.onImeRequestedChanged(windowState.mClient.asBinder(),
-                            windowState.isRequestedVisible(WindowInsets.Type.ime()));
+                            windowState.isRequestedVisible(WindowInsets.Type.ime()), statsToken);
                 });
+            } else {
+                ImeTracker.forLogging().onFailed(statsToken,
+                        ImeTracker.PHASE_WM_POSTING_CHANGED_IME_VISIBILITY);
             }
+        } else {
+            // TODO(b/353463205) We could combine the upper if's and remove the additional phase.
+            ImeTracker.forLogging().onFailed(statsToken,
+                    ImeTracker.PHASE_WM_DISPATCH_IME_REQUESTED_CHANGED);
         }
     }
 
@@ -399,9 +464,9 @@
             // This can later become ready, so we don't want to cancel the pending request here.
             return;
         }
+        // TODO(b/329229469) check if this is still triggered, as we don't go into STATE_SHOW_IME
+        //  (DefaultImeVisibilityApplier)
         if (android.view.inputmethod.Flags.refactorInsetsController()) {
-            // Clear token here so we don't report an error in abortShowImePostLayout().
-            abortShowImePostLayout();
             // The IME is drawn, so call into {@link WindowState#notifyInsetsControlChanged}
             // if we have a leash
             if (mControl != null && mControl.getLeash() != null
@@ -517,6 +582,33 @@
     }
 
     /**
+     * Sets the statsToken before the IMS was shown/hidden.
+     * @param visible {@code true} to make it visible, false to hide it.
+     * @param statsToken the token tracking the current IME request.
+     */
+    void receiveImeStatsToken(boolean visible,
+            @NonNull ImeTracker.Token statsToken) {
+        if (!android.view.inputmethod.Flags.refactorInsetsController()) {
+            return;
+        }
+
+        if (mStatsToken != null) {
+            // We have an ongoing show request will be cancelled by the newly received show
+            // request (cancelling the initial show) or hide request (aborting the initial show).
+            logIsScheduledAndReadyToShowIme(!visible /* aborted */);
+        }
+        if (visible) {
+            ImeTracker.forLogging().onCancelled(
+                    mStatsToken, ImeTracker.PHASE_WM_ABORT_SHOW_IME_POST_LAYOUT);
+            mStatsToken = statsToken;
+        } else {
+            ImeTracker.forLogging().onFailed(
+                    mStatsToken, ImeTracker.PHASE_WM_ABORT_SHOW_IME_POST_LAYOUT);
+            mStatsToken = null;
+        }
+    }
+
+    /**
      * Logs the current state that can be checked by {@link #isScheduledAndReadyToShowIme}.
      *
      * @param aborted whether the scheduled show IME request was aborted or cancelled.
diff --git a/services/core/java/com/android/server/wm/ImmediateDisplayUpdater.java b/services/core/java/com/android/server/wm/ImmediateDisplayUpdater.java
deleted file mode 100644
index 4af9013..0000000
--- a/services/core/java/com/android/server/wm/ImmediateDisplayUpdater.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm;
-
-import android.annotation.NonNull;
-import android.view.DisplayInfo;
-import android.window.DisplayAreaInfo;
-
-/**
- * DisplayUpdater that immediately applies new DisplayInfo properties
- */
-public class ImmediateDisplayUpdater implements DisplayUpdater {
-
-    private final DisplayContent mDisplayContent;
-    private final DisplayInfo mDisplayInfo = new DisplayInfo();
-
-    public ImmediateDisplayUpdater(@NonNull DisplayContent displayContent) {
-        mDisplayContent = displayContent;
-        mDisplayInfo.copyFrom(mDisplayContent.getDisplayInfo());
-    }
-
-    @Override
-    public void updateDisplayInfo(Runnable callback) {
-        mDisplayContent.mWmService.mDisplayManagerInternal.getNonOverrideDisplayInfo(
-                mDisplayContent.mDisplayId, mDisplayInfo);
-        mDisplayContent.onDisplayInfoUpdated(mDisplayInfo);
-        callback.run();
-    }
-
-    @Override
-    public void onDisplayContentDisplayPropertiesPreChanged(int displayId, int initialDisplayWidth,
-            int initialDisplayHeight, int newWidth, int newHeight) {
-        mDisplayContent.mDisplaySwitchTransitionLauncher.requestDisplaySwitchTransitionIfNeeded(
-                displayId, initialDisplayWidth, initialDisplayHeight, newWidth, newHeight);
-    }
-
-    @Override
-    public void onDisplayContentDisplayPropertiesPostChanged(int previousRotation, int newRotation,
-            DisplayAreaInfo newDisplayAreaInfo) {
-        mDisplayContent.mDisplaySwitchTransitionLauncher.onDisplayUpdated(previousRotation,
-                newRotation,
-                newDisplayAreaInfo);
-    }
-}
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index b496a65..b8869f1 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -439,8 +439,7 @@
                         final InputMethodManagerInternal inputMethodManagerInternal =
                                 LocalServices.getService(InputMethodManagerInternal.class);
                         if (inputMethodManagerInternal != null) {
-                            // TODO(b/308479256): Check if hiding "all" IMEs is OK or not.
-                            inputMethodManagerInternal.hideAllInputMethods(
+                            inputMethodManagerInternal.hideInputMethod(
                                     SoftInputShowHideReason.HIDE_RECENTS_ANIMATION,
                                     mDisplayContent.getDisplayId());
                         }
diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java
index 6288a42..62bef74 100644
--- a/services/core/java/com/android/server/wm/InsetsPolicy.java
+++ b/services/core/java/com/android/server/wm/InsetsPolicy.java
@@ -47,6 +47,7 @@
 import android.view.WindowInsetsAnimation;
 import android.view.WindowInsetsAnimation.Bounds;
 import android.view.WindowManager;
+import android.view.inputmethod.ImeTracker;
 import android.view.inputmethod.InputMethodManager;
 
 import com.android.internal.R;
@@ -433,8 +434,9 @@
         return originalState;
     }
 
-    void onRequestedVisibleTypesChanged(InsetsControlTarget caller) {
-        mStateController.onRequestedVisibleTypesChanged(caller);
+    void onRequestedVisibleTypesChanged(InsetsControlTarget caller,
+            @Nullable ImeTracker.Token statsToken) {
+        mStateController.onRequestedVisibleTypesChanged(caller, statsToken);
         checkAbortTransient(caller);
         updateBarControlTarget(mFocusedWin);
     }
@@ -803,7 +805,8 @@
         }
 
         @Override
-        public void updateRequestedVisibleTypes(int types) { }
+        public void updateRequestedVisibleTypes(int types, @Nullable ImeTracker.Token statsToken) {
+        }
 
         @Override
         public boolean hasAnimationCallbacks() {
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index 33dea54..f5c92f6 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -46,6 +46,7 @@
 import android.view.SurfaceControl;
 import android.view.SurfaceControl.Transaction;
 import android.view.WindowInsets;
+import android.view.inputmethod.ImeTracker;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.protolog.ProtoLog;
@@ -498,7 +499,8 @@
         );
     }
 
-    void updateControlForTarget(@Nullable InsetsControlTarget target, boolean force) {
+    void updateControlForTarget(@Nullable InsetsControlTarget target, boolean force,
+            @Nullable ImeTracker.Token statsToken) {
         if (mSeamlessRotating) {
             // We are un-rotating the window against the display rotation. We don't want the target
             // to control the window for now.
@@ -570,7 +572,8 @@
         mSeamlessRotating = false;
     }
 
-    boolean updateClientVisibility(InsetsControlTarget caller) {
+    boolean updateClientVisibility(InsetsControlTarget caller,
+            @Nullable ImeTracker.Token statsToken) {
         final boolean requestedVisible = caller.isRequestedVisible(mSource.getType());
         if (caller != mControlTarget || requestedVisible == mClientVisible) {
             return false;
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index dcadb0f..9c2a8de 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -40,6 +40,7 @@
 import android.view.InsetsState;
 import android.view.WindowInsets;
 import android.view.WindowInsets.Type.InsetsType;
+import android.view.inputmethod.ImeTracker;
 
 import com.android.internal.protolog.ProtoLog;
 import com.android.server.inputmethod.InputMethodManagerInternal;
@@ -216,10 +217,14 @@
         }
     }
 
-    void onRequestedVisibleTypesChanged(InsetsControlTarget caller) {
+    void onRequestedVisibleTypesChanged(InsetsControlTarget caller,
+            @Nullable ImeTracker.Token statsToken) {
         boolean changed = false;
         for (int i = mProviders.size() - 1; i >= 0; i--) {
-            changed |= mProviders.valueAt(i).updateClientVisibility(caller);
+            final InsetsSourceProvider provider = mProviders.valueAt(i);
+            final boolean isImeProvider = provider.getSource().getType() == WindowInsets.Type.ime();
+            changed |= provider.updateClientVisibility(caller,
+                    isImeProvider ? statsToken : null);
         }
         if (!android.view.inputmethod.Flags.refactorInsetsController()) {
             if (changed) {
@@ -310,7 +315,9 @@
             // aborted.
             provider.updateFakeControlTarget(target);
         } else {
-            provider.updateControlForTarget(target, false /* force */);
+            // TODO(b/329229469) if the IME controlTarget changes, any pending requests should fail
+            provider.updateControlForTarget(target, false /* force */,
+                    null /* TODO(b/329229469) check if needed here */);
 
             // Get control target again in case the provider didn't accept the one we passed to it.
             target = provider.getControlTarget();
@@ -394,7 +401,9 @@
             // to the clients, so that the clients can change the current visibilities to the
             // requested visibilities with animations.
             for (int i = newControlTargets.size() - 1; i >= 0; i--) {
-                onRequestedVisibleTypesChanged(newControlTargets.valueAt(i));
+                // TODO(b/353463205) the statsToken shouldn't be null as it is used later in the
+                //  IME provider. Check if we have to create a new request here
+                onRequestedVisibleTypesChanged(newControlTargets.valueAt(i), null /* statsToken */);
             }
             newControlTargets.clear();
             if (!android.view.inputmethod.Flags.refactorInsetsController()) {
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index 872b4e1..7a0fd3e 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -60,7 +60,6 @@
 import com.android.internal.policy.IKeyguardDismissCallback;
 import com.android.server.inputmethod.InputMethodManagerInternal;
 import com.android.server.policy.WindowManagerPolicy;
-import com.android.window.flags.Flags;
 
 import java.io.PrintWriter;
 
@@ -198,14 +197,6 @@
             setWakeTransitionReady();
             return;
         }
-        EventLogTags.writeWmSetKeyguardShown(
-                displayId,
-                keyguardShowing ? 1 : 0,
-                aodShowing ? 1 : 0,
-                state.mKeyguardGoingAway ? 1 : 0,
-                state.mOccluded ? 1 : 0,
-                "setKeyguardShown");
-
         // Update the task snapshot if the screen will not be turned off. To make sure that the
         // unlocking animation can animate consistent content. The conditions are:
         // - Either AOD or keyguard changes to be showing. So if the states change individually,
@@ -224,6 +215,7 @@
 
         state.mKeyguardShowing = keyguardShowing;
         state.mAodShowing = aodShowing;
+        state.writeEventLog("setKeyguardShown");
 
         if (keyguardChanged) {
             // Irrelevant to AOD.
@@ -232,19 +224,13 @@
                 state.mDismissalRequested = false;
             }
             if (goingAwayRemoved
-                    || (Flags.keyguardAppearTransition() && keyguardShowing
-                            && !Display.isOffState(dc.getDisplayInfo().state))) {
+                    || (keyguardShowing && !Display.isOffState(dc.getDisplayInfo().state))) {
                 // Keyguard decided to show or stopped going away. Send a transition to animate back
                 // to the locked state before holding the sleep token again
-                final DisplayContent transitionDc = Flags.keyguardAppearTransition()
-                        ? dc
-                        : mRootWindowContainer.getDefaultDisplay();
-                transitionDc.requestTransitionAndLegacyPrepare(
+                dc.requestTransitionAndLegacyPrepare(
                         TRANSIT_TO_FRONT, TRANSIT_FLAG_KEYGUARD_APPEARING);
-                if (Flags.keyguardAppearTransition()) {
-                    dc.mWallpaperController.adjustWallpaperWindows();
-                }
-                transitionDc.executeAppTransition();
+                dc.mWallpaperController.adjustWallpaperWindows();
+                dc.executeAppTransition();
             }
         }
 
@@ -284,13 +270,7 @@
         mService.deferWindowLayout();
         state.mKeyguardGoingAway = true;
         try {
-            EventLogTags.writeWmSetKeyguardShown(
-                    displayId,
-                    state.mKeyguardShowing ? 1 : 0,
-                    state.mAodShowing ? 1 : 0,
-                    1 /* keyguardGoingAway */,
-                    state.mOccluded ? 1 : 0,
-                    "keyguardGoingAway");
+            state.writeEventLog("keyguardGoingAway");
             final int transitFlags = convertTransitFlags(flags);
             final DisplayContent dc = mRootWindowContainer.getDefaultDisplay();
             dc.prepareAppTransition(TRANSIT_KEYGUARD_GOING_AWAY, transitFlags);
@@ -436,8 +416,9 @@
         }
 
         final TransitionController tc = mRootWindowContainer.mTransitionController;
+        final KeyguardDisplayState state = getDisplayState(displayId);
 
-        final boolean occluded = getDisplayState(displayId).mOccluded;
+        final boolean occluded = state.mOccluded;
         final boolean performTransition = isKeyguardLocked(displayId);
         final boolean executeTransition = performTransition && !tc.isCollecting();
 
@@ -481,7 +462,7 @@
     /**
      * Called when keyguard going away state changed.
      */
-    private void handleKeyguardGoingAwayChanged(DisplayContent dc) {
+    private void handleDismissInsecureKeyguard(DisplayContent dc) {
         mService.deferWindowLayout();
         try {
             dc.prepareAppTransition(TRANSIT_KEYGUARD_GOING_AWAY, 0 /* transitFlags */);
@@ -646,6 +627,16 @@
             mSleepTokenAcquirer.release(mDisplayId);
         }
 
+        void writeEventLog(String reason) {
+            EventLogTags.writeWmSetKeyguardShown(
+                    mDisplayId,
+                    mKeyguardShowing ? 1 : 0,
+                    mAodShowing ? 1 : 0,
+                    mKeyguardGoingAway ? 1 : 0,
+                    mOccluded ? 1 : 0,
+                    reason);
+        }
+
         /**
          * Updates keyguard status if the top task could be visible. The top task may occlude
          * keyguard, request to dismiss keyguard or make insecure keyguard go away based on its
@@ -715,23 +706,16 @@
             }
 
             boolean hasChange = false;
-            if (lastOccluded != mOccluded) {
-                if (mDisplayId == DEFAULT_DISPLAY) {
-                    EventLogTags.writeWmSetKeyguardShown(
-                            mDisplayId,
-                            mKeyguardShowing ? 1 : 0,
-                            mAodShowing ? 1 : 0,
-                            mKeyguardGoingAway ? 1 : 0,
-                            mOccluded ? 1 : 0,
-                            "updateVisibility");
-                }
+            if (!lastKeyguardGoingAway && mKeyguardGoingAway) {
+                writeEventLog("dismissIfInsecure");
+                controller.handleDismissInsecureKeyguard(display);
+                hasChange = true;
+            } else if (lastOccluded != mOccluded) {
                 controller.handleOccludedChanged(mDisplayId, mTopOccludesActivity);
                 hasChange = true;
-            } else if (!lastKeyguardGoingAway && mKeyguardGoingAway) {
-                controller.handleKeyguardGoingAwayChanged(display);
-                hasChange = true;
             }
-            // Collect the participates for shell transition, so that transition won't happen too
+
+            // Collect the participants for shell transition, so that transition won't happen too
             // early since the transition was set ready.
             if (hasChange && top != null && (mOccluded || mKeyguardGoingAway)) {
                 display.mTransitionController.collect(top);
diff --git a/services/core/java/com/android/server/wm/LegacyDimmer.java b/services/core/java/com/android/server/wm/LegacyDimmer.java
deleted file mode 100644
index 3265e60..0000000
--- a/services/core/java/com/android/server/wm/LegacyDimmer.java
+++ /dev/null
@@ -1,348 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm;
-
-import static com.android.server.wm.AlphaAnimationSpecProto.DURATION_MS;
-import static com.android.server.wm.AlphaAnimationSpecProto.FROM;
-import static com.android.server.wm.AlphaAnimationSpecProto.TO;
-import static com.android.server.wm.AnimationSpecProto.ALPHA;
-import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_DIMMER;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-
-import android.graphics.Rect;
-import android.util.Log;
-import android.util.proto.ProtoOutputStream;
-import android.view.Surface;
-import android.view.SurfaceControl;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-import java.io.PrintWriter;
-
-public class LegacyDimmer extends Dimmer {
-    private static final String TAG = TAG_WITH_CLASS_NAME ? "Dimmer" : TAG_WM;
-    // This is in milliseconds.
-    private static final int DEFAULT_DIM_ANIM_DURATION = 200;
-    DimState mDimState;
-    private WindowContainer mLastRequestedDimContainer;
-    private final SurfaceAnimatorStarter mSurfaceAnimatorStarter;
-
-    private class DimAnimatable implements SurfaceAnimator.Animatable {
-        private SurfaceControl mDimLayer;
-
-        private DimAnimatable(SurfaceControl dimLayer) {
-            mDimLayer = dimLayer;
-        }
-
-        @Override
-        public SurfaceControl.Transaction getSyncTransaction() {
-            return mHost.getSyncTransaction();
-        }
-
-        @Override
-        public SurfaceControl.Transaction getPendingTransaction() {
-            return mHost.getPendingTransaction();
-        }
-
-        @Override
-        public void commitPendingTransaction() {
-            mHost.commitPendingTransaction();
-        }
-
-        @Override
-        public void onAnimationLeashCreated(SurfaceControl.Transaction t, SurfaceControl leash) {
-        }
-
-        @Override
-        public void onAnimationLeashLost(SurfaceControl.Transaction t) {
-        }
-
-        @Override
-        public SurfaceControl.Builder makeAnimationLeash() {
-            return mHost.makeAnimationLeash();
-        }
-
-        @Override
-        public SurfaceControl getAnimationLeashParent() {
-            return mHost.getSurfaceControl();
-        }
-
-        @Override
-        public SurfaceControl getSurfaceControl() {
-            return mDimLayer;
-        }
-
-        @Override
-        public SurfaceControl getParentSurfaceControl() {
-            return mHost.getSurfaceControl();
-        }
-
-        @Override
-        public int getSurfaceWidth() {
-            // This will determine the size of the leash created. This should be the size of the
-            // host and not the dim layer since the dim layer may get bigger during animation. If
-            // that occurs, the leash size cannot change so we need to ensure the leash is big
-            // enough that the dim layer can grow.
-            // This works because the mHost will be a Task which has the display bounds.
-            return mHost.getSurfaceWidth();
-        }
-
-        @Override
-        public int getSurfaceHeight() {
-            // See getSurfaceWidth() above for explanation.
-            return mHost.getSurfaceHeight();
-        }
-
-        void removeSurface() {
-            if (mDimLayer != null && mDimLayer.isValid()) {
-                getSyncTransaction().remove(mDimLayer);
-            }
-            mDimLayer = null;
-        }
-    }
-
-    @VisibleForTesting
-    class DimState {
-        /**
-         * The layer where property changes should be invoked on.
-         */
-        SurfaceControl mDimLayer;
-        boolean mDimming;
-        boolean mIsVisible;
-
-        // TODO(b/64816140): Remove after confirming dimmer layer always matches its container.
-        final Rect mDimBounds = new Rect();
-
-        /**
-         * Determines whether the dim layer should animate before destroying.
-         */
-        boolean mAnimateExit = true;
-
-        /**
-         * Used for Dims not associated with a WindowContainer.
-         * See {@link Dimmer#adjustRelativeLayer(WindowContainer, int)} for details on Dim
-         * lifecycle.
-         */
-        boolean mDontReset;
-        SurfaceAnimator mSurfaceAnimator;
-
-        DimState(SurfaceControl dimLayer) {
-            mDimLayer = dimLayer;
-            mDimming = true;
-            final DimAnimatable dimAnimatable = new DimAnimatable(dimLayer);
-            mSurfaceAnimator = new SurfaceAnimator(dimAnimatable, (type, anim) -> {
-                if (!mDimming) {
-                    dimAnimatable.removeSurface();
-                }
-            }, mHost.mWmService);
-        }
-    }
-
-    @VisibleForTesting
-    interface SurfaceAnimatorStarter {
-        void startAnimation(SurfaceAnimator surfaceAnimator, SurfaceControl.Transaction t,
-                AnimationAdapter anim, boolean hidden, @SurfaceAnimator.AnimationType int type);
-    }
-
-    protected LegacyDimmer(WindowContainer host) {
-        this(host, SurfaceAnimator::startAnimation);
-    }
-
-    LegacyDimmer(WindowContainer host, SurfaceAnimatorStarter surfaceAnimatorStarter) {
-        super(host);
-        mSurfaceAnimatorStarter = surfaceAnimatorStarter;
-    }
-
-    private DimState obtainDimState(WindowContainer container) {
-        if (mDimState == null) {
-            try {
-                final SurfaceControl ctl = makeDimLayer();
-                mDimState = new DimState(ctl);
-            } catch (Surface.OutOfResourcesException e) {
-                Log.w(TAG, "OutOfResourcesException creating dim surface");
-            }
-        }
-
-        mLastRequestedDimContainer = container;
-        return mDimState;
-    }
-
-    private SurfaceControl makeDimLayer() {
-        return mHost.makeChildSurface(null)
-                .setParent(mHost.getSurfaceControl())
-                .setColorLayer()
-                .setName("Dim Layer for - " + mHost.getName())
-                .setCallsite("Dimmer.makeDimLayer")
-                .build();
-    }
-
-    @Override
-    SurfaceControl getDimLayer() {
-        return mDimState != null ? mDimState.mDimLayer : null;
-    }
-
-    @Override
-    void resetDimStates() {
-        if (mDimState == null) {
-            return;
-        }
-        if (!mDimState.mDontReset) {
-            mDimState.mDimming = false;
-        }
-    }
-
-    @Override
-    Rect getDimBounds() {
-        return mDimState != null ? mDimState.mDimBounds : null;
-    }
-
-    @Override
-    void dontAnimateExit() {
-        if (mDimState != null) {
-            mDimState.mAnimateExit = false;
-        }
-    }
-
-    @Override
-    protected void adjustAppearance(WindowContainer container, float alpha, int blurRadius) {
-        final DimState d = obtainDimState(container);
-        if (d == null) {
-            return;
-        }
-
-        // The dim method is called from WindowState.prepareSurfaces(), which is always called
-        // in the correct Z from lowest Z to highest. This ensures that the dim layer is always
-        // relative to the highest Z layer with a dim.
-        SurfaceControl.Transaction t = mHost.getPendingTransaction();
-        t.setAlpha(d.mDimLayer, alpha);
-        t.setBackgroundBlurRadius(d.mDimLayer, blurRadius);
-        d.mDimming = true;
-    }
-
-    @Override
-    protected void adjustRelativeLayer(WindowContainer container, int relativeLayer) {
-        final DimState d = mDimState;
-        if (d != null) {
-            SurfaceControl.Transaction t = mHost.getPendingTransaction();
-            t.setRelativeLayer(d.mDimLayer, container.getSurfaceControl(), relativeLayer);
-        }
-    }
-
-    @Override
-    boolean updateDims(SurfaceControl.Transaction t) {
-        if (mDimState == null) {
-            return false;
-        }
-
-        if (!mDimState.mDimming) {
-            if (!mDimState.mAnimateExit) {
-                if (mDimState.mDimLayer.isValid()) {
-                    t.remove(mDimState.mDimLayer);
-                }
-            } else {
-                startDimExit(mLastRequestedDimContainer,
-                        mDimState.mSurfaceAnimator, t);
-            }
-            mDimState = null;
-            return false;
-        } else {
-            final Rect bounds = mDimState.mDimBounds;
-            // TODO: Once we use geometry from hierarchy this falls away.
-            t.setPosition(mDimState.mDimLayer, bounds.left, bounds.top);
-            t.setWindowCrop(mDimState.mDimLayer, bounds.width(), bounds.height());
-            if (!mDimState.mIsVisible) {
-                mDimState.mIsVisible = true;
-                t.show(mDimState.mDimLayer);
-                // Skip enter animation while starting window is on top of its activity
-                final WindowState ws = mLastRequestedDimContainer.asWindowState();
-                if (ws == null || ws.mActivityRecord == null
-                        || ws.mActivityRecord.mStartingData == null) {
-                    startDimEnter(mLastRequestedDimContainer,
-                            mDimState.mSurfaceAnimator, t);
-                }
-            }
-            return true;
-        }
-    }
-
-    private long getDimDuration(WindowContainer container) {
-        // Use the same duration as the animation on the WindowContainer
-        AnimationAdapter animationAdapter = container.mSurfaceAnimator.getAnimation();
-        final float durationScale = container.mWmService.getTransitionAnimationScaleLocked();
-        return animationAdapter == null ? (long) (DEFAULT_DIM_ANIM_DURATION * durationScale)
-                : animationAdapter.getDurationHint();
-    }
-
-    private void startDimEnter(WindowContainer container, SurfaceAnimator animator,
-            SurfaceControl.Transaction t) {
-        startAnim(container, animator, t, 0 /* startAlpha */, 1 /* endAlpha */);
-    }
-
-    private void startDimExit(WindowContainer container, SurfaceAnimator animator,
-            SurfaceControl.Transaction t) {
-        startAnim(container, animator, t, 1 /* startAlpha */, 0 /* endAlpha */);
-    }
-
-    private void startAnim(WindowContainer container, SurfaceAnimator animator,
-            SurfaceControl.Transaction t, float startAlpha, float endAlpha) {
-        mSurfaceAnimatorStarter.startAnimation(animator, t, new LocalAnimationAdapter(
-                        new AlphaAnimationSpec(startAlpha, endAlpha, getDimDuration(container)),
-                        mHost.mWmService.mSurfaceAnimationRunner), false /* hidden */,
-                ANIMATION_TYPE_DIMMER);
-    }
-
-    private static class AlphaAnimationSpec implements LocalAnimationAdapter.AnimationSpec {
-        private final long mDuration;
-        private final float mFromAlpha;
-        private final float mToAlpha;
-
-        AlphaAnimationSpec(float fromAlpha, float toAlpha, long duration) {
-            mFromAlpha = fromAlpha;
-            mToAlpha = toAlpha;
-            mDuration = duration;
-        }
-
-        @Override
-        public long getDuration() {
-            return mDuration;
-        }
-
-        @Override
-        public void apply(SurfaceControl.Transaction t, SurfaceControl sc, long currentPlayTime) {
-            final float fraction = getFraction(currentPlayTime);
-            final float alpha = fraction * (mToAlpha - mFromAlpha) + mFromAlpha;
-            t.setAlpha(sc, alpha);
-        }
-
-        @Override
-        public void dump(PrintWriter pw, String prefix) {
-            pw.print(prefix); pw.print("from="); pw.print(mFromAlpha);
-            pw.print(" to="); pw.print(mToAlpha);
-            pw.print(" duration="); pw.println(mDuration);
-        }
-
-        @Override
-        public void dumpDebugInner(ProtoOutputStream proto) {
-            final long token = proto.start(ALPHA);
-            proto.write(FROM, mFromAlpha);
-            proto.write(TO, mToAlpha);
-            proto.write(DURATION_MS, mDuration);
-            proto.end(token);
-        }
-    }
-}
diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java
index be8e806..eb8a637 100644
--- a/services/core/java/com/android/server/wm/LetterboxUiController.java
+++ b/services/core/java/com/android/server/wm/LetterboxUiController.java
@@ -16,37 +16,16 @@
 
 package com.android.server.wm;
 
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
 
-import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__LETTERBOX_POSITION__BOTTOM;
-import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__LETTERBOX_POSITION__CENTER;
-import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__LETTERBOX_POSITION__LEFT;
-import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__LETTERBOX_POSITION__RIGHT;
-import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__LETTERBOX_POSITION__TOP;
-import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__LETTERBOX_POSITION__UNKNOWN_POSITION;
-import static com.android.internal.util.FrameworkStatsLog.LETTERBOX_POSITION_CHANGED__POSITION_CHANGE__BOTTOM_TO_CENTER;
-import static com.android.internal.util.FrameworkStatsLog.LETTERBOX_POSITION_CHANGED__POSITION_CHANGE__CENTER_TO_BOTTOM;
-import static com.android.internal.util.FrameworkStatsLog.LETTERBOX_POSITION_CHANGED__POSITION_CHANGE__CENTER_TO_LEFT;
-import static com.android.internal.util.FrameworkStatsLog.LETTERBOX_POSITION_CHANGED__POSITION_CHANGE__CENTER_TO_RIGHT;
-import static com.android.internal.util.FrameworkStatsLog.LETTERBOX_POSITION_CHANGED__POSITION_CHANGE__CENTER_TO_TOP;
-import static com.android.internal.util.FrameworkStatsLog.LETTERBOX_POSITION_CHANGED__POSITION_CHANGE__LEFT_TO_CENTER;
-import static com.android.internal.util.FrameworkStatsLog.LETTERBOX_POSITION_CHANGED__POSITION_CHANGE__RIGHT_TO_CENTER;
-import static com.android.internal.util.FrameworkStatsLog.LETTERBOX_POSITION_CHANGED__POSITION_CHANGE__TOP_TO_CENTER;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.AppCompatConfiguration.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND;
 import static com.android.server.wm.AppCompatConfiguration.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING;
 import static com.android.server.wm.AppCompatConfiguration.LETTERBOX_BACKGROUND_SOLID_COLOR;
 import static com.android.server.wm.AppCompatConfiguration.LETTERBOX_BACKGROUND_WALLPAPER;
-import static com.android.server.wm.AppCompatConfiguration.LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_CENTER;
-import static com.android.server.wm.AppCompatConfiguration.LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_LEFT;
-import static com.android.server.wm.AppCompatConfiguration.LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_RIGHT;
-import static com.android.server.wm.AppCompatConfiguration.LETTERBOX_VERTICAL_REACHABILITY_POSITION_BOTTOM;
-import static com.android.server.wm.AppCompatConfiguration.LETTERBOX_VERTICAL_REACHABILITY_POSITION_CENTER;
-import static com.android.server.wm.AppCompatConfiguration.LETTERBOX_VERTICAL_REACHABILITY_POSITION_TOP;
 import static com.android.server.wm.AppCompatConfiguration.letterboxBackgroundTypeToString;
 
 import android.annotation.NonNull;
@@ -68,7 +47,6 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.statusbar.LetterboxDetails;
 import com.android.server.wm.AppCompatConfiguration.LetterboxBackgroundType;
-import com.android.window.flags.Flags;
 
 import java.io.PrintWriter;
 
@@ -92,8 +70,6 @@
 
     private boolean mLastShouldShowLetterboxUi;
 
-    private boolean mDoubleTapEvent;
-
     LetterboxUiController(WindowManagerService wmService, ActivityRecord activityRecord) {
         mAppCompatConfiguration = wmService.mAppCompatConfiguration;
         // Given activityRecord may not be fully constructed since LetterboxUiController
@@ -116,83 +92,6 @@
         }
     }
 
-    /**
-     * Whether sending compat fake focus for split screen resumed activities is enabled. Needed
-     * because some game engines wait to get focus before drawing the content of the app which isn't
-     * guaranteed by default in multi-window modes.
-     *
-     * <p>This treatment is enabled when the following conditions are met:
-     * <ul>
-     *     <li>Flag gating the treatment is enabled
-     *     <li>Component property is NOT set to false
-     *     <li>Component property is set to true or per-app override is enabled
-     * </ul>
-     */
-    boolean shouldSendFakeFocus() {
-        return getAppCompatOverrides().shouldSendFakeFocus();
-    }
-
-    /**
-     * Whether we should apply the force resize per-app override. When this override is applied it
-     * forces the packages it is applied to to be resizable. It won't change whether the app can be
-     * put into multi-windowing mode, but allow the app to resize without going into size-compat
-     * mode when the window container resizes, such as display size change or screen rotation.
-     *
-     * <p>This method returns {@code true} when the following conditions are met:
-     * <ul>
-     *     <li>Opt-out component property isn't enabled
-     *     <li>Per-app override is enabled
-     * </ul>
-     */
-    boolean shouldOverrideForceResizeApp() {
-        return getAppCompatOverrides().shouldOverrideForceResizeApp();
-    }
-
-    /**
-     * Whether we should apply the force non resize per-app override. When this override is applied
-     * it forces the packages it is applied to to be non-resizable.
-     *
-     * <p>This method returns {@code true} when the following conditions are met:
-     * <ul>
-     *     <li>Opt-out component property isn't enabled
-     *     <li>Per-app override is enabled
-     * </ul>
-     */
-    boolean shouldOverrideForceNonResizeApp() {
-        return getAppCompatOverrides().shouldOverrideForceNonResizeApp();
-    }
-
-    /**
-     * Sets whether an activity is relaunching after the app has called {@link
-     * android.app.Activity#setRequestedOrientation}.
-     */
-    void setRelaunchingAfterRequestedOrientationChanged(boolean isRelaunching) {
-        getAppCompatOverrides().getAppCompatOrientationOverrides()
-                .setRelaunchingAfterRequestedOrientationChanged(isRelaunching);
-    }
-
-
-    boolean isOverrideRespectRequestedOrientationEnabled() {
-        return getAppCompatOverrides().isOverrideRespectRequestedOrientationEnabled();
-    }
-
-    /**
-     * Whether should fix display orientation to landscape natural orientation when a task is
-     * fullscreen and the display is ignoring orientation requests.
-     *
-     * <p>This treatment is enabled when the following conditions are met:
-     * <ul>
-     *     <li>Opt-out component property isn't enabled
-     *     <li>Opt-in per-app override is enabled
-     *     <li>Task is in fullscreen.
-     *     <li>{@link DisplayContent#getIgnoreOrientationRequest} is enabled
-     *     <li>Natural orientation of the display is landscape.
-     * </ul>
-     */
-    boolean shouldUseDisplayLandscapeNaturalOrientation() {
-        return getAppCompatOverrides().shouldUseDisplayLandscapeNaturalOrientation();
-    }
-
     boolean hasWallpaperBackgroundForLetterbox() {
         return mShowWallpaperForLetterboxBackground;
     }
@@ -308,7 +207,8 @@
                     .getTransparentPolicy().isRunning()
                     ? mActivityRecord.getBounds() : w.getFrame();
             mLetterbox.layout(spaceToFill, innerFrame, mTmpPoint);
-            if (mDoubleTapEvent) {
+            if (mActivityRecord.mAppCompatController
+                    .getAppCompatReachabilityOverrides().isDoubleTapEvent()) {
                 // We need to notify Shell that letterbox position has changed.
                 mActivityRecord.getTask().dispatchTaskInfoChangedIfNeeded(true /* force */);
             }
@@ -317,12 +217,6 @@
         }
     }
 
-    boolean isFromDoubleTap() {
-        final boolean isFromDoubleTap = mDoubleTapEvent;
-        mDoubleTapEvent = false;
-        return isFromDoubleTap;
-    }
-
     SurfaceControl getLetterboxParentSurface() {
         if (mActivityRecord.isInLetterboxAnimation()) {
             return mActivityRecord.getTask().getSurfaceControl();
@@ -349,314 +243,35 @@
                 && mActivityRecord.fillsParent();
     }
 
-    // Check if we are in the given pose and in fullscreen mode.
-    // Note that we check the task rather than the parent as with ActivityEmbedding the parent might
-    // be a TaskFragment, and its windowing mode is always MULTI_WINDOW, even if the task is
-    // actually fullscreen. If display is still in transition e.g. unfolding, don't return true
-    // for HALF_FOLDED state or app will flicker.
-    boolean isDisplayFullScreenAndInPosture(boolean isTabletop) {
-        Task task = mActivityRecord.getTask();
-        return mActivityRecord.mDisplayContent != null && task != null
-                && mActivityRecord.mDisplayContent.getDisplayRotation().isDeviceInPosture(
-                        DeviceStateController.DeviceState.HALF_FOLDED, isTabletop)
-                && !mActivityRecord.mDisplayContent.inTransition()
-                && task.getWindowingMode() == WINDOWING_MODE_FULLSCREEN;
+    float getHorizontalPositionMultiplier(@NonNull Configuration parentConfiguration) {
+        return mActivityRecord.mAppCompatController.getAppCompatReachabilityOverrides()
+                .getHorizontalPositionMultiplier(parentConfiguration);
     }
 
-    // Note that we check the task rather than the parent as with ActivityEmbedding the parent might
-    // be a TaskFragment, and its windowing mode is always MULTI_WINDOW, even if the task is
-    // actually fullscreen.
-    private boolean isDisplayFullScreenAndSeparatingHinge() {
-        Task task = mActivityRecord.getTask();
-        return mActivityRecord.mDisplayContent != null
-                && mActivityRecord.mDisplayContent.getDisplayRotation().isDisplaySeparatingHinge()
-                && task != null
-                && task.getWindowingMode() == WINDOWING_MODE_FULLSCREEN;
-    }
-
-
-    float getHorizontalPositionMultiplier(Configuration parentConfiguration) {
-        // Don't check resolved configuration because it may not be updated yet during
-        // configuration change.
-        boolean bookModeEnabled = isFullScreenAndBookModeEnabled();
-        return isHorizontalReachabilityEnabled(parentConfiguration)
-                // Using the last global dynamic position to avoid "jumps" when moving
-                // between apps or activities.
-                ? mAppCompatConfiguration.getHorizontalMultiplierForReachability(bookModeEnabled)
-                : mAppCompatConfiguration.getLetterboxHorizontalPositionMultiplier(bookModeEnabled);
-    }
-
-    private boolean isFullScreenAndBookModeEnabled() {
-        return isDisplayFullScreenAndInPosture(/* isTabletop */ false)
-                && mAppCompatConfiguration.getIsAutomaticReachabilityInBookModeEnabled();
-    }
-
-    float getVerticalPositionMultiplier(Configuration parentConfiguration) {
-        // Don't check resolved configuration because it may not be updated yet during
-        // configuration change.
-        boolean tabletopMode = isDisplayFullScreenAndInPosture(/* isTabletop */ true);
-        return isVerticalReachabilityEnabled(parentConfiguration)
-                // Using the last global dynamic position to avoid "jumps" when moving
-                // between apps or activities.
-                ? mAppCompatConfiguration.getVerticalMultiplierForReachability(tabletopMode)
-                : mAppCompatConfiguration.getLetterboxVerticalPositionMultiplier(tabletopMode);
-    }
-
-    float getFixedOrientationLetterboxAspectRatio(@NonNull Configuration parentConfiguration) {
-        return mActivityRecord.mAppCompatController.getAppCompatAspectRatioOverrides()
-                .getFixedOrientationLetterboxAspectRatio(parentConfiguration);
+    float getVerticalPositionMultiplier(@NonNull Configuration parentConfiguration) {
+        return mActivityRecord.mAppCompatController.getAppCompatReachabilityOverrides()
+                .getVerticalPositionMultiplier(parentConfiguration);
     }
 
     boolean isLetterboxEducationEnabled() {
         return mAppCompatConfiguration.getIsEducationEnabled();
     }
 
-    /**
-     * @return {@value true} if the resulting app is letterboxed in a way defined as thin.
-     */
-    boolean isVerticalThinLetterboxed() {
-        final int thinHeight = mAppCompatConfiguration.getThinLetterboxHeightPx();
-        if (thinHeight < 0) {
-            return false;
-        }
-        final Task task = mActivityRecord.getTask();
-        if (task == null) {
-            return false;
-        }
-        final int padding = Math.abs(
-                task.getBounds().height() - mActivityRecord.getBounds().height()) / 2;
-        return padding <= thinHeight;
-    }
-
-    /**
-     * @return {@value true} if the resulting app is pillarboxed in a way defined as thin.
-     */
-    boolean isHorizontalThinLetterboxed() {
-        final int thinWidth = mAppCompatConfiguration.getThinLetterboxWidthPx();
-        if (thinWidth < 0) {
-            return false;
-        }
-        final Task task = mActivityRecord.getTask();
-        if (task == null) {
-            return false;
-        }
-        final int padding = Math.abs(
-                task.getBounds().width() - mActivityRecord.getBounds().width()) / 2;
-        return padding <= thinWidth;
-    }
-
-
-    /**
-     * @return {@value true} if the vertical reachability should be allowed in case of
-     * thin letteboxing
-     */
-    boolean allowVerticalReachabilityForThinLetterbox() {
-        if (!Flags.disableThinLetterboxingPolicy()) {
-            return true;
-        }
-        // When the flag is enabled we allow vertical reachability only if the
-        // app is not thin letterboxed vertically.
-        return !isVerticalThinLetterboxed();
-    }
-
-    /**
-     * @return {@value true} if the vertical reachability should be enabled in case of
-     * thin letteboxing
-     */
-    boolean allowHorizontalReachabilityForThinLetterbox() {
-        if (!Flags.disableThinLetterboxingPolicy()) {
-            return true;
-        }
-        // When the flag is enabled we allow horizontal reachability only if the
-        // app is not thin pillarboxed.
-        return !isHorizontalThinLetterboxed();
-    }
-
-    boolean shouldOverrideMinAspectRatio() {
-        return mActivityRecord.mAppCompatController.getAppCompatAspectRatioOverrides()
-                .shouldOverrideMinAspectRatio();
-    }
-
-    @AppCompatConfiguration.LetterboxVerticalReachabilityPosition
-    int getLetterboxPositionForVerticalReachability() {
-        final boolean isInFullScreenTabletopMode = isDisplayFullScreenAndSeparatingHinge();
-        return mAppCompatConfiguration.getLetterboxPositionForVerticalReachability(
-                isInFullScreenTabletopMode);
-    }
-
-    @AppCompatConfiguration.LetterboxHorizontalReachabilityPosition
-    int getLetterboxPositionForHorizontalReachability() {
-        final boolean isInFullScreenBookMode = isFullScreenAndBookModeEnabled();
-        return mAppCompatConfiguration.getLetterboxPositionForHorizontalReachability(
-                isInFullScreenBookMode);
-    }
-
     @VisibleForTesting
     void handleHorizontalDoubleTap(int x) {
-        if (!isHorizontalReachabilityEnabled() || mActivityRecord.isInTransition()) {
-            return;
-        }
-
-        if (mLetterbox.getInnerFrame().left <= x && mLetterbox.getInnerFrame().right >= x) {
-            // Only react to clicks at the sides of the letterboxed app window.
-            return;
-        }
-
-        boolean isInFullScreenBookMode = isDisplayFullScreenAndSeparatingHinge()
-                && mAppCompatConfiguration.getIsAutomaticReachabilityInBookModeEnabled();
-        int letterboxPositionForHorizontalReachability = mAppCompatConfiguration
-                .getLetterboxPositionForHorizontalReachability(isInFullScreenBookMode);
-        if (mLetterbox.getInnerFrame().left > x) {
-            // Moving to the next stop on the left side of the app window: right > center > left.
-            mAppCompatConfiguration.movePositionForHorizontalReachabilityToNextLeftStop(
-                    isInFullScreenBookMode);
-            int changeToLog =
-                    letterboxPositionForHorizontalReachability
-                            == LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_CENTER
-                                ? LETTERBOX_POSITION_CHANGED__POSITION_CHANGE__CENTER_TO_LEFT
-                                : LETTERBOX_POSITION_CHANGED__POSITION_CHANGE__RIGHT_TO_CENTER;
-            logLetterboxPositionChange(changeToLog);
-            mDoubleTapEvent = true;
-        } else if (mLetterbox.getInnerFrame().right < x) {
-            // Moving to the next stop on the right side of the app window: left > center > right.
-            mAppCompatConfiguration.movePositionForHorizontalReachabilityToNextRightStop(
-                    isInFullScreenBookMode);
-            int changeToLog =
-                    letterboxPositionForHorizontalReachability
-                            == LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_CENTER
-                                ? LETTERBOX_POSITION_CHANGED__POSITION_CHANGE__CENTER_TO_RIGHT
-                                : LETTERBOX_POSITION_CHANGED__POSITION_CHANGE__LEFT_TO_CENTER;
-            logLetterboxPositionChange(changeToLog);
-            mDoubleTapEvent = true;
-        }
-        // TODO(197549949): Add animation for transition.
-        mActivityRecord.recomputeConfiguration();
+        mActivityRecord.mAppCompatController.getAppCompatReachabilityPolicy()
+                .handleHorizontalDoubleTap(x, mLetterbox::getInnerFrame);
     }
 
     @VisibleForTesting
     void handleVerticalDoubleTap(int y) {
-        if (!isVerticalReachabilityEnabled() || mActivityRecord.isInTransition()) {
-            return;
-        }
-
-        if (mLetterbox.getInnerFrame().top <= y && mLetterbox.getInnerFrame().bottom >= y) {
-            // Only react to clicks at the top and bottom of the letterboxed app window.
-            return;
-        }
-        boolean isInFullScreenTabletopMode = isDisplayFullScreenAndSeparatingHinge();
-        int letterboxPositionForVerticalReachability = mAppCompatConfiguration
-                .getLetterboxPositionForVerticalReachability(isInFullScreenTabletopMode);
-        if (mLetterbox.getInnerFrame().top > y) {
-            // Moving to the next stop on the top side of the app window: bottom > center > top.
-            mAppCompatConfiguration.movePositionForVerticalReachabilityToNextTopStop(
-                    isInFullScreenTabletopMode);
-            int changeToLog =
-                    letterboxPositionForVerticalReachability
-                            == LETTERBOX_VERTICAL_REACHABILITY_POSITION_CENTER
-                                ? LETTERBOX_POSITION_CHANGED__POSITION_CHANGE__CENTER_TO_TOP
-                                : LETTERBOX_POSITION_CHANGED__POSITION_CHANGE__BOTTOM_TO_CENTER;
-            logLetterboxPositionChange(changeToLog);
-            mDoubleTapEvent = true;
-        } else if (mLetterbox.getInnerFrame().bottom < y) {
-            // Moving to the next stop on the bottom side of the app window: top > center > bottom.
-            mAppCompatConfiguration.movePositionForVerticalReachabilityToNextBottomStop(
-                    isInFullScreenTabletopMode);
-            int changeToLog =
-                    letterboxPositionForVerticalReachability
-                            == LETTERBOX_VERTICAL_REACHABILITY_POSITION_CENTER
-                                ? LETTERBOX_POSITION_CHANGED__POSITION_CHANGE__CENTER_TO_BOTTOM
-                                : LETTERBOX_POSITION_CHANGED__POSITION_CHANGE__TOP_TO_CENTER;
-            logLetterboxPositionChange(changeToLog);
-            mDoubleTapEvent = true;
-        }
-        // TODO(197549949): Add animation for transition.
-        mActivityRecord.recomputeConfiguration();
-    }
-
-    /**
-     * Whether horizontal reachability is enabled for an activity in the current configuration.
-     *
-     * <p>Conditions that needs to be met:
-     * <ul>
-     *   <li>Windowing mode is fullscreen.
-     *   <li>Horizontal Reachability is enabled.
-     *   <li>First top opaque activity fills parent vertically, but not horizontally.
-     * </ul>
-     */
-    private boolean isHorizontalReachabilityEnabled(Configuration parentConfiguration) {
-        if (!allowHorizontalReachabilityForThinLetterbox()) {
-            return false;
-        }
-        final Rect parentAppBoundsOverride = mActivityRecord.getParentAppBoundsOverride();
-        final Rect parentAppBounds = parentAppBoundsOverride != null
-                ? parentAppBoundsOverride : parentConfiguration.windowConfiguration.getAppBounds();
-        // Use screen resolved bounds which uses resolved bounds or size compat bounds
-        // as activity bounds can sometimes be empty
-        final Rect opaqueActivityBounds = mActivityRecord.mAppCompatController
-                .getTransparentPolicy().getFirstOpaqueActivity()
-                .map(ActivityRecord::getScreenResolvedBounds)
-                .orElse(mActivityRecord.getScreenResolvedBounds());
-        return mAppCompatConfiguration.getIsHorizontalReachabilityEnabled()
-                && parentConfiguration.windowConfiguration.getWindowingMode()
-                        == WINDOWING_MODE_FULLSCREEN
-                // Check whether the activity fills the parent vertically.
-                && parentAppBounds.height() <= opaqueActivityBounds.height()
-                && parentAppBounds.width() > opaqueActivityBounds.width();
-    }
-
-    @VisibleForTesting
-    boolean isHorizontalReachabilityEnabled() {
-        return isHorizontalReachabilityEnabled(mActivityRecord.getParent().getConfiguration());
-    }
-
-    boolean isLetterboxDoubleTapEducationEnabled() {
-        return isHorizontalReachabilityEnabled() || isVerticalReachabilityEnabled();
-    }
-
-    // TODO(b/346264992): Remove after AppCompatController refactoring
-    private AppCompatOverrides getAppCompatOverrides() {
-        return mActivityRecord.mAppCompatController.getAppCompatOverrides();
-    }
-
-    /**
-     * Whether vertical reachability is enabled for an activity in the current configuration.
-     *
-     * <p>Conditions that needs to be met:
-     * <ul>
-     *   <li>Windowing mode is fullscreen.
-     *   <li>Vertical Reachability is enabled.
-     *   <li>First top opaque activity fills parent horizontally but not vertically.
-     * </ul>
-     */
-    private boolean isVerticalReachabilityEnabled(Configuration parentConfiguration) {
-        if (!allowVerticalReachabilityForThinLetterbox()) {
-            return false;
-        }
-        final Rect parentAppBoundsOverride = mActivityRecord.getParentAppBoundsOverride();
-        final Rect parentAppBounds = parentAppBoundsOverride != null
-                ? parentAppBoundsOverride : parentConfiguration.windowConfiguration.getAppBounds();
-        // Use screen resolved bounds which uses resolved bounds or size compat bounds
-        // as activity bounds can sometimes be empty.
-        final Rect opaqueActivityBounds = mActivityRecord.mAppCompatController
-                .getTransparentPolicy().getFirstOpaqueActivity()
-                .map(ActivityRecord::getScreenResolvedBounds)
-                .orElse(mActivityRecord.getScreenResolvedBounds());
-        return mAppCompatConfiguration.getIsVerticalReachabilityEnabled()
-                && parentConfiguration.windowConfiguration.getWindowingMode()
-                        == WINDOWING_MODE_FULLSCREEN
-                // Check whether the activity fills the parent horizontally.
-                && parentAppBounds.width() <= opaqueActivityBounds.width()
-                && parentAppBounds.height() > opaqueActivityBounds.height();
-    }
-
-    @VisibleForTesting
-    boolean isVerticalReachabilityEnabled() {
-        return isVerticalReachabilityEnabled(mActivityRecord.getParent().getConfiguration());
+        mActivityRecord.mAppCompatController.getAppCompatReachabilityPolicy()
+                .handleVerticalDoubleTap(y, mLetterbox::getInnerFrame);
     }
 
     @VisibleForTesting
     boolean shouldShowLetterboxUi(WindowState mainWindow) {
-        if (getAppCompatOverrides().getAppCompatOrientationOverrides()
+        if (mActivityRecord.mAppCompatController.getAppCompatOrientationOverrides()
                 .getIsRelaunchingAfterRequestedOrientationChanged()) {
             return mLastShouldShowLetterboxUi;
         }
@@ -816,11 +431,6 @@
         return null;
     }
 
-    boolean getIsRelaunchingAfterRequestedOrientationChanged() {
-        return getAppCompatOverrides().getAppCompatOrientationOverrides()
-                .getIsRelaunchingAfterRequestedOrientationChanged();
-    }
-
     private void adjustBoundsForTaskbar(final WindowState mainWindow, final Rect bounds) {
         // Rounded corners should be displayed above the taskbar. When taskbar is hidden,
         // an insets frame is equal to a navigation bar which shouldn't affect position of
@@ -905,8 +515,10 @@
         if (!shouldShowLetterboxUi) {
             return;
         }
-        pw.println(prefix + "  isVerticalThinLetterboxed=" + isVerticalThinLetterboxed());
-        pw.println(prefix + "  isHorizontalThinLetterboxed=" + isHorizontalThinLetterboxed());
+        pw.println(prefix + "  isVerticalThinLetterboxed=" + mActivityRecord.mAppCompatController
+                .getAppCompatReachabilityOverrides().isVerticalThinLetterboxed());
+        pw.println(prefix + "  isHorizontalThinLetterboxed=" + mActivityRecord.mAppCompatController
+                .getAppCompatReachabilityOverrides().isHorizontalThinLetterboxed());
         pw.println(prefix + "  letterboxBackgroundColor=" + Integer.toHexString(
                 getLetterboxBackgroundColor().toArgb()));
         pw.println(prefix + "  letterboxBackgroundType="
@@ -923,10 +535,12 @@
             pw.println(prefix + "  letterboxBackgroundWallpaperBlurRadius="
                     + getLetterboxWallpaperBlurRadiusPx());
         }
-
+        final AppCompatReachabilityOverrides reachabilityOverrides = mActivityRecord
+                .mAppCompatController.getAppCompatReachabilityOverrides();
         pw.println(prefix + "  isHorizontalReachabilityEnabled="
-                + isHorizontalReachabilityEnabled());
-        pw.println(prefix + "  isVerticalReachabilityEnabled=" + isVerticalReachabilityEnabled());
+                + reachabilityOverrides.isHorizontalReachabilityEnabled());
+        pw.println(prefix + "  isVerticalReachabilityEnabled="
+                + reachabilityOverrides.isVerticalReachabilityEnabled());
         pw.println(prefix + "  letterboxHorizontalPositionMultiplier="
                 + getHorizontalPositionMultiplier(mActivityRecord.getParent().getConfiguration()));
         pw.println(prefix + "  letterboxVerticalPositionMultiplier="
@@ -970,64 +584,6 @@
         return "UNKNOWN_REASON";
     }
 
-    private int letterboxHorizontalReachabilityPositionToLetterboxPosition(
-            @AppCompatConfiguration.LetterboxHorizontalReachabilityPosition int position) {
-        switch (position) {
-            case LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_LEFT:
-                return APP_COMPAT_STATE_CHANGED__LETTERBOX_POSITION__LEFT;
-            case LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_CENTER:
-                return APP_COMPAT_STATE_CHANGED__LETTERBOX_POSITION__CENTER;
-            case LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_RIGHT:
-                return APP_COMPAT_STATE_CHANGED__LETTERBOX_POSITION__RIGHT;
-            default:
-                throw new AssertionError(
-                        "Unexpected letterbox horizontal reachability position type: "
-                                + position);
-        }
-    }
-
-    private int letterboxVerticalReachabilityPositionToLetterboxPosition(
-            @AppCompatConfiguration.LetterboxVerticalReachabilityPosition int position) {
-        switch (position) {
-            case LETTERBOX_VERTICAL_REACHABILITY_POSITION_TOP:
-                return APP_COMPAT_STATE_CHANGED__LETTERBOX_POSITION__TOP;
-            case LETTERBOX_VERTICAL_REACHABILITY_POSITION_CENTER:
-                return APP_COMPAT_STATE_CHANGED__LETTERBOX_POSITION__CENTER;
-            case LETTERBOX_VERTICAL_REACHABILITY_POSITION_BOTTOM:
-                return APP_COMPAT_STATE_CHANGED__LETTERBOX_POSITION__BOTTOM;
-            default:
-                throw new AssertionError(
-                        "Unexpected letterbox vertical reachability position type: "
-                                + position);
-        }
-    }
-
-    int getLetterboxPositionForLogging() {
-        int positionToLog = APP_COMPAT_STATE_CHANGED__LETTERBOX_POSITION__UNKNOWN_POSITION;
-        if (isHorizontalReachabilityEnabled()) {
-            int letterboxPositionForHorizontalReachability = mAppCompatConfiguration
-                    .getLetterboxPositionForHorizontalReachability(
-                            isDisplayFullScreenAndInPosture(/* isTabletop */ false));
-            positionToLog = letterboxHorizontalReachabilityPositionToLetterboxPosition(
-                    letterboxPositionForHorizontalReachability);
-        } else if (isVerticalReachabilityEnabled()) {
-            int letterboxPositionForVerticalReachability = mAppCompatConfiguration
-                    .getLetterboxPositionForVerticalReachability(
-                            isDisplayFullScreenAndInPosture(/* isTabletop */ true));
-            positionToLog = letterboxVerticalReachabilityPositionToLetterboxPosition(
-                    letterboxPositionForVerticalReachability);
-        }
-        return positionToLog;
-    }
-
-    /**
-     * Logs letterbox position changes via {@link ActivityMetricsLogger#logLetterboxPositionChange}.
-     */
-    private void logLetterboxPositionChange(int letterboxPositionChange) {
-        mActivityRecord.mTaskSupervisor.getActivityMetricsLogger()
-                .logLetterboxPositionChange(mActivityRecord, letterboxPositionChange);
-    }
-
     @Nullable
     LetterboxDetails getLetterboxDetails() {
         final WindowState w = mActivityRecord.findMainWindow();
diff --git a/services/core/java/com/android/server/wm/PhysicalDisplaySwitchTransitionLauncher.java b/services/core/java/com/android/server/wm/PhysicalDisplaySwitchTransitionLauncher.java
deleted file mode 100644
index 3cf301c..0000000
--- a/services/core/java/com/android/server/wm/PhysicalDisplaySwitchTransitionLauncher.java
+++ /dev/null
@@ -1,187 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm;
-
-import static android.view.WindowManager.TRANSIT_CHANGE;
-import static android.view.WindowManager.TRANSIT_FLAG_PHYSICAL_DISPLAY_SWITCH;
-
-import static com.android.internal.R.bool.config_unfoldTransitionEnabled;
-import static com.android.server.wm.ActivityTaskManagerService.POWER_MODE_REASON_CHANGE_DISPLAY;
-import static com.android.server.wm.DeviceStateController.DeviceState.FOLDED;
-import static com.android.server.wm.DeviceStateController.DeviceState.HALF_FOLDED;
-import static com.android.server.wm.DeviceStateController.DeviceState.OPEN;
-
-import android.animation.ValueAnimator;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.Context;
-import android.graphics.Rect;
-import android.window.DisplayAreaInfo;
-import android.window.TransitionRequestInfo;
-import android.window.WindowContainerTransaction;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.protolog.ProtoLogGroup;
-import com.android.internal.protolog.ProtoLog;
-import com.android.server.wm.DeviceStateController.DeviceState;
-
-public class PhysicalDisplaySwitchTransitionLauncher {
-
-    private final DisplayContent mDisplayContent;
-    private final ActivityTaskManagerService mAtmService;
-    private final Context mContext;
-    private final TransitionController mTransitionController;
-
-    /**
-     * If on a foldable device represents whether we need to show unfold animation when receiving
-     * a physical display switch event
-     */
-    private boolean mShouldRequestTransitionOnDisplaySwitch = false;
-    /**
-     * Current device state from {@link android.hardware.devicestate.DeviceStateManager}
-     */
-    private DeviceState mDeviceState = DeviceState.UNKNOWN;
-    private Transition mTransition;
-
-    public PhysicalDisplaySwitchTransitionLauncher(DisplayContent displayContent,
-            TransitionController transitionController) {
-        this(displayContent, displayContent.mWmService.mAtmService,
-                displayContent.mWmService.mContext, transitionController);
-    }
-
-    @VisibleForTesting
-    public PhysicalDisplaySwitchTransitionLauncher(DisplayContent displayContent,
-            ActivityTaskManagerService service, Context context,
-            TransitionController transitionController) {
-        mDisplayContent = displayContent;
-        mAtmService = service;
-        mContext = context;
-        mTransitionController = transitionController;
-    }
-
-    /**
-     * Called by the display manager just before it applied the device state, it is guaranteed
-     * that in case of physical display change the
-     * {@link PhysicalDisplaySwitchTransitionLauncher#requestDisplaySwitchTransitionIfNeeded}
-     * method will be invoked *after* this one.
-     */
-    void foldStateChanged(DeviceState newDeviceState) {
-        boolean isUnfolding = mDeviceState == FOLDED
-                && (newDeviceState == HALF_FOLDED || newDeviceState == OPEN);
-
-        if (isUnfolding) {
-            // Request transition only if we are unfolding the device
-            mShouldRequestTransitionOnDisplaySwitch = true;
-        } else if (newDeviceState != HALF_FOLDED && newDeviceState != OPEN) {
-            // Cancel the transition request in case if we are folding or switching to back
-            // to the rear display before the displays got switched
-            mShouldRequestTransitionOnDisplaySwitch = false;
-        }
-
-        mDeviceState = newDeviceState;
-    }
-
-    /**
-     * Requests to start a transition for the physical display switch
-     */
-    public void requestDisplaySwitchTransitionIfNeeded(int displayId, int oldDisplayWidth,
-            int oldDisplayHeight, int newDisplayWidth, int newDisplayHeight) {
-        if (!mShouldRequestTransitionOnDisplaySwitch) return;
-        if (!mTransitionController.isShellTransitionsEnabled()) return;
-        if (!mDisplayContent.getLastHasContent()) return;
-
-        boolean shouldRequestUnfoldTransition = mContext.getResources()
-                .getBoolean(config_unfoldTransitionEnabled) && ValueAnimator.areAnimatorsEnabled();
-
-        if (!shouldRequestUnfoldTransition) {
-            return;
-        }
-
-        mTransition = null;
-
-        if (mTransitionController.isCollecting()) {
-            // Add display container to the currently collecting transition
-            mTransition = mTransitionController.getCollectingTransition();
-            mTransition.collect(mDisplayContent);
-
-            // Make sure that transition is not ready until we finish the remote display change
-            mTransition.setReady(mDisplayContent, false);
-            mTransition.addFlag(TRANSIT_FLAG_PHYSICAL_DISPLAY_SWITCH);
-
-            ProtoLog.d(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
-                    "Adding display switch to existing collecting transition");
-        } else {
-            final TransitionRequestInfo.DisplayChange displayChange =
-                    new TransitionRequestInfo.DisplayChange(displayId);
-
-            final Rect startAbsBounds = new Rect(0, 0, oldDisplayWidth, oldDisplayHeight);
-            displayChange.setStartAbsBounds(startAbsBounds);
-            final Rect endAbsBounds = new Rect(0, 0, newDisplayWidth, newDisplayHeight);
-            displayChange.setEndAbsBounds(endAbsBounds);
-            displayChange.setPhysicalDisplayChanged(true);
-
-            mTransition = mTransitionController.requestStartDisplayTransition(TRANSIT_CHANGE,
-                    0 /* flags */, mDisplayContent, null /* remoteTransition */, displayChange);
-            mTransition.collect(mDisplayContent);
-        }
-
-        if (mTransition != null) {
-            mAtmService.startPowerMode(POWER_MODE_REASON_CHANGE_DISPLAY);
-        }
-
-        mShouldRequestTransitionOnDisplaySwitch = false;
-    }
-
-    /**
-     * Called when physical display is getting updated, this could happen e.g. on foldable
-     * devices when the physical underlying display is replaced.
-     *
-     * @param fromRotation rotation before the display change
-     * @param toRotation rotation after the display change
-     * @param newDisplayAreaInfo display area info after the display change
-     */
-    public void onDisplayUpdated(int fromRotation, int toRotation,
-            @NonNull DisplayAreaInfo newDisplayAreaInfo) {
-        if (mTransition == null) return;
-
-        final boolean started = mDisplayContent.mRemoteDisplayChangeController
-                .performRemoteDisplayChange(fromRotation, toRotation, newDisplayAreaInfo,
-                        this::continueDisplayUpdate);
-
-        if (!started) {
-            markTransitionAsReady();
-        }
-    }
-
-    private void continueDisplayUpdate(@Nullable WindowContainerTransaction transaction) {
-        if (mTransition == null) return;
-
-        if (transaction != null) {
-            mAtmService.mWindowOrganizerController.applyTransaction(transaction);
-        }
-
-        markTransitionAsReady();
-    }
-
-    private void markTransitionAsReady() {
-        if (mTransition == null) return;
-
-        mTransition.setAllReady();
-        mTransition = null;
-    }
-
-}
diff --git a/services/core/java/com/android/server/wm/PossibleDisplayInfoMapper.java b/services/core/java/com/android/server/wm/PossibleDisplayInfoMapper.java
index e3a2065..6e87977 100644
--- a/services/core/java/com/android/server/wm/PossibleDisplayInfoMapper.java
+++ b/services/core/java/com/android/server/wm/PossibleDisplayInfoMapper.java
@@ -57,7 +57,7 @@
      * Returns, for the given displayId, a list of unique display infos. List contains each
      * supported device state.
      * <p>List contents are guaranteed to be unique, but returned as a list rather than a set to
-     * minimize copies needed to make an iteraable data structure.
+     * minimize copies needed to make an iterable data structure.
      */
     public List<DisplayInfo> getPossibleDisplayInfos(int displayId) {
         // Update display infos before returning, since any cached values would have been removed
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index f2ccbc4..bded98c 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -183,6 +183,7 @@
 
     private Object mLastWindowFreezeSource = null;
     private float mScreenBrightnessOverride = PowerManager.BRIGHTNESS_INVALID_FLOAT;
+    private CharSequence mScreenBrightnessOverrideTag;
     private long mUserActivityTimeout = -1;
     private boolean mUpdateRotation = false;
     // Only set while traversing the default display based on its content.
@@ -770,6 +771,7 @@
         }
 
         mScreenBrightnessOverride = PowerManager.BRIGHTNESS_INVALID_FLOAT;
+        mScreenBrightnessOverrideTag = null;
         mUserActivityTimeout = -1;
         mObscureApplicationContentOnSecondaryDisplays = false;
         mSustainedPerformanceModeCurrent = false;
@@ -881,11 +883,15 @@
             final float brightnessOverride = mScreenBrightnessOverride < PowerManager.BRIGHTNESS_MIN
                     || mScreenBrightnessOverride > PowerManager.BRIGHTNESS_MAX
                     ? PowerManager.BRIGHTNESS_INVALID_FLOAT : mScreenBrightnessOverride;
+            CharSequence overrideTag = null;
+            if (brightnessOverride != PowerManager.BRIGHTNESS_INVALID_FLOAT) {
+                overrideTag = mScreenBrightnessOverrideTag;
+            }
             int brightnessFloatAsIntBits = Float.floatToIntBits(brightnessOverride);
             // Post these on a handler such that we don't call into power manager service while
             // holding the window manager lock to avoid lock contention with power manager lock.
             mHandler.obtainMessage(SET_SCREEN_BRIGHTNESS_OVERRIDE, brightnessFloatAsIntBits,
-                    0).sendToTarget();
+                    0, overrideTag).sendToTarget();
             mHandler.obtainMessage(SET_USER_ACTIVITY_TIMEOUT, mUserActivityTimeout).sendToTarget();
         }
 
@@ -1040,6 +1046,7 @@
             if (!syswin && w.mAttrs.screenBrightness >= 0
                     && Float.isNaN(mScreenBrightnessOverride)) {
                 mScreenBrightnessOverride = w.mAttrs.screenBrightness;
+                mScreenBrightnessOverrideTag = w.getWindowTag();
             }
 
             // This function assumes that the contents of the default display are processed first
@@ -1112,7 +1119,7 @@
             switch (msg.what) {
                 case SET_SCREEN_BRIGHTNESS_OVERRIDE:
                     mWmService.mPowerManagerInternal.setScreenBrightnessOverrideFromWindowManager(
-                            Float.intBitsToFloat(msg.arg1));
+                            Float.intBitsToFloat(msg.arg1), (CharSequence) msg.obj);
                     break;
                 case SET_USER_ACTIVITY_TIMEOUT:
                     mWmService.mPowerManagerInternal.
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index c26684f..32ec020 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -37,9 +37,9 @@
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
 import static android.view.WindowManager.LayoutParams.isSystemAlertWindowType;
 
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_IME;
 import static com.android.internal.protolog.ProtoLogGroup.WM_SHOW_TRANSACTIONS;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_POSITIONING;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 
 import android.annotation.NonNull;
@@ -79,6 +79,7 @@
 import android.view.WindowInsets.Type.InsetsType;
 import android.view.WindowManager;
 import android.view.WindowRelayoutResult;
+import android.view.inputmethod.ImeTracker;
 import android.window.InputTransferToken;
 import android.window.OnBackInvokedCallbackInfo;
 
@@ -537,27 +538,11 @@
 
     @Override
     public boolean startMovingTask(IWindow window, float startX, float startY) {
-        if (DEBUG_TASK_POSITIONING) Slog.d(
-                TAG_WM, "startMovingTask: {" + startX + "," + startY + "}");
-
-        final long ident = Binder.clearCallingIdentity();
-        try {
-            return mService.mTaskPositioningController.startMovingTask(window, startX, startY);
-        } finally {
-            Binder.restoreCallingIdentity(ident);
-        }
+        return false;
     }
 
     @Override
     public void finishMovingTask(IWindow window) {
-        if (DEBUG_TASK_POSITIONING) Slog.d(TAG_WM, "finishMovingTask");
-
-        final long ident = Binder.clearCallingIdentity();
-        try {
-            mService.mTaskPositioningController.finishTaskPositioning(window);
-        } finally {
-            Binder.restoreCallingIdentity(ident);
-        }
     }
 
     @Override
@@ -727,13 +712,20 @@
     }
 
     @Override
-    public void updateRequestedVisibleTypes(IWindow window, @InsetsType int requestedVisibleTypes) {
+    public void updateRequestedVisibleTypes(IWindow window, @InsetsType int requestedVisibleTypes,
+            @Nullable ImeTracker.Token imeStatsToken) {
         synchronized (mService.mGlobalLock) {
             final WindowState win = mService.windowForClientLocked(this, window,
                     false /* throwOnError */);
             if (win != null) {
+                ImeTracker.forLogging().onProgress(imeStatsToken,
+                        ImeTracker.PHASE_WM_UPDATE_REQUESTED_VISIBLE_TYPES);
                 win.setRequestedVisibleTypes(requestedVisibleTypes);
-                win.getDisplayContent().getInsetsPolicy().onRequestedVisibleTypesChanged(win);
+                win.getDisplayContent().getInsetsPolicy().onRequestedVisibleTypesChanged(win,
+                        imeStatsToken);
+            } else {
+                ImeTracker.forLogging().onFailed(imeStatsToken,
+                        ImeTracker.PHASE_WM_UPDATE_REQUESTED_VISIBLE_TYPES);
             }
         }
     }
@@ -1002,4 +994,28 @@
             }
         }
     }
+
+    @Override
+    public void notifyImeWindowVisibilityChangedFromClient(IWindow window, boolean visible,
+            @NonNull ImeTracker.Token statsToken) {
+        synchronized (mService.mGlobalLock) {
+            // TODO(b/353463205) check if we can use mService.getDefaultDisplayContentLocked()
+            //  instead of window
+            final WindowState win = mService.windowForClientLocked(this, window,
+                    false /* throwOnError */);
+            if (win != null) {
+                final InsetsStateController insetsStateController =
+                        win.getDisplayContent().getInsetsStateController();
+                ProtoLog.d(WM_DEBUG_IME, "notifyImeWindowVisibilityChangedFromClient: %s",
+                        insetsStateController.getImeSourceProvider());
+                ImeTracker.forLogging().onProgress(statsToken,
+                        ImeTracker.PHASE_WM_NOTIFY_IME_VISIBILITY_CHANGED_FROM_CLIENT);
+                insetsStateController.getImeSourceProvider().receiveImeStatsToken(visible,
+                        statsToken);
+            } else {
+                ImeTracker.forLogging().onFailed(statsToken,
+                        ImeTracker.PHASE_WM_NOTIFY_IME_VISIBILITY_CHANGED_FROM_CLIENT);
+            }
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/wm/SmoothDimmer.java b/services/core/java/com/android/server/wm/SmoothDimmer.java
deleted file mode 100644
index 2b4d901..0000000
--- a/services/core/java/com/android/server/wm/SmoothDimmer.java
+++ /dev/null
@@ -1,228 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm;
-
-import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_DIMMER;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-
-import android.graphics.Rect;
-import android.util.Log;
-import android.view.Surface;
-import android.view.SurfaceControl;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.protolog.ProtoLog;
-
-class SmoothDimmer extends Dimmer {
-
-    private static final String TAG = TAG_WITH_CLASS_NAME ? "Dimmer" : TAG_WM;
-    DimState mDimState;
-    final DimmerAnimationHelper.AnimationAdapterFactory mAnimationAdapterFactory;
-
-    /**
-     * Controls the dim behaviour
-     */
-    @VisibleForTesting
-    class DimState {
-        /** Related objects */
-        SurfaceControl mDimSurface;
-        final WindowContainer mHostContainer;
-        // The last container to request to dim
-        private WindowContainer mLastRequestedDimContainer;
-        /** Animation */
-        private final DimmerAnimationHelper mAnimationHelper;
-        boolean mSkipAnimation = false;
-        // Determines whether the dim layer should animate before destroying.
-        boolean mAnimateExit = true;
-        /** Surface visibility and bounds */
-        private boolean mIsVisible = false;
-        // TODO(b/64816140): Remove after confirming dimmer layer always matches its container.
-        final Rect mDimBounds = new Rect();
-
-        DimState() {
-            mHostContainer = mHost;
-            mAnimationHelper = new DimmerAnimationHelper(mAnimationAdapterFactory);
-            try {
-                mDimSurface = makeDimLayer();
-            } catch (Surface.OutOfResourcesException e) {
-                Log.w(TAG, "OutOfResourcesException creating dim surface");
-            }
-        }
-
-        void ensureVisible(SurfaceControl.Transaction t) {
-            if (!mIsVisible) {
-                t.show(mDimSurface);
-                t.setAlpha(mDimSurface, 0f);
-                mIsVisible = true;
-            }
-        }
-
-        void adjustSurfaceLayout(SurfaceControl.Transaction t) {
-            // TODO: Once we use geometry from hierarchy this falls away.
-            t.setPosition(mDimSurface, mDimBounds.left, mDimBounds.top);
-            t.setWindowCrop(mDimSurface, mDimBounds.width(), mDimBounds.height());
-        }
-
-        /**
-         * Set the parameters to prepare the dim to change its appearance
-         */
-        void prepareLookChange(float alpha, int blurRadius) {
-            mAnimationHelper.setRequestedAppearance(alpha, blurRadius);
-        }
-
-        /**
-         * Prepare the dim for the exit animation
-         */
-        void exit(SurfaceControl.Transaction t) {
-            if (!mAnimateExit) {
-                remove(t);
-            } else {
-                mAnimationHelper.setExitParameters();
-                setReady(t);
-            }
-        }
-
-        void remove(SurfaceControl.Transaction t) {
-            mAnimationHelper.stopCurrentAnimation(mDimSurface);
-            if (mDimSurface.isValid()) {
-                t.remove(mDimSurface);
-                ProtoLog.d(WM_DEBUG_DIMMER,
-                        "Removing dim surface %s on transaction %s", this, t);
-            } else {
-                Log.w(TAG, "Tried to remove " + mDimSurface + " multiple times\n");
-            }
-        }
-
-        @Override
-        public String toString() {
-            return "SmoothDimmer#DimState with host=" + mHostContainer + ", surface=" + mDimSurface;
-        }
-
-        /**
-         * Set the parameters to prepare the dim to be relative parented to the dimming container
-         */
-        void prepareReparent(WindowContainer relativeParent, int relativeLayer) {
-            mAnimationHelper.setRequestedRelativeParent(relativeParent, relativeLayer);
-        }
-
-        /**
-         * Call when all the changes have been requested to have them applied
-         * @param t The transaction in which to apply the changes
-         */
-        void setReady(SurfaceControl.Transaction t) {
-            mAnimationHelper.applyChanges(t, this);
-        }
-
-        /**
-         * Whether anyone is currently requesting the dim
-         */
-        boolean isDimming() {
-            return mLastRequestedDimContainer != null;
-        }
-
-        private SurfaceControl makeDimLayer() {
-            return mHost.makeChildSurface(null)
-                    .setParent(mHost.getSurfaceControl())
-                    .setColorLayer()
-                    .setName("Dim Layer for - " + mHost.getName())
-                    .setCallsite("DimLayer.makeDimLayer")
-                    .build();
-        }
-    }
-
-    protected SmoothDimmer(WindowContainer host) {
-        this(host, new DimmerAnimationHelper.AnimationAdapterFactory());
-    }
-
-    @VisibleForTesting
-    SmoothDimmer(WindowContainer host,
-                 DimmerAnimationHelper.AnimationAdapterFactory animationFactory) {
-        super(host);
-        mAnimationAdapterFactory = animationFactory;
-    }
-
-    @Override
-    void resetDimStates() {
-        if (mDimState != null) {
-            mDimState.mLastRequestedDimContainer = null;
-        }
-    }
-
-    @Override
-    protected void adjustAppearance(WindowContainer container, float alpha, int blurRadius) {
-        final DimState d = obtainDimState(container);
-        d.prepareLookChange(alpha, blurRadius);
-    }
-
-    @Override
-    protected void adjustRelativeLayer(WindowContainer container, int relativeLayer) {
-        if (mDimState != null) {
-            mDimState.prepareReparent(container, relativeLayer);
-        }
-    }
-
-    @Override
-    boolean updateDims(SurfaceControl.Transaction t) {
-        if (mDimState == null) {
-            return false;
-        }
-        if (!mDimState.isDimming()) {
-            // No one is dimming, fade out and remove the dim
-            mDimState.exit(t);
-            mDimState = null;
-            return false;
-        } else {
-            // Someone is dimming, show the requested changes
-            mDimState.adjustSurfaceLayout(t);
-            final WindowState ws = mDimState.mLastRequestedDimContainer.asWindowState();
-            if (!mDimState.mIsVisible && ws != null && ws.mActivityRecord != null
-                    && ws.mActivityRecord.mStartingData != null) {
-                // Skip enter animation while starting window is on top of its activity
-                mDimState.mSkipAnimation = true;
-            }
-            mDimState.setReady(t);
-            return true;
-        }
-    }
-
-    private DimState obtainDimState(WindowContainer container) {
-        if (mDimState == null) {
-            mDimState = new DimState();
-        }
-        mDimState.mLastRequestedDimContainer = container;
-        return mDimState;
-    }
-
-    @Override
-    @VisibleForTesting
-    SurfaceControl getDimLayer() {
-        return mDimState != null ? mDimState.mDimSurface : null;
-    }
-
-    @Override
-    Rect getDimBounds() {
-        return mDimState != null ? mDimState.mDimBounds : null;
-    }
-
-    @Override
-    void dontAnimateExit() {
-        if (mDimState != null) {
-            mDimState.mAnimateExit = false;
-        }
-    }
-}
diff --git a/services/core/java/com/android/server/wm/SnapshotController.java b/services/core/java/com/android/server/wm/SnapshotController.java
index cb388ef..99e1e8b 100644
--- a/services/core/java/com/android/server/wm/SnapshotController.java
+++ b/services/core/java/com/android/server/wm/SnapshotController.java
@@ -186,11 +186,11 @@
         }
         mActivitySnapshotController.handleTransitionFinish(windows);
         mActivitySnapshotController.endSnapshotProcess();
-        // Remove task snapshot if it is visible at the end of transition.
+        // Remove task snapshot if it is visible at the end of transition, except for PiP.
         for (int i = changeInfos.size() - 1; i >= 0; --i) {
             final WindowContainer wc = changeInfos.get(i).mContainer;
             final Task task = wc.asTask();
-            if (task != null && wc.isVisibleRequested()) {
+            if (task != null && wc.isVisibleRequested() && !task.inPinnedWindowingMode()) {
                 final TaskSnapshot snapshot = mTaskSnapshotController.getSnapshot(task.mTaskId,
                         task.mUserId, false /* restoreFromDisk */, false /* isLowResolution */);
                 if (snapshot != null) {
diff --git a/services/core/java/com/android/server/wm/StartingSurfaceController.java b/services/core/java/com/android/server/wm/StartingSurfaceController.java
index a1e6701..2da1fec 100644
--- a/services/core/java/com/android/server/wm/StartingSurfaceController.java
+++ b/services/core/java/com/android/server/wm/StartingSurfaceController.java
@@ -148,8 +148,7 @@
         }
         // For snapshot surface, the top activity could be trampoline activity, so here should
         // search for top fullscreen activity in the task.
-        final WindowState mainWindow = task
-                .getTopFullscreenMainWindow(false /* includeStartingApp */);
+        final WindowState mainWindow = task.getTopFullscreenMainWindow();
         if (mainWindow == null) {
             Slog.w(TAG, "TaskSnapshotSurface.create: no main window in " + activity);
             return null;
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 8f83a7c..d3df5fd 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -130,9 +130,7 @@
 import android.app.ActivityManager.TaskDescription;
 import android.app.ActivityOptions;
 import android.app.ActivityTaskManager;
-import android.app.AppCompatTaskInfo;
 import android.app.AppGlobals;
-import android.app.CameraCompatTaskInfo;
 import android.app.IActivityController;
 import android.app.PictureInPictureParams;
 import android.app.TaskInfo;
@@ -1132,6 +1130,9 @@
             final Task oldParentTask = oldParent.asTask();
             if (oldParentTask != null) {
                 forAllActivities(oldParentTask::cleanUpActivityReferences);
+
+                // Update the task description of the previous parent as well
+                oldParentTask.updateTaskDescription();
             }
 
             if (newParent == null || !newParent.inPinnedWindowingMode()) {
@@ -1163,6 +1164,9 @@
                 } catch (RemoteException e) {
                 }
             }
+
+            // Update the ancestor tasks' task description after reparenting
+            updateTaskDescription();
         }
 
         // First time we are adding the task to the system.
@@ -2281,7 +2285,7 @@
         // Apply crop to root tasks only and clear the crops of the descendant tasks.
         int width = 0;
         int height = 0;
-        if (isRootTask()) {
+        if (isRootTask() && !mTransitionController.mIsWaitingForDisplayEnabled) {
             final Rect taskBounds = getBounds();
             width = taskBounds.width();
             height = taskBounds.height();
@@ -3007,17 +3011,9 @@
         return r.getTask().mTaskId != taskId && r.token != notTop && r.canBeTopRunning();
     }
 
-    WindowState getTopFullscreenMainWindow(boolean includeStartingApp) {
-        final WindowState[] candidate = new WindowState[1];
-        getActivity((r) -> {
-            final WindowState win = r.findMainWindow(includeStartingApp);
-            if (win != null && win.mAttrs.isFullscreen()) {
-                candidate[0] = win;
-                return true;
-            }
-            return false;
-        });
-        return candidate[0];
+    @Nullable
+    WindowState getTopFullscreenMainWindow() {
+        return getWindow(w -> w.mAttrs.type == TYPE_BASE_APPLICATION && w.mAttrs.isFullscreen());
     }
 
     /**
@@ -3201,6 +3197,14 @@
         return "Task=" + mTaskId;
     }
 
+    WindowContainer<?> getDimmerParent() {
+        if (!inMultiWindowMode() && isTranslucentForTransition()) {
+            return getRootDisplayArea();
+        }
+        return this;
+    }
+
+    @Deprecated
     @Override
     Dimmer getDimmer() {
         // If the window is in multi-window mode, we want to dim at the Task level to ensure the dim
@@ -3214,7 +3218,7 @@
         // Once at the root task level, we want to check {@link #isTranslucent(ActivityRecord)}.
         // If true, we want to get the Dimmer from the level above since we don't want to animate
         // the dim with the Task.
-        if (!isRootTask() || (Dimmer.DIMMER_REFACTOR && isTranslucentAndVisible())
+        if (!isRootTask() || isTranslucentAndVisible()
                 || (Flags.getDimmerOnClosing() ? isTranslucentForTransition()
                                                 : isTranslucent(null))) {
             return super.getDimmer();
@@ -3353,7 +3357,7 @@
 
         //TODO (AM refactor): Just use local once updateEffectiveIntent is run during all child
         //                    order changes.
-        final Task topTask = top != null ? top.getTask() : this;
+        final Task topTask = top != null && top.getTask() != null ? top.getTask() : this;
         info.resizeMode = topTask.mResizeMode;
         info.topActivityType = topTask.getActivityType();
         info.displayCutoutInsets = topTask.getDisplayCutoutInsets();
@@ -3374,29 +3378,6 @@
                 ? top.getLastParentBeforePip().mTaskId : INVALID_TASK_ID;
         info.shouldDockBigOverlays = top != null && top.shouldDockBigOverlays;
         info.mTopActivityLocusId = top != null ? top.getLocusId() : null;
-
-        final boolean isTopActivityResumed = top != null
-                && top.getOrganizedTask() == this && top.isState(RESUMED);
-        final boolean isTopActivityVisible = top != null
-                && top.getOrganizedTask() == this && top.isVisible();
-        final AppCompatTaskInfo appCompatTaskInfo = info.appCompatTaskInfo;
-        // Whether the direct top activity is in size compat mode
-        appCompatTaskInfo.topActivityInSizeCompat = isTopActivityVisible && top.inSizeCompatMode();
-        if (appCompatTaskInfo.topActivityInSizeCompat
-                && mWmService.mAppCompatConfiguration.isTranslucentLetterboxingEnabled()) {
-            // We hide the restart button in case of transparent activities.
-            appCompatTaskInfo.topActivityInSizeCompat = top.fillsParent();
-        }
-        // Whether the direct top activity is eligible for letterbox education.
-        appCompatTaskInfo.topActivityEligibleForLetterboxEducation = isTopActivityResumed
-                && top.isEligibleForLetterboxEducation();
-        appCompatTaskInfo.isLetterboxEducationEnabled = top != null
-                && top.mLetterboxUiController.isLetterboxEducationEnabled();
-        // Whether the direct top activity requested showing camera compat control.
-        appCompatTaskInfo.cameraCompatTaskInfo.cameraCompatControlState = isTopActivityResumed
-                ? top.getCameraCompatControlState()
-                : CameraCompatTaskInfo.CAMERA_COMPAT_CONTROL_HIDDEN;
-
         final Task parentTask = getParent() != null ? getParent().asTask() : null;
         info.parentTaskId = parentTask != null && parentTask.mCreatedByOrganizer
                 ? parentTask.mTaskId
@@ -3407,57 +3388,8 @@
         info.isSleeping = shouldSleepActivities();
         info.isTopActivityTransparent = top != null && !top.fillsParent();
         info.isTopActivityStyleFloating = top != null && top.isStyleFloating();
-        appCompatTaskInfo.topActivityLetterboxVerticalPosition = TaskInfo.PROPERTY_VALUE_UNSET;
-        appCompatTaskInfo.topActivityLetterboxHorizontalPosition = TaskInfo.PROPERTY_VALUE_UNSET;
-        appCompatTaskInfo.topActivityLetterboxWidth = TaskInfo.PROPERTY_VALUE_UNSET;
-        appCompatTaskInfo.topActivityLetterboxHeight = TaskInfo.PROPERTY_VALUE_UNSET;
-        appCompatTaskInfo.isUserFullscreenOverrideEnabled = top != null
-                && top.mAppCompatController.getAppCompatAspectRatioOverrides()
-                    .shouldApplyUserFullscreenOverride();
-        appCompatTaskInfo.isSystemFullscreenOverrideEnabled = top != null
-                && top.mAppCompatController.getAppCompatAspectRatioOverrides()
-                    .isSystemOverrideToFullscreenEnabled();
-        appCompatTaskInfo.isFromLetterboxDoubleTap = top != null
-                && top.mLetterboxUiController.isFromDoubleTap();
-        if (top != null) {
-            appCompatTaskInfo.topActivityLetterboxWidth = top.getBounds().width();
-            appCompatTaskInfo.topActivityLetterboxHeight = top.getBounds().height();
-        }
-        // We need to consider if letterboxed or pillarboxed
-        // TODO(b/336807329) Encapsulate reachability logic
-        appCompatTaskInfo.isLetterboxDoubleTapEnabled = top != null
-                && top.mLetterboxUiController.isLetterboxDoubleTapEducationEnabled();
-        if (appCompatTaskInfo.isLetterboxDoubleTapEnabled) {
-            if (appCompatTaskInfo.isTopActivityPillarboxed()) {
-                if (top.mLetterboxUiController.allowHorizontalReachabilityForThinLetterbox()) {
-                    // Pillarboxed
-                    appCompatTaskInfo.topActivityLetterboxHorizontalPosition =
-                            top.mLetterboxUiController
-                                    .getLetterboxPositionForHorizontalReachability();
-                } else {
-                    appCompatTaskInfo.isLetterboxDoubleTapEnabled = false;
-                }
-            } else {
-                if (top.mLetterboxUiController.allowVerticalReachabilityForThinLetterbox()) {
-                    // Letterboxed
-                    appCompatTaskInfo.topActivityLetterboxVerticalPosition =
-                            top.mLetterboxUiController
-                                    .getLetterboxPositionForVerticalReachability();
-                } else {
-                    appCompatTaskInfo.isLetterboxDoubleTapEnabled = false;
-                }
-            }
-        }
-        appCompatTaskInfo.topActivityEligibleForUserAspectRatioButton = top != null
-                && !appCompatTaskInfo.topActivityInSizeCompat
-                && top.mAppCompatController.getAppCompatAspectRatioOverrides()
-                    .shouldEnableUserAspectRatioSettings()
-                && !info.isTopActivityTransparent;
-        appCompatTaskInfo.topActivityBoundsLetterboxed = top != null && top.areBoundsLetterboxed();
-        appCompatTaskInfo.cameraCompatTaskInfo.freeformCameraCompatMode = top == null
-                ? CameraCompatTaskInfo.CAMERA_COMPAT_FREEFORM_NONE
-                : top.mAppCompatController.getAppCompatCameraOverrides()
-                        .getFreeformCameraCompatMode();
+        info.lastNonFullscreenBounds = topTask.mLastNonFullscreenBounds;
+        AppCompatUtils.fillAppCompatTaskInfo(this, info, top);
     }
 
     /**
@@ -3506,6 +3438,9 @@
         if (task.effectiveUid != baseActivityUid) {
             info.baseActivity = new ComponentName("", "");
         }
+
+        info.capturedLink = null;
+        info.capturedLinkTimestamp = 0;
     }
 
     @Nullable PictureInPictureParams getPictureInPictureParams() {
@@ -3585,8 +3520,7 @@
         // starting window because persisted configuration does not effect to Task.
         info.taskInfo.configuration.setTo(activity.getConfiguration());
         if (!Flags.drawSnapshotAspectRatioMatch()) {
-            final WindowState mainWindow =
-                    getTopFullscreenMainWindow(false /* includeStartingApp */);
+            final WindowState mainWindow = getTopFullscreenMainWindow();
             if (mainWindow != null) {
                 info.topOpaqueWindowInsetsState =
                         mainWindow.getInsetsStateWithVisibilityOverride();
@@ -6139,9 +6073,8 @@
 
     @Override
     void onChildPositionChanged(WindowContainer child) {
-        dispatchTaskInfoChangedIfNeeded(false /* force */);
-
         if (!mChildren.contains(child)) {
+            dispatchTaskInfoChangedIfNeeded(false /* force */);
             return;
         }
         if (child.asTask() != null) {
@@ -6153,6 +6086,10 @@
             // Send for TaskFragmentParentInfo#hasDirectActivity change.
             sendTaskFragmentParentInfoChangedIfNeeded();
         }
+
+        // Update the ancestor tasks' task description after any children have reparented
+        updateTaskDescription();
+        dispatchTaskInfoChangedIfNeeded(false /* force */);
     }
 
     void reparent(TaskDisplayArea newParent, boolean onTop) {
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index 9b2c022..ed0dc3b 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -216,8 +216,7 @@
      */
     int mMinHeight;
 
-    Dimmer mDimmer = Dimmer.DIMMER_REFACTOR
-            ? new SmoothDimmer(this) : new LegacyDimmer(this);
+    Dimmer mDimmer = new Dimmer(this);
 
     /** Apply the dim layer on the embedded TaskFragment. */
     static final int EMBEDDED_DIM_AREA_TASK_FRAGMENT = 0;
@@ -1611,18 +1610,15 @@
                         if (DEBUG_RESULTS) {
                             Slog.v(TAG_RESULTS, "Delivering results to " + next + ": " + a);
                         }
-                        final ActivityResultItem activityResultItem = ActivityResultItem.obtain(
-                                next.token, a);
-                        mAtmService.getLifecycleManager().scheduleTransactionItem(
-                                appThread, activityResultItem);
+                        final ActivityResultItem item = new ActivityResultItem(next.token, a);
+                        mAtmService.getLifecycleManager().scheduleTransactionItem(appThread, item);
                     }
                 }
 
                 if (next.newIntents != null) {
-                    final NewIntentItem newIntentItem = NewIntentItem.obtain(
-                            next.token, next.newIntents, true /* resume */);
-                    mAtmService.getLifecycleManager().scheduleTransactionItem(
-                            appThread, newIntentItem);
+                    final NewIntentItem item =
+                            new NewIntentItem(next.token, next.newIntents, true /* resume */);
+                    mAtmService.getLifecycleManager().scheduleTransactionItem(appThread, item);
                 }
 
                 // Well the app will no longer be stopped.
@@ -1636,7 +1632,7 @@
                 final int topProcessState = mAtmService.mTopProcessState;
                 next.app.setPendingUiCleanAndForceProcessStateUpTo(topProcessState);
                 next.abortAndClearOptionsAnimation();
-                final ResumeActivityItem resumeActivityItem = ResumeActivityItem.obtain(
+                final ResumeActivityItem resumeActivityItem = new ResumeActivityItem(
                         next.token, topProcessState, dc.isNextTransitionForward(),
                         next.shouldSendCompatFakeFocus());
                 mAtmService.getLifecycleManager().scheduleTransactionItem(
@@ -1882,9 +1878,9 @@
             EventLogTags.writeWmPauseActivity(prev.mUserId, System.identityHashCode(prev),
                     prev.shortComponentName, "userLeaving=" + userLeaving, reason);
 
-            mAtmService.getLifecycleManager().scheduleTransactionItem(prev.app.getThread(),
-                    PauseActivityItem.obtain(prev.token, prev.finishing, userLeaving,
-                            pauseImmediately, autoEnteringPip));
+            final PauseActivityItem item = new PauseActivityItem(prev.token, prev.finishing,
+                    userLeaving, pauseImmediately, autoEnteringPip);
+            mAtmService.getLifecycleManager().scheduleTransactionItem(prev.app.getThread(), item);
         } catch (Exception e) {
             // Ignore exception, if process died other code will cleanup.
             Slog.w(TAG, "Exception thrown during pause", e);
@@ -2436,7 +2432,7 @@
                     inOutConfig.smallestScreenWidthDp = (int) (0.5f
                             + Math.min(mTmpFullBounds.width(), mTmpFullBounds.height()) / density);
                 } else if (windowingMode == WINDOWING_MODE_MULTI_WINDOW && mIsEmbedded
-                        && insideParentBounds && !resolvedBounds.equals(parentBounds)) {
+                        && !resolvedBounds.equals(parentBounds)) {
                     // For embedded TFs, the smallest width should be updated. Otherwise, inherit
                     // from the parent task would result in applications loaded wrong resource.
                     inOutConfig.smallestScreenWidthDp =
@@ -2823,7 +2819,21 @@
                 mClearedTaskForReuse,
                 mClearedTaskFragmentForPip,
                 mClearedForReorderActivityToFront,
-                calculateMinDimension());
+                calculateMinDimension(),
+                isTopNonFinishingChild());
+    }
+
+    private boolean isTopNonFinishingChild() {
+        final WindowContainer<?> parent = getParent();
+        if (parent == null) {
+            // Either the TaskFragment is not attached or is going to destroy. Return false.
+            return false;
+        }
+        final ActivityRecord topNonFishingActivity = parent.getActivity(ar -> !ar.finishing);
+        // If the parent's top non-finishing activity is this TaskFragment's, it means
+        // this TaskFragment is the top non-finishing container of its parent.
+        return topNonFishingActivity != null && topNonFishingActivity
+                .equals(getTopNonFinishingActivity());
     }
 
     /**
@@ -3106,6 +3116,7 @@
         return forAllWindows(getDimBehindWindow, true);
     }
 
+    @Deprecated
     @Override
     Dimmer getDimmer() {
         // If this is in an embedded TaskFragment and we want the dim applies on the TaskFragment.
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index 6e36d427..2c5beda 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -17,7 +17,6 @@
 package com.android.server.wm;
 
 import static android.app.ActivityTaskManager.INVALID_TASK_ID;
-import static android.app.CameraCompatTaskInfo.cameraCompatControlStateToString;
 import static android.window.StartingWindowRemovalInfo.DEFER_MODE_NONE;
 import static android.window.StartingWindowRemovalInfo.DEFER_MODE_NORMAL;
 import static android.window.StartingWindowRemovalInfo.DEFER_MODE_ROTATION;
@@ -1128,35 +1127,6 @@
         }
     }
 
-    @Override
-    public void updateCameraCompatControlState(WindowContainerToken token, int state) {
-        enforceTaskPermission("updateCameraCompatControlState()");
-        final long origId = Binder.clearCallingIdentity();
-        try {
-            synchronized (mGlobalLock) {
-                final WindowContainer wc = WindowContainer.fromBinder(token.asBinder());
-                if (wc == null) {
-                    Slog.w(TAG, "Could not resolve window from token");
-                    return;
-                }
-                final Task task = wc.asTask();
-                if (task == null) {
-                    Slog.w(TAG, "Could not resolve task from token");
-                    return;
-                }
-                ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER,
-                        "Update camera compat control state to %s for taskId=%d",
-                        cameraCompatControlStateToString(state), task.mTaskId);
-                final ActivityRecord activity = task.getTopNonFinishingActivity();
-                if (activity != null) {
-                    activity.updateCameraCompatStateFromUser(state);
-                }
-            }
-        } finally {
-            Binder.restoreCallingIdentity(origId);
-        }
-    }
-
     public boolean handleInterceptBackPressedOnTaskRoot(Task task) {
         if (!shouldInterceptBackPressedOnRootTask(task)) {
             return false;
diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java
deleted file mode 100644
index 972dd2e..0000000
--- a/services/core/java/com/android/server/wm/TaskPositioner.java
+++ /dev/null
@@ -1,500 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm;
-
-import static android.app.ActivityTaskManager.RESIZE_MODE_USER;
-import static android.app.ActivityTaskManager.RESIZE_MODE_USER_FORCED;
-import static android.os.InputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
-import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
-
-import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_BOTTOM;
-import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_LEFT;
-import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_NONE;
-import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_RIGHT;
-import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_TOP;
-import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_POSITIONING;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-import static com.android.server.wm.WindowManagerService.dipToPixel;
-import static com.android.server.wm.WindowState.MINIMUM_VISIBLE_HEIGHT_IN_DP;
-import static com.android.server.wm.WindowState.MINIMUM_VISIBLE_WIDTH_IN_DP;
-
-import static java.util.concurrent.CompletableFuture.completedFuture;
-
-import android.annotation.NonNull;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.os.Binder;
-import android.os.IBinder;
-import android.os.InputConfig;
-import android.os.RemoteException;
-import android.os.Trace;
-import android.util.DisplayMetrics;
-import android.util.Slog;
-import android.view.BatchedInputEventReceiver;
-import android.view.InputApplicationHandle;
-import android.view.InputChannel;
-import android.view.InputDevice;
-import android.view.InputEvent;
-import android.view.InputEventReceiver;
-import android.view.InputWindowHandle;
-import android.view.MotionEvent;
-import android.view.WindowManager;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.policy.TaskResizingAlgorithm;
-import com.android.internal.policy.TaskResizingAlgorithm.CtrlType;
-import com.android.internal.protolog.ProtoLog;
-
-import java.util.concurrent.CompletableFuture;
-
-class TaskPositioner implements IBinder.DeathRecipient {
-    private static final boolean DEBUG_ORIENTATION_VIOLATIONS = false;
-    private static final String TAG_LOCAL = "TaskPositioner";
-    private static final String TAG = TAG_WITH_CLASS_NAME ? TAG_LOCAL : TAG_WM;
-
-    private static Factory sFactory;
-
-    public static final float RESIZING_HINT_ALPHA = 0.5f;
-
-    public static final int RESIZING_HINT_DURATION_MS = 0;
-
-    private final WindowManagerService mService;
-    private InputEventReceiver mInputEventReceiver;
-    private DisplayContent mDisplayContent;
-    private Rect mTmpRect = new Rect();
-    private int mMinVisibleWidth;
-    private int mMinVisibleHeight;
-
-    @VisibleForTesting
-    Task mTask;
-    WindowState mWindow;
-    private boolean mResizing;
-    private boolean mPreserveOrientation;
-    private boolean mStartOrientationWasLandscape;
-    private final Rect mWindowOriginalBounds = new Rect();
-    private final Rect mWindowDragBounds = new Rect();
-    private final Point mMaxVisibleSize = new Point();
-    private float mStartDragX;
-    private float mStartDragY;
-    @CtrlType
-    private int mCtrlType = CTRL_NONE;
-    @VisibleForTesting
-    boolean mDragEnded;
-    IBinder mClientCallback;
-
-    InputChannel mClientChannel;
-    InputApplicationHandle mDragApplicationHandle;
-    InputWindowHandle mDragWindowHandle;
-
-    /** Use {@link #create(WindowManagerService)} instead. */
-    @VisibleForTesting
-    TaskPositioner(WindowManagerService service) {
-        mService = service;
-    }
-
-    private boolean onInputEvent(InputEvent event) {
-        // All returns need to be in the try block to make sure the finishInputEvent is
-        // called correctly.
-        if (!(event instanceof MotionEvent)
-                || (event.getSource() & InputDevice.SOURCE_CLASS_POINTER) == 0) {
-            return false;
-        }
-        final MotionEvent motionEvent = (MotionEvent) event;
-        if (mDragEnded) {
-            // The drag has ended but the clean-up message has not been processed by
-            // window manager. Drop events that occur after this until window manager
-            // has a chance to clean-up the input handle.
-            return true;
-        }
-
-        final float newX = motionEvent.getRawX();
-        final float newY = motionEvent.getRawY();
-
-        switch (motionEvent.getAction()) {
-            case MotionEvent.ACTION_DOWN: {
-                if (DEBUG_TASK_POSITIONING) {
-                    Slog.w(TAG, "ACTION_DOWN @ {" + newX + ", " + newY + "}");
-                }
-            }
-            break;
-
-            case MotionEvent.ACTION_MOVE: {
-                if (DEBUG_TASK_POSITIONING) {
-                    Slog.w(TAG, "ACTION_MOVE @ {" + newX + ", " + newY + "}");
-                }
-                synchronized (mService.mGlobalLock) {
-                    mDragEnded = notifyMoveLocked(newX, newY);
-                    mTask.getDimBounds(mTmpRect);
-                }
-                if (!mTmpRect.equals(mWindowDragBounds)) {
-                    Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER,
-                            "wm.TaskPositioner.resizeTask");
-                    mService.mAtmService.resizeTask(
-                            mTask.mTaskId, mWindowDragBounds, RESIZE_MODE_USER);
-                    Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
-                }
-            }
-            break;
-
-            case MotionEvent.ACTION_UP: {
-                if (DEBUG_TASK_POSITIONING) {
-                    Slog.w(TAG, "ACTION_UP @ {" + newX + ", " + newY + "}");
-                }
-                mDragEnded = true;
-            }
-            break;
-
-            case MotionEvent.ACTION_CANCEL: {
-                if (DEBUG_TASK_POSITIONING) {
-                    Slog.w(TAG, "ACTION_CANCEL @ {" + newX + ", " + newY + "}");
-                }
-                mDragEnded = true;
-            }
-            break;
-        }
-
-        if (mDragEnded) {
-            final boolean wasResizing = mResizing;
-            synchronized (mService.mGlobalLock) {
-                endDragLocked();
-                mTask.getDimBounds(mTmpRect);
-            }
-            if (wasResizing && !mTmpRect.equals(mWindowDragBounds)) {
-                // We were using fullscreen surface during resizing. Request
-                // resizeTask() one last time to restore surface to window size.
-                mService.mAtmService.resizeTask(
-                        mTask.mTaskId, mWindowDragBounds, RESIZE_MODE_USER_FORCED);
-            }
-
-            // Post back to WM to handle clean-ups. We still need the input
-            // event handler for the last finishInputEvent()!
-            mService.mTaskPositioningController.finishTaskPositioning();
-        }
-        return true;
-    }
-
-    @VisibleForTesting
-    Rect getWindowDragBounds() {
-        return mWindowDragBounds;
-    }
-
-    /**
-     * @param displayContent The Display that the window being dragged is on.
-     * @param win The window which will be dragged.
-     */
-    CompletableFuture<Void> register(DisplayContent displayContent, @NonNull WindowState win) {
-        if (DEBUG_TASK_POSITIONING) {
-            Slog.d(TAG, "Registering task positioner");
-        }
-
-        if (mClientChannel != null) {
-            Slog.e(TAG, "Task positioner already registered");
-            return completedFuture(null);
-        }
-
-        mDisplayContent = displayContent;
-        mClientChannel = mService.mInputManager.createInputChannel(TAG);
-
-        mInputEventReceiver = new BatchedInputEventReceiver.SimpleBatchedInputEventReceiver(
-                mClientChannel, mService.mAnimationHandler.getLooper(),
-                mService.mAnimator.getChoreographer(), this::onInputEvent);
-
-        mDragApplicationHandle = new InputApplicationHandle(new Binder(), TAG,
-                DEFAULT_DISPATCHING_TIMEOUT_MILLIS);
-
-        mDragWindowHandle = new InputWindowHandle(mDragApplicationHandle,
-                displayContent.getDisplayId());
-        mDragWindowHandle.name = TAG;
-        mDragWindowHandle.token = mClientChannel.getToken();
-        mDragWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_DRAG;
-        mDragWindowHandle.dispatchingTimeoutMillis = DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
-        mDragWindowHandle.ownerPid = WindowManagerService.MY_PID;
-        mDragWindowHandle.ownerUid = WindowManagerService.MY_UID;
-        mDragWindowHandle.scaleFactor = 1.0f;
-        // When dragging the window around, we do not want to steal focus for the window.
-        mDragWindowHandle.inputConfig = InputConfig.NOT_FOCUSABLE;
-
-        // The drag window cannot receive new touches.
-        mDragWindowHandle.touchableRegion.setEmpty();
-
-        // Pause rotations before a drag.
-        ProtoLog.d(WM_DEBUG_ORIENTATION, "Pausing rotation during re-position");
-        mDisplayContent.getDisplayRotation().pause();
-
-        // Notify InputMonitor to take mDragWindowHandle.
-        return mService.mTaskPositioningController.showInputSurface(win.getDisplayId())
-            .thenRun(() -> {
-                // The global lock is held by the callers of register but released before the async
-                // results are waited on. We must acquire the lock in this callback to ensure thread
-                // safety.
-                synchronized (mService.mGlobalLock) {
-                    final Rect displayBounds = mTmpRect;
-                    displayContent.getBounds(displayBounds);
-                    final DisplayMetrics displayMetrics = displayContent.getDisplayMetrics();
-                    mMinVisibleWidth = dipToPixel(MINIMUM_VISIBLE_WIDTH_IN_DP, displayMetrics);
-                    mMinVisibleHeight = dipToPixel(MINIMUM_VISIBLE_HEIGHT_IN_DP, displayMetrics);
-                    mMaxVisibleSize.set(displayBounds.width(), displayBounds.height());
-
-                    mDragEnded = false;
-
-                    try {
-                        mClientCallback = win.mClient.asBinder();
-                        mClientCallback.linkToDeath(this, 0 /* flags */);
-                    } catch (RemoteException e) {
-                        // The caller has died, so clean up TaskPositioningController.
-                        mService.mTaskPositioningController.finishTaskPositioning();
-                        return;
-                    }
-                    mWindow = win;
-                    mTask = win.getTask();
-                }
-            });
-    }
-
-    void unregister() {
-        if (DEBUG_TASK_POSITIONING) {
-            Slog.d(TAG, "Unregistering task positioner");
-        }
-
-        if (mClientChannel == null) {
-            Slog.e(TAG, "Task positioner not registered");
-            return;
-        }
-
-        mService.mTaskPositioningController.hideInputSurface(mDisplayContent.getDisplayId());
-        mService.mInputManager.removeInputChannel(mClientChannel.getToken());
-
-        mInputEventReceiver.dispose();
-        mInputEventReceiver = null;
-        mClientChannel.dispose();
-        mClientChannel = null;
-
-        mDragWindowHandle = null;
-        mDragApplicationHandle = null;
-        mDragEnded = true;
-
-        // Notify InputMonitor to remove mDragWindowHandle.
-        mDisplayContent.getInputMonitor().updateInputWindowsLw(true /*force*/);
-
-        // Resume rotations after a drag.
-        ProtoLog.d(WM_DEBUG_ORIENTATION, "Resuming rotation after re-position");
-        mDisplayContent.getDisplayRotation().resume();
-        mDisplayContent = null;
-        if (mClientCallback != null) {
-            mClientCallback.unlinkToDeath(this, 0 /* flags */);
-        }
-        mWindow = null;
-    }
-
-    /**
-     * Starts moving or resizing the task. This method should be only called from
-     * {@link TaskPositioningController#startPositioningLocked} or unit tests.
-     */
-    void startDrag(boolean resize, boolean preserveOrientation, float startX, float startY) {
-        if (DEBUG_TASK_POSITIONING) {
-            Slog.d(TAG, "startDrag: win=" + mWindow + ", resize=" + resize
-                    + ", preserveOrientation=" + preserveOrientation + ", {" + startX + ", "
-                    + startY + "}");
-        }
-        // Use the bounds of the task which accounts for
-        // multiple app windows. Don't use any bounds from win itself as it
-        // may not be the same size as the task.
-        final Rect startBounds = mTmpRect;
-        mTask.getBounds(startBounds);
-
-        mCtrlType = CTRL_NONE;
-        mStartDragX = startX;
-        mStartDragY = startY;
-        mPreserveOrientation = preserveOrientation;
-
-        if (resize) {
-            if (startX < startBounds.left) {
-                mCtrlType |= CTRL_LEFT;
-            }
-            if (startX > startBounds.right) {
-                mCtrlType |= CTRL_RIGHT;
-            }
-            if (startY < startBounds.top) {
-                mCtrlType |= CTRL_TOP;
-            }
-            if (startY > startBounds.bottom) {
-                mCtrlType |= CTRL_BOTTOM;
-            }
-            mResizing = mCtrlType != CTRL_NONE;
-        }
-
-        // In case of !isDockedInEffect we are using the union of all task bounds. These might be
-        // made up out of multiple windows which are only partially overlapping. When that happens,
-        // the orientation from the window of interest to the entire stack might diverge. However
-        // for now we treat them as the same.
-        mStartOrientationWasLandscape = startBounds.width() >= startBounds.height();
-        mWindowOriginalBounds.set(startBounds);
-
-        // Notify the app that resizing has started, even though we haven't received any new
-        // bounds yet. This will guarantee that the app starts the backdrop renderer before
-        // configuration changes which could cause an activity restart.
-        if (mResizing) {
-            notifyMoveLocked(startX, startY);
-
-            // The WindowPositionerEventReceiver callbacks are delivered on the same handler so this
-            // initial resize is always guaranteed to happen before subsequent drag resizes.
-            mService.mH.post(() -> {
-                mService.mAtmService.resizeTask(
-                        mTask.mTaskId, startBounds, RESIZE_MODE_USER_FORCED);
-            });
-        }
-
-        // Make sure we always have valid drag bounds even if the drag ends before any move events
-        // have been handled.
-        mWindowDragBounds.set(startBounds);
-    }
-
-    private void endDragLocked() {
-        mResizing = false;
-        mTask.setDragResizing(false);
-    }
-
-    /** Returns true if the move operation should be ended. */
-    @VisibleForTesting
-    boolean notifyMoveLocked(float x, float y) {
-        if (DEBUG_TASK_POSITIONING) {
-            Slog.d(TAG, "notifyMoveLocked: {" + x + "," + y + "}");
-        }
-
-        if (mCtrlType != CTRL_NONE) {
-            resizeDrag(x, y);
-            mTask.setDragResizing(true);
-            return false;
-        }
-
-        // This is a moving or scrolling operation.
-        // Only allow to move in stable area so the target window won't be covered by system bar.
-        // Though {@link Task#resolveOverrideConfiguration} should also avoid the case.
-        mDisplayContent.getStableRect(mTmpRect);
-        // The task may be put in a limited display area.
-        mTmpRect.intersect(mTask.getRootTask().getParent().getBounds());
-
-        int nX = (int) x;
-        int nY = (int) y;
-        if (!mTmpRect.contains(nX, nY)) {
-            // For a moving operation we allow the pointer to go out of the stack bounds, but
-            // use the clamped pointer position for the drag bounds computation.
-            nX = Math.min(Math.max(nX, mTmpRect.left), mTmpRect.right);
-            nY = Math.min(Math.max(nY, mTmpRect.top), mTmpRect.bottom);
-        }
-
-        updateWindowDragBounds(nX, nY, mTmpRect);
-        return false;
-    }
-
-    /**
-     * The user is drag - resizing the window.
-     *
-     * @param x The x coordinate of the current drag coordinate.
-     * @param y the y coordinate of the current drag coordinate.
-     */
-    @VisibleForTesting
-    void resizeDrag(float x, float y) {
-        updateDraggedBounds(TaskResizingAlgorithm.resizeDrag(x, y, mStartDragX, mStartDragY,
-                mWindowOriginalBounds, mCtrlType, mMinVisibleWidth, mMinVisibleHeight,
-                mMaxVisibleSize, mPreserveOrientation, mStartOrientationWasLandscape));
-    }
-
-    private void updateDraggedBounds(Rect newBounds) {
-        mWindowDragBounds.set(newBounds);
-
-        checkBoundsForOrientationViolations(mWindowDragBounds);
-    }
-
-    /**
-     * Validate bounds against orientation violations (if DEBUG_ORIENTATION_VIOLATIONS is set).
-     *
-     * @param bounds The bounds to be checked.
-     */
-    private void checkBoundsForOrientationViolations(Rect bounds) {
-        // When using debug check that we are not violating the given constraints.
-        if (DEBUG_ORIENTATION_VIOLATIONS) {
-            if (mStartOrientationWasLandscape != (bounds.width() >= bounds.height())) {
-                Slog.e(TAG, "Orientation violation detected! should be "
-                        + (mStartOrientationWasLandscape ? "landscape" : "portrait")
-                        + " but is the other");
-            } else {
-                Slog.v(TAG, "new bounds size: " + bounds.width() + " x " + bounds.height());
-            }
-            if (mMinVisibleWidth > bounds.width() || mMinVisibleHeight > bounds.height()) {
-                Slog.v(TAG, "Minimum requirement violated: Width(min, is)=(" + mMinVisibleWidth
-                        + ", " + bounds.width() + ") Height(min,is)=("
-                        + mMinVisibleHeight + ", " + bounds.height() + ")");
-            }
-            if (mMaxVisibleSize.x < bounds.width() || mMaxVisibleSize.y < bounds.height()) {
-                Slog.v(TAG, "Maximum requirement violated: Width(min, is)=(" + mMaxVisibleSize.x
-                        + ", " + bounds.width() + ") Height(min,is)=("
-                        + mMaxVisibleSize.y + ", " + bounds.height() + ")");
-            }
-        }
-    }
-
-    private void updateWindowDragBounds(int x, int y, Rect rootTaskBounds) {
-        final int offsetX = Math.round(x - mStartDragX);
-        final int offsetY = Math.round(y - mStartDragY);
-        mWindowDragBounds.set(mWindowOriginalBounds);
-        // Horizontally, at least mMinVisibleWidth pixels of the window should remain visible.
-        final int maxLeft = rootTaskBounds.right - mMinVisibleWidth;
-        final int minLeft = rootTaskBounds.left + mMinVisibleWidth - mWindowOriginalBounds.width();
-
-        // Vertically, the top mMinVisibleHeight of the window should remain visible.
-        // (This assumes that the window caption bar is at the top of the window).
-        final int minTop = rootTaskBounds.top;
-        final int maxTop = rootTaskBounds.bottom - mMinVisibleHeight;
-
-        mWindowDragBounds.offsetTo(
-                Math.min(Math.max(mWindowOriginalBounds.left + offsetX, minLeft), maxLeft),
-                Math.min(Math.max(mWindowOriginalBounds.top + offsetY, minTop), maxTop));
-
-        if (DEBUG_TASK_POSITIONING) Slog.d(TAG,
-                "updateWindowDragBounds: " + mWindowDragBounds);
-    }
-
-    public String toShortString() {
-        return TAG;
-    }
-
-    static void setFactory(Factory factory) {
-        sFactory = factory;
-    }
-
-    static TaskPositioner create(WindowManagerService service) {
-        if (sFactory == null) {
-            sFactory = new Factory() {};
-        }
-
-        return sFactory.create(service);
-    }
-
-    @Override
-    public void binderDied() {
-        mService.mTaskPositioningController.finishTaskPositioning();
-    }
-
-    interface Factory {
-        default TaskPositioner create(WindowManagerService service) {
-            return new TaskPositioner(service);
-        }
-    }
-}
diff --git a/services/core/java/com/android/server/wm/TaskPositioningController.java b/services/core/java/com/android/server/wm/TaskPositioningController.java
deleted file mode 100644
index 6f548ab..0000000
--- a/services/core/java/com/android/server/wm/TaskPositioningController.java
+++ /dev/null
@@ -1,250 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm;
-
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_POSITIONING;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-
-import static java.util.concurrent.CompletableFuture.completedFuture;
-
-import android.annotation.Nullable;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.util.Slog;
-import android.view.Display;
-import android.view.IWindow;
-import android.view.InputWindowHandle;
-import android.view.SurfaceControl;
-
-import java.util.concurrent.CompletableFuture;
-
-/**
- * Controller for task positioning by drag.
- */
-class TaskPositioningController {
-    private final WindowManagerService mService;
-    private SurfaceControl mInputSurface;
-    private DisplayContent mPositioningDisplay;
-
-    private @Nullable TaskPositioner mTaskPositioner;
-
-    private final Rect mTmpClipRect = new Rect();
-
-    boolean isPositioningLocked() {
-        return mTaskPositioner != null;
-    }
-
-    final SurfaceControl.Transaction mTransaction;
-
-    InputWindowHandle getDragWindowHandleLocked() {
-        return mTaskPositioner != null ? mTaskPositioner.mDragWindowHandle : null;
-    }
-
-    TaskPositioningController(WindowManagerService service) {
-        mService = service;
-        mTransaction = service.mTransactionFactory.get();
-    }
-
-    void hideInputSurface(int displayId) {
-        if (mPositioningDisplay != null && mPositioningDisplay.getDisplayId() == displayId
-                && mInputSurface != null) {
-            mTransaction.hide(mInputSurface).apply();
-        }
-    }
-
-    /**
-     * @return a future that completes after window info is sent.
-     */
-    CompletableFuture<Void> showInputSurface(int displayId) {
-        if (mPositioningDisplay == null || mPositioningDisplay.getDisplayId() != displayId) {
-            return completedFuture(null);
-        }
-        final DisplayContent dc = mService.mRoot.getDisplayContent(displayId);
-        if (mInputSurface == null) {
-            mInputSurface = mService.makeSurfaceBuilder(dc.getSession())
-                    .setContainerLayer()
-                    .setName("Drag and Drop Input Consumer")
-                    .setCallsite("TaskPositioningController.showInputSurface")
-                    .setParent(dc.getOverlayLayer())
-                    .build();
-        }
-
-        final InputWindowHandle h = getDragWindowHandleLocked();
-        if (h == null) {
-            Slog.w(TAG_WM, "Drag is in progress but there is no "
-                    + "drag window handle.");
-            return completedFuture(null);
-        }
-
-        final Display display = dc.getDisplay();
-        final Point p = new Point();
-        display.getRealSize(p);
-        mTmpClipRect.set(0, 0, p.x, p.y);
-
-        CompletableFuture<Void> result = new CompletableFuture<>();
-        mTransaction.show(mInputSurface)
-                .setInputWindowInfo(mInputSurface, h)
-                .setLayer(mInputSurface, Integer.MAX_VALUE)
-                .setPosition(mInputSurface, 0, 0)
-                .setCrop(mInputSurface, mTmpClipRect)
-                .addWindowInfosReportedListener(() -> result.complete(null))
-                .apply();
-        return result;
-    }
-
-    boolean startMovingTask(IWindow window, float startX, float startY) {
-        WindowState win = null;
-        CompletableFuture<Boolean> startPositioningLockedFuture;
-        synchronized (mService.mGlobalLock) {
-            win = mService.windowForClientLocked(null, window, false);
-            startPositioningLockedFuture =
-                startPositioningLocked(
-                    win, false /*resize*/, false /*preserveOrientation*/, startX, startY);
-        }
-
-        try {
-            if (!startPositioningLockedFuture.get()) {
-                return false;
-            }
-        } catch (Exception exception) {
-            Slog.e(TAG_WM, "Exception thrown while waiting for startPositionLocked future",
-                    exception);
-            return false;
-        }
-
-        synchronized (mService.mGlobalLock) {
-            mService.mAtmService.setFocusedTask(win.getTask().mTaskId);
-        }
-        return true;
-    }
-
-    void handleTapOutsideTask(DisplayContent displayContent, int x, int y) {
-        mService.mH.post(() -> {
-            Task task;
-            CompletableFuture<Boolean> startPositioningLockedFuture;
-            synchronized (mService.mGlobalLock) {
-                task = displayContent.findTaskForResizePoint(x, y);
-                if (task == null || !task.isResizeable()) {
-                    // The task is not resizable, so don't do anything when the user drags the
-                    // the resize handles.
-                    return;
-                }
-                startPositioningLockedFuture =
-                    startPositioningLocked(task.getTopVisibleAppMainWindow(), true /*resize*/,
-                            task.preserveOrientationOnResize(), x, y);
-            }
-
-            try {
-                if (!startPositioningLockedFuture.get()) {
-                    return;
-                }
-            } catch (Exception exception) {
-                Slog.e(TAG_WM, "Exception thrown while waiting for startPositionLocked future",
-                        exception);
-                return;
-            }
-
-            synchronized (mService.mGlobalLock) {
-                mService.mAtmService.setFocusedTask(task.mTaskId);
-            }
-        });
-    }
-
-    private CompletableFuture<Boolean> startPositioningLocked(WindowState win, boolean resize,
-            boolean preserveOrientation, float startX, float startY) {
-        if (DEBUG_TASK_POSITIONING)
-            Slog.d(TAG_WM, "startPositioningLocked: "
-                    + "win=" + win + ", resize=" + resize + ", preserveOrientation="
-                    + preserveOrientation + ", {" + startX + ", " + startY + "}");
-
-        if (win == null || win.mActivityRecord == null) {
-            Slog.w(TAG_WM, "startPositioningLocked: Bad window " + win);
-            return completedFuture(false);
-        }
-        if (win.mInputChannel == null) {
-            Slog.wtf(TAG_WM, "startPositioningLocked: " + win + " has no input channel, "
-                    + " probably being removed");
-            return completedFuture(false);
-        }
-
-        final DisplayContent displayContent = win.getDisplayContent();
-        if (displayContent == null) {
-            Slog.w(TAG_WM, "startPositioningLocked: Invalid display content " + win);
-            return completedFuture(false);
-        }
-        mPositioningDisplay = displayContent;
-
-        mTaskPositioner = TaskPositioner.create(mService);
-        return mTaskPositioner.register(displayContent, win).thenApply(unused -> {
-            // The global lock is held by the callers of startPositioningLocked but released before
-            // the async results are waited on. We must acquire the lock in this callback to ensure
-            // thread safety.
-            synchronized (mService.mGlobalLock) {
-                // We need to grab the touch focus so that the touch events during the
-                // resizing/scrolling are not sent to the app. 'win' is the main window
-                // of the app, it may not have focus since there might be other windows
-                // on top (eg. a dialog window).
-                WindowState transferTouchFromWin = win;
-                if (displayContent.mCurrentFocus != null && displayContent.mCurrentFocus != win
-                        && displayContent.mCurrentFocus.mActivityRecord == win.mActivityRecord) {
-                    transferTouchFromWin = displayContent.mCurrentFocus;
-                }
-                if (!mService.mInputManager.transferTouchGesture(
-                        transferTouchFromWin.mInputChannel.getToken(),
-                        mTaskPositioner.mClientChannel.getToken())) {
-                    Slog.e(TAG_WM, "startPositioningLocked: Unable to transfer touch focus");
-                    cleanUpTaskPositioner();
-                    return false;
-                }
-
-                mTaskPositioner.startDrag(resize, preserveOrientation, startX, startY);
-                return true;
-            }
-        });
-    }
-
-    public void finishTaskPositioning(IWindow window) {
-        if (mTaskPositioner != null && mTaskPositioner.mClientCallback == window.asBinder()) {
-            finishTaskPositioning();
-        }
-    }
-
-    void finishTaskPositioning() {
-        // TaskPositioner attaches the InputEventReceiver to the animation thread. We need to
-        // dispose the receiver on the same thread to avoid race conditions.
-        mService.mAnimationHandler.post(() -> {
-            if (DEBUG_TASK_POSITIONING) Slog.d(TAG_WM, "finishPositioning");
-
-            synchronized (mService.mGlobalLock) {
-                cleanUpTaskPositioner();
-                mPositioningDisplay = null;
-            }
-        });
-    }
-
-    private void cleanUpTaskPositioner() {
-        final TaskPositioner positioner = mTaskPositioner;
-        if (positioner == null) {
-            return;
-        }
-
-        // We need to assign task positioner to null first to indicate that we're finishing task
-        // positioning.
-        mTaskPositioner = null;
-        positioner.unregister();
-    }
-}
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index 57c7753..1f82cdb 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -232,8 +232,14 @@
         if (imeWindow != null && imeWindow.isVisible()) {
             final Rect bounds = imeWindow.getParentFrame();
             bounds.offsetTo(0, 0);
-            imeBuffer = ScreenCapture.captureLayersExcluding(imeWindow.getSurfaceControl(),
-                    bounds, 1.0f, pixelFormat, null);
+            ScreenCapture.LayerCaptureArgs captureArgs = new ScreenCapture.LayerCaptureArgs.Builder(
+                    imeWindow.getSurfaceControl())
+                    .setSourceCrop(bounds)
+                    .setFrameScale(1.0f)
+                    .setPixelFormat(pixelFormat)
+                    .setCaptureSecureLayers(true)
+                    .build();
+            imeBuffer = ScreenCapture.captureLayers(captureArgs);
         }
         return imeBuffer;
     }
@@ -261,11 +267,6 @@
     }
 
     @Override
-    WindowState getTopFullscreenWindow(Task source) {
-        return source.getTopFullscreenMainWindow(true /* includeStartingApp */);
-    }
-
-    @Override
     ActivityManager.TaskDescription getTaskDescription(Task source) {
         return source.getTaskDescription();
     }
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index f6a68d5..5698750 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -18,6 +18,7 @@
 
 import static android.app.ActivityOptions.ANIM_CUSTOM;
 import static android.app.ActivityOptions.ANIM_OPEN_CROSS_PROFILE_APPS;
+import static android.app.ActivityOptions.ANIM_SCENE_TRANSITION;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
@@ -463,14 +464,26 @@
 
     boolean canApplyDim(@NonNull Task task) {
         if (mTransientLaunches == null) return true;
-        final Dimmer dimmer = task.getDimmer();
-        if (dimmer == null) {
-            return false;
-        }
-        if (dimmer.getHost().asTask() != null) {
+        if (Flags.useTasksDimOnly()) {
+            WindowContainer<?> dimmerParent = task.getDimmerParent();
+            if (dimmerParent == null) {
+                return false;
+            }
             // Always allow to dim if the host only affects its task.
-            return true;
+            if (dimmerParent.asTask() == task) {
+                return true;
+            }
+        } else {
+            final Dimmer dimmer = task.getDimmer();
+            if (dimmer == null) {
+                return false;
+            }
+            if (dimmer.hostIsTask()) {
+                // Always allow to dim if the host only affects its task.
+                return true;
+            }
         }
+
         // The dimmer host of a translucent task can be a display, then it is not in transient-hide.
         for (int i = mTransientLaunches.size() - 1; i >= 0; --i) {
             // The transient task is usually the task of recents/home activity.
@@ -745,6 +758,7 @@
         if (mController.isAnimating()) {
             dc.enableHighPerfTransition(true);
         }
+        mController.dispatchLegacyAppTransitionPending(dc.mDisplayId);
     }
 
     /**
@@ -1618,7 +1632,7 @@
         mController.mTransitionTracer.logAbortedTransition(this);
         // Syncengine abort will call through to onTransactionReady()
         mSyncEngine.abort(mSyncId);
-        mController.dispatchLegacyAppTransitionCancelled();
+        mController.dispatchLegacyAppTransitionCancelled(mTargetDisplays);
         invokeTransitionEndedListeners();
     }
 
@@ -1750,7 +1764,7 @@
 
         // Check whether the participants were animated from back navigation.
         mController.mAtm.mBackNavigationController.onTransactionReady(this, mTargets,
-                transaction);
+                transaction, mFinishTransaction);
         final TransitionInfo info = calculateTransitionInfo(mType, mFlags, mTargets, transaction);
         info.setDebugId(mSyncId);
         mController.assignTrack(this, info);
@@ -1766,7 +1780,19 @@
         }
 
         for (int i = 0; i < mTargets.size(); ++i) {
-            final DisplayArea da = mTargets.get(i).mContainer.asDisplayArea();
+            final WindowContainer<?> wc = mTargets.get(i).mContainer;
+            final WallpaperWindowToken wp = wc.asWallpaperToken();
+            if (wp != null) {
+                // If on a rotation leash, the wallpaper token surface needs to be shown explicitly
+                // because shell only gets the leash and the wallpaper token surface is not allowed
+                // to be changed by non-transition logic until the transition is finished.
+                if (Flags.ensureWallpaperInTransitions() && wp.isVisibleRequested()
+                        && wp.getFixedRotationLeash() != null) {
+                    transaction.show(wp.mSurfaceControl);
+                }
+                continue;
+            }
+            final DisplayArea<?> da = wc.asDisplayArea();
             if (da == null) continue;
             if (da.isVisibleRequested()) {
                 mController.mValidateDisplayVis.remove(da);
@@ -1911,8 +1937,7 @@
             for (int i = changes.size() - 1; i >= 0; --i) {
                 final WindowContainer<?> container = mTargets.get(i).mContainer;
                 if (container.asActivityRecord() != null
-                        || (container.asTask() != null
-                                && mOverrideOptions.getOverrideTaskTransition())) {
+                        || shouldApplyAnimOptionsToTask(container.asTask())) {
                     changes.get(i).setAnimationOptions(mOverrideOptions);
                     // TODO(b/295805497): Extract mBackgroundColor from AnimationOptions.
                     changes.get(i).setBackgroundColor(mOverrideOptions.getBackgroundColor());
@@ -1926,6 +1951,16 @@
         updateActivityTargetForCrossProfileAnimation(info);
     }
 
+    private boolean shouldApplyAnimOptionsToTask(@Nullable Task task) {
+        if (task == null || mOverrideOptions == null) {
+            return false;
+        }
+        final int animType = mOverrideOptions.getType();
+        // Only apply AnimationOptions to Task if it is specified in #getOverrideTaskTransition
+        // or it's ANIM_SCENE_TRANSITION.
+        return animType == ANIM_SCENE_TRANSITION || mOverrideOptions.getOverrideTaskTransition();
+    }
+
     private boolean shouldApplyAnimOptionsToEmbeddedTf(@Nullable TaskFragment taskFragment) {
         if (taskFragment == null || !taskFragment.isEmbedded()) {
             return false;
@@ -2160,21 +2195,17 @@
         for (int i = mParticipants.size() - 1; i >= 0; --i) {
             final WallpaperWindowToken wallpaper = mParticipants.valueAt(i).asWallpaperToken();
             if (wallpaper != null) {
-                if (!wallpaper.isVisible() && (wallpaper.isVisibleRequested()
-                        || (Flags.ensureWallpaperInTransitions() && showWallpaper))) {
+                if (!wallpaper.isVisible() && wallpaper.isVisibleRequested()) {
                     wallpaper.commitVisibility(showWallpaper);
                 } else if (Flags.ensureWallpaperInTransitions() && wallpaper.isVisible()
                         && !showWallpaper && !wallpaper.getDisplayContent().isKeyguardLocked()
                         && !wallpaperIsOwnTarget(wallpaper)) {
                     wallpaper.setVisibleRequested(false);
                 }
-                if (showWallpaper && Flags.ensureWallpaperInTransitions()
-                        && wallpaper.isVisibleRequested()
-                        && getLeashSurface(wallpaper, t) != wallpaper.getSurfaceControl()) {
-                    // If on a rotation leash, we need to explicitly show the wallpaper surface
-                    // because shell only gets the leash and we don't allow non-transition logic
-                    // to touch the surfaces until the transition is over.
-                    t.show(wallpaper.getSurfaceControl());
+                if (showWallpaper && wallpaper.isVisibleRequested()) {
+                    for (int j = wallpaper.mChildren.size() - 1; j >= 0; --j) {
+                        wallpaper.mChildren.get(j).mWinAnimator.prepareSurfaceLocked(t);
+                    }
                 }
             }
         }
@@ -2361,6 +2392,7 @@
         sb.append(" id=" + mSyncId);
         sb.append(" type=" + transitTypeToString(mType));
         sb.append(" flags=0x" + Integer.toHexString(mFlags));
+        sb.append(" overrideAnimOptions=" + mOverrideOptions);
         sb.append('}');
         return sb.toString();
     }
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index f4ff404..67d7b37 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -42,6 +42,7 @@
 import android.util.SparseArray;
 import android.util.TimeUtils;
 import android.util.proto.ProtoOutputStream;
+import android.view.Display;
 import android.view.WindowManager;
 import android.window.ITransitionMetricsReporter;
 import android.window.ITransitionPlayer;
@@ -326,7 +327,6 @@
         mCollectingTransition.startCollecting(timeoutMs);
         ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Start collecting in Transition: %s",
                 mCollectingTransition);
-        dispatchLegacyAppTransitionPending();
     }
 
     void registerTransitionPlayer(@Nullable ITransitionPlayer player,
@@ -1347,31 +1347,55 @@
         mLegacyListeners.remove(listener);
     }
 
-    void dispatchLegacyAppTransitionPending() {
+    private static boolean shouldDispatchLegacyListener(
+            WindowManagerInternal.AppTransitionListener listener, int displayId) {
+        // INVALID_DISPLAY means that it is a global listener.
+        return listener.mTargetDisplayId == Display.INVALID_DISPLAY
+                || listener.mTargetDisplayId == displayId;
+    }
+
+    void dispatchLegacyAppTransitionPending(int displayId) {
         for (int i = 0; i < mLegacyListeners.size(); ++i) {
-            mLegacyListeners.get(i).onAppTransitionPendingLocked();
+            final WindowManagerInternal.AppTransitionListener listener = mLegacyListeners.get(i);
+            if (shouldDispatchLegacyListener(listener, displayId)) {
+                listener.onAppTransitionPendingLocked();
+            }
         }
     }
 
     void dispatchLegacyAppTransitionStarting(TransitionInfo info, long statusBarTransitionDelay) {
+        final long now = SystemClock.uptimeMillis();
         for (int i = 0; i < mLegacyListeners.size(); ++i) {
-            // TODO(shell-transitions): handle (un)occlude transition.
-            mLegacyListeners.get(i).onAppTransitionStartingLocked(
-                    SystemClock.uptimeMillis() + statusBarTransitionDelay,
-                    AnimationAdapter.STATUS_BAR_TRANSITION_DURATION);
+            final WindowManagerInternal.AppTransitionListener listener = mLegacyListeners.get(i);
+            for (int j = 0; j < info.getRootCount(); ++j) {
+                final int displayId = info.getRoot(j).getDisplayId();
+                if (shouldDispatchLegacyListener(listener, displayId)) {
+                    listener.onAppTransitionStartingLocked(
+                            now + statusBarTransitionDelay,
+                            AnimationAdapter.STATUS_BAR_TRANSITION_DURATION);
+                }
+            }
         }
     }
 
     void dispatchLegacyAppTransitionFinished(ActivityRecord ar) {
         for (int i = 0; i < mLegacyListeners.size(); ++i) {
-            mLegacyListeners.get(i).onAppTransitionFinishedLocked(ar.token);
+            final WindowManagerInternal.AppTransitionListener listener = mLegacyListeners.get(i);
+            if (shouldDispatchLegacyListener(listener, ar.getDisplayId())) {
+                listener.onAppTransitionFinishedLocked(ar.token);
+            }
         }
     }
 
-    void dispatchLegacyAppTransitionCancelled() {
-        for (int i = 0; i < mLegacyListeners.size(); ++i) {
-            mLegacyListeners.get(i).onAppTransitionCancelledLocked(
-                    false /* keyguardGoingAwayCancelled */);
+    void dispatchLegacyAppTransitionCancelled(ArrayList<DisplayContent> targetDisplays) {
+        for (int i = 0; i < targetDisplays.size(); ++i) {
+            final int displayId = targetDisplays.get(i).mDisplayId;
+            for (int j = 0; j < mLegacyListeners.size(); ++j) {
+                final var listener = mLegacyListeners.get(j);
+                if (shouldDispatchLegacyListener(listener, displayId)) {
+                    listener.onAppTransitionCancelledLocked(false /* keyguardGoingAwayCancelled */);
+                }
+            }
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index eb1a80b..64f9c01 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -107,11 +107,10 @@
 import android.window.IWindowContainerToken;
 import android.window.WindowContainerToken;
 
-import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.graphics.ColorUtils;
-import com.android.internal.protolog.common.LogLevel;
 import com.android.internal.protolog.ProtoLog;
+import com.android.internal.protolog.common.LogLevel;
 import com.android.internal.util.ToBooleanFunction;
 import com.android.server.wm.SurfaceAnimator.Animatable;
 import com.android.server.wm.SurfaceAnimator.AnimationType;
@@ -3765,6 +3764,7 @@
         }, true /* traverseTopToBottom */);
     }
 
+    @Deprecated
     Dimmer getDimmer() {
         if (mParent == null) {
             return null;
diff --git a/services/core/java/com/android/server/wm/WindowContextListenerController.java b/services/core/java/com/android/server/wm/WindowContextListenerController.java
index cd785e5..87e120c 100644
--- a/services/core/java/com/android/server/wm/WindowContextListenerController.java
+++ b/services/core/java/com/android/server/wm/WindowContextListenerController.java
@@ -330,8 +330,8 @@
             mLastReportedConfig.setTo(config);
             mLastReportedDisplay = displayId;
 
-            mWpc.scheduleClientTransactionItem(WindowContextInfoChangeItem.obtain(
-                    mClientToken, config, displayId));
+            mWpc.scheduleClientTransactionItem(
+                    new WindowContextInfoChangeItem(mClientToken, config, displayId));
             mHasPendingConfiguration = false;
         }
 
@@ -356,7 +356,7 @@
                 }
             }
             mDeathRecipient.unlinkToDeath();
-            mWpc.scheduleClientTransactionItem(WindowContextWindowRemovalItem.obtain(mClientToken));
+            mWpc.scheduleClientTransactionItem(new WindowContextWindowRemovalItem(mClientToken));
             unregister();
         }
 
diff --git a/services/core/java/com/android/server/wm/WindowManagerDebugConfig.java b/services/core/java/com/android/server/wm/WindowManagerDebugConfig.java
index 42b556f..6125360 100644
--- a/services/core/java/com/android/server/wm/WindowManagerDebugConfig.java
+++ b/services/core/java/com/android/server/wm/WindowManagerDebugConfig.java
@@ -47,7 +47,6 @@
     static final boolean DEBUG_LAYOUT_REPEATS = false;
     static final boolean DEBUG_WINDOW_TRACE = false;
     static final boolean DEBUG_TASK_MOVEMENT = false;
-    static final boolean DEBUG_TASK_POSITIONING = false;
     static final boolean DEBUG_ROOT_TASK = false;
     static final boolean DEBUG_DISPLAY = false;
     static final boolean DEBUG_POWER = false;
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 2ea1cf8..a574845 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -41,7 +41,6 @@
 import android.view.IInputFilter;
 import android.view.IRemoteAnimationFinishedCallback;
 import android.view.IWindow;
-import android.view.InputChannel;
 import android.view.MagnificationSpec;
 import android.view.RemoteAnimationTarget;
 import android.view.Surface;
@@ -244,6 +243,22 @@
     public static abstract class AppTransitionListener {
 
         /**
+         * The display this listener is interested in. If it is INVALID_DISPLAY, then which display
+         * should be notified depends on the dispatcher.
+         */
+        public final int mTargetDisplayId;
+
+        /** Let transition controller decide which display should receive the callbacks. */
+        public AppTransitionListener() {
+            this(Display.INVALID_DISPLAY);
+        }
+
+        /** It will listen the transition on the given display. */
+        public AppTransitionListener(int displayId) {
+            mTargetDisplayId = displayId;
+        }
+
+        /**
          * Called when an app transition is being setup and about to be executed.
          */
         public void onAppTransitionPendingLocked() {}
@@ -349,8 +364,10 @@
          *
          * @param windowToken The window token
          * @param imeVisible {@code true} if the IME should be shown, {@code false} to hide
+         * @param statsToken the token tracking the current IME request.
          */
-        void onImeRequestedChanged(IBinder windowToken, boolean imeVisible);
+        void onImeRequestedChanged(IBinder windowToken, boolean imeVisible,
+                @Nullable ImeTracker.Token statsToken);
     }
 
     /**
@@ -359,10 +376,10 @@
     public interface IDragDropCallback {
         default CompletableFuture<Boolean> registerInputChannel(
                 DragState state, Display display, InputManagerService service,
-                InputChannel source) {
+                IBinder sourceInputChannelToken) {
             return state.register(display)
                 .thenApply(unused ->
-                    service.startDragAndDrop(source, state.getInputChannel()));
+                    service.startDragAndDrop(sourceInputChannelToken, state.getInputToken()));
         }
 
         /**
@@ -673,15 +690,10 @@
      * <p>Only {@link com.android.server.inputmethod.InputMethodManagerService} is the expected and
      * tested caller of this method.</p>
      *
-     * @param imeToken token to track the active input method. Corresponding IME windows can be
-     *                 identified by checking {@link android.view.WindowManager.LayoutParams#token}.
-     *                 Note that there is no guarantee that the corresponding window is already
-     *                 created
      * @param imeTargetWindowToken token to identify the target window that the IME is associated
      *                             with
      */
-    public abstract void updateInputMethodTargetWindow(@NonNull IBinder imeToken,
-            @NonNull IBinder imeTargetWindowToken);
+    public abstract void updateInputMethodTargetWindow(@NonNull IBinder imeTargetWindowToken);
 
     /**
       * Returns true when the hardware keyboard is available.
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index acd8b3f..d73d509 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -54,7 +54,6 @@
 import static android.view.ContentRecordingSession.RECORD_CONTENT_TASK;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.Display.INVALID_DISPLAY;
-import static android.view.flags.Flags.sensitiveContentAppProtection;
 import static android.view.WindowManager.DISPLAY_IME_POLICY_FALLBACK_DISPLAY;
 import static android.view.WindowManager.DISPLAY_IME_POLICY_LOCAL;
 import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
@@ -97,6 +96,7 @@
 import static android.view.WindowManagerPolicyConstants.TYPE_LAYER_MULTIPLIER;
 import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_MISSING_WINDOW;
 import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_NOT_VISIBLE_ON_SCREEN;
+import static android.view.flags.Flags.sensitiveContentAppProtection;
 import static android.window.WindowProviderService.isWindowProviderService;
 
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
@@ -118,12 +118,12 @@
 import static com.android.server.policy.PhoneWindowManager.TRACE_WAIT_FOR_ALL_WINDOWS_DRAWN_METHOD;
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
 import static com.android.server.wm.ActivityTaskManagerService.POWER_MODE_REASON_CHANGE_DISPLAY;
-import static com.android.server.wm.DisplayContent.IME_TARGET_CONTROL;
-import static com.android.server.wm.DisplayContent.IME_TARGET_LAYERING;
 import static com.android.server.wm.AppCompatConfiguration.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND;
 import static com.android.server.wm.AppCompatConfiguration.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING;
 import static com.android.server.wm.AppCompatConfiguration.LETTERBOX_BACKGROUND_SOLID_COLOR;
 import static com.android.server.wm.AppCompatConfiguration.LETTERBOX_BACKGROUND_WALLPAPER;
+import static com.android.server.wm.DisplayContent.IME_TARGET_CONTROL;
+import static com.android.server.wm.DisplayContent.IME_TARGET_LAYERING;
 import static com.android.server.wm.RootWindowContainer.MATCH_ATTACHED_TASK_OR_RECENT_TASKS;
 import static com.android.server.wm.SensitiveContentPackages.PackageInfo;
 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_ALL;
@@ -357,7 +357,6 @@
 import com.android.server.power.ShutdownThread;
 import com.android.server.utils.PriorityDump;
 import com.android.server.wallpaper.WallpaperCropper.WallpaperCropUtils;
-import com.android.window.flags.Flags;
 
 import dalvik.annotation.optimization.NeverCompile;
 
@@ -1070,7 +1069,6 @@
     /** Whether or not a layout can cause a wake up when theater mode is enabled. */
     boolean mAllowTheaterModeWakeFromLayout;
 
-    final TaskPositioningController mTaskPositioningController;
     final DragDropController mDragDropController;
 
     /** For frozen screen animations. */
@@ -1428,7 +1426,6 @@
         mAllowTheaterModeWakeFromLayout = context.getResources().getBoolean(
                 com.android.internal.R.bool.config_allowTheaterModeWakeFromWindowLayout);
 
-        mTaskPositioningController = new TaskPositioningController(this);
         mDragDropController = new DragDropController(this, mH.getLooper());
 
         mHighRefreshRateDenylist = HighRefreshRateDenylist.create(context.getResources());
@@ -3752,7 +3749,7 @@
             }
             mCurrentUserId = newUserId;
             mDisplayWindowSettingsProvider.setOverrideSettingsForUser(newUserId);
-            mDisplayWindowSettingsProvider.removeStaleDisplaySettings(mRoot);
+            mDisplayWindowSettingsProvider.removeStaleDisplaySettingsLocked(this, mRoot);
             mPolicy.setCurrentUserLw(newUserId);
             mKeyguardDisableHandler.setCurrentUser(newUserId);
 
@@ -4719,8 +4716,10 @@
                     return;
                 }
                 dc.mRemoteInsetsControlTarget.setRequestedVisibleTypes(requestedVisibleTypes);
+                // TODO(b/353463205) the statsToken shouldn't be null as it is used later in the
+                //  IME provider. Check if we have to create a new request here
                 dc.getInsetsStateController().onRequestedVisibleTypesChanged(
-                        dc.mRemoteInsetsControlTarget);
+                        dc.mRemoteInsetsControlTarget, null /* statsToken */);
             }
         } finally {
             Binder.restoreCallingIdentity(origId);
@@ -5491,7 +5490,7 @@
             mRoot.forAllDisplays(DisplayContent::reconfigureDisplayLocked);
             // Per-user display settings may leave outdated settings after user switches, especially
             // during reboots starting with the default user without setCurrentUser called.
-            mDisplayWindowSettingsProvider.removeStaleDisplaySettings(mRoot);
+            mDisplayWindowSettingsProvider.removeStaleDisplaySettingsLocked(this, mRoot);
         }
     }
 
@@ -8095,11 +8094,10 @@
         }
 
         @Override
-        public void updateInputMethodTargetWindow(@NonNull IBinder imeToken,
-                @NonNull IBinder imeTargetWindowToken) {
+        public void updateInputMethodTargetWindow(@NonNull IBinder imeTargetWindowToken) {
             // TODO (b/34628091): Use this method to address the window animation issue.
             if (DEBUG_INPUT_METHOD) {
-                Slog.w(TAG_WM, "updateInputMethodTargetWindow: imeToken=" + imeToken
+                Slog.w(TAG_WM, "updateInputMethodTargetWindow:"
                         + " imeTargetWindowToken=" + imeTargetWindowToken);
             }
             synchronized (mGlobalLock) {
@@ -9037,7 +9035,8 @@
         // Otherwise, handle the touch-outside event directly.
         final WindowState w = t.getWindowState();
         final ActivityRecord activity = w != null ? w.getActivityRecord() : null;
-        if (activity != null && activity.isEmbedded()
+        if (mFocusedInputTarget != t && mFocusedInputTarget != null
+                && activity != null && activity.isEmbedded()
                 && activity.getTaskFragment().getAdjacentTaskFragment() != null) {
             mPointerDownOutsideFocusRunnable = () -> handlePointerDownOutsideFocus(t);
             mH.postDelayed(mPointerDownOutsideFocusRunnable, POINTER_DOWN_OUTSIDE_FOCUS_TIMEOUT_MS);
@@ -9379,40 +9378,77 @@
     }
 
     /**
-     * Move focus to the adjacent embedded activity if the adjacent activity is more recently
-     * created or has a window more recently added.
+     * Returns the Activity that has the most recently created window in the adjacent activities
+     * if any.
      */
-    boolean moveFocusToAdjacentEmbeddedWindow(@NonNull WindowState focusedWindow) {
-        final TaskFragment taskFragment = focusedWindow.getTaskFragment();
+    @NonNull
+    ActivityRecord getMostRecentActivityInAdjacent(@NonNull ActivityRecord focusedActivity) {
+        final TaskFragment taskFragment = focusedActivity.getTaskFragment();
         if (taskFragment == null) {
-            // Skip if not an Activity window.
-            return false;
+            // Return if activity no attached.
+            return focusedActivity;
         }
 
-        if (!Flags.embeddedActivityBackNavFlag()) {
-            // Skip if flag is not enabled.
-            return false;
-        }
-
-        if (!focusedWindow.mActivityRecord.isEmbedded()) {
-            // Skip if the focused activity is not embedded
-            return false;
+        if (!focusedActivity.isEmbedded()) {
+            // Return if the focused activity is not embedded.
+            return focusedActivity;
         }
 
         final TaskFragment adjacentTaskFragment = taskFragment.getAdjacentTaskFragment();
         final ActivityRecord adjacentTopActivity =
                 adjacentTaskFragment != null ? adjacentTaskFragment.topRunningActivity() : null;
         if (adjacentTopActivity == null) {
-            return false;
+            // Return if no adjacent activity.
+            return focusedActivity;
         }
 
         if (adjacentTopActivity.getLastWindowCreateTime()
-                < focusedWindow.mActivityRecord.getLastWindowCreateTime()) {
-            // Skip if the current focus activity has more recently active window.
+                < focusedActivity.getLastWindowCreateTime()) {
+            // Return if the current focus activity has more recently active window.
+            return focusedActivity;
+        }
+
+        return adjacentTopActivity;
+    }
+
+    @NonNull
+    WindowState getMostRecentUsedEmbeddedWindowForBack(@NonNull WindowState focusedWindow) {
+        final ActivityRecord focusedActivity = focusedWindow.getActivityRecord();
+        if (focusedActivity == null) {
+            // Not an Activity.
+            return focusedWindow;
+        }
+
+        final ActivityRecord mostRecentActivityInAdjacent = getMostRecentActivityInAdjacent(
+                focusedActivity);
+        if (mostRecentActivityInAdjacent == focusedActivity) {
+            // Already be the most recent window.
+            return focusedWindow;
+        }
+
+        // Looks for a candidate focused window on the adjacent Activity for the back event.
+        final WindowState candidate =
+                mostRecentActivityInAdjacent.getDisplayContent().findFocusedWindow(
+                        mostRecentActivityInAdjacent);
+        return candidate != null ? candidate : focusedWindow;
+    }
+
+    /**
+     * Move focus to the adjacent embedded activity if the adjacent activity is more recently
+     * created or has a window more recently added.
+     * <p>
+     * Returns {@code true} if the focused window is changed. Otherwise, returns {@code false}.
+     */
+    boolean moveFocusToAdjacentEmbeddedWindow(@NonNull WindowState focusedWindow) {
+        final ActivityRecord activity = focusedWindow.getActivityRecord();
+        if (activity == null) {
             return false;
         }
 
-        moveFocusToActivity(adjacentTopActivity);
+        final ActivityRecord mostRecentActivityInAdjacent = getMostRecentActivityInAdjacent(
+                activity);
+
+        moveFocusToActivity(mostRecentActivityInAdjacent);
         return !focusedWindow.isFocused();
     }
 
@@ -9736,7 +9772,7 @@
                 Slog.e(TAG, "Host window not found");
                 return;
             }
-            if (hostWindow.mInputChannel == null) {
+            if (hostWindow.mInputChannelToken == null) {
                 Slog.e(TAG, "Host window does not have an input channel");
                 return;
             }
diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
index 51d5bc0..092a751 100644
--- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
+++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
@@ -284,30 +284,28 @@
 
     private int runDisplayDensity(PrintWriter pw) throws RemoteException {
         String densityStr = getNextArg();
-        String option = getNextOption();
         String arg = getNextArg();
         int density;
         int displayId = Display.DEFAULT_DISPLAY;
-        if ("-d".equals(option) && arg != null) {
+        if ("-d".equals(densityStr) && arg != null) {
             try {
                 displayId = Integer.parseInt(arg);
             } catch (NumberFormatException e) {
                 getErrPrintWriter().println("Error: bad number " + e);
             }
-        } else if ("-u".equals(option) && arg != null) {
+            densityStr = getNextArg();
+        } else if ("-u".equals(densityStr) && arg != null) {
             displayId = mInterface.getDisplayIdByUniqueId(arg);
             if (displayId == Display.INVALID_DISPLAY) {
                 getErrPrintWriter().println("Error: the uniqueId is invalid ");
                 return -1;
             }
+            densityStr = getNextArg();
         }
 
         if (densityStr == null) {
             printInitialDisplayDensity(pw, displayId);
             return 0;
-        } else if ("-d".equals(densityStr)) {
-            printInitialDisplayDensity(pw, displayId);
-            return 0;
         } else if ("reset".equals(densityStr)) {
             density = -1;
         } else {
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index bc32b91..0093e9d 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -390,7 +390,10 @@
 
     private static boolean hasActivityLaunch(@NonNull WindowContainerTransaction wct) {
         for (int i = 0; i < wct.getHierarchyOps().size(); ++i) {
-            if (wct.getHierarchyOps().get(i).getType() == HIERARCHY_OP_TYPE_LAUNCH_TASK) {
+            final WindowContainerTransaction.HierarchyOp op = wct.getHierarchyOps().get(i);
+            if (op.getType() == HIERARCHY_OP_TYPE_LAUNCH_TASK
+                    || (op.getType() == HIERARCHY_OP_TYPE_PENDING_INTENT
+                            && op.getPendingIntent().isActivity())) {
                 return true;
             }
         }
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 12c5073..2bae0a8 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -437,7 +437,7 @@
             final ConfigurationChangeItem configurationChangeItem;
             synchronized (mLastReportedConfiguration) {
                 onConfigurationChangePreScheduled(mLastReportedConfiguration);
-                configurationChangeItem = ConfigurationChangeItem.obtain(
+                configurationChangeItem = new ConfigurationChangeItem(
                         mLastReportedConfiguration, mLastTopActivityDeviceId);
             }
             // Schedule immediately to make sure the app component (e.g. receiver, service) can get
@@ -1674,6 +1674,22 @@
         // Otherwise if other places send wpc.getConfiguration() to client, the configuration may
         // be ignored due to the seq is older.
         resolvedConfig.seq = newParentConfig.seq;
+
+        if (mConfigActivityRecord != null) {
+            // Let the activity decide whether to apply the size override.
+            return;
+        }
+        final DisplayContent displayContent = mAtm.mWindowManager != null
+                ? mAtm.mWindowManager.getDefaultDisplayContentLocked()
+                : null;
+        applySizeOverrideIfNeeded(
+                displayContent,
+                mInfo,
+                newParentConfig,
+                resolvedConfig,
+                false /* optsOutEdgeToEdge */,
+                false /* hasFixedRotationTransform */,
+                false /* hasCompatDisplayInsets */);
     }
 
     void dispatchConfiguration(@NonNull Configuration config) {
@@ -1705,8 +1721,8 @@
         }
 
         onConfigurationChangePreScheduled(config);
-        scheduleClientTransactionItem(thread, ConfigurationChangeItem.obtain(
-                config, mLastTopActivityDeviceId));
+        scheduleClientTransactionItem(
+                thread, new ConfigurationChangeItem(config, mLastTopActivityDeviceId));
     }
 
     private void onConfigurationChangePreScheduled(@NonNull Configuration config) {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index a36cff6..153d41b 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -103,6 +103,7 @@
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ANIM;
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS;
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_BACK_PREVIEW;
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_DIMMER;
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_FOCUS;
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_FOCUS_LIGHT;
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION;
@@ -637,7 +638,7 @@
     /**
      * Only populated if flag REMOVE_INPUT_CHANNEL_FROM_WINDOWSTATE is disabled.
      */
-    InputChannel mInputChannel;
+    private InputChannel mInputChannel;
 
     /**
      * The token will be assigned to {@link InputWindowHandle#token} if this window can receive
@@ -3784,7 +3785,7 @@
 
         markRedrawForSyncReported();
         getProcess().scheduleClientTransactionItem(
-                WindowStateResizeItem.obtain(mClient, mLastReportedFrames, reportDraw,
+                new WindowStateResizeItem(mClient, mLastReportedFrames, reportDraw,
                         mLastReportedConfiguration, mLastReportedInsetsState, forceRelayout,
                         alwaysConsumeSystemBars, displayId,
                         syncWithBuffers ? mSyncSeqId : -1, isDragResizing,
@@ -3854,7 +3855,7 @@
         fillInsetsState(mLastReportedInsetsState, false /* copySources */);
         fillInsetsSourceControls(mLastReportedActiveControls, false /* copyControls */);
         if (Flags.insetsControlChangedItem()) {
-            getProcess().scheduleClientTransactionItem(WindowStateInsetsControlChangeItem.obtain(
+            getProcess().scheduleClientTransactionItem(new WindowStateInsetsControlChangeItem(
                     mClient, mLastReportedInsetsState, mLastReportedActiveControls));
         } else {
             try {
@@ -5195,9 +5196,10 @@
     }
 
     private void applyDims() {
+        Task task = getTask();
         if (((mAttrs.flags & FLAG_DIM_BEHIND) != 0 || shouldDrawBlurBehind())
-                && (Dimmer.DIMMER_REFACTOR ? mWinAnimator.getShown() : isVisibleNow())
-                && !mHidden && mTransitionController.canApplyDim(getTask())) {
+                && mWinAnimator.getShown()
+                && !mHidden && mTransitionController.canApplyDim(task)) {
             // Only show the Dimmer when the following is satisfied:
             // 1. The window has the flag FLAG_DIM_BEHIND or blur behind is requested
             // 2. The WindowToken is not hidden so dims aren't shown when the window is exiting.
@@ -5210,10 +5212,31 @@
             // If the window is visible from surface flinger perspective (mWinAnimator.getShown())
             // but not window manager visible (!isVisibleNow()), it can still be the parent of the
             // dim, but can not create a new surface or continue a dim alone.
-            if (isVisibleNow()) {
-                getDimmer().adjustAppearance(this, dimAmount, blurRadius);
+            Dimmer dimmer;
+            WindowContainer<?> geometryParent = task;
+            if (Flags.useTasksDimOnly()) {
+                if (task != null) {
+                    geometryParent = task.getDimmerParent();
+                    dimmer = task.mDimmer;
+                } else {
+                    RootDisplayArea displayArea = getRootDisplayArea();
+                    geometryParent = displayArea;
+                    dimmer = displayArea != null ? displayArea.getDimmer() : null;
+                }
+                if (dimmer == null) {
+                    ProtoLog.e(WM_DEBUG_DIMMER, "WindowState %s does not have task or"
+                            + " display area for dimming", this);
+                    return;
+                }
+            } else {
+                dimmer = getDimmer();
             }
-            getDimmer().adjustRelativeLayer(this, -1 /* relativeLayer */);
+
+            if (isVisibleNow()) {
+                dimmer.adjustAppearance(this, dimAmount, blurRadius);
+            }
+            dimmer.adjustPosition(geometryParent,
+                    this /* relativeParent */, -1 /* relativeLayer */);
         }
     }
 
@@ -5277,17 +5300,12 @@
     void prepareSurfaces() {
         mIsDimming = false;
         if (mHasSurface) {
-            if (!Dimmer.DIMMER_REFACTOR) {
-                applyDims();
-            }
             updateSurfacePositionNonOrganized();
             // Send information to SurfaceFlinger about the priority of the current window.
             updateFrameRateSelectionPriorityIfNeeded();
             updateScaleIfNeeded();
             mWinAnimator.prepareSurfaceLocked(getSyncTransaction());
-            if (Dimmer.DIMMER_REFACTOR) {
-                applyDims();
-            }
+            applyDims();
         }
         super.prepareSurfaces();
     }
@@ -5355,7 +5373,7 @@
             // change then delay the position update until it has redrawn to avoid any flickers.
             final boolean isLetterboxedAndRelaunching = activityRecord != null
                     && activityRecord.areBoundsLetterboxed()
-                    && activityRecord.mLetterboxUiController
+                    && activityRecord.mAppCompatController.getAppCompatOrientationOverrides()
                         .getIsRelaunchingAfterRequestedOrientationChanged();
             if (surfaceResizedWithoutMoveAnimation || isLetterboxedAndRelaunching) {
                 applyWithNextDraw(mSetSurfacePositionConsumer);
diff --git a/services/core/java/com/android/server/wm/utils/DesktopModeFlagsUtil.java b/services/core/java/com/android/server/wm/utils/DesktopModeFlagsUtil.java
index 4211764..70c66de 100644
--- a/services/core/java/com/android/server/wm/utils/DesktopModeFlagsUtil.java
+++ b/services/core/java/com/android/server/wm/utils/DesktopModeFlagsUtil.java
@@ -43,20 +43,16 @@
     // All desktop mode related flags to be overridden by developer option toggle will be added here
     DESKTOP_WINDOWING_MODE(
             Flags::enableDesktopWindowingMode, /* shouldOverrideByDevOption= */ true),
-    WALLPAPER_ACTIVITY(
-            Flags::enableDesktopWindowingWallpaperActivity, /* shouldOverrideByDevOption= */ true);
+    DYNAMIC_INITIAL_BOUNDS(Flags::enableWindowingDynamicInitialBounds, true);
 
     private static final String TAG = "DesktopModeFlagsUtil";
-    private static final String SYSTEM_PROPERTY_OVERRIDE_KEY =
-            "sys.wmshell.desktopmode.dev_toggle_override";
-
     // Function called to obtain aconfig flag value.
     private final Supplier<Boolean> mFlagFunction;
     // Whether the flag state should be affected by developer option.
     private final boolean mShouldOverrideByDevOption;
 
     // Local cache for toggle override, which is initialized once on its first access. It needs to
-    // be refreshed only on reboots as overridden state takes effect on reboots.
+    // be refreshed only on reboots as overridden state is expected to take effect on reboots.
     private static ToggleOverride sCachedToggleOverride;
 
     DesktopModeFlagsUtil(Supplier<Boolean> flagFunction, boolean shouldOverrideByDevOption) {
@@ -67,9 +63,6 @@
     /**
      * Determines state of flag based on the actual flag and desktop mode developer option
      * overrides.
-     *
-     * Note: this method makes sure that a constant developer toggle overrides is read until
-     * reboot.
      */
     public boolean isEnabled(Context context) {
         if (!Flags.showDesktopWindowingDevOption()
@@ -102,49 +95,15 @@
     }
 
     /**
-     *  Returns {@link ToggleOverride} from a non-persistent system property if present. Otherwise
-     *  initializes the system property by reading Settings.Global.
+     *  Returns {@link ToggleOverride} from Settings.Global set by toggle.
      */
     private ToggleOverride getToggleOverrideFromSystem(Context context) {
-        // A non-persistent System Property is used to store override to ensure it remains
-        // constant till reboot.
-        String overrideProperty = System.getProperty(SYSTEM_PROPERTY_OVERRIDE_KEY, null);
-        ToggleOverride overrideFromSystemProperties = convertToToggleOverride(overrideProperty);
-
-        // If valid system property, return it
-        if (overrideFromSystemProperties != null) {
-            return overrideFromSystemProperties;
-        }
-
-        // Fallback when System Property is not present (just after reboot) or not valid (user
-        // manually changed the value): Read from Settings.Global
         int settingValue = Settings.Global.getInt(
                 context.getContentResolver(),
                 Settings.Global.DEVELOPMENT_OVERRIDE_DESKTOP_MODE_FEATURES,
                 OVERRIDE_UNSET.getSetting()
         );
-        ToggleOverride overrideFromSettingsGlobal =
-                ToggleOverride.fromSetting(settingValue, OVERRIDE_UNSET);
-        // Initialize System Property
-        System.setProperty(SYSTEM_PROPERTY_OVERRIDE_KEY, String.valueOf(settingValue));
-        return overrideFromSettingsGlobal;
-    }
-
-    /**
-     * Converts {@code intString} into {@link ToggleOverride}. Return {@code null} if
-     * {@code intString} does not correspond to a {@link ToggleOverride}.
-     */
-    private static @Nullable ToggleOverride convertToToggleOverride(
-            @Nullable String intString
-    ) {
-        if (intString == null) return null;
-        try {
-            int intValue = Integer.parseInt(intString);
-            return ToggleOverride.fromSetting(intValue, null);
-        } catch (NumberFormatException e) {
-            Log.w(TAG, "Unknown toggleOverride int " + intString);
-            return null;
-        }
+        return ToggleOverride.fromSetting(settingValue, OVERRIDE_UNSET);
     }
 
     /** Override state of desktop mode developer option toggle. */
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 3cd5f76..f1e94de 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -79,6 +79,7 @@
         "com_android_server_wm_TaskFpsCallbackController.cpp",
         "onload.cpp",
         ":lib_cachedAppOptimizer_native",
+        ":lib_freezer_native",
         ":lib_gameManagerService_native",
         ":lib_oomConnection_native",
         ":lib_anrTimer_native",
@@ -193,7 +194,7 @@
         "android.hardware.thermal-V2-ndk",
         "android.hardware.tv.input@1.0",
         "android.hardware.tv.input-V2-ndk",
-        "android.hardware.vibrator-V2-ndk",
+        "android.hardware.vibrator-V3-ndk",
         "android.hardware.vibrator@1.0",
         "android.hardware.vibrator@1.1",
         "android.hardware.vibrator@1.2",
@@ -241,6 +242,13 @@
 }
 
 filegroup {
+    name: "lib_freezer_native",
+    srcs: [
+        "com_android_server_am_Freezer.cpp",
+    ],
+}
+
+filegroup {
     name: "lib_gameManagerService_native",
     srcs: [
         "com_android_server_app_GameManagerService.cpp",
diff --git a/services/core/jni/OWNERS b/services/core/jni/OWNERS
index 1667e27..b622751 100644
--- a/services/core/jni/OWNERS
+++ b/services/core/jni/OWNERS
@@ -27,7 +27,8 @@
 per-file com_android_server_security_* = file:/core/java/android/security/OWNERS
 per-file com_android_server_tv_* = file:/media/java/android/media/tv/OWNERS
 per-file com_android_server_vibrator_* = file:/services/core/java/com/android/server/vibrator/OWNERS
-per-file com_android_server_am_CachedAppOptimizer.cpp = timmurray@google.com, edgararriaga@google.com, dualli@google.com, carmenjackson@google.com, philipcuadra@google.com
+per-file com_android_server_am_CachedAppOptimizer.cpp = file:/PERFORMANCE_OWNERS
+per-file com_android_server_am_Freezer.cpp = file:/PERFORMANCE_OWNERS
 per-file com_android_server_companion_virtual_InputController.cpp = file:/services/companion/java/com/android/server/companion/virtual/OWNERS
 
 # Memory
diff --git a/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp b/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
index 95e7b19..a91fd08 100644
--- a/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
+++ b/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
@@ -24,7 +24,6 @@
 #include <android-base/stringprintf.h>
 #include <android-base/unique_fd.h>
 #include <android_runtime/AndroidRuntime.h>
-#include <binder/IPCThreadState.h>
 #include <cutils/compiler.h>
 #include <dirent.h>
 #include <jni.h>
@@ -34,7 +33,6 @@
 #include <meminfo/procmeminfo.h>
 #include <meminfo/sysmeminfo.h>
 #include <nativehelper/JNIHelp.h>
-#include <processgroup/processgroup.h>
 #include <stddef.h>
 #include <stdio.h>
 #include <sys/mman.h>
@@ -63,10 +61,6 @@
 using VmaToAdviseFunc = std::function<int(const Vma&)>;
 using android::base::unique_fd;
 
-#define SYNC_RECEIVED_WHILE_FROZEN (1)
-#define ASYNC_RECEIVED_WHILE_FROZEN (2)
-#define TXNS_PENDING_WHILE_FROZEN (4)
-
 #define MAX_RW_COUNT (INT_MAX & kPageMask)
 
 // Defines the maximum amount of VMAs we can send per process_madvise syscall.
@@ -527,58 +521,6 @@
     compactProcessOrFallback(pid, compactionFlags);
 }
 
-static jint com_android_server_am_CachedAppOptimizer_freezeBinder(JNIEnv* env, jobject clazz,
-                                                                  jint pid, jboolean freeze,
-                                                                  jint timeout_ms) {
-    jint retVal = IPCThreadState::freeze(pid, freeze, timeout_ms);
-    if (retVal != 0 && retVal != -EAGAIN) {
-        jniThrowException(env, "java/lang/RuntimeException", "Unable to freeze/unfreeze binder");
-    }
-
-    return retVal;
-}
-
-static jint com_android_server_am_CachedAppOptimizer_getBinderFreezeInfo(JNIEnv *env,
-        jobject clazz, jint pid) {
-    uint32_t syncReceived = 0, asyncReceived = 0;
-
-    int error = IPCThreadState::getProcessFreezeInfo(pid, &syncReceived, &asyncReceived);
-
-    if (error < 0) {
-        jniThrowException(env, "java/lang/RuntimeException", strerror(error));
-    }
-
-    jint retVal = 0;
-
-    // bit 0 of sync_recv goes to bit 0 of retVal
-    retVal |= syncReceived & SYNC_RECEIVED_WHILE_FROZEN;
-    // bit 0 of async_recv goes to bit 1 of retVal
-    retVal |= (asyncReceived << 1) & ASYNC_RECEIVED_WHILE_FROZEN;
-    // bit 1 of sync_recv goes to bit 2 of retVal
-    retVal |= (syncReceived << 1) & TXNS_PENDING_WHILE_FROZEN;
-
-    return retVal;
-}
-
-static jstring com_android_server_am_CachedAppOptimizer_getFreezerCheckPath(JNIEnv* env,
-                                                                            jobject clazz) {
-    std::string path;
-
-    if (!getAttributePathForTask("FreezerState", getpid(), &path)) {
-        path = "";
-    }
-
-    return env->NewStringUTF(path.c_str());
-}
-
-static jboolean com_android_server_am_CachedAppOptimizer_isFreezerProfileValid(JNIEnv* env) {
-    uid_t uid = getuid();
-    pid_t pid = getpid();
-
-    return isProfileValidForProcess("Frozen", uid, pid) &&
-            isProfileValidForProcess("Unfrozen", uid, pid);
-}
-
 static const JNINativeMethod sMethods[] = {
         /* name, signature, funcPtr */
         {"cancelCompaction", "()V",
@@ -592,13 +534,7 @@
          (void*)com_android_server_am_CachedAppOptimizer_getMemoryFreedCompaction},
         {"compactSystem", "()V", (void*)com_android_server_am_CachedAppOptimizer_compactSystem},
         {"compactProcess", "(II)V", (void*)com_android_server_am_CachedAppOptimizer_compactProcess},
-        {"freezeBinder", "(IZI)I", (void*)com_android_server_am_CachedAppOptimizer_freezeBinder},
-        {"getBinderFreezeInfo", "(I)I",
-         (void*)com_android_server_am_CachedAppOptimizer_getBinderFreezeInfo},
-        {"getFreezerCheckPath", "()Ljava/lang/String;",
-         (void*)com_android_server_am_CachedAppOptimizer_getFreezerCheckPath},
-        {"isFreezerProfileValid", "()Z",
-         (void*)com_android_server_am_CachedAppOptimizer_isFreezerProfileValid}};
+};
 
 int register_android_server_am_CachedAppOptimizer(JNIEnv* env)
 {
diff --git a/services/core/jni/com_android_server_am_Freezer.cpp b/services/core/jni/com_android_server_am_Freezer.cpp
new file mode 100644
index 0000000..8148728
--- /dev/null
+++ b/services/core/jni/com_android_server_am_Freezer.cpp
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "Freezer"
+//#define LOG_NDEBUG 0
+#define ATRACE_TAG ATRACE_TAG_ACTIVITY_MANAGER
+
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+#include <binder/IPCThreadState.h>
+#include <nativehelper/JNIHelp.h>
+#include <processgroup/processgroup.h>
+
+namespace android {
+namespace {
+
+// Binder status bit flags.
+static const int SYNC_RECEIVED_WHILE_FROZEN = 1;
+static const int ASYNC_RECEIVED_WHILE_FROZEN = 2;
+static const int TXNS_PENDING_WHILE_FROZEN = 4;
+
+jint freezeBinder(JNIEnv* env, jobject, jint pid, jboolean freeze, jint timeout_ms) {
+    jint retVal = IPCThreadState::freeze(pid, freeze, timeout_ms);
+    if (retVal != 0 && retVal != -EAGAIN) {
+        jniThrowException(env, "java/lang/RuntimeException", "Unable to freeze/unfreeze binder");
+    }
+
+    return retVal;
+}
+
+jint getBinderFreezeInfo(JNIEnv *env, jobject, jint pid) {
+    uint32_t syncReceived = 0, asyncReceived = 0;
+
+    int error = IPCThreadState::getProcessFreezeInfo(pid, &syncReceived, &asyncReceived);
+
+    if (error < 0) {
+        jniThrowException(env, "java/lang/RuntimeException", strerror(error));
+    }
+
+    jint retVal = 0;
+
+    // bit 0 of sync_recv goes to bit 0 of retVal
+    retVal |= syncReceived & SYNC_RECEIVED_WHILE_FROZEN;
+    // bit 0 of async_recv goes to bit 1 of retVal
+    retVal |= (asyncReceived << 1) & ASYNC_RECEIVED_WHILE_FROZEN;
+    // bit 1 of sync_recv goes to bit 2 of retVal
+    retVal |= (syncReceived << 1) & TXNS_PENDING_WHILE_FROZEN;
+
+    return retVal;
+}
+
+bool isFreezerSupported(JNIEnv *env, jclass) {
+    std::string path;
+    if (!getAttributePathForTask("FreezerState", getpid(), &path)) {
+        ALOGI("No attribute for FreezerState");
+        return false;
+    }
+    base::unique_fd fid(open(path.c_str(), O_RDONLY));
+    if (fid < 0) {
+        ALOGI("Cannot open freezer path \"%s\": %s", path.c_str(), strerror(errno));
+        return false;
+    }
+
+    char state;
+    if (::read(fid, &state, 1) != 1) {
+        ALOGI("Failed to read freezer state: %s", strerror(errno));
+        return false;
+    }
+    if (state != '1' && state != '0') {
+        ALOGE("Unexpected value in cgroup.freeze: %d", state);
+        return false;
+    }
+
+    uid_t uid = getuid();
+    pid_t pid = getpid();
+
+    uint32_t syncReceived = 0, asyncReceived = 0;
+    int error = IPCThreadState::getProcessFreezeInfo(pid, &syncReceived, &asyncReceived);
+    if (error < 0) {
+        ALOGE("Unable to read freezer info: %s", strerror(errno));
+        return false;
+    }
+
+    if (!isProfileValidForProcess("Frozen", uid, pid)
+            || !isProfileValidForProcess("Unfrozen", uid, pid)) {
+        ALOGE("Missing freezer profiles");
+        return false;
+    }
+
+    return true;
+}
+
+static const JNINativeMethod sMethods[] = {
+    {"nativeIsFreezerSupported",    "()Z",       (void*) isFreezerSupported },
+    {"nativeFreezeBinder",          "(IZI)I",    (void*) freezeBinder },
+    {"nativeGetBinderFreezeInfo",   "(I)I",      (void*) getBinderFreezeInfo },
+};
+
+} // end of anonymous namespace
+
+int register_android_server_am_Freezer(JNIEnv* env)
+{
+    char const *className = "com/android/server/am/Freezer";
+    return jniRegisterNativeMethods(env, className, sMethods, NELEM(sMethods));
+}
+
+} // end of namespace android
diff --git a/services/core/jni/com_android_server_vibrator_VibratorController.cpp b/services/core/jni/com_android_server_vibrator_VibratorController.cpp
index 2804a10..f12930a 100644
--- a/services/core/jni/com_android_server_vibrator_VibratorController.cpp
+++ b/services/core/jni/com_android_server_vibrator_VibratorController.cpp
@@ -17,7 +17,10 @@
 #define LOG_TAG "VibratorController"
 
 #include <aidl/android/hardware/vibrator/IVibrator.h>
+#include <android/binder_parcel.h>
+#include <android/binder_parcel_jni.h>
 #include <android/hardware/vibrator/1.3/IVibrator.h>
+#include <android/persistable_bundle_aidl.h>
 #include <nativehelper/JNIHelp.h>
 #include <utils/Log.h>
 #include <utils/misc.h>
@@ -32,6 +35,8 @@
 namespace V1_3 = android::hardware::vibrator::V1_3;
 namespace Aidl = aidl::android::hardware::vibrator;
 
+using aidl::android::os::PersistableBundle;
+
 namespace android {
 
 static JavaVM* sJvm = nullptr;
@@ -95,7 +100,7 @@
         return nullptr;
     }
     auto result = manager->getVibrator(vibratorId);
-    return result.isOk() ? std::move(result.value()) : nullptr;
+    return result.isOk() ? result.value() : nullptr;
 }
 
 class VibratorControllerWrapper {
@@ -192,6 +197,29 @@
     return effect;
 }
 
+static Aidl::VendorEffect vendorEffectFromJavaParcel(JNIEnv* env, jobject vendorData,
+                                                     jlong strength, jfloat scale) {
+    PersistableBundle bundle;
+    if (AParcel* parcel = AParcel_fromJavaParcel(env, vendorData); parcel != nullptr) {
+        if (binder_status_t status = bundle.readFromParcel(parcel); status == STATUS_OK) {
+            AParcel_delete(parcel);
+        } else {
+            jniThrowExceptionFmt(env, "android/os/BadParcelableException",
+                                 "Failed to readFromParcel, status %d (%s)", status,
+                                 strerror(-status));
+        }
+    } else {
+        jniThrowExceptionFmt(env, "android/os/BadParcelableException",
+                             "Failed to AParcel_fromJavaParcel, for nullptr");
+    }
+
+    Aidl::VendorEffect effect;
+    effect.vendorData = bundle;
+    effect.strength = static_cast<Aidl::EffectStrength>(strength);
+    effect.scale = static_cast<float>(scale);
+    return effect;
+}
+
 static void destroyNativeWrapper(void* ptr) {
     VibratorControllerWrapper* wrapper = reinterpret_cast<VibratorControllerWrapper*>(ptr);
     if (wrapper) {
@@ -289,6 +317,23 @@
     return result.isOk() ? result.value().count() : (result.isUnsupported() ? 0 : -1);
 }
 
+static jlong vibratorPerformVendorEffect(JNIEnv* env, jclass /* clazz */, jlong ptr,
+                                         jobject vendorData, jlong strength, jfloat scale,
+                                         jlong vibrationId) {
+    VibratorControllerWrapper* wrapper = reinterpret_cast<VibratorControllerWrapper*>(ptr);
+    if (wrapper == nullptr) {
+        ALOGE("vibratorPerformVendorEffect failed because native wrapper was not initialized");
+        return -1;
+    }
+    Aidl::VendorEffect effect = vendorEffectFromJavaParcel(env, vendorData, strength, scale);
+    auto callback = wrapper->createCallback(vibrationId);
+    auto performVendorEffectFn = [&effect, &callback](vibrator::HalWrapper* hal) {
+        return hal->performVendorEffect(effect, callback);
+    };
+    auto result = wrapper->halCall<void>(performVendorEffectFn, "performVendorEffect");
+    return result.isOk() ? std::numeric_limits<int64_t>::max() : (result.isUnsupported() ? 0 : -1);
+}
+
 static jlong vibratorPerformComposedEffect(JNIEnv* env, jclass /* clazz */, jlong ptr,
                                            jobjectArray composition, jlong vibrationId) {
     VibratorControllerWrapper* wrapper = reinterpret_cast<VibratorControllerWrapper*>(ptr);
@@ -466,6 +511,7 @@
         {"off", "(J)V", (void*)vibratorOff},
         {"setAmplitude", "(JF)V", (void*)vibratorSetAmplitude},
         {"performEffect", "(JJJJ)J", (void*)vibratorPerformEffect},
+        {"performVendorEffect", "(JLandroid/os/Parcel;JFJ)J", (void*)vibratorPerformVendorEffect},
         {"performComposedEffect", "(J[Landroid/os/vibrator/PrimitiveSegment;J)J",
          (void*)vibratorPerformComposedEffect},
         {"performPwleEffect", "(J[Landroid/os/vibrator/RampSegment;IJ)J",
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index 314ff9d..3c55d18 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -54,6 +54,7 @@
 int register_android_hardware_display_DisplayViewport(JNIEnv* env);
 int register_android_server_am_OomConnection(JNIEnv* env);
 int register_android_server_am_CachedAppOptimizer(JNIEnv* env);
+int register_android_server_am_Freezer(JNIEnv* env);
 int register_android_server_am_LowMemDetector(JNIEnv* env);
 int register_android_server_utils_AnrTimer(JNIEnv *env);
 int register_com_android_server_soundtrigger_middleware_AudioSessionProviderImpl(JNIEnv* env);
@@ -118,6 +119,7 @@
     register_android_hardware_display_DisplayViewport(env);
     register_android_server_am_OomConnection(env);
     register_android_server_am_CachedAppOptimizer(env);
+    register_android_server_am_Freezer(env);
     register_android_server_am_LowMemDetector(env);
     register_android_server_utils_AnrTimer(env);
     register_com_android_server_soundtrigger_middleware_AudioSessionProviderImpl(env);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 7cb8ace..9ed645b 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -3620,8 +3620,6 @@
             }
             default -> {
                 Slogf.wtf(LOG_TAG, "Unhandled password complexity: " + passwordComplexity);
-                // The following line is unreachable as Slogf.wtf crashes the process.
-                // But we need this to avoid compilation error missing return statement.
                 return DEVICE_POLICY_STATE__PASSWORD_COMPLEXITY__COMPLEXITY_UNSPECIFIED;
             }
         }
@@ -13636,7 +13634,28 @@
             setBackwardCompatibleUserRestriction(
                     caller, admin, key, enabledFromThisOwner, parent);
         }
-        logUserRestrictionCall(key, enabledFromThisOwner, parent, caller);
+        logUserRestrictionCall(key, enabledFromThisOwner, parent, caller, affectedUserId);
+    }
+
+    @Override
+    public void setUserRestrictionForUser(
+            @NonNull String systemEntity, String key, boolean enabled, @UserIdInt int targetUser) {
+        Objects.requireNonNull(systemEntity);
+
+        CallerIdentity caller = getCallerIdentity();
+        if (caller.getUid() != Process.SYSTEM_UID) {
+            throw new SecurityException("Only system services can call setUserRestrictionForUser"
+                    + " on a target user: " + targetUser);
+        }
+        if (VERBOSE_LOG) {
+            Slogf.v(LOG_TAG, "Creating SystemEnforcingAdmin %s for calling package %s",
+                    systemEntity, caller.getPackageName());
+        }
+        EnforcingAdmin admin = EnforcingAdmin.createSystemEnforcingAdmin(systemEntity);
+
+        setLocalUserRestrictionInternal(admin, key, enabled, targetUser);
+
+        logUserRestrictionCall(key, enabled, /* parent= */ false, caller, targetUser);
     }
 
     private void checkAdminCanSetRestriction(CallerIdentity caller, boolean parent, String key) {
@@ -13757,7 +13776,8 @@
 
         setGlobalUserRestrictionInternal(admin, key, /* enabled= */ true);
 
-        logUserRestrictionCall(key, /* enabled= */ true, /* parent= */ false, caller);
+        logUserRestrictionCall(key, /* enabled= */ true, /* parent= */ false, caller,
+                UserHandle.USER_ALL);
     }
     private void setLocalUserRestrictionInternal(
             EnforcingAdmin admin, String key, boolean enabled, int userId) {
@@ -13793,7 +13813,7 @@
     }
 
     private void logUserRestrictionCall(
-            String key, boolean enabled, boolean parent, CallerIdentity caller) {
+            String key, boolean enabled, boolean parent, CallerIdentity caller, int targetUserId) {
         final int eventId = enabled
                 ? DevicePolicyEnums.ADD_USER_RESTRICTION
                 : DevicePolicyEnums.REMOVE_USER_RESTRICTION;
@@ -13809,8 +13829,9 @@
             SecurityLog.writeEvent(eventTag, caller.getPackageName(), caller.getUserId(), key);
         }
 
-        Slogf.i(LOG_TAG, "Changing user restriction %s to: %b caller: %s",
-                key, enabled, caller.toString());
+        Slogf.i(LOG_TAG, "Changing user restriction %s on %s to: %b caller: %s",
+                key, (targetUserId == UserHandle.USER_ALL ? "all users" : ("user " + targetUserId)),
+                enabled, caller.toString());
     }
 
     @Override
@@ -13975,6 +13996,8 @@
         USER_RESTRICTION_PERMISSIONS.put(
                 UserManager.DISALLOW_ULTRA_WIDEBAND_RADIO, new String[]{MANAGE_DEVICE_POLICY_NEARBY_COMMUNICATION});
         USER_RESTRICTION_PERMISSIONS.put(
+                UserManager.DISALLOW_NEAR_FIELD_COMMUNICATION_RADIO, new String[]{MANAGE_DEVICE_POLICY_NEARBY_COMMUNICATION});
+        USER_RESTRICTION_PERMISSIONS.put(
                 UserManager.DISALLOW_UNIFIED_PASSWORD, new String[]{MANAGE_DEVICE_POLICY_LOCK_CREDENTIALS});
         USER_RESTRICTION_PERMISSIONS.put(
                 UserManager.DISALLOW_UNINSTALL_APPS, new String[]{MANAGE_DEVICE_POLICY_APPS_CONTROL});
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/EnforcingAdmin.java b/services/devicepolicy/java/com/android/server/devicepolicy/EnforcingAdmin.java
index 02590f9..e65e513 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/EnforcingAdmin.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/EnforcingAdmin.java
@@ -21,7 +21,6 @@
 import android.app.admin.Authority;
 import android.app.admin.DeviceAdminAuthority;
 import android.app.admin.DpcAuthority;
-import android.app.admin.PackagePermissionPolicyKey;
 import android.app.admin.RoleAuthority;
 import android.app.admin.UnknownAuthority;
 import android.content.ComponentName;
@@ -57,23 +56,29 @@
     static final String TAG = "EnforcingAdmin";
 
     static final String ROLE_AUTHORITY_PREFIX = "role:";
+    static final String SYSTEM_AUTHORITY_PREFIX = "system:";
     static final String DPC_AUTHORITY = "enterprise";
     static final String DEVICE_ADMIN_AUTHORITY = "device_admin";
     static final String DEFAULT_AUTHORITY = "default";
 
     private static final String ATTR_PACKAGE_NAME = "package-name";
+    private static final String ATTR_SYSTEM_ENTITY = "system-entity";
     private static final String ATTR_CLASS_NAME = "class-name";
     private static final String ATTR_AUTHORITIES = "authorities";
     private static final String ATTR_AUTHORITIES_SEPARATOR = ";";
     private static final String ATTR_USER_ID = "user-id";
     private static final String ATTR_IS_ROLE = "is-role";
+    private static final String ATTR_IS_SYSTEM = "is-system";
 
     private final String mPackageName;
+    // Name of the system entity. Only used when mIsSystemAuthority is true.
+    private final String mSystemEntity;
     // This is needed for DPCs and active admins
     private final ComponentName mComponentName;
     private Set<String> mAuthorities;
     private final int mUserId;
     private final boolean mIsRoleAuthority;
+    private final boolean mIsSystemAuthority;
     private final ActiveAdmin mActiveAdmin;
 
     static EnforcingAdmin createEnforcingAdmin(@NonNull String packageName, int userId,
@@ -106,6 +111,11 @@
                 userId, activeAdmin);
     }
 
+    static EnforcingAdmin createSystemEnforcingAdmin(@NonNull String systemEntity) {
+        Objects.requireNonNull(systemEntity);
+        return new EnforcingAdmin(systemEntity);
+    }
+
     static EnforcingAdmin createEnforcingAdmin(android.app.admin.EnforcingAdmin admin) {
         Objects.requireNonNull(admin);
         Authority authority = admin.getAuthority();
@@ -127,6 +137,7 @@
                     /* activeAdmin = */ null,
                     /* isRoleAuthority = */ true);
         }
+        // TODO(b/324899199): Consider supporting android.app.admin.SystemAuthority.
         return new EnforcingAdmin(admin.getPackageName(), admin.getComponentName(),
                 Set.of(), admin.getUserHandle().getIdentifier(),
                 /* activeAdmin = */ null);
@@ -159,9 +170,11 @@
         Objects.requireNonNull(packageName);
         Objects.requireNonNull(authorities);
 
-        // Role authorities should not be using this constructor
+        // Role/System authorities should not be using this constructor
         mIsRoleAuthority = false;
+        mIsSystemAuthority = false;
         mPackageName = packageName;
+        mSystemEntity = null;
         mComponentName = componentName;
         mAuthorities = new HashSet<>(authorities);
         mUserId = userId;
@@ -173,7 +186,9 @@
 
         // Only role authorities use this constructor.
         mIsRoleAuthority = true;
+        mIsSystemAuthority = false;
         mPackageName = packageName;
+        mSystemEntity = null;
         mUserId = userId;
         mComponentName = null;
         // authorities will be loaded when needed
@@ -181,6 +196,21 @@
         mActiveAdmin = activeAdmin;
     }
 
+    /** Constructor for System authorities. */
+    private EnforcingAdmin(@NonNull String systemEntity) {
+        Objects.requireNonNull(systemEntity);
+
+        // Only system authorities use this constructor.
+        mIsSystemAuthority = true;
+        mIsRoleAuthority = false;
+        mPackageName = null;
+        mSystemEntity = systemEntity;
+        mUserId = UserHandle.USER_SYSTEM;
+        mComponentName = null;
+        mAuthorities = getSystemAuthority(systemEntity);
+        mActiveAdmin = null;
+    }
+
     private EnforcingAdmin(
             String packageName, @Nullable ComponentName componentName, Set<String> authorities,
             int userId, @Nullable ActiveAdmin activeAdmin, boolean isRoleAuthority) {
@@ -188,7 +218,9 @@
         Objects.requireNonNull(authorities);
 
         mIsRoleAuthority = isRoleAuthority;
+        mIsSystemAuthority = false;
         mPackageName = packageName;
+        mSystemEntity = null;
         mComponentName = componentName;
         mAuthorities = new HashSet<>(authorities);
         mUserId = userId;
@@ -204,6 +236,18 @@
         return authorities.isEmpty() ? Set.of(DEFAULT_AUTHORITY) : authorities;
     }
 
+    /**
+     * Returns a set of authorities for system authority.
+     *
+     * <p>Note that a system authority enforcing admin has only one authority that has the package
+     * name of the calling system service. Therefore, the returned set always contains one element.
+     */
+    private static Set<String> getSystemAuthority(String systemEntity) {
+        Set<String> authorities = new HashSet<>();
+        authorities.add(SYSTEM_AUTHORITY_PREFIX + systemEntity);
+        return authorities;
+    }
+
     // TODO(b/259042794): move this logic to RoleManagerLocal
     private static Set<String> getRoles(String packageName, int userId) {
         RoleManagerLocal roleManagerLocal = LocalManagerRegistry.getManager(
@@ -264,6 +308,7 @@
         } else if (mAuthorities.contains(DEVICE_ADMIN_AUTHORITY)) {
             authority = DeviceAdminAuthority.DEVICE_ADMIN_AUTHORITY;
         } else {
+            // For now, System Authority returns UNKNOWN_AUTHORITY.
             authority = UnknownAuthority.UNKNOWN_AUTHORITY;
         }
         return new android.app.admin.EnforcingAdmin(
@@ -291,8 +336,10 @@
         if (o == null || getClass() != o.getClass()) return false;
         EnforcingAdmin other = (EnforcingAdmin) o;
         return Objects.equals(mPackageName, other.mPackageName)
+                && Objects.equals(mSystemEntity, other.mSystemEntity)
                 && Objects.equals(mComponentName, other.mComponentName)
                 && Objects.equals(mIsRoleAuthority, other.mIsRoleAuthority)
+                && (mIsSystemAuthority == other.mIsSystemAuthority)
                 && hasMatchingAuthorities(this, other);
     }
 
@@ -307,6 +354,8 @@
     public int hashCode() {
         if (mIsRoleAuthority) {
             return Objects.hash(mPackageName, mUserId);
+        } else if (mIsSystemAuthority) {
+            return Objects.hash(mSystemEntity);
         } else {
             return Objects.hash(
                     mComponentName == null ? mPackageName : mComponentName,
@@ -318,8 +367,12 @@
     void saveToXml(TypedXmlSerializer serializer) throws IOException {
         serializer.attribute(/* namespace= */ null, ATTR_PACKAGE_NAME, mPackageName);
         serializer.attributeBoolean(/* namespace= */ null, ATTR_IS_ROLE, mIsRoleAuthority);
+        serializer.attributeBoolean(/* namespace= */ null, ATTR_IS_SYSTEM, mIsSystemAuthority);
         serializer.attributeInt(/* namespace= */ null, ATTR_USER_ID, mUserId);
-        if (!mIsRoleAuthority) {
+        if (mIsSystemAuthority) {
+            serializer.attribute(/* namespace= */ null, ATTR_SYSTEM_ENTITY, mSystemEntity);
+        }
+        if (!mIsRoleAuthority && !mIsSystemAuthority) {
             if (mComponentName != null) {
                 serializer.attribute(
                         /* namespace= */ null, ATTR_CLASS_NAME, mComponentName.getClassName());
@@ -336,7 +389,10 @@
     static EnforcingAdmin readFromXml(TypedXmlPullParser parser)
             throws XmlPullParserException {
         String packageName = parser.getAttributeValue(/* namespace= */ null, ATTR_PACKAGE_NAME);
+        String systemEntity = parser.getAttributeValue(/* namespace= */ null, ATTR_SYSTEM_ENTITY);
         boolean isRoleAuthority = parser.getAttributeBoolean(/* namespace= */ null, ATTR_IS_ROLE);
+        boolean isSystemAuthority = parser.getAttributeBoolean(
+                /* namespace= */ null, ATTR_IS_SYSTEM, /* defaultValue= */ false);
         String authoritiesStr = parser.getAttributeValue(/* namespace= */ null, ATTR_AUTHORITIES);
         int userId = parser.getAttributeInt(/* namespace= */ null, ATTR_USER_ID);
 
@@ -348,6 +404,13 @@
             }
             // TODO(b/281697976): load active admin
             return new EnforcingAdmin(packageName, userId, null);
+        } else if (isSystemAuthority) {
+            if (systemEntity == null) {
+                Slogf.wtf(TAG, "Error parsing EnforcingAdmin with SystemAuthority, "
+                        + "systemEntity is null.");
+                return null;
+            }
+            return new EnforcingAdmin(systemEntity);
         } else {
             if (packageName == null || authoritiesStr == null) {
                 Slogf.wtf(TAG, "Error parsing EnforcingAdmin, packageName is "
@@ -381,6 +444,10 @@
         sb.append(mUserId);
         sb.append(", mIsRoleAuthority= ");
         sb.append(mIsRoleAuthority);
+        sb.append(", mIsSystemAuthority= ");
+        sb.append(mIsSystemAuthority);
+        sb.append(", mSystemEntity = ");
+        sb.append(mSystemEntity);
         sb.append(" }");
         return sb.toString();
     }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java
index 84d2b7f..8e3248e 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java
@@ -538,6 +538,9 @@
         USER_RESTRICTION_FLAGS.put(
                 UserManager.DISALLOW_ULTRA_WIDEBAND_RADIO, POLICY_FLAG_GLOBAL_ONLY_POLICY);
         USER_RESTRICTION_FLAGS.put(
+                UserManager.DISALLOW_NEAR_FIELD_COMMUNICATION_RADIO,
+                POLICY_FLAG_GLOBAL_ONLY_POLICY);
+        USER_RESTRICTION_FLAGS.put(
                 UserManager.DISALLOW_SIM_GLOBALLY,
                 POLICY_FLAG_GLOBAL_ONLY_POLICY);
         USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_ASSIST_CONTENT, /* flags= */ 0);
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index db4b171..9e8811f 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -153,6 +153,7 @@
 import com.android.server.contextualsearch.ContextualSearchManagerService;
 import com.android.server.coverage.CoverageService;
 import com.android.server.cpu.CpuMonitorService;
+import com.android.server.crashrecovery.CrashRecoveryModule;
 import com.android.server.credentials.CredentialManagerService;
 import com.android.server.criticalevents.CriticalEventLog;
 import com.android.server.devicepolicy.DevicePolicyManagerService;
@@ -381,8 +382,6 @@
                     + "OnDevicePersonalizationSystemService$Lifecycle";
     private static final String UPDATABLE_DEVICE_CONFIG_SERVICE_CLASS =
             "com.android.server.deviceconfig.DeviceConfigInit$Lifecycle";
-    private static final String CRASHRECOVERY_MODULE_LIFECYCLE_CLASS =
-            "com.android.server.crashrecovery.CrashRecoveryModule$Lifecycle";
 
 
     /*
@@ -2939,7 +2938,7 @@
 
         if (Flags.refactorCrashrecovery()) {
             t.traceBegin("StartCrashRecoveryModule");
-            mSystemServiceManager.startService(CRASHRECOVERY_MODULE_LIFECYCLE_CLASS);
+            mSystemServiceManager.startService(CrashRecoveryModule.Lifecycle.class);
             t.traceEnd();
         } else {
             if (Flags.recoverabilityDetection()) {
diff --git a/services/permission/java/com/android/server/permission/access/util/AtomicFileExtensions.kt b/services/permission/java/com/android/server/permission/access/util/AtomicFileExtensions.kt
index 996daf5..95ee958 100644
--- a/services/permission/java/com/android/server/permission/access/util/AtomicFileExtensions.kt
+++ b/services/permission/java/com/android/server/permission/access/util/AtomicFileExtensions.kt
@@ -19,6 +19,7 @@
 import android.os.FileUtils
 import android.util.AtomicFile
 import android.util.Slog
+import com.android.server.security.FileIntegrity;
 import java.io.File
 import java.io.FileInputStream
 import java.io.FileNotFoundException
@@ -49,6 +50,7 @@
 inline fun AtomicFile.writeWithReserveCopy(block: (FileOutputStream) -> Unit) {
     writeInlined(block)
     val reserveFile = File(baseFile.parentFile, baseFile.name + ".reservecopy")
+    reserveFile.delete()
     try {
         FileInputStream(baseFile).use { inputStream ->
             FileOutputStream(reserveFile).use { outputStream ->
@@ -59,6 +61,12 @@
     } catch (e: Exception) {
         Slog.e("AccessPersistence", "Failed to write $reserveFile", e)
     }
+    try {
+        FileIntegrity.setUpFsVerity(baseFile)
+        FileIntegrity.setUpFsVerity(reserveFile)
+    } catch (e: Exception) {
+        Slog.e("AccessPersistence", "Failed to verity-protect runtime-permissions", e)
+    }
 }
 
 /** Write to an [AtomicFile] and close everything safely when done. */
diff --git a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
index 3ed6ad7..dab3978 100644
--- a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
+++ b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
@@ -354,6 +354,9 @@
 
     private static void createAndUploadReport(ProfcollectForwardingService pfs) {
         BackgroundThread.get().getThreadHandler().post(() -> {
+            if (pfs.mIProfcollect == null) {
+                return;
+            }
             String reportName;
             try {
                 reportName = pfs.mIProfcollect.report(pfs.mUsageSetting) + ".zip";
@@ -398,17 +401,19 @@
                 if (randomNum >= traceFrequency) {
                     return;
                 }
-                // For a small percentage a traces, we collect the initialization behavior.
-                boolean traceInitialization = ThreadLocalRandom.current().nextInt(10) < 1;
-                int traceDelay = traceInitialization ? 0 : 1000;
-                String traceTag = traceInitialization ? "camera_init" : "camera";
-                BackgroundThread.get().getThreadHandler().postDelayed(() -> {
+                final int traceDuration = 5000;
+                final String traceTag = "camera";
+                BackgroundThread.get().getThreadHandler().post(() -> {
+                    if (mIProfcollect == null) {
+                        return;
+                    }
                     try {
-                        mIProfcollect.trace_process(traceTag, "android.hardware.camera.provider");
+                        mIProfcollect.trace_process(traceTag, "android.hardware.camera.provider",
+                                traceDuration);
                     } catch (RemoteException e) {
                         Log.e(LOG_TAG, "Failed to initiate trace: " + e.getMessage());
                     }
-                }, traceDelay);
+                });
             }
         }, null);
     }
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java
index 9e46f2f..8ae4f9a 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java
@@ -41,6 +41,7 @@
 
 import static java.util.Objects.requireNonNull;
 
+import android.annotation.Nullable;
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -50,6 +51,7 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import com.android.dx.mockito.inline.extended.ExtendedMockito;
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.inputmethod.InputBindResult;
 import com.android.internal.inputmethod.SoftInputShowHideReason;
 import com.android.internal.inputmethod.StartInputFlags;
@@ -70,15 +72,12 @@
 public class DefaultImeVisibilityApplierTest extends InputMethodManagerServiceTestBase {
     private DefaultImeVisibilityApplier mVisibilityApplier;
 
-    private int mUserId = 0;
-
     @Before
     public void setUp() throws RemoteException {
         super.setUp();
         synchronized (ImfLock.class) {
             mVisibilityApplier = mInputMethodManagerService.getVisibilityApplierLocked();
-            mUserId = mInputMethodManagerService.getCurrentImeUserIdLocked();
-            mInputMethodManagerService.setAttachedClientForTesting(requireNonNull(
+            setAttachedClientLocked(requireNonNull(
                     mInputMethodManagerService.getClientStateLocked(mMockInputMethodClient)));
         }
     }
@@ -171,7 +170,9 @@
         // Init a IME target client on the secondary display to show IME.
         mInputMethodManagerService.addClient(mMockInputMethodClient, mMockRemoteInputConnection,
                 10 /* selfReportedDisplayId */);
-        mInputMethodManagerService.setAttachedClientForTesting(null);
+        synchronized (ImfLock.class) {
+            setAttachedClientLocked(null);
+        }
         startInputOrWindowGainedFocus(mWindowToken, SOFT_INPUT_STATE_ALWAYS_VISIBLE);
 
         final var statsToken = ImeTracker.Token.empty();
@@ -209,7 +210,9 @@
 
     @Test
     public void testApplyImeVisibility_hideImeWhenUnbinding() {
-        mInputMethodManagerService.setAttachedClientForTesting(null);
+        synchronized (ImfLock.class) {
+            setAttachedClientLocked(null);
+        }
         startInputOrWindowGainedFocus(mWindowToken, SOFT_INPUT_STATE_ALWAYS_VISIBLE);
         ExtendedMockito.spyOn(mVisibilityApplier);
 
@@ -236,6 +239,11 @@
         }
     }
 
+    @GuardedBy("ImfLock.class")
+    private void setAttachedClientLocked(@Nullable ClientState cs) {
+        mInputMethodManagerService.getUserData(mUserId).mCurClient = cs;
+    }
+
     private InputBindResult startInputOrWindowGainedFocus(IBinder windowToken, int softInputMode) {
         return mInputMethodManagerService.startInputOrWindowGainedFocus(
                 StartInputReason.WINDOW_FOCUS_GAIN /* startInputReason */,
@@ -248,7 +256,7 @@
                 mMockRemoteInputConnection /* inputConnection */,
                 mMockRemoteAccessibilityInputConnection /* remoteAccessibilityInputConnection */,
                 mTargetSdkVersion /* unverifiedTargetSdkVersion */,
-                mCallingUserId /* userId */,
+                mUserId /* userId */,
                 mMockImeOnBackInvokedDispatcher /* imeDispatcher */);
     }
 }
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ImeVisibilityStateComputerTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ImeVisibilityStateComputerTest.java
index 337d5c1..dd3b33e 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ImeVisibilityStateComputerTest.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ImeVisibilityStateComputerTest.java
@@ -49,6 +49,7 @@
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.server.wm.ImeTargetChangeListener;
 import com.android.server.wm.WindowManagerInternal;
 
@@ -93,33 +94,37 @@
 
     @Test
     public void testRequestImeVisibility_showImplicit() {
-        initImeTargetWindowState(mWindowToken);
-        boolean res = mComputer.onImeShowFlags(ImeTracker.Token.empty(),
-                InputMethodManager.SHOW_IMPLICIT);
-        mComputer.requestImeVisibility(mWindowToken, res);
+        synchronized (ImfLock.class) {
+            initImeTargetWindowState(mWindowToken);
+            boolean res = mComputer.onImeShowFlags(ImeTracker.Token.empty(),
+                    InputMethodManager.SHOW_IMPLICIT);
+            mComputer.requestImeVisibility(mWindowToken, res);
 
-        final ImeTargetWindowState state = mComputer.getWindowStateOrNull(mWindowToken);
-        assertThat(state).isNotNull();
-        assertThat(state.hasEditorFocused()).isTrue();
-        assertThat(state.getSoftInputModeState()).isEqualTo(SOFT_INPUT_STATE_UNCHANGED);
-        assertThat(state.isRequestedImeVisible()).isTrue();
+            final ImeTargetWindowState state = mComputer.getWindowStateOrNull(mWindowToken);
+            assertThat(state).isNotNull();
+            assertThat(state.hasEditorFocused()).isTrue();
+            assertThat(state.getSoftInputModeState()).isEqualTo(SOFT_INPUT_STATE_UNCHANGED);
+            assertThat(state.isRequestedImeVisible()).isTrue();
 
-        assertThat(mComputer.mRequestedShowExplicitly).isFalse();
+            assertThat(mComputer.mRequestedShowExplicitly).isFalse();
+        }
     }
 
     @Test
     public void testRequestImeVisibility_showExplicit() {
-        initImeTargetWindowState(mWindowToken);
-        boolean res = mComputer.onImeShowFlags(ImeTracker.Token.empty(), 0 /* showFlags */);
-        mComputer.requestImeVisibility(mWindowToken, res);
+        synchronized (ImfLock.class) {
+            initImeTargetWindowState(mWindowToken);
+            boolean res = mComputer.onImeShowFlags(ImeTracker.Token.empty(), 0 /* showFlags */);
+            mComputer.requestImeVisibility(mWindowToken, res);
 
-        final ImeTargetWindowState state = mComputer.getWindowStateOrNull(mWindowToken);
-        assertThat(state).isNotNull();
-        assertThat(state.hasEditorFocused()).isTrue();
-        assertThat(state.getSoftInputModeState()).isEqualTo(SOFT_INPUT_STATE_UNCHANGED);
-        assertThat(state.isRequestedImeVisible()).isTrue();
+            final ImeTargetWindowState state = mComputer.getWindowStateOrNull(mWindowToken);
+            assertThat(state).isNotNull();
+            assertThat(state.hasEditorFocused()).isTrue();
+            assertThat(state.getSoftInputModeState()).isEqualTo(SOFT_INPUT_STATE_UNCHANGED);
+            assertThat(state.isRequestedImeVisible()).isTrue();
 
-        assertThat(mComputer.mRequestedShowExplicitly).isTrue();
+            assertThat(mComputer.mRequestedShowExplicitly).isTrue();
+        }
     }
 
     /**
@@ -128,12 +133,14 @@
      */
     @Test
     public void testRequestImeVisibility_showExplicit_thenShowImplicit() {
-        initImeTargetWindowState(mWindowToken);
-        mComputer.onImeShowFlags(ImeTracker.Token.empty(), 0 /* showFlags */);
-        assertThat(mComputer.mRequestedShowExplicitly).isTrue();
+        synchronized (ImfLock.class) {
+            initImeTargetWindowState(mWindowToken);
+            mComputer.onImeShowFlags(ImeTracker.Token.empty(), 0 /* showFlags */);
+            assertThat(mComputer.mRequestedShowExplicitly).isTrue();
 
-        mComputer.onImeShowFlags(null, InputMethodManager.SHOW_IMPLICIT);
-        assertThat(mComputer.mRequestedShowExplicitly).isTrue();
+            mComputer.onImeShowFlags(null, InputMethodManager.SHOW_IMPLICIT);
+            assertThat(mComputer.mRequestedShowExplicitly).isTrue();
+        }
     }
 
     /**
@@ -142,162 +149,181 @@
      */
     @Test
     public void testRequestImeVisibility_showForced_thenShowExplicit() {
-        initImeTargetWindowState(mWindowToken);
-        mComputer.onImeShowFlags(ImeTracker.Token.empty(), InputMethodManager.SHOW_FORCED);
-        assertThat(mComputer.mShowForced).isTrue();
+        synchronized (ImfLock.class) {
+            initImeTargetWindowState(mWindowToken);
+            mComputer.onImeShowFlags(ImeTracker.Token.empty(), InputMethodManager.SHOW_FORCED);
+            assertThat(mComputer.mShowForced).isTrue();
 
-        mComputer.onImeShowFlags(ImeTracker.Token.empty(), 0 /* showFlags */);
-        assertThat(mComputer.mShowForced).isTrue();
+            mComputer.onImeShowFlags(ImeTracker.Token.empty(), 0 /* showFlags */);
+            assertThat(mComputer.mShowForced).isTrue();
+        }
     }
 
     @Test
     public void testRequestImeVisibility_showImplicit_a11yNoImePolicy() {
-        // Precondition: set AccessibilityService#SHOW_MODE_HIDDEN policy
-        mComputer.getImePolicy().setA11yRequestNoSoftKeyboard(SHOW_MODE_HIDDEN);
+        synchronized (ImfLock.class) {
+            // Precondition: set AccessibilityService#SHOW_MODE_HIDDEN policy
+            mComputer.getImePolicy().setA11yRequestNoSoftKeyboard(SHOW_MODE_HIDDEN);
 
-        initImeTargetWindowState(mWindowToken);
-        boolean res = mComputer.onImeShowFlags(ImeTracker.Token.empty(),
-                InputMethodManager.SHOW_IMPLICIT);
-        mComputer.requestImeVisibility(mWindowToken, res);
+            initImeTargetWindowState(mWindowToken);
+            boolean res = mComputer.onImeShowFlags(ImeTracker.Token.empty(),
+                    InputMethodManager.SHOW_IMPLICIT);
+            mComputer.requestImeVisibility(mWindowToken, res);
 
-        final ImeTargetWindowState state = mComputer.getWindowStateOrNull(mWindowToken);
-        assertThat(state).isNotNull();
-        assertThat(state.hasEditorFocused()).isTrue();
-        assertThat(state.getSoftInputModeState()).isEqualTo(SOFT_INPUT_STATE_UNCHANGED);
-        assertThat(state.isRequestedImeVisible()).isFalse();
+            final ImeTargetWindowState state = mComputer.getWindowStateOrNull(mWindowToken);
+            assertThat(state).isNotNull();
+            assertThat(state.hasEditorFocused()).isTrue();
+            assertThat(state.getSoftInputModeState()).isEqualTo(SOFT_INPUT_STATE_UNCHANGED);
+            assertThat(state.isRequestedImeVisible()).isFalse();
 
-        assertThat(mComputer.mRequestedShowExplicitly).isFalse();
+            assertThat(mComputer.mRequestedShowExplicitly).isFalse();
+        }
     }
 
     @Test
     public void testRequestImeVisibility_showImplicit_imeHiddenPolicy() {
-        // Precondition: set IME hidden display policy before calling showSoftInput
-        mComputer.getImePolicy().setImeHiddenByDisplayPolicy(true);
+        synchronized (ImfLock.class) {
+            // Precondition: set IME hidden display policy before calling showSoftInput
+            mComputer.getImePolicy().setImeHiddenByDisplayPolicy(true);
 
-        initImeTargetWindowState(mWindowToken);
-        boolean res = mComputer.onImeShowFlags(ImeTracker.Token.empty(),
-                InputMethodManager.SHOW_IMPLICIT);
-        mComputer.requestImeVisibility(mWindowToken, res);
+            initImeTargetWindowState(mWindowToken);
+            boolean res = mComputer.onImeShowFlags(ImeTracker.Token.empty(),
+                    InputMethodManager.SHOW_IMPLICIT);
+            mComputer.requestImeVisibility(mWindowToken, res);
 
-        final ImeTargetWindowState state = mComputer.getWindowStateOrNull(mWindowToken);
-        assertThat(state).isNotNull();
-        assertThat(state.hasEditorFocused()).isTrue();
-        assertThat(state.getSoftInputModeState()).isEqualTo(SOFT_INPUT_STATE_UNCHANGED);
-        assertThat(state.isRequestedImeVisible()).isFalse();
+            final ImeTargetWindowState state = mComputer.getWindowStateOrNull(mWindowToken);
+            assertThat(state).isNotNull();
+            assertThat(state.hasEditorFocused()).isTrue();
+            assertThat(state.getSoftInputModeState()).isEqualTo(SOFT_INPUT_STATE_UNCHANGED);
+            assertThat(state.isRequestedImeVisible()).isFalse();
 
-        assertThat(mComputer.mRequestedShowExplicitly).isFalse();
+            assertThat(mComputer.mRequestedShowExplicitly).isFalse();
+        }
     }
 
     @Test
     public void testRequestImeVisibility_hideNotAlways() {
-        // Precondition: ensure IME has shown before hiding request.
-        mComputer.setInputShown(true);
+        synchronized (ImfLock.class) {
+            // Precondition: ensure IME has shown before hiding request.
+            mComputer.setInputShown(true);
 
-        initImeTargetWindowState(mWindowToken);
-        assertThat(mComputer.canHideIme(ImeTracker.Token.empty(),
-                InputMethodManager.HIDE_NOT_ALWAYS)).isTrue();
-        mComputer.requestImeVisibility(mWindowToken, false);
+            initImeTargetWindowState(mWindowToken);
+            assertThat(mComputer.canHideIme(ImeTracker.Token.empty(),
+                    InputMethodManager.HIDE_NOT_ALWAYS)).isTrue();
+            mComputer.requestImeVisibility(mWindowToken, false);
 
-        final ImeTargetWindowState state = mComputer.getWindowStateOrNull(mWindowToken);
-        assertThat(state).isNotNull();
-        assertThat(state.hasEditorFocused()).isTrue();
-        assertThat(state.getSoftInputModeState()).isEqualTo(SOFT_INPUT_STATE_UNCHANGED);
-        assertThat(state.isRequestedImeVisible()).isFalse();
+            final ImeTargetWindowState state = mComputer.getWindowStateOrNull(mWindowToken);
+            assertThat(state).isNotNull();
+            assertThat(state.hasEditorFocused()).isTrue();
+            assertThat(state.getSoftInputModeState()).isEqualTo(SOFT_INPUT_STATE_UNCHANGED);
+            assertThat(state.isRequestedImeVisible()).isFalse();
+        }
     }
 
     @Test
     public void testComputeImeDisplayId() {
-        final ImeTargetWindowState state = mComputer.getOrCreateWindowState(mWindowToken);
+        synchronized (ImfLock.class) {
+            final ImeTargetWindowState state = mComputer.getOrCreateWindowState(mWindowToken);
 
-        mImeDisplayPolicy = DISPLAY_IME_POLICY_LOCAL;
-        mComputer.computeImeDisplayId(state, DEFAULT_DISPLAY);
-        assertThat(mComputer.getImePolicy().isImeHiddenByDisplayPolicy()).isFalse();
-        assertThat(state.getImeDisplayId()).isEqualTo(DEFAULT_DISPLAY);
+            mImeDisplayPolicy = DISPLAY_IME_POLICY_LOCAL;
+            mComputer.computeImeDisplayId(state, DEFAULT_DISPLAY);
+            assertThat(mComputer.getImePolicy().isImeHiddenByDisplayPolicy()).isFalse();
+            assertThat(state.getImeDisplayId()).isEqualTo(DEFAULT_DISPLAY);
 
-        mComputer.computeImeDisplayId(state, 10 /* displayId */);
-        assertThat(mComputer.getImePolicy().isImeHiddenByDisplayPolicy()).isFalse();
-        assertThat(state.getImeDisplayId()).isEqualTo(10);
+            mComputer.computeImeDisplayId(state, 10 /* displayId */);
+            assertThat(mComputer.getImePolicy().isImeHiddenByDisplayPolicy()).isFalse();
+            assertThat(state.getImeDisplayId()).isEqualTo(10);
 
-        mImeDisplayPolicy = DISPLAY_IME_POLICY_HIDE;
-        mComputer.computeImeDisplayId(state, 10 /* displayId */);
-        assertThat(mComputer.getImePolicy().isImeHiddenByDisplayPolicy()).isTrue();
-        assertThat(state.getImeDisplayId()).isEqualTo(INVALID_DISPLAY);
+            mImeDisplayPolicy = DISPLAY_IME_POLICY_HIDE;
+            mComputer.computeImeDisplayId(state, 10 /* displayId */);
+            assertThat(mComputer.getImePolicy().isImeHiddenByDisplayPolicy()).isTrue();
+            assertThat(state.getImeDisplayId()).isEqualTo(INVALID_DISPLAY);
 
-        mImeDisplayPolicy = DISPLAY_IME_POLICY_FALLBACK_DISPLAY;
-        mComputer.computeImeDisplayId(state, 10 /* displayId */);
-        assertThat(mComputer.getImePolicy().isImeHiddenByDisplayPolicy()).isFalse();
-        assertThat(state.getImeDisplayId()).isEqualTo(FALLBACK_DISPLAY_ID);
+            mImeDisplayPolicy = DISPLAY_IME_POLICY_FALLBACK_DISPLAY;
+            mComputer.computeImeDisplayId(state, 10 /* displayId */);
+            assertThat(mComputer.getImePolicy().isImeHiddenByDisplayPolicy()).isFalse();
+            assertThat(state.getImeDisplayId()).isEqualTo(FALLBACK_DISPLAY_ID);
+        }
     }
 
     @Test
     public void testComputeState_lastImeRequestedVisible_preserved_When_StateUnChanged() {
-        // Assume the last IME targeted window has requested IME visible
-        final IBinder lastImeTargetWindowToken = new Binder();
-        mInputMethodManagerService.mLastImeTargetWindow = lastImeTargetWindowToken;
-        mComputer.requestImeVisibility(lastImeTargetWindowToken, true);
-        final ImeTargetWindowState lastState = mComputer.getWindowStateOrNull(
-                lastImeTargetWindowToken);
-        assertThat(lastState.isRequestedImeVisible()).isTrue();
+        synchronized (ImfLock.class) {
+            // Assume the last IME targeted window has requested IME visible
+            final IBinder lastImeTargetWindowToken = new Binder();
+            mComputer.setLastImeTargetWindow(lastImeTargetWindowToken);
+            mComputer.requestImeVisibility(lastImeTargetWindowToken, true);
+            final ImeTargetWindowState lastState = mComputer.getWindowStateOrNull(
+                    lastImeTargetWindowToken);
+            assertThat(lastState.isRequestedImeVisible()).isTrue();
 
-        // Verify when focusing the next window with STATE_UNCHANGED flag, the last IME
-        // visibility state will be preserved to the current window state.
-        final ImeTargetWindowState stateWithUnChangedFlag = initImeTargetWindowState(mWindowToken);
-        mComputer.computeState(stateWithUnChangedFlag, true /* allowVisible */);
-        assertThat(stateWithUnChangedFlag.isRequestedImeVisible()).isEqualTo(
-                lastState.isRequestedImeVisible());
+            // Verify when focusing the next window with STATE_UNCHANGED flag, the last IME
+            // visibility state will be preserved to the current window state.
+            final ImeTargetWindowState stateWithUnChangedFlag = initImeTargetWindowState(
+                    mWindowToken);
+            mComputer.computeState(stateWithUnChangedFlag, true /* allowVisible */);
+            assertThat(stateWithUnChangedFlag.isRequestedImeVisible()).isEqualTo(
+                    lastState.isRequestedImeVisible());
+        }
     }
 
     @Test
     public void testOnInteractiveChanged() {
-        mComputer.getOrCreateWindowState(mWindowToken);
-        // Precondition: ensure IME has shown before hiding request.
-        mComputer.requestImeVisibility(mWindowToken, true);
-        mComputer.setInputShown(true);
+        synchronized (ImfLock.class) {
+            mComputer.getOrCreateWindowState(mWindowToken);
+            // Precondition: ensure IME has shown before hiding request.
+            mComputer.requestImeVisibility(mWindowToken, true);
+            mComputer.setInputShown(true);
 
-        // No need any visibility change When initially shows IME on the device was interactive.
-        ImeVisibilityStateComputer.ImeVisibilityResult result = mComputer.onInteractiveChanged(
-                mWindowToken, true /* interactive */);
-        assertThat(result).isNull();
+            // No need any visibility change When initially shows IME on the device was interactive.
+            ImeVisibilityStateComputer.ImeVisibilityResult result = mComputer.onInteractiveChanged(
+                    mWindowToken, true /* interactive */);
+            assertThat(result).isNull();
 
-        // Show the IME screenshot to capture the last IME visible state when the device inactive.
-        result = mComputer.onInteractiveChanged(mWindowToken, false /* interactive */);
-        assertThat(result).isNotNull();
-        assertThat(result.getState()).isEqualTo(STATE_SHOW_IME_SNAPSHOT);
-        assertThat(result.getReason()).isEqualTo(SHOW_IME_SCREENSHOT_FROM_IMMS);
+            // Show the IME screenshot to capture the last IME visible state when the device
+            // inactive.
+            result = mComputer.onInteractiveChanged(mWindowToken, false /* interactive */);
+            assertThat(result).isNotNull();
+            assertThat(result.getState()).isEqualTo(STATE_SHOW_IME_SNAPSHOT);
+            assertThat(result.getReason()).isEqualTo(SHOW_IME_SCREENSHOT_FROM_IMMS);
 
-        // Remove the IME screenshot when the device became interactive again.
-        result = mComputer.onInteractiveChanged(mWindowToken, true /* interactive */);
-        assertThat(result).isNotNull();
-        assertThat(result.getState()).isEqualTo(STATE_REMOVE_IME_SNAPSHOT);
-        assertThat(result.getReason()).isEqualTo(REMOVE_IME_SCREENSHOT_FROM_IMMS);
+            // Remove the IME screenshot when the device became interactive again.
+            result = mComputer.onInteractiveChanged(mWindowToken, true /* interactive */);
+            assertThat(result).isNotNull();
+            assertThat(result.getState()).isEqualTo(STATE_REMOVE_IME_SNAPSHOT);
+            assertThat(result.getReason()).isEqualTo(REMOVE_IME_SCREENSHOT_FROM_IMMS);
+        }
     }
 
     @Test
     public void testOnApplyImeVisibilityFromComputer() {
-        final IBinder testImeTargetOverlay = new Binder();
-        final IBinder testImeInputTarget = new Binder();
+        synchronized (ImfLock.class) {
+            final IBinder testImeTargetOverlay = new Binder();
+            final IBinder testImeInputTarget = new Binder();
 
-        // Simulate a test IME input target was visible.
-        mListener.onImeInputTargetVisibilityChanged(testImeInputTarget, true, false);
+            // Simulate a test IME input target was visible.
+            mListener.onImeInputTargetVisibilityChanged(testImeInputTarget, true, false);
 
-        // Simulate a test IME layering target overlay fully occluded the IME input target.
-        mListener.onImeTargetOverlayVisibilityChanged(testImeTargetOverlay,
-                TYPE_APPLICATION_OVERLAY, true, false);
-        mListener.onImeInputTargetVisibilityChanged(testImeInputTarget, false, false);
-        final ArgumentCaptor<IBinder> targetCaptor = ArgumentCaptor.forClass(IBinder.class);
-        final ArgumentCaptor<ImeVisibilityResult> resultCaptor = ArgumentCaptor.forClass(
-                ImeVisibilityResult.class);
-        verify(mInputMethodManagerService).onApplyImeVisibilityFromComputer(targetCaptor.capture(),
-                notNull() /* statsToken */, resultCaptor.capture());
-        final IBinder imeInputTarget = targetCaptor.getValue();
-        final ImeVisibilityResult result = resultCaptor.getValue();
+            // Simulate a test IME layering target overlay fully occluded the IME input target.
+            mListener.onImeTargetOverlayVisibilityChanged(testImeTargetOverlay,
+                    TYPE_APPLICATION_OVERLAY, true, false);
+            mListener.onImeInputTargetVisibilityChanged(testImeInputTarget, false, false);
+            final ArgumentCaptor<IBinder> targetCaptor = ArgumentCaptor.forClass(IBinder.class);
+            final ArgumentCaptor<ImeVisibilityResult> resultCaptor = ArgumentCaptor.forClass(
+                    ImeVisibilityResult.class);
+            verify(mInputMethodManagerService).onApplyImeVisibilityFromComputerLocked(
+                    targetCaptor.capture(), notNull() /* statsToken */, resultCaptor.capture());
+            final IBinder imeInputTarget = targetCaptor.getValue();
+            final ImeVisibilityResult result = resultCaptor.getValue();
 
-        // Verify the computer will callback hiding IME state to IMMS.
-        assertThat(imeInputTarget).isEqualTo(testImeInputTarget);
-        assertThat(result.getState()).isEqualTo(STATE_HIDE_IME_EXPLICIT);
-        assertThat(result.getReason()).isEqualTo(HIDE_WHEN_INPUT_TARGET_INVISIBLE);
+            // Verify the computer will callback hiding IME state to IMMS.
+            assertThat(imeInputTarget).isEqualTo(testImeInputTarget);
+            assertThat(result.getState()).isEqualTo(STATE_HIDE_IME_EXPLICIT);
+            assertThat(result.getReason()).isEqualTo(HIDE_WHEN_INPUT_TARGET_INVISIBLE);
+        }
     }
 
+    @GuardedBy("ImfLock.class")
     private ImeTargetWindowState initImeTargetWindowState(IBinder windowToken) {
         final ImeTargetWindowState state = new ImeTargetWindowState(SOFT_INPUT_STATE_UNCHANGED,
                 0, true, true, true);
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ImmutableSparseArrayTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ImmutableSparseArrayTest.java
new file mode 100644
index 0000000..944b7c6
--- /dev/null
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ImmutableSparseArrayTest.java
@@ -0,0 +1,278 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.inputmethod;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertThrows;
+
+import android.annotation.NonNull;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+
+@RunWith(AndroidJUnit4.class)
+public final class ImmutableSparseArrayTest {
+
+    @Test
+    public void testEmptyObject() {
+        final ImmutableSparseArray<Object> empty = ImmutableSparseArray.empty();
+
+        assertThat(empty.size()).isEqualTo(0);
+        verifyCommonBehaviors(empty);
+    }
+
+    @Test
+    public void testEmptyMethod() {
+        assertThat(ImmutableSparseArray.empty()).isSameInstanceAs(ImmutableSparseArray.empty());
+    }
+
+    @Test
+    public void testCloneWithPutOrSelf_appendingFromEmpty() {
+        final int key1 = 1;
+        final Object value1 = new Object();
+        final int key2 = -2;  // intentionally negative
+        final Object value2 = new Object();
+        final int key3 = -3;  // intentionally negative
+        final Object value3 = new Object();
+        final int key4 = 4;
+        final Object value4 = new Object();
+
+        final ImmutableSparseArray<Object> oneItemArray = ImmutableSparseArray.empty()
+                .cloneWithPutOrSelf(key1, value1);
+        verifyCommonBehaviors(oneItemArray);
+        assertThat(oneItemArray.size()).isEqualTo(1);
+        assertThat(oneItemArray.get(key1)).isSameInstanceAs(value1);
+
+        final ImmutableSparseArray<Object> twoItemArray =
+                oneItemArray.cloneWithPutOrSelf(key2, value2);
+        assertThat(twoItemArray).isNotSameInstanceAs(oneItemArray);
+        verifyCommonBehaviors(twoItemArray);
+        assertThat(twoItemArray.size()).isEqualTo(2);
+        assertThat(twoItemArray.get(key1)).isSameInstanceAs(value1);
+        assertThat(twoItemArray.get(key2)).isSameInstanceAs(value2);
+
+        final ImmutableSparseArray<Object> threeItemArray =
+                twoItemArray.cloneWithPutOrSelf(key3, value3);
+        assertThat(threeItemArray).isNotSameInstanceAs(twoItemArray);
+        verifyCommonBehaviors(threeItemArray);
+        assertThat(threeItemArray.size()).isEqualTo(3);
+        assertThat(threeItemArray.get(key1)).isSameInstanceAs(value1);
+        assertThat(threeItemArray.get(key2)).isSameInstanceAs(value2);
+        assertThat(threeItemArray.get(key3)).isSameInstanceAs(value3);
+
+        final ImmutableSparseArray<Object> fourItemArray =
+                threeItemArray.cloneWithPutOrSelf(key4, value4);
+        assertThat(fourItemArray).isNotSameInstanceAs(threeItemArray);
+        verifyCommonBehaviors(fourItemArray);
+        assertThat(fourItemArray.size()).isEqualTo(4);
+        assertThat(fourItemArray.get(key1)).isSameInstanceAs(value1);
+        assertThat(fourItemArray.get(key2)).isSameInstanceAs(value2);
+        assertThat(fourItemArray.get(key3)).isSameInstanceAs(value3);
+        assertThat(fourItemArray.get(key4)).isSameInstanceAs(value4);
+    }
+
+    @Test
+    public void testCloneWithPutOrSelf_returnSelf() {
+        final int key1 = 1;
+        final Object value1 = new Object();
+        final ImmutableSparseArray<Object> array = ImmutableSparseArray
+                .empty()
+                .cloneWithPutOrSelf(key1, value1);
+        assertThat(array.cloneWithPutOrSelf(key1, value1)).isSameInstanceAs(array);
+    }
+
+    @Test
+    public void testCloneWithPutOrSelf_updateExistingValue() {
+        final int key1 = 1;
+        final Object value1 = new Object();
+        final int key2 = 2;
+        final Object value2 = new Object();
+        final Object value2updated = new Object();
+        final int key3 = 3;
+        final Object value3 = new Object();
+
+        final ImmutableSparseArray<Object> array = ImmutableSparseArray
+                .empty()
+                .cloneWithPutOrSelf(key1, value1)
+                .cloneWithPutOrSelf(key2, value2)
+                .cloneWithPutOrSelf(key3, value3);
+
+        final var updatedArray = array.cloneWithPutOrSelf(key2, value2updated);
+        verifyCommonBehaviors(updatedArray);
+
+        assertThat(updatedArray.size()).isEqualTo(3);
+        assertThat(updatedArray.get(key1)).isSameInstanceAs(value1);
+        assertThat(updatedArray.get(key2)).isSameInstanceAs(value2updated);
+        assertThat(updatedArray.get(key3)).isSameInstanceAs(value3);
+    }
+
+    @Test
+    public void testCloneWithRemoveOrSelf_empty() {
+        final ImmutableSparseArray<Object> empty = ImmutableSparseArray.empty();
+        assertThat(empty.cloneWithRemoveOrSelf(0)).isSameInstanceAs(empty);
+    }
+
+    @Test
+    public void testCloneWithRemoveOrSelf_singleInstance() {
+        final int key = 1;
+        final Object value = new Object();
+        final ImmutableSparseArray<Object> array = ImmutableSparseArray
+                .empty()
+                .cloneWithPutOrSelf(key, value);
+        assertThat(array.cloneWithRemoveOrSelf(key)).isSameInstanceAs(ImmutableSparseArray.empty());
+    }
+
+    @Test
+    public void testCloneWithRemoveOrSelf_firstItem() {
+        final int key1 = 1;
+        final Object value1 = new Object();
+        final int key2 = 2;
+        final Object value2 = new Object();
+        final int key3 = 3;
+        final Object value3 = new Object();
+
+        final ImmutableSparseArray<Object> array = ImmutableSparseArray
+                .empty()
+                .cloneWithPutOrSelf(key1, value1)
+                .cloneWithPutOrSelf(key2, value2)
+                .cloneWithPutOrSelf(key3, value3)
+                .cloneWithRemoveOrSelf(key1);
+        verifyCommonBehaviors(array);
+
+        assertThat(array.size()).isEqualTo(2);
+        assertThat(array.get(key1)).isNull();
+        assertThat(array.get(key2)).isSameInstanceAs(value2);
+        assertThat(array.get(key3)).isSameInstanceAs(value3);
+        assertThat(array.keyAt(0)).isEqualTo(key2);
+        assertThat(array.keyAt(1)).isEqualTo(key3);
+    }
+
+    @Test
+    public void testCloneWithRemoveOrSelf_lastItem() {
+        final int key1 = 1;
+        final Object value1 = new Object();
+        final int key2 = 2;
+        final Object value2 = new Object();
+        final int key3 = 3;
+        final Object value3 = new Object();
+
+        final ImmutableSparseArray<Object> array = ImmutableSparseArray
+                .empty()
+                .cloneWithPutOrSelf(key1, value1)
+                .cloneWithPutOrSelf(key2, value2)
+                .cloneWithPutOrSelf(key3, value3)
+                .cloneWithRemoveOrSelf(key3);
+        verifyCommonBehaviors(array);
+
+        assertThat(array.size()).isEqualTo(2);
+        assertThat(array.get(key1)).isSameInstanceAs(value1);
+        assertThat(array.get(key2)).isSameInstanceAs(value2);
+        assertThat(array.get(key3)).isNull();
+    }
+
+    @Test
+    public void testCloneWithRemoveOrSelf_middleItem() {
+        final int key1 = 1;
+        final Object value1 = new Object();
+        final int key2 = 2;
+        final Object value2 = new Object();
+        final int key3 = 3;
+        final Object value3 = new Object();
+
+        final ImmutableSparseArray<Object> array = ImmutableSparseArray
+                .empty()
+                .cloneWithPutOrSelf(key1, value1)
+                .cloneWithPutOrSelf(key2, value2)
+                .cloneWithPutOrSelf(key3, value3)
+                .cloneWithRemoveOrSelf(key2);
+        verifyCommonBehaviors(array);
+
+        assertThat(array.size()).isEqualTo(2);
+        assertThat(array.get(key1)).isSameInstanceAs(value1);
+        assertThat(array.get(key2)).isNull();
+        assertThat(array.get(key3)).isSameInstanceAs(value3);
+    }
+
+    @Test
+    public void testCloneWithRemoveOrSelf_nonExistentItem() {
+        final int key1 = 1;
+        final Object value1 = new Object();
+        final int key2 = 2;
+        final Object value2 = new Object();
+        final int key3 = 3;
+        final Object value3 = new Object();
+        final int key4 = 4;
+
+        final ImmutableSparseArray<Object> array = ImmutableSparseArray
+                .empty()
+                .cloneWithPutOrSelf(key1, value1)
+                .cloneWithPutOrSelf(key2, value2)
+                .cloneWithPutOrSelf(key3, value3);
+
+        assertThat(array.cloneWithRemoveOrSelf(key4)).isSameInstanceAs(array);
+    }
+
+    @Test
+    public void testForEach() {
+        final int key1 = 1;
+        final Object value1 = new Object();
+        final int key2 = 2;
+        final Object value2 = new Object();
+        final int key3 = 3;
+        final Object value3 = new Object();
+
+        final ImmutableSparseArray<Object> array = ImmutableSparseArray
+                .empty()
+                .cloneWithPutOrSelf(key1, value1)
+                .cloneWithPutOrSelf(key2, value2)
+                .cloneWithPutOrSelf(key3, value3);
+
+        final ArrayList<Object> list = new ArrayList<>();
+        array.forEach(list::add);
+        assertThat(list).containsExactlyElementsIn(new Object[]{ value1, value2, value3 })
+                .inOrder();
+    }
+
+
+    private void verifyCommonBehaviors(@NonNull ImmutableSparseArray<Object> sparseArray) {
+        verifyInvalidKeyBehaviors(sparseArray);
+        verifyOutOfBoundsBehaviors(sparseArray);
+    }
+
+    private void verifyInvalidKeyBehaviors(@NonNull ImmutableSparseArray<Object> sparseArray) {
+        final int invalid_key = -123456678;
+        assertThat(sparseArray.get(invalid_key)).isNull();
+        assertThat(sparseArray.indexOfKey(invalid_key)).isEqualTo(-1);
+    }
+
+    private void verifyOutOfBoundsBehaviors(@NonNull ImmutableSparseArray<Object> sparseArray) {
+        final int size = sparseArray.size();
+        assertThrows(ArrayIndexOutOfBoundsException.class,
+                () -> sparseArray.keyAt(size));
+        assertThrows(ArrayIndexOutOfBoundsException.class,
+                () -> sparseArray.valueAt(size));
+        assertThrows(ArrayIndexOutOfBoundsException.class,
+                () -> sparseArray.keyAt(-1));
+        assertThrows(ArrayIndexOutOfBoundsException.class,
+                () -> sparseArray.valueAt(-1));
+    }
+}
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodBindingControllerTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodBindingControllerTest.java
index 4d28b3c..e552621 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodBindingControllerTest.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodBindingControllerTest.java
@@ -79,10 +79,8 @@
         // from system.
         synchronized (ImfLock.class) {
             mBindingController =
-                    new InputMethodBindingController(
-                            mInputMethodManagerService.getCurrentImeUserIdLocked(),
-                            mInputMethodManagerService, mImeConnectionBindFlags,
-                            mCountDownLatch);
+                    new InputMethodBindingController(mUserId, mInputMethodManagerService,
+                            mImeConnectionBindFlags, mCountDownLatch);
         }
     }
 
@@ -140,8 +138,7 @@
         final InputMethodInfo info;
         synchronized (ImfLock.class) {
             mBindingController.setSelectedMethodId(TEST_IME_ID);
-            info = InputMethodSettingsRepository.get(mCallingUserId).getMethodMap()
-                    .get(TEST_IME_ID);
+            info = InputMethodSettingsRepository.get(mUserId).getMethodMap().get(TEST_IME_ID);
         }
         assertThat(info).isNotNull();
         assertThat(info.getId()).isEqualTo(TEST_IME_ID);
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceRestrictImeAmountTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceRestrictImeAmountTest.java
index 2ea2e22..a86d61bb 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceRestrictImeAmountTest.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceRestrictImeAmountTest.java
@@ -122,8 +122,8 @@
 
     private List<InputMethodInfo> filterInputMethodServices(List<ResolveInfo> resolveInfoList,
             List<String> enabledComponents) {
-        final InputMethodMap methodMap = InputMethodManagerService.filterInputMethodServices(
-                AdditionalSubtypeMap.EMPTY_MAP, enabledComponents, mContext, resolveInfoList);
+        final var methodMap = InputMethodManagerService.filterInputMethodServices(
+                enabledComponents, mContext, resolveInfoList);
         return methodMap.values();
     }
 
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java
index ec9bfa7..9a25104 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java
@@ -50,7 +50,6 @@
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.ServiceManager;
-import android.os.UserHandle;
 import android.util.ArraySet;
 import android.view.InputChannel;
 import android.view.inputmethod.EditorInfo;
@@ -128,7 +127,7 @@
     protected Context mContext;
     protected MockitoSession mMockingSession;
     protected int mTargetSdkVersion;
-    protected int mCallingUserId;
+    protected int mUserId;
     protected EditorInfo mEditorInfo;
     protected IInputMethodInvoker mMockInputMethodInvoker;
     protected InputMethodManagerService mInputMethodManagerService;
@@ -165,12 +164,12 @@
                         .spyStatic(AdditionalSubtypeUtils.class)
                         .startMocking();
 
-        mContext = spy(InstrumentationRegistry.getInstrumentation().getContext());
+        mContext = spy(InstrumentationRegistry.getInstrumentation().getTargetContext());
 
         mTargetSdkVersion = mContext.getApplicationInfo().targetSdkVersion;
         mIsLargeScreen = mContext.getResources().getConfiguration()
                 .isLayoutSizeAtLeast(Configuration.SCREENLAYOUT_SIZE_LARGE);
-        mCallingUserId = UserHandle.getCallingUserId();
+        mUserId = mContext.getUserId();
         mEditorInfo = new EditorInfo();
         mEditorInfo.packageName = TEST_EDITOR_PKG_NAME;
 
@@ -202,7 +201,7 @@
         // Injecting and mocked InputMethodBindingController and InputMethod.
         mMockInputMethodInvoker = IInputMethodInvoker.create(mMockInputMethod);
         mInputManagerGlobalSession = InputManagerGlobal.createTestSession(mMockIInputManager);
-        when(mMockInputMethodBindingController.getUserId()).thenReturn(mCallingUserId);
+        when(mMockInputMethodBindingController.getUserId()).thenReturn(mUserId);
         synchronized (ImfLock.class) {
             when(mMockInputMethodBindingController.getCurMethod())
                     .thenReturn(mMockInputMethodInvoker);
@@ -222,7 +221,7 @@
                 .thenReturn(new int[] {0});
         when(mMockUserManagerInternal.getUserIds()).thenReturn(new int[] {0});
         when(mMockActivityManagerInternal.isSystemReady()).thenReturn(true);
-        when(mMockActivityManagerInternal.getCurrentUserId()).thenReturn(mCallingUserId);
+        when(mMockActivityManagerInternal.getCurrentUserId()).thenReturn(mUserId);
         when(mMockPackageManagerInternal.getPackageUid(anyString(), anyLong(), anyInt()))
                 .thenReturn(Binder.getCallingUid());
         when(mMockPackageManagerInternal.isSameApp(anyString(), anyLong(), anyInt(), anyInt()))
@@ -270,16 +269,21 @@
         LocalServices.removeServiceForTest(InputMethodManagerInternal.class);
         lifecycle.onStart();
 
+        final var userData = mInputMethodManagerService.getUserData(mUserId);
+
         // Certain tests rely on TEST_IME_ID that is installed with AndroidTest.xml.
         // TODO(b/352615651): Consider just synthesizing test InputMethodInfo then injecting it.
-        AdditionalSubtypeMapRepository.ensureInitializedAndGet(mCallingUserId);
-        final var settings = InputMethodManagerService.queryInputMethodServicesInternal(mContext,
-                mCallingUserId, AdditionalSubtypeMapRepository.get(mCallingUserId),
-                DirectBootAwareness.AUTO);
-        InputMethodSettingsRepository.put(mCallingUserId, settings);
+        AdditionalSubtypeMapRepository.initializeIfNecessary(mUserId);
+        final var rawMethodMap = InputMethodManagerService.queryRawInputMethodServiceMap(mContext,
+                mUserId);
+        userData.mRawInputMethodMap.set(rawMethodMap);
+        final var settings = InputMethodSettings.create(rawMethodMap.toInputMethodMap(
+                AdditionalSubtypeMap.EMPTY_MAP, DirectBootAwareness.AUTO, true /* userUnlocked */),
+                mUserId);
+        InputMethodSettingsRepository.put(mUserId, settings);
 
         // Emulate that the user initialization is done.
-        mInputMethodManagerService.getUserData(mCallingUserId).mBackgroundLoadLatch.countDown();
+        userData.mBackgroundLoadLatch.countDown();
 
         // After this boot phase, services can broadcast Intents.
         lifecycle.onBootPhase(SystemService.PHASE_ACTIVITY_MANAGER_READY);
@@ -291,7 +295,7 @@
 
     @After
     public void tearDown() {
-        InputMethodSettingsRepository.remove(mCallingUserId);
+        InputMethodSettingsRepository.remove(mUserId);
 
         if (mInputMethodManagerService != null) {
             mInputMethodManagerService.mInputMethodDeviceConfigs.destroy();
@@ -347,8 +351,8 @@
         synchronized (ImfLock.class) {
             ClientState cs = mInputMethodManagerService.getClientStateLocked(client);
             cs.mCurSession = new InputMethodManagerService.SessionState(cs,
-                    mMockInputMethodInvoker, mMockInputMethodSession, mock(
-                    InputChannel.class));
+                    mMockInputMethodInvoker, mMockInputMethodSession, mock(InputChannel.class),
+                    mUserId);
         }
     }
 }
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceWindowGainedFocusTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceWindowGainedFocusTest.java
index ffc4df8..c5b5668 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceWindowGainedFocusTest.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceWindowGainedFocusTest.java
@@ -263,7 +263,7 @@
                 mMockRemoteInputConnection /* inputConnection */,
                 mMockRemoteAccessibilityInputConnection /* remoteAccessibilityInputConnection */,
                 mTargetSdkVersion /* unverifiedTargetSdkVersion */,
-                mCallingUserId /* userId */,
+                mUserId /* userId */,
                 mMockImeOnBackInvokedDispatcher /* imeDispatcher */);
     }
 
diff --git a/services/tests/PackageManagerServiceTests/server/Android.bp b/services/tests/PackageManagerServiceTests/server/Android.bp
index a738acb..598e273 100644
--- a/services/tests/PackageManagerServiceTests/server/Android.bp
+++ b/services/tests/PackageManagerServiceTests/server/Android.bp
@@ -63,7 +63,7 @@
     libs: [
         "android.hardware.power-V1-java",
         "android.hardware.tv.cec-V1.0-java",
-        "android.hardware.vibrator-V2-java",
+        "android.hardware.vibrator-V3-java",
         "android.hidl.manager-V1.0-java",
         "android.test.mock",
         "android.test.base",
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerServiceUtilsTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerServiceUtilsTest.java
new file mode 100644
index 0000000..4a2396d
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerServiceUtilsTest.java
@@ -0,0 +1,341 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.pm;
+
+import static org.testng.Assert.assertThrows;
+
+import android.content.pm.PackageInfoLite;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.internal.pm.parsing.pkg.PackageImpl;
+import com.android.server.pm.pkg.AndroidPackage;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.util.UUID;
+
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class PackageManagerServiceUtilsTest {
+
+    private static final String PACKAGE_NAME = "com.android.app";
+    private static final File CODE_PATH =
+            InstrumentationRegistry.getInstrumentation().getContext().getFilesDir();
+
+    @Test
+    public void testCheckDowngrade_packageSetting_versionCodeSmaller_throwException()
+            throws Exception {
+        final PackageSetting before = createPackageSetting();
+        before.setLongVersionCode(2);
+        final PackageInfoLite after = new PackageInfoLite();
+        after.versionCode = 1;
+
+        assertThrows(PackageManagerException.class,
+                () -> PackageManagerServiceUtils.checkDowngrade(before, after));
+    }
+
+    @Test
+    public void testCheckDowngrade_packageSetting_baseRevisionCodeSmaller_throwException()
+            throws Exception {
+        final PackageSetting before = createPackageSetting();
+        before.setLongVersionCode(1);
+        before.setBaseRevisionCode(2);
+        final PackageInfoLite after = new PackageInfoLite();
+        after.versionCode = 1;
+        after.baseRevisionCode = 1;
+
+        assertThrows(PackageManagerException.class,
+                () -> PackageManagerServiceUtils.checkDowngrade(before, after));
+    }
+
+    @Test
+    public void testCheckDowngrade_packageSetting_splitArraySizeIsDifferent_throwException()
+            throws Exception {
+        final String splitOne = "one";
+        final String splitTwo = "two";
+        final int revisionOne = 311;
+        final int revisionTwo = 330;
+        final String[] splitNames = new String[] { splitOne, splitTwo };
+        final int[] beforeSplitRevisionCodes = new int[] { revisionOne };
+        final int[] afterSplitRevisionCodes = new int[] { revisionOne, revisionTwo };
+
+        final PackageSetting before = createPackageSetting();
+        before.setLongVersionCode(1);
+        before.setSplitNames(splitNames);
+        before.setSplitRevisionCodes(beforeSplitRevisionCodes);
+        final PackageInfoLite after = new PackageInfoLite();
+        after.versionCode = 1;
+        after.splitNames = splitNames;
+        after.splitRevisionCodes = afterSplitRevisionCodes;
+
+        assertThrows(PackageManagerException.class,
+                () -> PackageManagerServiceUtils.checkDowngrade(before, after));
+    }
+
+    @Test
+    public void testCheckDowngrade_packageSetting_splitRevisionCodeSmaller_throwException()
+            throws Exception {
+        final String splitOne = "one";
+        final String splitTwo = "two";
+        final int revisionOne = 311;
+        final int revisionTwo = 330;
+        final int revisionThree = 360;
+        final String[] splitNames = new String[] { splitOne, splitTwo };
+        final int[] beforeSplitRevisionCodes = new int[] { revisionTwo, revisionThree};
+        final int[] afterSplitRevisionCodes = new int[] { revisionOne, revisionTwo };
+
+        final PackageSetting before = createPackageSetting();
+        before.setLongVersionCode(1);
+        before.setSplitNames(splitNames);
+        before.setSplitRevisionCodes(beforeSplitRevisionCodes);
+        final PackageInfoLite after = new PackageInfoLite();
+        after.versionCode = 1;
+        after.splitNames = splitNames;
+        after.splitRevisionCodes = afterSplitRevisionCodes;
+
+        assertThrows(PackageManagerException.class,
+                () -> PackageManagerServiceUtils.checkDowngrade(before, after));
+    }
+
+    @Test
+    public void testCheckDowngrade_packageSetting_sameSplitNameRevisionsBigger()
+            throws Exception {
+        final String splitOne = "one";
+        final String splitTwo = "two";
+        final int revisionOne = 311;
+        final int revisionTwo = 330;
+        final int revisionThree = 360;
+        final String[] splitNames = new String[] { splitOne, splitTwo };
+        final int[] beforeSplitRevisionCodes = new int[] { revisionOne, revisionTwo};
+        final int[] afterSplitRevisionCodes = new int[] { revisionOne, revisionThree };
+
+        final PackageSetting before = createPackageSetting();
+        before.setLongVersionCode(1);
+        before.setSplitNames(splitNames);
+        before.setSplitRevisionCodes(beforeSplitRevisionCodes);
+        final PackageInfoLite after = new PackageInfoLite();
+        after.versionCode = 1;
+        after.splitNames = splitNames;
+        after.splitRevisionCodes = afterSplitRevisionCodes;
+
+        PackageManagerServiceUtils.checkDowngrade(before, after);
+    }
+
+    @Test
+    public void testCheckDowngrade_packageSetting_hasDifferentSplitNames() throws Exception {
+        final String splitOne = "one";
+        final String splitTwo = "two";
+        final int revisionOne = 311;
+        final int revisionTwo = 330;
+        final int revisionThree = 360;
+        final String[] beforeSplitNames = new String[] { splitOne, splitTwo };
+        final String[] afterSplitNames = new String[] { splitTwo };
+        final int[] beforeSplitRevisionCodes = new int[] { revisionOne, revisionTwo};
+        final int[] afterSplitRevisionCodes = new int[] { revisionThree };
+
+        final PackageSetting before = createPackageSetting();
+        before.setLongVersionCode(1);
+        before.setSplitNames(beforeSplitNames);
+        before.setSplitRevisionCodes(beforeSplitRevisionCodes);
+        final PackageInfoLite after = new PackageInfoLite();
+        after.versionCode = 1;
+        after.splitNames = afterSplitNames;
+        after.splitRevisionCodes = afterSplitRevisionCodes;
+
+        PackageManagerServiceUtils.checkDowngrade(before, after);
+    }
+
+    @Test
+    public void testCheckDowngrade_packageSetting_newSplitName() throws Exception {
+        final String splitOne = "one";
+        final String splitTwo = "two";
+        final int revisionOne = 311;
+        final int revisionTwo = 330;
+        final String[] beforeSplitNames = new String[] { splitOne };
+        final String[] afterSplitNames = new String[] { splitTwo };
+        final int[] beforeSplitRevisionCodes = new int[] { revisionTwo };
+        final int[] afterSplitRevisionCodes = new int[] { revisionOne };
+
+        final PackageSetting before = createPackageSetting();
+        before.setLongVersionCode(1);
+        before.setSplitNames(beforeSplitNames);
+        before.setSplitRevisionCodes(beforeSplitRevisionCodes);
+        final PackageInfoLite after = new PackageInfoLite();
+        after.versionCode = 1;
+        after.splitNames = afterSplitNames;
+        after.splitRevisionCodes = afterSplitRevisionCodes;
+
+        PackageManagerServiceUtils.checkDowngrade(before, after);
+    }
+
+    @Test
+    public void testCheckDowngrade_androidPackage_versionCodeSmaller_throwException()
+            throws Exception {
+        final AndroidPackage before = PackageImpl.forTesting(PACKAGE_NAME).hideAsParsed()
+                .setVersionCode(2).hideAsFinal();
+        final PackageInfoLite after = new PackageInfoLite();
+        after.versionCode = 1;
+
+        assertThrows(PackageManagerException.class,
+                () -> PackageManagerServiceUtils.checkDowngrade(before, after));
+    }
+
+    @Test
+    public void testCheckDowngrade_androidPackage_baseRevisionCodeSmaller_throwException()
+            throws Exception {
+        final AndroidPackage before = PackageImpl.forTesting(PACKAGE_NAME).setBaseRevisionCode(2)
+                .hideAsParsed().setVersionCode(1).hideAsFinal();
+        final PackageInfoLite after = new PackageInfoLite();
+        after.versionCode = 1;
+        after.baseRevisionCode = 1;
+
+        assertThrows(PackageManagerException.class,
+                () -> PackageManagerServiceUtils.checkDowngrade(before, after));
+    }
+
+    @Test
+    public void testCheckDowngrade_androidPackage_splitArraySizeIsDifferent_throwException()
+            throws Exception {
+        final String splitOne = "one";
+        final String splitTwo = "two";
+        final int revisionOne = 311;
+        final int revisionTwo = 330;
+        final String[] splitNames = new String[] { splitOne, splitTwo };
+        final int[] beforeSplitRevisionCodes = new int[] { revisionOne };
+        final int[] afterSplitRevisionCodes = new int[] { revisionOne, revisionTwo };
+
+        final AndroidPackage before = PackageImpl.forTesting(PACKAGE_NAME)
+                .asSplit(splitNames, /* splitCodePaths= */ null,
+                        beforeSplitRevisionCodes, /* splitDependencies= */ null)
+                .hideAsParsed().setVersionCode(1).hideAsFinal();
+        final PackageInfoLite after = new PackageInfoLite();
+        after.versionCode = 1;
+        after.splitNames = splitNames;
+        after.splitRevisionCodes = afterSplitRevisionCodes;
+
+        assertThrows(PackageManagerException.class,
+                () -> PackageManagerServiceUtils.checkDowngrade(before, after));
+    }
+
+    @Test
+    public void testCheckDowngrade_androidPackage_splitRevisionCodeSmaller_throwException()
+            throws Exception {
+        final String splitOne = "one";
+        final String splitTwo = "two";
+        final int revisionOne = 311;
+        final int revisionTwo = 330;
+        final int revisionThree = 360;
+        final String[] splitNames = new String[] { splitOne, splitTwo };
+        final int[] beforeSplitRevisionCodes = new int[] { revisionTwo, revisionThree};
+        final int[] afterSplitRevisionCodes = new int[] { revisionOne, revisionTwo };
+
+        final AndroidPackage before = PackageImpl.forTesting(PACKAGE_NAME)
+                .asSplit(splitNames, /* splitCodePaths= */ null,
+                        beforeSplitRevisionCodes, /* splitDependencies= */ null)
+                .hideAsParsed().setVersionCode(1).hideAsFinal();
+        final PackageInfoLite after = new PackageInfoLite();
+        after.versionCode = 1;
+        after.splitNames = splitNames;
+        after.splitRevisionCodes = afterSplitRevisionCodes;
+
+        assertThrows(PackageManagerException.class,
+                () -> PackageManagerServiceUtils.checkDowngrade(before, after));
+    }
+
+    @Test
+    public void testCheckDowngrade_androidPackage_sameSplitNameRevisionsBigger()
+            throws Exception {
+        final String splitOne = "one";
+        final String splitTwo = "two";
+        final int revisionOne = 311;
+        final int revisionTwo = 330;
+        final int revisionThree = 360;
+        final String[] splitNames = new String[] { splitOne, splitTwo };
+        final int[] beforeSplitRevisionCodes = new int[] { revisionOne, revisionTwo};
+        final int[] afterSplitRevisionCodes = new int[] { revisionOne, revisionThree };
+
+        final AndroidPackage before = PackageImpl.forTesting(PACKAGE_NAME)
+                .asSplit(splitNames, /* splitCodePaths= */ null,
+                        beforeSplitRevisionCodes, /* splitDependencies= */ null)
+                .hideAsParsed().setVersionCode(1).hideAsFinal();
+        final PackageInfoLite after = new PackageInfoLite();
+        after.versionCode = 1;
+        after.splitNames = splitNames;
+        after.splitRevisionCodes = afterSplitRevisionCodes;
+
+        PackageManagerServiceUtils.checkDowngrade(before, after);
+    }
+
+    @Test
+    public void testCheckDowngrade_androidPackage_hasDifferentSplitNames() throws Exception {
+        final String splitOne = "one";
+        final String splitTwo = "two";
+        final int revisionOne = 311;
+        final int revisionTwo = 330;
+        final int revisionThree = 360;
+        final String[] beforeSplitNames = new String[] { splitOne, splitTwo };
+        final String[] afterSplitNames = new String[] { splitTwo };
+        final int[] beforeSplitRevisionCodes = new int[] { revisionOne, revisionTwo};
+        final int[] afterSplitRevisionCodes = new int[] { revisionThree };
+
+        final AndroidPackage before = PackageImpl.forTesting(PACKAGE_NAME)
+                .asSplit(beforeSplitNames, /* splitCodePaths= */ null,
+                        beforeSplitRevisionCodes, /* splitDependencies= */ null)
+                .hideAsParsed().setVersionCode(1).hideAsFinal();
+        final PackageInfoLite after = new PackageInfoLite();
+        after.versionCode = 1;
+        after.splitNames = afterSplitNames;
+        after.splitRevisionCodes = afterSplitRevisionCodes;
+
+        PackageManagerServiceUtils.checkDowngrade(before, after);
+    }
+
+    @Test
+    public void testCheckDowngrade_androidPackage_newSplitName() throws Exception {
+        final String splitOne = "one";
+        final String splitTwo = "two";
+        final int revisionOne = 311;
+        final int revisionTwo = 330;
+        final String[] beforeSplitNames = new String[] { splitOne };
+        final String[] afterSplitNames = new String[] { splitTwo };
+        final int[] beforeSplitRevisionCodes = new int[] { revisionTwo };
+        final int[] afterSplitRevisionCodes = new int[] { revisionOne };
+
+        final AndroidPackage before = PackageImpl.forTesting(PACKAGE_NAME)
+                .asSplit(beforeSplitNames, /* splitCodePaths= */ null,
+                        beforeSplitRevisionCodes, /* splitDependencies= */ null)
+                .hideAsParsed().setVersionCode(1).hideAsFinal();
+        final PackageInfoLite after = new PackageInfoLite();
+        after.versionCode = 1;
+        after.splitNames = afterSplitNames;
+        after.splitRevisionCodes = afterSplitRevisionCodes;
+
+        PackageManagerServiceUtils.checkDowngrade(before, after);
+    }
+
+    private PackageSetting createPackageSetting() {
+        return new PackageSetting(PACKAGE_NAME, PACKAGE_NAME, CODE_PATH, /* pkgFlags= */ 0,
+                /* privateFlags= */ 0 , UUID.randomUUID());
+    }
+}
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java
index dec4634..d7af443 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -1092,7 +1092,7 @@
     }
 
     @Test
-    public void testNoPkg_writeReadSplitVersions() {
+    public void testNoPkgDifferentRevisions_writeReadSplitVersions() {
         Settings settings = makeSettings();
         PackageSetting packageSetting = createPackageSetting(PACKAGE_NAME_1);
         packageSetting.setAppId(Process.FIRST_APPLICATION_UID);
@@ -1117,6 +1117,54 @@
     }
 
     @Test
+    public void testNoPkgSameRevisions_writeReadSplitVersions() {
+        Settings settings = makeSettings();
+        PackageSetting packageSetting = createPackageSetting(PACKAGE_NAME_1);
+        packageSetting.setAppId(Process.FIRST_APPLICATION_UID);
+
+        final String splitOne = "one";
+        final String splitTwo = "two";
+        final int revisionOne = 311;
+        packageSetting.setSplitNames(new String[] { splitOne, splitTwo});
+        packageSetting.setSplitRevisionCodes(new int[] { revisionOne, revisionOne});
+        settings.mPackages.put(PACKAGE_NAME_1, packageSetting);
+
+        settings.writeLPr(computer, /* sync= */ true);
+        settings.mPackages.clear();
+
+        assertThat(settings.readLPw(computer, createFakeUsers()), is(true));
+        PackageSetting resultSetting = settings.getPackageLPr(PACKAGE_NAME_1);
+        assertThat(resultSetting.getSplitNames()[0], is(splitOne));
+        assertThat(resultSetting.getSplitNames()[1], is(splitTwo));
+        assertThat(resultSetting.getSplitRevisionCodes()[0], is(revisionOne));
+        assertThat(resultSetting.getSplitRevisionCodes()[1], is(revisionOne));
+    }
+
+    @Test
+    public void testNoPkgSameSplitNames_writeReadSplitVersions() {
+        Settings settings = makeSettings();
+        PackageSetting packageSetting = createPackageSetting(PACKAGE_NAME_1);
+        packageSetting.setAppId(Process.FIRST_APPLICATION_UID);
+
+        final String splitOne = "one";
+        final int revisionOne = 311;
+        final int revisionTwo = 330;
+        packageSetting.setSplitNames(new String[] { splitOne, splitOne});
+        packageSetting.setSplitRevisionCodes(new int[] { revisionOne, revisionTwo});
+        settings.mPackages.put(PACKAGE_NAME_1, packageSetting);
+
+        settings.writeLPr(computer, /* sync= */ true);
+        settings.mPackages.clear();
+
+        assertThat(settings.readLPw(computer, createFakeUsers()), is(true));
+        PackageSetting resultSetting = settings.getPackageLPr(PACKAGE_NAME_1);
+        assertThat(resultSetting.getSplitNames().length, is(1));
+        assertThat(resultSetting.getSplitRevisionCodes().length, is(1));
+        assertThat(resultSetting.getSplitNames()[0], is(splitOne));
+        assertThat(resultSetting.getSplitRevisionCodes()[0], is(revisionTwo));
+    }
+
+    @Test
     public void testWriteReadArchiveState() {
         Settings settings = makeSettings();
         PackageSetting packageSetting = createPackageSetting(PACKAGE_NAME_1);
diff --git a/services/tests/displayservicetests/src/com/android/server/display/BrightnessMappingStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/BrightnessMappingStrategyTest.java
index fb73aff..f3cd0c9 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/BrightnessMappingStrategyTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/BrightnessMappingStrategyTest.java
@@ -693,6 +693,21 @@
     }
 
     @Test
+    public void testGetPreset() {
+        int preset = Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_DIM;
+        Settings.System.putInt(mContext.getContentResolver(),
+                Settings.System.SCREEN_BRIGHTNESS_FOR_ALS, preset);
+        setUpResources();
+        DisplayDeviceConfig ddc = new DdcBuilder()
+                .setAutoBrightnessLevels(AUTO_BRIGHTNESS_MODE_DEFAULT, preset, DISPLAY_LEVELS)
+                .setAutoBrightnessLevelsLux(AUTO_BRIGHTNESS_MODE_DEFAULT, preset, LUX_LEVELS)
+                .build();
+        BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(mContext, ddc,
+                AUTO_BRIGHTNESS_MODE_DEFAULT, /* displayWhiteBalanceController= */ null);
+        assertEquals(preset, strategy.getPreset());
+    }
+
+    @Test
     public void testAutoBrightnessModeAndPreset() {
         int mode = AUTO_BRIGHTNESS_MODE_DOZE;
         int preset = Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_DIM;
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 8ed38a6..6394b27 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
@@ -936,6 +936,9 @@
 
     @Test
     public void testSetScreenOffBrightnessSensorDisabled_DisplayIsOn() {
+        Settings.System.putInt(mContext.getContentResolver(),
+                Settings.System.SCREEN_BRIGHTNESS_MODE,
+                Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
         DisplayPowerRequest dpr = new DisplayPowerRequest();
         dpr.policy = DisplayPowerRequest.POLICY_BRIGHT;
 
@@ -948,6 +951,9 @@
 
     @Test
     public void testSetScreenOffBrightnessSensorDisabled_DisplayIsAFollower() {
+        Settings.System.putInt(mContext.getContentResolver(),
+                Settings.System.SCREEN_BRIGHTNESS_MODE,
+                Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
         DisplayPowerRequest dpr = new DisplayPowerRequest();
         dpr.policy = DisplayPowerRequest.POLICY_OFF;
 
@@ -960,6 +966,24 @@
     }
 
     @Test
+    public void testSetScreenOffBrightnessSensorDisabled_AutoBrightnessInDoze() {
+        Settings.System.putInt(mContext.getContentResolver(),
+                Settings.System.SCREEN_BRIGHTNESS_MODE,
+                Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
+        mContext.getOrCreateTestableResources().addOverride(
+                com.android.internal.R.bool.config_allowAutoBrightnessWhileDozing, true);
+        mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID);
+        DisplayPowerRequest dpr = new DisplayPowerRequest();
+        dpr.policy = DisplayPowerRequest.POLICY_DOZE;
+
+        mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+        advanceTime(1); // Run updatePowerState
+
+        verify(mHolder.screenOffBrightnessSensorController, atLeastOnce())
+                .setLightSensorEnabled(false);
+    }
+
+    @Test
     public void testStopScreenOffBrightnessSensorControllerWhenDisplayDeviceChanges() {
         // New display device
         setUpDisplay(DISPLAY_ID, "new_unique_id", mHolder.display, mock(DisplayDevice.class),
@@ -1589,16 +1613,21 @@
         advanceTime(1); // Run updatePowerState
 
         reset(mHolder.wakelockController);
+        when(mHolder.wakelockController
+                .acquireWakelock(WakelockController.WAKE_LOCK_OVERRIDE_DOZE_SCREEN_STATE))
+                .thenReturn(true);
         mHolder.dpc.overrideDozeScreenState(
                 supportedTargetState, Display.STATE_REASON_DEFAULT_POLICY);
-        advanceTime(1); // Run updatePowerState
 
         // Should get a wakelock to notify powermanager
-        verify(mHolder.wakelockController, atLeastOnce()).acquireWakelock(
-                eq(WakelockController.WAKE_LOCK_UNFINISHED_BUSINESS));
+        verify(mHolder.wakelockController).acquireWakelock(
+                eq(WakelockController.WAKE_LOCK_OVERRIDE_DOZE_SCREEN_STATE));
 
+        advanceTime(1); // Run updatePowerState
         verify(mHolder.displayPowerState)
                 .setScreenState(supportedTargetState, Display.STATE_REASON_DEFAULT_POLICY);
+        verify(mHolder.wakelockController).releaseWakelock(
+                eq(WakelockController.WAKE_LOCK_OVERRIDE_DOZE_SCREEN_STATE));
     }
 
     @Test
diff --git a/services/tests/displayservicetests/src/com/android/server/display/ExternalDisplayStatsServiceTest.java b/services/tests/displayservicetests/src/com/android/server/display/ExternalDisplayStatsServiceTest.java
index 98ba9ae..abb354b 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/ExternalDisplayStatsServiceTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/ExternalDisplayStatsServiceTest.java
@@ -151,9 +151,10 @@
     }
 
     @Test
-    public void testDisplayInteractivityChanges(
+    public void testDisplayInteractivityChangesWhileMirroring(
             @TestParameter final boolean isExternalDisplayUsedForAudio) {
         mExternalDisplayStatsService.onDisplayConnected(mMockedLogicalDisplay);
+        mExternalDisplayStatsService.onDisplayAdded(EXTERNAL_DISPLAY_ID);
         mHandler.flush();
         assertThat(mInteractivityReceiver).isNotNull();
 
@@ -180,12 +181,38 @@
         mInteractivityReceiver.onReceive(null, null);
         assertThat(mExternalDisplayStatsService.isInteractiveExternalDisplays()).isTrue();
         verify(mMockedInjector).writeLog(FrameworkStatsLog.EXTERNAL_DISPLAY_STATE_CHANGED,
-                FrameworkStatsLog.EXTERNAL_DISPLAY_STATE_CHANGED__STATE__CONNECTED,
+                FrameworkStatsLog.EXTERNAL_DISPLAY_STATE_CHANGED__STATE__MIRRORING,
                 /*numberOfDisplays=*/ 1,
                 isExternalDisplayUsedForAudio);
     }
 
     @Test
+    public void testDisplayInteractivityChangesWhileConnected() {
+        mExternalDisplayStatsService.onDisplayConnected(mMockedLogicalDisplay);
+        mHandler.flush();
+        assertThat(mInteractivityReceiver).isNotNull();
+        clearInvocations(mMockedInjector);
+
+        // Default is 'interactive', so no log should be written.
+        mInteractivityReceiver.onReceive(null, null);
+        assertThat(mExternalDisplayStatsService.isInteractiveExternalDisplays()).isTrue();
+        verify(mMockedInjector, never()).writeLog(anyInt(), anyInt(), anyInt(), anyBoolean());
+
+        // Change to non-interactive should not produce log
+        when(mMockedInjector.isInteractive(eq(EXTERNAL_DISPLAY_ID))).thenReturn(false);
+        mInteractivityReceiver.onReceive(null, null);
+        assertThat(mExternalDisplayStatsService.isInteractiveExternalDisplays()).isFalse();
+        verify(mMockedInjector, never()).writeLog(anyInt(), anyInt(), anyInt(), anyBoolean());
+        clearInvocations(mMockedInjector);
+
+        // Change back to interactive should not produce log
+        when(mMockedInjector.isInteractive(eq(EXTERNAL_DISPLAY_ID))).thenReturn(true);
+        mInteractivityReceiver.onReceive(null, null);
+        assertThat(mExternalDisplayStatsService.isInteractiveExternalDisplays()).isTrue();
+        verify(mMockedInjector, never()).writeLog(anyInt(), anyInt(), anyInt(), anyBoolean());
+    }
+
+    @Test
     public void testAudioPlaybackChanges() {
         mExternalDisplayStatsService.onDisplayConnected(mMockedLogicalDisplay);
         mHandler.flush();
diff --git a/services/tests/displayservicetests/src/com/android/server/display/WakelockControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/WakelockControllerTest.java
index c23d4b1..019b70e 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/WakelockControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/WakelockControllerTest.java
@@ -64,6 +64,8 @@
                 "[" + DISPLAY_ID + "]prox negative");
         assertEquals(mWakelockController.getSuspendBlockerProxDebounceId(),
                 "[" + DISPLAY_ID + "]prox debounce");
+        assertEquals(mWakelockController.getSuspendBlockerOverrideDozeScreenState(),
+                "[" + DISPLAY_ID + "]override doze screen state");
     }
 
     @Test
@@ -162,6 +164,28 @@
     }
 
     @Test
+    public void acquireOverrideDozeScreenStateSuspendBlocker() throws Exception {
+        // Acquire the suspend blocker
+        verifyWakelockAcquisitionAndReaquisition(WakelockController
+                        .WAKE_LOCK_OVERRIDE_DOZE_SCREEN_STATE,
+                () -> mWakelockController.isOverrideDozeScreenStateAcquired());
+
+        // Verify acquire happened only once
+        verify(mDisplayPowerCallbacks, times(1))
+                .acquireSuspendBlocker(mWakelockController
+                        .getSuspendBlockerOverrideDozeScreenState());
+
+        // Release the suspend blocker
+        verifyWakelockReleaseAndRerelease(WakelockController.WAKE_LOCK_OVERRIDE_DOZE_SCREEN_STATE,
+                () -> mWakelockController.isOverrideDozeScreenStateAcquired());
+
+        // Verify suspend blocker was released only once
+        verify(mDisplayPowerCallbacks, times(1))
+                .releaseSuspendBlocker(mWakelockController
+                        .getSuspendBlockerOverrideDozeScreenState());
+    }
+
+    @Test
     public void proximityPositiveRunnableWorksAsExpected() {
         // Acquire the suspend blocker twice
         assertTrue(mWakelockController.acquireWakelock(
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/BrightnessEventTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/BrightnessEventTest.java
index 26f6e91..df09b04 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/BrightnessEventTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/BrightnessEventTest.java
@@ -46,6 +46,7 @@
         mBrightnessEvent.setPhysicalDisplayId("987654321");
         mBrightnessEvent.setPhysicalDisplayName("display_name");
         mBrightnessEvent.setDisplayState(Display.STATE_ON);
+        mBrightnessEvent.setDisplayStateReason(Display.STATE_REASON_DEFAULT_POLICY);
         mBrightnessEvent.setDisplayPolicy(POLICY_BRIGHT);
         mBrightnessEvent.setLux(100.0f);
         mBrightnessEvent.setPercent(46.5f);
@@ -82,9 +83,9 @@
         String actualString = mBrightnessEvent.toString(false);
         String expectedString =
                 "BrightnessEvent: brt=0.6 (46.5%), nits= 893.8, lux=100.0, reason=doze [ "
-                        + "low_pwr ], strat=strategy_name, state=ON, policy=BRIGHT, flags=, "
-                        + "initBrt=25.0, rcmdBrt=0.6, preBrt=NaN, preLux=150.0, "
-                        + "wasShortTermModelActive=true, autoBrightness=true (idle), "
+                        + "low_pwr ], strat=strategy_name, state=ON, stateReason=DEFAULT_POLICY, "
+                        + "policy=BRIGHT, flags=, initBrt=25.0, rcmdBrt=0.6, preBrt=NaN, "
+                        + "preLux=150.0, wasShortTermModelActive=true, autoBrightness=true (idle), "
                         + "unclampedBrt=0.65, hbmMax=0.62, hbmMode=off, thrmMax=0.65, "
                         + "rbcStrength=-1, powerFactor=0.2, physDisp=display_name(987654321), "
                         + "logicalId=1";
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/BrightnessReasonTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/BrightnessReasonTest.java
index 990c383..04b79b4 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/BrightnessReasonTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/BrightnessReasonTest.java
@@ -18,7 +18,6 @@
 
 import static org.junit.Assert.assertEquals;
 
-
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
@@ -43,17 +42,34 @@
         assertEquals(mBrightnessReason.getReason(), BrightnessReason.REASON_UNKNOWN);
         assertEquals(mBrightnessReason.getModifier(), 0);
 
+        CharSequence tag = "my tag";
         mBrightnessReason.set(
                 getReason(BrightnessReason.REASON_BOOST, BrightnessReason.MODIFIER_THROTTLED));
+        mBrightnessReason.setTag(tag);
+
         assertEquals(mBrightnessReason.getReason(), BrightnessReason.REASON_BOOST);
         assertEquals(mBrightnessReason.getModifier(), BrightnessReason.MODIFIER_THROTTLED);
+        assertEquals(mBrightnessReason.getTag().toString(), tag);
     }
 
     @Test
-    public void toStringGeneratesExpectedString() {
-        String actualString = mBrightnessReason.toString();
-        String expectedString = "doze [ low_pwr ]";
-        assertEquals(actualString, expectedString);
+    public void toStringGeneratedExpectedString() {
+        assertEquals("doze [ low_pwr ]", mBrightnessReason.toString());
+    }
+
+    @Test
+    public void overrideTagString() {
+        // Should not print out the tag for "doze"
+        mBrightnessReason.setTag("my/tag");
+        assertEquals("doze(my/tag) [ low_pwr ]", mBrightnessReason.toString());
+
+        // Should print out tag for "override"
+        mBrightnessReason.setReason(BrightnessReason.REASON_OVERRIDE);
+        assertEquals("override(my/tag) [ low_pwr ]", mBrightnessReason.toString());
+
+        // Should not print anything if no tag.
+        mBrightnessReason.setTag(null);
+        assertEquals("override [ low_pwr ]", mBrightnessReason.toString());
     }
 
     @Test
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java
index d7936fe..c069875 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java
@@ -545,17 +545,18 @@
         DisplayDeviceConfig displayDeviceConfig = mock(DisplayDeviceConfig.class);
         Handler handler = mock(Handler.class);
         BrightnessMappingStrategy brightnessMappingStrategy = mock(BrightnessMappingStrategy.class);
-        boolean isEnabled = true;
+        boolean isDisplayEnabled = true;
         int leadDisplayId = 2;
 
         mDisplayBrightnessController.setUpAutoBrightness(automaticBrightnessController,
-                sensorManager, displayDeviceConfig, handler, brightnessMappingStrategy, isEnabled,
-                leadDisplayId);
+                sensorManager, displayDeviceConfig, handler, brightnessMappingStrategy,
+                isDisplayEnabled, leadDisplayId);
         assertEquals(automaticBrightnessController,
                 mDisplayBrightnessController.mAutomaticBrightnessController);
         verify(automaticBrightnessStrategy).setAutomaticBrightnessController(
                 automaticBrightnessController);
         verify(autoBrightnessFallbackStrategy).setupAutoBrightnessFallbackSensor(sensorManager,
-                displayDeviceConfig, handler, brightnessMappingStrategy, isEnabled, leadDisplayId);
+                displayDeviceConfig, handler, brightnessMappingStrategy, isDisplayEnabled,
+                leadDisplayId);
     }
 }
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessLowLuxModifierTest.kt b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessLowLuxModifierTest.kt
index d672435..6929690 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessLowLuxModifierTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessLowLuxModifierTest.kt
@@ -225,5 +225,37 @@
         assertThat(modifier.brightnessReason).isEqualTo(BrightnessReason.MODIFIER_MIN_LUX)
         assertThat(modifier.brightnessLowerBound).isEqualTo(LOW_LUX_BRIGHTNESS)
     }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_EVEN_DIMMER)
+    fun testUserSwitch() {
+        // nits: 0.5 -> backlight 0.01 -> brightness -> 0.05
+        whenever(mockDisplayDeviceConfig.getBacklightFromNits(/* nits= */ 0.5f))
+            .thenReturn(0.01f)
+        whenever(mockDisplayDeviceConfig.getBrightnessFromBacklight(/* backlight = */ 0.01f))
+            .thenReturn(0.05f)
+
+        Settings.Secure.putIntForUser(context.contentResolver,
+            Settings.Secure.EVEN_DIMMER_ACTIVATED, 0, USER_ID) // off
+        Settings.Secure.putFloatForUser(context.contentResolver,
+            Settings.Secure.EVEN_DIMMER_MIN_NITS, 1.0f, USER_ID)
+
+        modifier.recalculateLowerBound()
+
+        assertThat(modifier.isActive).isFalse()
+        assertThat(modifier.brightnessLowerBound).isEqualTo(TRANSITION_POINT)
+        assertThat(modifier.brightnessReason).isEqualTo(0) // no reason - i.e. off
+
+        Settings.Secure.putIntForUser(context.contentResolver,
+            Settings.Secure.EVEN_DIMMER_ACTIVATED, 1, USER_ID) // on
+        Settings.Secure.putFloatForUser(context.contentResolver,
+            Settings.Secure.EVEN_DIMMER_MIN_NITS, 0.5f, USER_ID)
+        modifier.onSwitchUser()
+
+        assertThat(modifier.isActive).isTrue()
+        assertThat(modifier.brightnessReason).isEqualTo(
+            BrightnessReason.MODIFIER_MIN_USER_SET_LOWER_BOUND)
+        assertThat(modifier.brightnessLowerBound).isEqualTo(0.05f)
+    }
 }
 
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/HdrBrightnessModifierTest.kt b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/HdrBrightnessModifierTest.kt
index 0ed96ae..bb025cc 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/HdrBrightnessModifierTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/HdrBrightnessModifierTest.kt
@@ -16,7 +16,10 @@
 
 package com.android.server.display.brightness.clamper
 
+import android.content.Context
+import android.database.ContentObserver
 import android.hardware.display.DisplayManagerInternal
+import android.net.Uri
 import android.os.IBinder
 import android.os.PowerManager.BRIGHTNESS_MAX
 import android.util.Spline
@@ -51,7 +54,7 @@
 
     private val stoppedClock = OffsettableClock.Stopped()
     private val testHandler = TestHandler(null, stoppedClock)
-    private val testInjector = TestInjector()
+    private val testInjector = TestInjector(mock<Context>())
     private val mockChangeListener = mock<ClamperChangeListener>()
     private val mockDisplayDeviceConfig = mock<DisplayDeviceConfig>()
     private val mockDisplayBinder = mock<IBinder>()
@@ -63,14 +66,14 @@
     private val dummyData = createDisplayDeviceData(mockDisplayDeviceConfig, mockDisplayBinder)
 
     @Test
-    fun `change listener is not called on init`() {
+    fun changeListenerIsNotCalledOnInit() {
         initHdrModifier()
 
         verify(mockChangeListener, never()).onChanged()
     }
 
     @Test
-    fun `hdr listener registered on init if hdr data is present`() {
+    fun hdrListenerRegisteredOnInit_hdrDataPresent() {
         initHdrModifier()
 
         assertThat(testInjector.registeredHdrListener).isNotNull()
@@ -78,22 +81,19 @@
     }
 
     @Test
-    fun `hdr listener not registered on init if hdr data is missing`() {
-        initHdrModifier(null)
-
-        testHandler.flush()
+    fun hdrListenerNotRegisteredOnInit_hdrDataMissing() {
+        initHdrModifier(hdrBrightnessData = null)
 
         assertThat(testInjector.registeredHdrListener).isNull()
         assertThat(testInjector.registeredToken).isNull()
     }
 
     @Test
-    fun `unsubscribes hdr listener when display changed with no hdr data`() {
+    fun unsubscribeHdrListener_displayChangedWithNoHdrData() {
         initHdrModifier()
 
         whenever(mockDisplayDeviceConfig.hdrBrightnessData).thenReturn(null)
         modifier.onDisplayChanged(dummyData)
-        testHandler.flush()
 
         assertThat(testInjector.registeredHdrListener).isNull()
         assertThat(testInjector.registeredToken).isNull()
@@ -101,12 +101,11 @@
     }
 
     @Test
-    fun `resubscribes hdr listener when display changed with different token`() {
+    fun resubscribesHdrListener_displayChangedWithDifferentToken() {
         initHdrModifier()
 
         modifier.onDisplayChanged(
             createDisplayDeviceData(mockDisplayDeviceConfig, mockDisplayBinderOther))
-        testHandler.flush()
 
         assertThat(testInjector.registeredHdrListener).isNotNull()
         assertThat(testInjector.registeredToken).isEqualTo(mockDisplayBinderOther)
@@ -114,7 +113,28 @@
     }
 
     @Test
-    fun `test NO_HDR mode`() {
+    fun contentObserverNotRegisteredOnInit_hdrDataMissing() {
+        initHdrModifier(null)
+
+        assertThat(testInjector.registeredContentObserver).isNull()
+    }
+
+    @Test
+    fun contentObserverNotRegisteredOnInit_allowedInLowPowerMode() {
+        initHdrModifier(createHdrBrightnessData(allowInLowPowerMode = true))
+
+        assertThat(testInjector.registeredContentObserver).isNull()
+    }
+
+    @Test
+    fun contentObserverRegisteredOnInit_notAllowedInLowPowerMode() {
+        initHdrModifier(createHdrBrightnessData(allowInLowPowerMode = false))
+
+        assertThat(testInjector.registeredContentObserver).isNotNull()
+    }
+
+    @Test
+    fun testNoHdrMode() {
         initHdrModifier()
         // screen size = 10_000
         setupDisplay(width = 100, height = 100, hdrBrightnessData = createHdrBrightnessData(
@@ -131,7 +151,7 @@
     }
 
     @Test
-    fun `test NBM_HDR mode`() {
+    fun testNbmHdrMode() {
         initHdrModifier()
         // screen size = 10_000
         val transitionPoint = 0.55f
@@ -157,7 +177,7 @@
     }
 
     @Test
-    fun `test HBM_HDR mode`() {
+    fun testHbmHdrMode() {
         initHdrModifier()
         // screen size = 10_000
         setupDisplay(width = 100, height = 100, hdrBrightnessData = createHdrBrightnessData(
@@ -182,7 +202,7 @@
     }
 
     @Test
-    fun `test display change no HDR content`() {
+    fun testDisplayChange_noHdrContent() {
         initHdrModifier()
         setupDisplay(width = 100, height = 100)
         assertModifierState()
@@ -195,7 +215,7 @@
     }
 
     @Test
-    fun `test display change with HDR content`() {
+    fun testDisplayChange_hdrContent() {
         initHdrModifier()
         setupDisplay(width = 100, height = 100)
         setupHdrLayer(width = 100, height = 100, maxHdrRatio = 5f)
@@ -218,7 +238,7 @@
     }
 
     @Test
-    fun `test ambient lux decrease above maxBrightnessLimits no HDR`() {
+    fun testSetAmbientLux_decreaseAboveMaxBrightnessLimitNoHdr() {
         initHdrModifier()
         modifier.setAmbientLux(1000f)
         setupDisplay(width = 100, height = 100, hdrBrightnessData = createHdrBrightnessData(
@@ -234,7 +254,7 @@
     }
 
     @Test
-    fun `test ambient lux decrease above maxBrightnessLimits with HDR`() {
+    fun testSetAmbientLux_decreaseAboveMaxBrightnessLimitWithHdr() {
         initHdrModifier()
         modifier.setAmbientLux(1000f)
         setupDisplay(width = 200, height = 200, hdrBrightnessData = createHdrBrightnessData(
@@ -260,7 +280,7 @@
     }
 
     @Test
-    fun `test ambient lux decrease below maxBrightnessLimits no HDR`() {
+    fun testSetAmbientLux_decreaseBelowMaxBrightnessLimitNoHdr() {
         initHdrModifier()
         modifier.setAmbientLux(1000f)
         setupDisplay(width = 100, height = 100, hdrBrightnessData = createHdrBrightnessData(
@@ -276,7 +296,7 @@
     }
 
     @Test
-    fun `test ambient lux decrease below maxBrightnessLimits with HDR`() {
+    fun testSetAmbientLux_decreaseBelowMaxBrightnessLimitWithHdr() {
         initHdrModifier()
         modifier.setAmbientLux(1000f)
         val maxBrightness = 0.6f
@@ -322,6 +342,23 @@
         )
     }
 
+    @Test
+    fun testLowPower_notAllowedInLowPower() {
+        initHdrModifier()
+        setupDisplay(width = 100, height = 100, hdrBrightnessData = createHdrBrightnessData(
+            allowInLowPowerMode = false
+        ))
+        setupHdrLayer(width = 100, height = 100)
+        clearInvocations(mockChangeListener)
+
+        testInjector.isLowPower = true
+        testInjector.registeredContentObserver!!.onChange(true)
+
+        verify(mockChangeListener).onChanged()
+        assertModifierState()
+    }
+
+    // Helper functions
     private fun setupHdrLayer(width: Int = 100, height: Int = 100, maxHdrRatio: Float = 0.8f) {
         testInjector.registeredHdrListener!!.onHdrInfoChanged(
             mockDisplayBinder, 1, width, height, 0, maxHdrRatio
@@ -345,7 +382,6 @@
             width = width,
             height = height
         ))
-        testHandler.flush()
     }
 
     private fun initHdrModifier(hdrBrightnessData: HdrBrightnessData? = createHdrBrightnessData()) {
@@ -384,9 +420,12 @@
         assertThat(stateBuilder.customAnimationRate).isEqualTo(animationRate)
     }
 
-    internal class TestInjector : Injector() {
+    internal class TestInjector(context: Context) : Injector(context) {
         var registeredHdrListener: SurfaceControlHdrLayerInfoListener? = null
         var registeredToken: IBinder? = null
+        var registeredContentObserver: ContentObserver? = null
+
+        var isLowPower: Boolean = false
 
         override fun registerHdrListener(
             listener: SurfaceControlHdrLayerInfoListener, token: IBinder
@@ -401,5 +440,17 @@
             registeredHdrListener = null
             registeredToken = null
         }
+
+        override fun registerContentObserver(observer: ContentObserver, uri: Uri) {
+            registeredContentObserver = observer
+        }
+
+        override fun unregisterContentObserver(observer: ContentObserver) {
+            registeredContentObserver = null
+        }
+
+        override fun isLowPowerMode(): Boolean {
+            return isLowPower
+        }
     }
 }
\ No newline at end of file
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/LightSensorControllerTest.kt b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/LightSensorControllerTest.kt
index f59e127..79b99b5 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/LightSensorControllerTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/LightSensorControllerTest.kt
@@ -29,6 +29,7 @@
 import com.android.server.display.config.createSensorData
 import com.android.server.display.utils.AmbientFilter
 import org.junit.Before
+import org.junit.Test
 import org.mockito.kotlin.any
 import org.mockito.kotlin.argumentCaptor
 import org.mockito.kotlin.eq
@@ -62,31 +63,35 @@
             mockLightSensorListener, mockHandler, testInjector)
     }
 
-    fun `does not register light sensor if is not configured`() {
+    @Test
+    fun doesNotRegisterLightSensorIfNotConfigured() {
         controller.restart()
 
         verifyNoMoreInteractions(mockSensorManager, mockAmbientFilter, mockLightSensorListener)
     }
 
-    fun `does not register light sensor if missing`() {
+    @Test
+    fun doesNotRegisterLightSensorIfMissing() {
         controller.configure(dummySensorData, DISPLAY_ID)
         controller.restart()
 
         verifyNoMoreInteractions(mockSensorManager, mockAmbientFilter, mockLightSensorListener)
     }
 
-    fun `register light sensor if configured and present`() {
+    @Test
+    fun registerLightSensorIfConfiguredAndPresent() {
         testInjector.lightSensor = TestUtils
                 .createSensor(Sensor.TYPE_LIGHT, Sensor.STRING_TYPE_LIGHT)
         controller.configure(dummySensorData, DISPLAY_ID)
         controller.restart()
 
         verify(mockSensorManager).registerListener(any(),
-            testInjector.lightSensor, LIGHT_SENSOR_RATE * 1000, mockHandler)
+            eq(testInjector.lightSensor), eq(LIGHT_SENSOR_RATE * 1000), eq(mockHandler))
         verifyNoMoreInteractions(mockSensorManager, mockAmbientFilter, mockLightSensorListener)
     }
 
-    fun `register light sensor once if not changed`() {
+    @Test
+    fun registerLightSensorOnceIfNotChanged() {
         testInjector.lightSensor = TestUtils
                 .createSensor(Sensor.TYPE_LIGHT, Sensor.STRING_TYPE_LIGHT)
         controller.configure(dummySensorData, DISPLAY_ID)
@@ -95,11 +100,12 @@
         controller.restart()
 
         verify(mockSensorManager).registerListener(any(),
-            testInjector.lightSensor, LIGHT_SENSOR_RATE * 1000, mockHandler)
+            eq(testInjector.lightSensor), eq(LIGHT_SENSOR_RATE * 1000), eq(mockHandler))
         verifyNoMoreInteractions(mockSensorManager, mockAmbientFilter, mockLightSensorListener)
     }
 
-    fun `register new light sensor and unregister old if changed`() {
+    @Test
+    fun registerNewAndUnregisterOldLightSensorIfChanged() {
         val lightSensor1 = TestUtils
                 .createSensor(Sensor.TYPE_LIGHT, Sensor.STRING_TYPE_LIGHT)
         testInjector.lightSensor = lightSensor1
@@ -112,19 +118,21 @@
         controller.configure(dummySensorData, DISPLAY_ID)
         controller.restart()
 
-        inOrder {
+        inOrder(mockSensorManager, mockAmbientFilter, mockLightSensorListener) {
             verify(mockSensorManager).registerListener(any(),
-                lightSensor1, LIGHT_SENSOR_RATE * 1000, mockHandler)
-            verify(mockSensorManager).unregisterListener(any<SensorEventListener>())
+                eq(lightSensor1), eq(LIGHT_SENSOR_RATE * 1000), eq(mockHandler))
+            verify(mockSensorManager).registerListener(any(),
+                eq(lightSensor2), eq(LIGHT_SENSOR_RATE * 1000), eq(mockHandler))
+            verify(mockSensorManager).unregisterListener(any<SensorEventListener>(),
+                eq(lightSensor1))
             verify(mockAmbientFilter).clear()
             verify(mockLightSensorListener).onAmbientLuxChange(LightSensorController.INVALID_LUX)
-            verify(mockSensorManager).registerListener(any(),
-                lightSensor2, LIGHT_SENSOR_RATE * 1000, mockHandler)
         }
         verifyNoMoreInteractions(mockSensorManager, mockAmbientFilter, mockLightSensorListener)
     }
 
-    fun `notifies listener on ambient lux change`() {
+    @Test
+    fun notifiesListenerOnAmbientLuxChange() {
         val expectedLux = 40f
         val eventLux = 50
         val eventTime = 60L
@@ -141,7 +149,7 @@
         listener.onSensorChanged(TestUtils.createSensorEvent(testInjector.lightSensor,
             eventLux, eventTime * 1_000_000))
 
-        inOrder {
+        inOrder(mockAmbientFilter, mockLightSensorListener) {
             verify(mockAmbientFilter).addValue(eventTime, eventLux.toFloat())
             verify(mockLightSensorListener).onAmbientLuxChange(expectedLux)
         }
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutoBrightnessFallbackStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutoBrightnessFallbackStrategyTest.java
index bb24c0f..99dfa73 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutoBrightnessFallbackStrategyTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutoBrightnessFallbackStrategyTest.java
@@ -16,15 +16,22 @@
 
 package com.android.server.display.brightness.strategy;
 
+import static android.hardware.display.DisplayManagerInternal.DisplayPowerRequest.POLICY_BRIGHT;
+import static android.hardware.display.DisplayManagerInternal.DisplayPowerRequest.POLICY_DOZE;
+import static android.hardware.display.DisplayManagerInternal.DisplayPowerRequest.POLICY_OFF;
+
+import static com.android.server.display.layout.Layout.NO_LEAD_DISPLAY;
 
 import static org.junit.Assert.assertEquals;
-import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.hardware.Sensor;
 import android.hardware.SensorManager;
 import android.hardware.display.DisplayManagerInternal;
 import android.os.Handler;
+import android.os.PowerManager;
+import android.view.Display;
 
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -35,6 +42,7 @@
 import com.android.server.display.ScreenOffBrightnessSensorController;
 import com.android.server.display.brightness.BrightnessReason;
 import com.android.server.display.brightness.StrategyExecutionRequest;
+import com.android.server.display.brightness.StrategySelectionNotifyRequest;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -53,9 +61,24 @@
     @Mock
     private ScreenOffBrightnessSensorController mScreenOffBrightnessSensorController;
 
+    @Mock
+    private SensorManager mSensorManager;
+
+    @Mock
+    private DisplayDeviceConfig mDisplayDeviceConfig;
+
+    @Mock
+    private Handler mHandler;
+
+    @Mock
+    private BrightnessMappingStrategy mBrightnessMappingStrategy;
+
     @Before
     public void before() {
         MockitoAnnotations.initMocks(this);
+        int[] sensorValueToLux = new int[]{50, 100};
+        when(mDisplayDeviceConfig.getScreenOffBrightnessSensorValueToLux())
+                .thenReturn(sensorValueToLux);
         mAutoBrightnessFallbackStrategy = new AutoBrightnessFallbackStrategy(
                 new AutoBrightnessFallbackStrategy.Injector() {
                     @Override
@@ -78,20 +101,11 @@
 
     @Test
     public void testUpdateBrightnessWhenScreenDozeStateIsRequested() {
-        // Setup the argument mocks
-        SensorManager sensorManager = mock(SensorManager.class);
-        DisplayDeviceConfig displayDeviceConfig = mock(DisplayDeviceConfig.class);
-        Handler handler = mock(Handler.class);
-        BrightnessMappingStrategy brightnessMappingStrategy = mock(BrightnessMappingStrategy.class);
-        boolean isEnabled = true;
+        boolean isDisplayEnabled = true;
         int leadDisplayId = 2;
-
-        int[] sensorValueToLux = new int[]{50, 100};
-        when(displayDeviceConfig.getScreenOffBrightnessSensorValueToLux()).thenReturn(
-                sensorValueToLux);
-
-        mAutoBrightnessFallbackStrategy.setupAutoBrightnessFallbackSensor(sensorManager,
-                displayDeviceConfig, handler, brightnessMappingStrategy, isEnabled, leadDisplayId);
+        mAutoBrightnessFallbackStrategy.setupAutoBrightnessFallbackSensor(mSensorManager,
+                mDisplayDeviceConfig,
+                mHandler, mBrightnessMappingStrategy, isDisplayEnabled, leadDisplayId);
 
         assertEquals(mScreenOffBrightnessSensor,
                 mAutoBrightnessFallbackStrategy.mScreenOffBrightnessSensor);
@@ -119,4 +133,157 @@
         assertEquals(updatedDisplayBrightnessState, expectedDisplayBrightnessState);
     }
 
+    @Test
+    public void testPostProcess_EnableSensor_PolicyOff() {
+        boolean isDisplayEnabled = true;
+        int leadDisplayId = NO_LEAD_DISPLAY;
+        mAutoBrightnessFallbackStrategy.setupAutoBrightnessFallbackSensor(mSensorManager,
+                mDisplayDeviceConfig,
+                mHandler, mBrightnessMappingStrategy, isDisplayEnabled, leadDisplayId);
+
+        DisplayManagerInternal.DisplayPowerRequest dpr =
+                new DisplayManagerInternal.DisplayPowerRequest();
+        dpr.policy = POLICY_OFF;
+        StrategySelectionNotifyRequest ssnr = new StrategySelectionNotifyRequest(dpr,
+                Display.STATE_OFF, mAutoBrightnessFallbackStrategy,
+                /* lastUserSetScreenBrightness= */ PowerManager.BRIGHTNESS_INVALID_FLOAT,
+                /* userSetBrightnessChanged= */ false,
+                /* allowAutoBrightnessWhileDozingConfig= */ false,
+                /* isAutoBrightnessEnabled= */ true);
+        mAutoBrightnessFallbackStrategy.strategySelectionPostProcessor(ssnr);
+
+        verify(mScreenOffBrightnessSensorController).setLightSensorEnabled(true);
+    }
+
+    @Test
+    public void testPostProcess_EnableSensor_PolicyDoze() {
+        boolean isDisplayEnabled = true;
+        int leadDisplayId = NO_LEAD_DISPLAY;
+        mAutoBrightnessFallbackStrategy.setupAutoBrightnessFallbackSensor(mSensorManager,
+                mDisplayDeviceConfig,
+                mHandler, mBrightnessMappingStrategy, isDisplayEnabled, leadDisplayId);
+
+        DisplayManagerInternal.DisplayPowerRequest dpr =
+                new DisplayManagerInternal.DisplayPowerRequest();
+        dpr.policy = POLICY_DOZE;
+        StrategySelectionNotifyRequest ssnr = new StrategySelectionNotifyRequest(dpr,
+                Display.STATE_DOZE, mAutoBrightnessFallbackStrategy,
+                /* lastUserSetScreenBrightness= */ PowerManager.BRIGHTNESS_INVALID_FLOAT,
+                /* userSetBrightnessChanged= */ false,
+                /* allowAutoBrightnessWhileDozingConfig= */ false,
+                /* isAutoBrightnessEnabled= */ true);
+        mAutoBrightnessFallbackStrategy.strategySelectionPostProcessor(ssnr);
+
+        verify(mScreenOffBrightnessSensorController).setLightSensorEnabled(true);
+    }
+
+    @Test
+    public void testPostProcess_DisableSensor_AutoBrightnessDisabled() {
+        boolean isDisplayEnabled = true;
+        int leadDisplayId = NO_LEAD_DISPLAY;
+        mAutoBrightnessFallbackStrategy.setupAutoBrightnessFallbackSensor(mSensorManager,
+                mDisplayDeviceConfig,
+                mHandler, mBrightnessMappingStrategy, isDisplayEnabled, leadDisplayId);
+
+        DisplayManagerInternal.DisplayPowerRequest dpr =
+                new DisplayManagerInternal.DisplayPowerRequest();
+        dpr.policy = POLICY_OFF;
+        StrategySelectionNotifyRequest ssnr = new StrategySelectionNotifyRequest(dpr,
+                Display.STATE_OFF, mAutoBrightnessFallbackStrategy,
+                /* lastUserSetScreenBrightness= */ PowerManager.BRIGHTNESS_INVALID_FLOAT,
+                /* userSetBrightnessChanged= */ false,
+                /* allowAutoBrightnessWhileDozingConfig= */ false,
+                /* isAutoBrightnessEnabled= */ false);
+        mAutoBrightnessFallbackStrategy.strategySelectionPostProcessor(ssnr);
+
+        verify(mScreenOffBrightnessSensorController).setLightSensorEnabled(false);
+    }
+
+    @Test
+    public void testPostProcess_DisableSensor_DisplayDisabled() {
+        boolean isDisplayEnabled = false;
+        int leadDisplayId = NO_LEAD_DISPLAY;
+        mAutoBrightnessFallbackStrategy.setupAutoBrightnessFallbackSensor(mSensorManager,
+                mDisplayDeviceConfig,
+                mHandler, mBrightnessMappingStrategy, isDisplayEnabled, leadDisplayId);
+
+        DisplayManagerInternal.DisplayPowerRequest dpr =
+                new DisplayManagerInternal.DisplayPowerRequest();
+        dpr.policy = POLICY_OFF;
+        StrategySelectionNotifyRequest ssnr = new StrategySelectionNotifyRequest(dpr,
+                Display.STATE_OFF, mAutoBrightnessFallbackStrategy,
+                /* lastUserSetScreenBrightness= */ PowerManager.BRIGHTNESS_INVALID_FLOAT,
+                /* userSetBrightnessChanged= */ false,
+                /* allowAutoBrightnessWhileDozingConfig= */ false,
+                /* isAutoBrightnessEnabled= */ true);
+        mAutoBrightnessFallbackStrategy.strategySelectionPostProcessor(ssnr);
+
+        verify(mScreenOffBrightnessSensorController).setLightSensorEnabled(false);
+    }
+
+    @Test
+    public void testPostProcess_DisableSensor_PolicyBright() {
+        boolean isDisplayEnabled = true;
+        int leadDisplayId = NO_LEAD_DISPLAY;
+        mAutoBrightnessFallbackStrategy.setupAutoBrightnessFallbackSensor(mSensorManager,
+                mDisplayDeviceConfig,
+                mHandler, mBrightnessMappingStrategy, isDisplayEnabled, leadDisplayId);
+
+        DisplayManagerInternal.DisplayPowerRequest dpr =
+                new DisplayManagerInternal.DisplayPowerRequest();
+        dpr.policy = POLICY_BRIGHT;
+        StrategySelectionNotifyRequest ssnr = new StrategySelectionNotifyRequest(dpr,
+                Display.STATE_ON, mAutoBrightnessFallbackStrategy,
+                /* lastUserSetScreenBrightness= */ PowerManager.BRIGHTNESS_INVALID_FLOAT,
+                /* userSetBrightnessChanged= */ false,
+                /* allowAutoBrightnessWhileDozingConfig= */ false,
+                /* isAutoBrightnessEnabled= */ true);
+        mAutoBrightnessFallbackStrategy.strategySelectionPostProcessor(ssnr);
+
+        verify(mScreenOffBrightnessSensorController).setLightSensorEnabled(false);
+    }
+
+    @Test
+    public void testPostProcess_DisableSensor_AutoBrightnessInDoze() {
+        boolean isDisplayEnabled = true;
+        int leadDisplayId = NO_LEAD_DISPLAY;
+        mAutoBrightnessFallbackStrategy.setupAutoBrightnessFallbackSensor(mSensorManager,
+                mDisplayDeviceConfig,
+                mHandler, mBrightnessMappingStrategy, isDisplayEnabled, leadDisplayId);
+
+        DisplayManagerInternal.DisplayPowerRequest dpr =
+                new DisplayManagerInternal.DisplayPowerRequest();
+        dpr.policy = POLICY_DOZE;
+        StrategySelectionNotifyRequest ssnr = new StrategySelectionNotifyRequest(dpr,
+                Display.STATE_DOZE, mAutoBrightnessFallbackStrategy,
+                /* lastUserSetScreenBrightness= */ PowerManager.BRIGHTNESS_INVALID_FLOAT,
+                /* userSetBrightnessChanged= */ false,
+                /* allowAutoBrightnessWhileDozingConfig= */ true,
+                /* isAutoBrightnessEnabled= */ true);
+        mAutoBrightnessFallbackStrategy.strategySelectionPostProcessor(ssnr);
+
+        verify(mScreenOffBrightnessSensorController).setLightSensorEnabled(false);
+    }
+
+    @Test
+    public void testPostProcess_DisableSensor_DisplayIsFollower() {
+        boolean isDisplayEnabled = true;
+        int leadDisplayId = 3;
+        mAutoBrightnessFallbackStrategy.setupAutoBrightnessFallbackSensor(mSensorManager,
+                mDisplayDeviceConfig,
+                mHandler, mBrightnessMappingStrategy, isDisplayEnabled, leadDisplayId);
+
+        DisplayManagerInternal.DisplayPowerRequest dpr =
+                new DisplayManagerInternal.DisplayPowerRequest();
+        dpr.policy = POLICY_OFF;
+        StrategySelectionNotifyRequest ssnr = new StrategySelectionNotifyRequest(dpr,
+                Display.STATE_OFF, mAutoBrightnessFallbackStrategy,
+                /* lastUserSetScreenBrightness= */ PowerManager.BRIGHTNESS_INVALID_FLOAT,
+                /* userSetBrightnessChanged= */ false,
+                /* allowAutoBrightnessWhileDozingConfig= */ false,
+                /* isAutoBrightnessEnabled= */ true);
+        mAutoBrightnessFallbackStrategy.strategySelectionPostProcessor(ssnr);
+
+        verify(mScreenOffBrightnessSensorController).setLightSensorEnabled(false);
+    }
 }
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
index 242d559..62400eb 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
@@ -1088,6 +1088,21 @@
     }
 
     @Test
+    public void testModeSwitching_UserSwitch() {
+        DisplayModeDirector director = createDirectorFromFpsRange(0, 90);
+        assertThat(director.getModeSwitchingType()).isEqualTo(
+                DisplayManager.SWITCHING_TYPE_WITHIN_GROUPS);
+
+        int newModeSwitchingType = DisplayManager.SWITCHING_TYPE_NONE;
+        Settings.Secure.putInt(mContext.getContentResolver(),
+                Settings.Secure.MATCH_CONTENT_FRAME_RATE, newModeSwitchingType);
+        director.onSwitchUser();
+        waitForIdleSync();
+
+        assertThat(director.getModeSwitchingType()).isEqualTo(newModeSwitchingType);
+    }
+
+    @Test
     public void testDefaultDisplayModeIsSelectedIfAvailable() {
         final float[] refreshRates = new float[]{24f, 25f, 30f, 60f, 90f};
         final int defaultModeId = 3;
@@ -1883,6 +1898,62 @@
     }
 
     @Test
+    public void testPeakRefreshRate_UserSwitch() {
+        when(mDisplayManagerFlags.isBackUpSmoothDisplayAndForcePeakRefreshRateEnabled())
+                .thenReturn(true);
+        DisplayModeDirector director =
+                new DisplayModeDirector(mContext, mHandler, mInjector,
+                        mDisplayManagerFlags, mDisplayDeviceConfigProvider);
+        director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON);
+
+        Display.Mode[] modes1 = new Display.Mode[] {
+                new Display.Mode(/* modeId= */ 1, /* width= */ 1280, /* height= */ 720,
+                        /* refreshRate= */ 60),
+                new Display.Mode(/* modeId= */ 2, /* width= */ 1280, /* height= */ 720,
+                        /* refreshRate= */ 130),
+        };
+        Display.Mode[] modes2 = new Display.Mode[] {
+                new Display.Mode(/* modeId= */ 1, /* width= */ 1280, /* height= */ 720,
+                        /* refreshRate= */ 60),
+                new Display.Mode(/* modeId= */ 2, /* width= */ 1280, /* height= */ 720,
+                        /* refreshRate= */ 140),
+        };
+        SparseArray<Display.Mode[]> supportedModesByDisplay = new SparseArray<>();
+        supportedModesByDisplay.put(DISPLAY_ID, modes1);
+        supportedModesByDisplay.put(DISPLAY_ID_2, modes2);
+
+        Sensor lightSensor = createLightSensor();
+        SensorManager sensorManager = createMockSensorManager(lightSensor);
+        director.start(sensorManager);
+        director.injectSupportedModesByDisplay(supportedModesByDisplay);
+
+        // Disable Smooth Display
+        setPeakRefreshRate(RefreshRateSettingsUtils.DEFAULT_REFRESH_RATE);
+
+        Vote vote1 = director.getVote(DISPLAY_ID,
+                Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE);
+        Vote vote2 = director.getVote(DISPLAY_ID_2,
+                Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE);
+        assertVoteForRenderFrameRateRange(vote1, /* frameRateLow= */ 0,
+                /* frameRateHigh= */ RefreshRateSettingsUtils.DEFAULT_REFRESH_RATE);
+        assertVoteForRenderFrameRateRange(vote2, /* frameRateLow= */ 0,
+                /* frameRateHigh= */ RefreshRateSettingsUtils.DEFAULT_REFRESH_RATE);
+
+        // Switch user to one that has Smooth Display Enabled
+        Settings.System.putFloat(mContext.getContentResolver(), Settings.System.PEAK_REFRESH_RATE,
+                Float.POSITIVE_INFINITY);
+        director.onSwitchUser();
+        waitForIdleSync();
+
+        vote1 = director.getVote(DISPLAY_ID,
+                Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE);
+        vote2 = director.getVote(DISPLAY_ID_2,
+                Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE);
+        assertVoteForRenderFrameRateRange(vote1, /* frameRateLow= */ 0, /* frameRateHigh= */ 130);
+        assertVoteForRenderFrameRateRange(vote2, /* frameRateLow= */ 0, /* frameRateHigh= */ 140);
+    }
+
+    @Test
     @Parameters({
         "true, true, 60",
         "false, true, 50",
@@ -2036,6 +2107,62 @@
     }
 
     @Test
+    public void testMinRefreshRate_UserSwitch() {
+        when(mDisplayManagerFlags.isBackUpSmoothDisplayAndForcePeakRefreshRateEnabled())
+                .thenReturn(true);
+        DisplayModeDirector director =
+                new DisplayModeDirector(mContext, mHandler, mInjector,
+                        mDisplayManagerFlags, mDisplayDeviceConfigProvider);
+        director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON);
+
+        Display.Mode[] modes1 = new Display.Mode[] {
+                new Display.Mode(/* modeId= */ 1, /* width= */ 1280, /* height= */ 720,
+                        /* refreshRate= */ 60),
+                new Display.Mode(/* modeId= */ 2, /* width= */ 1280, /* height= */ 720,
+                        /* refreshRate= */ 130),
+        };
+        Display.Mode[] modes2 = new Display.Mode[] {
+                new Display.Mode(/* modeId= */ 1, /* width= */ 1280, /* height= */ 720,
+                        /* refreshRate= */ 60),
+                new Display.Mode(/* modeId= */ 2, /* width= */ 1280, /* height= */ 720,
+                        /* refreshRate= */ 140),
+        };
+        SparseArray<Display.Mode[]> supportedModesByDisplay = new SparseArray<>();
+        supportedModesByDisplay.put(DISPLAY_ID, modes1);
+        supportedModesByDisplay.put(DISPLAY_ID_2, modes2);
+
+        Sensor lightSensor = createLightSensor();
+        SensorManager sensorManager = createMockSensorManager(lightSensor);
+        director.start(sensorManager);
+        director.injectSupportedModesByDisplay(supportedModesByDisplay);
+
+        // Disable Force Peak Refresh Rate
+        setMinRefreshRate(0);
+
+        Vote vote1 = director.getVote(DISPLAY_ID, Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE);
+        Vote vote2 = director.getVote(DISPLAY_ID_2,
+                Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE);
+        assertVoteForRenderFrameRateRange(vote1, /* frameRateLow= */ 0,
+                /* frameRateHigh= */ Float.POSITIVE_INFINITY);
+        assertVoteForRenderFrameRateRange(vote2, /* frameRateLow= */ 0,
+                /* frameRateHigh= */ Float.POSITIVE_INFINITY);
+
+        // Switch user to one that has Force Peak Refresh Rate enabled
+        Settings.System.putFloat(mContext.getContentResolver(), Settings.System.MIN_REFRESH_RATE,
+                Float.POSITIVE_INFINITY);
+        director.onSwitchUser();
+        waitForIdleSync();
+
+        vote1 = director.getVote(DISPLAY_ID, Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE);
+        vote2 = director.getVote(DISPLAY_ID_2,
+                Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE);
+        assertVoteForRenderFrameRateRange(vote1, /* frameRateLow= */ 130,
+                /* frameRateHigh= */ Float.POSITIVE_INFINITY);
+        assertVoteForRenderFrameRateRange(vote2, /* frameRateLow= */ 140,
+                /* frameRateHigh= */ Float.POSITIVE_INFINITY);
+    }
+
+    @Test
     public void testPeakAndMinRefreshRate_FlagEnabled_DisplayWithOneMode() {
         when(mDisplayManagerFlags.isBackUpSmoothDisplayAndForcePeakRefreshRateEnabled())
                 .thenReturn(true);
diff --git a/services/tests/dreamservicetests/src/com/android/server/dreams/DreamAccessibilityTest.java b/services/tests/dreamservicetests/src/com/android/server/dreams/DreamAccessibilityTest.java
index 99968d5..9da695a 100644
--- a/services/tests/dreamservicetests/src/com/android/server/dreams/DreamAccessibilityTest.java
+++ b/services/tests/dreamservicetests/src/com/android/server/dreams/DreamAccessibilityTest.java
@@ -19,10 +19,12 @@
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.argThat;
-import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+
 
 import android.content.Context;
 import android.content.res.Resources;
@@ -44,9 +46,6 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
-import java.util.ArrayList;
-import java.util.Collections;
-
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class DreamAccessibilityTest {
@@ -73,7 +72,8 @@
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
-        mDreamAccessibility = new DreamAccessibility(mContext, mView);
+        Runnable mDismissCallback = () -> {};
+        mDreamAccessibility = new DreamAccessibility(mContext, mView, mDismissCallback);
 
         when(mContext.getResources()).thenReturn(mResources);
         when(mResources.getString(R.string.dream_accessibility_action_click))
@@ -84,80 +84,55 @@
      */
     @Test
     public void testConfigureAccessibilityActions() {
-        when(mAccessibilityNodeInfo.getActionList()).thenReturn(new ArrayList<>());
+        when(mView.getAccessibilityDelegate()).thenReturn(null);
 
-        mDreamAccessibility.updateAccessibilityConfiguration(false);
+        mDreamAccessibility.updateAccessibilityConfiguration();
 
         verify(mView).setAccessibilityDelegate(mAccessibilityDelegateArgumentCaptor.capture());
-        View.AccessibilityDelegate capturedDelegate =
-                mAccessibilityDelegateArgumentCaptor.getValue();
+        View.AccessibilityDelegate capturedDelegate = mAccessibilityDelegateArgumentCaptor
+                .getValue();
 
         capturedDelegate.onInitializeAccessibilityNodeInfo(mView, mAccessibilityNodeInfo);
 
         verify(mAccessibilityNodeInfo).addAction(argThat(action ->
-                action.getId() == AccessibilityNodeInfo.ACTION_CLICK
+                action.getId() == AccessibilityNodeInfo.ACTION_DISMISS
                         && TextUtils.equals(action.getLabel(), CUSTOM_ACTION)));
     }
 
     /**
-     * Test to verify the configuration of accessibility actions within a view delegate,
-     * specifically checking the removal of an existing click action and addition
-     * of a new custom action.
+     * Test to verify no accessibility configuration is added if one exist.
      */
     @Test
-    public void testConfigureAccessibilityActions_RemovesExistingClickAction() {
-        AccessibilityNodeInfo.AccessibilityAction existingAction =
-                new AccessibilityNodeInfo.AccessibilityAction(AccessibilityNodeInfo.ACTION_CLICK,
-                        EXISTING_ACTION);
-        when(mAccessibilityNodeInfo.getActionList())
-                .thenReturn(Collections.singletonList(existingAction));
+    public void testNotAddingDuplicateAccessibilityConfiguration() {
+        View.AccessibilityDelegate existingDelegate = mock(View.AccessibilityDelegate.class);
+        when(mView.getAccessibilityDelegate()).thenReturn(existingDelegate);
 
-        mDreamAccessibility.updateAccessibilityConfiguration(false);
+        mDreamAccessibility.updateAccessibilityConfiguration();
 
-        verify(mView).setAccessibilityDelegate(mAccessibilityDelegateArgumentCaptor.capture());
-        View.AccessibilityDelegate capturedDelegate =
-                mAccessibilityDelegateArgumentCaptor.getValue();
-
-        capturedDelegate.onInitializeAccessibilityNodeInfo(mView, mAccessibilityNodeInfo);
-
-        verify(mAccessibilityNodeInfo).removeAction(existingAction);
-        verify(mAccessibilityNodeInfo).addAction(argThat(action ->
-                action.getId() == AccessibilityNodeInfo.ACTION_CLICK
-                        && TextUtils.equals(action.getLabel(), CUSTOM_ACTION)));
-
-    }
-
-    /**
-     * Test to verify the removal of a custom accessibility action within a view delegate.
-     */
-    @Test
-    public void testRemoveCustomAccessibilityAction() {
-
-        AccessibilityNodeInfo.AccessibilityAction existingAction =
-                new AccessibilityNodeInfo.AccessibilityAction(AccessibilityNodeInfo.ACTION_CLICK,
-                        EXISTING_ACTION);
-        when(mAccessibilityNodeInfo.getActionList())
-                .thenReturn(Collections.singletonList(existingAction));
-
-        mDreamAccessibility.updateAccessibilityConfiguration(false);
-        verify(mView).setAccessibilityDelegate(mAccessibilityDelegateArgumentCaptor.capture());
-        View.AccessibilityDelegate capturedDelegate =
-                mAccessibilityDelegateArgumentCaptor.getValue();
-        when(mView.getAccessibilityDelegate()).thenReturn(capturedDelegate);
-        clearInvocations(mView);
-
-        mDreamAccessibility.updateAccessibilityConfiguration(true);
-        verify(mView).setAccessibilityDelegate(null);
-    }
-
-    /**
-     * Test to verify the removal of custom accessibility action is not called if delegate is not
-     * set by the dreamService.
-     */
-    @Test
-    public void testRemoveCustomAccessibility_DoesNotRemoveDelegateNotSetByDreamAccessibility() {
-        mDreamAccessibility.updateAccessibilityConfiguration(true);
         verify(mView, never()).setAccessibilityDelegate(any());
     }
+
+    /**
+     * Test to verify dismiss callback is called
+     */
+    @Test
+    public void testPerformAccessibilityAction() {
+        Runnable mockDismissCallback = mock(Runnable.class);
+        DreamAccessibility dreamAccessibility = new DreamAccessibility(mContext,
+                mView, mockDismissCallback);
+
+        dreamAccessibility.updateAccessibilityConfiguration();
+
+        verify(mView).setAccessibilityDelegate(mAccessibilityDelegateArgumentCaptor.capture());
+        View.AccessibilityDelegate capturedDelegate = mAccessibilityDelegateArgumentCaptor
+                .getValue();
+
+        boolean result = capturedDelegate.performAccessibilityAction(mView,
+                AccessibilityNodeInfo.ACTION_DISMISS, null);
+
+        assertTrue(result);
+        verify(mockDismissCallback).run();
+    }
+
 }
 
diff --git a/services/tests/mockingservicestests/jni/Android.bp b/services/tests/mockingservicestests/jni/Android.bp
index 1eb9888..00543a8 100644
--- a/services/tests/mockingservicestests/jni/Android.bp
+++ b/services/tests/mockingservicestests/jni/Android.bp
@@ -21,6 +21,7 @@
 
     srcs: [
         ":lib_cachedAppOptimizer_native",
+        ":lib_freezer_native",
         ":lib_gameManagerService_native",
         ":lib_oomConnection_native",
         "onload.cpp",
diff --git a/services/tests/mockingservicestests/jni/onload.cpp b/services/tests/mockingservicestests/jni/onload.cpp
index fb91051..cb246d1 100644
--- a/services/tests/mockingservicestests/jni/onload.cpp
+++ b/services/tests/mockingservicestests/jni/onload.cpp
@@ -25,6 +25,7 @@
 
 namespace android {
 int register_android_server_am_CachedAppOptimizer(JNIEnv* env);
+int register_android_server_am_Freezer(JNIEnv* env);
 int register_android_server_app_GameManagerService(JNIEnv* env);
 int register_android_server_am_OomConnection(JNIEnv* env);
 };
@@ -42,8 +43,8 @@
     }
     ALOG_ASSERT(env, "Could not retrieve the env!");
     register_android_server_am_CachedAppOptimizer(env);
+    register_android_server_am_Freezer(env);
     register_android_server_app_GameManagerService(env);
     register_android_server_am_OomConnection(env);
     return JNI_VERSION_1_4;
 }
-
diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
index cb15d6f..b980ca0 100644
--- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
@@ -132,6 +132,7 @@
 import android.content.Intent;
 import android.content.PermissionChecker;
 import android.content.pm.PackageManagerInternal;
+import android.content.pm.UserInfo;
 import android.net.Uri;
 import android.os.BatteryManager;
 import android.os.Bundle;
@@ -149,6 +150,7 @@
 import android.os.ServiceManager;
 import android.os.SystemProperties;
 import android.os.UserHandle;
+import android.os.UserManager;
 import android.platform.test.annotations.DisableFlags;
 import android.platform.test.annotations.EnableFlags;
 import android.platform.test.annotations.Presubmit;
@@ -176,6 +178,7 @@
 import com.android.server.LocalServices;
 import com.android.server.SystemClockTime.TimeConfidence;
 import com.android.server.SystemService;
+import com.android.server.pm.UserManagerInternal;
 import com.android.server.pm.permission.PermissionManagerService;
 import com.android.server.pm.permission.PermissionManagerServiceInternal;
 import com.android.server.pm.pkg.AndroidPackage;
@@ -250,6 +253,8 @@
     @Mock
     private ActivityManagerInternal mActivityManagerInternal;
     @Mock
+    private UserManagerInternal mUserManagerInternal;
+    @Mock
     private ActivityManager mActivityManager;
     @Mock
     private PackageManagerInternal mPackageManagerInternal;
@@ -447,6 +452,8 @@
                 () -> LocalServices.getService(PermissionManagerServiceInternal.class));
         doReturn(mActivityManagerInternal).when(
                 () -> LocalServices.getService(ActivityManagerInternal.class));
+        doReturn(mUserManagerInternal).when(
+                () -> LocalServices.getService(UserManagerInternal.class));
         doReturn(mPackageManagerInternal).when(
                 () -> LocalServices.getService(PackageManagerInternal.class));
         doReturn(mAppStateTracker).when(() -> LocalServices.getService(AppStateTracker.class));
@@ -1252,6 +1259,26 @@
     }
 
     @Test
+    public void wakeupShouldBeScheduledForFullUsers_skipsGuestSystemAndProfiles() {
+        final int systemUserId = 0;
+        final int fullUserId = 10;
+        final int privateProfileId = 12;
+        final int guestUserId = 13;
+        when(mUserManagerInternal.getUserInfo(fullUserId)).thenReturn(new UserInfo(fullUserId,
+                "TestUser2", UserInfo.FLAG_FULL));
+        when(mUserManagerInternal.getUserInfo(privateProfileId)).thenReturn(new UserInfo(
+                privateProfileId, "TestUser3", UserInfo.FLAG_PROFILE));
+        when(mUserManagerInternal.getUserInfo(guestUserId)).thenReturn(new UserInfo(
+                guestUserId, "TestUserGuest", null, 0, UserManager.USER_TYPE_FULL_GUEST));
+        when(mUserManagerInternal.getUserInfo(systemUserId)).thenReturn(new UserInfo(
+                systemUserId, "TestUserSystem", null, 0, UserManager.USER_TYPE_FULL_SYSTEM));
+        assertTrue(mService.shouldAddWakeupForUser(fullUserId));
+        assertFalse(mService.shouldAddWakeupForUser(systemUserId));
+        assertFalse(mService.shouldAddWakeupForUser(privateProfileId));
+        assertFalse(mService.shouldAddWakeupForUser(guestUserId));
+    }
+
+    @Test
     public void sendsTimeTickOnInteractive() {
         final ArgumentCaptor<Runnable> runnableCaptor = ArgumentCaptor.forClass(Runnable.class);
         // Stubbing so the handler doesn't actually run the runnable.
diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/UserWakeupStoreTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/UserWakeupStoreTest.java
index 5bd919f..72883e2 100644
--- a/services/tests/mockingservicestests/src/com/android/server/alarm/UserWakeupStoreTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/alarm/UserWakeupStoreTest.java
@@ -23,7 +23,6 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
-import static org.testng.AssertJUnit.assertFalse;
 
 import android.os.Environment;
 import android.os.FileUtils;
@@ -52,7 +51,6 @@
     private static final int USER_ID_1 = 10;
     private static final int USER_ID_2 = 11;
     private static final int USER_ID_3 = 12;
-    private static final int USER_ID_SYSTEM = 0;
     private static final long TEST_TIMESTAMP = 150_000;
     private static final File TEST_SYSTEM_DIR = new File(InstrumentationRegistry
             .getInstrumentation().getContext().getDataDir(), "alarmsTestDir");
@@ -112,14 +110,6 @@
     }
 
     @Test
-    public void testAddWakeupForSystemUser_shouldDoNothing() {
-        mUserWakeupStore.addUserWakeup(USER_ID_SYSTEM, TEST_TIMESTAMP - 19_000);
-        assertEquals(0, mUserWakeupStore.getUserIdsToWakeup(TEST_TIMESTAMP).length);
-        final File file = new File(ROOT_DIR , "usersWithAlarmClocks.xml");
-        assertFalse(file.exists());
-    }
-
-    @Test
     public void testAddMultipleWakeupsForUser_ensureOnlyLastWakeupRemains() {
         final long finalAlarmTime = TEST_TIMESTAMP - 13_000;
         mUserWakeupStore.addUserWakeup(USER_ID_1, TEST_TIMESTAMP - 29_000);
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java
index adcbf5c..194bf4b 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java
@@ -442,20 +442,24 @@
                 IMPORTANCE_FOREGROUND_SERVICE,       // importance
                 null);                               // description
 
-        // Case 4: Create a process from another package with kill from lmkd
+        /*
+         * Case 4: Create a process from another package with kill from lmkd
+         * We expect LMKD's reported RSS to be the process' last seen RSS.
+         */
         final int app2UidUser2 = 1010234;
         final int app2PidUser2 = 12348;
         final long app2Pss1 = 54321;
         final long app2Rss1 = 65432;
+        final long lmkd_reported_rss = 43215;
         final String app2ProcessName = "com.android.test.stub2:process";
         final String app2PackageName = "com.android.test.stub2";
 
         sleep(1);
         final long now4 = System.currentTimeMillis();
-        doReturn(new Pair<Long, Object>(now4, Integer.valueOf(0)))
+        doReturn(null)
                 .when(mAppExitInfoTracker.mAppExitInfoSourceZygote)
                 .remove(anyInt(), anyInt());
-        doReturn(new Pair<Long, Object>(now4, null))
+        doReturn(new Pair<Long, Object>(now4, Long.valueOf(lmkd_reported_rss)))
                 .when(mAppExitInfoTracker.mAppExitInfoSourceLmkd)
                 .remove(anyInt(), anyInt());
 
@@ -490,7 +494,7 @@
                 null,                                     // subReason
                 0,                                        // status
                 app2Pss1,                                 // pss
-                app2Rss1,                                 // rss
+                lmkd_reported_rss,                        // rss
                 IMPORTANCE_CACHED,                        // importance
                 null);                                    // description
 
@@ -499,6 +503,11 @@
         mAppExitInfoTracker.getExitInfo(null, app2UidUser2, 0, 0, list);
         assertEquals(1, list.size());
 
+        info = list.get(0);
+
+        // Verify the AppExitInfo has the LMKD reported RSS
+        assertEquals(lmkd_reported_rss, info.getRss());
+
         // Case 5: App native crash
         final int app3UidUser2 = 1010345;
         final int app3PidUser2 = 12349;
@@ -599,7 +608,7 @@
                 null,                                     // subReason
                 0,                                        // status
                 app2Pss1,                                 // pss
-                app2Rss1,                                 // rss
+                lmkd_reported_rss,                        // rss
                 IMPORTANCE_CACHED,                        // importance
                 null);                                    // description
 
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java b/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java
index 03439e55..32ff569 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java
@@ -22,8 +22,16 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.anyInt;
 import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
 
 import android.content.ComponentName;
 import android.content.Context;
@@ -33,12 +41,14 @@
 import android.os.HandlerThread;
 import android.os.MessageQueue;
 import android.os.Process;
+import android.os.SystemClock;
 import android.platform.test.annotations.Presubmit;
 import android.provider.DeviceConfig;
 import android.text.TextUtils;
 
 import androidx.test.platform.app.InstrumentationRegistry;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.modules.utils.testing.ExtendedMockitoRule;
 import com.android.modules.utils.testing.TestableDeviceConfig;
 import com.android.server.LocalServices;
@@ -55,9 +65,11 @@
 
 import java.io.File;
 import java.io.IOException;
+import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.Set;
 import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executor;
 import java.util.concurrent.TimeUnit;
 
 /**
@@ -79,12 +91,17 @@
     private CountDownLatch mCountDown;
     private ActivityManagerService mAms;
     private Context mContext;
+    private TestFreezer mFreezer;
+    private CountDownLatch mFreezeCounter;
     private TestInjector mInjector;
     private TestProcessDependencies mProcessDependencies;
 
     @Mock
     private PackageManagerInternal mPackageManagerInt;
 
+    // Control whether the freezer mock reports that freezing is enabled or not.
+    private boolean mUseFreezer;
+
     @Rule
     public final ApplicationExitInfoTest.ServiceThreadRule
             mServiceThreadRule = new ApplicationExitInfoTest.ServiceThreadRule();
@@ -103,9 +120,12 @@
                 true /* allowIo */);
         mThread.start();
         mContext = InstrumentationRegistry.getInstrumentation().getContext();
+
+        mUseFreezer = false;
+        mFreezer = new TestFreezer();
+
         mInjector = new TestInjector(mContext);
-        mAms = new ActivityManagerService(
-                new TestInjector(mContext), mServiceThreadRule.getThread());
+        mAms = new ActivityManagerService(mInjector, mServiceThreadRule.getThread());
         doReturn(new ComponentName("", "")).when(mPackageManagerInt).getSystemUiServiceComponent();
         mProcessDependencies = new TestProcessDependencies();
         mCachedAppOptimizerUnderTest = new CachedAppOptimizer(mAms,
@@ -126,6 +146,7 @@
         mHandlerThread.quit();
         mThread.quit();
         mCountDown = null;
+        mFreezeCounter = null;
     }
 
     private ProcessRecord makeProcessRecord(int pid, int uid, int packageUid, String processName,
@@ -179,7 +200,7 @@
         assertThat(mCachedAppOptimizerUnderTest.mProcStateThrottle)
                 .containsExactlyElementsIn(expected);
 
-        Assume.assumeTrue(mCachedAppOptimizerUnderTest.isFreezerSupported());
+        Assume.assumeTrue(mAms.isAppFreezerSupported());
         assertThat(mCachedAppOptimizerUnderTest.useFreezer()).isEqualTo(
                 CachedAppOptimizer.DEFAULT_USE_FREEZER);
     }
@@ -265,8 +286,8 @@
                 CachedAppOptimizer.DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB + 1);
         assertThat(mCachedAppOptimizerUnderTest.mProcStateThrottle).containsExactly(1, 2, 3);
 
-        Assume.assumeTrue(CachedAppOptimizer.isFreezerSupported());
-        if (CachedAppOptimizer.isFreezerSupported()) {
+        Assume.assumeTrue(mAms.isAppFreezerSupported());
+        if (mAms.isAppFreezerSupported()) {
             if (CachedAppOptimizer.DEFAULT_USE_FREEZER) {
                 assertThat(mCachedAppOptimizerUnderTest.useFreezer()).isFalse();
             } else {
@@ -300,7 +321,7 @@
 
     @Test
     public void useFreeze_doesNotListenToDeviceConfigChanges() throws InterruptedException {
-        Assume.assumeTrue(CachedAppOptimizer.isFreezerSupported());
+        Assume.assumeTrue(mAms.isAppFreezerSupported());
 
         assertThat(mCachedAppOptimizerUnderTest.useFreezer()).isFalse();
 
@@ -353,7 +374,7 @@
 
     @Test
     public void useFreeze_listensToDeviceConfigChangesBadValues() throws InterruptedException {
-        Assume.assumeTrue(CachedAppOptimizer.isFreezerSupported());
+        Assume.assumeTrue(mAms.isAppFreezerSupported());
         assertThat(mCachedAppOptimizerUnderTest.useFreezer()).isFalse();
 
         // When we push an invalid flag value...
@@ -982,6 +1003,40 @@
         }
     }
 
+    @Test
+    public void testFreezerDelegator() throws Exception {
+        mUseFreezer = true;
+        mProcessDependencies.setRss(new long[] {
+                    0 /*total_rss*/,
+                    0 /*file*/,
+                    0 /*anon*/,
+                    0 /*swap*/,
+                    0 /*shmem*/
+                });
+
+        // Force the system to use the freezer
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT,
+                CachedAppOptimizer.KEY_USE_FREEZER, "true", false);
+        mCachedAppOptimizerUnderTest.init();
+        initActivityManagerService();
+
+        assertTrue(mAms.isAppFreezerSupported());
+        assertThat(mCachedAppOptimizerUnderTest.useFreezer()).isTrue();
+
+        int pid = 10000;
+        int uid = 2;
+        int pkgUid = 3;
+        ProcessRecord app = makeProcessRecord(pid, uid, pkgUid, "p1", "app1");
+
+        mFreezeCounter = new CountDownLatch(1);
+        mCachedAppOptimizerUnderTest.forceFreezeForTest(app, true);
+        assertTrue(mFreezeCounter.await(5, TimeUnit.SECONDS));
+
+        mFreezeCounter = new CountDownLatch(1);
+        mCachedAppOptimizerUnderTest.forceFreezeForTest(app, false);
+        assertTrue(mFreezeCounter.await(5, TimeUnit.SECONDS));
+    }
+
     private void setFlag(String key, String value, boolean defaultValue) throws Exception {
         mCountDown = new CountDownLatch(1);
         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, key, value, defaultValue);
@@ -1042,6 +1097,11 @@
         public Handler getUiHandler(ActivityManagerService service) {
             return mHandler;
         }
+
+        @Override
+        public Freezer getFreezer() {
+            return mFreezer;
+        }
     }
 
     // Test implementation for ProcessDependencies.
@@ -1069,4 +1129,27 @@
             mRssAfterCompaction = newValues;
         }
     }
+
+    // Intercept Freezer calls.
+    private class TestFreezer extends Freezer {
+        @Override
+        public void setProcessFrozen(int pid, int uid, boolean frozen) {
+            mFreezeCounter.countDown();
+        }
+
+        @Override
+        public int freezeBinder(int pid, boolean freeze, int timeoutMs) {
+            return 0;
+        }
+
+        @Override
+        public int getBinderFreezeInfo(int pid) {
+            return 0;
+        }
+
+        @Override
+        public boolean isFreezerSupported() {
+            return mUseFreezer;
+        }
+    }
 }
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/OWNERS b/services/tests/mockingservicestests/src/com/android/server/am/OWNERS
index 2cbc226..4fac647 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/OWNERS
+++ b/services/tests/mockingservicestests/src/com/android/server/am/OWNERS
@@ -1,3 +1,4 @@
 include /services/core/java/com/android/server/am/OWNERS
 
 per-file ApplicationStartInfoTest.java = yforta@google.com, carmenjackson@google.com, jji@google.com
+per-file CachedAppOptimizerTest.java = file:/PERFORMANCE_OWNERS
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ProcessObserverTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ProcessObserverTest.java
index 3572d23..014b98c 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/ProcessObserverTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/ProcessObserverTest.java
@@ -56,6 +56,7 @@
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 import org.mockito.Mock;
@@ -238,6 +239,7 @@
     /**
      * Verify that a process start event is dispatched to process observers.
      */
+    @Ignore("b/323959187")
     @Test
     public void testNormal() throws Exception {
         ProcessRecord app = startProcess();
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java
index 8d0b279..fc28f9e 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java
@@ -266,8 +266,8 @@
         rule.mocks().getHandler().flush();
 
         ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
-        verify(mIntentSender).sendIntent(any(), anyInt(), intentCaptor.capture(), any(), any(),
-                any(), any());
+        verify(mIntentSender).sendIntent(any(), anyInt(), intentCaptor.capture(), any(),
+                (Bundle) any(), any(), any());
         Intent value = intentCaptor.getValue();
         assertThat(value.getStringExtra(PackageInstaller.EXTRA_PACKAGE_NAME)).isEqualTo(PACKAGE);
         assertThat(value.getIntExtra(PackageInstaller.EXTRA_STATUS, 0)).isEqualTo(
@@ -336,8 +336,8 @@
         rule.mocks().getHandler().flush();
 
         ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
-        verify(mIntentSender).sendIntent(any(), anyInt(), intentCaptor.capture(), any(), any(),
-                any(), any());
+        verify(mIntentSender).sendIntent(any(), anyInt(), intentCaptor.capture(), any(),
+                (Bundle) any(), any(), any());
         Intent value = intentCaptor.getValue();
         assertThat(value.getStringExtra(PackageInstaller.EXTRA_PACKAGE_NAME)).isEqualTo(PACKAGE);
         assertThat(value.getIntExtra(PackageInstaller.EXTRA_STATUS, 0)).isEqualTo(
diff --git a/services/tests/performancehinttests/Android.bp b/services/tests/performancehinttests/Android.bp
new file mode 100644
index 0000000..1692921c
--- /dev/null
+++ b/services/tests/performancehinttests/Android.bp
@@ -0,0 +1,34 @@
+package {
+    default_team: "trendy_team_games",
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test {
+    name: "PerformanceHintTests",
+    srcs: [
+        "src/**/*.java",
+    ],
+    static_libs: [
+        "androidx.test.runner",
+        "androidx.test.rules",
+        "flag-junit",
+        "junit",
+        "mockito-target-minus-junit4",
+        "platform-test-annotations",
+        "services.core",
+        "truth",
+    ],
+    libs: [
+        "android.test.base",
+    ],
+    test_suites: [
+        "automotive-tests",
+        "device-tests",
+    ],
+    platform_apis: true,
+    certificate: "platform",
+    dxflags: ["--multi-dex"],
+    optimize: {
+        enabled: false,
+    },
+}
diff --git a/services/tests/performancehinttests/AndroidManifest.xml b/services/tests/performancehinttests/AndroidManifest.xml
new file mode 100644
index 0000000..d955234
--- /dev/null
+++ b/services/tests/performancehinttests/AndroidManifest.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2024 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+     package="com.android.server.power.hinttests">
+
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+         android:targetPackage="com.android.server.power.hinttests"
+         android:label="ADPF Performance Hint Service Test"/>
+</manifest>
diff --git a/services/tests/performancehinttests/AndroidTest.xml b/services/tests/performancehinttests/AndroidTest.xml
new file mode 100644
index 0000000..578b7d6
--- /dev/null
+++ b/services/tests/performancehinttests/AndroidTest.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2024 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<configuration description="Runs Performance Hint Tests.">
+    <option name="test-suite-tag" value="apct" />
+    <option name="test-suite-tag" value="apct-instrumentation" />
+
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true" />
+        <option name="install-arg" value="-t" />
+        <option name="test-file-name" value="PerformanceHintTests.apk" />
+    </target_preparer>
+
+    <option name="test-tag" value="PerformanceHintTests" />
+
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="package" value="com.android.server.power.hinttests" />
+        <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+        <option name="hidden-api-checks" value="false"/>
+        <option name="exclude-annotation" value="androidx.test.filters.FlakyTest" />
+    </test>
+</configuration>
diff --git a/services/tests/servicestests/src/com/android/server/power/hint/OWNERS b/services/tests/performancehinttests/OWNERS
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/power/hint/OWNERS
rename to services/tests/performancehinttests/OWNERS
diff --git a/services/tests/performancehinttests/TEST_MAPPING b/services/tests/performancehinttests/TEST_MAPPING
new file mode 100644
index 0000000..fa7b897
--- /dev/null
+++ b/services/tests/performancehinttests/TEST_MAPPING
@@ -0,0 +1,19 @@
+{
+  "presubmit": [
+    {
+      "name": "PerformanceHintTests",
+      "options": [
+        {"exclude-annotation": "org.junit.Ignore"}
+      ]
+    }
+  ],
+  "ravenwood-postsubmit": [
+    {
+      "name": "PerformanceHintTestsRavenwood",
+      "host": true,
+      "options": [
+        {"exclude-annotation": "android.platform.test.annotations.DisabledOnRavenwood"}
+      ]
+    }
+  ]
+}
diff --git a/services/tests/servicestests/src/com/android/server/power/hint/HintManagerServiceTest.java b/services/tests/performancehinttests/src/com/android/server/power/hint/HintManagerServiceTest.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/power/hint/HintManagerServiceTest.java
rename to services/tests/performancehinttests/src/com/android/server/power/hint/HintManagerServiceTest.java
index 1decd36..7d04470 100644
--- a/services/tests/servicestests/src/com/android/server/power/hint/HintManagerServiceTest.java
+++ b/services/tests/performancehinttests/src/com/android/server/power/hint/HintManagerServiceTest.java
@@ -596,7 +596,7 @@
                 ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND, 0, 0);
         service.mUidObserver.onUidStateChanged(UID,
                 ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND, 0, 0);
-        LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(500) + TimeUnit.MILLISECONDS.toNanos(
+        LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(1000) + TimeUnit.MILLISECONDS.toNanos(
                 CLEAN_UP_UID_DELAY_MILLIS));
         verify(mNativeWrapperMock, never()).halSetThreads(eq(sessionPtr1), any());
         reset(mNativeWrapperMock);
@@ -653,7 +653,7 @@
                 ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND, 0, 0);
         service.mUidObserver.onUidStateChanged(UID,
                 ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND, 0, 0);
-        LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(500) + TimeUnit.MILLISECONDS.toNanos(
+        LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(1000) + TimeUnit.MILLISECONDS.toNanos(
                 CLEAN_UP_UID_DELAY_MILLIS));
         verify(mNativeWrapperMock, never()).halSetThreads(eq(sessionPtr1), any());
         verify(mNativeWrapperMock, never()).halSetThreads(eq(sessionPtr2), any());
@@ -666,7 +666,7 @@
         service.mUidObserver.onUidStateChanged(UID,
                 ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND, 0, 0);
         // wait for the async uid state change to trigger resume and setThreads
-        LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(500));
+        LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(1000));
         verify(mNativeWrapperMock, times(1)).halSetThreads(eq(sessionPtr2), eq(expectedTids2));
         reset(mNativeWrapperMock);
 
@@ -675,7 +675,7 @@
         LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(100));
         service.mUidObserver.onUidStateChanged(UID,
                 ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND, 0, 0);
-        LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(500) + TimeUnit.MILLISECONDS.toNanos(
+        LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(1000) + TimeUnit.MILLISECONDS.toNanos(
                 CLEAN_UP_UID_DELAY_MILLIS));
         verify(mNativeWrapperMock, times(1)).halPauseHintSession(eq(sessionPtr1));
         verify(mNativeWrapperMock, never()).halSetThreads(eq(sessionPtr1), any());
@@ -684,7 +684,7 @@
         verifyAllHintsEnabled(session2, false);
         service.mUidObserver.onUidStateChanged(UID,
                 ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND, 0, 0);
-        LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(500));
+        LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(1000));
         verifyAllHintsEnabled(session1, false);
         verifyAllHintsEnabled(session2, true);
         reset(mNativeWrapperMock);
@@ -705,7 +705,7 @@
         LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(100));
         service.mUidObserver.onUidStateChanged(UID,
                 ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND, 0, 0);
-        LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(500) + TimeUnit.MILLISECONDS.toNanos(
+        LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(1000) + TimeUnit.MILLISECONDS.toNanos(
                 CLEAN_UP_UID_DELAY_MILLIS));
         verify(mNativeWrapperMock, times(1)).halPauseHintSession(eq(sessionPtr1));
         verify(mNativeWrapperMock, never()).halSetThreads(eq(sessionPtr1), any());
@@ -721,7 +721,7 @@
 
         service.mUidObserver.onUidStateChanged(UID,
                 ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND, 0, 0);
-        LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(500) + TimeUnit.MILLISECONDS.toNanos(
+        LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(1000) + TimeUnit.MILLISECONDS.toNanos(
                 CLEAN_UP_UID_DELAY_MILLIS));
         verify(mNativeWrapperMock, times(1)).halSetThreads(eq(sessionPtr1),
                 eq(SESSION_TIDS_A));
diff --git a/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java b/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java
index ce2bb95..d45e312 100644
--- a/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java
+++ b/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java
@@ -44,6 +44,7 @@
 import android.os.RemoteException;
 import android.os.VibrationAttributes;
 import android.os.Vibrator;
+import android.os.WorkSource;
 import android.os.test.TestLooper;
 import android.provider.Settings;
 import android.testing.TestableContext;
@@ -60,6 +61,7 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.Mock;
+import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 
 import java.util.concurrent.Executor;
@@ -231,7 +233,7 @@
     }
 
     @Test
-    public void testOnWakeLockListener_RemoteException_NoRethrow() {
+    public void testOnWakeLockListener_RemoteException_NoRethrow() throws RemoteException {
         when(mPowerManagerFlags.improveWakelockLatency()).thenReturn(true);
         createNotifier();
         clearInvocations(mWakeLockLog, mBatteryStats, mAppOpsManager);
@@ -249,33 +251,58 @@
         verifyZeroInteractions(mWakeLockLog);
         mTestLooper.dispatchAll();
         verify(mWakeLockLog).onWakeLockReleased("wakelockTag", uid, 1);
-
+        clearInvocations(mBatteryStats);
         mNotifier.onWakeLockAcquired(PowerManager.PARTIAL_WAKE_LOCK, "wakelockTag",
                 "my.package.name", uid, pid, /* workSource= */ null, /* historyTag= */ null,
                 exceptingCallback);
-        mNotifier.onWakeLockChanging(PowerManager.PARTIAL_WAKE_LOCK, "wakelockTag",
-                "my.package.name", uid, pid, /* workSource= */ null, /* historyTag= */ null,
-                exceptingCallback,
-                PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "wakelockTag",
-                "my.package.name", uid, pid, /* newWorkSource= */ null, /* newHistoryTag= */ null,
-                exceptingCallback);
-        verifyNoMoreInteractions(mWakeLockLog);
+
+        verifyNoMoreInteractions(mWakeLockLog, mBatteryStats);
         mTestLooper.dispatchAll();
         verify(mWakeLockLog).onWakeLockAcquired("wakelockTag", uid,
                 PowerManager.PARTIAL_WAKE_LOCK, 1);
+        verify(mBatteryStats).noteStartWakelock(uid, pid, "wakelockTag", /* historyTag= */ null,
+                BatteryStats.WAKE_TYPE_PARTIAL, false);
+
+        verifyNoMoreInteractions(mWakeLockLog, mBatteryStats);
+        WorkSource worksourceOld = Mockito.mock(WorkSource.class);
+        WorkSource worksourceNew = Mockito.mock(WorkSource.class);
+
+        mNotifier.onWakeLockChanging(PowerManager.PARTIAL_WAKE_LOCK, "wakelockTag",
+                "my.package.name", uid, pid, worksourceOld, /* historyTag= */ null,
+                exceptingCallback,
+                PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "wakelockTag",
+                "my.package.name", uid, pid, worksourceNew, /* newHistoryTag= */ null,
+                exceptingCallback);
+        mTestLooper.dispatchAll();
+        verify(mBatteryStats).noteChangeWakelockFromSource(worksourceOld, pid, "wakelockTag",
+                null, BatteryStats.WAKE_TYPE_PARTIAL, worksourceNew, pid, "wakelockTag",
+                null, BatteryStats.WAKE_TYPE_FULL, false);
         // If we didn't throw, we're good!
 
         // Test with improveWakelockLatency flag false, hence the wakelock log will run on the same
         // thread
-        clearInvocations(mWakeLockLog);
+        clearInvocations(mWakeLockLog, mBatteryStats);
         when(mPowerManagerFlags.improveWakelockLatency()).thenReturn(false);
 
+        // Acquire the wakelock
         mNotifier.onWakeLockAcquired(PowerManager.PARTIAL_WAKE_LOCK, "wakelockTag",
                 "my.package.name", uid, pid, /* workSource= */ null, /* historyTag= */ null,
                 exceptingCallback);
         verify(mWakeLockLog).onWakeLockAcquired("wakelockTag", uid,
                 PowerManager.PARTIAL_WAKE_LOCK, -1);
 
+        // Update the wakelock
+        mNotifier.onWakeLockChanging(PowerManager.PARTIAL_WAKE_LOCK, "wakelockTag",
+                "my.package.name", uid, pid, worksourceOld, /* historyTag= */ null,
+                exceptingCallback,
+                PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "wakelockTag",
+                "my.package.name", uid, pid, worksourceNew, /* newHistoryTag= */ null,
+                exceptingCallback);
+        verify(mBatteryStats).noteChangeWakelockFromSource(worksourceOld, pid, "wakelockTag",
+                null, BatteryStats.WAKE_TYPE_PARTIAL, worksourceNew, pid, "wakelockTag",
+                null, BatteryStats.WAKE_TYPE_FULL, false);
+
+        // Release the wakelock
         mNotifier.onWakeLockReleased(PowerManager.PARTIAL_WAKE_LOCK, "wakelockTag",
                 "my.package.name", uid, pid, /* workSource= */ null, /* historyTag= */ null,
                 exceptingCallback);
diff --git a/services/tests/powerservicetests/src/com/android/server/power/PowerGroupTest.java b/services/tests/powerservicetests/src/com/android/server/power/PowerGroupTest.java
index 94b8d68..39def75 100644
--- a/services/tests/powerservicetests/src/com/android/server/power/PowerGroupTest.java
+++ b/services/tests/powerservicetests/src/com/android/server/power/PowerGroupTest.java
@@ -256,7 +256,9 @@
                 .setBrightnessFactor(brightnessFactor)
                 .build();
 
+        CharSequence tag = "my/tag";
         mPowerGroup.updateLocked(/* screenBrightnessOverride= */ BRIGHTNESS,
+                /* overrideTag= */ tag,
                 /* useProximitySensor= */ false,
                 /* boostScreenBrightness= */ false,
                 /* dozeScreenStateOverride= */ Display.STATE_ON,
@@ -274,6 +276,7 @@
                 mPowerGroup.mDisplayPowerRequest;
         assertThat(displayPowerRequest.policy).isEqualTo(POLICY_DIM);
         assertThat(displayPowerRequest.screenBrightnessOverride).isWithin(PRECISION).of(BRIGHTNESS);
+        assertThat(displayPowerRequest.screenBrightnessOverrideTag.toString()).isEqualTo(tag);
         assertThat(displayPowerRequest.useProximitySensor).isEqualTo(false);
         assertThat(displayPowerRequest.boostScreenBrightness).isEqualTo(false);
         assertThat(displayPowerRequest.dozeScreenState).isEqualTo(Display.STATE_UNKNOWN);
@@ -297,6 +300,7 @@
         mPowerGroup.setWakeLockSummaryLocked(WAKE_LOCK_DOZE);
 
         mPowerGroup.updateLocked(/* screenBrightnessOverride= */ BRIGHTNESS,
+                /* overrideTag= */ null,
                 /* useProximitySensor= */ true,
                 /* boostScreenBrightness= */ true,
                 /* dozeScreenStateOverride= */ Display.STATE_ON,
@@ -336,6 +340,7 @@
         assertThat(mPowerGroup.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_DOZING);
 
         mPowerGroup.updateLocked(/* screenBrightnessOverride= */ BRIGHTNESS,
+                /* overrideTag= */ null,
                 /* useProximitySensor= */ true,
                 /* boostScreenBrightness= */ true,
                 /* dozeScreenStateOverride= */ Display.STATE_ON,
@@ -374,6 +379,7 @@
         assertThat(mPowerGroup.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);
 
         mPowerGroup.updateLocked(/* screenBrightnessOverride= */ BRIGHTNESS,
+                /* overrideTag= */ null,
                 /* useProximitySensor= */ true,
                 /* boostScreenBrightness= */ true,
                 /* dozeScreenStateOverride= */ Display.STATE_ON,
@@ -412,6 +418,7 @@
         mPowerGroup.sleepLocked(TIMESTAMP1, UID, GO_TO_SLEEP_REASON_TIMEOUT);
         assertThat(mPowerGroup.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_ASLEEP);
         mPowerGroup.updateLocked(/* screenBrightnessOverride= */ BRIGHTNESS,
+                /* overrideTag= */ null,
                 /* useProximitySensor= */ true,
                 /* boostScreenBrightness= */ true,
                 /* dozeScreenStateOverride= */ Display.STATE_ON,
@@ -451,6 +458,7 @@
         assertThat(mPowerGroup.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_DREAMING);
         mPowerGroup.setWakeLockSummaryLocked(WAKE_LOCK_SCREEN_BRIGHT);
         mPowerGroup.updateLocked(/* screenBrightnessOverride= */ BRIGHTNESS,
+                /* overrideTag= */ null,
                 /* useProximitySensor= */ true,
                 /* boostScreenBrightness= */ true,
                 /* dozeScreenStateOverride= */ Display.STATE_ON,
@@ -488,6 +496,7 @@
                 .build();
         assertThat(mPowerGroup.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);
         mPowerGroup.updateLocked(/* screenBrightnessOverride= */ BRIGHTNESS,
+                /* overrideTag= */ null,
                 /* useProximitySensor= */ true,
                 /* boostScreenBrightness= */ true,
                 /* dozeScreenStateOverride= */ Display.STATE_ON,
@@ -526,6 +535,7 @@
         assertThat(mPowerGroup.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);
         mPowerGroup.setUserActivitySummaryLocked(USER_ACTIVITY_SCREEN_BRIGHT);
         mPowerGroup.updateLocked(/* screenBrightnessOverride= */ BRIGHTNESS,
+                /* overrideTag= */ null,
                 /* useProximitySensor= */ true,
                 /* boostScreenBrightness= */ true,
                 /* dozeScreenStateOverride= */ Display.STATE_ON,
@@ -563,6 +573,7 @@
                 .build();
         assertThat(mPowerGroup.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);
         mPowerGroup.updateLocked(/* screenBrightnessOverride= */ BRIGHTNESS,
+                /* overrideTag= */ null,
                 /* useProximitySensor= */ true,
                 /* boostScreenBrightness= */ true,
                 /* dozeScreenStateOverride= */ Display.STATE_ON,
diff --git a/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java
index b737e0f..40c521a 100644
--- a/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java
+++ b/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java
@@ -1304,6 +1304,7 @@
                 .setDozeOverrideFromDreamManager(
                         Display.STATE_ON,
                         Display.STATE_REASON_DEFAULT_POLICY,
+                        PowerManager.BRIGHTNESS_INVALID_FLOAT,
                         PowerManager.BRIGHTNESS_DEFAULT);
         assertTrue(isAcquired[0]);
     }
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/AmbientDisplayPowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/AmbientDisplayPowerStatsProcessorTest.java
new file mode 100644
index 0000000..8d2849b
--- /dev/null
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/AmbientDisplayPowerStatsProcessorTest.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.power.stats;
+
+import static com.android.server.power.stats.AggregatedPowerStatsConfig.POWER_STATE_BATTERY;
+import static com.android.server.power.stats.AggregatedPowerStatsConfig.POWER_STATE_OTHER;
+import static com.android.server.power.stats.AggregatedPowerStatsConfig.SCREEN_STATE_ON;
+import static com.android.server.power.stats.AggregatedPowerStatsConfig.SCREEN_STATE_OTHER;
+import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_POWER;
+import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_SCREEN;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.when;
+
+import android.hardware.power.stats.EnergyConsumerType;
+import android.os.BatteryConsumer;
+import android.os.Handler;
+import android.platform.test.ravenwood.RavenwoodRule;
+
+import com.android.internal.os.Clock;
+import com.android.internal.os.PowerProfile;
+import com.android.internal.os.PowerStats;
+import com.android.server.power.stats.ScreenPowerStatsCollector.Injector;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.function.IntSupplier;
+
+public class AmbientDisplayPowerStatsProcessorTest {
+
+    @Rule(order = 0)
+    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
+            .setProvideMainThread(true)
+            .build();
+
+    @Rule(order = 1)
+    public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
+            .setNumDisplays(2)
+            .setAveragePowerForOrdinal(PowerProfile.POWER_GROUP_DISPLAY_AMBIENT, 0, 180.0)
+            .setAveragePowerForOrdinal(PowerProfile.POWER_GROUP_DISPLAY_AMBIENT, 1, 360.0);
+
+    private static final double PRECISION = 0.1;
+    private static final int VOLTAGE_MV = 3500;
+
+    @Mock
+    private PowerStatsCollector.ConsumedEnergyRetriever mConsumedEnergyRetriever;
+    @Mock
+    private ScreenPowerStatsCollector.ScreenUsageTimeRetriever mScreenUsageTimeRetriever;
+
+    private final Injector mInjector = new Injector() {
+        @Override
+        public Handler getHandler() {
+            return mStatsRule.getHandler();
+        }
+
+        @Override
+        public Clock getClock() {
+            return mStatsRule.getMockClock();
+        }
+
+        @Override
+        public PowerStatsUidResolver getUidResolver() {
+            return new PowerStatsUidResolver();
+        }
+
+        @Override
+        public long getPowerStatsCollectionThrottlePeriod(String powerComponentName) {
+            return 0;
+        }
+
+        @Override
+        public PowerStatsCollector.ConsumedEnergyRetriever getConsumedEnergyRetriever() {
+            return mConsumedEnergyRetriever;
+        }
+
+        @Override
+        public IntSupplier getVoltageSupplier() {
+            return () -> VOLTAGE_MV;
+        }
+
+        @Override
+        public int getDisplayCount() {
+            return 2;
+        }
+
+        @Override
+        public ScreenPowerStatsCollector.ScreenUsageTimeRetriever getScreenUsageTimeRetriever() {
+            return mScreenUsageTimeRetriever;
+        }
+    };
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+    }
+
+    @Test
+    public void processPowerStats() {
+        PowerComponentAggregatedPowerStats stats = collectAndAggregatePowerStats();
+
+        assertPowerEstimate(stats, POWER_STATE_BATTERY, SCREEN_STATE_OTHER, 16.2);
+        assertPowerEstimate(stats, POWER_STATE_OTHER, SCREEN_STATE_OTHER, 5.4);
+        assertPowerEstimate(stats, POWER_STATE_BATTERY, SCREEN_STATE_ON, 0);
+        assertPowerEstimate(stats, POWER_STATE_OTHER, SCREEN_STATE_ON, 0);
+    }
+
+    private PowerComponentAggregatedPowerStats collectAndAggregatePowerStats() {
+        ScreenPowerStatsProcessor screenPowerStatsProcessor =
+                new ScreenPowerStatsProcessor(mStatsRule.getPowerProfile());
+        AmbientDisplayPowerStatsProcessor ambientDisplayPowerStatsProcessor =
+                new AmbientDisplayPowerStatsProcessor();
+
+        AggregatedPowerStatsConfig config = new AggregatedPowerStatsConfig();
+        config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_SCREEN)
+                .trackDeviceStates(STATE_POWER, STATE_SCREEN)
+                .trackUidStates(STATE_POWER, STATE_SCREEN)
+                .setProcessor(screenPowerStatsProcessor);
+        config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_AMBIENT_DISPLAY,
+                        BatteryConsumer.POWER_COMPONENT_SCREEN)
+                .setProcessor(ambientDisplayPowerStatsProcessor);
+
+        AggregatedPowerStats stats = new AggregatedPowerStats(config);
+
+        stats.setDeviceState(STATE_POWER, POWER_STATE_OTHER, 0);
+        stats.setDeviceState(STATE_SCREEN, SCREEN_STATE_OTHER, 0);
+
+        ScreenPowerStatsCollector collector = new ScreenPowerStatsCollector(mInjector);
+        collector.setEnabled(true);
+
+        when(mConsumedEnergyRetriever.getEnergyConsumerIds(EnergyConsumerType.DISPLAY))
+                .thenReturn(new int[0]);
+
+        stats.addPowerStats(collector.collectStats(), 1000);
+
+        when(mScreenUsageTimeRetriever.getScreenOnTimeMs(0))
+                .thenReturn(60_000L);
+        when(mScreenUsageTimeRetriever.getScreenOnTimeMs(1))
+                .thenReturn(120_000L);
+        when(mScreenUsageTimeRetriever.getScreenDozeTimeMs(0))
+                .thenReturn(180_000L);
+        when(mScreenUsageTimeRetriever.getScreenDozeTimeMs(1))
+                .thenReturn(240_000L);
+        stats.setDeviceState(STATE_POWER, POWER_STATE_BATTERY, 101_000);
+        stats.setDeviceState(STATE_SCREEN, SCREEN_STATE_ON, 401_000);
+
+        // Slightly larger than 600_000 total screen time, to simulate a sight race
+        // between state changes and power stats collection
+        stats.addPowerStats(collector.collectStats(), 612_000);
+
+        stats.finish(612_000);
+
+        return stats.getPowerComponentStats(BatteryConsumer.POWER_COMPONENT_AMBIENT_DISPLAY);
+    }
+
+    private void assertPowerEstimate(PowerComponentAggregatedPowerStats aggregatedStats,
+            int powerState, int screenState, double expectedPowerEstimate) {
+        PowerStats.Descriptor descriptor = aggregatedStats.getPowerStatsDescriptor();
+        PowerStatsLayout layout = new PowerStatsLayout(descriptor);
+        long[] stats = new long[descriptor.statsArrayLength];
+        aggregatedStats.getDeviceStats(stats, new int[]{powerState, screenState});
+        assertThat(layout.getDevicePowerEstimate(stats)).isWithin(PRECISION)
+                .of(expectedPowerEstimate);
+    }
+}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorValidationTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorValidationTest.java
index 644ae47..005ceee 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorValidationTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorValidationTest.java
@@ -130,7 +130,7 @@
         boolean inCpuSection = false;
         for (int i = 0; i < lines.length; i++) {
             if (!inCpuSection) {
-                if (lines[i].startsWith("CpuPowerStatsCollector")) {
+                if (lines[i].startsWith("cpu (1)")) {
                     inCpuSection = true;
                 }
             } else if (lines[i].startsWith(" ")) {
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/CustomEnergyConsumerPowerStatsTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/CustomEnergyConsumerPowerStatsTest.java
index 7bec13f6..1621d47d 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/CustomEnergyConsumerPowerStatsTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/CustomEnergyConsumerPowerStatsTest.java
@@ -149,9 +149,9 @@
                 .isEqualTo(20000);
         assertThat(ps2.uidStats.size()).isEqualTo(2);
         assertThat(POWER_STATS_LAYOUT.getUidConsumedEnergy(ps2.uidStats.get(APP_UID1), 0))
-                .isEqualTo(14000);
+                .isEqualTo(4000);
         assertThat(POWER_STATS_LAYOUT.getUidConsumedEnergy(ps2.uidStats.get(APP_UID2), 0))
-                .isEqualTo(21000);
+                .isEqualTo(6000);
     }
 
     @Test
@@ -209,8 +209,8 @@
         assertThat(POWER_STATS_LAYOUT.getDevicePowerEstimate(deviceStats))
                 .isWithin(PRECISION).of(expectedPower * 0.75);
 
-        // UID1: estimated power = 14,000 uC = 0.00388 mAh
-        expectedPower = 0.00388;
+        // UID1: estimated power = 4,000 uC = 0.00111 mAh
+        expectedPower = 0.00111;
         ps2.getUidStats(uidStats, APP_UID1,
                 states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_FOREGROUND));
         assertThat(POWER_STATS_LAYOUT.getUidPowerEstimate(uidStats))
@@ -221,8 +221,8 @@
         assertThat(POWER_STATS_LAYOUT.getUidPowerEstimate(uidStats))
                 .isWithin(PRECISION).of(expectedPower * 0.75);
 
-        // UID2: estimated power = 21,000 uC = 0.00583 mAh
-        expectedPower = 0.00583;
+        // UID2: estimated power = 6,000 uC = 0.00166 mAh
+        expectedPower = 0.00167;
         ps2.getUidStats(uidStats, APP_UID2,
                 states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_CACHED));
         assertThat(POWER_STATS_LAYOUT.getUidPowerEstimate(uidStats))
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/ScreenPowerStatsCollectorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/ScreenPowerStatsCollectorTest.java
new file mode 100644
index 0000000..817fdcb
--- /dev/null
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/ScreenPowerStatsCollectorTest.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.power.stats;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.when;
+
+import android.hardware.power.stats.EnergyConsumerType;
+import android.os.BatteryConsumer;
+import android.os.BatteryStats;
+import android.os.Handler;
+import android.platform.test.ravenwood.RavenwoodRule;
+
+import com.android.internal.os.Clock;
+import com.android.internal.os.PowerStats;
+import com.android.server.power.stats.ScreenPowerStatsCollector.Injector;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.function.IntSupplier;
+
+public class ScreenPowerStatsCollectorTest {
+    private static final int APP_UID1 = 42;
+    private static final int APP_UID2 = 24;
+    private static final int ISOLATED_UID = 99123;
+
+    @Rule(order = 0)
+    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
+            .setProvideMainThread(true)
+            .build();
+
+    @Rule(order = 1)
+    public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
+            .setPowerStatsThrottlePeriodMillis(BatteryConsumer.POWER_COMPONENT_SCREEN, 1000);
+
+    @Mock
+    private PowerStatsCollector.ConsumedEnergyRetriever mConsumedEnergyRetriever;
+    @Mock
+    private PowerStatsUidResolver mPowerStatsUidResolver;
+    @Mock
+    private ScreenPowerStatsCollector.ScreenUsageTimeRetriever mScreenUsageTimeRetriever;
+
+    private final Injector mInjector = new Injector() {
+        @Override
+        public Handler getHandler() {
+            return mStatsRule.getHandler();
+        }
+
+        @Override
+        public Clock getClock() {
+            return mStatsRule.getMockClock();
+        }
+
+        @Override
+        public PowerStatsUidResolver getUidResolver() {
+            return mPowerStatsUidResolver;
+        }
+
+        @Override
+        public long getPowerStatsCollectionThrottlePeriod(String powerComponentName) {
+            return 0;
+        }
+
+        @Override
+        public PowerStatsCollector.ConsumedEnergyRetriever getConsumedEnergyRetriever() {
+            return mConsumedEnergyRetriever;
+        }
+
+        @Override
+        public IntSupplier getVoltageSupplier() {
+            return () -> 3500;
+        }
+
+        @Override
+        public int getDisplayCount() {
+            return 2;
+        }
+
+        @Override
+        public ScreenPowerStatsCollector.ScreenUsageTimeRetriever getScreenUsageTimeRetriever() {
+            return mScreenUsageTimeRetriever;
+        }
+    };
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        when(mPowerStatsUidResolver.mapUid(anyInt())).thenAnswer(invocation -> {
+            int uid = invocation.getArgument(0);
+            if (uid == ISOLATED_UID) {
+                return APP_UID2;
+            } else {
+                return uid;
+            }
+        });
+    }
+
+    @Test
+    public void collectStats() {
+        ScreenPowerStatsCollector collector = new ScreenPowerStatsCollector(mInjector);
+        collector.setEnabled(true);
+
+        // Establish a baseline
+        when(mConsumedEnergyRetriever.getEnergyConsumerIds(EnergyConsumerType.DISPLAY))
+                .thenReturn(new int[]{77});
+        when(mConsumedEnergyRetriever.getConsumedEnergyUws(new int[]{77}))
+                .thenReturn(new long[]{10_000});
+
+        doAnswer(inv -> {
+            ScreenPowerStatsCollector.ScreenUsageTimeRetriever.Callback callback =
+                    inv.getArgument(0);
+            callback.onUidTopActivityTime(APP_UID1, 1000);
+            callback.onUidTopActivityTime(APP_UID2, 2000);
+            return null;
+        }).when(mScreenUsageTimeRetriever).retrieveTopActivityTimes(any(
+                ScreenPowerStatsCollector.ScreenUsageTimeRetriever.Callback.class));
+
+        collector.collectStats();
+
+        when(mConsumedEnergyRetriever.getConsumedEnergyUws(new int[]{77}))
+                .thenReturn(new long[]{45_000});
+        when(mScreenUsageTimeRetriever.getScreenOnTimeMs(0))
+                .thenReturn(60_000L);
+        when(mScreenUsageTimeRetriever.getBrightnessLevelTimeMs(0,
+                BatteryStats.SCREEN_BRIGHTNESS_DARK))
+                .thenReturn(10_000L);
+        when(mScreenUsageTimeRetriever.getBrightnessLevelTimeMs(0,
+                BatteryStats.SCREEN_BRIGHTNESS_MEDIUM))
+                .thenReturn(20_000L);
+        when(mScreenUsageTimeRetriever.getBrightnessLevelTimeMs(0,
+                BatteryStats.SCREEN_BRIGHTNESS_BRIGHT))
+                .thenReturn(30_000L);
+        when(mScreenUsageTimeRetriever.getScreenOnTimeMs(1))
+                .thenReturn(120_000L);
+        when(mScreenUsageTimeRetriever.getScreenDozeTimeMs(0))
+                .thenReturn(180_000L);
+        when(mScreenUsageTimeRetriever.getScreenDozeTimeMs(1))
+                .thenReturn(240_000L);
+        doAnswer(inv -> {
+            ScreenPowerStatsCollector.ScreenUsageTimeRetriever.Callback callback =
+                    inv.getArgument(0);
+            callback.onUidTopActivityTime(APP_UID1, 3000);
+            callback.onUidTopActivityTime(APP_UID2, 5000);
+            callback.onUidTopActivityTime(ISOLATED_UID, 7000);
+            return null;
+        }).when(mScreenUsageTimeRetriever).retrieveTopActivityTimes(any(
+                ScreenPowerStatsCollector.ScreenUsageTimeRetriever.Callback.class));
+
+
+        PowerStats powerStats = collector.collectStats();
+
+        ScreenPowerStatsLayout layout = new ScreenPowerStatsLayout();
+        layout.fromExtras(powerStats.descriptor.extras);
+
+        // (45000 - 10000) / 3500
+        assertThat(layout.getConsumedEnergy(powerStats.stats, 0))
+                .isEqualTo(10_000);
+
+        assertThat(layout.getScreenOnDuration(powerStats.stats, 0))
+                .isEqualTo(60_000);
+        assertThat(layout.getBrightnessLevelDuration(powerStats.stats, 0,
+                BatteryStats.SCREEN_BRIGHTNESS_DARK))
+                .isEqualTo(10_000);
+        assertThat(layout.getBrightnessLevelDuration(powerStats.stats, 0,
+                BatteryStats.SCREEN_BRIGHTNESS_MEDIUM))
+                .isEqualTo(20_000);
+        assertThat(layout.getBrightnessLevelDuration(powerStats.stats, 0,
+                BatteryStats.SCREEN_BRIGHTNESS_BRIGHT))
+                .isEqualTo(30_000);
+        assertThat(layout.getScreenOnDuration(powerStats.stats, 1))
+                .isEqualTo(120_000);
+        assertThat(layout.getScreenDozeDuration(powerStats.stats, 0))
+                .isEqualTo(180_000);
+        assertThat(layout.getScreenDozeDuration(powerStats.stats, 1))
+                .isEqualTo(240_000);
+
+        assertThat(powerStats.uidStats.size()).isEqualTo(2);
+        // 3000 - 1000
+        assertThat(layout.getUidTopActivityDuration(powerStats.uidStats.get(APP_UID1)))
+                .isEqualTo(2000);
+        // (5000 - 2000) + 7000
+        assertThat(layout.getUidTopActivityDuration(powerStats.uidStats.get(APP_UID2)))
+                .isEqualTo(10000);
+    }
+}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/ScreenPowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/ScreenPowerStatsProcessorTest.java
new file mode 100644
index 0000000..9fde61a
--- /dev/null
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/ScreenPowerStatsProcessorTest.java
@@ -0,0 +1,287 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.power.stats;
+
+import static android.os.BatteryConsumer.PROCESS_STATE_ANY;
+
+import static com.android.server.power.stats.AggregatedPowerStatsConfig.POWER_STATE_BATTERY;
+import static com.android.server.power.stats.AggregatedPowerStatsConfig.POWER_STATE_OTHER;
+import static com.android.server.power.stats.AggregatedPowerStatsConfig.SCREEN_STATE_ON;
+import static com.android.server.power.stats.AggregatedPowerStatsConfig.SCREEN_STATE_OTHER;
+import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_POWER;
+import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_SCREEN;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.hardware.power.stats.EnergyConsumerType;
+import android.os.BatteryConsumer;
+import android.os.BatteryStats;
+import android.os.Handler;
+import android.os.Process;
+import android.platform.test.ravenwood.RavenwoodRule;
+
+import com.android.internal.os.Clock;
+import com.android.internal.os.PowerProfile;
+import com.android.internal.os.PowerStats;
+import com.android.server.power.stats.ScreenPowerStatsCollector.Injector;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.function.IntSupplier;
+
+public class ScreenPowerStatsProcessorTest {
+
+    @Rule(order = 0)
+    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
+            .setProvideMainThread(true)
+            .build();
+
+    @Rule(order = 1)
+    public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
+            .setNumDisplays(2)
+            .setAveragePowerForOrdinal(PowerProfile.POWER_GROUP_DISPLAY_AMBIENT, 0, 180.0)
+            .setAveragePowerForOrdinal(PowerProfile.POWER_GROUP_DISPLAY_AMBIENT, 1, 360.0)
+            .setAveragePowerForOrdinal(PowerProfile.POWER_GROUP_DISPLAY_SCREEN_ON, 0, 480.0)
+            .setAveragePowerForOrdinal(PowerProfile.POWER_GROUP_DISPLAY_SCREEN_ON, 1, 720.0)
+            .setAveragePowerForOrdinal(PowerProfile.POWER_GROUP_DISPLAY_SCREEN_FULL, 0, 4800.0)
+            .setAveragePowerForOrdinal(PowerProfile.POWER_GROUP_DISPLAY_SCREEN_ON, 1, 7200.0)
+            .initMeasuredEnergyStatsLocked();
+
+    private static final double PRECISION = 0.1;
+    private static final int APP_UID1 = Process.FIRST_APPLICATION_UID + 42;
+    private static final int APP_UID2 = Process.FIRST_APPLICATION_UID + 101;
+    private static final int VOLTAGE_MV = 3500;
+
+    @Mock
+    private PowerStatsCollector.ConsumedEnergyRetriever mConsumedEnergyRetriever;
+    @Mock
+    private ScreenPowerStatsCollector.ScreenUsageTimeRetriever mScreenUsageTimeRetriever;
+
+    private final Injector mInjector = new Injector() {
+        @Override
+        public Handler getHandler() {
+            return mStatsRule.getHandler();
+        }
+
+        @Override
+        public Clock getClock() {
+            return mStatsRule.getMockClock();
+        }
+
+        @Override
+        public PowerStatsUidResolver getUidResolver() {
+            return new PowerStatsUidResolver();
+        }
+
+        @Override
+        public long getPowerStatsCollectionThrottlePeriod(String powerComponentName) {
+            return 0;
+        }
+
+        @Override
+        public PowerStatsCollector.ConsumedEnergyRetriever getConsumedEnergyRetriever() {
+            return mConsumedEnergyRetriever;
+        }
+
+        @Override
+        public IntSupplier getVoltageSupplier() {
+            return () -> VOLTAGE_MV;
+        }
+
+        @Override
+        public int getDisplayCount() {
+            return 2;
+        }
+
+        @Override
+        public ScreenPowerStatsCollector.ScreenUsageTimeRetriever getScreenUsageTimeRetriever() {
+            return mScreenUsageTimeRetriever;
+        }
+    };
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+    }
+
+    @Test
+    public void processPowerStats_powerProfile() {
+        PowerComponentAggregatedPowerStats stats = collectAndAggregatePowerStats(false);
+
+        assertDevicePowerEstimate(stats, POWER_STATE_BATTERY, SCREEN_STATE_ON, 195.5, 0);
+        assertDevicePowerEstimate(stats, POWER_STATE_BATTERY, SCREEN_STATE_OTHER, 0, 0.6);
+        assertDevicePowerEstimate(stats, POWER_STATE_OTHER, SCREEN_STATE_ON, 97.8, 0);
+        assertDevicePowerEstimate(stats, POWER_STATE_OTHER, SCREEN_STATE_OTHER, 0, 0);
+
+        assertUidPowerEstimate(stats, APP_UID1, POWER_STATE_BATTERY, SCREEN_STATE_ON, 78.2);
+        assertUidPowerEstimate(stats, APP_UID1, POWER_STATE_BATTERY, SCREEN_STATE_OTHER, 0);
+        assertUidPowerEstimate(stats, APP_UID1, POWER_STATE_OTHER, SCREEN_STATE_ON, 39.1);
+        assertUidPowerEstimate(stats, APP_UID1, POWER_STATE_OTHER, SCREEN_STATE_OTHER, 0);
+
+        assertUidPowerEstimate(stats, APP_UID2, POWER_STATE_BATTERY, SCREEN_STATE_ON, 117.3);
+        assertUidPowerEstimate(stats, APP_UID2, POWER_STATE_BATTERY, SCREEN_STATE_OTHER, 0);
+        assertUidPowerEstimate(stats, APP_UID2, POWER_STATE_OTHER, SCREEN_STATE_ON, 58.7);
+        assertUidPowerEstimate(stats, APP_UID2, POWER_STATE_OTHER, SCREEN_STATE_OTHER, 0);
+    }
+
+    @Test
+    public void processPowerStats_energyConsumer() {
+        PowerComponentAggregatedPowerStats stats = collectAndAggregatePowerStats(true);
+
+        assertDevicePowerEstimate(stats, POWER_STATE_BATTERY, SCREEN_STATE_ON, 261.9, 0);
+        assertDevicePowerEstimate(stats, POWER_STATE_BATTERY, SCREEN_STATE_OTHER, 0, 7.2);
+        assertDevicePowerEstimate(stats, POWER_STATE_OTHER, SCREEN_STATE_ON, 130.9, 0);
+        assertDevicePowerEstimate(stats, POWER_STATE_OTHER, SCREEN_STATE_OTHER, 0, 0);
+
+        assertUidPowerEstimate(stats, APP_UID1, POWER_STATE_BATTERY, SCREEN_STATE_ON, 104.8);
+        assertUidPowerEstimate(stats, APP_UID1, POWER_STATE_BATTERY, SCREEN_STATE_OTHER, 0);
+        assertUidPowerEstimate(stats, APP_UID1, POWER_STATE_OTHER, SCREEN_STATE_ON, 52.4);
+        assertUidPowerEstimate(stats, APP_UID1, POWER_STATE_OTHER, SCREEN_STATE_OTHER, 0);
+
+        assertUidPowerEstimate(stats, APP_UID2, POWER_STATE_BATTERY, SCREEN_STATE_ON, 157.1);
+        assertUidPowerEstimate(stats, APP_UID2, POWER_STATE_BATTERY, SCREEN_STATE_OTHER, 0);
+        assertUidPowerEstimate(stats, APP_UID2, POWER_STATE_OTHER, SCREEN_STATE_ON, 78.6);
+        assertUidPowerEstimate(stats, APP_UID2, POWER_STATE_OTHER, SCREEN_STATE_OTHER, 0);
+    }
+
+    private PowerComponentAggregatedPowerStats collectAndAggregatePowerStats(
+            boolean energyConsumer) {
+        ScreenPowerStatsProcessor processor =
+                new ScreenPowerStatsProcessor(mStatsRule.getPowerProfile());
+
+        PowerComponentAggregatedPowerStats aggregatedStats = createAggregatedPowerStats(processor);
+
+        ScreenPowerStatsCollector collector = new ScreenPowerStatsCollector(mInjector);
+        collector.setEnabled(true);
+
+        if (energyConsumer) {
+            when(mConsumedEnergyRetriever.getEnergyConsumerIds(EnergyConsumerType.DISPLAY))
+                    .thenReturn(new int[]{77});
+            when(mConsumedEnergyRetriever.getConsumedEnergyUws(new int[]{77}))
+                    .thenReturn(new long[]{10_000});
+        } else {
+            when(mConsumedEnergyRetriever.getEnergyConsumerIds(EnergyConsumerType.DISPLAY))
+                    .thenReturn(new int[0]);
+        }
+
+        doAnswer(inv -> {
+            ScreenPowerStatsCollector.ScreenUsageTimeRetriever.Callback callback =
+                    inv.getArgument(0);
+            callback.onUidTopActivityTime(APP_UID1, 1000);
+            callback.onUidTopActivityTime(APP_UID2, 2000);
+            return null;
+        }).when(mScreenUsageTimeRetriever).retrieveTopActivityTimes(any(
+                ScreenPowerStatsCollector.ScreenUsageTimeRetriever.Callback.class));
+
+        aggregatedStats.addPowerStats(collector.collectStats(), 1000);
+
+        if (energyConsumer) {
+            // 400 mAh represented as microWattSeconds
+            long energyUws = 400L * 3600 * VOLTAGE_MV;
+            when(mConsumedEnergyRetriever.getConsumedEnergyUws(new int[]{77}))
+                    .thenReturn(new long[]{10_000 + energyUws});
+        }
+
+        when(mScreenUsageTimeRetriever.getScreenOnTimeMs(0))
+                .thenReturn(60_000L);
+        when(mScreenUsageTimeRetriever.getBrightnessLevelTimeMs(0,
+                BatteryStats.SCREEN_BRIGHTNESS_DARK))
+                .thenReturn(10_000L);
+        when(mScreenUsageTimeRetriever.getBrightnessLevelTimeMs(0,
+                BatteryStats.SCREEN_BRIGHTNESS_MEDIUM))
+                .thenReturn(20_000L);
+        when(mScreenUsageTimeRetriever.getBrightnessLevelTimeMs(0,
+                BatteryStats.SCREEN_BRIGHTNESS_BRIGHT))
+                .thenReturn(30_000L);
+        when(mScreenUsageTimeRetriever.getScreenOnTimeMs(1))
+                .thenReturn(120_000L);
+        when(mScreenUsageTimeRetriever.getScreenDozeTimeMs(0))
+                .thenReturn(180_000L);
+        when(mScreenUsageTimeRetriever.getScreenDozeTimeMs(1))
+                .thenReturn(240_000L);
+        doAnswer(inv -> {
+            ScreenPowerStatsCollector.ScreenUsageTimeRetriever.Callback callback =
+                    inv.getArgument(0);
+            callback.onUidTopActivityTime(APP_UID1, 3000);
+            callback.onUidTopActivityTime(APP_UID2, 5000);
+            return null;
+        }).when(mScreenUsageTimeRetriever).retrieveTopActivityTimes(any(
+                ScreenPowerStatsCollector.ScreenUsageTimeRetriever.Callback.class));
+
+        aggregatedStats.setState(STATE_POWER, POWER_STATE_BATTERY, 201_000);
+        aggregatedStats.setState(STATE_SCREEN, SCREEN_STATE_OTHER, 601_000);
+
+        // Slightly larger than 600_000 total screen time, to simulate a sight race
+        // between state changes and power stats collection
+        aggregatedStats.addPowerStats(collector.collectStats(), 612_000);
+
+        aggregatedStats.getConfig().getProcessor().finish(aggregatedStats, 180_000);
+        return aggregatedStats;
+    }
+
+    private static PowerComponentAggregatedPowerStats createAggregatedPowerStats(
+            ScreenPowerStatsProcessor processor) {
+        AggregatedPowerStatsConfig.PowerComponent config =
+                new AggregatedPowerStatsConfig.PowerComponent(
+                        BatteryConsumer.POWER_COMPONENT_SCREEN)
+                        .trackDeviceStates(STATE_POWER, STATE_SCREEN)
+                        .trackUidStates(STATE_POWER, STATE_SCREEN)
+                        .setProcessor(processor);
+
+        PowerComponentAggregatedPowerStats aggregatedStats =
+                new PowerComponentAggregatedPowerStats(
+                        new AggregatedPowerStats(mock(AggregatedPowerStatsConfig.class)), config);
+
+        aggregatedStats.setState(STATE_POWER, POWER_STATE_OTHER, 0);
+        aggregatedStats.setState(STATE_SCREEN, SCREEN_STATE_ON, 0);
+
+        return aggregatedStats;
+    }
+
+    private void assertDevicePowerEstimate(PowerComponentAggregatedPowerStats aggregatedStats,
+            int powerState, int screenState, double expectedScreenPowerEstimate,
+            double expectedDozePowerEstimate) {
+        PowerStats.Descriptor descriptor = aggregatedStats.getPowerStatsDescriptor();
+        ScreenPowerStatsLayout layout = new ScreenPowerStatsLayout(descriptor);
+        long[] stats = new long[descriptor.statsArrayLength];
+        aggregatedStats.getDeviceStats(stats, new int[]{powerState, screenState});
+        assertThat(layout.getDevicePowerEstimate(stats)).isWithin(PRECISION)
+                .of(expectedScreenPowerEstimate);
+        assertThat(layout.getScreenDozePowerEstimate(stats)).isWithin(PRECISION)
+                .of(expectedDozePowerEstimate);
+    }
+
+    private void assertUidPowerEstimate(PowerComponentAggregatedPowerStats aggregatedStats, int uid,
+            int powerState, int screenState, double expectedScreenPowerEstimate) {
+        PowerStats.Descriptor descriptor = aggregatedStats.getPowerStatsDescriptor();
+        ScreenPowerStatsLayout layout = new ScreenPowerStatsLayout(descriptor);
+        long[] stats = new long[descriptor.uidStatsArrayLength];
+        aggregatedStats.getUidStats(stats, uid,
+                new int[]{powerState, screenState, PROCESS_STATE_ANY});
+        assertThat(layout.getUidPowerEstimate(stats)).isWithin(PRECISION)
+                .of(expectedScreenPowerEstimate);
+    }
+}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/SensorPowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/SensorPowerStatsProcessorTest.java
new file mode 100644
index 0000000..7000487
--- /dev/null
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/SensorPowerStatsProcessorTest.java
@@ -0,0 +1,241 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.power.stats;
+
+import static android.os.BatteryConsumer.PROCESS_STATE_BACKGROUND;
+import static android.os.BatteryConsumer.PROCESS_STATE_CACHED;
+import static android.os.BatteryConsumer.PROCESS_STATE_FOREGROUND;
+import static android.os.BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE;
+
+import static com.android.server.power.stats.AggregatedPowerStatsConfig.POWER_STATE_OTHER;
+import static com.android.server.power.stats.AggregatedPowerStatsConfig.SCREEN_STATE_ON;
+import static com.android.server.power.stats.AggregatedPowerStatsConfig.SCREEN_STATE_OTHER;
+import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_POWER;
+import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_PROCESS_STATE;
+import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_SCREEN;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.hardware.Sensor;
+import android.hardware.SensorManager;
+import android.hardware.input.InputSensorInfo;
+import android.os.BatteryConsumer;
+import android.os.BatteryStats;
+import android.os.Process;
+import android.platform.test.ravenwood.RavenwoodRule;
+
+import com.android.internal.os.MonotonicClock;
+import com.android.internal.os.PowerStats;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.List;
+
+public class SensorPowerStatsProcessorTest {
+    @Rule(order = 0)
+    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
+            .setProvideMainThread(true)
+            .build();
+
+    @Rule(order = 1)
+    public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
+            .initMeasuredEnergyStatsLocked();
+
+    private static final double PRECISION = 0.00001;
+    private static final int APP_UID1 = Process.FIRST_APPLICATION_UID + 42;
+    private static final int APP_UID2 = Process.FIRST_APPLICATION_UID + 101;
+    private static final int SENSOR_HANDLE_1 = 77;
+    private static final int SENSOR_HANDLE_2 = 88;
+    private static final int SENSOR_HANDLE_3 = 99;
+
+    @Mock
+    private SensorManager mSensorManager;
+
+    private MonotonicClock mMonotonicClock;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        mMonotonicClock = new MonotonicClock(0, mStatsRule.getMockClock());
+        Sensor sensor1 = createSensor(SENSOR_HANDLE_1, Sensor.TYPE_STEP_COUNTER,
+                Sensor.STRING_TYPE_STEP_COUNTER, "dancing", 100);
+        Sensor sensor2 = createSensor(SENSOR_HANDLE_2, Sensor.TYPE_MOTION_DETECT,
+                "com.example", "tango", 200);
+        Sensor sensor3 = createSensor(SENSOR_HANDLE_3, Sensor.TYPE_MOTION_DETECT,
+                "com.example", "waltz", 300);
+        when(mSensorManager.getSensorList(Sensor.TYPE_ALL)).thenReturn(
+                List.of(sensor1, sensor2, sensor3));
+    }
+
+    @Test
+    public void testPowerEstimation() {
+        SensorPowerStatsProcessor processor = new SensorPowerStatsProcessor(() -> mSensorManager);
+
+        PowerComponentAggregatedPowerStats stats = createAggregatedPowerStats(processor);
+
+        processor.noteStateChange(stats, buildHistoryItem(0, true, APP_UID1, SENSOR_HANDLE_1));
+
+        // Turn the screen off after 2.5 seconds
+        stats.setState(STATE_SCREEN, SCREEN_STATE_OTHER, 2500);
+        stats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_BACKGROUND, 2500);
+        stats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND_SERVICE, 5000);
+
+        processor.noteStateChange(stats, buildHistoryItem(6000, false, APP_UID1, SENSOR_HANDLE_1));
+        processor.noteStateChange(stats, buildHistoryItem(7000, true, APP_UID2, SENSOR_HANDLE_1));
+        processor.noteStateChange(stats, buildHistoryItem(8000, true, APP_UID2, SENSOR_HANDLE_2));
+        processor.noteStateChange(stats, buildHistoryItem(9000, false, APP_UID2, SENSOR_HANDLE_1));
+
+        processor.finish(stats, 10000);
+
+        PowerStats.Descriptor descriptor = stats.getPowerStatsDescriptor();
+        SensorPowerStatsLayout statsLayout = new SensorPowerStatsLayout();
+        statsLayout.fromExtras(descriptor.extras);
+
+        String dump = stats.toString();
+        assertThat(dump).contains(" step_counter: ");
+        assertThat(dump).contains(" com.example.tango: ");
+
+        long[] uidStats = new long[descriptor.uidStatsArrayLength];
+
+        // For UID1:
+        // SENSOR1 was on for 6000 ms.
+        //   Estimated power: 6000 * 100 = 0.167 mAh
+        //     split between three different states
+        //          fg screen-on: 6000 * 2500/10000
+        //          bg screen-off: 6000 * 2500/10000
+        //          fgs screen-off: 6000 * 5000/10000
+        double expectedPower1 = 0.166666;
+        stats.getUidStats(uidStats, APP_UID1,
+                states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_FOREGROUND));
+        assertThat(statsLayout.getUidPowerEstimate(uidStats))
+                .isWithin(PRECISION).of(expectedPower1 * 2500 / 10000);
+        stats.getUidStats(uidStats, APP_UID1,
+                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_BACKGROUND));
+        assertThat(statsLayout.getUidPowerEstimate(uidStats))
+                .isWithin(PRECISION).of(expectedPower1 * 2500 / 10000);
+        stats.getUidStats(uidStats, APP_UID1,
+                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_FOREGROUND_SERVICE));
+        assertThat(statsLayout.getUidPowerEstimate(uidStats))
+                .isWithin(PRECISION).of(expectedPower1 * 5000 / 10000);
+
+        // For UID2:
+        // SENSOR1 was on for 2000 ms.
+        //   Estimated power: 2000 * 100 = 0.0556 mAh
+        //     split between three different states
+        //          cached screen-on: 2000 * 2500/10000
+        //          cached screen-off: 2000 * 7500/10000
+        // SENSOR2 was on for 2000 ms.
+        //   Estimated power: 2000 * 200 = 0.11111 mAh
+        //     split between three different states
+        //          cached screen-on: 2000 * 2500/10000
+        //          cached screen-off: 2000 * 7500/10000
+        double expectedPower2 = 0.05555 + 0.11111;
+        stats.getUidStats(uidStats, APP_UID2,
+                states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_CACHED));
+        assertThat(statsLayout.getUidPowerEstimate(uidStats))
+                .isWithin(PRECISION).of(expectedPower2 * 2500 / 10000);
+        stats.getUidStats(uidStats, APP_UID2,
+                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_CACHED));
+        assertThat(statsLayout.getUidPowerEstimate(uidStats))
+                .isWithin(PRECISION).of(expectedPower2 * 7500 / 10000);
+
+        long[] deviceStats = new long[descriptor.statsArrayLength];
+
+        stats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_ON));
+        assertThat(statsLayout.getDevicePowerEstimate(deviceStats))
+                .isWithin(PRECISION).of((expectedPower1 + expectedPower2) * 2500 / 10000);
+
+        stats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_OTHER));
+        assertThat(statsLayout.getDevicePowerEstimate(deviceStats))
+                .isWithin(PRECISION).of((expectedPower1 + expectedPower2) * 7500 / 10000);
+    }
+
+    private BatteryStats.HistoryItem buildHistoryItem(int timestamp, boolean stateOn,
+            int uid, int sensor) {
+        mStatsRule.setTime(timestamp, timestamp);
+        BatteryStats.HistoryItem historyItem = new BatteryStats.HistoryItem();
+        historyItem.time = mMonotonicClock.monotonicTime();
+        historyItem.states = stateOn ? BatteryStats.HistoryItem.STATE_SENSOR_ON_FLAG : 0;
+        if (stateOn) {
+            historyItem.eventCode = BatteryStats.HistoryItem.EVENT_STATE_CHANGE
+                    | BatteryStats.HistoryItem.EVENT_FLAG_START;
+        } else {
+            historyItem.eventCode = BatteryStats.HistoryItem.EVENT_STATE_CHANGE
+                    | BatteryStats.HistoryItem.EVENT_FLAG_FINISH;
+        }
+        historyItem.eventTag = historyItem.localEventTag;
+        historyItem.eventTag.uid = uid;
+        historyItem.eventTag.string = "sensor:0x" + Integer.toHexString(sensor);
+        return historyItem;
+    }
+
+    private int[] states(int... states) {
+        return states;
+    }
+
+    private static PowerComponentAggregatedPowerStats createAggregatedPowerStats(
+            SensorPowerStatsProcessor processor) {
+        AggregatedPowerStatsConfig config = new AggregatedPowerStatsConfig();
+        config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_SENSORS)
+                .trackDeviceStates(
+                        AggregatedPowerStatsConfig.STATE_POWER,
+                        AggregatedPowerStatsConfig.STATE_SCREEN)
+                .trackUidStates(
+                        AggregatedPowerStatsConfig.STATE_POWER,
+                        AggregatedPowerStatsConfig.STATE_SCREEN,
+                        AggregatedPowerStatsConfig.STATE_PROCESS_STATE)
+                .setProcessor(processor);
+
+        AggregatedPowerStats aggregatedPowerStats = new AggregatedPowerStats(config);
+        PowerComponentAggregatedPowerStats powerComponentStats =
+                aggregatedPowerStats.getPowerComponentStats(
+                        BatteryConsumer.POWER_COMPONENT_SENSORS);
+        processor.start(powerComponentStats, 0);
+
+        powerComponentStats.setState(STATE_POWER, POWER_STATE_OTHER, 0);
+        powerComponentStats.setState(STATE_SCREEN, SCREEN_STATE_ON, 0);
+        powerComponentStats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND, 0);
+        powerComponentStats.setUidState(APP_UID2, STATE_PROCESS_STATE, PROCESS_STATE_CACHED, 0);
+
+        return powerComponentStats;
+    }
+
+    private Sensor createSensor(int handle, int type, String stringType, String name, float power) {
+        if (RavenwoodRule.isOnRavenwood()) {
+            Sensor sensor = mock(Sensor.class);
+            when(sensor.getHandle()).thenReturn(handle);
+            when(sensor.getType()).thenReturn(type);
+            when(sensor.getStringType()).thenReturn(stringType);
+            when(sensor.getName()).thenReturn(name);
+            when(sensor.getPower()).thenReturn(power);
+            return sensor;
+        } else {
+            return new Sensor(new InputSensorInfo(name, "vendor", 0 /* version */,
+                    handle, type, 100.0f /*maxRange */, 0.02f /* resolution */,
+                    (float) power, 1000 /* minDelay */, 0 /* fifoReservedEventCount */,
+                    0 /* fifoMaxEventCount */, stringType /* stringType */,
+                    "" /* requiredPermission */, 0 /* maxDelay */, 0 /* flags */, 0 /* id */));
+        }
+    }
+}
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index b9e99dd..a86289b 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -78,7 +78,6 @@
         // TODO: remove once Android migrates to JUnit 4.12,
         // which provides assertThrows
         "testng",
-        "truth",
         "junit",
         "junit-params",
         "ActivityContext",
@@ -94,7 +93,7 @@
     libs: [
         "android.hardware.power-V1-java",
         "android.hardware.tv.cec-V1.0-java",
-        "android.hardware.vibrator-V2-java",
+        "android.hardware.vibrator-V3-java",
         "android.hidl.manager-V1.0-java",
         "android.test.mock",
         "android.test.base",
diff --git a/services/tests/servicestests/jni/Android.bp b/services/tests/servicestests/jni/Android.bp
index c30e4eb..0a31037 100644
--- a/services/tests/servicestests/jni/Android.bp
+++ b/services/tests/servicestests/jni/Android.bp
@@ -21,6 +21,7 @@
 
     srcs: [
         ":lib_cachedAppOptimizer_native",
+        ":lib_freezer_native",
         ":lib_gameManagerService_native",
         ":lib_oomConnection_native",
         ":lib_anrTimer_native",
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 20b9592..1afe12f 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
@@ -840,6 +840,10 @@
         info_a.setComponentName(COMPONENT_NAME);
         final AccessibilityServiceInfo info_b = new AccessibilityServiceInfo();
         info_b.setComponentName(new ComponentName("package", "class"));
+        writeStringsToSetting(Set.of(
+                info_a.getComponentName().flattenToString(),
+                info_b.getComponentName().flattenToString()),
+                Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
 
         AccessibilityUserState userState = mA11yms.getCurrentUserState();
         userState.mInstalledServices.clear();
@@ -858,10 +862,9 @@
         userState = mA11yms.getCurrentUserState();
         assertThat(userState.mEnabledServices).containsExactly(info_b.getComponentName());
         //Assert setting change
-        final Set<ComponentName> componentsFromSetting = new ArraySet<>();
-        mA11yms.readComponentNamesFromSettingLocked(Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
-                userState.mUserId, componentsFromSetting);
-        assertThat(componentsFromSetting).containsExactly(info_b.getComponentName());
+        final Set<String> enabledServices =
+                readStringsFromSetting(Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
+        assertThat(enabledServices).containsExactly(info_b.getComponentName().flattenToString());
     }
 
     @Test
@@ -880,6 +883,10 @@
                         info_a.getComponentName().flattenToString(),
                         info_b.getComponentName().flattenToString()),
                 SOFTWARE);
+        writeStringsToSetting(Set.of(
+                        info_a.getComponentName().flattenToString(),
+                        info_b.getComponentName().flattenToString()),
+                ShortcutUtils.convertToKey(SOFTWARE));
 
         // despite force stopping both packages, only the first service has the relevant flag,
         // so only the first should be removed.
@@ -896,13 +903,53 @@
         assertThat(userState.getShortcutTargetsLocked(SOFTWARE)).containsExactly(
                 info_b.getComponentName().flattenToString());
         //Assert setting change
-        final Set<String> targetsFromSetting = new ArraySet<>();
-        mA11yms.readColonDelimitedSettingToSet(ShortcutUtils.convertToKey(SOFTWARE),
-                userState.mUserId, str -> str, targetsFromSetting);
+        final Set<String> targetsFromSetting = readStringsFromSetting(
+                ShortcutUtils.convertToKey(SOFTWARE));
         assertThat(targetsFromSetting).containsExactly(info_b.getComponentName().flattenToString());
     }
 
     @Test
+    public void testPackagesForceStopped_otherServiceStopped_doesNotRemoveContinuousTarget() {
+        final AccessibilityServiceInfo info_a = new AccessibilityServiceInfo();
+        info_a.setComponentName(COMPONENT_NAME);
+        info_a.flags = FLAG_REQUEST_ACCESSIBILITY_BUTTON;
+        final AccessibilityServiceInfo info_b = new AccessibilityServiceInfo();
+        info_b.setComponentName(new ComponentName("package", "class"));
+        writeStringsToSetting(Set.of(
+                        info_a.getComponentName().flattenToString(),
+                        info_b.getComponentName().flattenToString()),
+                ShortcutUtils.convertToKey(SOFTWARE));
+
+        AccessibilityUserState userState = mA11yms.getCurrentUserState();
+        userState.mInstalledServices.clear();
+        userState.mInstalledServices.add(info_a);
+        userState.mInstalledServices.add(info_b);
+        userState.updateShortcutTargetsLocked(Set.of(
+                        info_a.getComponentName().flattenToString(),
+                        info_b.getComponentName().flattenToString()),
+                SOFTWARE);
+
+        // Force stopping a service should not disable unrelated continuous services.
+        synchronized (mA11yms.getLock()) {
+            mA11yms.onPackagesForceStoppedLocked(
+                    new String[]{info_b.getComponentName().getPackageName()},
+                    userState);
+        }
+
+        //Assert user state change
+        userState = mA11yms.getCurrentUserState();
+        assertThat(userState.getShortcutTargetsLocked(SOFTWARE)).containsExactly(
+                info_a.getComponentName().flattenToString(),
+                info_b.getComponentName().flattenToString());
+        //Assert setting unchanged
+        final Set<String> targetsFromSetting = readStringsFromSetting(
+                ShortcutUtils.convertToKey(SOFTWARE));
+        assertThat(targetsFromSetting).containsExactly(
+                info_a.getComponentName().flattenToString(),
+                info_b.getComponentName().flattenToString());
+    }
+
+    @Test
     public void testPackageMonitorScanPackages_scansWithoutHoldingLock() {
         setupAccessibilityServiceConnection(0);
         final AtomicReference<Set<Boolean>> lockState = collectLockStateWhilePackageScanning();
@@ -1844,6 +1891,11 @@
         return result;
     }
 
+    private void writeStringsToSetting(Set<String> strings, String setting) {
+        mA11yms.persistColonDelimitedSetToSettingLocked(
+                setting, UserHandle.USER_SYSTEM, strings, str -> str);
+    }
+
     private void broadcastSettingRestored(String setting, String newValue) {
         Intent intent = new Intent(Intent.ACTION_SETTING_RESTORED)
                 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY)
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/a11ychecker/AccessibilityCheckerManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/a11ychecker/AccessibilityCheckerManagerTest.java
new file mode 100644
index 0000000..c1b3929
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/accessibility/a11ychecker/AccessibilityCheckerManagerTest.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.accessibility.a11ychecker;
+
+import static com.android.server.accessibility.Flags.FLAG_ENABLE_A11Y_CHECKER_LOGGING;
+import static com.android.server.accessibility.a11ychecker.AccessibilityCheckerConstants.MIN_DURATION_BETWEEN_CHECKS;
+import static com.android.server.accessibility.a11ychecker.TestUtils.QUALIFIED_TEST_ACTIVITY_NAME;
+import static com.android.server.accessibility.a11ychecker.TestUtils.TEST_A11Y_SERVICE_CLASS_NAME;
+import static com.android.server.accessibility.a11ychecker.TestUtils.TEST_A11Y_SERVICE_SOURCE_PACKAGE_NAME;
+import static com.android.server.accessibility.a11ychecker.TestUtils.TEST_ACTIVITY_NAME;
+import static com.android.server.accessibility.a11ychecker.TestUtils.TEST_DEFAULT_BROWSER;
+import static com.android.server.accessibility.a11ychecker.TestUtils.createAtom;
+import static com.android.server.accessibility.a11ychecker.TestUtils.getMockPackageManagerWithInstalledApps;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.content.ComponentName;
+import android.content.pm.PackageManager;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
+import android.view.accessibility.AccessibilityNodeInfo;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.google.android.apps.common.testing.accessibility.framework.AccessibilityCheckResult;
+import com.google.android.apps.common.testing.accessibility.framework.AccessibilityHierarchyCheck;
+import com.google.android.apps.common.testing.accessibility.framework.AccessibilityHierarchyCheckResult;
+import com.google.android.apps.common.testing.accessibility.framework.checks.TouchTargetSizeCheck;
+import com.google.android.apps.common.testing.accessibility.framework.uielement.AccessibilityHierarchy;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.time.Duration;
+import java.time.Instant;
+import java.util.List;
+import java.util.Set;
+
+@RunWith(AndroidJUnit4.class)
+public class AccessibilityCheckerManagerTest {
+    @Rule
+    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
+    private AccessibilityCheckerManager mAccessibilityCheckerManager;
+
+    @Before
+    public void setup() throws PackageManager.NameNotFoundException {
+        PackageManager mockPackageManager = getMockPackageManagerWithInstalledApps();
+        mAccessibilityCheckerManager = new AccessibilityCheckerManager(setupMockChecks(),
+                nodeInfo -> mock(AccessibilityHierarchy.class), mockPackageManager);
+    }
+
+
+    @Test
+    @EnableFlags(FLAG_ENABLE_A11Y_CHECKER_LOGGING)
+    public void shouldRunA11yChecker_firstUpdate() {
+        assertThat(mAccessibilityCheckerManager.shouldRunA11yChecker()).isTrue();
+    }
+
+    @Test
+    @EnableFlags(FLAG_ENABLE_A11Y_CHECKER_LOGGING)
+    public void shouldRunA11yChecker_minDurationPassed() {
+        mAccessibilityCheckerManager.mTimer.setLastCheckTime(
+                Instant.now().minus(MIN_DURATION_BETWEEN_CHECKS.plus(Duration.ofSeconds(2))));
+        assertThat(mAccessibilityCheckerManager.shouldRunA11yChecker()).isTrue();
+    }
+
+    @Test
+    @EnableFlags(FLAG_ENABLE_A11Y_CHECKER_LOGGING)
+    public void shouldRunA11yChecker_tooEarly() {
+        mAccessibilityCheckerManager.mTimer.setLastCheckTime(
+                Instant.now().minus(MIN_DURATION_BETWEEN_CHECKS.minus(Duration.ofSeconds(2))));
+        assertThat(mAccessibilityCheckerManager.shouldRunA11yChecker()).isFalse();
+    }
+
+    @Test
+    @DisableFlags(FLAG_ENABLE_A11Y_CHECKER_LOGGING)
+    public void shouldRunA11yChecker_featureDisabled() {
+        assertThat(mAccessibilityCheckerManager.shouldRunA11yChecker()).isFalse();
+    }
+
+    @Test
+    @EnableFlags(FLAG_ENABLE_A11Y_CHECKER_LOGGING)
+    public void maybeRunA11yChecker_happyPath() {
+        AccessibilityNodeInfo mockNodeInfo1 =
+                new MockAccessibilityNodeInfoBuilder()
+                        .setViewIdResourceName("node1")
+                        .build();
+        AccessibilityNodeInfo mockNodeInfo2 =
+                new MockAccessibilityNodeInfoBuilder()
+                        .setViewIdResourceName("node2")
+                        .build();
+
+        Set<A11yCheckerProto.AccessibilityCheckResultReported> results =
+                mAccessibilityCheckerManager.maybeRunA11yChecker(
+                        List.of(mockNodeInfo1, mockNodeInfo2), QUALIFIED_TEST_ACTIVITY_NAME,
+                        new ComponentName(TEST_A11Y_SERVICE_SOURCE_PACKAGE_NAME,
+                                TEST_A11Y_SERVICE_CLASS_NAME), /*userId=*/ 0);
+
+        assertThat(results).containsExactly(
+                createAtom(/*viewIdResourceName=*/ "node1", TEST_ACTIVITY_NAME,
+                        A11yCheckerProto.AccessibilityCheckClass.TOUCH_TARGET_SIZE_CHECK,
+                        A11yCheckerProto.AccessibilityCheckResultType.ERROR, /*resultId=*/ 2),
+                createAtom(/*viewIdResourceName=*/ "node2", TEST_ACTIVITY_NAME,
+                        A11yCheckerProto.AccessibilityCheckClass.TOUCH_TARGET_SIZE_CHECK,
+                        A11yCheckerProto.AccessibilityCheckResultType.ERROR, /*resultId=*/ 2)
+        );
+    }
+
+    @Test
+    @EnableFlags(FLAG_ENABLE_A11Y_CHECKER_LOGGING)
+    public void maybeRunA11yChecker_skipsNodesFromDefaultBrowser() {
+        AccessibilityNodeInfo mockNodeInfo =
+                new MockAccessibilityNodeInfoBuilder()
+                        .setPackageName(TEST_DEFAULT_BROWSER)
+                        .setViewIdResourceName("node1")
+                        .build();
+
+        Set<A11yCheckerProto.AccessibilityCheckResultReported> results =
+                mAccessibilityCheckerManager.maybeRunA11yChecker(
+                        List.of(mockNodeInfo), QUALIFIED_TEST_ACTIVITY_NAME,
+                        new ComponentName(TEST_A11Y_SERVICE_SOURCE_PACKAGE_NAME,
+                                TEST_A11Y_SERVICE_CLASS_NAME), /*userId=*/ 0);
+
+        assertThat(results).isEmpty();
+    }
+
+    @Test
+    @EnableFlags(FLAG_ENABLE_A11Y_CHECKER_LOGGING)
+    public void maybeRunA11yChecker_doesNotStoreDuplicates() {
+        AccessibilityNodeInfo mockNodeInfo =
+                new MockAccessibilityNodeInfoBuilder()
+                        .setViewIdResourceName("node1")
+                        .build();
+        AccessibilityNodeInfo mockNodeInfoDuplicate =
+                new MockAccessibilityNodeInfoBuilder()
+                        .setViewIdResourceName("node1")
+                        .build();
+
+        Set<A11yCheckerProto.AccessibilityCheckResultReported> results =
+                mAccessibilityCheckerManager.maybeRunA11yChecker(
+                        List.of(mockNodeInfo, mockNodeInfoDuplicate), QUALIFIED_TEST_ACTIVITY_NAME,
+                        new ComponentName(TEST_A11Y_SERVICE_SOURCE_PACKAGE_NAME,
+                                TEST_A11Y_SERVICE_CLASS_NAME), /*userId=*/ 0);
+
+        assertThat(results).containsExactly(
+                createAtom(/*viewIdResourceName=*/ "node1", TEST_ACTIVITY_NAME,
+                        A11yCheckerProto.AccessibilityCheckClass.TOUCH_TARGET_SIZE_CHECK,
+                        A11yCheckerProto.AccessibilityCheckResultType.ERROR, /*resultId=*/ 2)
+        );
+    }
+
+    private Set<AccessibilityHierarchyCheck> setupMockChecks() {
+        AccessibilityHierarchyCheck mockCheck1 = mock(AccessibilityHierarchyCheck.class);
+        AccessibilityHierarchyCheckResult infoTypeResult =
+                new AccessibilityHierarchyCheckResult(
+                        TouchTargetSizeCheck.class,
+                        AccessibilityCheckResult.AccessibilityCheckResultType.INFO, null, 1, null);
+        when(mockCheck1.runCheckOnHierarchy(any())).thenReturn(List.of(infoTypeResult));
+
+        AccessibilityHierarchyCheck mockCheck2 = mock(AccessibilityHierarchyCheck.class);
+        AccessibilityHierarchyCheckResult errorTypeResult =
+                new AccessibilityHierarchyCheckResult(
+                        TouchTargetSizeCheck.class,
+                        AccessibilityCheckResult.AccessibilityCheckResultType.ERROR, null, 2,
+                        null);
+        when(mockCheck2.runCheckOnHierarchy(any())).thenReturn(List.of(errorTypeResult));
+
+        return Set.of(mockCheck1, mockCheck2);
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/a11ychecker/AccessibilityCheckerUtilsTest.java b/services/tests/servicestests/src/com/android/server/accessibility/a11ychecker/AccessibilityCheckerUtilsTest.java
index 90d4275..5b4e72e 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/a11ychecker/AccessibilityCheckerUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/a11ychecker/AccessibilityCheckerUtilsTest.java
@@ -16,13 +16,12 @@
 
 package com.android.server.accessibility.a11ychecker;
 
+import static com.android.server.accessibility.a11ychecker.TestUtils.QUALIFIED_TEST_ACTIVITY_NAME;
 import static com.android.server.accessibility.a11ychecker.TestUtils.TEST_A11Y_SERVICE_CLASS_NAME;
 import static com.android.server.accessibility.a11ychecker.TestUtils.TEST_A11Y_SERVICE_SOURCE_PACKAGE_NAME;
-import static com.android.server.accessibility.a11ychecker.TestUtils.TEST_A11Y_SERVICE_SOURCE_VERSION_CODE;
 import static com.android.server.accessibility.a11ychecker.TestUtils.TEST_ACTIVITY_NAME;
 import static com.android.server.accessibility.a11ychecker.TestUtils.TEST_APP_PACKAGE_NAME;
-import static com.android.server.accessibility.a11ychecker.TestUtils.TEST_APP_VERSION_CODE;
-import static com.android.server.accessibility.a11ychecker.TestUtils.TEST_WINDOW_TITLE;
+import static com.android.server.accessibility.a11ychecker.TestUtils.createAtom;
 import static com.android.server.accessibility.a11ychecker.TestUtils.getMockPackageManagerWithInstalledApps;
 
 import static com.google.common.truth.Truth.assertThat;
@@ -31,7 +30,6 @@
 
 import android.content.ComponentName;
 import android.content.pm.PackageManager;
-import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityNodeInfo;
 
 import androidx.test.runner.AndroidJUnit4;
@@ -99,9 +97,11 @@
                                 TEST_A11Y_SERVICE_CLASS_NAME));
 
         assertThat(atoms).containsExactly(
-                createAtom(A11yCheckerProto.AccessibilityCheckClass.SPEAKABLE_TEXT_PRESENT_CHECK,
+                createAtom("TargetNode", "",
+                        A11yCheckerProto.AccessibilityCheckClass.SPEAKABLE_TEXT_PRESENT_CHECK,
                         A11yCheckerProto.AccessibilityCheckResultType.WARNING, 1),
-                createAtom(A11yCheckerProto.AccessibilityCheckClass.TOUCH_TARGET_SIZE_CHECK,
+                createAtom("TargetNode", "",
+                        A11yCheckerProto.AccessibilityCheckClass.TOUCH_TARGET_SIZE_CHECK,
                         A11yCheckerProto.AccessibilityCheckResultType.ERROR, 2)
         );
     }
@@ -139,14 +139,15 @@
     }
 
     @Test
-    public void getActivityName_hasWindowStateChangedEvent_returnsActivityName() {
-        AccessibilityEvent accessibilityEvent =
-                AccessibilityEvent.obtain(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
-        accessibilityEvent.setPackageName(TEST_APP_PACKAGE_NAME);
-        accessibilityEvent.setClassName(TEST_ACTIVITY_NAME);
-
+    public void getActivityName_hasValidActivityClassName_returnsActivityName() {
         assertThat(AccessibilityCheckerUtils.getActivityName(mMockPackageManager,
-                accessibilityEvent)).isEqualTo("MainActivity");
+                TEST_APP_PACKAGE_NAME, QUALIFIED_TEST_ACTIVITY_NAME)).isEqualTo(TEST_ACTIVITY_NAME);
+    }
+
+    @Test
+    public void getActivityName_hasInvalidActivityClassName_returnsActivityName() {
+        assertThat(AccessibilityCheckerUtils.getActivityName(mMockPackageManager,
+                TEST_APP_PACKAGE_NAME, "com.NonActivityClass")).isEmpty();
     }
 
     // Makes sure the AccessibilityHierarchyCheck class to enum mapping is up to date with the
@@ -164,24 +165,4 @@
                 .containsExactlyElementsIn(latestCheckClasses);
     }
 
-
-    private static A11yCheckerProto.AccessibilityCheckResultReported createAtom(
-            A11yCheckerProto.AccessibilityCheckClass checkClass,
-            A11yCheckerProto.AccessibilityCheckResultType resultType,
-            int resultId) {
-        return A11yCheckerProto.AccessibilityCheckResultReported.newBuilder()
-                .setPackageName(TEST_APP_PACKAGE_NAME)
-                .setAppVersionCode(TEST_APP_VERSION_CODE)
-                .setUiElementPath(TEST_APP_PACKAGE_NAME + ":TargetNode")
-                .setWindowTitle(TEST_WINDOW_TITLE)
-                .setActivityName("")
-                .setSourceComponentName(new ComponentName(TEST_A11Y_SERVICE_SOURCE_PACKAGE_NAME,
-                        TEST_A11Y_SERVICE_CLASS_NAME).flattenToString())
-                .setSourceVersionCode(TEST_A11Y_SERVICE_SOURCE_VERSION_CODE)
-                .setResultCheckClass(checkClass)
-                .setResultType(resultType)
-                .setResultId(resultId)
-                .build();
-    }
-
 }
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/a11ychecker/TestUtils.java b/services/tests/servicestests/src/com/android/server/accessibility/a11ychecker/TestUtils.java
index a04bbee..acf64b6 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/a11ychecker/TestUtils.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/a11ychecker/TestUtils.java
@@ -16,6 +16,8 @@
 
 package com.android.server.accessibility.a11ychecker;
 
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.anyInt;
 import static org.mockito.Mockito.when;
 
 import android.annotation.Nullable;
@@ -23,40 +25,49 @@
 import android.content.pm.ActivityInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
+import android.view.accessibility.AccessibilityEvent;
 
+import org.mockito.AdditionalMatchers;
 import org.mockito.Mockito;
 
 public class TestUtils {
     static final String TEST_APP_PACKAGE_NAME = "com.example.app";
     static final int TEST_APP_VERSION_CODE = 12321;
-    static final String TEST_ACTIVITY_NAME = "com.example.app.MainActivity";
+    static final String TEST_ACTIVITY_NAME = "MainActivity";
+    static final String QUALIFIED_TEST_ACTIVITY_NAME = "com.example.app.MainActivity";
     static final String TEST_A11Y_SERVICE_SOURCE_PACKAGE_NAME = "com.assistive.app";
     static final String TEST_A11Y_SERVICE_CLASS_NAME = "MyA11yService";
     static final int TEST_A11Y_SERVICE_SOURCE_VERSION_CODE = 333555;
     static final String TEST_WINDOW_TITLE = "Example window";
+    static final String TEST_DEFAULT_BROWSER = "com.android.chrome";
 
     static PackageManager getMockPackageManagerWithInstalledApps()
             throws PackageManager.NameNotFoundException {
         PackageManager mockPackageManager = Mockito.mock(PackageManager.class);
         ActivityInfo testActivityInfo = getTestActivityInfo();
         ComponentName testActivityComponentName = new ComponentName(TEST_APP_PACKAGE_NAME,
-                TEST_ACTIVITY_NAME);
+                QUALIFIED_TEST_ACTIVITY_NAME);
 
-        when(mockPackageManager.getActivityInfo(testActivityComponentName, 0))
+        when(mockPackageManager.getActivityInfo(eq(testActivityComponentName), eq(0)))
                 .thenReturn(testActivityInfo);
+        when(mockPackageManager.getActivityInfo(
+                AdditionalMatchers.not(eq(testActivityComponentName)), eq(0)))
+                .thenThrow(PackageManager.NameNotFoundException.class);
         when(mockPackageManager.getPackageInfo(TEST_APP_PACKAGE_NAME, 0))
                 .thenReturn(createPackageInfo(TEST_APP_PACKAGE_NAME, TEST_APP_VERSION_CODE,
                         testActivityInfo));
         when(mockPackageManager.getPackageInfo(TEST_A11Y_SERVICE_SOURCE_PACKAGE_NAME, 0))
                 .thenReturn(createPackageInfo(TEST_A11Y_SERVICE_SOURCE_PACKAGE_NAME,
                         TEST_A11Y_SERVICE_SOURCE_VERSION_CODE, null));
+        when(mockPackageManager.getDefaultBrowserPackageNameAsUser(anyInt())).thenReturn(
+                TEST_DEFAULT_BROWSER);
         return mockPackageManager;
     }
 
     static ActivityInfo getTestActivityInfo() {
         ActivityInfo activityInfo = new ActivityInfo();
         activityInfo.packageName = TEST_APP_PACKAGE_NAME;
-        activityInfo.name = TEST_ACTIVITY_NAME;
+        activityInfo.name = QUALIFIED_TEST_ACTIVITY_NAME;
         return activityInfo;
     }
 
@@ -69,6 +80,34 @@
             packageInfo.activities = new ActivityInfo[]{activityInfo};
         }
         return packageInfo;
+    }
 
+    static AccessibilityEvent getTestAccessibilityEvent() {
+        AccessibilityEvent accessibilityEvent =
+                AccessibilityEvent.obtain(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
+        accessibilityEvent.setPackageName(TEST_APP_PACKAGE_NAME);
+        accessibilityEvent.setClassName(QUALIFIED_TEST_ACTIVITY_NAME);
+        return accessibilityEvent;
+    }
+
+    static A11yCheckerProto.AccessibilityCheckResultReported createAtom(
+            String viewIdResourceName,
+            String activityName,
+            A11yCheckerProto.AccessibilityCheckClass checkClass,
+            A11yCheckerProto.AccessibilityCheckResultType resultType,
+            int resultId) {
+        return A11yCheckerProto.AccessibilityCheckResultReported.newBuilder()
+                .setPackageName(TEST_APP_PACKAGE_NAME)
+                .setAppVersionCode(TEST_APP_VERSION_CODE)
+                .setUiElementPath(TEST_APP_PACKAGE_NAME + ":" + viewIdResourceName)
+                .setWindowTitle(TEST_WINDOW_TITLE)
+                .setActivityName(activityName)
+                .setSourceComponentName(new ComponentName(TEST_A11Y_SERVICE_SOURCE_PACKAGE_NAME,
+                        TEST_A11Y_SERVICE_CLASS_NAME).flattenToString())
+                .setSourceVersionCode(TEST_A11Y_SERVICE_SOURCE_VERSION_CODE)
+                .setResultCheckClass(checkClass)
+                .setResultType(resultType)
+                .setResultId(resultId)
+                .build();
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
index 3ef81fd..60bcecc 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
@@ -620,7 +620,7 @@
 
     @Test
     @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_MULTIPLE_FINGER_MULTIPLE_TAP_GESTURE)
-    public void testTwoFingerTap_StateIsActivated_shouldInDelegating() {
+    public void testTwoFingerTap_StateIsActivated_shouldInDetecting() {
         assumeTrue(isWatch());
         enableOneFingerPanning(false);
         goFromStateIdleTo(STATE_ACTIVATED);
@@ -629,14 +629,15 @@
         send(downEvent());
         send(pointerEvent(ACTION_POINTER_DOWN, DEFAULT_X * 2, DEFAULT_Y));
         send(upEvent());
-        fastForward(ViewConfiguration.getDoubleTapTimeout());
+        fastForward(mMgh.mDetectingState.mMultiTapMaxDelay);
 
-        assertTrue(mMgh.mCurrentState == mMgh.mDelegatingState);
+        verify(mMgh.getNext(), times(3)).onMotionEvent(any(), any(), anyInt());
+        assertTrue(mMgh.mCurrentState == mMgh.mDetectingState);
     }
 
     @Test
     @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_MULTIPLE_FINGER_MULTIPLE_TAP_GESTURE)
-    public void testTwoFingerTap_StateIsIdle_shouldInDelegating() {
+    public void testTwoFingerTap_StateIsIdle_shouldInDetecting() {
         assumeTrue(isWatch());
         enableOneFingerPanning(false);
         goFromStateIdleTo(STATE_IDLE);
@@ -645,9 +646,10 @@
         send(downEvent());
         send(pointerEvent(ACTION_POINTER_DOWN, DEFAULT_X * 2, DEFAULT_Y));
         send(upEvent());
-        fastForward(ViewConfiguration.getDoubleTapTimeout());
+        fastForward(mMgh.mDetectingState.mMultiTapMaxDelay);
 
-        assertTrue(mMgh.mCurrentState == mMgh.mDelegatingState);
+        verify(mMgh.getNext(), times(3)).onMotionEvent(any(), any(), anyInt());
+        assertTrue(mMgh.mCurrentState == mMgh.mDetectingState);
     }
 
     @Test
@@ -982,6 +984,53 @@
     }
 
     @Test
+    public void testSingleFingerOverscrollAtTopEdge_isWatch_scrollDiagonally_noOverscroll() {
+        assumeTrue(isWatch());
+        goFromStateIdleTo(STATE_SINGLE_PANNING);
+        float centerX =
+                (INITIAL_MAGNIFICATION_BOUNDS.right + INITIAL_MAGNIFICATION_BOUNDS.left) / 2.0f;
+        mFullScreenMagnificationController.setCenter(
+                DISPLAY_0, centerX, INITIAL_MAGNIFICATION_BOUNDS.top, false, 1);
+        final float swipeMinDistance = ViewConfiguration.get(mContext).getScaledTouchSlop() + 1;
+        PointF initCoords =
+                new PointF(
+                        mFullScreenMagnificationController.getCenterX(DISPLAY_0),
+                        mFullScreenMagnificationController.getCenterY(DISPLAY_0));
+        PointF edgeCoords = new PointF(initCoords.x, initCoords.y);
+        // Scroll diagonally towards top-right with a bigger right delta
+        edgeCoords.offset(swipeMinDistance * 2, swipeMinDistance);
+
+        swipeAndHold(initCoords, edgeCoords);
+
+        assertTrue(mMgh.mOverscrollHandler.mOverscrollState == mMgh.OVERSCROLL_NONE);
+        assertTrue(isZoomed());
+    }
+
+    @Test
+    public void
+            testSingleFingerOverscrollAtTopEdge_isWatch_scrollDiagonally_expectedOverscrollState() {
+        assumeTrue(isWatch());
+        goFromStateIdleTo(STATE_SINGLE_PANNING);
+        float centerX =
+                (INITIAL_MAGNIFICATION_BOUNDS.right + INITIAL_MAGNIFICATION_BOUNDS.left) / 2.0f;
+        mFullScreenMagnificationController.setCenter(
+                DISPLAY_0, centerX, INITIAL_MAGNIFICATION_BOUNDS.top, false, 1);
+        final float swipeMinDistance = ViewConfiguration.get(mContext).getScaledTouchSlop() + 1;
+        PointF initCoords =
+                new PointF(
+                        mFullScreenMagnificationController.getCenterX(DISPLAY_0),
+                        mFullScreenMagnificationController.getCenterY(DISPLAY_0));
+        PointF edgeCoords = new PointF(initCoords.x, initCoords.y);
+        // Scroll diagonally towards top-right with a bigger top delta
+        edgeCoords.offset(swipeMinDistance, swipeMinDistance * 2);
+
+        swipeAndHold(initCoords, edgeCoords);
+
+        assertTrue(mMgh.mOverscrollHandler.mOverscrollState == mMgh.OVERSCROLL_VERTICAL_EDGE);
+        assertTrue(isZoomed());
+    }
+
+    @Test
     public void testSingleFingerScrollAtEdge_isWatch_noOverscroll() {
         assumeTrue(isWatch());
         goFromStateIdleTo(STATE_SINGLE_PANNING);
@@ -1057,9 +1106,24 @@
         assumeTrue(isWatch());
         goFromStateIdleTo(STATE_ACTIVATED);
 
-        swipeAndHold();
+        PointF pointer = DEFAULT_POINT;
+        send(downEvent(pointer.x, pointer.y));
+
+        // first move triggers the panning state
+        pointer.offset(100, 100);
         fastForward(20);
-        swipe(DEFAULT_POINT, new PointF(DEFAULT_X * 2, DEFAULT_Y * 2), /* durationMs= */ 20);
+        send(moveEvent(pointer.x, pointer.y));
+
+        // second move actually pans
+        pointer.offset(100, 100);
+        fastForward(20);
+        send(moveEvent(pointer.x, pointer.y));
+        pointer.offset(100, 100);
+        fastForward(20);
+        send(moveEvent(pointer.x, pointer.y));
+
+        fastForward(20);
+        send(upEvent(pointer.x, pointer.y));
 
         verify(mMockScroller).fling(
                 /* startX= */ anyInt(),
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java
index c99e040..31e15de 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java
@@ -341,7 +341,8 @@
                     twoFingerTap();
                     twoFingerTap();
                     // Wait for two-finger tap gesture completed.
-                    SystemClock.sleep(ViewConfiguration.getDoubleTapMinTime() + 500);
+                    SystemClock.sleep(MagnificationGestureMatcher
+                            .getMagnificationMultiTapTimeout(mContext) + 100);
                     InstrumentationRegistry.getInstrumentation().waitForIdleSync();
                 }
                 break;
@@ -403,7 +404,8 @@
                 twoFingerTap();
                 twoFingerTap();
                 // Wait for two-finger tap gesture completed.
-                SystemClock.sleep(ViewConfiguration.getDoubleTapMinTime() + 500);
+                SystemClock.sleep(MagnificationGestureMatcher
+                        .getMagnificationMultiTapTimeout(mContext) + 100);
                 InstrumentationRegistry.getInstrumentation().waitForIdleSync();
             }
             break;
diff --git a/services/tests/servicestests/src/com/android/server/appop/DiscreteAppOpPersistenceTest.java b/services/tests/servicestests/src/com/android/server/appop/DiscreteAppOpPersistenceTest.java
new file mode 100644
index 0000000..bc3a5ca
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/appop/DiscreteAppOpPersistenceTest.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.ATTRIBUTION_FLAG_RECEIVER;
+import static android.app.AppOpsManager.OP_FLAGS_ALL_TRUSTED;
+import static android.app.AppOpsManager.OP_FLAG_SELF;
+import static android.app.AppOpsManager.UID_STATE_FOREGROUND;
+import static android.app.AppOpsManager.UID_STATE_FOREGROUND_SERVICE;
+
+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 android.permission.flags.Flags;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+public class DiscreteAppOpPersistenceTest {
+    private DiscreteRegistry mDiscreteRegistry;
+    private final Object mLock = new Object();
+    private File mMockDataDirectory;
+    private final Context mContext =
+            InstrumentationRegistry.getInstrumentation().getTargetContext();
+
+    @Rule
+    public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
+    @Before
+    public void setUp() {
+        mMockDataDirectory = mContext.getDir("mock_data", Context.MODE_PRIVATE);
+        mDiscreteRegistry = new DiscreteRegistry(mLock, mMockDataDirectory);
+        mDiscreteRegistry.systemReady();
+    }
+
+    @After
+    public void cleanUp() {
+        mDiscreteRegistry.writeAndClearAccessHistory();
+        FileUtils.deleteContents(mMockDataDirectory);
+    }
+
+    @RequiresFlagsEnabled(Flags.FLAG_DEVICE_AWARE_APP_OP_NEW_SCHEMA_ENABLED)
+    @Test
+    public void defaultDevice_recordAccess_persistToDisk() {
+        int uid = Process.myUid();
+        String packageName = mContext.getOpPackageName();
+        int op = AppOpsManager.OP_CAMERA;
+        String deviceId = VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT;
+        long accessTime = System.currentTimeMillis();
+        long duration = 60000L;
+        int uidState = UID_STATE_FOREGROUND;
+        int opFlags = OP_FLAGS_ALL_TRUSTED;
+        int attributionFlags = ATTRIBUTION_FLAG_ACCESSOR;
+        int attributionChainId = AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE;
+
+        mDiscreteRegistry.recordDiscreteAccess(uid, packageName, deviceId, op, null, opFlags,
+                uidState, accessTime, duration, attributionFlags, attributionChainId);
+
+        // 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();
+
+        // Verify the storage file is created and then verify its content is correct
+        File[] files = FileUtils.listFilesOrEmpty(mMockDataDirectory);
+        assertThat(files.length).isEqualTo(1);
+        fetchDiscreteOpsAndValidate(uid, packageName, op, deviceId, null, accessTime,
+                duration, uidState, opFlags, attributionFlags, attributionChainId);
+    }
+
+    @RequiresFlagsEnabled(Flags.FLAG_DEVICE_AWARE_APP_OP_NEW_SCHEMA_ENABLED)
+    @Test
+    public void externalDevice_recordAccess_persistToDisk() {
+        int uid = Process.myUid();
+        String packageName = mContext.getOpPackageName();
+        int op = AppOpsManager.OP_CAMERA;
+        String deviceId = "companion:1";
+        long accessTime = System.currentTimeMillis();
+        long duration = -1;
+        int uidState = UID_STATE_FOREGROUND_SERVICE;
+        int opFlags = OP_FLAG_SELF;
+        int attributionFlags = ATTRIBUTION_FLAG_RECEIVER;
+        int attributionChainId = 10;
+
+        mDiscreteRegistry.recordDiscreteAccess(uid, packageName, deviceId, op, null, opFlags,
+                uidState, accessTime, duration, attributionFlags, attributionChainId);
+
+        fetchDiscreteOpsAndValidate(uid, packageName, op, deviceId, null, accessTime,
+                duration, uidState, opFlags, attributionFlags, attributionChainId);
+
+        mDiscreteRegistry.writeAndClearAccessHistory();
+
+        File[] files = FileUtils.listFilesOrEmpty(mMockDataDirectory);
+        assertThat(files.length).isEqualTo(1);
+        fetchDiscreteOpsAndValidate(uid, packageName, op, deviceId, null, accessTime,
+                duration, uidState, opFlags, attributionFlags, attributionChainId);
+    }
+
+    private void fetchDiscreteOpsAndValidate(int expectedUid, String expectedPackageName,
+            int expectedOp, String expectedDeviceId, String expectedAttrTag,
+            long expectedAccessTime, long expectedAccessDuration, int expectedUidState,
+            int expectedOpFlags, int expectedAttrFlags, int expectedAttrChainId) {
+        DiscreteRegistry.DiscreteOps discreteOps = mDiscreteRegistry.getAllDiscreteOps();
+
+        assertThat(discreteOps.isEmpty()).isFalse();
+        assertThat(discreteOps.mUids.size()).isEqualTo(1);
+
+        DiscreteRegistry.DiscreteUidOps discreteUidOps = discreteOps.mUids.get(expectedUid);
+        assertThat(discreteUidOps.mPackages.size()).isEqualTo(1);
+
+        DiscreteRegistry.DiscretePackageOps discretePackageOps =
+                discreteUidOps.mPackages.get(expectedPackageName);
+        assertThat(discretePackageOps.mPackageOps.size()).isEqualTo(1);
+
+        DiscreteRegistry.DiscreteOp discreteOp = discretePackageOps.mPackageOps.get(expectedOp);
+        assertThat(discreteOp.mDeviceAttributedOps.size()).isEqualTo(1);
+
+        DiscreteRegistry.DiscreteDeviceOp discreteDeviceOp =
+                discreteOp.mDeviceAttributedOps.get(expectedDeviceId);
+        assertThat(discreteDeviceOp.mAttributedOps.size()).isEqualTo(1);
+
+        List<DiscreteRegistry.DiscreteOpEvent> discreteOpEvents =
+                discreteDeviceOp.mAttributedOps.get(expectedAttrTag);
+        assertThat(discreteOpEvents.size()).isEqualTo(1);
+
+        DiscreteRegistry.DiscreteOpEvent discreteOpEvent = discreteOpEvents.get(0);
+        assertThat(discreteOpEvent.mNoteTime).isEqualTo(expectedAccessTime);
+        assertThat(discreteOpEvent.mNoteDuration).isEqualTo(expectedAccessDuration);
+        assertThat(discreteOpEvent.mUidState).isEqualTo(expectedUidState);
+        assertThat(discreteOpEvent.mOpFlag).isEqualTo(expectedOpFlags);
+        assertThat(discreteOpEvent.mAttributionFlags).isEqualTo(expectedAttrFlags);
+        assertThat(discreteOpEvent.mAttributionChainId).isEqualTo(expectedAttrChainId);
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/audio/AudioDeviceInventoryTest.java b/services/tests/servicestests/src/com/android/server/audio/AudioDeviceInventoryTest.java
new file mode 100644
index 0000000..b5a538f
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/audio/AudioDeviceInventoryTest.java
@@ -0,0 +1,144 @@
+/*
+ * 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.audio;
+
+import static com.android.media.audio.Flags.asDeviceConnectionFailure;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.Manifest;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothProfile;
+import android.content.Context;
+import android.media.AudioDeviceAttributes;
+import android.media.AudioManager;
+import android.media.AudioSystem;
+import android.platform.test.annotations.Presubmit;
+import android.util.Log;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.MediumTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Spy;
+
+@MediumTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class AudioDeviceInventoryTest {
+
+    private static final String TAG = "AudioDeviceInventoryTest";
+
+    @Mock private AudioService mMockAudioService;
+    private AudioDeviceInventory mDevInventory;
+    @Spy private AudioDeviceBroker mSpyAudioDeviceBroker;
+    @Spy private AudioSystemAdapter mSpyAudioSystem;
+
+    private SystemServerAdapter mSystemServer;
+
+    private BluetoothDevice mFakeBtDevice;
+
+    @Before
+    public void setUp() throws Exception {
+        Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
+
+        mMockAudioService = mock(AudioService.class);
+        mSpyAudioSystem = spy(new NoOpAudioSystemAdapter());
+        mDevInventory = new AudioDeviceInventory(mSpyAudioSystem);
+        mSystemServer = new NoOpSystemServerAdapter();
+        mSpyAudioDeviceBroker = spy(new AudioDeviceBroker(context, mMockAudioService, mDevInventory,
+                mSystemServer, mSpyAudioSystem));
+        mDevInventory.setDeviceBroker(mSpyAudioDeviceBroker);
+
+        BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+        mFakeBtDevice = adapter.getRemoteDevice("00:01:02:03:04:05");
+    }
+
+    @After
+    public void tearDown() throws Exception { }
+
+    /**
+     * test that for DEVICE_OUT_BLUETOOTH_A2DP devices, when the device connects, it's only
+     * added to the connected devices when the connection through AudioSystem is successful
+     * @throws Exception on error
+     */
+    @Test
+    public void testSetDeviceConnectionStateA2dp() throws Exception {
+        Log.i(TAG, "starting testSetDeviceConnectionStateA2dp");
+        assertTrue("collection of connected devices not empty at start",
+                mDevInventory.getConnectedDevices().isEmpty());
+
+        final AudioDeviceAttributes ada = new AudioDeviceAttributes(
+                AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, mFakeBtDevice.getAddress());
+        AudioDeviceBroker.BtDeviceInfo btInfo =
+                new AudioDeviceBroker.BtDeviceInfo(mFakeBtDevice, BluetoothProfile.A2DP,
+                        BluetoothProfile.STATE_CONNECTED, AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
+                        AudioSystem.AUDIO_FORMAT_SBC);
+
+        // test that no device is added when AudioSystem returns AUDIO_STATUS_ERROR
+        // when setDeviceConnectionState is called for the connection
+        // NOTE: for now this is only when flag asDeviceConnectionFailure is true
+        if (asDeviceConnectionFailure()) {
+            when(mSpyAudioSystem.setDeviceConnectionState(ada, AudioSystem.DEVICE_STATE_AVAILABLE,
+                    AudioSystem.AUDIO_FORMAT_DEFAULT))
+                    .thenReturn(AudioSystem.AUDIO_STATUS_ERROR);
+            runWithBluetoothPrivilegedPermission(
+                    () ->  mDevInventory.onSetBtActiveDevice(/*btInfo*/ btInfo,
+                        /*codec*/ AudioSystem.AUDIO_FORMAT_DEFAULT, AudioManager.STREAM_MUSIC));
+
+            assertEquals(0, mDevInventory.getConnectedDevices().size());
+        }
+
+        // test that the device is added when AudioSystem returns AUDIO_STATUS_OK
+        // when setDeviceConnectionState is called for the connection
+        when(mSpyAudioSystem.setDeviceConnectionState(ada, AudioSystem.DEVICE_STATE_AVAILABLE,
+                AudioSystem.AUDIO_FORMAT_DEFAULT))
+                .thenReturn(AudioSystem.AUDIO_STATUS_OK);
+        runWithBluetoothPrivilegedPermission(
+                () ->  mDevInventory.onSetBtActiveDevice(/*btInfo*/ btInfo,
+                    /*codec*/ AudioSystem.AUDIO_FORMAT_DEFAULT, AudioManager.STREAM_MUSIC));
+        assertEquals(1, mDevInventory.getConnectedDevices().size());
+    }
+
+    // TODO add test for hearing aid
+
+    // TODO add test for BLE
+
+    /**
+     * Executes a Runnable while holding the BLUETOOTH_PRIVILEGED permission
+     * @param toRunWithPermission the runnable to run with BT privileges
+     */
+    private void runWithBluetoothPrivilegedPermission(Runnable toRunWithPermission) {
+        try {
+            InstrumentationRegistry.getInstrumentation().getUiAutomation()
+                    .adoptShellPermissionIdentity(Manifest.permission.BLUETOOTH_PRIVILEGED);
+            toRunWithPermission.run();
+        } finally {
+            InstrumentationRegistry.getInstrumentation().getUiAutomation()
+                    .dropShellPermissionIdentity();
+        }
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/autofill/HelperTest.java b/services/tests/servicestests/src/com/android/server/autofill/HelperTest.java
new file mode 100644
index 0000000..f698bea
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/autofill/HelperTest.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.autofill;
+
+import static com.android.server.autofill.Helper.SaveInfoStats;
+import static com.android.server.autofill.Helper.getSaveInfoStatsFromFillResponses;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Bundle;
+import android.service.autofill.FillResponse;
+import android.service.autofill.SaveInfo;
+import android.util.SparseArray;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class HelperTest {
+
+    @Test
+    public void testGetSaveInfoStatsFromFillResponses_nullFillResponses() {
+        SaveInfoStats saveInfoStats = getSaveInfoStatsFromFillResponses(null);
+
+        assertThat(saveInfoStats.saveInfoCount).isEqualTo(-1);
+        assertThat(saveInfoStats.saveDataTypeCount).isEqualTo(-1);
+    }
+
+    @Test
+    public void testGetSaveInfoStatsFromFillResponses_emptyFillResponseSparseArray() {
+        SaveInfoStats saveInfoStats = getSaveInfoStatsFromFillResponses(new SparseArray<>());
+
+        assertThat(saveInfoStats.saveInfoCount).isEqualTo(0);
+        assertThat(saveInfoStats.saveDataTypeCount).isEqualTo(0);
+    }
+
+    @Test
+    public void testGetSaveInfoStatsFromFillResponses_singleResponseWithoutSaveInfo() {
+        FillResponse.Builder fillResponseBuilder = new FillResponse.Builder();
+        // Add client state to satisfy the sanity check in FillResponseBuilder.build()
+        Bundle clientState = new Bundle();
+        fillResponseBuilder.setClientState(clientState);
+        FillResponse testFillResponse = fillResponseBuilder.build();
+
+        SparseArray<FillResponse> testFillResponses = new SparseArray<>();
+        testFillResponses.put(0, testFillResponse);
+
+        SaveInfoStats saveInfoStats = getSaveInfoStatsFromFillResponses(testFillResponses);
+
+        assertThat(saveInfoStats.saveInfoCount).isEqualTo(0);
+        assertThat(saveInfoStats.saveDataTypeCount).isEqualTo(0);
+    }
+
+    @Test
+    public void testGetSaveInfoStatsFromFillResponses_singleResponseWithSaveInfo() {
+        FillResponse.Builder fillResponseBuilder = new FillResponse.Builder();
+        SaveInfo.Builder saveInfoBuilder = new SaveInfo.Builder(SaveInfo.SAVE_DATA_TYPE_GENERIC);
+        fillResponseBuilder.setSaveInfo(saveInfoBuilder.build());
+        FillResponse testFillResponse = fillResponseBuilder.build();
+
+        SparseArray<FillResponse> testFillResponses = new SparseArray<>();
+        testFillResponses.put(0, testFillResponse);
+
+        SaveInfoStats saveInfoStats = getSaveInfoStatsFromFillResponses(testFillResponses);
+
+        assertThat(saveInfoStats.saveInfoCount).isEqualTo(1);
+        assertThat(saveInfoStats.saveDataTypeCount).isEqualTo(1);
+    }
+
+    @Test
+    public void testGetSaveInfoStatsFromFillResponses_multipleResponseWithDifferentTypeSaveInfo() {
+        FillResponse.Builder fillResponseBuilder1 = new FillResponse.Builder();
+        SaveInfo.Builder saveInfoBuilder1 = new SaveInfo.Builder(SaveInfo.SAVE_DATA_TYPE_GENERIC);
+        fillResponseBuilder1.setSaveInfo(saveInfoBuilder1.build());
+        FillResponse testFillResponse1 = fillResponseBuilder1.build();
+
+        FillResponse.Builder fillResponseBuilder2 = new FillResponse.Builder();
+        SaveInfo.Builder saveInfoBuilder2 = new SaveInfo.Builder(SaveInfo.SAVE_DATA_TYPE_ADDRESS);
+        fillResponseBuilder2.setSaveInfo(saveInfoBuilder2.build());
+        FillResponse testFillResponse2 = fillResponseBuilder2.build();
+
+        FillResponse.Builder fillResponseBuilder3 = new FillResponse.Builder();
+        SaveInfo.Builder saveInfoBuilder3 = new SaveInfo.Builder(SaveInfo.SAVE_DATA_TYPE_ADDRESS);
+        fillResponseBuilder3.setSaveInfo(saveInfoBuilder3.build());
+        FillResponse testFillResponse3 = fillResponseBuilder3.build();
+
+        SparseArray<FillResponse> testFillResponses = new SparseArray<>();
+        testFillResponses.put(0, testFillResponse1);
+        testFillResponses.put(1, testFillResponse2);
+        testFillResponses.put(2, testFillResponse3);
+
+        SaveInfoStats saveInfoStats = getSaveInfoStatsFromFillResponses(testFillResponses);
+
+        // Save info count is 3. Since two save info share the same save data type, the distinct
+        // save data type count is 2.
+        assertThat(saveInfoStats.saveInfoCount).isEqualTo(3);
+        assertThat(saveInfoStats.saveDataTypeCount).isEqualTo(2);
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
index 0f38532..a4222ff 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
@@ -1518,7 +1518,8 @@
         mBiometricService.onStart();
 
         when(mTrustManager.isInSignificantPlace()).thenReturn(false);
-        when(mBiometricService.mSettingObserver.getMandatoryBiometricsEnabledForUser(anyInt()))
+        when(mBiometricService.mSettingObserver
+                .getMandatoryBiometricsEnabledAndRequirementsSatisfiedForUser(anyInt()))
                 .thenReturn(true);
 
         setupAuthForOnly(TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG);
@@ -1540,7 +1541,8 @@
         mBiometricService.onStart();
 
         when(mTrustManager.isInSignificantPlace()).thenReturn(false);
-        when(mBiometricService.mSettingObserver.getMandatoryBiometricsEnabledForUser(anyInt()))
+        when(mBiometricService.mSettingObserver
+                .getMandatoryBiometricsEnabledAndRequirementsSatisfiedForUser(anyInt()))
                 .thenReturn(true);
 
         setupAuthForOnly(TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG);
@@ -1564,7 +1566,8 @@
         mBiometricService.onStart();
 
         when(mTrustManager.isInSignificantPlace()).thenReturn(false);
-        when(mBiometricService.mSettingObserver.getMandatoryBiometricsEnabledForUser(anyInt()))
+        when(mBiometricService.mSettingObserver
+                .getMandatoryBiometricsEnabledAndRequirementsSatisfiedForUser(anyInt()))
                 .thenReturn(true);
 
         setupAuthForOnly(TYPE_CREDENTIAL, Authenticators.DEVICE_CREDENTIAL);
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/PreAuthInfoTest.java b/services/tests/servicestests/src/com/android/server/biometrics/PreAuthInfoTest.java
index b831ef5..240da9f 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/PreAuthInfoTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/PreAuthInfoTest.java
@@ -90,7 +90,8 @@
         when(mDevicePolicyManager.getKeyguardDisabledFeatures(any(), anyInt()))
                 .thenReturn(KEYGUARD_DISABLE_FEATURES_NONE);
         when(mSettingObserver.getEnabledForApps(anyInt())).thenReturn(true);
-        when(mSettingObserver.getMandatoryBiometricsEnabledForUser(anyInt())).thenReturn(true);
+        when(mSettingObserver.getMandatoryBiometricsEnabledAndRequirementsSatisfiedForUser(
+                anyInt())).thenReturn(true);
         when(mFaceAuthenticator.hasEnrolledTemplates(anyInt(), any())).thenReturn(true);
         when(mFaceAuthenticator.isHardwareDetected(any())).thenReturn(true);
         when(mFaceAuthenticator.getLockoutModeForUser(anyInt()))
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
index 3789531..36a7b3d 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
@@ -1296,6 +1296,11 @@
             mFingerprints.add((Fingerprint) identifier);
         }
 
+        @Override
+        protected int getModality() {
+            return 0;
+        }
+
         public List<Fingerprint> getFingerprints() {
             return mFingerprints;
         }
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java
index 2d4dbb7..78c9372 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java
@@ -479,6 +479,15 @@
                 eq(mContext.getOpPackageName()), anyInt(), anyInt(), any());
     }
 
+    @Test
+    public void testCancelAuth_whenClientWaitingForCookie() throws RemoteException {
+        final FaceAuthenticationClient client = createClient(true);
+        client.waitForCookie(mCallback);
+        client.cancel();
+
+        verify(mCallback).onClientFinished(client, false);
+    }
+
     private FaceAuthenticationClient createClient() throws RemoteException {
         return createClient(2 /* version */, mClientMonitorCallbackConverter,
                 false /* allowBackgroundAuthentication */, true /* isBiometricPrompt */,
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java
index 6ec888c..7e1d421 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java
@@ -117,7 +117,6 @@
     private static final int TOUCH_Y = 20;
     private static final float TOUCH_MAJOR = 4.4f;
     private static final float TOUCH_MINOR = 5.5f;
-    private static final int FINGER_UP = 111;
 
     @Rule
     public final TestableContext mContext = new TestableContext(
@@ -383,6 +382,8 @@
 
     @Test
     public void subscribeContextAndStartHal() throws RemoteException {
+        when(mHal.authenticateWithContext(anyLong(), any())).thenReturn(mCancellationSignal);
+
         final FingerprintAuthenticationClient client = createClient();
         client.start(mCallback);
 
@@ -691,6 +692,17 @@
         verify(mLockoutTracker).addFailedAttemptForUser(USER_ID);
     }
 
+    @Test
+    public void testCancelAuth_whenClientWaitingForCookie() throws RemoteException {
+        final FingerprintAuthenticationClient client = createClientWithoutBackgroundAuth();
+        client.waitForCookie(mCallback);
+        client.cancel();
+        mLooper.moveTimeForward(10);
+        mLooper.dispatchAll();
+
+        verify(mCallback).onClientFinished(client, false);
+    }
+
     private FingerprintAuthenticationClient createClient() throws RemoteException {
         return createClient(100 /* version */, true /* allowBackgroundAuthentication */,
                 true /* isBiometricPrompt */,
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClientTest.java
index c9482ce..242880c 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClientTest.java
@@ -19,6 +19,7 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.eq;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
@@ -30,12 +31,16 @@
 import android.hardware.fingerprint.Fingerprint;
 import android.os.RemoteException;
 import android.platform.test.annotations.Presubmit;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
 import android.testing.TestableContext;
 
 import androidx.annotation.NonNull;
 import androidx.test.filters.SmallTest;
 import androidx.test.platform.app.InstrumentationRegistry;
 
+import com.android.server.biometrics.Flags;
 import com.android.server.biometrics.log.BiometricContext;
 import com.android.server.biometrics.log.BiometricLogger;
 import com.android.server.biometrics.sensors.ClientMonitorCallback;
@@ -69,6 +74,10 @@
     public final TestableContext mContext = new TestableContext(
             InstrumentationRegistry.getInstrumentation().getTargetContext(), null);
 
+    @Rule
+    public final CheckFlagsRule mCheckFlagsRule =
+            DeviceFlagsValueProvider.createCheckFlagsRule();
+
     @Mock
     ISession mSession;
     @Mock
@@ -168,6 +177,21 @@
         assertThat(mClient.getUnknownHALTemplates()).isEmpty();
     }
 
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_NOTIFY_FINGERPRINTS_LOE)
+    public void invalidBiometricUserState() throws Exception {
+        mClient =  createClient();
+
+        final List<Fingerprint> list = new ArrayList<>();
+        doReturn(true).when(mFingerprintUtils)
+                .hasValidBiometricUserState(mContext, 2);
+        doReturn(list).when(mFingerprintUtils).getBiometricsForUser(mContext, 2);
+
+        mClient.start(mCallback);
+        mClient.onEnumerationResult(null, 0);
+        verify(mFingerprintUtils).deleteStateForUser(2);
+    }
+
     protected FingerprintInternalCleanupClient createClient() {
         final Map<Integer, Long> authenticatorIds = new HashMap<>();
         return new FingerprintInternalCleanupClient(mContext, () -> mAidlSession, 2 /* userId */,
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/GenericWindowPolicyControllerTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/GenericWindowPolicyControllerTest.java
index e078238..7eabfac 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/GenericWindowPolicyControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/GenericWindowPolicyControllerTest.java
@@ -518,19 +518,6 @@
     }
 
     @Test
-    public void canActivityBeLaunched_permissionComponent_isBlocked() {
-        GenericWindowPolicyController gwpc = createGwpcWithPermissionComponent(BLOCKED_COMPONENT);
-        gwpc.setDisplayId(DISPLAY_ID, /* isMirrorDisplay= */ false);
-
-        ActivityInfo activityInfo = getActivityInfo(
-                BLOCKED_PACKAGE_NAME,
-                BLOCKED_PACKAGE_NAME,
-                /* displayOnRemoteDevices */ true,
-                /* targetDisplayCategory */ null);
-        assertActivityIsBlocked(gwpc, activityInfo);
-    }
-
-    @Test
     public void registerRunningAppsChangedListener_onRunningAppsChanged_listenersNotified() {
         ArraySet<Integer> uids = new ArraySet<>(Arrays.asList(TEST_UID));
         GenericWindowPolicyController gwpc = createGwpc();
@@ -740,7 +727,6 @@
                 /* activityPolicyExemptions= */ new ArraySet<>(),
                 /* crossTaskNavigationAllowedByDefault= */ true,
                 /* crossTaskNavigationExemptions= */ new ArraySet<>(),
-                /* permissionDialogComponent= */ null,
                 /* activityListener= */ mActivityListener,
                 /* activityBlockedCallback= */ mActivityBlockedCallback,
                 /* secureWindowCallback= */ mSecureWindowCallback,
@@ -760,7 +746,6 @@
                 /* activityPolicyExemptions= */ new ArraySet<>(),
                 /* crossTaskNavigationAllowedByDefault= */ true,
                 /* crossTaskNavigationExemptions= */ new ArraySet<>(),
-                /* permissionDialogComponent= */ null,
                 /* activityListener= */ mActivityListener,
                 /* activityBlockedCallback= */ mActivityBlockedCallback,
                 /* secureWindowCallback= */ mSecureWindowCallback,
@@ -781,7 +766,6 @@
                 /* activityPolicyExemptions= */ new ArraySet<>(),
                 /* crossTaskNavigationAllowedByDefault= */ true,
                 /* crossTaskNavigationExemptions= */ new ArraySet<>(),
-                /* permissionDialogComponent= */ null,
                 /* activityListener= */ mActivityListener,
                 /* activityBlockedCallback= */ mActivityBlockedCallback,
                 /* secureWindowCallback= */ null,
@@ -802,7 +786,6 @@
                 /* activityPolicyExemptions= */ Collections.singleton(blockedComponent),
                 /* crossTaskNavigationAllowedByDefault= */ true,
                 /* crossTaskNavigationExemptions= */ new ArraySet<>(),
-                /* permissionDialogComponent= */ null,
                 /* activityListener= */ mActivityListener,
                 /* activityBlockedCallback= */ mActivityBlockedCallback,
                 /* secureWindowCallback= */ null,
@@ -823,7 +806,6 @@
                 /* activityPolicyExemptions= */ Collections.singleton(allowedComponent),
                 /* crossTaskNavigationAllowedByDefault= */ true,
                 /* crossTaskNavigationExemptions= */ new ArraySet<>(),
-                /* permissionDialogComponent= */ null,
                 /* activityListener= */ mActivityListener,
                 /* activityBlockedCallback= */ mActivityBlockedCallback,
                 /* secureWindowCallback= */ null,
@@ -844,7 +826,6 @@
                 /* activityPolicyExemptions= */ new ArraySet<>(),
                 /* crossTaskNavigationAllowedByDefault= */ true,
                 /* crossTaskNavigationExemptions= */ new ArraySet<>(),
-                /* permissionDialogComponent= */ null,
                 /* activityListener= */ mActivityListener,
                 /* activityBlockedCallback= */ mActivityBlockedCallback,
                 /* secureWindowCallback= */ null,
@@ -865,7 +846,6 @@
                 /* activityPolicyExemptions= */ new ArraySet<>(),
                 /* crossTaskNavigationAllowedByDefault= */ true,
                 /* crossTaskNavigationExemptions= */ Collections.singleton(blockedComponent),
-                /* permissionDialogComponent= */ null,
                 /* activityListener= */ mActivityListener,
                 /* activityBlockedCallback= */ mActivityBlockedCallback,
                 /* secureWindowCallback= */ null,
@@ -886,29 +866,6 @@
                 /* activityPolicyExemptions= */ new ArraySet<>(),
                 /* crossTaskNavigationAllowedByDefault= */ false,
                 /* crossTaskNavigationExemptions= */ Collections.singleton(allowedComponent),
-                /* permissionDialogComponent= */ null,
-                /* activityListener= */ mActivityListener,
-                /* activityBlockedCallback= */ mActivityBlockedCallback,
-                /* secureWindowCallback= */ null,
-                /* intentListenerCallback= */ mIntentListenerCallback,
-                /* displayCategories= */ new ArraySet<>(),
-                /* showTasksInHostDeviceRecents= */ true,
-                /* customHomeComponent= */ null);
-    }
-
-    private GenericWindowPolicyController createGwpcWithPermissionComponent(
-            ComponentName permissionComponent) {
-        //TODO instert the component
-        return new GenericWindowPolicyController(
-                0,
-                0,
-                AttributionSource.myAttributionSource(),
-                /* allowedUsers= */ new ArraySet<>(getCurrentUserId()),
-                /* activityLaunchAllowedByDefault= */ true,
-                /* activityPolicyExemptions= */ new ArraySet<>(),
-                /* crossTaskNavigationAllowedByDefault= */ false,
-                /* crossTaskNavigationExemptions= */ new ArraySet<>(),
-                /* permissionDialogComponent= */ permissionComponent,
                 /* activityListener= */ mActivityListener,
                 /* activityBlockedCallback= */ mActivityBlockedCallback,
                 /* secureWindowCallback= */ null,
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 da8961d..19712ea 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
@@ -1550,8 +1550,7 @@
     }
 
     @Test
-    public void openPermissionControllerOnVirtualDisplay_displayOnRemoteDevices_startsWhenFlagIsEnabled() {
-        mSetFlagsRule.enableFlags(Flags.FLAG_STREAM_PERMISSIONS);
+    public void openPermissionControllerOnVirtualDisplay_displayOnRemoteDevices_starts() {
         addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
         GenericWindowPolicyController gwpc = mDeviceImpl.getDisplayWindowPolicyControllerForTest(
                 DISPLAY_ID_1);
@@ -1572,8 +1571,7 @@
     }
 
     @Test
-    public void openPermissionControllerOnVirtualDisplay_dontDisplayOnRemoteDevices_startsWhenFlagIsEnabled() {
-        mSetFlagsRule.enableFlags(Flags.FLAG_STREAM_PERMISSIONS);
+    public void openPermissionControllerOnVirtualDisplay_dontDisplayOnRemoteDevices_starts() {
         addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
         GenericWindowPolicyController gwpc = mDeviceImpl.getDisplayWindowPolicyControllerForTest(
                 DISPLAY_ID_1);
@@ -1755,35 +1753,7 @@
     }
 
     @Test
-    public void canActivityBeLaunched_permissionDialog_flagDisabled_isBlocked() {
-        mSetFlagsRule.disableFlags(Flags.FLAG_STREAM_PERMISSIONS);
-        VirtualDeviceParams params = new VirtualDeviceParams.Builder().build();
-        mDeviceImpl.close();
-        mDeviceImpl = createVirtualDevice(VIRTUAL_DEVICE_ID_1, DEVICE_OWNER_UID_1, params);
-        doNothing().when(mContext).startActivityAsUser(any(), any(), any());
-
-        addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
-        GenericWindowPolicyController gwpc = mDeviceImpl.getDisplayWindowPolicyControllerForTest(
-                DISPLAY_ID_1);
-        ComponentName permissionComponent = getPermissionDialogComponent();
-        ActivityInfo activityInfo = getActivityInfo(
-                permissionComponent.getPackageName(),
-                permissionComponent.getClassName(),
-                /* displayOnRemoteDevices */ true,
-                /* targetDisplayCategory */ null);
-        assertThat(gwpc.canActivityBeLaunched(activityInfo, null,
-                WindowConfiguration.WINDOWING_MODE_FULLSCREEN, DISPLAY_ID_1, /*isNewTask=*/false))
-                .isFalse();
-
-        Intent blockedAppIntent = BlockedAppStreamingActivity.createIntent(
-                activityInfo, mAssociationInfo.getDisplayName());
-        verify(mContext).startActivityAsUser(argThat(intent ->
-                intent.filterEquals(blockedAppIntent)), any(), any());
-    }
-
-    @Test
-    public void canActivityBeLaunched_permissionDialog_flagEnabled_isStreamed() {
-        mSetFlagsRule.enableFlags(Flags.FLAG_STREAM_PERMISSIONS);
+    public void canActivityBeLaunched_permissionDialog_isStreamed() {
         VirtualDeviceParams params = new VirtualDeviceParams.Builder().build();
         mDeviceImpl.close();
         mDeviceImpl = createVirtualDevice(VIRTUAL_DEVICE_ID_1, DEVICE_OWNER_UID_1, params);
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/audio/VirtualAudioControllerTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/audio/VirtualAudioControllerTest.java
index b946a43..c3db396 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/audio/VirtualAudioControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/audio/VirtualAudioControllerTest.java
@@ -85,7 +85,6 @@
                         /* activityPolicyExemptions= */ new ArraySet<>(),
                         /* crossTaskNavigationAllowedByDefault= */ true,
                         /* crossTaskNavigationExemptions= */ new ArraySet<>(),
-                        /* permissionDialogComponent */ null,
                         /* activityListener= */ null,
                         /* activityBlockedCallback= */ null,
                         /* secureWindowCallback= */ null,
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
index 87b52e6..f98bbf9 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
@@ -28,6 +28,7 @@
 import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_WAKE_UP_MESSAGE;
 import static com.android.server.hdmi.HdmiControlService.STANDBY_SCREEN_OFF;
 import static com.android.server.hdmi.HdmiControlService.WAKE_UP_SCREEN_ON;
+import static com.android.server.hdmi.RequestActiveSourceAction.TIMEOUT_WAIT_FOR_LAUNCHERX_API_CALL_MS;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -1792,7 +1793,7 @@
         mTestLooper.dispatchAll();
 
         // Skip the LauncherX API timeout.
-        mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS * 2);
+        mTestLooper.moveTimeForward(TIMEOUT_WAIT_FOR_LAUNCHERX_API_CALL_MS);
         mTestLooper.dispatchAll();
 
         assertThat(mNativeWrapper.getResultMessages()).contains(requestActiveSource);
@@ -1825,7 +1826,7 @@
         mTestLooper.dispatchAll();
 
         // Skip the LauncherX API timeout.
-        mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS * 2);
+        mTestLooper.moveTimeForward(TIMEOUT_WAIT_FOR_LAUNCHERX_API_CALL_MS);
         mTestLooper.dispatchAll();
 
         assertThat(mNativeWrapper.getResultMessages()).contains(requestActiveSource);
@@ -1861,7 +1862,7 @@
         mTestLooper.dispatchAll();
 
         // Skip the LauncherX API timeout.
-        mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS * 2);
+        mTestLooper.moveTimeForward(TIMEOUT_WAIT_FOR_LAUNCHERX_API_CALL_MS);
         mTestLooper.dispatchAll();
 
         assertThat(mNativeWrapper.getResultMessages()).contains(requestActiveSource);
@@ -1904,7 +1905,7 @@
         mTestLooper.dispatchAll();
 
         // Skip the LauncherX API timeout.
-        mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS * 2);
+        mTestLooper.moveTimeForward(TIMEOUT_WAIT_FOR_LAUNCHERX_API_CALL_MS);
         mTestLooper.dispatchAll();
 
         assertThat(mNativeWrapper.getResultMessages()).contains(requestActiveSource);
@@ -1941,7 +1942,7 @@
         mHdmiControlService.sendCecCommand(setStreamPathFromTv);
 
         // Skip the LauncherX API timeout.
-        mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS * 2);
+        mTestLooper.moveTimeForward(TIMEOUT_WAIT_FOR_LAUNCHERX_API_CALL_MS);
         mTestLooper.dispatchAll();
 
         assertThat(mNativeWrapper.getResultMessages()).doesNotContain(requestActiveSource);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecNetworkTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecNetworkTest.java
index 599a3b8..4f7f381 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecNetworkTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecNetworkTest.java
@@ -270,6 +270,36 @@
     }
 
     @Test
+    public void cecDevices_tracking_event_update_device() {
+        int logicalAddress = Constants.ADDR_PLAYBACK_1;
+        int initialPhysicalAddress = 0x1000;
+        int type = HdmiDeviceInfo.DEVICE_PLAYBACK;
+        String osdName = "Test Device";
+
+        mHdmiCecNetwork.handleCecMessage(
+                HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(logicalAddress,
+                        initialPhysicalAddress, type));
+        // Change OSD to make the info different
+        mHdmiCecNetwork.handleCecMessage(
+                HdmiCecMessageBuilder.buildSetOsdNameCommand(logicalAddress,
+                        Constants.ADDR_BROADCAST, osdName));
+        synchronized (mHdmiCecNetwork.mLock) {
+            assertThat(mHdmiCecNetwork.getSafeCecDevicesLocked()).hasSize(1);
+        }
+
+        HdmiDeviceInfo cecDeviceInfo = mHdmiCecNetwork.getCecDeviceInfo(logicalAddress);
+        assertThat(cecDeviceInfo.getLogicalAddress()).isEqualTo(logicalAddress);
+        assertThat(cecDeviceInfo.getPhysicalAddress()).isEqualTo(initialPhysicalAddress);
+        assertThat(cecDeviceInfo.getDeviceType()).isEqualTo(type);
+
+        // ADD for physical address first detected
+        // UPDATE for updating device with new OSD name
+        assertThat(mDeviceEventListenerStatuses).containsExactly(
+                HdmiControlManager.DEVICE_EVENT_ADD_DEVICE,
+                HdmiControlManager.DEVICE_EVENT_UPDATE_DEVICE);
+    }
+
+    @Test
     public void cecDevices_tracking_updateDeviceInfo_sameDoesntNotify() {
         int logicalAddress = Constants.ADDR_PLAYBACK_1;
         int physicalAddress = 0x1000;
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/OneTouchPlayActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/OneTouchPlayActionTest.java
index 2f4a660..a8856dd 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/OneTouchPlayActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/OneTouchPlayActionTest.java
@@ -20,6 +20,7 @@
 import static com.android.server.hdmi.Constants.ADDR_TV;
 import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC;
 import static com.android.server.hdmi.HdmiControlService.WAKE_UP_SCREEN_ON;
+import static com.android.server.hdmi.OneTouchPlayAction.LOOP_COUNTER_MAX;
 import static com.android.server.hdmi.OneTouchPlayAction.STATE_WAITING_FOR_REPORT_POWER_STATUS;
 
 import static com.google.common.truth.Truth.assertThat;
@@ -335,7 +336,7 @@
             mTestLooper.dispatchAll();
         }
 
-        assertThat(mNativeWrapper.getResultMessages()).doesNotContain(textViewOn);
+        assertThat(mNativeWrapper.getResultMessages()).contains(textViewOn);
         assertThat(mNativeWrapper.getResultMessages()).doesNotContain(activeSource);
         assertThat(mNativeWrapper.getResultMessages()).contains(giveDevicePowerStatus);
         action.handleTimerEvent(STATE_WAITING_FOR_REPORT_POWER_STATUS);
@@ -672,7 +673,122 @@
                         mHdmiControlService.playback().getDeviceInfo().getLogicalAddress(),
                         ADDR_TV);
         assertThat(mNativeWrapper.getResultMessages()).doesNotContain(textViewOn);
+    }
 
+    @Test
+    public void waitForReportPowerStatus_resendTextViewOn_timeout() throws Exception {
+        setUp(true);
+
+        HdmiCecLocalDevicePlayback playbackDevice = mHdmiControlService.playback();
+        mTestLooper.dispatchAll();
+
+        mNativeWrapper.setPollAddressResponse(ADDR_TV, SendMessageResult.SUCCESS);
+        mHdmiControlService.getHdmiCecNetwork().addCecDevice(INFO_TV);
+        mTestLooper.dispatchAll();
+        mNativeWrapper.clearResultMessages();
+
+        TestActionTimer actionTimer = new TestActionTimer();
+        TestCallback callback = new TestCallback();
+        OneTouchPlayAction action = createOneTouchPlayAction(playbackDevice, actionTimer, callback,
+                false);
+        playbackDevice.addAndStartAction(action);
+        mTestLooper.dispatchAll();
+
+        HdmiCecMessage activeSource =
+                HdmiCecMessageBuilder.buildActiveSource(
+                        playbackDevice.getDeviceInfo().getLogicalAddress(), mPhysicalAddress);
+        HdmiCecMessage textViewOn =
+                HdmiCecMessageBuilder.buildTextViewOn(
+                        playbackDevice.getDeviceInfo().getLogicalAddress(), ADDR_TV);
+        HdmiCecMessage giveDevicePowerStatus =
+                HdmiCecMessageBuilder.buildGiveDevicePowerStatus(
+                        playbackDevice.getDeviceInfo().getLogicalAddress(), ADDR_TV);
+
+        assertThat(mNativeWrapper.getResultMessages()).contains(textViewOn);
+        assertThat(mNativeWrapper.getResultMessages()).contains(activeSource);
+        assertThat(mNativeWrapper.getResultMessages()).contains(giveDevicePowerStatus);
+        mNativeWrapper.clearResultMessages();
+        assertThat(actionTimer.getState()).isEqualTo(STATE_WAITING_FOR_REPORT_POWER_STATUS);
+
+        int counter = 0;
+        while (counter++ < LOOP_COUNTER_MAX) {
+            action.handleTimerEvent(STATE_WAITING_FOR_REPORT_POWER_STATUS);
+            mTestLooper.dispatchAll();
+
+            if (counter % 3 == 0) {
+                assertThat(mNativeWrapper.getResultMessages()).contains(textViewOn);
+            }
+            assertThat(mNativeWrapper.getResultMessages()).contains(giveDevicePowerStatus);
+            mNativeWrapper.clearResultMessages();
+            mTestLooper.dispatchAll();
+        }
+
+        action.handleTimerEvent(STATE_WAITING_FOR_REPORT_POWER_STATUS);
+        assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_TIMEOUT);
+    }
+
+    @Test
+    public void waitForReportPowerStatus_resendTextViewOn_success() throws Exception {
+        setUp(true);
+
+        HdmiCecLocalDevicePlayback playbackDevice = mHdmiControlService.playback();
+        mTestLooper.dispatchAll();
+
+        mNativeWrapper.setPollAddressResponse(ADDR_TV, SendMessageResult.SUCCESS);
+        mHdmiControlService.getHdmiCecNetwork().addCecDevice(INFO_TV);
+        mTestLooper.dispatchAll();
+        mNativeWrapper.clearResultMessages();
+
+        TestActionTimer actionTimer = new TestActionTimer();
+        TestCallback callback = new TestCallback();
+        OneTouchPlayAction action = createOneTouchPlayAction(playbackDevice, actionTimer, callback,
+                false);
+        playbackDevice.addAndStartAction(action);
+        mTestLooper.dispatchAll();
+
+        HdmiCecMessage activeSource =
+                HdmiCecMessageBuilder.buildActiveSource(
+                        playbackDevice.getDeviceInfo().getLogicalAddress(), mPhysicalAddress);
+        HdmiCecMessage textViewOn =
+                HdmiCecMessageBuilder.buildTextViewOn(
+                        playbackDevice.getDeviceInfo().getLogicalAddress(), ADDR_TV);
+        HdmiCecMessage giveDevicePowerStatus =
+                HdmiCecMessageBuilder.buildGiveDevicePowerStatus(
+                        playbackDevice.getDeviceInfo().getLogicalAddress(), ADDR_TV);
+
+        assertThat(mNativeWrapper.getResultMessages()).contains(textViewOn);
+        assertThat(mNativeWrapper.getResultMessages()).contains(activeSource);
+        assertThat(mNativeWrapper.getResultMessages()).contains(giveDevicePowerStatus);
+        mNativeWrapper.clearResultMessages();
+        assertThat(actionTimer.getState()).isEqualTo(STATE_WAITING_FOR_REPORT_POWER_STATUS);
+
+        int counter = 0;
+        while (counter++ < LOOP_COUNTER_MAX) {
+            action.handleTimerEvent(STATE_WAITING_FOR_REPORT_POWER_STATUS);
+            mTestLooper.dispatchAll();
+
+            if (counter % 3 == 0) {
+                assertThat(mNativeWrapper.getResultMessages()).contains(textViewOn);
+            }
+            assertThat(mNativeWrapper.getResultMessages()).contains(giveDevicePowerStatus);
+            mNativeWrapper.clearResultMessages();
+            mTestLooper.dispatchAll();
+        }
+
+        assertThat(actionTimer.getState()).isEqualTo(STATE_WAITING_FOR_REPORT_POWER_STATUS);
+        HdmiCecMessage reportPowerStatusOn =
+                HdmiCecMessage.build(
+                        ADDR_TV,
+                        playbackDevice.getDeviceInfo().getLogicalAddress(),
+                        Constants.MESSAGE_REPORT_POWER_STATUS,
+                        POWER_ON);
+        action.processCommand(reportPowerStatusOn);
+        mTestLooper.dispatchAll();
+
+        assertThat(mNativeWrapper.getResultMessages()).doesNotContain(textViewOn);
+        assertThat(mNativeWrapper.getResultMessages()).contains(activeSource);
+        assertThat(mNativeWrapper.getResultMessages()).doesNotContain(giveDevicePowerStatus);
+        assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_SUCCESS);
     }
 
     private static class TestActionTimer implements ActionTimer {
diff --git a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java
index 316b5fa..689b241 100644
--- a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java
@@ -39,6 +39,7 @@
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
@@ -364,6 +365,39 @@
     @EnableFlags(android.companion.virtualdevice.flags
             .Flags.FLAG_MEDIA_PROJECTION_KEYGUARD_RESTRICTIONS)
     @Test
+    public void testReuseProjection_keyguardNotLocked_startConsentDialog()
+            throws NameNotFoundException {
+        MediaProjectionManagerService.MediaProjection projection = startProjectionPreconditions();
+        projection.start(mIMediaProjectionCallback);
+
+        doNothing().when(mContext).startActivityAsUser(any(), any());
+        doReturn(false).when(mKeyguardManager).isKeyguardLocked();
+
+        MediaProjectionManagerService.BinderService mediaProjectionBinderService =
+                mService.new BinderService(mContext);
+        mediaProjectionBinderService.requestConsentForInvalidProjection(projection);
+
+        verify(mContext).startActivityAsUser(any(), any());
+    }
+
+    @EnableFlags(android.companion.virtualdevice.flags
+            .Flags.FLAG_MEDIA_PROJECTION_KEYGUARD_RESTRICTIONS)
+    @Test
+    public void testReuseProjection_keyguardLocked_noConsentDialog() throws NameNotFoundException {
+        MediaProjectionManagerService.MediaProjection projection = startProjectionPreconditions();
+        projection.start(mIMediaProjectionCallback);
+
+        doReturn(true).when(mKeyguardManager).isKeyguardLocked();
+        MediaProjectionManagerService.BinderService mediaProjectionBinderService =
+                mService.new BinderService(mContext);
+        mediaProjectionBinderService.requestConsentForInvalidProjection(projection);
+
+        verify(mContext, never()).startActivityAsUser(any(), any());
+    }
+
+    @EnableFlags(android.companion.virtualdevice.flags
+            .Flags.FLAG_MEDIA_PROJECTION_KEYGUARD_RESTRICTIONS)
+    @Test
     public void testKeyguardLocked_stopsActiveProjection() throws Exception {
         MediaProjectionManagerService service =
                 new MediaProjectionManagerService(mContext, mMediaProjectionMetricsLoggerInjector);
diff --git a/services/tests/servicestests/src/com/android/server/power/hint/TEST_MAPPING b/services/tests/servicestests/src/com/android/server/power/hint/TEST_MAPPING
deleted file mode 100644
index 874eec7..0000000
--- a/services/tests/servicestests/src/com/android/server/power/hint/TEST_MAPPING
+++ /dev/null
@@ -1,15 +0,0 @@
-{
-  "presubmit": [
-    {
-      "name": "FrameworksServicesTests",
-      "options": [
-        {
-          "include-filter": "com.android.server.power.hint"
-        },
-        {
-          "exclude-annotation": "androidx.test.filters.FlakyTest"
-        }
-      ]
-    }
-  ]
-}
diff --git a/services/tests/timetests/src/com/android/server/timezonedetector/location/ZoneInfoDbTimeZoneProviderEventPreProcessorTest.java b/services/tests/timetests/src/com/android/server/timezonedetector/location/ZoneInfoDbTimeZoneProviderEventPreProcessorTest.java
index f3440f7..ea3b409 100644
--- a/services/tests/timetests/src/com/android/server/timezonedetector/location/ZoneInfoDbTimeZoneProviderEventPreProcessorTest.java
+++ b/services/tests/timetests/src/com/android/server/timezonedetector/location/ZoneInfoDbTimeZoneProviderEventPreProcessorTest.java
@@ -39,13 +39,23 @@
 
     private static final long ARBITRARY_TIME_MILLIS = 11223344;
 
+    private final List<String> mNonExistingTimeZones = Arrays.asList(
+            "SystemV/HST10", "Atlantic/Atlantis", "EUROPE/LONDON", "Etc/GMT-5:30");
     private final ZoneInfoDbTimeZoneProviderEventPreProcessor mPreProcessor =
             new ZoneInfoDbTimeZoneProviderEventPreProcessor();
 
+    private static final TimeZoneProviderStatus ARBITRARY_TIME_ZONE_PROVIDER_STATUS =
+            new TimeZoneProviderStatus.Builder()
+                    .setConnectivityDependencyStatus(DEPENDENCY_STATUS_OK)
+                    .setLocationDetectionDependencyStatus(DEPENDENCY_STATUS_OK)
+                    .setTimeZoneResolutionOperationStatus(OPERATION_STATUS_OK)
+                    .build();
+
     @Test
     public void timeZoneIdsFromZoneInfoDbAreValid() {
         for (String timeZone : TimeZone.getAvailableIDs()) {
-            TimeZoneProviderEvent event = timeZoneProviderEvent(timeZone);
+            TimeZoneProviderEvent event = timeZoneProviderEvent(timeZone,
+                    ARBITRARY_TIME_ZONE_PROVIDER_STATUS);
             assertWithMessage("Time zone %s should be supported", timeZone)
                     .that(mPreProcessor.preProcess(event)).isEqualTo(event);
         }
@@ -53,11 +63,9 @@
 
     @Test
     public void eventWithNonExistingZones_areMappedToUncertainEvent() {
-        List<String> nonExistingTimeZones = Arrays.asList(
-                "SystemV/HST10", "Atlantic/Atlantis", "EUROPE/LONDON", "Etc/GMT-5:30");
-
-        for (String timeZone : nonExistingTimeZones) {
-            TimeZoneProviderEvent event = timeZoneProviderEvent(timeZone);
+        for (String timeZone : mNonExistingTimeZones) {
+            TimeZoneProviderEvent event = timeZoneProviderEvent(timeZone,
+                    ARBITRARY_TIME_ZONE_PROVIDER_STATUS);
 
             TimeZoneProviderStatus expectedProviderStatus =
                     new TimeZoneProviderStatus.Builder(event.getTimeZoneProviderStatus())
@@ -73,14 +81,31 @@
         }
     }
 
-    private static TimeZoneProviderEvent timeZoneProviderEvent(String... timeZoneIds) {
-        TimeZoneProviderStatus providerStatus = new TimeZoneProviderStatus.Builder()
-                .setLocationDetectionDependencyStatus(DEPENDENCY_STATUS_OK)
-                .setConnectivityDependencyStatus(DEPENDENCY_STATUS_OK)
-                .setTimeZoneResolutionOperationStatus(OPERATION_STATUS_OK)
-                .build();
+    @Test
+    public void eventWithNullProviderStatus_areMappedToUncertainEvent() {
+        for (String timeZone : mNonExistingTimeZones) {
+            TimeZoneProviderEvent eventWithNullStatus = timeZoneProviderEvent(timeZone,
+                    /* providerStatus= */ null);
+
+            TimeZoneProviderStatus expectedProviderStatus =
+                    new TimeZoneProviderStatus.Builder()
+                            .setTimeZoneResolutionOperationStatus(OPERATION_STATUS_FAILED)
+                            .build();
+
+            TimeZoneProviderEvent expectedResultEvent =
+                    TimeZoneProviderEvent.createUncertainEvent(
+                            eventWithNullStatus.getCreationElapsedMillis(),
+                            expectedProviderStatus);
+            assertWithMessage(timeZone + " with null time zone provider status")
+                    .that(mPreProcessor.preProcess(eventWithNullStatus))
+                    .isEqualTo(expectedResultEvent);
+        }
+    }
+
+    private static TimeZoneProviderEvent timeZoneProviderEvent(String timeZoneId,
+            TimeZoneProviderStatus providerStatus) {
         TimeZoneProviderSuggestion suggestion = new TimeZoneProviderSuggestion.Builder()
-                .setTimeZoneIds(Arrays.asList(timeZoneIds))
+                .setTimeZoneIds(Arrays.asList(timeZoneId))
                 .setElapsedRealtimeMillis(ARBITRARY_TIME_MILLIS)
                 .build();
         return TimeZoneProviderEvent.createSuggestionEvent(
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
index e5c42082..fb82b872c 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
@@ -888,7 +888,7 @@
             return true;
         });
 
-        mockServiceInfoWithMetaData(List.of(cn), service, new ArrayMap<>());
+        mockServiceInfoWithMetaData(List.of(cn), service, pm, new ArrayMap<>());
         service.addApprovedList("a", 0, true);
 
         service.reregisterService(cn, 0);
@@ -919,7 +919,7 @@
             return true;
         });
 
-        mockServiceInfoWithMetaData(List.of(cn), service, new ArrayMap<>());
+        mockServiceInfoWithMetaData(List.of(cn), service, pm, new ArrayMap<>());
         service.addApprovedList("a", 0, false);
 
         service.reregisterService(cn, 0);
@@ -950,7 +950,7 @@
             return true;
         });
 
-        mockServiceInfoWithMetaData(List.of(cn), service, new ArrayMap<>());
+        mockServiceInfoWithMetaData(List.of(cn), service, pm, new ArrayMap<>());
         service.addApprovedList("a/a", 0, true);
 
         service.reregisterService(cn, 0);
@@ -981,7 +981,7 @@
             return true;
         });
 
-        mockServiceInfoWithMetaData(List.of(cn), service, new ArrayMap<>());
+        mockServiceInfoWithMetaData(List.of(cn), service, pm, new ArrayMap<>());
         service.addApprovedList("a/a", 0, false);
 
         service.reregisterService(cn, 0);
@@ -1211,6 +1211,64 @@
     }
 
     @Test
+    public void testUpgradeAppNoIntentFilterNoRebind() throws Exception {
+        Context context = spy(getContext());
+        doReturn(true).when(context).bindServiceAsUser(any(), any(), anyInt(), any());
+
+        ManagedServices service = new TestManagedServices(context, mLock, mUserProfiles,
+                mIpm, APPROVAL_BY_COMPONENT);
+
+        List<String> packages = new ArrayList<>();
+        packages.add("package");
+        addExpectedServices(service, packages, 0);
+
+        final ComponentName unapprovedComponent = ComponentName.unflattenFromString("package/C1");
+        final ComponentName approvedComponent = ComponentName.unflattenFromString("package/C2");
+
+        // Both components are approved initially
+        mExpectedPrimaryComponentNames.clear();
+        mExpectedPrimaryPackages.clear();
+        mExpectedPrimaryComponentNames.put(0, "package/C1:package/C2");
+        mExpectedSecondaryComponentNames.clear();
+        mExpectedSecondaryPackages.clear();
+
+        loadXml(service);
+
+        //Component package/C1 loses serviceInterface intent filter
+        ManagedServices.Config config = service.getConfig();
+        when(mPm.queryIntentServicesAsUser(any(), anyInt(), anyInt())).
+            thenAnswer(new Answer<List<ResolveInfo>>() {
+                @Override
+                public List<ResolveInfo> answer(InvocationOnMock invocationOnMock)
+                    throws Throwable {
+                    Object[] args = invocationOnMock.getArguments();
+                    Intent invocationIntent = (Intent) args[0];
+                    if (invocationIntent != null) {
+                        if (invocationIntent.getAction().equals(config.serviceInterface)
+                            && packages.contains(invocationIntent.getPackage())) {
+                            List<ResolveInfo> dummyServices = new ArrayList<>();
+                            ResolveInfo resolveInfo = new ResolveInfo();
+                            ServiceInfo serviceInfo = new ServiceInfo();
+                            serviceInfo.packageName = invocationIntent.getPackage();
+                            serviceInfo.name = approvedComponent.getClassName();
+                            serviceInfo.permission = service.getConfig().bindPermission;
+                            resolveInfo.serviceInfo = serviceInfo;
+                            dummyServices.add(resolveInfo);
+                            return dummyServices;
+                        }
+                    }
+                    return new ArrayList<>();
+                }
+            });
+
+        // Trigger package update
+        service.onPackagesChanged(false, new String[]{"package"}, new int[]{0});
+
+        assertFalse(service.isComponentEnabledForCurrentProfiles(unapprovedComponent));
+        assertTrue(service.isComponentEnabledForCurrentProfiles(approvedComponent));
+    }
+
+    @Test
     public void testSetPackageOrComponentEnabled() throws Exception {
         for (int approvalLevel : new int[] {APPROVAL_BY_COMPONENT, APPROVAL_BY_PACKAGE}) {
             ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles,
@@ -1915,7 +1973,7 @@
         metaDataAutobindAllow.putBoolean(META_DATA_DEFAULT_AUTOBIND, true);
         metaDatas.put(cn_allowed, metaDataAutobindAllow);
 
-        mockServiceInfoWithMetaData(componentNames, service, metaDatas);
+        mockServiceInfoWithMetaData(componentNames, service, pm, metaDatas);
 
         service.addApprovedList(cn_allowed.flattenToString(), 0, true);
         service.addApprovedList(cn_disallowed.flattenToString(), 0, true);
@@ -1960,7 +2018,7 @@
         metaDataAutobindDisallow.putBoolean(META_DATA_DEFAULT_AUTOBIND, false);
         metaDatas.put(cn_disallowed, metaDataAutobindDisallow);
 
-        mockServiceInfoWithMetaData(componentNames, service, metaDatas);
+        mockServiceInfoWithMetaData(componentNames, service, pm, metaDatas);
 
         service.addApprovedList(cn_disallowed.flattenToString(), 0, true);
 
@@ -1999,7 +2057,7 @@
         metaDataAutobindDisallow.putBoolean(META_DATA_DEFAULT_AUTOBIND, false);
         metaDatas.put(cn_disallowed, metaDataAutobindDisallow);
 
-        mockServiceInfoWithMetaData(componentNames, service, metaDatas);
+        mockServiceInfoWithMetaData(componentNames, service, pm, metaDatas);
 
         service.addApprovedList(cn_disallowed.flattenToString(), 0, true);
 
@@ -2070,8 +2128,8 @@
     }
 
     private void mockServiceInfoWithMetaData(List<ComponentName> componentNames,
-            ManagedServices service, ArrayMap<ComponentName, Bundle> metaDatas)
-            throws RemoteException {
+            ManagedServices service, PackageManager packageManager,
+            ArrayMap<ComponentName, Bundle> metaDatas) throws RemoteException {
         when(mIpm.getServiceInfo(any(), anyLong(), anyInt())).thenAnswer(
                 (Answer<ServiceInfo>) invocation -> {
                     ComponentName invocationCn = invocation.getArgument(0);
@@ -2086,6 +2144,39 @@
                     return null;
                 }
         );
+
+        // add components to queryIntentServicesAsUser response
+        final List<String> packages = new ArrayList<>();
+        for (ComponentName cn: componentNames) {
+            packages.add(cn.getPackageName());
+        }
+        ManagedServices.Config config = service.getConfig();
+        when(packageManager.queryIntentServicesAsUser(any(), anyInt(), anyInt())).
+                thenAnswer(new Answer<List<ResolveInfo>>() {
+                @Override
+                public List<ResolveInfo> answer(InvocationOnMock invocationOnMock)
+                    throws Throwable {
+                    Object[] args = invocationOnMock.getArguments();
+                    Intent invocationIntent = (Intent) args[0];
+                    if (invocationIntent != null) {
+                        if (invocationIntent.getAction().equals(config.serviceInterface)
+                            && packages.contains(invocationIntent.getPackage())) {
+                            List<ResolveInfo> dummyServices = new ArrayList<>();
+                            for (ComponentName cn: componentNames) {
+                                ResolveInfo resolveInfo = new ResolveInfo();
+                                ServiceInfo serviceInfo = new ServiceInfo();
+                                serviceInfo.packageName = invocationIntent.getPackage();
+                                serviceInfo.name = cn.getClassName();
+                                serviceInfo.permission = service.getConfig().bindPermission;
+                                resolveInfo.serviceInfo = serviceInfo;
+                                dummyServices.add(resolveInfo);
+                            }
+                            return dummyServices;
+                        }
+                    }
+                    return new ArrayList<>();
+                }
+            });
     }
 
     private void resetComponentsAndPackages() {
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java
index e06d939..de70280 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java
@@ -2454,6 +2454,50 @@
     }
 
     @Test
+    public void testBeepVolume_politeNotif_Avalanche_exemptCategories() throws Exception {
+        mSetFlagsRule.enableFlags(Flags.FLAG_POLITE_NOTIFICATIONS);
+        mSetFlagsRule.enableFlags(Flags.FLAG_CROSS_APP_POLITE_NOTIFICATIONS);
+        mSetFlagsRule.enableFlags(Flags.FLAG_POLITE_NOTIFICATIONS_ATTN_UPDATE);
+        TestableFlagResolver flagResolver = new TestableFlagResolver();
+        flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME1, 50);
+        flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME2, 0);
+        initAttentionHelper(flagResolver);
+
+        // Trigger avalanche trigger intent
+        final Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
+        intent.putExtra("state", false);
+        mAvalancheBroadcastReceiver.onReceive(getContext(), intent);
+
+        // CATEGORY_ALARM is exempted
+        NotificationRecord r = getBeepyNotification();
+        r.getNotification().category = Notification.CATEGORY_ALARM;
+        // Should beep at 100% volume
+        mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+        verifyBeepVolume(1.0f);
+        assertNotEquals(-1, r.getLastAudiblyAlertedMs());
+
+        // CATEGORY_CAR_EMERGENCY is exempted
+        Mockito.reset(mRingtonePlayer);
+        NotificationRecord r2 = getBeepyNotification();
+        r2.getNotification().category = Notification.CATEGORY_CAR_EMERGENCY;
+        // Should beep at 100% volume
+        mAttentionHelper.buzzBeepBlinkLocked(r2, DEFAULT_SIGNALS);
+        verifyBeepVolume(1.0f);
+        assertNotEquals(-1, r2.getLastAudiblyAlertedMs());
+
+        // CATEGORY_CAR_WARNING is exempted
+        Mockito.reset(mRingtonePlayer);
+        NotificationRecord r3 = getBeepyNotification();
+        r3.getNotification().category = Notification.CATEGORY_CAR_WARNING;
+        // Should beep at 100% volume
+        mAttentionHelper.buzzBeepBlinkLocked(r3, DEFAULT_SIGNALS);
+        verifyBeepVolume(1.0f);
+        assertNotEquals(-1, r3.getLastAudiblyAlertedMs());
+
+        verify(mAccessibilityService, times(3)).sendAccessibilityEvent(any(), anyInt());
+    }
+
+    @Test
     public void testBeepVolume_politeNotif_exemptEmergency() throws Exception {
         mSetFlagsRule.enableFlags(Flags.FLAG_POLITE_NOTIFICATIONS);
         mSetFlagsRule.disableFlags(Flags.FLAG_CROSS_APP_POLITE_NOTIFICATIONS);
@@ -2492,6 +2536,73 @@
     }
 
     @Test
+    public void testBeepVolume_politeNotif_exemptCategories() throws Exception {
+        mSetFlagsRule.enableFlags(Flags.FLAG_POLITE_NOTIFICATIONS);
+        mSetFlagsRule.disableFlags(Flags.FLAG_CROSS_APP_POLITE_NOTIFICATIONS);
+        mSetFlagsRule.enableFlags(Flags.FLAG_POLITE_NOTIFICATIONS_ATTN_UPDATE);
+        TestableFlagResolver flagResolver = new TestableFlagResolver();
+        flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME1, 50);
+        flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME2, 0);
+        // NOTIFICATION_COOLDOWN_ALL setting is enabled
+        Settings.System.putInt(getContext().getContentResolver(),
+                Settings.System.NOTIFICATION_COOLDOWN_ALL, 1);
+        initAttentionHelper(flagResolver);
+
+        // CATEGORY_ALARM is exempted
+        NotificationRecord r = getBeepyNotification();
+        r.getNotification().category = Notification.CATEGORY_ALARM;
+        mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+        Mockito.reset(mRingtonePlayer);
+
+        // update should beep at 100% volume
+        mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+        assertNotEquals(-1, r.getLastAudiblyAlertedMs());
+        verifyBeepVolume(1.0f);
+
+        // 2nd update should beep at 100% volume
+        Mockito.reset(mRingtonePlayer);
+        mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+        assertNotEquals(-1, r.getLastAudiblyAlertedMs());
+        verifyBeepVolume(1.0f);
+
+        // CATEGORY_CAR_WARNING is exempted
+        r = getBeepyNotification();
+        r.getNotification().category = Notification.CATEGORY_CAR_WARNING;
+        mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+        Mockito.reset(mRingtonePlayer);
+
+        // update should beep at 100% volume
+        mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+        assertNotEquals(-1, r.getLastAudiblyAlertedMs());
+        verifyBeepVolume(1.0f);
+
+        // 2nd update should beep at 100% volume
+        Mockito.reset(mRingtonePlayer);
+        mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+        assertNotEquals(-1, r.getLastAudiblyAlertedMs());
+        verifyBeepVolume(1.0f);
+
+        // CATEGORY_CAR_EMERGENCY is exempted
+        r = getBeepyNotification();
+        r.getNotification().category = Notification.CATEGORY_CAR_EMERGENCY;
+        mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+        Mockito.reset(mRingtonePlayer);
+
+        // update should beep at 100% volume
+        mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+        assertNotEquals(-1, r.getLastAudiblyAlertedMs());
+        verifyBeepVolume(1.0f);
+
+        // 2nd update should beep at 100% volume
+        Mockito.reset(mRingtonePlayer);
+        mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+        assertNotEquals(-1, r.getLastAudiblyAlertedMs());
+        verifyBeepVolume(1.0f);
+
+        verify(mAccessibilityService, times(9)).sendAccessibilityEvent(any(), anyInt());
+    }
+
+    @Test
     public void testBeepVolume_politeNotif_applyPerApp() throws Exception {
         mSetFlagsRule.enableFlags(Flags.FLAG_POLITE_NOTIFICATIONS);
         mSetFlagsRule.disableFlags(Flags.FLAG_CROSS_APP_POLITE_NOTIFICATIONS);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index c1f5a01..1b999e4 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -3105,6 +3105,29 @@
     }
 
     @Test
+    @EnableFlags(android.app.Flags.FLAG_LIFETIME_EXTENSION_REFACTOR)
+    public void testLifetimeExtendedCancelledOnClick() throws Exception {
+        // Adds a lifetime extended notification.
+        final NotificationRecord notif = generateNotificationRecord(mTestNotificationChannel, 1,
+                null, false);
+        notif.getSbn().getNotification().flags =
+                Notification.FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY;
+        mService.addNotification(notif);
+        // Verify that the notification is posted and active.
+        assertThat(mBinderService.getActiveNotifications(mPkg).length).isEqualTo(1);
+
+        // Click the notification.
+        final NotificationVisibility nv = NotificationVisibility.obtain(notif.getKey(), 1, 2, true);
+        mService.mNotificationDelegate.onNotificationClick(mUid, Binder.getCallingPid(),
+                notif.getKey(), nv);
+        waitForIdle();
+
+        // The notification has been cancelled.
+        StatusBarNotification[] notifs = mBinderService.getActiveNotifications(mPkg);
+        assertThat(notifs.length).isEqualTo(0);
+    }
+
+    @Test
     public void testCancelNotificationWithTag_fromApp_cannotCancelFgsChild()
             throws Exception {
         when(mAmi.applyForegroundServiceNotification(
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/SystemZenRulesTest.java b/services/tests/uiservicestests/src/com/android/server/notification/SystemZenRulesTest.java
index 7aa208b..5de323b 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/SystemZenRulesTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/SystemZenRulesTest.java
@@ -207,4 +207,28 @@
         assertThat(getTriggerDescriptionForScheduleTime(mContext, scheduleInfo))
                 .isEqualTo("Mon,Wed,Fri-Sat,10:00 AM-4:00 PM");
     }
+
+    @Test
+    public void getShortDaysSummary_onlyDays() {
+        ScheduleInfo scheduleInfo = new ScheduleInfo();
+        scheduleInfo.startHour = 10;
+        scheduleInfo.endHour = 16;
+        scheduleInfo.days = new int[] {Calendar.MONDAY, Calendar.TUESDAY,
+                Calendar.WEDNESDAY, Calendar.THURSDAY, Calendar.FRIDAY};
+
+        assertThat(SystemZenRules.getShortDaysSummary(mContext, scheduleInfo))
+                .isEqualTo("Mon-Fri");
+    }
+
+    @Test
+    public void getTimeSummary_onlyTime() {
+        ScheduleInfo scheduleInfo = new ScheduleInfo();
+        scheduleInfo.startHour = 11;
+        scheduleInfo.endHour = 15;
+        scheduleInfo.days = new int[] {Calendar.MONDAY, Calendar.TUESDAY,
+                Calendar.WEDNESDAY, Calendar.THURSDAY, Calendar.FRIDAY};
+
+        assertThat(SystemZenRules.getTimeSummary(mContext, scheduleInfo))
+                .isEqualTo("11:00 AM-3:00 PM");
+    }
 }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
index f7340ab..3c3c2f3 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
@@ -19,12 +19,18 @@
 import static android.app.AutomaticZenRule.TYPE_BEDTIME;
 import static android.app.Flags.FLAG_MODES_UI;
 import static android.app.Flags.modesUi;
+import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT;
+import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT;
+import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS;
+import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
+import static android.app.NotificationManager.Policy.suppressedEffectsToString;
 import static android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
 import static android.provider.Settings.Global.ZEN_MODE_OFF;
 import static android.service.notification.Condition.SOURCE_UNKNOWN;
 import static android.service.notification.Condition.SOURCE_USER_ACTION;
 import static android.service.notification.Condition.STATE_FALSE;
 import static android.service.notification.Condition.STATE_TRUE;
+import static android.service.notification.NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_ON;
 import static android.service.notification.ZenPolicy.CONVERSATION_SENDERS_IMPORTANT;
 import static android.service.notification.ZenPolicy.CONVERSATION_SENDERS_NONE;
 import static android.service.notification.ZenPolicy.PEOPLE_TYPE_ANYONE;
@@ -219,8 +225,8 @@
         priorityCategories |= Policy.PRIORITY_CATEGORY_REMINDERS;
         priorityCategories |= Policy.PRIORITY_CATEGORY_EVENTS;
         priorityCategories |= Policy.PRIORITY_CATEGORY_CONVERSATIONS;
-        suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_LIGHTS;
-        suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_AMBIENT;
+        suppressedVisualEffects |= SUPPRESSED_EFFECT_LIGHTS;
+        suppressedVisualEffects |= SUPPRESSED_EFFECT_AMBIENT;
 
         Policy expectedPolicy = new Policy(priorityCategories, priorityCallSenders,
                 priorityMessageSenders, suppressedVisualEffects, 0, priorityConversationsSenders);
@@ -256,8 +262,8 @@
         priorityCategories |= Policy.PRIORITY_CATEGORY_REMINDERS;
         priorityCategories |= Policy.PRIORITY_CATEGORY_EVENTS;
         priorityCategories |= Policy.PRIORITY_CATEGORY_CONVERSATIONS;
-        suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_LIGHTS;
-        suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_AMBIENT;
+        suppressedVisualEffects |= SUPPRESSED_EFFECT_LIGHTS;
+        suppressedVisualEffects |= SUPPRESSED_EFFECT_AMBIENT;
 
         Policy expectedPolicy = new Policy(priorityCategories, priorityCallSenders,
                 priorityMessageSenders, suppressedVisualEffects,
@@ -309,8 +315,8 @@
             config.setAllowMessagesFrom(Policy.PRIORITY_SENDERS_STARRED);
             config.setAllowConversationsFrom(CONVERSATION_SENDERS_NONE);
             config.setSuppressedVisualEffects(config.getSuppressedVisualEffects()
-                    | Policy.SUPPRESSED_EFFECT_BADGE | Policy.SUPPRESSED_EFFECT_LIGHTS
-                    | Policy.SUPPRESSED_EFFECT_AMBIENT);
+                    | Policy.SUPPRESSED_EFFECT_BADGE | SUPPRESSED_EFFECT_LIGHTS
+                    | SUPPRESSED_EFFECT_AMBIENT);
         }
         ZenPolicy actual = config.getZenPolicy();
 
@@ -357,8 +363,8 @@
             config.setAllowConversationsFrom(CONVERSATION_SENDERS_NONE);
             config.setAllowPriorityChannels(false);
             config.setSuppressedVisualEffects(config.getSuppressedVisualEffects()
-                    | Policy.SUPPRESSED_EFFECT_BADGE | Policy.SUPPRESSED_EFFECT_LIGHTS
-                    | Policy.SUPPRESSED_EFFECT_AMBIENT);
+                    | Policy.SUPPRESSED_EFFECT_BADGE | SUPPRESSED_EFFECT_LIGHTS
+                    | SUPPRESSED_EFFECT_AMBIENT);
         }
         ZenPolicy actual = config.getZenPolicy();
 
@@ -1063,6 +1069,43 @@
                 .isEqualTo("name");
     }
 
+    @Test
+    public void toNotificationPolicy_withNewSuppressedEffects_returnsSuppressedEffects() {
+        ZenModeConfig config = getCustomConfig();
+        // From LegacyNotificationManagerTest.testSetNotificationPolicy_preP_setNewFields
+        // When a pre-P app sets SUPPRESSED_EFFECT_NOTIFICATION_LIST, it's converted by NMS into:
+        Policy policy = new Policy(0, 0, 0,
+                SUPPRESSED_EFFECT_FULL_SCREEN_INTENT | SUPPRESSED_EFFECT_LIGHTS
+                        | SUPPRESSED_EFFECT_PEEK | SUPPRESSED_EFFECT_AMBIENT);
+
+        config.applyNotificationPolicy(policy);
+        Policy result = config.toNotificationPolicy();
+
+        assertThat(suppressedEffectsOf(result)).isEqualTo(suppressedEffectsOf(policy));
+    }
+
+    @Test
+    public void toNotificationPolicy_withOldAndNewSuppressedEffects_returnsSuppressedEffects() {
+        ZenModeConfig config = getCustomConfig();
+        // From LegacyNotificationManagerTest.testSetNotificationPolicy_preP_setOldNewFields.
+        // When a pre-P app sets SUPPRESSED_EFFECT_SCREEN_ON | SUPPRESSED_EFFECT_STATUS_BAR, it's
+        // converted by NMS into:
+        Policy policy = new Policy(0, 0, 0,
+                SUPPRESSED_EFFECT_SCREEN_ON | SUPPRESSED_EFFECT_FULL_SCREEN_INTENT
+                        | SUPPRESSED_EFFECT_LIGHTS | SUPPRESSED_EFFECT_PEEK
+                        | SUPPRESSED_EFFECT_AMBIENT);
+
+        config.applyNotificationPolicy(policy);
+        Policy result = config.toNotificationPolicy();
+
+        assertThat(suppressedEffectsOf(result)).isEqualTo(suppressedEffectsOf(policy));
+    }
+
+    private static String suppressedEffectsOf(Policy policy) {
+        return suppressedEffectsToString(policy.suppressedVisualEffects) + "("
+                + policy.suppressedVisualEffects + ")";
+    }
+
     private ZenModeConfig getMutedRingerConfig() {
         ZenModeConfig config = new ZenModeConfig();
 
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java
index 57587f7..9af0021 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java
@@ -61,6 +61,7 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashSet;
+import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Optional;
 import java.util.Set;
@@ -80,19 +81,6 @@
                     ? Set.of("version", "manualRule", "automaticRules", "deletedRules")
                     : Set.of("version", "manualRule", "automaticRules");
 
-    // Differences for flagged fields are only generated if the flag is enabled.
-    // "Metadata" fields (userModifiedFields, deletionInstant, disabledOrigin) are not compared.
-    private static final Set<String> ZEN_RULE_EXEMPT_FIELDS =
-            android.app.Flags.modesApi()
-                    ? Set.of("userModifiedFields", "zenPolicyUserModifiedFields",
-                            "zenDeviceEffectsUserModifiedFields", "deletionInstant",
-                            "disabledOrigin")
-                    : Set.of(RuleDiff.FIELD_TYPE, RuleDiff.FIELD_TRIGGER_DESCRIPTION,
-                            RuleDiff.FIELD_ICON_RES, RuleDiff.FIELD_ALLOW_MANUAL,
-                            RuleDiff.FIELD_ZEN_DEVICE_EFFECTS, "userModifiedFields",
-                            "zenPolicyUserModifiedFields", "zenDeviceEffectsUserModifiedFields",
-                            "deletionInstant", "disabledOrigin");
-
     // allowPriorityChannels is flagged by android.app.modes_api
     public static final Set<String> ZEN_MODE_CONFIG_FLAGGED_FIELDS =
             Set.of("allowPriorityChannels");
@@ -102,8 +90,7 @@
 
     @Parameters(name = "{0}")
     public static List<FlagsParameterization> getParams() {
-        return FlagsParameterization.allCombinationsOf(
-                FLAG_MODES_UI);
+        return FlagsParameterization.progressionOf(FLAG_MODES_API, FLAG_MODES_UI);
     }
 
     public ZenModeDiffTest(FlagsParameterization flags) {
@@ -140,7 +127,7 @@
         ArrayMap<String, Object> expectedFrom = new ArrayMap<>();
         ArrayMap<String, Object> expectedTo = new ArrayMap<>();
         List<Field> fieldsForDiff = getFieldsForDiffCheck(
-                ZenModeConfig.ZenRule.class, ZEN_RULE_EXEMPT_FIELDS);
+                ZenModeConfig.ZenRule.class, getZenRuleExemptFields());
         generateFieldDiffs(r1, r2, fieldsForDiff, expectedFrom, expectedTo);
 
         ZenModeDiff.RuleDiff d = new ZenModeDiff.RuleDiff(r1, r2);
@@ -158,6 +145,25 @@
         }
     }
 
+    private static Set<String> getZenRuleExemptFields() {
+        // "Metadata" fields are never compared.
+        Set<String> exemptFields = new LinkedHashSet<>(
+                Set.of("userModifiedFields", "zenPolicyUserModifiedFields",
+                        "zenDeviceEffectsUserModifiedFields", "deletionInstant", "disabledOrigin"));
+        // Flagged fields are only compared if their flag is on.
+        if (!Flags.modesApi()) {
+            exemptFields.addAll(
+                    Set.of(RuleDiff.FIELD_TYPE, RuleDiff.FIELD_TRIGGER_DESCRIPTION,
+                            RuleDiff.FIELD_ICON_RES, RuleDiff.FIELD_ALLOW_MANUAL,
+                            RuleDiff.FIELD_ZEN_DEVICE_EFFECTS,
+                            RuleDiff.FIELD_LEGACY_SUPPRESSED_EFFECTS));
+        }
+        if (!(Flags.modesApi() && Flags.modesUi())) {
+            exemptFields.add(RuleDiff.FIELD_LEGACY_SUPPRESSED_EFFECTS);
+        }
+        return exemptFields;
+    }
+
     @Test
     public void testConfigDiff_addRemoveSame() {
         // Default config, will test add, remove, and no change
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 b07940a..d7bae45 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -1044,6 +1044,8 @@
         AudioManagerInternal mAudioManager = mock(AudioManagerInternal.class);
         mZenModeHelper.mAudioManager = mAudioManager;
         setupZenConfig();
+        mTestableLooper.processAllMessages();
+        reset(mAudioManager);
 
         // Turn manual zen mode on
         mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, UPDATE_ORIGIN_APP,
@@ -1063,6 +1065,44 @@
     }
 
     @Test
+    public void testSetConfig_updatesAudioForSequentialChangesToZenMode() {
+        AudioManagerInternal mAudioManager = mock(AudioManagerInternal.class);
+        mZenModeHelper.mAudioManager = mAudioManager;
+        setupZenConfig();
+        mTestableLooper.processAllMessages();
+        reset(mAudioManager);
+
+        // Turn manual zen mode on
+        mZenModeHelper.setManualZenMode(
+                ZEN_MODE_IMPORTANT_INTERRUPTIONS,
+                null,
+                UPDATE_ORIGIN_APP,
+                null,
+                "test",
+                CUSTOM_PKG_UID);
+        mZenModeHelper.setManualZenMode(
+                ZEN_MODE_IMPORTANT_INTERRUPTIONS,
+                null,
+                UPDATE_ORIGIN_APP,
+                null,
+                "test",
+                CUSTOM_PKG_UID);
+
+        // audio manager shouldn't do anything until the handler processes its messages
+        verify(mAudioManager, never()).updateRingerModeAffectedStreamsInternal();
+
+        // now process the looper's messages
+        mTestableLooper.processAllMessages();
+
+        // Expect calls to audio manager
+        verify(mAudioManager, times(2)).updateRingerModeAffectedStreamsInternal();
+        verify(mAudioManager, times(1)).setRingerModeInternal(anyInt(), anyString());
+
+        // called during applyZenToRingerMode(), which should be true since zen changed
+        verify(mAudioManager, atLeastOnce()).getRingerModeInternal();
+    }
+
+    @Test
     public void testParcelConfig() {
         mZenModeHelper.setNotificationPolicy(new Policy(PRIORITY_CATEGORY_EVENTS
                         | PRIORITY_CATEGORY_MESSAGES | PRIORITY_CATEGORY_REPEAT_CALLERS
diff --git a/services/tests/vibrator/Android.bp b/services/tests/vibrator/Android.bp
index da21cd3..757bcd8 100644
--- a/services/tests/vibrator/Android.bp
+++ b/services/tests/vibrator/Android.bp
@@ -16,7 +16,7 @@
     ],
 
     libs: [
-        "android.hardware.vibrator-V2-java",
+        "android.hardware.vibrator-V3-java",
         "android.test.mock",
         "android.test.base",
         "android.test.runner",
@@ -36,7 +36,6 @@
         "platform-test-annotations",
         "service-permission.stubs.system_server",
         "services.core",
-        "flag-junit",
     ],
     jni_libs: ["libdexmakerjvmtiagent"],
     platform_apis: true,
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/DeviceAdapterTest.java b/services/tests/vibrator/src/com/android/server/vibrator/DeviceAdapterTest.java
index 3013ed0..59d5577 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/DeviceAdapterTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/DeviceAdapterTest.java
@@ -25,6 +25,7 @@
 import android.hardware.vibrator.IVibrator;
 import android.os.CombinedVibration;
 import android.os.Handler;
+import android.os.PersistableBundle;
 import android.os.VibrationEffect;
 import android.os.test.TestLooper;
 import android.os.vibrator.PrebakedSegment;
@@ -32,6 +33,7 @@
 import android.os.vibrator.RampSegment;
 import android.os.vibrator.StepSegment;
 import android.os.vibrator.VibrationEffectSegment;
+import android.platform.test.annotations.RequiresFlagsEnabled;
 import android.util.SparseArray;
 
 import androidx.test.InstrumentationRegistry;
@@ -103,6 +105,17 @@
     }
 
     @Test
+    @RequiresFlagsEnabled(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
+    public void testVendorEffect_returnsOriginalSegment() {
+        PersistableBundle vendorData = new PersistableBundle();
+        vendorData.putInt("key", 1);
+        VibrationEffect effect = VibrationEffect.createVendorEffect(vendorData);
+
+        assertThat(mAdapter.adaptToVibrator(EMPTY_VIBRATOR_ID, effect)).isEqualTo(effect);
+        assertThat(mAdapter.adaptToVibrator(PWLE_VIBRATOR_ID, effect)).isEqualTo(effect);
+    }
+
+    @Test
     public void testStepAndRampSegments_withoutPwleCapability_convertsRampsToSteps() {
         VibrationEffect.Composed effect = new VibrationEffect.Composed(Arrays.asList(
                 // Step(amplitude, frequencyHz, duration)
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/HapticFeedbackVibrationProviderTest.java b/services/tests/vibrator/src/com/android/server/vibrator/HapticFeedbackVibrationProviderTest.java
index 901c036..4f75931 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/HapticFeedbackVibrationProviderTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/HapticFeedbackVibrationProviderTest.java
@@ -20,7 +20,7 @@
 import static android.os.VibrationAttributes.CATEGORY_UNKNOWN;
 import static android.os.VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY;
 import static android.os.VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF;
-import static android.os.VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_SCALE;
+import static android.os.VibrationAttributes.USAGE_IME_FEEDBACK;
 import static android.os.VibrationAttributes.USAGE_TOUCH;
 import static android.os.VibrationEffect.Composition.PRIMITIVE_CLICK;
 import static android.os.VibrationEffect.Composition.PRIMITIVE_TICK;
@@ -346,7 +346,7 @@
     }
 
     @Test
-    public void testVibrationAttribute_keyboardCategoryOff_isIme_notUseKeyboardCategory() {
+    public void testVibrationAttribute_keyboardCategoryOff_isIme_useTouchUsage() {
         mSetFlagsRule.disableFlags(Flags.FLAG_KEYBOARD_CATEGORY_ENABLED);
         HapticFeedbackVibrationProvider hapticProvider = createProviderWithDefaultCustomizations();
 
@@ -362,7 +362,7 @@
     }
 
     @Test
-    public void testVibrationAttribute_keyboardCategoryOn_notIme_notUseKeyboardCategory() {
+    public void testVibrationAttribute_keyboardCategoryOn_notIme_useTouchUsage() {
         mSetFlagsRule.enableFlags(Flags.FLAG_KEYBOARD_CATEGORY_ENABLED);
         HapticFeedbackVibrationProvider hapticProvider = createProviderWithDefaultCustomizations();
 
@@ -377,7 +377,7 @@
     }
 
     @Test
-    public void testVibrationAttribute_keyboardCategoryOn_isIme_useKeyboardCategory() {
+    public void testVibrationAttribute_keyboardCategoryOn_isIme_useImeFeedbackUsage() {
         mSetFlagsRule.enableFlags(Flags.FLAG_KEYBOARD_CATEGORY_ENABLED);
         HapticFeedbackVibrationProvider hapticProvider = createProviderWithDefaultCustomizations();
 
@@ -385,64 +385,14 @@
             VibrationAttributes attrs = hapticProvider.getVibrationAttributesForHapticFeedback(
                     effectId, /* flags */ 0,
                     HapticFeedbackConstants.PRIVATE_FLAG_APPLY_INPUT_METHOD_SETTINGS);
-            assertWithMessage("Expected USAGE_TOUCH for effect " + effectId)
-                    .that(attrs.getUsage()).isEqualTo(USAGE_TOUCH);
+            assertWithMessage("Expected USAGE_IME_FEEDBACK for effect " + effectId)
+                    .that(attrs.getUsage()).isEqualTo(USAGE_IME_FEEDBACK);
             assertWithMessage("Expected CATEGORY_KEYBOARD for effect " + effectId)
                     .that(attrs.getCategory()).isEqualTo(CATEGORY_KEYBOARD);
         }
     }
 
     @Test
-    public void testVibrationAttribute_noFixAmplitude_notBypassIntensityScale() {
-        mSetFlagsRule.enableFlags(Flags.FLAG_KEYBOARD_CATEGORY_ENABLED);
-        mockVibratorPrimitiveSupport(PRIMITIVE_CLICK, PRIMITIVE_TICK);
-        mockKeyboardVibrationFixedAmplitude(-1);
-        HapticFeedbackVibrationProvider hapticProvider = createProviderWithDefaultCustomizations();
-
-        for (int effectId : KEYBOARD_FEEDBACK_CONSTANTS) {
-            VibrationAttributes attrs = hapticProvider.getVibrationAttributesForHapticFeedback(
-                    effectId, /* flags */ 0,
-                    HapticFeedbackConstants.PRIVATE_FLAG_APPLY_INPUT_METHOD_SETTINGS);
-            assertWithMessage("Expected no FLAG_BYPASS_USER_VIBRATION_INTENSITY_SCALE for effect "
-                    + effectId)
-                    .that(attrs.isFlagSet(FLAG_BYPASS_USER_VIBRATION_INTENSITY_SCALE)).isFalse();
-        }
-    }
-
-    @Test
-    public void testVibrationAttribute_notIme_notBypassIntensityScale() {
-        mSetFlagsRule.enableFlags(Flags.FLAG_KEYBOARD_CATEGORY_ENABLED);
-        mockVibratorPrimitiveSupport(PRIMITIVE_CLICK, PRIMITIVE_TICK);
-        mockKeyboardVibrationFixedAmplitude(KEYBOARD_VIBRATION_FIXED_AMPLITUDE);
-        HapticFeedbackVibrationProvider hapticProvider = createProviderWithDefaultCustomizations();
-
-        for (int effectId : KEYBOARD_FEEDBACK_CONSTANTS) {
-            VibrationAttributes attrs = hapticProvider.getVibrationAttributesForHapticFeedback(
-                    effectId, /* flags */ 0, /* privFlags */ 0);
-            assertWithMessage("Expected no FLAG_BYPASS_USER_VIBRATION_INTENSITY_SCALE for effect "
-                    + effectId)
-                    .that(attrs.isFlagSet(FLAG_BYPASS_USER_VIBRATION_INTENSITY_SCALE)).isFalse();
-        }
-    }
-
-    @Test
-    public void testVibrationAttribute_fixAmplitude_isIme_bypassIntensityScale() {
-        mSetFlagsRule.enableFlags(Flags.FLAG_KEYBOARD_CATEGORY_ENABLED);
-        mockVibratorPrimitiveSupport(PRIMITIVE_CLICK, PRIMITIVE_TICK);
-        mockKeyboardVibrationFixedAmplitude(KEYBOARD_VIBRATION_FIXED_AMPLITUDE);
-        HapticFeedbackVibrationProvider hapticProvider = createProviderWithDefaultCustomizations();
-
-        for (int effectId : KEYBOARD_FEEDBACK_CONSTANTS) {
-            VibrationAttributes attrs = hapticProvider.getVibrationAttributesForHapticFeedback(
-                    effectId, /* flags */ 0,
-                    HapticFeedbackConstants.PRIVATE_FLAG_APPLY_INPUT_METHOD_SETTINGS);
-            assertWithMessage("Expected FLAG_BYPASS_USER_VIBRATION_INTENSITY_SCALE for effect "
-                    + effectId)
-                    .that(attrs.isFlagSet(FLAG_BYPASS_USER_VIBRATION_INTENSITY_SCALE)).isTrue();
-        }
-    }
-
-    @Test
     public void testIsRestricted_biometricConstants_returnsTrue() {
         HapticFeedbackVibrationProvider hapticProvider = createProviderWithDefaultCustomizations();
 
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibrationScalerTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibrationScalerTest.java
index b264435..9ebeaa8 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibrationScalerTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibrationScalerTest.java
@@ -37,6 +37,7 @@
 import android.content.pm.PackageManagerInternal;
 import android.os.ExternalVibrationScale;
 import android.os.Handler;
+import android.os.PersistableBundle;
 import android.os.PowerManagerInternal;
 import android.os.UserHandle;
 import android.os.VibrationAttributes;
@@ -232,6 +233,34 @@
     }
 
     @Test
+    @RequiresFlagsEnabled(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
+    public void scale_withVendorEffect_setsEffectStrengthBasedOnSettings() {
+        setDefaultIntensity(USAGE_NOTIFICATION, VIBRATION_INTENSITY_LOW);
+        setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, VIBRATION_INTENSITY_HIGH);
+        PersistableBundle vendorData = new PersistableBundle();
+        vendorData.putString("key", "value");
+        VibrationEffect effect = VibrationEffect.createVendorEffect(vendorData);
+
+        VibrationEffect.VendorEffect scaled =
+                (VibrationEffect.VendorEffect) mVibrationScaler.scale(effect, USAGE_NOTIFICATION);
+        assertEquals(scaled.getEffectStrength(), VibrationEffect.EFFECT_STRENGTH_STRONG);
+
+        setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
+                VIBRATION_INTENSITY_MEDIUM);
+        scaled = (VibrationEffect.VendorEffect) mVibrationScaler.scale(effect, USAGE_NOTIFICATION);
+        assertEquals(scaled.getEffectStrength(), VibrationEffect.EFFECT_STRENGTH_MEDIUM);
+
+        setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, VIBRATION_INTENSITY_LOW);
+        scaled = (VibrationEffect.VendorEffect) mVibrationScaler.scale(effect, USAGE_NOTIFICATION);
+        assertEquals(scaled.getEffectStrength(), VibrationEffect.EFFECT_STRENGTH_LIGHT);
+
+        setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, VIBRATION_INTENSITY_OFF);
+        scaled = (VibrationEffect.VendorEffect) mVibrationScaler.scale(effect, USAGE_NOTIFICATION);
+        // Vibration setting being bypassed will use default setting.
+        assertEquals(scaled.getEffectStrength(), VibrationEffect.EFFECT_STRENGTH_LIGHT);
+    }
+
+    @Test
     public void scale_withOneShotAndWaveform_resolvesAmplitude() {
         // No scale, default amplitude still resolved
         setDefaultIntensity(USAGE_RINGTONE, VIBRATION_INTENSITY_LOW);
@@ -365,6 +394,30 @@
         assertTrue(scaled.getAmplitude() > 0.5);
     }
 
+    @Test
+    @RequiresFlagsEnabled({
+            android.os.vibrator.Flags.FLAG_ADAPTIVE_HAPTICS_ENABLED,
+            android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS,
+    })
+    public void scale_adaptiveHapticsOnVendorEffect_setsLinearScaleParameter() {
+        setDefaultIntensity(USAGE_RINGTONE, VIBRATION_INTENSITY_HIGH);
+
+        mVibrationScaler.updateAdaptiveHapticsScale(USAGE_RINGTONE, 0.5f);
+
+        PersistableBundle vendorData = new PersistableBundle();
+        vendorData.putInt("key", 1);
+        VibrationEffect effect = VibrationEffect.createVendorEffect(vendorData);
+
+        VibrationEffect.VendorEffect scaled =
+                (VibrationEffect.VendorEffect) mVibrationScaler.scale(effect, USAGE_RINGTONE);
+        assertEquals(scaled.getLinearScale(), 0.5f);
+
+        mVibrationScaler.removeAdaptiveHapticsScale(USAGE_RINGTONE);
+
+        scaled = (VibrationEffect.VendorEffect) mVibrationScaler.scale(effect, USAGE_RINGTONE);
+        assertEquals(scaled.getLinearScale(), 1.0f);
+    }
+
     private void setDefaultIntensity(@VibrationAttributes.Usage int usage,
             @Vibrator.VibrationIntensity int intensity) {
         when(mVibrationConfigMock.getDefaultVibrationIntensity(eq(usage))).thenReturn(intensity);
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibrationSettingsTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibrationSettingsTest.java
index 60d8964..8d4a6aa 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibrationSettingsTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibrationSettingsTest.java
@@ -23,6 +23,7 @@
 import static android.os.VibrationAttributes.USAGE_ALARM;
 import static android.os.VibrationAttributes.USAGE_COMMUNICATION_REQUEST;
 import static android.os.VibrationAttributes.USAGE_HARDWARE_FEEDBACK;
+import static android.os.VibrationAttributes.USAGE_IME_FEEDBACK;
 import static android.os.VibrationAttributes.USAGE_MEDIA;
 import static android.os.VibrationAttributes.USAGE_NOTIFICATION;
 import static android.os.VibrationAttributes.USAGE_PHYSICAL_EMULATION;
@@ -893,6 +894,22 @@
     }
 
     @Test
+    public void getCurrentIntensity_ImeFeedbackValueReflectsToKeyboardVibrationSettings() {
+        setDefaultIntensity(USAGE_IME_FEEDBACK, VIBRATION_INTENSITY_MEDIUM);
+        setDefaultIntensity(USAGE_TOUCH, VIBRATION_INTENSITY_HIGH);
+
+        setKeyboardVibrationSettingsSupported(false);
+        mVibrationSettings.update();
+        assertEquals(VIBRATION_INTENSITY_HIGH,
+                mVibrationSettings.getCurrentIntensity(USAGE_IME_FEEDBACK));
+
+        setKeyboardVibrationSettingsSupported(true);
+        mVibrationSettings.update();
+        assertEquals(VIBRATION_INTENSITY_MEDIUM,
+                mVibrationSettings.getCurrentIntensity(USAGE_IME_FEEDBACK));
+    }
+
+    @Test
     public void getFallbackEffect_returnsEffectsFromSettings() {
         assertNotNull(mVibrationSettings.getFallbackEffect(VibrationEffect.EFFECT_TICK));
         assertNotNull(mVibrationSettings.getFallbackEffect(VibrationEffect.EFFECT_TEXTURE_TICK));
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java
index 9dac23f..3bd56de 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java
@@ -48,6 +48,7 @@
 import android.os.CombinedVibration;
 import android.os.Handler;
 import android.os.IBinder;
+import android.os.PersistableBundle;
 import android.os.PowerManager;
 import android.os.Process;
 import android.os.SystemClock;
@@ -560,8 +561,37 @@
         // fail at waitForCompletion(vibrationThread) if the vibration not cancelled immediately.
         Thread cancellingThread =
                 new Thread(() -> mVibrationConductor.notifyCancelled(
-                        new Vibration.EndInfo(
-                                Vibration.Status.CANCELLED_BY_SETTINGS_UPDATE),
+                        new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_SETTINGS_UPDATE),
+                        /* immediate= */ false));
+        cancellingThread.start();
+
+        waitForCompletion(/* timeout= */ 50);
+        cancellingThread.join();
+
+        verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED_BY_SETTINGS_UPDATE);
+        assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
+    }
+
+    @Test
+    @RequiresFlagsEnabled(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
+    public void vibrate_singleVibratorVendorEffectCancel_cancelsVibrationImmediately()
+            throws Exception {
+        mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_PERFORM_VENDOR_EFFECTS);
+        // Set long vendor effect duration to check it gets cancelled quickly.
+        mVibratorProviders.get(VIBRATOR_ID).setVendorEffectDuration(10 * TEST_TIMEOUT_MILLIS);
+
+        VibrationEffect effect = VibrationEffect.createVendorEffect(createTestVendorData());
+        long vibrationId = startThreadAndDispatcher(effect);
+
+        assertTrue(waitUntil(() -> mControllers.get(VIBRATOR_ID).isVibrating(),
+                TEST_TIMEOUT_MILLIS));
+        assertTrue(mThread.isRunningVibrationId(vibrationId));
+
+        // Run cancel in a separate thread so if VibrationThread.cancel blocks then this test should
+        // fail at waitForCompletion(vibrationThread) if the vibration not cancelled immediately.
+        Thread cancellingThread =
+                new Thread(() -> mVibrationConductor.notifyCancelled(
+                        new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_SETTINGS_UPDATE),
                         /* immediate= */ false));
         cancellingThread.start();
 
@@ -588,8 +618,7 @@
         // fail at waitForCompletion(vibrationThread) if the vibration not cancelled immediately.
         Thread cancellingThread =
                 new Thread(() -> mVibrationConductor.notifyCancelled(
-                        new Vibration.EndInfo(
-                                Vibration.Status.CANCELLED_BY_SCREEN_OFF),
+                        new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_SCREEN_OFF),
                         /* immediate= */ false));
         cancellingThread.start();
 
@@ -654,6 +683,27 @@
     }
 
     @Test
+    @RequiresFlagsEnabled(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
+    public void vibrate_singleVibratorVendorEffect_runsVibration() {
+        mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_PERFORM_VENDOR_EFFECTS);
+
+        VibrationEffect effect = VibrationEffect.createVendorEffect(createTestVendorData());
+        long vibrationId = startThreadAndDispatcher(effect);
+        waitForCompletion();
+
+        verify(mManagerHooks).noteVibratorOn(eq(UID),
+                eq(PerformVendorEffectVibratorStep.VENDOR_EFFECT_MAX_DURATION_MS));
+        verify(mManagerHooks).noteVibratorOff(eq(UID));
+        verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
+        verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
+        assertThat(mControllers.get(VIBRATOR_ID).isVibrating()).isFalse();
+
+        assertThat(mVibratorProviders.get(VIBRATOR_ID).getVendorEffects(vibrationId))
+                .containsExactly(effect)
+                .inOrder();
+    }
+
+    @Test
     public void vibrate_singleVibratorComposed_runsVibration() {
         FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(VIBRATOR_ID);
         fakeVibrator.setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
@@ -1437,16 +1487,48 @@
                 .combine();
         long vibrationId = startThreadAndDispatcher(effect);
 
-        assertTrue(waitUntil(() -> mControllers.get(2).isVibrating(),
-                TEST_TIMEOUT_MILLIS));
+        assertTrue(waitUntil(() -> mControllers.get(2).isVibrating(), TEST_TIMEOUT_MILLIS));
         assertTrue(mThread.isRunningVibrationId(vibrationId));
 
         // Run cancel in a separate thread so if VibrationThread.cancel blocks then this test should
         // fail at waitForCompletion(vibrationThread) if the vibration not cancelled immediately.
         Thread cancellingThread = new Thread(
                 () -> mVibrationConductor.notifyCancelled(
-                        new Vibration.EndInfo(
-                                Vibration.Status.CANCELLED_BY_SCREEN_OFF),
+                        new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_SCREEN_OFF),
+                        /* immediate= */ false));
+        cancellingThread.start();
+
+        waitForCompletion(/* timeout= */ 50);
+        cancellingThread.join();
+
+        verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED_BY_SCREEN_OFF);
+        assertFalse(mControllers.get(1).isVibrating());
+        assertFalse(mControllers.get(2).isVibrating());
+    }
+
+    @Test
+    @RequiresFlagsEnabled(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
+    public void vibrate_multipleVendorEffectCancel_cancelsVibrationImmediately() throws Exception {
+        mockVibrators(1, 2);
+        mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_PERFORM_VENDOR_EFFECTS);
+        mVibratorProviders.get(1).setVendorEffectDuration(10 * TEST_TIMEOUT_MILLIS);
+        mVibratorProviders.get(2).setCapabilities(IVibrator.CAP_PERFORM_VENDOR_EFFECTS);
+        mVibratorProviders.get(2).setVendorEffectDuration(10 * TEST_TIMEOUT_MILLIS);
+
+        CombinedVibration effect = CombinedVibration.startParallel()
+                .addVibrator(1, VibrationEffect.createVendorEffect(createTestVendorData()))
+                .addVibrator(2, VibrationEffect.createVendorEffect(createTestVendorData()))
+                .combine();
+        long vibrationId = startThreadAndDispatcher(effect);
+
+        assertTrue(waitUntil(() -> mControllers.get(2).isVibrating(), TEST_TIMEOUT_MILLIS));
+        assertTrue(mThread.isRunningVibrationId(vibrationId));
+
+        // Run cancel in a separate thread so if VibrationThread.cancel blocks then this test should
+        // fail at waitForCompletion(vibrationThread) if the vibration not cancelled immediately.
+        Thread cancellingThread = new Thread(
+                () -> mVibrationConductor.notifyCancelled(
+                        new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_SCREEN_OFF),
                         /* immediate= */ false));
         cancellingThread.start();
 
@@ -1614,6 +1696,25 @@
     }
 
     @Test
+    @RequiresFlagsEnabled(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
+    public void vibrate_vendorEffectWithRampDown_doesNotAddRampDown() {
+        when(mVibrationConfigMock.getRampDownDurationMs()).thenReturn(15);
+        mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_PERFORM_VENDOR_EFFECTS);
+
+        VibrationEffect effect = VibrationEffect.createVendorEffect(createTestVendorData());
+        long vibrationId = startThreadAndDispatcher(effect);
+        waitForCompletion();
+
+        verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
+        verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
+
+        assertThat(mVibratorProviders.get(VIBRATOR_ID).getVendorEffects(vibrationId))
+                .containsExactly(effect)
+                .inOrder();
+        assertThat(mVibratorProviders.get(VIBRATOR_ID).getAmplitudes()).isEmpty();
+    }
+
+    @Test
     public void vibrate_composedWithRampDown_doesNotAddRampDown() {
         when(mVibrationConfigMock.getRampDownDurationMs()).thenReturn(15);
         mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL,
@@ -1746,10 +1847,6 @@
         assertTrue("Tested duration=" + duration4, duration4 < 2000);
 
         // Effect5: played normally after effect4, which may or may not have played.
-
-        verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId5));
-        verifyCallbacksTriggered(vibrationId5, Vibration.Status.FINISHED);
-
         assertEquals(Arrays.asList(expectedPrebaked(VibrationEffect.EFFECT_CLICK)),
                 fakeVibrator.getEffectSegments(vibrationId5));
     }
@@ -1835,6 +1932,16 @@
         return array;
     }
 
+    private static PersistableBundle createTestVendorData() {
+        PersistableBundle vendorData = new PersistableBundle();
+        vendorData.putInt("id", 1);
+        vendorData.putDouble("scale", 0.5);
+        vendorData.putBoolean("loop", false);
+        vendorData.putLongArray("amplitudes", new long[] { 0, 255, 128 });
+        vendorData.putString("label", "vibration");
+        return vendorData;
+    }
+
     private VibrationEffectSegment expectedOneShot(long millis) {
         return new StepSegment(VibrationEffect.DEFAULT_AMPLITUDE,
                 /* frequencyHz= */ 0, (int) millis);
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java
index 8ca8623..c496bbb 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java
@@ -45,6 +45,11 @@
 import android.os.IBinder;
 import android.os.Process;
 import android.os.test.TestLooper;
+import android.os.vibrator.Flags;
+import android.platform.test.annotations.RequiresFlagsDisabled;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
 import android.util.SparseArray;
 
 import androidx.test.InstrumentationRegistry;
@@ -63,7 +68,6 @@
 import java.util.Arrays;
 import java.util.List;
 import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.TimeUnit;
 
 public class VibratorControlServiceTest {
 
@@ -71,6 +75,8 @@
 
     @Rule
     public MockitoRule rule = MockitoJUnit.rule();
+    @Rule
+    public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
 
     @Mock private VibrationScaler mMockVibrationScaler;
     @Mock private PackageManagerInternal mPackageManagerInternalMock;
@@ -98,6 +104,7 @@
         mVibratorControlService = new VibratorControlService(
                 InstrumentationRegistry.getContext(), new VibratorControllerHolder(),
                 mMockVibrationScaler, mVibrationSettings, mStatsLoggerMock, mLock);
+        mFakeVibratorController.setVibratorControlService(mVibratorControlService);
     }
 
     @Test
@@ -280,10 +287,10 @@
         CompletableFuture<Void> future =
                 mVibratorControlService.triggerVibrationParamsRequest(UID, USAGE_RINGTONE,
                         timeoutInMillis);
-        try {
-            future.orTimeout(timeoutInMillis, TimeUnit.MILLISECONDS).get();
-        } catch (Throwable ignored) {
-        }
+        mTestLooper.dispatchAll();
+
+        assertThat(future).isNotNull();
+        assertThat(future.isDone()).isTrue();
         assertThat(mFakeVibratorController.didRequestVibrationParams).isTrue();
         assertThat(mFakeVibratorController.requestVibrationType).isEqualTo(
                 ScaleParam.TYPE_RINGTONE);
@@ -315,6 +322,46 @@
         }
     }
 
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_THROTTLE_VIBRATION_PARAMS_REQUESTS)
+    public void testRequestVibrationParams_withOngoingRequestAndSameUsage_returnOngoingFuture() {
+        int timeoutInMillis = 10;
+        mVibratorControlService.registerVibratorController(mFakeVibratorController);
+        CompletableFuture<Void> future =
+                mVibratorControlService.triggerVibrationParamsRequest(UID, USAGE_RINGTONE,
+                        timeoutInMillis);
+        CompletableFuture<Void> future2 =
+                mVibratorControlService.triggerVibrationParamsRequest(UID, USAGE_RINGTONE,
+                        timeoutInMillis);
+        mTestLooper.dispatchAll();
+
+        assertThat(future).isNotNull();
+        assertThat(future).isEqualTo(future2);
+        assertThat(future.isDone()).isTrue();
+        assertThat(mFakeVibratorController.requestVibrationParamsCounter).isEqualTo(1);
+    }
+
+    @Test
+    @RequiresFlagsDisabled(Flags.FLAG_THROTTLE_VIBRATION_PARAMS_REQUESTS)
+    public void testRequestVibrationParams_withOngoingRequestAndSameUsage_returnNewFuture() {
+        int timeoutInMillis = 10;
+        mVibratorControlService.registerVibratorController(mFakeVibratorController);
+        CompletableFuture<Void> future =
+                mVibratorControlService.triggerVibrationParamsRequest(UID, USAGE_RINGTONE,
+                        timeoutInMillis);
+        CompletableFuture<Void> future2 =
+                mVibratorControlService.triggerVibrationParamsRequest(UID, USAGE_RINGTONE,
+                        timeoutInMillis);
+        mTestLooper.dispatchAll();
+
+        assertThat(future).isNotNull();
+        assertThat(future2).isNotNull();
+        assertThat(future).isNotEqualTo(future2);
+        assertThat(future.isDone()).isTrue();
+        assertThat(future2.isDone()).isTrue();
+        assertThat(mFakeVibratorController.requestVibrationParamsCounter).isEqualTo(2);
+    }
+
     private static int buildVibrationTypesMask(int... types) {
         int typesMask = 0;
         for (int type : types) {
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
index 5ae5677b9b5..e411a17 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
@@ -16,6 +16,8 @@
 
 package com.android.server.vibrator;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -67,6 +69,7 @@
 import android.os.IExternalVibrationController;
 import android.os.IVibratorStateListener;
 import android.os.Looper;
+import android.os.PersistableBundle;
 import android.os.PowerManager;
 import android.os.PowerManagerInternal;
 import android.os.PowerSaveState;
@@ -151,6 +154,9 @@
     private static final VibrationAttributes RINGTONE_ATTRS =
             new VibrationAttributes.Builder().setUsage(
                     VibrationAttributes.USAGE_RINGTONE).build();
+    private static final VibrationAttributes IME_FEEDBACK_ATTRS =
+            new VibrationAttributes.Builder().setUsage(
+                    VibrationAttributes.USAGE_IME_FEEDBACK).build();
     private static final VibrationAttributes UNKNOWN_ATTRS =
             new VibrationAttributes.Builder().setUsage(VibrationAttributes.USAGE_UNKNOWN).build();
 
@@ -850,6 +856,7 @@
         vibrate(service, VibrationEffect.createOneShot(2000, 200),
                 new VibrationAttributes.Builder().setUsage(
                         VibrationAttributes.USAGE_UNKNOWN).build());
+        vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK), IME_FEEDBACK_ATTRS);
 
         InOrder inOrderVerifier = inOrder(mAppOpsManagerMock);
         inOrderVerifier.verify(mAppOpsManagerMock).checkAudioOpNoThrow(eq(AppOpsManager.OP_VIBRATE),
@@ -865,6 +872,8 @@
                 anyInt(), anyString());
         inOrderVerifier.verify(mAppOpsManagerMock).checkAudioOpNoThrow(eq(AppOpsManager.OP_VIBRATE),
                 eq(AudioAttributes.USAGE_UNKNOWN), anyInt(), anyString());
+        inOrderVerifier.verify(mAppOpsManagerMock).checkAudioOpNoThrow(eq(AppOpsManager.OP_VIBRATE),
+                eq(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION), anyInt(), anyString());
     }
 
     @Test
@@ -1573,6 +1582,50 @@
     }
 
     @Test
+    @RequiresFlagsEnabled(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
+    public void vibrate_vendorEffectsWithoutPermission_doesNotVibrate() throws Exception {
+        // Deny permission to vibrate with vendor effects
+        denyPermission(android.Manifest.permission.VIBRATE_VENDOR_EFFECTS);
+        mockVibrators(1);
+        FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(1);
+        fakeVibrator.setCapabilities(IVibrator.CAP_PERFORM_VENDOR_EFFECTS);
+        fakeVibrator.setSupportedEffects(VibrationEffect.EFFECT_TICK);
+        VibratorManagerService service = createSystemReadyService();
+
+        PersistableBundle vendorData = new PersistableBundle();
+        vendorData.putString("key", "value");
+        VibrationEffect vendorEffect = VibrationEffect.createVendorEffect(vendorData);
+        VibrationEffect tickEffect = VibrationEffect.createPredefined(VibrationEffect.EFFECT_TICK);
+
+        vibrateAndWaitUntilFinished(service, vendorEffect, RINGTONE_ATTRS);
+        vibrateAndWaitUntilFinished(service, tickEffect, RINGTONE_ATTRS);
+
+        // No vendor effect played, but predefined TICK plays successfully.
+        assertThat(fakeVibrator.getAllVendorEffects()).isEmpty();
+        assertThat(fakeVibrator.getAllEffectSegments()).hasSize(1);
+        assertThat(fakeVibrator.getAllEffectSegments().get(0)).isInstanceOf(PrebakedSegment.class);
+    }
+
+    @Test
+    @RequiresFlagsEnabled(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
+    public void vibrate_vendorEffectsWithPermission_successful() throws Exception {
+        // Grant permission to vibrate with vendor effects
+        grantPermission(android.Manifest.permission.VIBRATE_VENDOR_EFFECTS);
+        mockVibrators(1);
+        FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(1);
+        fakeVibrator.setCapabilities(IVibrator.CAP_PERFORM_VENDOR_EFFECTS);
+        VibratorManagerService service = createSystemReadyService();
+
+        PersistableBundle vendorData = new PersistableBundle();
+        vendorData.putString("key", "value");
+        VibrationEffect vendorEffect = VibrationEffect.createVendorEffect(vendorData);
+
+        vibrateAndWaitUntilFinished(service, vendorEffect, RINGTONE_ATTRS);
+
+        assertThat(fakeVibrator.getAllVendorEffects()).containsExactly(vendorEffect);
+    }
+
+    @Test
     public void vibrate_withIntensitySettings_appliesSettingsToScaleVibrations() throws Exception {
         int defaultNotificationIntensity =
                 mVibrator.getDefaultVibrationIntensity(VibrationAttributes.USAGE_NOTIFICATION);
@@ -1637,40 +1690,6 @@
     }
 
     @Test
-    public void vibrate_withBypassScaleFlag_ignoresIntensitySettingsAndResolvesAmplitude()
-            throws Exception {
-        // Permission needed for bypassing user settings
-        grantPermission(android.Manifest.permission.MODIFY_PHONE_STATE);
-
-        int defaultTouchIntensity =
-                mVibrator.getDefaultVibrationIntensity(VibrationAttributes.USAGE_TOUCH);
-        // This will scale down touch vibrations.
-        setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY,
-                defaultTouchIntensity > Vibrator.VIBRATION_INTENSITY_LOW
-                        ? defaultTouchIntensity - 1
-                        : defaultTouchIntensity);
-
-        int defaultAmplitude = mContextSpy.getResources().getInteger(
-                com.android.internal.R.integer.config_defaultVibrationAmplitude);
-
-        mockVibrators(1);
-        FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(1);
-        fakeVibrator.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
-        VibratorManagerService service = createSystemReadyService();
-
-        vibrateAndWaitUntilFinished(service,
-                VibrationEffect.createOneShot(100, VibrationEffect.DEFAULT_AMPLITUDE),
-                new VibrationAttributes.Builder()
-                        .setUsage(VibrationAttributes.USAGE_TOUCH)
-                        .setFlags(VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_SCALE)
-                        .build());
-
-        assertEquals(1, fakeVibrator.getAllEffectSegments().size());
-
-        assertEquals(defaultAmplitude / 255f, fakeVibrator.getAmplitudes().get(0), 1e-5);
-    }
-
-    @Test
     @RequiresFlagsEnabled(android.os.vibrator.Flags.FLAG_ADAPTIVE_HAPTICS_ENABLED)
     public void vibrate_withAdaptiveHaptics_appliesCorrectAdaptiveScales() throws Exception {
         // Keep user settings the same as device default so only adaptive scale is applied.
@@ -1714,6 +1733,42 @@
     }
 
     @Test
+    @RequiresFlagsEnabled({
+            android.os.vibrator.Flags.FLAG_ADAPTIVE_HAPTICS_ENABLED,
+            android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS,
+    })
+    public void vibrate_withIntensitySettingsAndAdaptiveHaptics_appliesSettingsToVendorEffects()
+            throws Exception {
+        // Grant permission to vibrate with vendor effects
+        grantPermission(android.Manifest.permission.VIBRATE_VENDOR_EFFECTS);
+
+        setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
+                Vibrator.VIBRATION_INTENSITY_LOW);
+
+        mockVibrators(1);
+        FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(1);
+        fakeVibrator.setCapabilities(IVibrator.CAP_PERFORM_VENDOR_EFFECTS);
+        VibratorManagerService service = createSystemReadyService();
+
+        SparseArray<Float> vibrationScales = new SparseArray<>();
+        vibrationScales.put(ScaleParam.TYPE_NOTIFICATION, 0.4f);
+
+        mVibratorControlService.setVibrationParams(
+                VibrationParamGenerator.generateVibrationParams(vibrationScales),
+                mFakeVibratorController);
+
+        PersistableBundle vendorData = new PersistableBundle();
+        vendorData.putString("key", "value");
+        VibrationEffect vendorEffect = VibrationEffect.createVendorEffect(vendorData);
+        vibrateAndWaitUntilFinished(service, vendorEffect, NOTIFICATION_ATTRS);
+
+        assertThat(fakeVibrator.getAllVendorEffects()).hasSize(1);
+        VibrationEffect.VendorEffect scaled = fakeVibrator.getAllVendorEffects().get(0);
+        assertThat(scaled.getEffectStrength()).isEqualTo(VibrationEffect.EFFECT_STRENGTH_LIGHT);
+        assertThat(scaled.getLinearScale()).isEqualTo(0.4f);
+    }
+
+    @Test
     public void vibrate_withPowerModeChange_cancelVibrationIfNotAllowed() throws Exception {
         mockVibrators(1, 2);
         VibratorManagerService service = createSystemReadyService();
@@ -2729,7 +2784,9 @@
             CombinedVibration effect, VibrationAttributes attrs) {
         HalVibration vib = service.vibrateWithPermissionCheck(UID, deviceId, PACKAGE_NAME, effect,
                 attrs, "some reason", service);
-        mPendingVibrations.add(vib);
+        if (vib != null) {
+            mPendingVibrations.add(vib);
+        }
         return vib;
     }
 
diff --git a/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorController.java b/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorController.java
index 0cd88ef..c0e1407 100644
--- a/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorController.java
+++ b/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorController.java
@@ -39,6 +39,7 @@
     public boolean didRequestVibrationParams = false;
     public int requestVibrationType = VibrationAttributes.USAGE_UNKNOWN;
     public long requestTimeoutInMillis = 0;
+    public int requestVibrationParamsCounter = 0;
 
     public FakeVibratorController(Looper looper) {
         mHandler = new Handler(looper);
@@ -58,6 +59,7 @@
         didRequestVibrationParams = true;
         requestVibrationType = vibrationType;
         requestTimeoutInMillis = timeoutInMillis;
+        requestVibrationParamsCounter++;
         mHandler.post(() -> {
             if (mVibratorControlService != null) {
                 mVibratorControlService.onRequestVibrationParamsComplete(token, mRequestResult);
diff --git a/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorControllerProvider.java b/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorControllerProvider.java
index 2ddb47b..96c3e97 100644
--- a/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorControllerProvider.java
+++ b/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorControllerProvider.java
@@ -17,8 +17,11 @@
 package com.android.server.vibrator;
 
 import android.annotation.Nullable;
+import android.hardware.vibrator.IVibrator;
 import android.os.Handler;
 import android.os.Looper;
+import android.os.Parcel;
+import android.os.PersistableBundle;
 import android.os.VibrationEffect;
 import android.os.VibratorInfo;
 import android.os.vibrator.PrebakedSegment;
@@ -45,6 +48,7 @@
 
     private final Map<Long, PrebakedSegment> mEnabledAlwaysOnEffects = new HashMap<>();
     private final Map<Long, List<VibrationEffectSegment>> mEffectSegments = new TreeMap<>();
+    private final Map<Long, List<VibrationEffect.VendorEffect>> mVendorEffects = new TreeMap<>();
     private final Map<Long, List<Integer>> mBraking = new HashMap<>();
     private final List<Float> mAmplitudes = new ArrayList<>();
     private final List<Boolean> mExternalControlStates = new ArrayList<>();
@@ -69,11 +73,16 @@
     private float mFrequencyResolution = Float.NaN;
     private float mQFactor = Float.NaN;
     private float[] mMaxAmplitudes;
+    private long mVendorEffectDuration = EFFECT_DURATION;
 
     void recordEffectSegment(long vibrationId, VibrationEffectSegment segment) {
         mEffectSegments.computeIfAbsent(vibrationId, k -> new ArrayList<>()).add(segment);
     }
 
+    void recordVendorEffect(long vibrationId, VibrationEffect.VendorEffect vendorEffect) {
+        mVendorEffects.computeIfAbsent(vibrationId, k -> new ArrayList<>()).add(vendorEffect);
+    }
+
     void recordBraking(long vibrationId, int braking) {
         mBraking.computeIfAbsent(vibrationId, k -> new ArrayList<>()).add(braking);
     }
@@ -130,6 +139,21 @@
         }
 
         @Override
+        public long performVendorEffect(Parcel vendorData, long strength, float scale,
+                long vibrationId) {
+            if ((mCapabilities & IVibrator.CAP_PERFORM_VENDOR_EFFECTS) == 0) {
+                return 0;
+            }
+            PersistableBundle bundle = PersistableBundle.CREATOR.createFromParcel(vendorData);
+            recordVendorEffect(vibrationId,
+                    new VibrationEffect.VendorEffect(bundle, (int) strength, scale));
+            applyLatency(mOnLatency);
+            scheduleListener(mVendorEffectDuration, vibrationId);
+            // HAL has unknown duration for vendor effects.
+            return Long.MAX_VALUE;
+        }
+
+        @Override
         public long compose(PrimitiveSegment[] primitives, long vibrationId) {
             if (mSupportedPrimitives == null) {
                 return 0;
@@ -328,6 +352,11 @@
         mMaxAmplitudes = maxAmplitudes;
     }
 
+    /** Set the duration of vendor effects in fake vibrator hardware. */
+    public void setVendorEffectDuration(long durationMs) {
+        mVendorEffectDuration = durationMs;
+    }
+
     /**
      * Return the amplitudes set by this controller, including zeroes for each time the vibrator was
      * turned off.
@@ -366,6 +395,29 @@
         }
         return result;
     }
+
+    /** Return list of {@link VibrationEffect.VendorEffect} played by this controller, in order. */
+    public List<VibrationEffect.VendorEffect> getVendorEffects(long vibrationId) {
+        if (mVendorEffects.containsKey(vibrationId)) {
+            return new ArrayList<>(mVendorEffects.get(vibrationId));
+        } else {
+            return new ArrayList<>();
+        }
+    }
+
+    /**
+     * Returns a list of all vibrations' effect segments, for external-use where vibration IDs
+     * aren't exposed.
+     */
+    public List<VibrationEffect.VendorEffect> getAllVendorEffects() {
+        // Returns segments in order of vibrationId, which increases over time. TreeMap gives order.
+        ArrayList<VibrationEffect.VendorEffect> result = new ArrayList<>();
+        for (List<VibrationEffect.VendorEffect> subList : mVendorEffects.values()) {
+            result.addAll(subList);
+        }
+        return result;
+    }
+
     /** Return list of states set for external control to the fake vibrator hardware. */
     public List<Boolean> getExternalControlStates() {
         return mExternalControlStates;
diff --git a/services/tests/wmtests/res/xml/bookmarks.xml b/services/tests/wmtests/res/xml/bookmarks.xml
index 88419e9..197b366 100644
--- a/services/tests/wmtests/res/xml/bookmarks.xml
+++ b/services/tests/wmtests/res/xml/bookmarks.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2024 The Android Open Source Project
+<!-- Copyright 2024 The Android Open Source Project
 
      Licensed under the Apache License, Version 2.0 (the "License");
      you may not use this file except in compliance with the License.
@@ -14,6 +14,8 @@
      limitations under the License.
 -->
 <bookmarks>
+    <!-- the key combinations for the following shortcuts must be in sync
+         with the key combinations sent by the test in ModifierShortcutTests.java -->
     <bookmark
         role="android.app.role.BROWSER"
         shortcut="b" />
@@ -38,4 +40,40 @@
     <bookmark
         category="android.intent.category.APP_CALCULATOR"
         shortcut="u" />
+
+    <bookmark
+        role="android.app.role.BROWSER"
+        shortcut="b"
+        shift="true" />
+
+    <bookmark
+        category="android.intent.category.APP_CONTACTS"
+        shortcut="c"
+        shift="true" />
+
+    <bookmark
+        package="com.test"
+        class="com.test.BookmarkTest"
+        shortcut="j"
+        shift="true" />
+
+    <!-- The following shortcuts will not be invoked by tests but are here to
+         provide test coverage of parsing the different types of shortcut. -->
+    <bookmark
+        package="com.test"
+        class="com.test.BookmarkTest"
+        shortcut="j" />
+    <bookmark
+        package="com.test2"
+        class="com.test.BookmarkTest"
+        shortcut="d" />
+
+
+    <!-- It's intended that this package/class will NOT resolve so we test the resolution
+         failure case. -->
+    <bookmark
+        package="com.test3"
+        class="com.test.BookmarkTest"
+        shortcut="f" />
+
 </bookmarks>
diff --git a/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutManagerTests.java b/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutManagerTests.java
index 8c375d4..d147325 100644
--- a/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutManagerTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutManagerTests.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -19,19 +19,28 @@
 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
 
 import static org.junit.Assert.assertEquals;
+import static org.mockito.AdditionalMatchers.aryEq;
 import static org.mockito.Mockito.anyInt;
 import static org.mockito.Mockito.anyObject;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.when;
 
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
 import android.content.res.Resources;
 import android.content.res.XmlResourceParser;
 import android.os.Handler;
 import android.os.Looper;
+import android.os.UserHandle;
 import android.view.KeyEvent;
 import android.view.KeyboardShortcutGroup;
 import android.view.KeyboardShortcutInfo;
@@ -58,27 +67,61 @@
     private Handler mHandler;
     private Context mContext;
     private Resources mResources;
+    private PackageManager mPackageManager;
 
     @Before
     public void setUp() {
         mHandler = new Handler(Looper.getMainLooper());
         mContext = spy(getInstrumentation().getTargetContext());
         mResources = spy(mContext.getResources());
+        mPackageManager = spy(mContext.getPackageManager());
 
         XmlResourceParser testBookmarks = mResources.getXml(
                 com.android.frameworks.wmtests.R.xml.bookmarks);
 
+        doReturn(mContext).when(mContext).createContextAsUser(anyObject(), anyInt());
         when(mContext.getResources()).thenReturn(mResources);
+        when(mContext.getPackageManager()).thenReturn(mPackageManager);
         when(mResources.getXml(R.xml.bookmarks)).thenReturn(testBookmarks);
+        try {
+            // Keep packageName / className in sync with
+            // services/tests/wmtests/res/xml/bookmarks.xml
+            ActivityInfo testActivityInfo = new ActivityInfo();
+            testActivityInfo.applicationInfo = new ApplicationInfo();
+            testActivityInfo.packageName =
+                    testActivityInfo.applicationInfo.packageName = "com.test";
+            ResolveInfo testResolveInfo = new ResolveInfo();
+            testResolveInfo.activityInfo = testActivityInfo;
 
-        mModifierShortcutManager = new ModifierShortcutManager(mContext, mHandler);
+            doReturn(testActivityInfo).when(mPackageManager).getActivityInfo(
+                    eq(new ComponentName("com.test", "com.test.BookmarkTest")), anyInt());
+            doReturn(testResolveInfo).when(mPackageManager).resolveActivity(anyObject(), anyInt());
+            doThrow(new PackageManager.NameNotFoundException("com.test3")).when(mPackageManager)
+                    .getActivityInfo(eq(new ComponentName("com.test3", "com.test.BookmarkTest")),
+                        anyInt());
+        } catch (PackageManager.NameNotFoundException ignored) { }
+        doReturn(new String[] { "com.test" }).when(mPackageManager)
+                .canonicalToCurrentPackageNames(aryEq(new String[] { "com.test2" }));
+
+
+        mModifierShortcutManager = new ModifierShortcutManager(
+                mContext, mHandler, UserHandle.SYSTEM);
     }
 
     @Test
     public void test_getApplicationLaunchKeyboardShortcuts() {
+        // Expected values here determined by the number of shortcuts defined in
+        // services/tests/wmtests/res/xml/bookmarks.xml
+
+        // Total valid shortcuts.
         KeyboardShortcutGroup group =
                 mModifierShortcutManager.getApplicationLaunchKeyboardShortcuts(-1);
-        assertEquals(8, group.getItems().size());
+        assertEquals(13, group.getItems().size());
+
+        // Total valid shift shortcuts.
+        assertEquals(3, group.getItems().stream()
+                .filter(s -> s.getModifiers() == (KeyEvent.META_SHIFT_ON | KeyEvent.META_META_ON))
+                .count());
     }
 
     @Test
diff --git a/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java b/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java
index 526c351..71f90a2 100644
--- a/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java
@@ -25,6 +25,7 @@
 import static android.view.KeyEvent.KEYCODE_E;
 import static android.view.KeyEvent.KEYCODE_ENTER;
 import static android.view.KeyEvent.KEYCODE_H;
+import static android.view.KeyEvent.KEYCODE_J;
 import static android.view.KeyEvent.KEYCODE_K;
 import static android.view.KeyEvent.KEYCODE_M;
 import static android.view.KeyEvent.KEYCODE_META_LEFT;
@@ -40,6 +41,7 @@
 import static android.view.KeyEvent.KEYCODE_Z;
 
 import android.app.role.RoleManager;
+import android.content.ComponentName;
 import android.content.Intent;
 import android.os.RemoteException;
 import android.platform.test.annotations.Presubmit;
@@ -105,6 +107,17 @@
             sendKeyCombination(new int[]{KEYCODE_META_LEFT, keyCode}, 0);
             mPhoneWindowManager.assertLaunchRole(role);
         }
+
+        sendKeyCombination(new int[]{KEYCODE_META_LEFT, KEYCODE_SHIFT_LEFT, KEYCODE_B}, 0);
+        mPhoneWindowManager.assertLaunchRole(RoleManager.ROLE_BROWSER);
+
+        sendKeyCombination(new int[]{KEYCODE_META_LEFT, KEYCODE_SHIFT_LEFT, KEYCODE_C}, 0);
+        mPhoneWindowManager.assertLaunchCategory(Intent.CATEGORY_APP_CONTACTS);
+
+        sendKeyCombination(new int[]{KEYCODE_META_LEFT, KEYCODE_SHIFT_LEFT, KEYCODE_J}, 0);
+        mPhoneWindowManager.assertActivityTargetLaunched(
+                new ComponentName("com.test", "com.test.BookmarkTest"));
+
     }
 
     /**
diff --git a/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java b/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java
index f5c8fb8..37e4fd6 100644
--- a/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java
+++ b/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java
@@ -43,11 +43,17 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
 import static com.android.server.policy.WindowManagerPolicy.ACTION_PASS_TO_USER;
 
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyObject;
 import static org.mockito.ArgumentMatchers.eq;
 
 import static java.util.Collections.unmodifiableMap;
 
+import android.content.ComponentName;
 import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
 import android.content.res.Resources;
 import android.content.res.XmlResourceParser;
 import android.platform.test.flag.junit.SetFlagsRule;
@@ -76,6 +82,7 @@
     public RuleChain rules = RuleChain.outerRule(mSettingsProviderRule).around(mSetFlagsRule);
 
     private Resources mResources;
+    private PackageManager mPackageManager;
     TestPhoneWindowManager mPhoneWindowManager;
     DispatchedKeyHandler mDispatchedKeyHandler = event -> false;
     Context mContext;
@@ -100,12 +107,25 @@
     public void setup() {
         mContext = spy(getInstrumentation().getTargetContext());
         mResources = spy(mContext.getResources());
+        mPackageManager = spy(mContext.getPackageManager());
+        doReturn(mContext).when(mContext).createContextAsUser(anyObject(), anyInt());
         doReturn(mResources).when(mContext).getResources();
         doReturn(mSettingsProviderRule.mockContentResolver(mContext))
                 .when(mContext).getContentResolver();
         XmlResourceParser testBookmarks = mResources.getXml(
                 com.android.frameworks.wmtests.R.xml.bookmarks);
         doReturn(testBookmarks).when(mResources).getXml(com.android.internal.R.xml.bookmarks);
+
+        try {
+            // Keep packageName / className in sync with
+            // services/tests/wmtests/res/xml/bookmarks.xml
+            ActivityInfo testActivityInfo = new ActivityInfo();
+            testActivityInfo.applicationInfo = new ApplicationInfo();
+            testActivityInfo.packageName =
+                    testActivityInfo.applicationInfo.packageName = "com.test";
+            doReturn(testActivityInfo).when(mPackageManager).getActivityInfo(
+                    eq(new ComponentName("com.test", "com.test.BookmarkTest")), anyInt());
+        } catch (PackageManager.NameNotFoundException ignored) { }
     }
 
 
diff --git a/services/tests/wmtests/src/com/android/server/policy/StemKeyGestureTests.java b/services/tests/wmtests/src/com/android/server/policy/StemKeyGestureTests.java
index 00414e1..9b92ff4 100644
--- a/services/tests/wmtests/src/com/android/server/policy/StemKeyGestureTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/StemKeyGestureTests.java
@@ -36,6 +36,7 @@
 import android.provider.Settings;
 import android.view.Display;
 
+import org.junit.Before;
 import org.junit.Test;
 
 /**
@@ -48,6 +49,13 @@
 
     private static final String TEST_TARGET_ACTIVITY = "com.android.server.policy/.TestActivity";
 
+    @Before
+    public void setup() {
+        super.setup();
+        overrideResource(com.android.internal.R.integer.config_longPressOnStemPrimaryBehavior,
+                LONG_PRESS_PRIMARY_LAUNCH_VOICE_ASSISTANT);
+    }
+
     /**
      * Stem single key should not launch behavior during set up.
      */
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 2b7e7ab..6f8c91c 100644
--- a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
+++ b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
@@ -50,7 +50,6 @@
 import static org.mockito.ArgumentMatchers.isNull;
 import static org.mockito.Mockito.CALLS_REAL_METHODS;
 import static org.mockito.Mockito.after;
-import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.mockingDetails;
 import static org.mockito.Mockito.timeout;
 import static org.mockito.Mockito.withSettings;
@@ -311,13 +310,6 @@
                 eq(AccessibilityManager.class));
         doReturn(false).when(mAccessibilityManager).isEnabled();
         doReturn(false).when(mPackageManager).hasSystemFeature(any());
-        try {
-            doThrow(new PackageManager.NameNotFoundException("test")).when(mPackageManager)
-                    .getActivityInfo(any(), anyInt());
-            doReturn(new String[] { "testPackage" }).when(mPackageManager)
-                    .canonicalToCurrentPackageNames(any());
-        } catch (PackageManager.NameNotFoundException ignored) { }
-
         doReturn(false).when(mTelecomManager).isInCall();
         doReturn(false).when(mTelecomManager).isRinging();
         doReturn(mTelecomManager).when(mPhoneWindowManager).getTelecommService();
@@ -740,7 +732,6 @@
         Mockito.clearInvocations(mContext);
     }
 
-
     void assertShowRecentApps() {
         mTestLooper.dispatchAll();
         verify(mStatusBarManagerInternal).showRecentApps(anyBoolean());
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 24fc7ee..ea825c7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -18,10 +18,6 @@
 
 import static android.app.AppOpsManager.MODE_ALLOWED;
 import static android.app.AppOpsManager.OP_PICTURE_IN_PICTURE;
-import static android.app.CameraCompatTaskInfo.CAMERA_COMPAT_CONTROL_DISMISSED;
-import static android.app.CameraCompatTaskInfo.CAMERA_COMPAT_CONTROL_HIDDEN;
-import static android.app.CameraCompatTaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED;
-import static android.app.CameraCompatTaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
@@ -119,7 +115,6 @@
 
 import android.app.ActivityOptions;
 import android.app.AppOpsManager;
-import android.app.ICompatCameraControlCallback;
 import android.app.PictureInPictureParams;
 import android.app.servertransaction.ActivityConfigurationChangeItem;
 import android.app.servertransaction.ClientTransaction;
@@ -523,9 +518,8 @@
         assertEquals(newConfig.uiMode, activityConfig.uiMode);
 
         // The configuration change is still sent to the activity, even if it doesn't relaunch.
-        final ActivityConfigurationChangeItem expected =
-                ActivityConfigurationChangeItem.obtain(activity.token, activityConfig,
-                        activity.getActivityWindowInfo());
+        final ActivityConfigurationChangeItem expected = new ActivityConfigurationChangeItem(
+                activity.token, activityConfig, activity.getActivityWindowInfo());
         verify(mClientLifecycleManager).scheduleTransactionItem(
                 eq(activity.app.getThread()), eq(expected));
     }
@@ -596,9 +590,8 @@
 
         final Configuration currentConfig = activity.getConfiguration();
         assertEquals(expectedOrientation, currentConfig.orientation);
-        final ActivityConfigurationChangeItem expected =
-                ActivityConfigurationChangeItem.obtain(activity.token, currentConfig,
-                        activity.getActivityWindowInfo());
+        final ActivityConfigurationChangeItem expected = new ActivityConfigurationChangeItem(
+                activity.token, currentConfig, activity.getActivityWindowInfo());
         verify(mClientLifecycleManager).scheduleTransactionItem(activity.app.getThread(), expected);
         verify(displayRotation).onSetRequestedOrientation();
     }
@@ -817,9 +810,8 @@
 
             activity.ensureActivityConfiguration(true /* ignoreVisibility */);
 
-            final ActivityConfigurationChangeItem expected =
-                    ActivityConfigurationChangeItem.obtain(activity.token,
-                            activity.getConfiguration(), activity.getActivityWindowInfo());
+            final ActivityConfigurationChangeItem expected = new ActivityConfigurationChangeItem(
+                    activity.token, activity.getConfiguration(), activity.getActivityWindowInfo());
             verify(mClientLifecycleManager).scheduleTransactionItem(
                     activity.app.getThread(), expected);
         } finally {
@@ -3354,7 +3346,8 @@
 
         // app1 requests IME visible.
         app1.setRequestedVisibleTypes(ime(), ime());
-        mDisplayContent.getInsetsStateController().onRequestedVisibleTypesChanged(app1);
+        mDisplayContent.getInsetsStateController().onRequestedVisibleTypesChanged(app1,
+                null /* statsToken */);
 
         // Verify app1's IME insets is visible and app2's IME insets frozen flag set.
         assertTrue(app1.getInsetsState().peekSource(ID_IME).isVisible());
@@ -3425,7 +3418,7 @@
         assertFalse(activity2.mImeInsetsFrozenUntilStartInput);
 
         app1.setRequestedVisibleTypes(ime());
-        controller.onRequestedVisibleTypesChanged(app1);
+        controller.onRequestedVisibleTypesChanged(app1, null /* statsToken */);
 
         // Expect all activities in split-screen will get IME insets visible state
         assertTrue(app1.getInsetsState().peekSource(ID_IME).isVisible());
@@ -3485,178 +3478,6 @@
         assertFalse(app.mActivityRecord.isSurfaceShowing());
     }
 
-    @Test
-    public void testUpdateCameraCompatState_flagIsEnabled_controlStateIsUpdated() {
-        final ActivityRecord activity = createActivityWithTask();
-        // Mock a flag being enabled.
-        doReturn(true).when(activity).isCameraCompatControlEnabled();
-
-        activity.updateCameraCompatState(/* showControl */ true,
-                /* transformationApplied */ false, /* callback */ null);
-
-        assertEquals(activity.getCameraCompatControlState(),
-                CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
-
-        activity.updateCameraCompatState(/* showControl */ true,
-                /* transformationApplied */ true, /* callback */ null);
-
-        assertEquals(activity.getCameraCompatControlState(),
-                CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED);
-
-        activity.updateCameraCompatState(/* showControl */ false,
-                /* transformationApplied */ false, /* callback */ null);
-
-        assertEquals(activity.getCameraCompatControlState(), CAMERA_COMPAT_CONTROL_HIDDEN);
-
-        activity.updateCameraCompatState(/* showControl */ false,
-                /* transformationApplied */ true, /* callback */ null);
-
-        assertEquals(activity.getCameraCompatControlState(), CAMERA_COMPAT_CONTROL_HIDDEN);
-    }
-
-    @Test
-    public void testUpdateCameraCompatState_flagIsDisabled_controlStateIsHidden() {
-        final ActivityRecord activity = createActivityWithTask();
-        // Mock a flag being disabled.
-        doReturn(false).when(activity).isCameraCompatControlEnabled();
-
-        activity.updateCameraCompatState(/* showControl */ true,
-                /* transformationApplied */ false, /* callback */ null);
-
-        assertEquals(activity.getCameraCompatControlState(), CAMERA_COMPAT_CONTROL_HIDDEN);
-
-        activity.updateCameraCompatState(/* showControl */ true,
-                /* transformationApplied */ true, /* callback */ null);
-
-        assertEquals(activity.getCameraCompatControlState(), CAMERA_COMPAT_CONTROL_HIDDEN);
-    }
-
-    @Test
-    public void testUpdateCameraCompatStateFromUser_clickedOnDismiss() throws RemoteException {
-        final ActivityRecord activity = createActivityWithTask();
-        // Mock a flag being enabled.
-        doReturn(true).when(activity).isCameraCompatControlEnabled();
-
-        ICompatCameraControlCallback callback = getCompatCameraControlCallback();
-        spyOn(callback);
-        activity.updateCameraCompatState(/* showControl */ true,
-                /* transformationApplied */ false, callback);
-
-        assertEquals(activity.getCameraCompatControlState(),
-                CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
-
-        // Clicking on the button.
-        activity.updateCameraCompatStateFromUser(CAMERA_COMPAT_CONTROL_DISMISSED);
-
-        verify(callback, never()).revertCameraCompatTreatment();
-        verify(callback, never()).applyCameraCompatTreatment();
-        assertEquals(activity.getCameraCompatControlState(), CAMERA_COMPAT_CONTROL_DISMISSED);
-
-        // All following updates are ignored.
-        activity.updateCameraCompatState(/* showControl */ true,
-                /* transformationApplied */ false, /* callback */ null);
-
-        assertEquals(activity.getCameraCompatControlState(), CAMERA_COMPAT_CONTROL_DISMISSED);
-
-        activity.updateCameraCompatState(/* showControl */ true,
-                /* transformationApplied */ true, /* callback */ null);
-
-        assertEquals(activity.getCameraCompatControlState(), CAMERA_COMPAT_CONTROL_DISMISSED);
-
-        activity.updateCameraCompatState(/* showControl */ false,
-                /* transformationApplied */ true, /* callback */ null);
-
-        assertEquals(activity.getCameraCompatControlState(), CAMERA_COMPAT_CONTROL_DISMISSED);
-    }
-
-    @Test
-    public void testUpdateCameraCompatStateFromUser_clickedOnApplyTreatment()
-            throws RemoteException {
-        final ActivityRecord activity = createActivityWithTask();
-        // Mock a flag being enabled.
-        doReturn(true).when(activity).isCameraCompatControlEnabled();
-
-        ICompatCameraControlCallback callback = getCompatCameraControlCallback();
-        spyOn(callback);
-        activity.updateCameraCompatState(/* showControl */ true,
-                /* transformationApplied */ false, callback);
-
-        assertEquals(activity.getCameraCompatControlState(),
-                CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
-
-        // Clicking on the button.
-        activity.updateCameraCompatStateFromUser(CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED);
-
-        verify(callback, never()).revertCameraCompatTreatment();
-        verify(callback).applyCameraCompatTreatment();
-        assertEquals(activity.getCameraCompatControlState(),
-                CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED);
-
-        // Request from the client to show the control are ignored respecting the user choice.
-        activity.updateCameraCompatState(/* showControl */ true,
-                /* transformationApplied */ false, /* callback */ null);
-
-        assertEquals(activity.getCameraCompatControlState(),
-                CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED);
-
-        // Request from the client to hide the control is respected.
-        activity.updateCameraCompatState(/* showControl */ false,
-                /* transformationApplied */ true, /* callback */ null);
-
-        assertEquals(activity.getCameraCompatControlState(), CAMERA_COMPAT_CONTROL_HIDDEN);
-
-        // Request from the client to show the control again is respected.
-        activity.updateCameraCompatState(/* showControl */ true,
-                /* transformationApplied */ false, /* callback */ null);
-
-        assertEquals(activity.getCameraCompatControlState(),
-                CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
-    }
-
-    @Test
-    public void testUpdateCameraCompatStateFromUser_clickedOnRevertTreatment()
-            throws RemoteException {
-        final ActivityRecord activity = createActivityWithTask();
-        // Mock a flag being enabled.
-        doReturn(true).when(activity).isCameraCompatControlEnabled();
-
-        ICompatCameraControlCallback callback = getCompatCameraControlCallback();
-        spyOn(callback);
-        activity.updateCameraCompatState(/* showControl */ true,
-                /* transformationApplied */ true, callback);
-
-        assertEquals(activity.getCameraCompatControlState(),
-                CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED);
-
-        // Clicking on the button.
-        activity.updateCameraCompatStateFromUser(CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
-
-        verify(callback).revertCameraCompatTreatment();
-        verify(callback, never()).applyCameraCompatTreatment();
-        assertEquals(activity.getCameraCompatControlState(),
-                CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
-
-        // Request from the client to show the control are ignored respecting the user choice.
-        activity.updateCameraCompatState(/* showControl */ true,
-                /* transformationApplied */ true, /* callback */ null);
-
-        assertEquals(activity.getCameraCompatControlState(),
-                CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
-
-        // Request from the client to hide the control is respected.
-        activity.updateCameraCompatState(/* showControl */ false,
-                /* transformationApplied */ true, /* callback */ null);
-
-        assertEquals(activity.getCameraCompatControlState(), CAMERA_COMPAT_CONTROL_HIDDEN);
-
-        // Request from the client to show the control again is respected.
-        activity.updateCameraCompatState(/* showControl */ true,
-                /* transformationApplied */ true, /* callback */ null);
-
-        assertEquals(activity.getCameraCompatControlState(),
-                CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED);
-    }
-
     @Test // b/162542125
     public void testInputDispatchTimeout() throws RemoteException {
         final ActivityRecord activity = createActivityWithTask();
@@ -3820,16 +3641,6 @@
         assertTrue(appWindow.mResizeReported);
     }
 
-    private ICompatCameraControlCallback getCompatCameraControlCallback() {
-        return new ICompatCameraControlCallback.Stub() {
-            @Override
-            public void applyCameraCompatTreatment() {}
-
-            @Override
-            public void revertCameraCompatTreatment() {}
-        };
-    }
-
     private void assertHasStartingWindow(ActivityRecord atoken) {
         assertNotNull(atoken.mStartingSurface);
         assertNotNull(atoken.mStartingData);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRefresherTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRefresherTests.java
index 6ad1044..14fbbe4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRefresherTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRefresherTests.java
@@ -191,9 +191,9 @@
         verify(mActivity.mAppCompatController.getAppCompatCameraOverrides(),
                 times(refreshRequested ? 1 : 0)).setIsRefreshRequested(true);
 
-        final RefreshCallbackItem refreshCallbackItem = RefreshCallbackItem.obtain(mActivity.token,
-                cycleThroughStop ? ON_STOP : ON_PAUSE);
-        final ResumeActivityItem resumeActivityItem = ResumeActivityItem.obtain(mActivity.token,
+        final RefreshCallbackItem refreshCallbackItem =
+                new RefreshCallbackItem(mActivity.token, cycleThroughStop ? ON_STOP : ON_PAUSE);
+        final ResumeActivityItem resumeActivityItem = new ResumeActivityItem(mActivity.token,
                 /* isForward */ false, /* shouldSendCompatFakeFocus */ false);
 
         verify(mActivity.mAtmService.getLifecycleManager(), times(refreshRequested ? 1 : 0))
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index ff1c6c8..d0080d2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -102,6 +102,7 @@
 import android.service.voice.IVoiceInteractionSession;
 import android.util.Pair;
 import android.util.Size;
+import android.view.Display;
 import android.view.Gravity;
 import android.view.RemoteAnimationAdapter;
 import android.window.TaskFragmentOrganizerToken;
@@ -941,6 +942,91 @@
                 notNull() /* options */);
     }
 
+
+    /**
+     * This test ensures that activity launch on a secondary display is allowed if the activity did
+     * not opt out from showing on remote devices.
+     */
+    @Test
+    public void testStartActivityOnVirtualDisplay() {
+        final ActivityStarter starter = prepareStarter(FLAG_ACTIVITY_NEW_TASK,
+                false /* mockGetRootTask */);
+        starter.mRequest.activityInfo.flags |= ActivityInfo.FLAG_CAN_DISPLAY_ON_REMOTE_DEVICES;
+
+        // Create a virtual display at bottom.
+        final TestDisplayContent secondaryDisplay =
+                new TestDisplayContent.Builder(mAtm, 1000, 1500)
+                        .setType(Display.TYPE_VIRTUAL)
+                        .setPosition(POSITION_BOTTOM).build();
+        final TaskDisplayArea secondaryTaskContainer = secondaryDisplay.getDefaultTaskDisplayArea();
+        final Task stack = secondaryTaskContainer.createRootTask(
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+
+        // Create an activity record on the top of secondary display.
+        final ActivityRecord topActivityOnSecondaryDisplay = createSingleTaskActivityOn(stack);
+
+        // Put an activity on default display as the top focused activity.
+        new ActivityBuilder(mAtm).setCreateTask(true).build();
+
+        // Start activity with the same intent as {@code topActivityOnSecondaryDisplay}
+        // on secondary display.
+        final ActivityOptions options = ActivityOptions.makeBasic()
+                .setLaunchDisplayId(secondaryDisplay.mDisplayId);
+        final int result = starter.setReason("testStartActivityOnVirtualDisplay")
+                .setIntent(topActivityOnSecondaryDisplay.intent)
+                .setActivityOptions(options.toBundle())
+                .execute();
+
+        // Ensure result is delivering intent to top.
+        assertEquals(START_DELIVERED_TO_TOP, result);
+
+        // Ensure secondary display only creates one stack.
+        verify(secondaryTaskContainer, times(1)).createRootTask(anyInt(), anyInt(), anyBoolean());
+    }
+
+    /**
+     * This test ensures that activity launch on a secondary display is disallowed if the activity
+     * opted out from showing on remote devices.
+     */
+    @EnableFlags(android.companion.virtualdevice.flags.Flags
+            .FLAG_ENFORCE_REMOTE_DEVICE_OPT_OUT_ON_ALL_VIRTUAL_DISPLAYS)
+    @Test
+    public void testStartOptedOutActivityOnVirtualDisplay() {
+        final ActivityStarter starter = prepareStarter(FLAG_ACTIVITY_NEW_TASK,
+                false /* mockGetRootTask */);
+        starter.mRequest.activityInfo.flags &= ~ActivityInfo.FLAG_CAN_DISPLAY_ON_REMOTE_DEVICES;
+
+        // Create a virtual display at bottom.
+        final TestDisplayContent secondaryDisplay =
+                new TestDisplayContent.Builder(mAtm, 1000, 1500)
+                        .setType(Display.TYPE_VIRTUAL)
+                        .setPosition(POSITION_BOTTOM).build();
+        final TaskDisplayArea secondaryTaskContainer = secondaryDisplay.getDefaultTaskDisplayArea();
+        final Task stack = secondaryTaskContainer.createRootTask(
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+
+        // Create an activity record on the top of secondary display.
+        final ActivityRecord topActivityOnSecondaryDisplay = createSingleTaskActivityOn(stack);
+
+        // Put an activity on default display as the top focused activity.
+        new ActivityBuilder(mAtm).setCreateTask(true).build();
+
+        // Start activity with the same intent as {@code topActivityOnSecondaryDisplay}
+        // on secondary display.
+        final ActivityOptions options = ActivityOptions.makeBasic()
+                .setLaunchDisplayId(secondaryDisplay.mDisplayId);
+        final int result = starter.setReason("testStartOptedOutActivityOnVirtualDisplay")
+                .setIntent(topActivityOnSecondaryDisplay.intent)
+                .setActivityOptions(options.toBundle())
+                .execute();
+
+        // Ensure result is canceled.
+        assertEquals(START_CANCELED, result);
+
+        // Ensure secondary display only creates one stack.
+        verify(secondaryTaskContainer, times(1)).createRootTask(anyInt(), anyInt(), anyBoolean());
+    }
+
     @Test
     public void testWasVisibleInRestartAttempt() {
         final ActivityStarter starter = prepareStarter(
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatActivityRobot.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatActivityRobot.java
index 867f01f..220248c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatActivityRobot.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatActivityRobot.java
@@ -16,6 +16,9 @@
 
 package com.android.server.wm;
 
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
@@ -123,6 +126,10 @@
                 .isCameraActive(any(ActivityRecord.class), anyBoolean());
     }
 
+    void setDisplayNaturalOrientation(@Configuration.Orientation int naturalOrientation) {
+        doReturn(naturalOrientation).when(mDisplayContent).getNaturalOrientation();
+    }
+
     @NonNull
     ActivityRecord top() {
         return mActivityStack.top();
@@ -186,10 +193,36 @@
         mDisplayContent.setIgnoreOrientationRequest(enabled);
     }
 
+    void setTopTaskInMultiWindowMode(boolean inMultiWindowMode) {
+        doReturn(inMultiWindowMode).when(mTaskStack.top())
+                .inMultiWindowMode();
+    }
+
     void setTopActivityAsEmbedded(boolean embedded) {
         doReturn(embedded).when(mActivityStack.top()).isEmbedded();
     }
 
+    void setTopActivityInMultiWindowMode(boolean multiWindowMode) {
+        doReturn(multiWindowMode).when(mActivityStack.top()).inMultiWindowMode();
+        if (multiWindowMode) {
+            doReturn(WINDOWING_MODE_MULTI_WINDOW).when(mActivityStack.top()).getWindowingMode();
+        }
+    }
+
+    void setTopActivityInPinnedWindowingMode(boolean pinnedWindowingMode) {
+        doReturn(pinnedWindowingMode).when(mActivityStack.top()).inPinnedWindowingMode();
+        if (pinnedWindowingMode) {
+            doReturn(WINDOWING_MODE_PINNED).when(mActivityStack.top()).getWindowingMode();
+        }
+    }
+
+    void setTopActivityInFreeformWindowingMode(boolean freeformWindowingMode) {
+        doReturn(freeformWindowingMode).when(mActivityStack.top()).inFreeformWindowingMode();
+        if (freeformWindowingMode) {
+            doReturn(WINDOWING_MODE_FREEFORM).when(mActivityStack.top()).getWindowingMode();
+        }
+    }
+
     void destroyTopActivity() {
         mActivityStack.top().removeImmediately();
     }
@@ -201,20 +234,21 @@
     void createNewDisplay() {
         mDisplayContent = new TestDisplayContent.Builder(mAtm, mDisplayWidth, mDisplayHeight)
                 .build();
+        spyOn(mDisplayContent);
         spyOnAppCompatCameraPolicy();
     }
 
     void createNewTask() {
         final Task newTask = new WindowTestsBase.TaskBuilder(mSupervisor)
                 .setDisplay(mDisplayContent).build();
-        mTaskStack.push(newTask);
+        pushTask(newTask);
     }
 
     void createNewTaskWithBaseActivity() {
         final Task newTask = new WindowTestsBase.TaskBuilder(mSupervisor)
                 .setCreateActivity(true)
                 .setDisplay(mDisplayContent).build();
-        mTaskStack.push(newTask);
+        pushTask(newTask);
         pushActivity(newTask.getTopNonFinishingActivity());
     }
 
@@ -401,12 +435,20 @@
     private void pushActivity(@NonNull ActivityRecord activity) {
         mActivityStack.push(activity);
         spyOn(activity);
+        // TODO (b/351763164): Use these spyOn calls only when necessary.
         spyOn(activity.mAppCompatController.getTransparentPolicy());
         spyOn(activity.mAppCompatController.getAppCompatAspectRatioOverrides());
         spyOn(activity.mAppCompatController.getAppCompatAspectRatioPolicy());
+        spyOn(activity.mAppCompatController.getAppCompatFocusOverrides());
+        spyOn(activity.mAppCompatController.getAppCompatResizeOverrides());
         spyOn(activity.mLetterboxUiController);
     }
 
+    private void pushTask(@NonNull Task task) {
+        spyOn(task);
+        mTaskStack.push(task);
+    }
+
     private void spyOnAppCompatCameraPolicy() {
         spyOn(mDisplayContent.mAppCompatCameraPolicy);
         if (mDisplayContent.mAppCompatCameraPolicy.hasDisplayRotationCompatPolicy()) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatAspectRatioOverridesTest.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatAspectRatioOverridesTest.java
new file mode 100644
index 0000000..a6fd112
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatAspectRatioOverridesTest.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.wm;
+
+import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO;
+import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_3_2;
+import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_FULLSCREEN;
+import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE;
+import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_FULLSCREEN_OVERRIDE;
+import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import android.compat.testing.PlatformCompatChangeRule;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.annotation.NonNull;
+
+import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges;
+import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestRule;
+import org.junit.runner.RunWith;
+
+import java.util.function.Consumer;
+
+/**
+ * Test class for {@link AppCompatAspectRatioOverrides}.
+ * <p>
+ * Build/Install/Run:
+ * atest WmTests:AppCompatAspectRatioOverridesTest
+ */
+@Presubmit
+@RunWith(WindowTestRunner.class)
+public class AppCompatAspectRatioOverridesTest extends WindowTestsBase {
+
+    @Rule
+    public TestRule compatChangeRule = new PlatformCompatChangeRule();
+
+    @Test
+    public void testShouldApplyUserFullscreenOverride_trueProperty_returnsFalse() {
+        runTestScenario((robot)-> {
+            robot.prop().enable(PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_FULLSCREEN_OVERRIDE);
+            robot.conf().enableUserAppAspectRatioFullscreen(/* enabled */ false);
+
+            robot.activity().createActivityWithComponent();
+
+            robot.checkShouldApplyUserFullscreenOverride(/* expected */ false);
+        });
+    }
+
+    @Test
+    public void testShouldApplyUserFullscreenOverride_falseFullscreenProperty_returnsFalse() {
+        runTestScenario((robot)-> {
+            robot.conf().enableUserAppAspectRatioFullscreen(/* enabled */ true);
+            robot.activity().setIgnoreOrientationRequest(/* enabled */ true);
+            robot.prop().disable(PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_FULLSCREEN_OVERRIDE);
+            robot.activity().createActivityWithComponent();
+            robot.activity().setGetUserMinAspectRatioOverrideCode(USER_MIN_ASPECT_RATIO_FULLSCREEN);
+
+            robot.checkShouldApplyUserFullscreenOverride(/* expected */ false);
+        });
+    }
+
+    @Test
+    public void testShouldApplyUserFullscreenOverride_falseSettingsProperty_returnsFalse() {
+        runTestScenario((robot)-> {
+            robot.conf().enableUserAppAspectRatioSettings(/* enabled */ true);
+            robot.activity().setIgnoreOrientationRequest(/* enabled */ true);
+            robot.prop().disable(PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE);
+            robot.activity().createActivityWithComponent();
+            robot.activity().setGetUserMinAspectRatioOverrideCode(USER_MIN_ASPECT_RATIO_FULLSCREEN);
+            robot.checkShouldApplyUserFullscreenOverride(/* expected */ false);
+        });
+    }
+
+
+    @Test
+    public void testShouldApplyUserFullscreenOverride_returnsTrue() {
+        runTestScenario((robot)-> {
+            robot.conf().enableUserAppAspectRatioFullscreen(/* enabled */ true);
+            robot.activity().setIgnoreOrientationRequest(/* enabled */ true);
+            robot.activity().createActivityWithComponent();
+            robot.activity().setGetUserMinAspectRatioOverrideCode(USER_MIN_ASPECT_RATIO_FULLSCREEN);
+
+            robot.checkShouldApplyUserFullscreenOverride(/* expected */ true);
+        });
+    }
+
+    @Test
+    public void testShouldEnableUserAspectRatioSettings_falseProperty_returnsFalse() {
+        runTestScenario((robot)-> {
+            robot.conf().enableUserAppAspectRatioSettings(/* enabled */ true);
+            robot.activity().setIgnoreOrientationRequest(/* enabled */ true);
+            robot.prop().disable(PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE);
+            robot.activity().createActivityWithComponent();
+            robot.activity().setGetUserMinAspectRatioOverrideCode(USER_MIN_ASPECT_RATIO_3_2);
+
+            robot.checkShouldEnableUserAspectRatioSettings(/* expected */ false);
+        });
+    }
+
+    @Test
+    public void testShouldEnableUserAspectRatioSettings_trueProperty_returnsTrue() {
+        runTestScenario((robot)-> {
+            robot.conf().enableUserAppAspectRatioSettings(/* enabled */ true);
+            robot.activity().setIgnoreOrientationRequest(/* enabled */ true);
+            robot.prop().enable(PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE);
+            robot.activity().createActivityWithComponent();
+            robot.activity().setGetUserMinAspectRatioOverrideCode(USER_MIN_ASPECT_RATIO_3_2);
+
+            robot.checkShouldEnableUserAspectRatioSettings(/* expected */ true);
+        });
+    }
+
+    @Test
+    public void testShouldEnableUserAspectRatioSettings_ignoreOrientation_returnsFalse() {
+        runTestScenario((robot)-> {
+            robot.conf().enableUserAppAspectRatioSettings(/* enabled */ false);
+            robot.activity().setIgnoreOrientationRequest(/* enabled */ true);
+            robot.prop().enable(PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE);
+            robot.activity().createActivityWithComponent();
+            robot.activity().setGetUserMinAspectRatioOverrideCode(USER_MIN_ASPECT_RATIO_3_2);
+
+            robot.checkShouldEnableUserAspectRatioSettings(/* expected */ false);
+        });
+    }
+
+    @Test
+    public void testShouldApplyUserMinAspectRatioOverride_falseProperty_returnsFalse() {
+        runTestScenario((robot)-> {
+            robot.conf().enableUserAppAspectRatioSettings(/* enabled */ true);
+            robot.activity().setIgnoreOrientationRequest(/* enabled */ true);
+            robot.prop().disable(PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE);
+            robot.activity().createActivityWithComponent();
+            robot.activity().setGetUserMinAspectRatioOverrideCode(USER_MIN_ASPECT_RATIO_3_2);
+
+            robot.checkShouldEnableUserAspectRatioSettings(/* expected */ false);
+        });
+    }
+
+    @Test
+    public void testShouldApplyUserMinAspectRatioOverride_trueProperty_returnsFalse() {
+        runTestScenario((robot)-> {
+            robot.conf().enableUserAppAspectRatioSettings(/* enabled */ false);
+            robot.prop().enable(PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE);
+            robot.activity().createActivityWithComponent();
+
+            robot.checkShouldEnableUserAspectRatioSettings(/* enabled */ false);
+        });
+    }
+
+    @Test
+    public void testShouldApplyUserMinAspectRatioOverride_disabledIgnoreOrientationRequest() {
+        runTestScenario((robot)-> {
+            robot.conf().enableUserAppAspectRatioSettings(/* enabled */ true);
+            robot.activity().setIgnoreOrientationRequest(/* enabled */ false);
+            robot.activity().createActivityWithComponent();
+            robot.activity().setGetUserMinAspectRatioOverrideCode(USER_MIN_ASPECT_RATIO_3_2);
+
+            robot.checkShouldApplyUserMinAspectRatioOverride(/* expected */ false);
+        });
+    }
+
+    @Test
+    public void testShouldApplyUserMinAspectRatioOverride_returnsTrue() {
+        runTestScenario((robot)-> {
+            robot.conf().enableUserAppAspectRatioSettings(/* enabled */ true);
+            robot.activity().setIgnoreOrientationRequest(/* enabled */ true);
+            robot.activity().createActivityWithComponent();
+            robot.activity().setGetUserMinAspectRatioOverrideCode(USER_MIN_ASPECT_RATIO_3_2);
+
+            robot.checkShouldApplyUserMinAspectRatioOverride(/* expected */ true);
+        });
+    }
+
+    @Test
+    public void testShouldApplyUserMinAspectRatioOverride_ignoreOrientation_returnsFalse() {
+        runTestScenario((robot)-> {
+            robot.conf().enableUserAppAspectRatioSettings(/* enabled */ false);
+            robot.activity().setIgnoreOrientationRequest(/* enabled */ true);
+            robot.activity().createActivityWithComponent();
+            robot.activity().setGetUserMinAspectRatioOverrideCode(USER_MIN_ASPECT_RATIO_3_2);
+
+            robot.checkShouldApplyUserMinAspectRatioOverride(/* expected */ false);
+        });
+    }
+
+    @Test
+    @EnableCompatChanges({OVERRIDE_MIN_ASPECT_RATIO})
+    public void testShouldOverrideMinAspectRatio_overrideEnabled_returnsTrue() {
+        runTestScenario((robot)-> {
+            robot.activity().createActivityWithComponent();
+
+            robot.checkShouldOverrideMinAspectRatio(/* expected */ true);
+        });
+    }
+
+    @Test
+    @EnableCompatChanges({OVERRIDE_MIN_ASPECT_RATIO})
+    public void testShouldOverrideMinAspectRatio_propertyTrue_overrideEnabled_returnsTrue() {
+        runTestScenario((robot)-> {
+            robot.prop().enable(PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE);
+            robot.activity().createActivityWithComponent();
+
+            robot.checkShouldOverrideMinAspectRatio(/* expected */ true);
+        });
+    }
+
+    @Test
+    @DisableCompatChanges({OVERRIDE_MIN_ASPECT_RATIO})
+    public void testShouldOverrideMinAspectRatio_propertyTrue_overrideDisabled_returnsFalse() {
+        runTestScenario((robot)-> {
+            robot.prop().enable(PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE);
+            robot.activity().createActivityWithComponent();
+
+            robot.checkShouldOverrideMinAspectRatio(/* expected */ false);
+        });
+    }
+
+    @Test
+    @DisableCompatChanges({OVERRIDE_MIN_ASPECT_RATIO})
+    public void testShouldOverrideMinAspectRatio_overrideDisabled_returnsFalse() {
+        runTestScenario((robot)-> {
+            robot.activity().createActivityWithComponent();
+
+            robot.checkShouldOverrideMinAspectRatio(/* expected */ false);
+        });
+    }
+
+    @Test
+    @EnableCompatChanges({OVERRIDE_MIN_ASPECT_RATIO})
+    public void testshouldOverrideMinAspectRatio_propertyFalse_overrideEnabled_returnsFalse() {
+        runTestScenario((robot)-> {
+            robot.prop().disable(PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE);
+            robot.activity().createActivityWithComponent();
+
+            robot.checkShouldOverrideMinAspectRatio(/* expected */ false);
+        });
+    }
+
+    @Test
+    @DisableCompatChanges({OVERRIDE_MIN_ASPECT_RATIO})
+    public void testshouldOverrideMinAspectRatio_propertyFalse_noOverride_returnsFalse() {
+        runTestScenario((robot)-> {
+            robot.prop().disable(PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE);
+            robot.activity().createActivityWithComponent();
+
+            robot.checkShouldOverrideMinAspectRatio(/* expected */ false);
+        });
+    }
+
+    @Test
+    public void testGetFixedOrientationLetterboxAspectRatio_splitScreenAspectEnabled() {
+        runTestScenario((robot)-> {
+            robot.applyOnConf((c) -> {
+                c.enableCameraCompatTreatment(/* enabled */ true);
+                c.enableCameraCompatTreatmentAtBuildTime(/* enabled */ true);
+                c.enableCameraCompatSplitScreenAspectRatio(/* enabled */ true);
+                c.enableDisplayAspectRatioEnabledForFixedOrientationLetterbox(/* enabled */ false);
+                c.setFixedOrientationLetterboxAspectRatio(/* aspectRatio */ 1.5f);
+            });
+            robot.activity().createActivityWithComponentInNewTaskAndDisplay();
+            robot.checkFixedOrientationLetterboxAspectRatioForTopParent(/* expected */ 1.5f);
+
+            robot.activity().enableTreatmentForTopActivity(/* enabled */ true);
+            robot.checkAspectRatioForTopParentIsSplitScreenRatio(/* expected */ true);
+        });
+    }
+
+    /**
+     * Runs a test scenario providing a Robot.
+     */
+    void runTestScenario(@NonNull Consumer<AspectRatioOverridesRobotTest> consumer) {
+        spyOn(mWm.mAppCompatConfiguration);
+        final AspectRatioOverridesRobotTest robot =
+                new AspectRatioOverridesRobotTest(mWm, mAtm, mSupervisor);
+        consumer.accept(robot);
+    }
+
+    private static class AspectRatioOverridesRobotTest extends AppCompatRobotBase {
+
+        AspectRatioOverridesRobotTest(@NonNull WindowManagerService wm,
+                @NonNull ActivityTaskManagerService atm,
+                @NonNull ActivityTaskSupervisor supervisor) {
+            super(wm, atm, supervisor);
+        }
+
+        void checkShouldApplyUserFullscreenOverride(boolean expected) {
+            assertEquals(expected, getTopActivityAppCompatAspectRatioOverrides()
+                    .shouldApplyUserFullscreenOverride());
+        }
+
+        void checkShouldEnableUserAspectRatioSettings(boolean expected) {
+            assertEquals(expected, getTopActivityAppCompatAspectRatioOverrides()
+                    .shouldEnableUserAspectRatioSettings());
+        }
+
+        void checkShouldApplyUserMinAspectRatioOverride(boolean expected) {
+            assertEquals(expected, getTopActivityAppCompatAspectRatioOverrides()
+                    .shouldApplyUserMinAspectRatioOverride());
+        }
+
+        void checkShouldOverrideMinAspectRatio(boolean expected) {
+            assertEquals(expected, getTopActivityAppCompatAspectRatioOverrides()
+                    .shouldOverrideMinAspectRatio());
+        }
+
+        @NonNull
+        void checkFixedOrientationLetterboxAspectRatioForTopParent(float expected) {
+            assertEquals(expected,
+                    getTopActivityAppCompatAspectRatioOverrides()
+                            .getFixedOrientationLetterboxAspectRatio(
+                                    activity().top().getParent().getConfiguration()),
+                                        FLOAT_TOLLERANCE);
+        }
+
+        void checkAspectRatioForTopParentIsSplitScreenRatio(boolean expected) {
+            final AppCompatAspectRatioOverrides aspectRatioOverrides =
+                    getTopActivityAppCompatAspectRatioOverrides();
+            if (expected) {
+                assertEquals(aspectRatioOverrides.getSplitScreenAspectRatio(),
+                        aspectRatioOverrides.getFixedOrientationLetterboxAspectRatio(
+                                activity().top().getParent().getConfiguration()), FLOAT_TOLLERANCE);
+            } else {
+                assertNotEquals(aspectRatioOverrides.getSplitScreenAspectRatio(),
+                        aspectRatioOverrides.getFixedOrientationLetterboxAspectRatio(
+                                activity().top().getParent().getConfiguration()), FLOAT_TOLLERANCE);
+            }
+        }
+
+        private AppCompatAspectRatioOverrides getTopActivityAppCompatAspectRatioOverrides() {
+            return activity().top().mAppCompatController.getAppCompatAspectRatioOverrides();
+        }
+    }
+
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatCameraOverridesTest.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatCameraOverridesTest.java
index d8c7fb3..de99f54 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatCameraOverridesTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatCameraOverridesTest.java
@@ -25,6 +25,7 @@
 import static android.view.WindowManager.PROPERTY_CAMERA_COMPAT_ALLOW_FORCE_ROTATION;
 import static android.view.WindowManager.PROPERTY_CAMERA_COMPAT_ALLOW_REFRESH;
 import static android.view.WindowManager.PROPERTY_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE;
+import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 import static com.android.window.flags.Flags.FLAG_CAMERA_COMPAT_FOR_FREEFORM;
@@ -36,6 +37,7 @@
 
 import androidx.annotation.NonNull;
 
+import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges;
 import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
 
 import org.junit.Rule;
@@ -286,6 +288,88 @@
         });
     }
 
+
+    @Test
+    @EnableCompatChanges({OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA})
+    public void shouldOverrideMinAspectRatioForCamera_overrideEnabled_returnsTrue() {
+        runTestScenario((robot) -> {
+            robot.activity().createActivityWithComponent();
+            robot.activity().activateCameraInPolicy(/* isCameraActive */ true);
+
+            robot.checkShouldOverrideMinAspectRatioForCamera(/* expected */ true);
+        });
+    }
+
+    @Test
+    @EnableCompatChanges({OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA})
+    public void shouldOverrideMinAspectRatioForCamera_propertyTrue_overrideEnabled_returnsTrue() {
+        runTestScenario((robot) -> {
+            robot.prop().enable(PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE);
+            robot.activity().createActivityWithComponent();
+            robot.activity().activateCameraInPolicy(/* isCameraActive */ true);
+
+            robot.checkShouldOverrideMinAspectRatioForCamera(/* expected */ true);
+        });
+    }
+
+    @Test
+    @EnableCompatChanges({OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA})
+    public void shouldOverrideMinAspectRatioForCamera_propertyTrue_overrideEnabled_returnsFalse() {
+        runTestScenario((robot) -> {
+            robot.prop().enable(PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE);
+            robot.activity().createActivityWithComponent();
+            robot.activity().activateCameraInPolicy(/* isCameraActive */ false);
+
+            robot.checkShouldOverrideMinAspectRatioForCamera(/* expected */ false);
+        });
+    }
+
+    @Test
+    @DisableCompatChanges({OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA})
+    public void shouldOverrideMinAspectRatioForCamera_propertyTrue_overrideDisabled_returnsFalse() {
+        runTestScenario((robot) -> {
+            robot.prop().enable(PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE);
+            robot.activity().createActivityWithComponent();
+            robot.activity().activateCameraInPolicy(/* isCameraActive */ true);
+
+            robot.checkShouldOverrideMinAspectRatioForCamera(/* expected */ false);
+        });
+    }
+
+    @Test
+    @DisableCompatChanges({OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA})
+    public void shouldOverrideMinAspectRatioForCamera_overrideDisabled_returnsFalse() {
+        runTestScenario((robot) -> {
+            robot.activity().createActivityWithComponent();
+            robot.activity().activateCameraInPolicy(/* isCameraActive */ true);
+
+            robot.checkShouldOverrideMinAspectRatioForCamera(/* expected */ false);
+        });
+    }
+
+    @Test
+    @EnableCompatChanges({OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA})
+    public void shouldOverrideMinAspectRatioForCamera_propertyFalse_overrideEnabled_returnsFalse() {
+        runTestScenario((robot) -> {
+            robot.prop().disable(PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE);
+            robot.activity().createActivityWithComponent();
+
+            robot.checkShouldOverrideMinAspectRatioForCamera(/* expected */ false);
+        });
+    }
+
+    @Test
+    @DisableCompatChanges({OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA})
+    public void shouldOverrideMinAspectRatioForCamera_propertyFalse_noOverride_returnsFalse() {
+        runTestScenario((robot) -> {
+            robot.prop().disable(PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE);
+            robot.activity().createActivityWithComponent();
+            robot.activity().activateCameraInPolicy(/* isCameraActive */ true);
+
+            robot.checkShouldOverrideMinAspectRatioForCamera(/* expected */ false);
+        });
+    }
+
     /**
      * Runs a test scenario providing a Robot.
      */
@@ -323,6 +407,11 @@
                     .shouldApplyFreeformTreatmentForCameraCompat(), expected);
         }
 
+        void checkShouldOverrideMinAspectRatioForCamera(boolean expected) {
+            Assert.assertEquals(getAppCompatCameraOverrides()
+                    .shouldOverrideMinAspectRatioForCamera(), expected);
+        }
+
         void checkIsCameraActive(boolean active) {
             Assert.assertEquals(getAppCompatCameraOverrides().isCameraActive(), active);
         }
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatComponentPropRobot.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatComponentPropRobot.java
index d568eec..361177f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatComponentPropRobot.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatComponentPropRobot.java
@@ -32,27 +32,27 @@
  */
 class AppCompatComponentPropRobot {
     @NonNull
-    private final WindowManagerService mWm;
+    private final PackageManager mPackageManager;
 
     AppCompatComponentPropRobot(@NonNull WindowManagerService wm) {
-        mWm = wm;
+        mPackageManager = wm.mContext.getPackageManager();
+        spyOn(mPackageManager);
     }
 
     void enable(@NonNull String propertyName) {
-        setPropertyValue(propertyName, /* enabled */ true);
+        setPropertyValue(propertyName, "", "", /* enabled */ true);
     }
 
     void disable(@NonNull String propertyName) {
-        setPropertyValue(propertyName, /* enabled */ false);
+        setPropertyValue(propertyName, "", "", /* enabled */ false);
     }
 
-    private void setPropertyValue(@NonNull String propertyName, boolean enabled) {
+    private void setPropertyValue(@NonNull String propertyName, @NonNull String packageName,
+            @NonNull String className, boolean enabled) {
         final PackageManager.Property property = new PackageManager.Property(propertyName,
-                /* value */ enabled, /* packageName */ "", /* className */ "");
-        final PackageManager pm = mWm.mContext.getPackageManager();
-        spyOn(pm);
+                /* value */ enabled, packageName, className);
         try {
-            doReturn(property).when(pm).getProperty(eq(propertyName), anyString());
+            doReturn(property).when(mPackageManager).getProperty(eq(propertyName), anyString());
         } catch (PackageManager.NameNotFoundException e) {
             fail(e.getLocalizedMessage());
         }
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatConfigurationRobot.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatConfigurationRobot.java
index cb3cf6b..6592f26 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatConfigurationRobot.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatConfigurationRobot.java
@@ -67,5 +67,17 @@
                 .isCameraCompatSplitScreenAspectRatioEnabled();
     }
 
+    void enableCompatFakeFocus(boolean enabled) {
+        doReturn(enabled).when(mAppCompatConfiguration).isCompatFakeFocusEnabled();
+    }
 
+    void enableDisplayAspectRatioEnabledForFixedOrientationLetterbox(boolean enabled) {
+        doReturn(enabled).when(mAppCompatConfiguration)
+                .getIsDisplayAspectRatioEnabledForFixedOrientationLetterbox();
+    }
+
+    void setFixedOrientationLetterboxAspectRatio(float aspectRatio) {
+        doReturn(aspectRatio).when(mAppCompatConfiguration)
+                .getFixedOrientationLetterboxAspectRatio();
+    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatFocusOverridesTest.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatFocusOverridesTest.java
new file mode 100644
index 0000000..27c5e4e
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatFocusOverridesTest.java
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static android.content.pm.ActivityInfo.OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS;
+import static android.view.WindowManager.PROPERTY_COMPAT_ENABLE_FAKE_FOCUS;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+
+import android.compat.testing.PlatformCompatChangeRule;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.annotation.NonNull;
+
+import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges;
+import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestRule;
+import org.junit.runner.RunWith;
+import org.testng.Assert;
+
+import java.util.function.Consumer;
+
+/**
+ * Test class for {@link AppCompatFocusOverrides}.
+ * <p>
+ * Build/Install/Run:
+ * atest WmTests:AppCompatFocusOverridesTest
+ */
+@Presubmit
+@RunWith(WindowTestRunner.class)
+public class AppCompatFocusOverridesTest extends WindowTestsBase {
+
+    @Rule
+    public TestRule compatChangeRule = new PlatformCompatChangeRule();
+
+    @Test
+    @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS})
+    public void testShouldSendFakeFocus_overrideEnabled_inMultiWindow_returnsTrue() {
+        runTestScenario((robot) -> {
+            robot.conf().enableCompatFakeFocus(/* enabled */ true);
+            robot.applyOnActivity((a) -> {
+                a.createActivityWithComponent();
+                a.setTopActivityInMultiWindowMode(/* multiWindowMode */ true);
+            });
+
+            robot.checkShouldSendFakeFocusOnTopActivity(/* expected */ true);
+        });
+    }
+
+    @Test
+    @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS})
+    public void testShouldSendFakeFocus_overrideEnabled_noMultiWindowMode_returnsFalse() {
+        runTestScenario((robot) -> {
+            robot.conf().enableCompatFakeFocus(/* enabled */ true);
+            robot.applyOnActivity((a) -> {
+                a.createActivityWithComponent();
+                a.setTopActivityInMultiWindowMode(/* multiWindowMode */ false);
+            });
+
+            robot.checkShouldSendFakeFocusOnTopActivity(/* expected */ false);
+        });
+    }
+
+    @Test
+    @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS})
+    public void testShouldSendFakeFocus_overrideEnabled_pinnedWindowMode_returnsFalse() {
+        runTestScenario((robot) -> {
+            robot.conf().enableCompatFakeFocus(/* enabled */ true);
+            robot.applyOnActivity((a) -> {
+                a.createActivityWithComponent();
+                a.setTopActivityInPinnedWindowingMode(/* multiWindowMode */ true);
+            });
+
+            robot.checkShouldSendFakeFocusOnTopActivity(/* expected */ false);
+        });
+    }
+
+    @Test
+    @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS})
+    public void testShouldSendFakeFocus_overrideEnabled_freeformMode_returnsFalse() {
+        runTestScenario((robot) -> {
+            robot.conf().enableCompatFakeFocus(/* enabled */ true);
+            robot.applyOnActivity((a) -> {
+                a.createActivityWithComponent();
+                a.setTopActivityInFreeformWindowingMode(/* freeformWindowingMode */ true);
+            });
+
+            robot.checkShouldSendFakeFocusOnTopActivity(/* expected */ false);
+        });
+    }
+
+    @Test
+    @DisableCompatChanges({OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS})
+    public void testShouldSendFakeFocus_overrideDisabled_returnsFalse() {
+        runTestScenario((robot) -> {
+            robot.conf().enableCompatFakeFocus(/* enabled */ true);
+            robot.applyOnActivity((a) -> {
+                a.createActivityWithComponent();
+                a.setTopActivityInMultiWindowMode(/* multiWindowMode */ true);
+            });
+            robot.checkShouldSendFakeFocusOnTopActivity(/* expected */ false);
+        });
+    }
+
+    @Test
+    @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS})
+    public void testIsCompatFakeFocusEnabled_propertyDisabledAndOverrideOn_fakeFocusDisabled() {
+        runTestScenario((robot) -> {
+            robot.conf().enableCompatFakeFocus(/* enabled */ true);
+            robot.prop().disable(PROPERTY_COMPAT_ENABLE_FAKE_FOCUS);
+            robot.applyOnActivity((a) -> {
+                a.createActivityWithComponent();
+                a.setTopActivityInMultiWindowMode(/* multiWindowMode */ true);
+            });
+            robot.checkShouldSendFakeFocusOnTopActivity(/* expected */ false);
+        });
+    }
+
+    @Test
+    @DisableCompatChanges({OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS})
+    public void testIsCompatFakeFocusEnabled_propertyEnabled_noOverride_fakeFocusEnabled() {
+        runTestScenario((robot) -> {
+            robot.conf().enableCompatFakeFocus(/* enabled */ true);
+            robot.prop().enable(PROPERTY_COMPAT_ENABLE_FAKE_FOCUS);
+            robot.applyOnActivity((a) -> {
+                a.createActivityWithComponent();
+                a.setTopActivityInMultiWindowMode(/* multiWindowMode */ true);
+            });
+            robot.checkShouldSendFakeFocusOnTopActivity(/* expected */ true);
+        });
+    }
+
+    @Test
+    public void testIsCompatFakeFocusEnabled_propertyDisabled_fakeFocusDisabled() {
+        runTestScenario((robot) -> {
+            robot.conf().enableCompatFakeFocus(/* enabled */ true);
+            robot.prop().disable(PROPERTY_COMPAT_ENABLE_FAKE_FOCUS);
+            robot.applyOnActivity((a) -> {
+                a.createActivityWithComponent();
+                a.setTopActivityInMultiWindowMode(/* multiWindowMode */ true);
+            });
+            robot.checkShouldSendFakeFocusOnTopActivity(/* expected */ false);
+        });
+    }
+
+    @Test
+    public void testIsCompatFakeFocusEnabled_propertyEnabled_fakeFocusEnabled() {
+        runTestScenario((robot) -> {
+            robot.conf().enableCompatFakeFocus(/* enabled */ true);
+            robot.prop().enable(PROPERTY_COMPAT_ENABLE_FAKE_FOCUS);
+            robot.applyOnActivity((a) -> {
+                a.createActivityWithComponent();
+                a.setTopActivityInMultiWindowMode(/* multiWindowMode */ true);
+            });
+            robot.checkShouldSendFakeFocusOnTopActivity(/* expected */ true);
+        });
+    }
+
+    /**
+     * Runs a test scenario providing a Robot.
+     */
+    void runTestScenario(@NonNull Consumer<FocusOverridesRobotTest> consumer) {
+        spyOn(mWm.mAppCompatConfiguration);
+        final FocusOverridesRobotTest robot = new FocusOverridesRobotTest(mWm, mAtm, mSupervisor);
+        consumer.accept(robot);
+    }
+
+    private static class FocusOverridesRobotTest extends AppCompatRobotBase {
+
+        FocusOverridesRobotTest(@NonNull WindowManagerService wm,
+                @NonNull ActivityTaskManagerService atm,
+                @NonNull ActivityTaskSupervisor supervisor) {
+            super(wm, atm, supervisor);
+        }
+
+        void checkShouldSendFakeFocusOnTopActivity(boolean expected) {
+            Assert.assertEquals(activity().top().mAppCompatController.getAppCompatFocusOverrides()
+                    .shouldSendFakeFocus(), expected);
+        }
+    }
+
+}
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 634453f..6c0d8c4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatOrientationOverridesTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatOrientationOverridesTest.java
@@ -16,6 +16,10 @@
 package com.android.server.wm;
 
 import static android.content.pm.ActivityInfo.OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED;
+import static android.content.pm.ActivityInfo.OVERRIDE_USE_DISPLAY_LANDSCAPE_NATURAL_ORIENTATION;
+import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
+import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
+import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_DISPLAY_ORIENTATION_OVERRIDE;
 import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_IGNORING_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
@@ -158,6 +162,72 @@
         });
     }
 
+    @Test
+    @EnableCompatChanges({OVERRIDE_USE_DISPLAY_LANDSCAPE_NATURAL_ORIENTATION})
+    public void testShouldUseDisplayLandscapeNaturalOrientation_override_returnsTrue() {
+        runTestScenario((robot) -> {
+            robot.applyOnActivity((a) -> {
+                a.setDisplayNaturalOrientation(ORIENTATION_LANDSCAPE);
+                a.setIgnoreOrientationRequest(true);
+                a.createActivityWithComponent();
+            });
+            robot.checkShouldUseDisplayLandscapeNaturalOrientation(/* expected */ true);
+        });
+    }
+
+    @Test
+    @EnableCompatChanges({OVERRIDE_USE_DISPLAY_LANDSCAPE_NATURAL_ORIENTATION})
+    public void testShouldUseDisplayLandscapeNaturalOrientation_falseProperty_returnsFalse() {
+        runTestScenario((robot) -> {
+            robot.prop().disable(PROPERTY_COMPAT_ALLOW_DISPLAY_ORIENTATION_OVERRIDE);
+            robot.applyOnActivity((a) -> {
+                a.setDisplayNaturalOrientation(ORIENTATION_LANDSCAPE);
+                a.setIgnoreOrientationRequest(true);
+                a.createActivityWithComponent();
+            });
+            robot.checkShouldUseDisplayLandscapeNaturalOrientation(/* expected */ false);
+        });
+    }
+
+    @Test
+    @EnableCompatChanges({OVERRIDE_USE_DISPLAY_LANDSCAPE_NATURAL_ORIENTATION})
+    public void testShouldUseDisplayLandscapeNaturalOrientation_portrait_isFalse() {
+        runTestScenario((robot) -> {
+            robot.applyOnActivity((a) -> {
+                a.setDisplayNaturalOrientation(ORIENTATION_PORTRAIT);
+                a.setIgnoreOrientationRequest(true);
+                a.createActivityWithComponent();
+            });
+            robot.checkShouldUseDisplayLandscapeNaturalOrientation(/* expected */ false);
+        });
+    }
+    @Test
+    @EnableCompatChanges({OVERRIDE_USE_DISPLAY_LANDSCAPE_NATURAL_ORIENTATION})
+    public void testShouldUseDisplayLandscapeNaturalOrientation_noIgnoreRequest_isFalse() {
+        runTestScenario((robot) -> {
+            robot.applyOnActivity((a) -> {
+                a.setDisplayNaturalOrientation(ORIENTATION_LANDSCAPE);
+                a.setIgnoreOrientationRequest(false);
+                a.createActivityWithComponent();
+            });
+            robot.checkShouldUseDisplayLandscapeNaturalOrientation(/* expected */ false);
+        });
+    }
+
+    @Test
+    @EnableCompatChanges({OVERRIDE_USE_DISPLAY_LANDSCAPE_NATURAL_ORIENTATION})
+    public void testShouldUseDisplayLandscapeNaturalOrientation_inMultiWindowMode_returnsFalse() {
+        runTestScenario((robot) -> {
+            robot.applyOnActivity((a) -> {
+                a.setDisplayNaturalOrientation(ORIENTATION_LANDSCAPE);
+                a.setIgnoreOrientationRequest(true);
+                a.createActivityWithComponent();
+                a.setTopTaskInMultiWindowMode(/* inMultiWindowMode */ true);
+            });
+            robot.checkShouldUseDisplayLandscapeNaturalOrientation(/* expected */ false);
+        });
+    }
+
     /**
      * Runs a test scenario providing a Robot.
      */
@@ -215,6 +285,11 @@
             }
         }
 
+        void checkShouldUseDisplayLandscapeNaturalOrientation(boolean expected) {
+            assertEquals(expected,
+                    getTopOrientationOverrides().shouldUseDisplayLandscapeNaturalOrientation());
+        }
+
         private AppCompatOrientationOverrides getTopOrientationOverrides() {
             return activity().top().mAppCompatController.getAppCompatOverrides()
                     .getAppCompatOrientationOverrides();
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatResizeOverridesTest.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatResizeOverridesTest.java
new file mode 100644
index 0000000..8fc1a77
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatResizeOverridesTest.java
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+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 com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+
+import android.compat.testing.PlatformCompatChangeRule;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.annotation.NonNull;
+
+import libcore.junit.util.compat.CoreCompatChangeRule;
+
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestRule;
+import org.junit.runner.RunWith;
+
+import java.util.function.Consumer;
+
+/**
+ * Test class for {@link AppCompatResizeOverrides}.
+ * <p>
+ * Build/Install/Run:
+ * atest WmTests:AppCompatResizeOverridesTest
+ */
+@Presubmit
+@RunWith(WindowTestRunner.class)
+public class AppCompatResizeOverridesTest extends WindowTestsBase {
+
+    @Rule
+    public TestRule compatChangeRule = new PlatformCompatChangeRule();
+
+    @Test
+    @CoreCompatChangeRule.EnableCompatChanges({FORCE_RESIZE_APP})
+    public void testShouldOverrideForceResizeApp_overrideEnabled_returnsTrue() {
+        runTestScenario((robot) -> {
+            robot.activity().createActivityWithComponent();
+            robot.checkShouldOverrideForceResizeApp(/* expected */ true);
+        });
+    }
+
+    @Test
+    @CoreCompatChangeRule.EnableCompatChanges({FORCE_RESIZE_APP})
+    public void testShouldOverrideForceResizeApp_propertyTrue_overrideEnabled_returnsTrue() {
+        runTestScenario((robot) -> {
+            robot.prop().enable(PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES);
+            robot.activity().createActivityWithComponent();
+            robot.checkShouldOverrideForceResizeApp(/* expected */ true);
+        });
+    }
+
+    @Test
+    @CoreCompatChangeRule.DisableCompatChanges({FORCE_RESIZE_APP})
+    public void testShouldOverrideForceResizeApp_propertyTrue_overrideDisabled_returnsFalse() {
+        runTestScenario((robot) -> {
+            robot.prop().enable(PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES);
+            robot.activity().createActivityWithComponent();
+            robot.checkShouldOverrideForceResizeApp(/* expected */ false);
+        });
+    }
+
+    @Test
+    @CoreCompatChangeRule.DisableCompatChanges({FORCE_RESIZE_APP})
+    public void testShouldOverrideForceResizeApp_overrideDisabled_returnsFalse() {
+        runTestScenario((robot) -> {
+            robot.activity().createActivityWithComponent();
+            robot.checkShouldOverrideForceResizeApp(/* expected */ false);
+        });
+    }
+
+    @Test
+    @CoreCompatChangeRule.EnableCompatChanges({FORCE_RESIZE_APP})
+    public void testShouldOverrideForceResizeApp_propertyFalse_overrideEnabled_returnsFalse() {
+        runTestScenario((robot) -> {
+            robot.prop().disable(PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES);
+            robot.activity().createActivityWithComponent();
+            robot.checkShouldOverrideForceResizeApp(/* expected */ false);
+        });
+    }
+
+    @Test
+    @CoreCompatChangeRule.DisableCompatChanges({FORCE_RESIZE_APP})
+    public void testShouldOverrideForceResizeApp_propertyFalse_noOverride_returnsFalse() {
+        runTestScenario((robot) -> {
+            robot.prop().disable(PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES);
+            robot.activity().createActivityWithComponent();
+            robot.checkShouldOverrideForceResizeApp(/* expected */ false);
+        });
+    }
+
+
+    @Test
+    @CoreCompatChangeRule.EnableCompatChanges({FORCE_NON_RESIZE_APP})
+    public void testShouldOverrideForceNonResizeApp_overrideEnabled_returnsTrue() {
+        runTestScenario((robot) -> {
+            robot.activity().createActivityWithComponent();
+            robot.checkShouldOverrideForceNonResizeApp(/* expected */ true);
+        });
+    }
+
+    @Test
+    @CoreCompatChangeRule.EnableCompatChanges({FORCE_NON_RESIZE_APP})
+    public void testShouldOverrideForceNonResizeApp_propertyTrue_overrideEnabled_returnsTrue() {
+        runTestScenario((robot) -> {
+            robot.prop().enable(PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES);
+            robot.activity().createActivityWithComponent();
+            robot.checkShouldOverrideForceNonResizeApp(/* expected */ true);
+        });
+    }
+
+    @Test
+    @CoreCompatChangeRule.DisableCompatChanges({FORCE_NON_RESIZE_APP})
+    public void testShouldOverrideForceNonResizeApp_propertyTrue_overrideDisabled_returnsFalse() {
+        runTestScenario((robot) -> {
+            robot.prop().enable(PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES);
+            robot.activity().createActivityWithComponent();
+            robot.checkShouldOverrideForceNonResizeApp(/* expected */ false);
+        });
+    }
+
+    @Test
+    @CoreCompatChangeRule.DisableCompatChanges({FORCE_NON_RESIZE_APP})
+    public void testShouldOverrideForceNonResizeApp_overrideDisabled_returnsFalse() {
+        runTestScenario((robot) -> {
+            robot.activity().createActivityWithComponent();
+            robot.checkShouldOverrideForceNonResizeApp(/* expected */ false);
+        });
+    }
+
+    @Test
+    @CoreCompatChangeRule.EnableCompatChanges({FORCE_NON_RESIZE_APP})
+    public void testShouldOverrideForceNonResizeApp_propertyFalse_overrideEnabled_returnsFalse() {
+        runTestScenario((robot) -> {
+            robot.prop().disable(PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES);
+            robot.activity().createActivityWithComponent();
+            robot.checkShouldOverrideForceNonResizeApp(/* expected */ false);
+        });
+    }
+
+    @Test
+    @CoreCompatChangeRule.DisableCompatChanges({FORCE_NON_RESIZE_APP})
+    public void testShouldOverrideForceNonResizeApp_propertyFalse_noOverride_returnsFalse() {
+        runTestScenario((robot) -> {
+            robot.prop().disable(PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES);
+            robot.activity().createActivityWithComponent();
+            robot.checkShouldOverrideForceNonResizeApp(/* expected */ false);
+        });
+    }
+
+    /**
+     * Runs a test scenario providing a Robot.
+     */
+    void runTestScenario(@NonNull Consumer<ResizeOverridesRobotTest> consumer) {
+        spyOn(mWm.mAppCompatConfiguration);
+        final ResizeOverridesRobotTest robot = new ResizeOverridesRobotTest(mWm, mAtm, mSupervisor);
+        consumer.accept(robot);
+    }
+
+    private static class ResizeOverridesRobotTest extends AppCompatRobotBase {
+
+        ResizeOverridesRobotTest(@NonNull WindowManagerService wm,
+                @NonNull ActivityTaskManagerService atm,
+                @NonNull ActivityTaskSupervisor supervisor) {
+            super(wm, atm, supervisor);
+        }
+
+
+        void checkShouldOverrideForceResizeApp(boolean expected) {
+            Assert.assertEquals(expected, activity().top().mAppCompatController
+                    .getAppCompatResizeOverrides().shouldOverrideForceResizeApp());
+        }
+
+        void checkShouldOverrideForceNonResizeApp(boolean expected) {
+            Assert.assertEquals(expected, activity().top().mAppCompatController
+                    .getAppCompatResizeOverrides().shouldOverrideForceNonResizeApp());
+        }
+    }
+
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatRobotBase.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatRobotBase.java
index 92f246b..6939f97 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatRobotBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatRobotBase.java
@@ -28,6 +28,8 @@
     private static final int DEFAULT_DISPLAY_WIDTH = 1000;
     private static final int DEFAULT_DISPLAY_HEIGHT = 2000;
 
+    static final float FLOAT_TOLLERANCE = 0.01f;
+
     @NonNull
     private final AppCompatActivityRobot mActivityRobot;
     @NonNull
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatSizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatSizeCompatTests.java
new file mode 100644
index 0000000..439c633
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatSizeCompatTests.java
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static android.content.pm.ActivityInfo.OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS;
+import static android.view.WindowManager.PROPERTY_COMPAT_ENABLE_FAKE_FOCUS;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+
+import android.compat.testing.PlatformCompatChangeRule;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.annotation.NonNull;
+import androidx.test.filters.MediumTest;
+
+import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges;
+import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
+
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestRule;
+import org.junit.runner.RunWith;
+
+import java.util.function.Consumer;
+
+/**
+ * Tests for App Compat specific code about sizes.
+ *
+ * Build/Install/Run:
+ *  atest WmTests:AppCompatSizeCompatTests
+ */
+@MediumTest
+@Presubmit
+@RunWith(WindowTestRunner.class)
+public class AppCompatSizeCompatTests extends WindowTestsBase {
+
+    @Rule
+    public TestRule compatChangeRule = new PlatformCompatChangeRule();
+
+    @Test
+    @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS})
+    public void testShouldSendFakeFocus_compatFakeFocusEnabledUnsetProp() {
+        runTestScenario((robot) -> {
+            robot.conf().enableCompatFakeFocus(/* enabled */ true);
+            robot.activity().createActivityWithComponent();
+
+            robot.putTopActivityInMultiWindowMode();
+            robot.checkShouldSendCompatFakeFocus(/* expected */ true);
+
+            robot.putTopActivityInPinnedWindowingMode();
+            robot.checkShouldSendCompatFakeFocus(/* expected */ false);
+
+            robot.putTopActivityInFreeformWindowingMode();
+            robot.checkShouldSendCompatFakeFocus(/* expected */ false);
+        });
+    }
+
+    @Test
+    @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS})
+    public void testShouldSendFakeFocus_compatFakeFocusEnabledTrueProp() {
+        runTestScenario((robot) -> {
+            robot.conf().enableCompatFakeFocus(/* enabled */ true);
+            robot.prop().enable(PROPERTY_COMPAT_ENABLE_FAKE_FOCUS);
+            robot.activity().createActivityWithComponent();
+
+            robot.putTopActivityInMultiWindowMode();
+            robot.checkShouldSendCompatFakeFocus(/* expected */ true);
+
+            robot.putTopActivityInPinnedWindowingMode();
+            robot.checkShouldSendCompatFakeFocus(/* expected */ false);
+
+            robot.putTopActivityInFreeformWindowingMode();
+            robot.checkShouldSendCompatFakeFocus(/* expected */ false);
+        });
+    }
+
+    @Test
+    @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS})
+    public void testShouldSendFakeFocus_compatFakeFocusEnabledFalseProp() {
+        runTestScenario((robot) -> {
+            robot.conf().enableCompatFakeFocus(/* enabled */ true);
+            robot.prop().disable(PROPERTY_COMPAT_ENABLE_FAKE_FOCUS);
+            robot.activity().createActivityWithComponent();
+
+            robot.putTopActivityInMultiWindowMode();
+            robot.checkShouldSendCompatFakeFocus(/* expected */ false);
+
+            robot.putTopActivityInPinnedWindowingMode();
+            robot.checkShouldSendCompatFakeFocus(/* expected */ false);
+
+            robot.putTopActivityInFreeformWindowingMode();
+            robot.checkShouldSendCompatFakeFocus(/* expected */ false);
+        });
+    }
+
+    @Test
+    @DisableCompatChanges({OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS})
+    public void testShouldSendFakeFocus_compatFakeFocusDisabledUnsetProp() {
+        runTestScenario((robot) -> {
+            robot.conf().enableCompatFakeFocus(/* enabled */ true);
+            robot.activity().createActivityWithComponent();
+
+            robot.putTopActivityInMultiWindowMode();
+            robot.checkShouldSendCompatFakeFocus(/* expected */ false);
+
+            robot.putTopActivityInPinnedWindowingMode();
+            robot.checkShouldSendCompatFakeFocus(/* expected */ false);
+
+            robot.putTopActivityInFreeformWindowingMode();
+            robot.checkShouldSendCompatFakeFocus(/* expected */ false);
+        });
+    }
+
+    @Test
+    @DisableCompatChanges({OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS})
+    public void testShouldSendFakeFocus_compatFakeFocusDisabledTrueProp() {
+        runTestScenario((robot) -> {
+            robot.conf().enableCompatFakeFocus(/* enabled */ true);
+            robot.prop().enable(PROPERTY_COMPAT_ENABLE_FAKE_FOCUS);
+            robot.activity().createActivityWithComponent();
+
+            robot.putTopActivityInMultiWindowMode();
+            robot.checkShouldSendCompatFakeFocus(/* expected */ true);
+
+            robot.putTopActivityInPinnedWindowingMode();
+            robot.checkShouldSendCompatFakeFocus(/* expected */ false);
+
+            robot.putTopActivityInFreeformWindowingMode();
+            robot.checkShouldSendCompatFakeFocus(/* expected */ false);
+        });
+    }
+
+    @Test
+    @DisableCompatChanges({OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS})
+    public void testShouldSendFakeFocus_compatFakeFocusDisabledFalseProp() {
+        runTestScenario((robot) -> {
+            robot.conf().enableCompatFakeFocus(/* enabled */ true);
+            robot.prop().disable(PROPERTY_COMPAT_ENABLE_FAKE_FOCUS);
+            robot.activity().createActivityWithComponent();
+
+            robot.putTopActivityInMultiWindowMode();
+            robot.checkShouldSendCompatFakeFocus(/* expected */ false);
+
+            robot.putTopActivityInPinnedWindowingMode();
+            robot.checkShouldSendCompatFakeFocus(/* expected */ false);
+
+            robot.putTopActivityInFreeformWindowingMode();
+            robot.checkShouldSendCompatFakeFocus(/* expected */ false);
+        });
+    }
+
+    @Test
+    @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS})
+    public void testShouldSendFakeFocus_compatFakeFocusEnabledFeatureDisabled() {
+        runTestScenario((robot) -> {
+            robot.conf().enableCompatFakeFocus(/* enabled */ false);
+            robot.activity().createActivityWithComponent();
+
+            robot.putTopActivityInMultiWindowMode();
+            robot.checkShouldSendCompatFakeFocus(/* expected */ false);
+
+            robot.putTopActivityInPinnedWindowingMode();
+            robot.checkShouldSendCompatFakeFocus(/* expected */ false);
+
+            robot.putTopActivityInFreeformWindowingMode();
+            robot.checkShouldSendCompatFakeFocus(/* expected */ false);
+        });
+    }
+
+    /**
+     * Runs a test scenario providing a Robot.
+     */
+    void runTestScenario(@NonNull Consumer<SizeCompatRobotTest> consumer) {
+        spyOn(mWm.mAppCompatConfiguration);
+        final SizeCompatRobotTest robot = new SizeCompatRobotTest(mWm, mAtm, mSupervisor);
+        consumer.accept(robot);
+    }
+
+    private static class SizeCompatRobotTest extends AppCompatRobotBase {
+
+        SizeCompatRobotTest(@NonNull WindowManagerService wm,
+                @NonNull ActivityTaskManagerService atm,
+                @NonNull ActivityTaskSupervisor supervisor) {
+            super(wm, atm, supervisor);
+        }
+
+        void checkShouldSendCompatFakeFocus(boolean expected) {
+            Assert.assertEquals(expected, activity().top().shouldSendCompatFakeFocus());
+        }
+
+        void putTopActivityInMultiWindowMode() {
+            applyOnActivity((a) -> {
+                a.setTopActivityInMultiWindowMode(true);
+                a.setTopActivityInFreeformWindowingMode(false);
+                a.setTopActivityInPinnedWindowingMode(false);
+            });
+        }
+
+        void putTopActivityInPinnedWindowingMode() {
+            applyOnActivity((a) -> {
+                a.setTopActivityInMultiWindowMode(false);
+                a.setTopActivityInPinnedWindowingMode(true);
+                a.setTopActivityInFreeformWindowingMode(false);
+            });
+        }
+
+        void putTopActivityInFreeformWindowingMode() {
+            applyOnActivity((a) -> {
+                a.setTopActivityInMultiWindowMode(false);
+                a.setTopActivityInPinnedWindowingMode(false);
+                a.setTopActivityInFreeformWindowingMode(true);
+            });
+        }
+    }
+}
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 63c14b9..44837d7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
@@ -58,6 +58,7 @@
 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;
@@ -67,6 +68,7 @@
 import android.window.OnBackInvokedCallback;
 import android.window.OnBackInvokedCallbackInfo;
 import android.window.OnBackInvokedDispatcher;
+import android.window.TaskFragmentOrganizer;
 import android.window.TaskSnapshot;
 import android.window.WindowOnBackInvokedDispatcher;
 
@@ -612,6 +614,7 @@
     }
 
     @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
@@ -670,25 +673,28 @@
     }
 
     @Test
-    public void testAdjacentFocusInActivityEmbedding() {
-        mSetFlagsRule.enableFlags(Flags.FLAG_EMBEDDED_ACTIVITY_BACK_NAV_FLAG);
+    public void testBackOnMostRecentWindowInActivityEmbedding() {
         final Task task = createTask(mDefaultDisplay);
-        final TaskFragment primaryTf = createTaskFragmentWithActivity(task);
-        final TaskFragment secondaryTf = createTaskFragmentWithActivity(task);
+        final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
+        final TaskFragment primaryTf = createTaskFragmentWithEmbeddedActivity(task, organizer);
+        final TaskFragment secondaryTf = createTaskFragmentWithEmbeddedActivity(task, organizer);
         final ActivityRecord primaryActivity = primaryTf.getTopMostActivity();
         final ActivityRecord secondaryActivity = secondaryTf.getTopMostActivity();
         primaryTf.setAdjacentTaskFragment(secondaryTf);
         secondaryTf.setAdjacentTaskFragment(primaryTf);
 
-        final WindowState windowState = mock(WindowState.class);
-        windowState.mActivityRecord = primaryActivity;
-        doReturn(windowState).when(mWm).getFocusedWindowLocked();
-        doReturn(primaryTf).when(windowState).getTaskFragment();
+        final WindowState primaryWindow = mock(WindowState.class);
+        final WindowState secondaryWindow = mock(WindowState.class);
+        doReturn(primaryActivity).when(primaryWindow).getActivityRecord();
+        doReturn(secondaryActivity).when(secondaryWindow).getActivityRecord();
         doReturn(1L).when(primaryActivity).getLastWindowCreateTime();
         doReturn(2L).when(secondaryActivity).getLastWindowCreateTime();
+        doReturn(mDisplayContent).when(primaryActivity).getDisplayContent();
+        doReturn(secondaryWindow).when(mDisplayContent).findFocusedWindow(eq(secondaryActivity));
 
-        startBackNavigation();
-        verify(mWm).moveFocusToActivity(eq(secondaryActivity));
+        final WindowState mostRecentUsedWindow =
+                mWm.getMostRecentUsedEmbeddedWindowForBack(primaryWindow);
+        assertThat(mostRecentUsedWindow).isEqualTo(secondaryWindow);
     }
 
     /**
diff --git a/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerExemptionTests.java b/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerExemptionTests.java
index 366e519..6e48818 100644
--- a/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerExemptionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerExemptionTests.java
@@ -16,6 +16,9 @@
 
 package com.android.server.wm;
 
+import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED;
+import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS;
+
 import static com.android.server.wm.ActivityTaskManagerService.APP_SWITCH_ALLOW;
 import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_ALLOWLISTED_COMPONENT;
 import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_FOREGROUND;
@@ -23,7 +26,6 @@
 import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_SAW_PERMISSION;
 import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_VISIBLE_WINDOW;
 
-import static com.google.common.truth.Truth.assertThat;
 import static com.google.common.truth.Truth.assertWithMessage;
 
 import static org.mockito.ArgumentMatchers.anyInt;
@@ -58,7 +60,6 @@
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 import org.mockito.Mock;
-import org.mockito.Mockito;
 import org.mockito.quality.Strictness;
 
 import java.lang.reflect.Field;
@@ -134,9 +135,8 @@
 
     ActivityOptions mCheckedOptions = ActivityOptions.makeBasic()
             .setPendingIntentCreatorBackgroundActivityStartMode(
-                    ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED)
-            .setPendingIntentBackgroundActivityStartMode(
-                    ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED);
+                    MODE_BACKGROUND_ACTIVITY_START_ALLOWED)
+            .setPendingIntentBackgroundActivityStartMode(MODE_BACKGROUND_ACTIVITY_START_ALLOWED);
 
     class TestableBackgroundActivityStartController extends BackgroundActivityStartController {
         private Set<Pair<Integer, Integer>> mBalPermissionUidPidPairs = new HashSet<>();
@@ -175,7 +175,6 @@
         when(mService.getAppOpsManager()).thenReturn(mAppOpsManager);
         setViaReflection(mService, "mProcessMap", mProcessMap);
 
-        //Mockito.when(mSupervisor.getBackgroundActivityLaunchController()).thenReturn(mController);
         setViaReflection(mSupervisor, "mRecentTasks", mRecentTasks);
 
         mController = new TestableBackgroundActivityStartController(mService, mSupervisor);
@@ -397,7 +396,7 @@
 
         // setup state
         WindowProcessControllerMap mProcessMap = new WindowProcessControllerMap();
-        WindowProcessController otherProcess = Mockito.mock(WindowProcessController.class);
+        WindowProcessController otherProcess = mock(WindowProcessController.class);
         mProcessMap.put(callingPid, mCallerApp);
         mProcessMap.put(REGULAR_PID_1_1, otherProcess);
         setViaReflection(mService, "mProcessMap", mProcessMap);
@@ -516,14 +515,13 @@
         BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
         Intent intent = TEST_INTENT;
         ActivityOptions checkedOptions = mCheckedOptions;
-        checkedOptions.setPendingIntentBackgroundActivityLaunchAllowedByPermission(true);
+        checkedOptions.setPendingIntentBackgroundActivityStartMode(
+                MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS);
         BackgroundActivityStartController.BalState balState = mController.new BalState(callingUid,
                 callingPid, callingPackage, realCallingUid, realCallingPid, null,
                 originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
                 checkedOptions);
 
-        assertThat(balState.isPendingIntentBalAllowedByPermission()).isTrue();
-
         // call
         BalVerdict realCallerVerdict = mController.checkBackgroundActivityStartAllowedBySender(
                 balState);
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 f110c69..e364264 100644
--- a/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerTests.java
@@ -547,7 +547,7 @@
         assertThat(balState.callerExplicitOptInOrOut()).isFalse();
         assertThat(balState.realCallerExplicitOptInOrAutoOptIn()).isTrue();
         assertThat(balState.realCallerExplicitOptInOrOut()).isFalse();
-        assertThat(balState.toString()).contains(
+        assertThat(balState.toString()).startsWith(
                 "[callingPackage: package.app1; "
                         + "callingPackageTargetSdk: -1; "
                         + "callingUid: 10001; "
@@ -563,6 +563,7 @@
                         + "balAllowedByPiCreator: BSP.ALLOW_BAL; "
                         + "balAllowedByPiCreatorWithHardening: BSP.ALLOW_BAL; "
                         + "resultIfPiCreatorAllowsBal: null; "
+                        + "callerStartMode: MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED; "
                         + "hasRealCaller: true; "
                         + "isCallForResult: false; "
                         + "isPendingIntent: false; "
@@ -646,7 +647,7 @@
         assertThat(balState.callerExplicitOptInOrOut()).isFalse();
         assertThat(balState.realCallerExplicitOptInOrAutoOptIn()).isFalse();
         assertThat(balState.realCallerExplicitOptInOrOut()).isFalse();
-        assertThat(balState.toString()).contains(
+        assertThat(balState.toString()).startsWith(
                 "[callingPackage: package.app1; "
                         + "callingPackageTargetSdk: -1; "
                         + "callingUid: 10001; "
@@ -662,6 +663,7 @@
                         + "balAllowedByPiCreator: BSP.NONE; "
                         + "balAllowedByPiCreatorWithHardening: BSP.NONE; "
                         + "resultIfPiCreatorAllowsBal: null; "
+                        + "callerStartMode: MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED; "
                         + "hasRealCaller: true; "
                         + "isCallForResult: false; "
                         + "isPendingIntent: true; "
diff --git a/services/tests/wmtests/src/com/android/server/wm/CameraCompatFreeformPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/CameraCompatFreeformPolicyTests.java
index eaa1641..f2592d2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/CameraCompatFreeformPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/CameraCompatFreeformPolicyTests.java
@@ -307,9 +307,9 @@
         verify(mActivity.mAppCompatController.getAppCompatCameraOverrides(),
                 times(refreshRequested ? 1 : 0)).setIsRefreshRequested(true);
 
-        final RefreshCallbackItem refreshCallbackItem = RefreshCallbackItem.obtain(mActivity.token,
-                cycleThroughStop ? ON_STOP : ON_PAUSE);
-        final ResumeActivityItem resumeActivityItem = ResumeActivityItem.obtain(mActivity.token,
+        final RefreshCallbackItem refreshCallbackItem =
+                new RefreshCallbackItem(mActivity.token, cycleThroughStop ? ON_STOP : ON_PAUSE);
+        final ResumeActivityItem resumeActivityItem = new ResumeActivityItem(mActivity.token,
                 /* isForward */ false, /* shouldSendCompatFakeFocus */ false);
 
         verify(mActivity.mAtmService.getLifecycleManager(), times(refreshRequested ? 1 : 0))
diff --git a/services/tests/wmtests/src/com/android/server/wm/ClientLifecycleManagerTests.java b/services/tests/wmtests/src/com/android/server/wm/ClientLifecycleManagerTests.java
index 2bda950..3ddf8da 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ClientLifecycleManagerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ClientLifecycleManagerTests.java
@@ -87,24 +87,6 @@
     }
 
     @Test
-    public void testScheduleTransaction_recycleBinderClientTransaction() throws Exception {
-        final ClientTransaction item = spy(ClientTransaction.obtain(mClient));
-
-        mLifecycleManager.scheduleTransaction(item);
-
-        verify(item).recycle();
-    }
-
-    @Test
-    public void testScheduleTransaction_notRecycleNonBinderClientTransaction() throws Exception {
-        final ClientTransaction item = spy(ClientTransaction.obtain(mNonBinderClient));
-
-        mLifecycleManager.scheduleTransaction(item);
-
-        verify(item, never()).recycle();
-    }
-
-    @Test
     public void testScheduleTransactionItem() throws RemoteException {
         spyOn(mWms.mWindowPlacerLocked);
         doReturn(true).when(mWms.mWindowPlacerLocked).isTraversalScheduled();
@@ -194,7 +176,6 @@
 
         assertTrue(mLifecycleManager.mPendingTransactions.isEmpty());
         verify(mTransaction).schedule();
-        verify(mTransaction).recycle();
     }
 
     @Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java b/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
index 9efbe35..771e290 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
@@ -21,12 +21,10 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
-import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_DIMMER;
 import static com.android.server.wm.utils.LastCallVerifier.lastCall;
 
 import static org.junit.Assert.assertNotNull;
 import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.reset;
@@ -35,7 +33,6 @@
 import android.graphics.Rect;
 import android.platform.test.annotations.Presubmit;
 import android.platform.test.annotations.RequiresFlagsDisabled;
-import android.platform.test.annotations.RequiresFlagsEnabled;
 import android.view.SurfaceControl;
 import android.view.SurfaceSession;
 
@@ -131,41 +128,28 @@
         }
     }
 
-    private static class SurfaceAnimatorStarterImpl implements LegacyDimmer.SurfaceAnimatorStarter {
-        @Override
-        public void startAnimation(SurfaceAnimator surfaceAnimator, SurfaceControl.Transaction t,
-                AnimationAdapter anim, boolean hidden, @SurfaceAnimator.AnimationType int type) {
-            surfaceAnimator.mStaticAnimationFinishedCallback.onAnimationFinished(type, anim);
-        }
-    }
-
     private MockSurfaceBuildingContainer mHost;
     private Dimmer mDimmer;
     private SurfaceControl.Transaction mTransaction;
     private TestWindowContainer mChild;
     private static AnimationAdapter sTestAnimation;
-    private static LegacyDimmer.SurfaceAnimatorStarter sSurfaceAnimatorStarter;
 
     @Before
     public void setUp() throws Exception {
         mHost = new MockSurfaceBuildingContainer(mWm);
         mTransaction = spy(StubTransaction.class);
         mChild = new TestWindowContainer(mWm);
-        if (Dimmer.DIMMER_REFACTOR) {
-            sTestAnimation = spy(new MockAnimationAdapter());
-            mDimmer = new SmoothDimmer(mHost, new MockAnimationAdapterFactory());
-        } else {
-            sSurfaceAnimatorStarter = spy(new SurfaceAnimatorStarterImpl());
-            mDimmer = new LegacyDimmer(mHost, sSurfaceAnimatorStarter);
-        }
+        sTestAnimation = spy(new MockAnimationAdapter());
+        mDimmer = new Dimmer(mHost, new MockAnimationAdapterFactory());
     }
 
     @Test
+    @RequiresFlagsDisabled(Flags.FLAG_USE_TASKS_DIM_ONLY)
     public void testUpdateDimsAppliesCrop() {
         mHost.addChild(mChild, 0);
 
         mDimmer.adjustAppearance(mChild, 1, 1);
-        mDimmer.adjustRelativeLayer(mChild, -1);
+        mDimmer.adjustPosition(mChild, mChild, -1);
 
         int width = 100;
         int height = 300;
@@ -177,13 +161,12 @@
     }
 
     @Test
-    @RequiresFlagsEnabled(Flags.FLAG_INTRODUCE_SMOOTHER_DIMMER)
-    public void testDimBelowWithChildSurfaceCreatesSurfaceBelowChild_Smooth() {
+    public void testDimBelowWithChildSurfaceCreatesSurfaceBelowChild() {
         final float alpha = 0.7f;
         final int blur = 50;
         mHost.addChild(mChild, 0);
         mDimmer.adjustAppearance(mChild, alpha, blur);
-        mDimmer.adjustRelativeLayer(mChild, -1);
+        mDimmer.adjustPosition(mChild, mChild, -1);
         SurfaceControl dimLayer = mDimmer.getDimLayer();
 
         assertNotNull("Dimmer should have created a surface", dimLayer);
@@ -197,60 +180,25 @@
     }
 
     @Test
-    @RequiresFlagsDisabled(Flags.FLAG_INTRODUCE_SMOOTHER_DIMMER)
-    public void testDimBelowWithChildSurfaceCreatesSurfaceBelowChild_Legacy() {
-        final float alpha = 0.7f;
-        mHost.addChild(mChild, 0);
-        mDimmer.adjustAppearance(mChild, alpha, 20);
-        mDimmer.adjustRelativeLayer(mChild, -1);
-        SurfaceControl dimLayer = mDimmer.getDimLayer();
-
-        assertNotNull("Dimmer should have created a surface", dimLayer);
-
-        verify(mHost.getPendingTransaction()).setAlpha(dimLayer, alpha);
-        verify(mHost.getPendingTransaction()).setRelativeLayer(dimLayer, mChild.mControl, -1);
-    }
-
-    @Test
-    @RequiresFlagsEnabled(Flags.FLAG_INTRODUCE_SMOOTHER_DIMMER)
-    public void testDimBelowWithChildSurfaceDestroyedWhenReset_Smooth() {
+    public void testDimBelowWithChildSurfaceDestroyedWhenReset() {
         mHost.addChild(mChild, 0);
 
         final float alpha = 0.8f;
         final int blur = 50;
         // Dim once
         mDimmer.adjustAppearance(mChild, alpha, blur);
-        mDimmer.adjustRelativeLayer(mChild, -1);
+        mDimmer.adjustPosition(mChild, mChild, -1);
         SurfaceControl dimLayer = mDimmer.getDimLayer();
         mDimmer.updateDims(mTransaction);
         // Reset, and don't dim
         mDimmer.resetDimStates();
-        mDimmer.adjustRelativeLayer(mChild, -1);
+        mDimmer.adjustPosition(mChild, mChild, -1);
         mDimmer.updateDims(mTransaction);
         verify(mTransaction).show(dimLayer);
         verify(mTransaction).remove(dimLayer);
     }
 
     @Test
-    @RequiresFlagsDisabled(Flags.FLAG_INTRODUCE_SMOOTHER_DIMMER)
-    public void testDimBelowWithChildSurfaceDestroyedWhenReset_Legacy() {
-        mHost.addChild(mChild, 0);
-
-        final float alpha = 0.8f;
-        mDimmer.adjustAppearance(mChild, alpha, 20);
-        mDimmer.adjustRelativeLayer(mChild, -1);
-        SurfaceControl dimLayer = mDimmer.getDimLayer();
-        mDimmer.resetDimStates();
-
-        mDimmer.updateDims(mTransaction);
-        verify(sSurfaceAnimatorStarter).startAnimation(any(SurfaceAnimator.class),
-                any(SurfaceControl.Transaction.class), any(AnimationAdapter.class),
-                anyBoolean(),
-                eq(ANIMATION_TYPE_DIMMER));
-        verify(mHost.getPendingTransaction()).remove(dimLayer);
-    }
-
-    @Test
     public void testDimBelowWithChildSurfaceNotDestroyedWhenPersisted() {
         mHost.addChild(mChild, 0);
 
@@ -258,24 +206,25 @@
         final int blur = 20;
         // Dim once
         mDimmer.adjustAppearance(mChild, alpha, blur);
-        mDimmer.adjustRelativeLayer(mChild, -1);
+        mDimmer.adjustPosition(mChild, mChild, -1);
         SurfaceControl dimLayer = mDimmer.getDimLayer();
         mDimmer.updateDims(mTransaction);
         // Reset and dim again
         mDimmer.resetDimStates();
         mDimmer.adjustAppearance(mChild, alpha, blur);
-        mDimmer.adjustRelativeLayer(mChild, -1);
+        mDimmer.adjustPosition(mChild, mChild, -1);
         mDimmer.updateDims(mTransaction);
         verify(mTransaction).show(dimLayer);
         verify(mTransaction, never()).remove(dimLayer);
     }
 
     @Test
+    @RequiresFlagsDisabled(Flags.FLAG_USE_TASKS_DIM_ONLY)
     public void testDimUpdateWhileDimming() {
         mHost.addChild(mChild, 0);
         final float alpha = 0.8f;
         mDimmer.adjustAppearance(mChild, alpha, 20);
-        mDimmer.adjustRelativeLayer(mChild, -1);
+        mDimmer.adjustPosition(mChild, mChild, -1);
         final Rect bounds = mDimmer.getDimBounds();
 
         SurfaceControl dimLayer = mDimmer.getDimLayer();
@@ -292,11 +241,10 @@
     }
 
     @Test
-    @RequiresFlagsEnabled(Flags.FLAG_INTRODUCE_SMOOTHER_DIMMER)
-    public void testRemoveDimImmediately_Smooth() {
+    public void testRemoveDimImmediately() {
         mHost.addChild(mChild, 0);
         mDimmer.adjustAppearance(mChild, 1, 2);
-        mDimmer.adjustRelativeLayer(mChild, -1);
+        mDimmer.adjustPosition(mChild, mChild, -1);
         SurfaceControl dimLayer = mDimmer.getDimLayer();
         mDimmer.updateDims(mTransaction);
         verify(mTransaction, times(1)).show(dimLayer);
@@ -311,65 +259,28 @@
         verify(mTransaction).remove(dimLayer);
     }
 
-    @Test
-    @RequiresFlagsDisabled(Flags.FLAG_INTRODUCE_SMOOTHER_DIMMER)
-    public void testRemoveDimImmediately_Legacy() {
-        mHost.addChild(mChild, 0);
-        mDimmer.adjustAppearance(mChild, 1, 0);
-        mDimmer.adjustRelativeLayer(mChild, -1);
-        SurfaceControl dimLayer = mDimmer.getDimLayer();
-        mDimmer.updateDims(mTransaction);
-        verify(mTransaction, times(1)).show(dimLayer);
-
-        reset(sSurfaceAnimatorStarter);
-        mDimmer.dontAnimateExit();
-        mDimmer.resetDimStates();
-        mDimmer.updateDims(mTransaction);
-        verify(sSurfaceAnimatorStarter, never()).startAnimation(any(SurfaceAnimator.class),
-                any(SurfaceControl.Transaction.class), any(AnimationAdapter.class), anyBoolean(),
-                eq(ANIMATION_TYPE_DIMMER));
-        verify(mTransaction).remove(dimLayer);
-    }
-
-    @Test
-    @RequiresFlagsDisabled(Flags.FLAG_INTRODUCE_SMOOTHER_DIMMER)
-    public void testDimmerWithBlurUpdatesTransaction_Legacy() {
-        mHost.addChild(mChild, 0);
-
-        final int blurRadius = 50;
-        mDimmer.adjustAppearance(mChild, 1, blurRadius);
-        mDimmer.adjustRelativeLayer(mChild, -1);
-        SurfaceControl dimLayer = mDimmer.getDimLayer();
-
-        assertNotNull("Dimmer should have created a surface", dimLayer);
-
-        verify(mHost.getPendingTransaction()).setBackgroundBlurRadius(dimLayer, blurRadius);
-        verify(mHost.getPendingTransaction()).setRelativeLayer(dimLayer, mChild.mControl, -1);
-    }
-
     /**
      * mChild is requesting the dim values to be set directly. In this case, dim won't play the
      * standard animation, but directly apply mChild's requests to the dim surface
      */
     @Test
-    @RequiresFlagsEnabled(Flags.FLAG_INTRODUCE_SMOOTHER_DIMMER)
     public void testContainerDimsOpeningAnimationByItself() {
         mHost.addChild(mChild, 0);
 
         mDimmer.resetDimStates();
         mDimmer.adjustAppearance(mChild, 0.1f, 0);
-        mDimmer.adjustRelativeLayer(mChild, -1);
+        mDimmer.adjustPosition(mChild, mChild, -1);
         SurfaceControl dimLayer = mDimmer.getDimLayer();
         mDimmer.updateDims(mTransaction);
 
         mDimmer.resetDimStates();
         mDimmer.adjustAppearance(mChild, 0.2f, 0);
-        mDimmer.adjustRelativeLayer(mChild, -1);
+        mDimmer.adjustPosition(mChild, mChild, -1);
         mDimmer.updateDims(mTransaction);
 
         mDimmer.resetDimStates();
         mDimmer.adjustAppearance(mChild, 0.3f, 0);
-        mDimmer.adjustRelativeLayer(mChild, -1);
+        mDimmer.adjustPosition(mChild, mChild, -1);
         mDimmer.updateDims(mTransaction);
 
         verify(mTransaction).setAlpha(dimLayer, 0.2f);
@@ -384,24 +295,23 @@
      * alpha is animated to 0. This corner case is needed to verify that the layer is removed anyway
      */
     @Test
-    @RequiresFlagsEnabled(Flags.FLAG_INTRODUCE_SMOOTHER_DIMMER)
     public void testContainerDimsClosingAnimationByItself() {
         mHost.addChild(mChild, 0);
 
         mDimmer.resetDimStates();
         mDimmer.adjustAppearance(mChild, 0.2f, 0);
-        mDimmer.adjustRelativeLayer(mChild, -1);
+        mDimmer.adjustPosition(mChild, mChild, -1);
         SurfaceControl dimLayer = mDimmer.getDimLayer();
         mDimmer.updateDims(mTransaction);
 
         mDimmer.resetDimStates();
         mDimmer.adjustAppearance(mChild, 0.1f, 0);
-        mDimmer.adjustRelativeLayer(mChild, -1);
+        mDimmer.adjustPosition(mChild, mChild, -1);
         mDimmer.updateDims(mTransaction);
 
         mDimmer.resetDimStates();
         mDimmer.adjustAppearance(mChild, 0f, 0);
-        mDimmer.adjustRelativeLayer(mChild, -1);
+        mDimmer.adjustPosition(mChild, mChild, -1);
         mDimmer.updateDims(mTransaction);
 
         mDimmer.resetDimStates();
@@ -413,7 +323,6 @@
      * Check the handover of the dim between two windows and the consequent dim animation in between
      */
     @Test
-    @RequiresFlagsEnabled(Flags.FLAG_INTRODUCE_SMOOTHER_DIMMER)
     public void testMultipleContainersDimmingConsecutively() {
         TestWindowContainer first = mChild;
         TestWindowContainer second = new TestWindowContainer(mWm);
@@ -421,13 +330,13 @@
         mHost.addChild(second, 1);
 
         mDimmer.adjustAppearance(first, 0.5f, 0);
-        mDimmer.adjustRelativeLayer(first, -1);
+        mDimmer.adjustPosition(mChild, first, -1);
         SurfaceControl dimLayer = mDimmer.getDimLayer();
         mDimmer.updateDims(mTransaction);
 
         mDimmer.resetDimStates();
         mDimmer.adjustAppearance(second, 0.9f, 0);
-        mDimmer.adjustRelativeLayer(second, -1);
+        mDimmer.adjustPosition(mChild, second, -1);
         mDimmer.updateDims(mTransaction);
 
         verify(sTestAnimation, times(2)).startAnimation(
@@ -442,7 +351,6 @@
      * updateDims will be satisfied
      */
     @Test
-    @RequiresFlagsEnabled(Flags.FLAG_INTRODUCE_SMOOTHER_DIMMER)
     public void testMultipleContainersDimmingAtTheSameTime() {
         TestWindowContainer first = mChild;
         TestWindowContainer second = new TestWindowContainer(mWm);
@@ -450,10 +358,10 @@
         mHost.addChild(second, 1);
 
         mDimmer.adjustAppearance(first, 0.5f, 0);
-        mDimmer.adjustRelativeLayer(first, -1);
+        mDimmer.adjustPosition(mChild, first, -1);
         SurfaceControl dimLayer = mDimmer.getDimLayer();
         mDimmer.adjustAppearance(second, 0.9f, 0);
-        mDimmer.adjustRelativeLayer(second, -1);
+        mDimmer.adjustPosition(mChild, second, -1);
         mDimmer.updateDims(mTransaction);
 
         verify(sTestAnimation, times(1)).startAnimation(
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 57118f2..f843386 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentDeferredUpdateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentDeferredUpdateTests.java
@@ -63,11 +63,6 @@
 
     private final Message mScreenUnblocker = mock(Message.class);
 
-    @Override
-    protected void onBeforeSystemServicesCreated() {
-        mSetFlagsRule.enableFlags(Flags.FLAG_DEFER_DISPLAY_UPDATES);
-    }
-
     @Before
     public void before() {
         doReturn(true).when(mDisplayContent).getLastHasContent();
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 c77a4d6..caeb41c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
@@ -554,7 +554,7 @@
         final DisplayPolicy displayPolicy = mDisplayContent.getDisplayPolicy();
         displayPolicy.addWindowLw(mNavBarWindow, mNavBarWindow.mAttrs);
         final InsetsSourceProvider navBarProvider = mNavBarWindow.getControllableInsetProvider();
-        navBarProvider.updateControlForTarget(mAppWindow, false);
+        navBarProvider.updateControlForTarget(mAppWindow, false, null /* statsToken */);
         navBarProvider.getSource().setVisible(false);
 
         displayPolicy.setCanSystemBarsBeShownByUser(false);
@@ -579,7 +579,7 @@
         final DisplayPolicy displayPolicy = mDisplayContent.getDisplayPolicy();
         displayPolicy.addWindowLw(mNavBarWindow, mNavBarWindow.mAttrs);
         final InsetsSourceProvider navBarProvider = mNavBarWindow.getControllableInsetProvider();
-        navBarProvider.updateControlForTarget(win, false);
+        navBarProvider.updateControlForTarget(win, false, null /* statsToken */);
         navBarProvider.getSource().setVisible(false);
 
         displayPolicy.setCanSystemBarsBeShownByUser(true);
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java
index e9fcc40..2dea6ba 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java
@@ -613,9 +613,9 @@
         verify(mActivity.mAppCompatController.getAppCompatCameraOverrides(),
                 times(refreshRequested ? 1 : 0)).setIsRefreshRequested(true);
 
-        final RefreshCallbackItem refreshCallbackItem = RefreshCallbackItem.obtain(mActivity.token,
-                cycleThroughStop ? ON_STOP : ON_PAUSE);
-        final ResumeActivityItem resumeActivityItem = ResumeActivityItem.obtain(mActivity.token,
+        final RefreshCallbackItem refreshCallbackItem =
+                new RefreshCallbackItem(mActivity.token, cycleThroughStop ? ON_STOP : ON_PAUSE);
+        final ResumeActivityItem resumeActivityItem = new ResumeActivityItem(mActivity.token,
                 /* isForward */ false, /* shouldSendCompatFakeFocus */ false);
 
         verify(mActivity.mAtmService.getLifecycleManager(), times(refreshRequested ? 1 : 0))
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsProviderTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsProviderTests.java
index 3fcf304..2f2b473 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsProviderTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsProviderTests.java
@@ -24,12 +24,14 @@
 
 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
 
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertTrue;
-import static org.junit.Assume.assumeTrue;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.testng.Assert.assertFalse;
 
 import android.annotation.Nullable;
@@ -58,6 +60,7 @@
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.nio.charset.StandardCharsets;
+import java.util.List;
 import java.util.function.Consumer;
 
 /**
@@ -352,20 +355,54 @@
     }
 
     @Test
-    public void testRemovesStaleDisplaySettings() {
-        assumeTrue(com.android.window.flags.Flags.perUserDisplayWindowSettings());
-
-        final DisplayWindowSettingsProvider provider =
-                new DisplayWindowSettingsProvider(mDefaultVendorSettingsStorage,
-                        mOverrideSettingsStorage);
-        final DisplayInfo displayInfo = mSecondaryDisplay.getDisplayInfo();
-        updateOverrideSettings(provider, displayInfo, settings -> settings.mForcedDensity = 356);
+    public void testRemovesStaleDisplaySettings_defaultDisplay_removesStaleDisplaySettings() {
+        // Write density setting for second display then remove it.
+        final DisplayWindowSettingsProvider provider = new DisplayWindowSettingsProvider(
+                mDefaultVendorSettingsStorage, mOverrideSettingsStorage);
+        final DisplayInfo secDisplayInfo = mSecondaryDisplay.getDisplayInfo();
+        updateOverrideSettings(provider, secDisplayInfo, setting -> setting.mForcedDensity = 356);
         mRootWindowContainer.removeChild(mSecondaryDisplay);
 
-        provider.removeStaleDisplaySettings(mRootWindowContainer);
+        // Write density setting for inner and outer default display.
+        final DisplayInfo innerDisplayInfo = mPrimaryDisplay.getDisplayInfo();
+        final DisplayInfo outerDisplayInfo = new DisplayInfo(secDisplayInfo);
+        outerDisplayInfo.displayId = mPrimaryDisplay.mDisplayId;
+        outerDisplayInfo.uniqueId = "TEST_OUTER_DISPLAY_" + System.currentTimeMillis();
+        updateOverrideSettings(provider, innerDisplayInfo, setting -> setting.mForcedDensity = 490);
+        updateOverrideSettings(provider, outerDisplayInfo, setting -> setting.mForcedDensity = 420);
+        final List<DisplayInfo> possibleDisplayInfos = List.of(innerDisplayInfo, outerDisplayInfo);
+        doReturn(possibleDisplayInfos)
+                .when(mWm).getPossibleDisplayInfoLocked(eq(innerDisplayInfo.displayId));
+
+        provider.removeStaleDisplaySettingsLocked(mWm, mRootWindowContainer);
 
         assertThat(mOverrideSettingsStorage.wasWriteSuccessful()).isTrue();
-        assertThat(provider.getOverrideSettingsSize()).isEqualTo(0);
+        assertThat(provider.getOverrideSettingsSize()).isEqualTo(2);
+        assertThat(provider.getOverrideSettings(innerDisplayInfo).mForcedDensity).isEqualTo(490);
+        assertThat(provider.getOverrideSettings(outerDisplayInfo).mForcedDensity).isEqualTo(420);
+    }
+
+    @Test
+    public void testRemovesStaleDisplaySettings_displayNotInLayout_keepsDisplaySettings() {
+        // Write density setting for primary display.
+        final DisplayWindowSettingsProvider provider = new DisplayWindowSettingsProvider(
+                mDefaultVendorSettingsStorage, mOverrideSettingsStorage);
+        final DisplayInfo primDisplayInfo = mPrimaryDisplay.getDisplayInfo();
+        updateOverrideSettings(provider, primDisplayInfo, setting -> setting.mForcedDensity = 420);
+
+        // Add a virtual display and write density setting for it.
+        final DisplayInfo virtDisplayInfo = new DisplayInfo(primDisplayInfo);
+        virtDisplayInfo.uniqueId = "TEST_VIRTUAL_DISPLAY_" + System.currentTimeMillis();
+        createNewDisplay(virtDisplayInfo);
+        waitUntilHandlersIdle();  // Wait until unfrozen after a display is added.
+        updateOverrideSettings(provider, virtDisplayInfo, setting -> setting.mForcedDensity = 490);
+
+        provider.removeStaleDisplaySettingsLocked(mWm, mRootWindowContainer);
+
+        assertThat(mOverrideSettingsStorage.wasWriteSuccessful()).isTrue();
+        assertThat(provider.getOverrideSettingsSize()).isEqualTo(2);
+        assertThat(provider.getOverrideSettings(primDisplayInfo).mForcedDensity).isEqualTo(420);
+        assertThat(provider.getOverrideSettings(virtDisplayInfo).mForcedDensity).isEqualTo(490);
     }
 
     /**
diff --git a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
index 8cdb574..4a9d5c7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
@@ -152,11 +152,10 @@
         // Use a new TestIWindow so we don't collect events for other windows
         final WindowState window = createWindow(
                 null, TYPE_BASE_APPLICATION, activity, name, ownerId, false, new TestIWindow());
-        window.mInputChannel = new InputChannel();
-        window.mInputChannelToken = window.mInputChannel.getToken();
+        InputChannel channel = new InputChannel();
+        window.openInputChannel(channel);
         window.mHasSurface = true;
         mWm.mWindowMap.put(window.mClient.asBinder(), window);
-        mWm.mInputToWindowMap.put(window.mInputChannelToken, window);
         return window;
     }
 
@@ -178,8 +177,8 @@
                 TEST_PID, TEST_UID);
         mWindow = createDropTargetWindow("Drag test window", 0);
         doReturn(mWindow).when(mDisplayContent).getTouchableWinAtPointLocked(0, 0);
-        when(mWm.mInputManager.startDragAndDrop(any(InputChannel.class),
-                any(InputChannel.class))).thenReturn(true);
+        when(mWm.mInputManager.startDragAndDrop(any(IBinder.class),
+                any(IBinder.class))).thenReturn(true);
 
         mWm.mWindowMap.put(mWindow.mClient.asBinder(), mWindow);
     }
@@ -707,8 +706,7 @@
                     .setFormat(PixelFormat.TRANSLUCENT)
                     .build();
 
-            assertTrue(mWm.mInputManager.startDragAndDrop(new InputChannel(),
-                    new InputChannel()));
+            assertTrue(mWm.mInputManager.startDragAndDrop(new Binder(), new Binder()));
             mToken = mTarget.performDrag(TEST_PID, 0, mWindow.mClient,
                     flag, surface, 0, 0, 0, 0, 0, 0, 0, data);
             assertNotNull(mToken);
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
index eeec54f..b26c267 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
@@ -361,7 +361,7 @@
 
         mAppWindow.setRequestedVisibleTypes(
                 navigationBars() | statusBars(), navigationBars() | statusBars());
-        policy.onRequestedVisibleTypesChanged(mAppWindow);
+        policy.onRequestedVisibleTypesChanged(mAppWindow, null /* statsToken */);
         waitUntilWindowAnimatorIdle();
 
         controls = mDisplayContent.getInsetsStateController().getControlsForDispatch(mAppWindow);
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
index 2a025cd..6190807 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
@@ -112,13 +112,13 @@
         statusBar.getFrame().set(0, 0, 500, 100);
 
         // We must not have control or control target before we have the insets source window.
-        mProvider.updateControlForTarget(target, true /* force */);
+        mProvider.updateControlForTarget(target, true /* force */, null /* statsToken */);
         assertNull(mProvider.getControl(target));
         assertNull(mProvider.getControlTarget());
 
         // We can have the control or the control target after we have the insets source window.
         mProvider.setWindowContainer(statusBar, null, null);
-        mProvider.updateControlForTarget(target, false /* force */);
+        mProvider.updateControlForTarget(target, false /* force */, null /* statsToken */);
         assertNotNull(mProvider.getControl(target));
         assertNotNull(mProvider.getControlTarget());
 
@@ -127,25 +127,25 @@
         mProvider.startSeamlessRotation();
         assertNull(mProvider.getControl(target));
         assertNull(mProvider.getControlTarget());
-        mProvider.updateControlForTarget(target, true /* force */);
+        mProvider.updateControlForTarget(target, true /* force */, null /* statsToken */);
         assertNull(mProvider.getControl(target));
         assertNull(mProvider.getControlTarget());
 
         // We can have the control and the control target after seamless rotation.
         mProvider.finishSeamlessRotation();
-        mProvider.updateControlForTarget(target, false /* force */);
+        mProvider.updateControlForTarget(target, false /* force */, null /* statsToken */);
         assertNotNull(mProvider.getControl(target));
         assertNotNull(mProvider.getControlTarget());
 
         // We can clear the control and the control target.
-        mProvider.updateControlForTarget(null, false /* force */);
+        mProvider.updateControlForTarget(null, false /* force */, null /* statsToken */);
         assertNull(mProvider.getControl(target));
         assertNull(mProvider.getControlTarget());
 
         // We must not have control or control target if the insets source window doesn't have a
         // surface.
         statusBar.setSurfaceControl(null);
-        mProvider.updateControlForTarget(target, true /* force */);
+        mProvider.updateControlForTarget(target, true /* force */, null /* statsToken */);
         assertNull(mProvider.getControl(target));
         assertNull(mProvider.getControlTarget());
     }
@@ -173,7 +173,7 @@
 
         // We must not have control or control target before we have the insets source window,
         // so also no leash.
-        mProvider.updateControlForTarget(target, true /* force */);
+        mProvider.updateControlForTarget(target, true /* force */, null /* statsToken */);
         assertNull(mProvider.getControl(target));
         assertNull(mProvider.getControlTarget());
         assertNull(mProvider.getLeash(target));
@@ -181,7 +181,7 @@
         // We can have the control or the control target after we have the insets source window,
         // but no leash as this is not yet ready for dispatching.
         mProvider.setWindowContainer(statusBar, null, null);
-        mProvider.updateControlForTarget(target, false /* force */);
+        mProvider.updateControlForTarget(target, false /* force */, null /* statsToken */);
         assertNotNull(mProvider.getControl(target));
         assertNotNull(mProvider.getControlTarget());
         assertEquals(mProvider.getControlTarget(), target);
@@ -265,9 +265,9 @@
         final WindowState target = createWindow(null, TYPE_APPLICATION, "target");
         statusBar.getFrame().set(0, 0, 500, 100);
         mProvider.setWindowContainer(statusBar, null, null);
-        mProvider.updateControlForTarget(target, false /* force */);
+        mProvider.updateControlForTarget(target, false /* force */, null /* statsToken */);
         target.setRequestedVisibleTypes(0, statusBars());
-        mProvider.updateClientVisibility(target);
+        mProvider.updateClientVisibility(target, null /* statsToken */);
         assertFalse(mSource.isVisible());
     }
 
@@ -278,7 +278,7 @@
         statusBar.getFrame().set(0, 0, 500, 100);
         mProvider.setWindowContainer(statusBar, null, null);
         target.setRequestedVisibleTypes(0, statusBars());
-        mProvider.updateClientVisibility(target);
+        mProvider.updateClientVisibility(target, null /* statsToken */);
         assertTrue(mSource.isVisible());
     }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
index c69faed..0dc56f8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
@@ -201,7 +201,7 @@
                 .setWindowContainer(mImeWindow, null, null);
         getController().onImeControlTargetChanged(base);
         base.setRequestedVisibleTypes(ime(), ime());
-        getController().onRequestedVisibleTypesChanged(base);
+        getController().onRequestedVisibleTypesChanged(base, null /* statsToken */);
 
         // Send our spy window (app) into the system so that we can detect the invocation.
         final WindowState win = createWindow(null, TYPE_APPLICATION, "app");
@@ -445,7 +445,7 @@
 
         waitUntilHandlersIdle();
         clearInvocations(mDisplayContent);
-        imeSourceProvider.updateControlForTarget(app, false /* force */);
+        imeSourceProvider.updateControlForTarget(app, false /* force */, null /* statsToken */);
         imeSourceProvider.setClientVisible(true);
         verify(mDisplayContent).assignWindowLayers(anyBoolean());
         waitUntilHandlersIdle();
@@ -497,7 +497,7 @@
         mDisplayContent.updateImeInputAndControlTarget(app);
 
         app.setRequestedVisibleTypes(ime(), ime());
-        getController().onRequestedVisibleTypesChanged(app);
+        getController().onRequestedVisibleTypesChanged(app, null /* statsToken */);
         assertTrue(ime.getControllableInsetProvider().getSource().isVisible());
 
         getController().updateAboveInsetsState(true /* notifyInsetsChange */);
@@ -544,7 +544,7 @@
         imeInsetsProvider.setWindowContainer(mImeWindow, null, null);
         imeInsetsProvider.updateSourceFrame(mImeWindow.getFrame());
 
-        imeInsetsProvider.updateControlForTarget(app1, false);
+        imeInsetsProvider.updateControlForTarget(app1, false, null /* statsToken */);
         imeInsetsProvider.onPostLayout();
         final InsetsSourceControl control1 = imeInsetsProvider.getControl(app1);
         assertNotNull(control1);
@@ -553,7 +553,7 @@
 
         // Simulate the IME control target updated from app1 to app2 when IME insets was invisible.
         imeInsetsProvider.setServerVisible(false);
-        imeInsetsProvider.updateControlForTarget(app2, false);
+        imeInsetsProvider.updateControlForTarget(app2, false, null /* statsToken */);
 
         // Verify insetsHint of the new control is same as last IME source frame after the layout.
         imeInsetsProvider.onPostLayout();
diff --git a/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
index d318f00..33df5d8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
@@ -16,26 +16,9 @@
 
 package com.android.server.wm;
 
-import static android.content.pm.ActivityInfo.FORCE_NON_RESIZE_APP;
-import static android.content.pm.ActivityInfo.FORCE_RESIZE_APP;
-import static android.content.pm.ActivityInfo.OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS;
-import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO;
-import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA;
-import static android.content.pm.ActivityInfo.OVERRIDE_USE_DISPLAY_LANDSCAPE_NATURAL_ORIENTATION;
-import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_3_2;
-import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_FULLSCREEN;
-import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
-import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
 import static android.view.InsetsSource.FLAG_INSETS_ROUNDED_CORNER;
-import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_DISPLAY_ORIENTATION_OVERRIDE;
-import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE;
-import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES;
-import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_FULLSCREEN_OVERRIDE;
-import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE;
-import static android.view.WindowManager.PROPERTY_COMPAT_ENABLE_FAKE_FOCUS;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 
@@ -45,15 +28,12 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.annotation.Nullable;
 import android.compat.testing.PlatformCompatChangeRule;
 import android.content.ComponentName;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.Property;
 import android.content.res.Resources;
 import android.graphics.Rect;
 import android.platform.test.annotations.DisableFlags;
@@ -71,9 +51,6 @@
 import com.android.internal.R;
 import com.android.window.flags.Flags;
 
-import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges;
-import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
-
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -119,8 +96,6 @@
         mController = new LetterboxUiController(mWm, mActivity);
     }
 
-
-
     @Test
     public void testGetCropBoundsIfNeeded_handleCropForTransparentActivityBasedOnOpaqueBounds() {
         final InsetsSource taskbar = new InsetsSource(/*id=*/ 0,
@@ -320,583 +295,14 @@
         return mainWindow;
     }
 
-    // shouldApplyUser...Override
-    @Test
-    public void testShouldApplyUserFullscreenOverride_trueProperty_returnsFalse() throws Exception {
-        mockThatProperty(PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_FULLSCREEN_OVERRIDE,
-                /* value */ true);
-
-        doReturn(false).when(mAppCompatConfiguration).isUserAppAspectRatioFullscreenEnabled();
-        mActivity = setUpActivityWithComponent();
-
-        assertFalse(mActivity.mAppCompatController.getAppCompatAspectRatioOverrides()
-                .shouldApplyUserFullscreenOverride());
-    }
-
-    @Test
-    public void testShouldApplyUserFullscreenOverride_falseFullscreenProperty_returnsFalse()
-            throws Exception {
-        prepareActivityThatShouldApplyUserFullscreenOverride();
-        mockThatProperty(PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_FULLSCREEN_OVERRIDE,
-                /* value */ false);
-
-        mActivity = setUpActivityWithComponent();
-
-        assertFalse(mActivity.mAppCompatController.getAppCompatAspectRatioOverrides()
-                .shouldApplyUserFullscreenOverride());
-    }
-
-    @Test
-    public void testShouldApplyUserFullscreenOverride_falseSettingsProperty_returnsFalse()
-            throws Exception {
-        prepareActivityThatShouldApplyUserFullscreenOverride();
-        mockThatProperty(PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE, /* value */ false);
-
-        mActivity = setUpActivityWithComponent();
-
-        assertFalse(mActivity.mAppCompatController.getAppCompatAspectRatioOverrides()
-                .shouldApplyUserFullscreenOverride());
-    }
-
-    @Test
-    public void testShouldApplyUserFullscreenOverride_returnsTrue() {
-        prepareActivityThatShouldApplyUserFullscreenOverride();
-
-        assertTrue(mActivity.mAppCompatController.getAppCompatAspectRatioOverrides()
-                .shouldApplyUserFullscreenOverride());
-    }
-
-    @Test
-    public void testShouldEnableUserAspectRatioSettings_falseProperty_returnsFalse()
-            throws Exception {
-        prepareActivityThatShouldApplyUserMinAspectRatioOverride();
-        mockThatProperty(PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE, /* value */ false);
-
-        mActivity = setUpActivityWithComponent();
-        mController = new LetterboxUiController(mWm, mActivity);
-
-        assertFalse(mActivity.mAppCompatController.getAppCompatAspectRatioOverrides()
-                .shouldEnableUserAspectRatioSettings());
-    }
-
-    @Test
-    public void testShouldEnableUserAspectRatioSettings_trueProperty_returnsTrue()
-            throws Exception {
-
-        mockThatProperty(PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE, /* value */ true);
-        mActivity = setUpActivityWithComponent();
-        prepareActivityThatShouldApplyUserMinAspectRatioOverride();
-
-        mController = new LetterboxUiController(mWm, mActivity);
-
-        assertTrue(mActivity.mAppCompatController.getAppCompatAspectRatioOverrides()
-                .shouldEnableUserAspectRatioSettings());
-    }
-
-    @Test
-    public void testShouldEnableUserAspectRatioSettings_noIgnoreOrientation_returnsFalse()
-            throws Exception {
-        prepareActivityForShouldApplyUserMinAspectRatioOverride(/* orientationRequest */ false);
-        mockThatProperty(PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE, /* value */ true);
-
-        mController = new LetterboxUiController(mWm, mActivity);
-
-        assertFalse(mActivity.mAppCompatController.getAppCompatAspectRatioOverrides()
-                .shouldEnableUserAspectRatioSettings());
-    }
-
-    @Test
-    public void testShouldApplyUserMinAspectRatioOverride_falseProperty_returnsFalse()
-            throws Exception {
-        prepareActivityThatShouldApplyUserMinAspectRatioOverride();
-        mockThatProperty(PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE, /* value */ false);
-
-        mActivity = setUpActivityWithComponent();
-
-        assertFalse(mActivity.mAppCompatController.getAppCompatAspectRatioOverrides()
-                .shouldEnableUserAspectRatioSettings());
-    }
-
-    @Test
-    public void testShouldApplyUserMinAspectRatioOverride_trueProperty_returnsFalse()
-            throws Exception {
-        doReturn(false).when(mAppCompatConfiguration).isUserAppAspectRatioSettingsEnabled();
-        mockThatProperty(PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE, /* value */ true);
-
-        mActivity = setUpActivityWithComponent();
-
-        assertFalse(mActivity.mAppCompatController.getAppCompatAspectRatioOverrides()
-                .shouldApplyUserMinAspectRatioOverride());
-    }
-
-    @Test
-    public void testShouldApplyUserMinAspectRatioOverride_disabledIgnoreOrientationRequest() {
-        prepareActivityThatShouldApplyUserMinAspectRatioOverride();
-        mDisplayContent.setIgnoreOrientationRequest(false);
-
-        assertFalse(mActivity.mAppCompatController.getAppCompatAspectRatioOverrides()
-                .shouldApplyUserMinAspectRatioOverride());
-    }
-
-    @Test
-    public void testShouldApplyUserMinAspectRatioOverride_returnsTrue() {
-        prepareActivityThatShouldApplyUserMinAspectRatioOverride();
-
-        assertTrue(mActivity.mAppCompatController.getAppCompatAspectRatioOverrides()
-                .shouldApplyUserMinAspectRatioOverride());
-    }
-
-    @Test
-    public void testShouldApplyUserMinAspectRatioOverride_noIgnoreOrientation_returnsFalse() {
-        prepareActivityForShouldApplyUserMinAspectRatioOverride(/* orientationRequest */ false);
-
-        assertFalse(mActivity.mAppCompatController.getAppCompatAspectRatioOverrides()
-                .shouldApplyUserMinAspectRatioOverride());
-    }
-
-    private void prepareActivityForShouldApplyUserMinAspectRatioOverride(
-            boolean orientationRequest) {
-        spyOn(mActivity.mAppCompatController.getAppCompatAspectRatioOverrides());
-        doReturn(orientationRequest).when(
-                mAppCompatConfiguration).isUserAppAspectRatioSettingsEnabled();
-        mDisplayContent.setIgnoreOrientationRequest(true);
-        doReturn(USER_MIN_ASPECT_RATIO_3_2)
-                .when(mActivity.mAppCompatController.getAppCompatAspectRatioOverrides())
-                .getUserMinAspectRatioOverrideCode();
-    }
-
-    private void prepareActivityThatShouldApplyUserMinAspectRatioOverride() {
-        prepareActivityForShouldApplyUserMinAspectRatioOverride(/* orientationRequest */ true);
-    }
-
-    private void prepareActivityThatShouldApplyUserFullscreenOverride() {
-        spyOn(mActivity.mAppCompatController.getAppCompatAspectRatioOverrides());
-        doReturn(true).when(mAppCompatConfiguration).isUserAppAspectRatioFullscreenEnabled();
-        mDisplayContent.setIgnoreOrientationRequest(true);
-        doReturn(USER_MIN_ASPECT_RATIO_FULLSCREEN)
-                .when(mActivity.mAppCompatController.getAppCompatAspectRatioOverrides())
-                .getUserMinAspectRatioOverrideCode();
-    }
-
-    // shouldUseDisplayLandscapeNaturalOrientation
-
-    @Test
-    @EnableCompatChanges({OVERRIDE_USE_DISPLAY_LANDSCAPE_NATURAL_ORIENTATION})
-    public void testShouldUseDisplayLandscapeNaturalOrientation_override_returnsTrue() {
-        prepareActivityThatShouldUseDisplayLandscapeNaturalOrientation();
-        assertTrue(mController.shouldUseDisplayLandscapeNaturalOrientation());
-    }
-
-    @Test
-    @EnableCompatChanges({OVERRIDE_USE_DISPLAY_LANDSCAPE_NATURAL_ORIENTATION})
-    public void testShouldUseDisplayLandscapeNaturalOrientation_overrideAndFalseProperty_returnsFalse()
-            throws Exception {
-        mockThatProperty(PROPERTY_COMPAT_ALLOW_DISPLAY_ORIENTATION_OVERRIDE, /* value */ false);
-
-        mController = new LetterboxUiController(mWm, mActivity);
-
-        prepareActivityThatShouldUseDisplayLandscapeNaturalOrientation();
-        assertFalse(mController.shouldUseDisplayLandscapeNaturalOrientation());
-    }
-
-    @Test
-    @EnableCompatChanges({OVERRIDE_USE_DISPLAY_LANDSCAPE_NATURAL_ORIENTATION})
-    public void testShouldUseDisplayLandscapeNaturalOrientation_portraitNaturalOrientation_returnsFalse() {
-        prepareActivityThatShouldUseDisplayLandscapeNaturalOrientation();
-        doReturn(ORIENTATION_PORTRAIT).when(mDisplayContent).getNaturalOrientation();
-
-        assertFalse(mController.shouldUseDisplayLandscapeNaturalOrientation());
-    }
-
-    @Test
-    @EnableCompatChanges({OVERRIDE_USE_DISPLAY_LANDSCAPE_NATURAL_ORIENTATION})
-    public void testShouldUseDisplayLandscapeNaturalOrientation_disabledIgnoreOrientationRequest_returnsFalse() {
-        prepareActivityThatShouldUseDisplayLandscapeNaturalOrientation();
-        mDisplayContent.setIgnoreOrientationRequest(false);
-
-        assertFalse(mController.shouldUseDisplayLandscapeNaturalOrientation());
-    }
-
-    @Test
-    @EnableCompatChanges({OVERRIDE_USE_DISPLAY_LANDSCAPE_NATURAL_ORIENTATION})
-    public void testShouldUseDisplayLandscapeNaturalOrientation_inMultiWindowMode_returnsFalse() {
-        prepareActivityThatShouldUseDisplayLandscapeNaturalOrientation();
-
-        spyOn(mTask);
-        doReturn(true).when(mTask).inMultiWindowMode();
-
-        assertFalse(mController.shouldUseDisplayLandscapeNaturalOrientation());
-    }
-
-    @Test
-    @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS})
-    public void testShouldSendFakeFocus_overrideEnabled_returnsTrue() {
-        doReturn(true).when(mAppCompatConfiguration).isCompatFakeFocusEnabled();
-
-        mController = new LetterboxUiController(mWm, mActivity);
-
-        assertTrue(mController.shouldSendFakeFocus());
-    }
-
-    @Test
-    @DisableCompatChanges({OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS})
-    public void testShouldSendFakeFocus_overrideDisabled_returnsFalse() {
-        doReturn(true).when(mAppCompatConfiguration).isCompatFakeFocusEnabled();
-
-        mController = new LetterboxUiController(mWm, mActivity);
-
-        assertFalse(mController.shouldSendFakeFocus());
-    }
-
-    @Test
-    @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS})
-    public void testIsCompatFakeFocusEnabled_propertyDisabledAndOverrideEnabled_fakeFocusDisabled()
-            throws Exception {
-        doReturn(true).when(mAppCompatConfiguration).isCompatFakeFocusEnabled();
-        mockThatProperty(PROPERTY_COMPAT_ENABLE_FAKE_FOCUS, /* value */ false);
-
-        mController = new LetterboxUiController(mWm, mActivity);
-
-        assertFalse(mController.shouldSendFakeFocus());
-    }
-
-    @Test
-    @DisableCompatChanges({OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS})
-    public void testIsCompatFakeFocusEnabled_propertyEnabled_noOverride_fakeFocusEnabled()
-            throws Exception {
-        doReturn(true).when(mAppCompatConfiguration).isCompatFakeFocusEnabled();
-        mockThatProperty(PROPERTY_COMPAT_ENABLE_FAKE_FOCUS, /* value */ true);
-
-        mController = new LetterboxUiController(mWm, mActivity);
-
-        assertTrue(mController.shouldSendFakeFocus());
-    }
-
-    @Test
-    public void testIsCompatFakeFocusEnabled_propertyDisabled_fakeFocusDisabled()
-            throws Exception {
-        doReturn(true).when(mAppCompatConfiguration).isCompatFakeFocusEnabled();
-        mockThatProperty(PROPERTY_COMPAT_ENABLE_FAKE_FOCUS, /* value */ false);
-
-        mController = new LetterboxUiController(mWm, mActivity);
-
-        assertFalse(mController.shouldSendFakeFocus());
-    }
-
-    @Test
-    public void testIsCompatFakeFocusEnabled_propertyEnabled_fakeFocusEnabled()
-            throws Exception {
-        doReturn(true).when(mAppCompatConfiguration).isCompatFakeFocusEnabled();
-        mockThatProperty(PROPERTY_COMPAT_ENABLE_FAKE_FOCUS, /* value */ true);
-
-        mController = new LetterboxUiController(mWm, mActivity);
-
-        assertTrue(mController.shouldSendFakeFocus());
-    }
-
-    @Test
-    @EnableCompatChanges({OVERRIDE_MIN_ASPECT_RATIO})
-    public void testshouldOverrideMinAspectRatio_overrideEnabled_returnsTrue() {
-        mActivity = setUpActivityWithComponent();
-
-        assertTrue(mActivity.mAppCompatController.getAppCompatAspectRatioOverrides()
-                .shouldOverrideMinAspectRatio());
-    }
-
-    @Test
-    @EnableCompatChanges({OVERRIDE_MIN_ASPECT_RATIO})
-    public void testshouldOverrideMinAspectRatio_propertyTrue_overrideEnabled_returnsTrue()
-            throws Exception {
-        mockThatProperty(PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE, /* value */ true);
-        mActivity = setUpActivityWithComponent();
-
-        assertTrue(mActivity.mAppCompatController.getAppCompatAspectRatioOverrides()
-                .shouldOverrideMinAspectRatio());
-    }
-
-    @Test
-    @DisableCompatChanges({OVERRIDE_MIN_ASPECT_RATIO})
-    public void testshouldOverrideMinAspectRatio_propertyTrue_overrideDisabled_returnsFalse()
-            throws Exception {
-        mockThatProperty(PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE, /* value */ true);
-        mActivity = setUpActivityWithComponent();
-
-        assertFalse(mActivity.mAppCompatController.getAppCompatAspectRatioOverrides()
-                .shouldOverrideMinAspectRatio());
-    }
-
-    @Test
-    @DisableCompatChanges({OVERRIDE_MIN_ASPECT_RATIO})
-    public void testshouldOverrideMinAspectRatio_overrideDisabled_returnsFalse() {
-        mActivity = setUpActivityWithComponent();
-
-        assertFalse(mActivity.mAppCompatController.getAppCompatAspectRatioOverrides()
-                .shouldOverrideMinAspectRatio());
-    }
-
-    @Test
-    @EnableCompatChanges({OVERRIDE_MIN_ASPECT_RATIO})
-    public void testshouldOverrideMinAspectRatio_propertyFalse_overrideEnabled_returnsFalse()
-            throws Exception {
-        mockThatProperty(PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE, /* value */ false);
-
-        mActivity = setUpActivityWithComponent();
-
-        assertFalse(mActivity.mAppCompatController.getAppCompatAspectRatioOverrides()
-                .shouldOverrideMinAspectRatio());
-    }
-
-    @Test
-    @DisableCompatChanges({OVERRIDE_MIN_ASPECT_RATIO})
-    public void testshouldOverrideMinAspectRatio_propertyFalse_noOverride_returnsFalse()
-            throws Exception {
-        mockThatProperty(PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE, /* value */ false);
-        mActivity = setUpActivityWithComponent();
-
-        assertFalse(mActivity.mAppCompatController.getAppCompatAspectRatioOverrides()
-                .shouldOverrideMinAspectRatio());
-    }
-
-    @Test
-    @EnableCompatChanges({OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA})
-    public void shouldOverrideMinAspectRatioForCamera_overrideEnabled_returnsTrue() {
-        mActivity = setUpActivityWithComponent();
-        doReturn(true).when(mActivity.mAppCompatController
-                .getAppCompatCameraOverrides()).isCameraActive();
-
-        assertTrue(mActivity.mAppCompatController.getAppCompatCameraOverrides()
-                .shouldOverrideMinAspectRatioForCamera());
-    }
-
-    @Test
-    @EnableCompatChanges({OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA})
-    public void shouldOverrideMinAspectRatioForCamera_propertyTrue_overrideEnabled_returnsTrue()
-            throws Exception {
-        mockThatProperty(PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE, /* value */ true);
-        mActivity = setUpActivityWithComponent();
-        doReturn(true).when(mActivity.mAppCompatController
-                .getAppCompatCameraOverrides()).isCameraActive();
-
-        assertTrue(mActivity.mAppCompatController.getAppCompatCameraOverrides()
-                .shouldOverrideMinAspectRatioForCamera());
-    }
-
-    @Test
-    @EnableCompatChanges({OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA})
-    public void shouldOverrideMinAspectRatioForCamera_propertyTrue_overrideEnabled_returnsFalse()
-            throws Exception {
-        mockThatProperty(PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE, /* value */ true);
-        mActivity = setUpActivityWithComponent();
-        doReturn(false).when(mActivity.mAppCompatController
-                .getAppCompatCameraOverrides()).isCameraActive();
-
-        assertFalse(mActivity.mAppCompatController.getAppCompatCameraOverrides()
-                .shouldOverrideMinAspectRatioForCamera());
-    }
-
-    @Test
-    @DisableCompatChanges({OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA})
-    public void shouldOverrideMinAspectRatioForCamera_propertyTrue_overrideDisabled_returnsFalse()
-            throws Exception {
-        mockThatProperty(PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE, /* value */ true);
-        mActivity = setUpActivityWithComponent();
-        doReturn(true).when(mActivity.mAppCompatController
-                .getAppCompatCameraOverrides()).isCameraActive();
-
-        assertFalse(mActivity.mAppCompatController.getAppCompatCameraOverrides()
-                .shouldOverrideMinAspectRatioForCamera());
-    }
-
-    @Test
-    @DisableCompatChanges({OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA})
-    public void shouldOverrideMinAspectRatioForCamera_overrideDisabled_returnsFalse() {
-        mActivity = setUpActivityWithComponent();
-        doReturn(true).when(mActivity.mAppCompatController
-                .getAppCompatCameraOverrides()).isCameraActive();
-
-        assertFalse(mActivity.mAppCompatController.getAppCompatCameraOverrides()
-                .shouldOverrideMinAspectRatioForCamera());
-    }
-
-    @Test
-    @EnableCompatChanges({OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA})
-    public void shouldOverrideMinAspectRatioForCamera_propertyFalse_overrideEnabled_returnsFalse()
-            throws Exception {
-        mockThatProperty(PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE, /* value */ false);
-        mActivity = setUpActivityWithComponent();
-
-        assertFalse(mActivity.mAppCompatController.getAppCompatCameraOverrides()
-                .shouldOverrideMinAspectRatioForCamera());
-    }
-
-    @Test
-    @DisableCompatChanges({OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA})
-    public void shouldOverrideMinAspectRatioForCamera_propertyFalse_noOverride_returnsFalse()
-            throws Exception {
-        mockThatProperty(PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE, /* value */ false);
-
-        mActivity = setUpActivityWithComponent();
-
-        doReturn(true).when(mActivity.mAppCompatController
-                .getAppCompatCameraOverrides()).isCameraActive();
-
-        assertFalse(mActivity.mAppCompatController.getAppCompatCameraOverrides()
-                .shouldOverrideMinAspectRatioForCamera());
-    }
-
-    @Test
-    @EnableCompatChanges({FORCE_RESIZE_APP})
-    public void testshouldOverrideForceResizeApp_overrideEnabled_returnsTrue() {
-        mController = new LetterboxUiController(mWm, mActivity);
-
-        assertTrue(mController.shouldOverrideForceResizeApp());
-    }
-
-    @Test
-    @EnableCompatChanges({FORCE_RESIZE_APP})
-    public void testshouldOverrideForceResizeApp_propertyTrue_overrideEnabled_returnsTrue()
-            throws Exception {
-        mockThatProperty(PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES, /* value */ true);
-        mController = new LetterboxUiController(mWm, mActivity);
-
-        assertTrue(mController.shouldOverrideForceResizeApp());
-    }
-
-    @Test
-    @DisableCompatChanges({FORCE_RESIZE_APP})
-    public void testshouldOverrideForceResizeApp_propertyTrue_overrideDisabled_returnsFalse()
-            throws Exception {
-        mockThatProperty(PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES, /* value */ true);
-        mController = new LetterboxUiController(mWm, mActivity);
-
-        assertFalse(mController.shouldOverrideForceResizeApp());
-    }
-
-    @Test
-    @DisableCompatChanges({FORCE_RESIZE_APP})
-    public void testshouldOverrideForceResizeApp_overrideDisabled_returnsFalse() {
-        mController = new LetterboxUiController(mWm, mActivity);
-
-        assertFalse(mController.shouldOverrideForceResizeApp());
-    }
-
-    @Test
-    @EnableCompatChanges({FORCE_RESIZE_APP})
-    public void testshouldOverrideForceResizeApp_propertyFalse_overrideEnabled_returnsFalse()
-            throws Exception {
-        mockThatProperty(PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES, /* value */ false);
-
-        mActivity = setUpActivityWithComponent();
-        mController = new LetterboxUiController(mWm, mActivity);
-
-        assertFalse(mController.shouldOverrideForceResizeApp());
-    }
-
-    @Test
-    @DisableCompatChanges({FORCE_RESIZE_APP})
-    public void testshouldOverrideForceResizeApp_propertyFalse_noOverride_returnsFalse()
-            throws Exception {
-        mockThatProperty(PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES, /* value */ false);
-        mController = new LetterboxUiController(mWm, mActivity);
-
-        assertFalse(mController.shouldOverrideForceResizeApp());
-    }
-
-    @Test
-    @EnableCompatChanges({FORCE_NON_RESIZE_APP})
-    public void testshouldOverrideForceNonResizeApp_overrideEnabled_returnsTrue() {
-        mController = new LetterboxUiController(mWm, mActivity);
-
-        assertTrue(mController.shouldOverrideForceNonResizeApp());
-    }
-
-    @Test
-    @EnableCompatChanges({FORCE_NON_RESIZE_APP})
-    public void testshouldOverrideForceNonResizeApp_propertyTrue_overrideEnabled_returnsTrue()
-            throws Exception {
-        mockThatProperty(PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES, /* value */ true);
-        mController = new LetterboxUiController(mWm, mActivity);
-
-        assertTrue(mController.shouldOverrideForceNonResizeApp());
-    }
-
-    @Test
-    @DisableCompatChanges({FORCE_NON_RESIZE_APP})
-    public void testshouldOverrideForceNonResizeApp_propertyTrue_overrideDisabled_returnsFalse()
-            throws Exception {
-        mockThatProperty(PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES, /* value */ true);
-        mController = new LetterboxUiController(mWm, mActivity);
-
-        assertFalse(mController.shouldOverrideForceNonResizeApp());
-    }
-
-    @Test
-    @DisableCompatChanges({FORCE_NON_RESIZE_APP})
-    public void testshouldOverrideForceNonResizeApp_overrideDisabled_returnsFalse() {
-        mController = new LetterboxUiController(mWm, mActivity);
-
-        assertFalse(mController.shouldOverrideForceNonResizeApp());
-    }
-
-    @Test
-    @EnableCompatChanges({FORCE_NON_RESIZE_APP})
-    public void testshouldOverrideForceNonResizeApp_propertyFalse_overrideEnabled_returnsFalse()
-            throws Exception {
-        mockThatProperty(PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES, /* value */ false);
-
-        mActivity = setUpActivityWithComponent();
-        mController = new LetterboxUiController(mWm, mActivity);
-
-        assertFalse(mController.shouldOverrideForceNonResizeApp());
-    }
-
-    @Test
-    @DisableCompatChanges({FORCE_NON_RESIZE_APP})
-    public void testshouldOverrideForceNonResizeApp_propertyFalse_noOverride_returnsFalse()
-            throws Exception {
-        mockThatProperty(PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES, /* value */ false);
-        mController = new LetterboxUiController(mWm, mActivity);
-
-        assertFalse(mController.shouldOverrideForceNonResizeApp());
-    }
-
-    @Test
-    public void testgetFixedOrientationLetterboxAspectRatio_splitScreenAspectEnabled() {
-        doReturn(true).when(mActivity.mWmService.mAppCompatConfiguration)
-                .isCameraCompatTreatmentEnabled();
-        doReturn(true).when(mActivity.mWmService.mAppCompatConfiguration)
-                .isCameraCompatTreatmentEnabledAtBuildTime();
-        doReturn(true).when(mActivity.mWmService.mAppCompatConfiguration)
-                .isCameraCompatSplitScreenAspectRatioEnabled();
-        doReturn(false).when(mActivity.mWmService.mAppCompatConfiguration)
-                .getIsDisplayAspectRatioEnabledForFixedOrientationLetterbox();
-        doReturn(1.5f).when(mActivity.mWmService.mAppCompatConfiguration)
-                .getFixedOrientationLetterboxAspectRatio();
-
-        // Recreate DisplayContent with DisplayRotationCompatPolicy
-        mActivity = setUpActivityWithComponent();
-        mController = new LetterboxUiController(mWm, mActivity);
-
-        assertEquals(1.5f, mController.getFixedOrientationLetterboxAspectRatio(
-                mActivity.getParent().getConfiguration()), /* delta */ 0.01);
-
-        spyOn(mDisplayContent.mAppCompatCameraPolicy);
-        doReturn(true).when(mDisplayContent.mAppCompatCameraPolicy)
-                .isTreatmentEnabledForActivity(eq(mActivity));
-
-        final AppCompatAspectRatioOverrides aspectRatioOverrides =
-                mActivity.mAppCompatController.getAppCompatAspectRatioOverrides();
-        assertEquals(aspectRatioOverrides.getSplitScreenAspectRatio(),
-                aspectRatioOverrides.getFixedOrientationLetterboxAspectRatio(
-                        mActivity.getParent().getConfiguration()), /* delta */  0.01);
-    }
-
     @Test
     public void testIsVerticalThinLetterboxed() {
         // Vertical thin letterbox disabled
         doReturn(-1).when(mActivity.mWmService.mAppCompatConfiguration)
                 .getThinLetterboxHeightPx();
-        assertFalse(mController.isVerticalThinLetterboxed());
+        final AppCompatReachabilityOverrides reachabilityOverrides = mActivity.mAppCompatController
+                .getAppCompatReachabilityOverrides();
+        assertFalse(reachabilityOverrides.isVerticalThinLetterboxed());
         // Define a Task 100x100
         final Task task = mock(Task.class);
         doReturn(new Rect(0, 0, 100, 100)).when(task).getBounds();
@@ -905,21 +311,21 @@
 
         // Vertical thin letterbox disabled without Task
         doReturn(null).when(mActivity).getTask();
-        assertFalse(mController.isVerticalThinLetterboxed());
+        assertFalse(reachabilityOverrides.isVerticalThinLetterboxed());
         // Assign a Task for the Activity
         doReturn(task).when(mActivity).getTask();
 
         // (task.width() - act.width()) / 2  = 5 < 10
         doReturn(new Rect(5, 5, 95, 95)).when(mActivity).getBounds();
-        assertTrue(mController.isVerticalThinLetterboxed());
+        assertTrue(reachabilityOverrides.isVerticalThinLetterboxed());
 
         // (task.width() - act.width()) / 2  = 10 = 10
         doReturn(new Rect(10, 10, 90, 90)).when(mActivity).getBounds();
-        assertTrue(mController.isVerticalThinLetterboxed());
+        assertTrue(reachabilityOverrides.isVerticalThinLetterboxed());
 
         // (task.width() - act.width()) / 2  = 11 > 10
         doReturn(new Rect(11, 11, 89, 89)).when(mActivity).getBounds();
-        assertFalse(mController.isVerticalThinLetterboxed());
+        assertFalse(reachabilityOverrides.isVerticalThinLetterboxed());
     }
 
     @Test
@@ -927,7 +333,9 @@
         // Horizontal thin letterbox disabled
         doReturn(-1).when(mActivity.mWmService.mAppCompatConfiguration)
                 .getThinLetterboxWidthPx();
-        assertFalse(mController.isHorizontalThinLetterboxed());
+        final AppCompatReachabilityOverrides reachabilityOverrides = mActivity.mAppCompatController
+                .getAppCompatReachabilityOverrides();
+        assertFalse(reachabilityOverrides.isHorizontalThinLetterboxed());
         // Define a Task 100x100
         final Task task = mock(Task.class);
         doReturn(new Rect(0, 0, 100, 100)).when(task).getBounds();
@@ -936,51 +344,55 @@
 
         // Vertical thin letterbox disabled without Task
         doReturn(null).when(mActivity).getTask();
-        assertFalse(mController.isHorizontalThinLetterboxed());
+        assertFalse(reachabilityOverrides.isHorizontalThinLetterboxed());
         // Assign a Task for the Activity
         doReturn(task).when(mActivity).getTask();
 
         // (task.height() - act.height()) / 2  = 5 < 10
         doReturn(new Rect(5, 5, 95, 95)).when(mActivity).getBounds();
-        assertTrue(mController.isHorizontalThinLetterboxed());
+        assertTrue(reachabilityOverrides.isHorizontalThinLetterboxed());
 
         // (task.height() - act.height()) / 2  = 10 = 10
         doReturn(new Rect(10, 10, 90, 90)).when(mActivity).getBounds();
-        assertTrue(mController.isHorizontalThinLetterboxed());
+        assertTrue(reachabilityOverrides.isHorizontalThinLetterboxed());
 
         // (task.height() - act.height()) / 2  = 11 > 10
         doReturn(new Rect(11, 11, 89, 89)).when(mActivity).getBounds();
-        assertFalse(mController.isHorizontalThinLetterboxed());
+        assertFalse(reachabilityOverrides.isHorizontalThinLetterboxed());
     }
 
     @Test
     @EnableFlags(Flags.FLAG_DISABLE_THIN_LETTERBOXING_POLICY)
     public void testAllowReachabilityForThinLetterboxWithFlagEnabled() {
-        spyOn(mController);
-        doReturn(true).when(mController).isVerticalThinLetterboxed();
-        assertFalse(mController.allowVerticalReachabilityForThinLetterbox());
-        doReturn(true).when(mController).isHorizontalThinLetterboxed();
-        assertFalse(mController.allowHorizontalReachabilityForThinLetterbox());
+        final AppCompatReachabilityOverrides reachabilityOverrides =
+                mActivity.mAppCompatController.getAppCompatReachabilityOverrides();
+        spyOn(reachabilityOverrides);
+        doReturn(true).when(reachabilityOverrides).isVerticalThinLetterboxed();
+        assertFalse(reachabilityOverrides.allowVerticalReachabilityForThinLetterbox());
+        doReturn(true).when(reachabilityOverrides).isHorizontalThinLetterboxed();
+        assertFalse(reachabilityOverrides.allowHorizontalReachabilityForThinLetterbox());
 
-        doReturn(false).when(mController).isVerticalThinLetterboxed();
-        assertTrue(mController.allowVerticalReachabilityForThinLetterbox());
-        doReturn(false).when(mController).isHorizontalThinLetterboxed();
-        assertTrue(mController.allowHorizontalReachabilityForThinLetterbox());
+        doReturn(false).when(reachabilityOverrides).isVerticalThinLetterboxed();
+        assertTrue(reachabilityOverrides.allowVerticalReachabilityForThinLetterbox());
+        doReturn(false).when(reachabilityOverrides).isHorizontalThinLetterboxed();
+        assertTrue(reachabilityOverrides.allowHorizontalReachabilityForThinLetterbox());
     }
 
     @Test
     @DisableFlags(Flags.FLAG_DISABLE_THIN_LETTERBOXING_POLICY)
     public void testAllowReachabilityForThinLetterboxWithFlagDisabled() {
-        spyOn(mController);
-        doReturn(true).when(mController).isVerticalThinLetterboxed();
-        assertTrue(mController.allowVerticalReachabilityForThinLetterbox());
-        doReturn(true).when(mController).isHorizontalThinLetterboxed();
-        assertTrue(mController.allowHorizontalReachabilityForThinLetterbox());
+        final AppCompatReachabilityOverrides reachabilityOverrides =
+                mActivity.mAppCompatController.getAppCompatReachabilityOverrides();
+        spyOn(reachabilityOverrides);
+        doReturn(true).when(reachabilityOverrides).isVerticalThinLetterboxed();
+        assertTrue(reachabilityOverrides.allowVerticalReachabilityForThinLetterbox());
+        doReturn(true).when(reachabilityOverrides).isHorizontalThinLetterboxed();
+        assertTrue(reachabilityOverrides.allowHorizontalReachabilityForThinLetterbox());
 
-        doReturn(false).when(mController).isVerticalThinLetterboxed();
-        assertTrue(mController.allowVerticalReachabilityForThinLetterbox());
-        doReturn(false).when(mController).isHorizontalThinLetterboxed();
-        assertTrue(mController.allowHorizontalReachabilityForThinLetterbox());
+        doReturn(false).when(reachabilityOverrides).isVerticalThinLetterboxed();
+        assertTrue(reachabilityOverrides.allowVerticalReachabilityForThinLetterbox());
+        doReturn(false).when(reachabilityOverrides).isHorizontalThinLetterboxed();
+        assertTrue(reachabilityOverrides.allowHorizontalReachabilityForThinLetterbox());
     }
 
     @Test
@@ -989,20 +401,6 @@
         verify(mAppCompatConfiguration).getIsEducationEnabled();
     }
 
-    private void mockThatProperty(String propertyName, boolean value) throws Exception {
-        Property property = new Property(propertyName, /* value */ value, /* packageName */ "",
-                /* className */ "");
-        PackageManager pm = mWm.mContext.getPackageManager();
-        spyOn(pm);
-        doReturn(property).when(pm).getProperty(eq(propertyName), anyString());
-    }
-
-    private void prepareActivityThatShouldUseDisplayLandscapeNaturalOrientation() {
-        spyOn(mDisplayContent);
-        doReturn(ORIENTATION_LANDSCAPE).when(mDisplayContent).getNaturalOrientation();
-        mDisplayContent.setIgnoreOrientationRequest(true);
-    }
-
     private ActivityRecord setUpActivityWithComponent() {
         mDisplayContent = new TestDisplayContent
                 .Builder(mAtm, /* dw */ 1000, /* dh */ 2000).build();
diff --git a/services/tests/wmtests/src/com/android/server/wm/PhysicalDisplaySwitchTransitionLauncherTest.java b/services/tests/wmtests/src/com/android/server/wm/PhysicalDisplaySwitchTransitionLauncherTest.java
deleted file mode 100644
index 78509db..0000000
--- a/services/tests/wmtests/src/com/android/server/wm/PhysicalDisplaySwitchTransitionLauncherTest.java
+++ /dev/null
@@ -1,283 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm;
-
-import static com.android.internal.R.bool.config_unfoldTransitionEnabled;
-import static com.android.server.wm.DeviceStateController.DeviceState.REAR;
-import static com.android.server.wm.DeviceStateController.DeviceState.FOLDED;
-import static com.android.server.wm.DeviceStateController.DeviceState.HALF_FOLDED;
-import static com.android.server.wm.DeviceStateController.DeviceState.OPEN;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.when;
-
-import android.animation.ValueAnimator;
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Rect;
-import android.platform.test.annotations.Presubmit;
-
-import androidx.test.filters.SmallTest;
-
-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;
-
-/**
- * Tests for the {@link WindowToken} class.
- *
- * Build/Install/Run:
- * atest WmTests:PhysicalDisplaySwitchTransitionLauncherTest
- */
-@SmallTest
-@Presubmit
-@RunWith(WindowTestRunner.class)
-public class PhysicalDisplaySwitchTransitionLauncherTest extends WindowTestsBase {
-
-    @Mock
-    Context mContext;
-    @Mock
-    Resources mResources;
-    @Mock
-    BLASTSyncEngine mSyncEngine;
-
-    WindowTestsBase.TestTransitionPlayer mPlayer;
-    TransitionController mTransitionController;
-    DisplayContent mDisplayContent;
-
-    private PhysicalDisplaySwitchTransitionLauncher mTarget;
-    private float mOriginalAnimationScale;
-
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-        mTransitionController = new WindowTestsBase.TestTransitionController(mAtm);
-        mTransitionController.setSyncEngine(mSyncEngine);
-        mPlayer = new WindowTestsBase.TestTransitionPlayer(
-                mTransitionController, mAtm.mWindowOrganizerController);
-        when(mContext.getResources()).thenReturn(mResources);
-        mDisplayContent = new TestDisplayContent.Builder(mAtm, 100, 150).build();
-        mTarget = new PhysicalDisplaySwitchTransitionLauncher(mDisplayContent, mAtm, mContext,
-                mTransitionController);
-        mOriginalAnimationScale = ValueAnimator.getDurationScale();
-    }
-
-    @After
-    public void after() {
-        ValueAnimator.setDurationScale(mOriginalAnimationScale);
-    }
-
-    @Test
-    public void testDisplaySwitchAfterUnfoldToOpen_animationsEnabled_requestsTransition() {
-        givenAllAnimationsEnabled();
-        mTarget.foldStateChanged(FOLDED);
-
-        mTarget.foldStateChanged(OPEN);
-        final Rect origBounds = new Rect();
-        mDisplayContent.getBounds(origBounds);
-        origBounds.offsetTo(0, 0);
-        mTarget.requestDisplaySwitchTransitionIfNeeded(
-                mDisplayContent.getDisplayId(),
-                origBounds.width(),
-                origBounds.height(),
-                /* newDisplayWidth= */ 200,
-                /* newDisplayHeight= */ 250
-        );
-
-        assertNotNull(mPlayer.mLastRequest);
-        assertEquals(mDisplayContent.getDisplayId(),
-                mPlayer.mLastRequest.getDisplayChange().getDisplayId());
-        assertEquals(origBounds, mPlayer.mLastRequest.getDisplayChange().getStartAbsBounds());
-        assertEquals(new Rect(0, 0, 200, 250),
-                mPlayer.mLastRequest.getDisplayChange().getEndAbsBounds());
-    }
-
-    @Test
-    public void testDisplaySwitchAfterFolding_animationEnabled_doesNotRequestTransition() {
-        givenAllAnimationsEnabled();
-        mTarget.foldStateChanged(OPEN);
-
-        mTarget.foldStateChanged(FOLDED);
-        requestDisplaySwitch();
-
-        assertTransitionNotRequested();
-    }
-
-    @Test
-    public void testDisplaySwitchAfterUnfoldingToHalf_animationEnabled_requestsTransition() {
-        givenAllAnimationsEnabled();
-        mTarget.foldStateChanged(FOLDED);
-
-        mTarget.foldStateChanged(HALF_FOLDED);
-        requestDisplaySwitch();
-
-        assertTransitionRequested();
-    }
-
-    @Test
-    public void testDisplaySwitchSecondTimeAfterUnfolding_animationEnabled_noTransition() {
-        givenAllAnimationsEnabled();
-        mTarget.foldStateChanged(FOLDED);
-        mTarget.foldStateChanged(OPEN);
-        requestDisplaySwitch();
-        mPlayer.mLastRequest = null;
-
-        requestDisplaySwitch();
-
-        assertTransitionNotRequested();
-    }
-
-
-    @Test
-    public void testDisplaySwitchAfterGoingToRearAndBack_animationEnabled_noTransition() {
-        givenAllAnimationsEnabled();
-        mTarget.foldStateChanged(OPEN);
-
-        mTarget.foldStateChanged(REAR);
-        mTarget.foldStateChanged(OPEN);
-        requestDisplaySwitch();
-
-        assertTransitionNotRequested();
-    }
-
-    @Test
-    public void testDisplaySwitchAfterUnfoldingAndFolding_animationEnabled_noTransition() {
-        givenAllAnimationsEnabled();
-        mTarget.foldStateChanged(FOLDED);
-        mTarget.foldStateChanged(OPEN);
-        // No request display switch event (simulate very fast fold after unfold, even before
-        // the displays switched)
-        mTarget.foldStateChanged(FOLDED);
-
-        requestDisplaySwitch();
-
-        assertTransitionNotRequested();
-    }
-
-    @Test
-    public void testDisplaySwitch_whenShellTransitionsNotEnabled_noTransition() {
-        givenAllAnimationsEnabled();
-        givenShellTransitionsEnabled(false);
-        mTarget.foldStateChanged(FOLDED);
-
-        mTarget.foldStateChanged(OPEN);
-        requestDisplaySwitch();
-
-        assertTransitionNotRequested();
-    }
-
-    @Test
-    public void testDisplaySwitch_whenAnimationsDisabled_noTransition() {
-        givenAllAnimationsEnabled();
-        givenAnimationsEnabled(false);
-        mTarget.foldStateChanged(FOLDED);
-
-        mTarget.foldStateChanged(OPEN);
-        requestDisplaySwitch();
-
-        assertTransitionNotRequested();
-    }
-
-    @Test
-    public void testDisplaySwitch_whenUnfoldAnimationDisabled_noTransition() {
-        givenAllAnimationsEnabled();
-        givenUnfoldTransitionEnabled(false);
-        mTarget.foldStateChanged(FOLDED);
-
-        mTarget.foldStateChanged(OPEN);
-        requestDisplaySwitch();
-
-        assertTransitionNotRequested();
-    }
-
-    @Test
-    public void testDisplaySwitchAfterUnfolding_otherCollectingTransition_collectsDisplaySwitch() {
-        givenAllAnimationsEnabled();
-        mTarget.foldStateChanged(FOLDED);
-
-        mTarget.foldStateChanged(OPEN);
-        requestDisplaySwitch();
-
-        // Collects to the current transition
-        assertTrue(mTransitionController.getCollectingTransition().mParticipants.contains(
-                mDisplayContent));
-    }
-
-
-    @Test
-    public void testDisplaySwitch_whenNoContentInDisplayContent_noTransition() {
-        givenAllAnimationsEnabled();
-        givenDisplayContentHasContent(false);
-        mTarget.foldStateChanged(FOLDED);
-
-        mTarget.foldStateChanged(OPEN);
-        requestDisplaySwitch();
-
-        assertTransitionNotRequested();
-    }
-
-    private void assertTransitionRequested() {
-        assertNotNull(mPlayer.mLastRequest);
-    }
-
-    private void assertTransitionNotRequested() {
-        assertNull(mPlayer.mLastRequest);
-    }
-
-    private void requestDisplaySwitch() {
-        mTarget.requestDisplaySwitchTransitionIfNeeded(
-                mDisplayContent.getDisplayId(),
-                mDisplayContent.getBounds().width(),
-                mDisplayContent.getBounds().height(),
-                /* newDisplayWidth= */ 200,
-                /* newDisplayHeight= */ 250
-        );
-    }
-
-    private void givenAllAnimationsEnabled() {
-        givenAnimationsEnabled(true);
-        givenUnfoldTransitionEnabled(true);
-        givenShellTransitionsEnabled(true);
-        givenDisplayContentHasContent(true);
-    }
-
-    private void givenUnfoldTransitionEnabled(boolean enabled) {
-        when(mResources.getBoolean(config_unfoldTransitionEnabled)).thenReturn(enabled);
-    }
-
-    private void givenAnimationsEnabled(boolean enabled) {
-        ValueAnimator.setDurationScale(enabled ? 1.0f : 0.0f);
-    }
-
-    private void givenShellTransitionsEnabled(boolean enabled) {
-        if (enabled) {
-            mTransitionController.registerTransitionPlayer(mPlayer, null /* proc */);
-        } else {
-            mTransitionController.unregisterTransitionPlayer(mPlayer);
-        }
-    }
-
-    private void givenDisplayContentHasContent(boolean hasContent) {
-        when(mDisplayContent.getLastHasContent()).thenReturn(hasContent);
-    }
-}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ScreenshotTests.java b/services/tests/wmtests/src/com/android/server/wm/ScreenshotTests.java
index 280fe4c..34f7ebb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ScreenshotTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ScreenshotTests.java
@@ -66,6 +66,7 @@
 import org.junit.Test;
 import org.junit.rules.TestName;
 
+import java.time.Duration;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
@@ -131,7 +132,7 @@
         assertTrue("Failed to wait for transaction to get committed",
                 countDownLatch.await(WAIT_TIME_S, TimeUnit.SECONDS));
         assertTrue("Failed to wait for stable geometry",
-                waitForStableWindowGeometry(WAIT_TIME_S, TimeUnit.SECONDS));
+                waitForStableWindowGeometry(Duration.ofSeconds(WAIT_TIME_S)));
 
         ScreenCapture.LayerCaptureArgs args = new ScreenCapture.LayerCaptureArgs.Builder(secureSC)
                 .setCaptureSecureLayers(true)
@@ -212,7 +213,7 @@
         assertTrue("Failed to wait for transaction to get committed",
                 countDownLatch.await(WAIT_TIME_S, TimeUnit.SECONDS));
         assertTrue("Failed to wait for stable geometry",
-                waitForStableWindowGeometry(WAIT_TIME_S, TimeUnit.SECONDS));
+                waitForStableWindowGeometry(Duration.ofSeconds(WAIT_TIME_S)));
 
         ScreenshotHardwareBuffer[] screenCapture = new ScreenshotHardwareBuffer[1];
         Bitmap screenshot = null;
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 8981f71..7dc3b07 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -19,7 +19,6 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
-import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
@@ -789,7 +788,7 @@
         // Change the fixed orientation.
         mActivity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
         assertTrue(mActivity.isRelaunching());
-        assertTrue(mActivity.mLetterboxUiController
+        assertTrue(mActivity.mAppCompatController.getAppCompatOrientationOverrides()
                 .getIsRelaunchingAfterRequestedOrientationChanged());
 
         assertFitted();
@@ -3432,9 +3431,10 @@
         mActivity.getWindowConfiguration().setBounds(null);
 
         setUpAllowThinLetterboxed(/* thinLetterboxAllowed */ false);
-
-        assertFalse(mActivity.mLetterboxUiController.isVerticalReachabilityEnabled());
-        assertFalse(mActivity.mLetterboxUiController.isHorizontalReachabilityEnabled());
+        final AppCompatReachabilityOverrides reachabilityOverrides =
+                mActivity.mAppCompatController.getAppCompatReachabilityOverrides();
+        assertFalse(reachabilityOverrides.isVerticalReachabilityEnabled());
+        assertFalse(reachabilityOverrides.isHorizontalReachabilityEnabled());
     }
 
     @Test
@@ -3457,7 +3457,8 @@
         assertEquals(WINDOWING_MODE_MULTI_WINDOW, mActivity.getWindowingMode());
 
         // Horizontal reachability is disabled because the app is in split screen.
-        assertFalse(mActivity.mLetterboxUiController.isHorizontalReachabilityEnabled());
+        assertFalse(mActivity.mAppCompatController.getAppCompatReachabilityOverrides()
+                .isHorizontalReachabilityEnabled());
     }
 
     @Test
@@ -3480,7 +3481,8 @@
         assertEquals(WINDOWING_MODE_MULTI_WINDOW, mActivity.getWindowingMode());
 
         // Vertical reachability is disabled because the app is in split screen.
-        assertFalse(mActivity.mLetterboxUiController.isVerticalReachabilityEnabled());
+        assertFalse(mActivity.mAppCompatController.getAppCompatReachabilityOverrides()
+                .isVerticalReachabilityEnabled());
     }
 
     @Test
@@ -3502,7 +3504,8 @@
         // Vertical reachability is disabled because the app does not match parent width
         assertNotEquals(mActivity.getScreenResolvedBounds().width(),
                 mActivity.mDisplayContent.getBounds().width());
-        assertFalse(mActivity.mLetterboxUiController.isVerticalReachabilityEnabled());
+        assertFalse(mActivity.mAppCompatController.getAppCompatReachabilityOverrides()
+                .isVerticalReachabilityEnabled());
     }
 
     @Test
@@ -3519,7 +3522,8 @@
         assertEquals(new Rect(0, 0, 0, 0), mActivity.getBounds());
 
         // Vertical reachability is still enabled as resolved bounds is not empty
-        assertTrue(mActivity.mLetterboxUiController.isVerticalReachabilityEnabled());
+        assertTrue(mActivity.mAppCompatController.getAppCompatReachabilityOverrides()
+                .isVerticalReachabilityEnabled());
     }
 
     @Test
@@ -3536,7 +3540,8 @@
         assertEquals(new Rect(0, 0, 0, 0), mActivity.getBounds());
 
         // Horizontal reachability is still enabled as resolved bounds is not empty
-        assertTrue(mActivity.mLetterboxUiController.isHorizontalReachabilityEnabled());
+        assertTrue(mActivity.mAppCompatController.getAppCompatReachabilityOverrides()
+                .isHorizontalReachabilityEnabled());
     }
 
     @Test
@@ -3550,7 +3555,8 @@
         prepareMinAspectRatio(mActivity, OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE,
                 SCREEN_ORIENTATION_PORTRAIT);
 
-        assertTrue(mActivity.mLetterboxUiController.isHorizontalReachabilityEnabled());
+        assertTrue(mActivity.mAppCompatController.getAppCompatReachabilityOverrides()
+                .isHorizontalReachabilityEnabled());
     }
 
     @Test
@@ -3564,7 +3570,8 @@
         prepareMinAspectRatio(mActivity, OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE,
                 SCREEN_ORIENTATION_LANDSCAPE);
 
-        assertTrue(mActivity.mLetterboxUiController.isVerticalReachabilityEnabled());
+        assertTrue(mActivity.mAppCompatController.getAppCompatReachabilityOverrides()
+                .isVerticalReachabilityEnabled());
     }
 
     @Test
@@ -3586,7 +3593,8 @@
         // Horizontal reachability is disabled because the app does not match parent height
         assertNotEquals(mActivity.getScreenResolvedBounds().height(),
                 mActivity.mDisplayContent.getBounds().height());
-        assertFalse(mActivity.mLetterboxUiController.isHorizontalReachabilityEnabled());
+        assertFalse(mActivity.mAppCompatController.getAppCompatReachabilityOverrides()
+                .isHorizontalReachabilityEnabled());
     }
 
     @Test
@@ -3608,7 +3616,8 @@
         // Horizontal reachability is enabled because the app matches parent height
         assertEquals(mActivity.getScreenResolvedBounds().height(),
                 mActivity.mDisplayContent.getBounds().height());
-        assertTrue(mActivity.mLetterboxUiController.isHorizontalReachabilityEnabled());
+        assertTrue(mActivity.mAppCompatController.getAppCompatReachabilityOverrides()
+                .isHorizontalReachabilityEnabled());
     }
 
     @Test
@@ -3630,7 +3639,8 @@
         // Vertical reachability is enabled because the app matches parent width
         assertEquals(mActivity.getScreenResolvedBounds().width(),
                 mActivity.mDisplayContent.getBounds().width());
-        assertTrue(mActivity.mLetterboxUiController.isVerticalReachabilityEnabled());
+        assertTrue(mActivity.mAppCompatController.getAppCompatReachabilityOverrides()
+                .isVerticalReachabilityEnabled());
     }
 
     @Test
@@ -4809,57 +4819,13 @@
         assertEquals(newDensity, mActivity.getConfiguration().densityDpi);
     }
 
-    @Test
-    public void testShouldSendFakeFocus_compatFakeFocusEnabled() {
-        final ActivityRecord activity = new ActivityBuilder(mAtm)
-                .setCreateTask(true)
-                .setOnTop(true)
-                // Set the component to be that of the test class in order to enable compat changes
-                .setComponent(ComponentName.createRelative(mContext,
-                        com.android.server.wm.SizeCompatTests.class.getName()))
-                .build();
-        final Task task = activity.getTask();
-        spyOn(activity.mLetterboxUiController);
-        doReturn(true).when(activity.mLetterboxUiController).shouldSendFakeFocus();
-
-        task.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
-        assertTrue(activity.shouldSendCompatFakeFocus());
-
-        task.setWindowingMode(WINDOWING_MODE_PINNED);
-        assertFalse(activity.shouldSendCompatFakeFocus());
-
-        task.setWindowingMode(WINDOWING_MODE_FREEFORM);
-        assertFalse(activity.shouldSendCompatFakeFocus());
-    }
-
-    @Test
-    public void testShouldSendFakeFocus_compatFakeFocusDisabled() {
-        final ActivityRecord activity = new ActivityBuilder(mAtm)
-                .setCreateTask(true)
-                .setOnTop(true)
-                // Set the component to be that of the test class in order to enable compat changes
-                .setComponent(ComponentName.createRelative(mContext,
-                        com.android.server.wm.SizeCompatTests.class.getName()))
-                .build();
-        final Task task = activity.getTask();
-        spyOn(activity.mLetterboxUiController);
-        doReturn(false).when(activity.mLetterboxUiController).shouldSendFakeFocus();
-
-        task.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
-        assertFalse(activity.shouldSendCompatFakeFocus());
-
-        task.setWindowingMode(WINDOWING_MODE_PINNED);
-        assertFalse(activity.shouldSendCompatFakeFocus());
-
-        task.setWindowingMode(WINDOWING_MODE_FREEFORM);
-        assertFalse(activity.shouldSendCompatFakeFocus());
-    }
-
     private void setUpAllowThinLetterboxed(boolean thinLetterboxAllowed) {
-        spyOn(mActivity.mLetterboxUiController);
-        doReturn(thinLetterboxAllowed).when(mActivity.mLetterboxUiController)
+        final AppCompatReachabilityOverrides reachabilityOverrides =
+                mActivity.mAppCompatController.getAppCompatReachabilityOverrides();
+        spyOn(reachabilityOverrides);
+        doReturn(thinLetterboxAllowed).when(reachabilityOverrides)
                 .allowVerticalReachabilityForThinLetterbox();
-        doReturn(thinLetterboxAllowed).when(mActivity.mLetterboxUiController)
+        doReturn(thinLetterboxAllowed).when(reachabilityOverrides)
                 .allowHorizontalReachabilityForThinLetterbox();
     }
 
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 d5d2847..b92af87 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
@@ -77,6 +77,7 @@
 import android.view.SurfaceControl;
 
 import com.android.dx.mockito.inline.extended.StaticMockitoSession;
+import com.android.internal.os.BackgroundThread;
 import com.android.server.AnimationThread;
 import com.android.server.DisplayThread;
 import com.android.server.LocalServices;
@@ -553,6 +554,9 @@
         // This is a different handler object than the wm.mAnimationHandler above.
         waitHandlerIdle(AnimationThread.getHandler());
         waitHandlerIdle(SurfaceAnimationThread.getHandler());
+        // Some binder calls are posted to BackgroundThread.getHandler(), we should wait for them
+        // to finish to run next test.
+        waitHandlerIdle(BackgroundThread.getHandler());
     }
 
     static void waitHandlerIdle(Handler handler) {
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 47d34a6..6be1af2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
@@ -995,16 +995,14 @@
         // The focus should change.
         assertEquals(winLeftTop, mDisplayContent.mCurrentFocus);
 
-        if (Flags.embeddedActivityBackNavFlag()) {
-            // Move focus if the adjacent activity is more recently active.
-            doReturn(1L).when(appLeftTop).getLastWindowCreateTime();
-            doReturn(2L).when(appRightTop).getLastWindowCreateTime();
-            assertTrue(mWm.moveFocusToAdjacentEmbeddedWindow(winLeftTop));
+        // Move focus if the adjacent activity is more recently active.
+        doReturn(1L).when(appLeftTop).getLastWindowCreateTime();
+        doReturn(2L).when(appRightTop).getLastWindowCreateTime();
+        assertTrue(mWm.moveFocusToAdjacentEmbeddedWindow(winLeftTop));
 
-            // Do not move the focus if the adjacent activity is less recently active.
-            doReturn(3L).when(appLeftTop).getLastWindowCreateTime();
-            assertFalse(mWm.moveFocusToAdjacentEmbeddedWindow(winLeftTop));
-        }
+        // Do not move the focus if the adjacent activity is less recently active.
+        doReturn(3L).when(appLeftTop).getLastWindowCreateTime();
+        assertFalse(mWm.moveFocusToAdjacentEmbeddedWindow(winLeftTop));
     }
 
     @Test
@@ -1049,6 +1047,28 @@
         assertFalse(taskFragment.shouldBeVisible(null));
     }
 
+    @Test
+    public void testTaskFragmentSmallestScreenWidthDp() {
+        // Create an embedded TaskFragment in a Task.
+        final Task task = createTask(mDisplayContent);
+        final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm)
+                .setParentTask(task)
+                .createActivityCount(1)
+                .build();
+        final Rect taskBounds = task.getBounds();
+
+        // Making the bounds of the embedded TaskFragment smaller than the parent Task.
+        taskFragment.setBounds(taskBounds.left, taskBounds.top, taskBounds.right / 2,
+                taskBounds.bottom);
+
+        // The swdp should be calculated via the TF bounds when it is a multi-window TF.
+        final Configuration outConfig = new Configuration();
+        outConfig.windowConfiguration.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+        taskFragment.computeConfigResourceOverrides(outConfig, task.getConfiguration());
+        assertEquals(outConfig.smallestScreenWidthDp,
+                Math.min(outConfig.screenWidthDp, outConfig.screenHeightDp));
+    }
+
     private WindowState createAppWindow(ActivityRecord app, String name) {
         final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, app, name,
                 0 /* ownerId */, false /* ownerCanAddInternalSystemWindow */, new TestIWindow());
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java
deleted file mode 100644
index d535677..0000000
--- a/services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java
+++ /dev/null
@@ -1,526 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm;
-
-import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
-import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
-
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
-import static com.android.internal.policy.TaskResizingAlgorithm.MIN_ASPECT;
-import static com.android.server.wm.WindowManagerService.dipToPixel;
-import static com.android.server.wm.WindowState.MINIMUM_VISIBLE_HEIGHT_IN_DP;
-import static com.android.server.wm.WindowState.MINIMUM_VISIBLE_WIDTH_IN_DP;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-
-import android.graphics.Rect;
-import android.platform.test.annotations.Presubmit;
-import android.util.DisplayMetrics;
-import android.util.Log;
-
-import androidx.test.filters.SmallTest;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * Tests for the {@link TaskPositioner} class.
- *
- * Build/Install/Run:
- *  atest WmTests:TaskPositionerTests
- */
-@SmallTest
-@Presubmit
-@RunWith(WindowTestRunner.class)
-public class TaskPositionerTests extends WindowTestsBase {
-
-    private static final boolean DEBUGGING = false;
-    private static final String TAG = "TaskPositionerTest";
-
-    private static final int MOUSE_DELTA_X = 5;
-    private static final int MOUSE_DELTA_Y = 5;
-
-    private int mMinVisibleWidth;
-    private int mMinVisibleHeight;
-    private TaskPositioner mPositioner;
-
-    @Before
-    public void setUp() {
-        TaskPositioner.setFactory(null);
-
-        final DisplayMetrics dm = mDisplayContent.getDisplayMetrics();
-
-        // This should be the same calculation as the TaskPositioner uses.
-        mMinVisibleWidth = dipToPixel(MINIMUM_VISIBLE_WIDTH_IN_DP, dm);
-        mMinVisibleHeight = dipToPixel(MINIMUM_VISIBLE_HEIGHT_IN_DP, dm);
-        removeGlobalMinSizeRestriction();
-
-        final ActivityRecord activity = new ActivityBuilder(mAtm)
-                .setCreateTask(true)
-                .build();
-        final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, activity, "window");
-        mPositioner = new TaskPositioner(mWm);
-        mPositioner.register(mDisplayContent, win);
-
-        win.getRootTask().setWindowingMode(WINDOWING_MODE_FREEFORM);
-    }
-
-    @After
-    public void tearDown() {
-        TaskPositioner.setFactory(null);
-    }
-
-    @Test
-    public void testOverrideFactory() {
-        final boolean[] created = new boolean[1];
-        created[0] = false;
-        TaskPositioner.setFactory(new TaskPositioner.Factory() {
-            @Override
-            public TaskPositioner create(WindowManagerService service) {
-                created[0] = true;
-                return null;
-            }
-        });
-
-        assertNull(TaskPositioner.create(mWm));
-        assertTrue(created[0]);
-    }
-
-    /** This tests that the window can move in all directions. */
-    @Test
-    public void testMoveWindow() {
-        final Rect displayBounds = mDisplayContent.getBounds();
-        final int windowSize = Math.min(displayBounds.width(), displayBounds.height()) / 2;
-        final int left = displayBounds.centerX() - windowSize / 2;
-        final int top = displayBounds.centerY() - windowSize / 2;
-        final Rect r = new Rect(left, top, left + windowSize, top + windowSize);
-        mPositioner.mTask.setBounds(r);
-        mPositioner.startDrag(false /* resizing */, false /* preserveOrientation */, left, top);
-
-        // Move upper left.
-        mPositioner.notifyMoveLocked(left - MOUSE_DELTA_X, top - MOUSE_DELTA_Y);
-        r.offset(-MOUSE_DELTA_X, -MOUSE_DELTA_Y);
-        assertBoundsEquals(r, mPositioner.getWindowDragBounds());
-
-        // Move bottom right.
-        mPositioner.notifyMoveLocked(left, top);
-        r.offset(MOUSE_DELTA_X, MOUSE_DELTA_Y);
-        assertBoundsEquals(r, mPositioner.getWindowDragBounds());
-    }
-
-    /**
-     * This tests that free resizing will allow to change the orientation as well
-     * as does some basic tests (e.g. dragging in Y only will keep X stable).
-     */
-    @Test
-    public void testBasicFreeWindowResizing() {
-        final Rect r = new Rect(100, 220, 700, 520);
-        final int midY = (r.top + r.bottom) / 2;
-        mPositioner.mTask.setBounds(r, true);
-
-        // Start a drag resize starting upper left.
-        mPositioner.startDrag(true /* resizing */, false /* preserveOrientation */,
-                r.left - MOUSE_DELTA_X, r.top - MOUSE_DELTA_Y);
-        assertBoundsEquals(r, mPositioner.getWindowDragBounds());
-
-        // Drag to a good landscape size.
-        mPositioner.resizeDrag(0.0f, 0.0f);
-        assertBoundsEquals(new Rect(MOUSE_DELTA_X, MOUSE_DELTA_Y, r.right, r.bottom),
-                mPositioner.getWindowDragBounds());
-
-        // Drag to a good portrait size.
-        mPositioner.resizeDrag(400.0f, 0.0f);
-        assertBoundsEquals(new Rect(400 + MOUSE_DELTA_X, MOUSE_DELTA_Y, r.right, r.bottom),
-                mPositioner.getWindowDragBounds());
-
-        // Drag to a too small size for the width.
-        mPositioner.resizeDrag(2000.0f, r.top);
-        assertBoundsEquals(
-                new Rect(r.right - mMinVisibleWidth, r.top + MOUSE_DELTA_Y, r.right, r.bottom),
-                mPositioner.getWindowDragBounds());
-
-        // Drag to a too small size for the height.
-        mPositioner.resizeDrag(r.left, 2000.0f);
-        assertBoundsEquals(
-                new Rect(r.left + MOUSE_DELTA_X, r.bottom - mMinVisibleHeight, r.right, r.bottom),
-                mPositioner.getWindowDragBounds());
-
-        // Start a drag resize left and see that only the left coord changes..
-        mPositioner.startDrag(true /* resizing */, false /* preserveOrientation */,
-                r.left - MOUSE_DELTA_X, midY);
-
-        // Drag to the left.
-        mPositioner.resizeDrag(0.0f, midY);
-        assertBoundsEquals(new Rect(MOUSE_DELTA_X, r.top, r.right, r.bottom),
-                mPositioner.getWindowDragBounds());
-
-        // Drag to the right.
-        mPositioner.resizeDrag(200.0f, midY);
-        assertBoundsEquals(new Rect(200 + MOUSE_DELTA_X, r.top, r.right, r.bottom),
-                mPositioner.getWindowDragBounds());
-
-        // Drag to the top
-        mPositioner.resizeDrag(r.left, 0.0f);
-        assertBoundsEquals(new Rect(r.left + MOUSE_DELTA_X, r.top, r.right, r.bottom),
-                mPositioner.getWindowDragBounds());
-
-        // Drag to the bottom
-        mPositioner.resizeDrag(r.left, 1000.0f);
-        assertBoundsEquals(new Rect(r.left + MOUSE_DELTA_X, r.top, r.right, r.bottom),
-                mPositioner.getWindowDragBounds());
-    }
-
-    /**
-     * This tests that by dragging any edge, the fixed / opposite edge(s) remains anchored.
-     */
-    @Test
-    public void testFreeWindowResizingTestAllEdges() {
-        final Rect r = new Rect(100, 220, 700, 520);
-        final int midX = (r.left + r.right) / 2;
-        final int midY = (r.top + r.bottom) / 2;
-        mPositioner.mTask.setBounds(r, true);
-
-        // Drag upper left.
-        mPositioner.startDrag(true /* resizing */, false /* preserveOrientation */,
-                r.left - MOUSE_DELTA_X, r.top - MOUSE_DELTA_Y);
-        mPositioner.resizeDrag(0.0f, 0.0f);
-        assertNotEquals(r.left, mPositioner.getWindowDragBounds().left);
-        assertEquals(r.right, mPositioner.getWindowDragBounds().right);
-        assertNotEquals(r.top, mPositioner.getWindowDragBounds().top);
-        assertEquals(r.bottom, mPositioner.getWindowDragBounds().bottom);
-
-        // Drag upper.
-        mPositioner.startDrag(true /* resizing */, false /* preserveOrientation */, midX,
-                r.top - MOUSE_DELTA_Y);
-        mPositioner.resizeDrag(0.0f, 0.0f);
-        assertEquals(r.left, mPositioner.getWindowDragBounds().left);
-        assertEquals(r.right, mPositioner.getWindowDragBounds().right);
-        assertNotEquals(r.top, mPositioner.getWindowDragBounds().top);
-        assertEquals(r.bottom, mPositioner.getWindowDragBounds().bottom);
-
-        // Drag upper right.
-        mPositioner.startDrag(true /* resizing */, false /* preserveOrientation */,
-                r.right + MOUSE_DELTA_X, r.top - MOUSE_DELTA_Y);
-        mPositioner.resizeDrag(r.right + 100, 0.0f);
-        assertEquals(r.left, mPositioner.getWindowDragBounds().left);
-        assertNotEquals(r.right, mPositioner.getWindowDragBounds().right);
-        assertNotEquals(r.top, mPositioner.getWindowDragBounds().top);
-        assertEquals(r.bottom, mPositioner.getWindowDragBounds().bottom);
-
-        // Drag right.
-        mPositioner.startDrag(true /* resizing */, false /* preserveOrientation */,
-                r.right + MOUSE_DELTA_X, midY);
-        mPositioner.resizeDrag(r.right + 100, 0.0f);
-        assertEquals(r.left, mPositioner.getWindowDragBounds().left);
-        assertNotEquals(r.right, mPositioner.getWindowDragBounds().right);
-        assertEquals(r.top, mPositioner.getWindowDragBounds().top);
-        assertEquals(r.bottom, mPositioner.getWindowDragBounds().bottom);
-
-        // Drag bottom right.
-        mPositioner.startDrag(true /* resizing */, false /* preserveOrientation */,
-                r.right + MOUSE_DELTA_X, r.bottom + MOUSE_DELTA_Y);
-        mPositioner.resizeDrag(r.right + 100, r.bottom + 100);
-        assertEquals(r.left, mPositioner.getWindowDragBounds().left);
-        assertNotEquals(r.right, mPositioner.getWindowDragBounds().right);
-        assertEquals(r.top, mPositioner.getWindowDragBounds().top);
-        assertNotEquals(r.bottom, mPositioner.getWindowDragBounds().bottom);
-
-        // Drag bottom.
-        mPositioner.startDrag(true /* resizing */, false /* preserveOrientation */, midX,
-                r.bottom + MOUSE_DELTA_Y);
-        mPositioner.resizeDrag(r.right + 100, r.bottom + 100);
-        assertEquals(r.left, mPositioner.getWindowDragBounds().left);
-        assertEquals(r.right, mPositioner.getWindowDragBounds().right);
-        assertEquals(r.top, mPositioner.getWindowDragBounds().top);
-        assertNotEquals(r.bottom, mPositioner.getWindowDragBounds().bottom);
-
-        // Drag bottom left.
-        mPositioner.startDrag(true /* resizing */, false /* preserveOrientation */,
-                r.left - MOUSE_DELTA_X, r.bottom + MOUSE_DELTA_Y);
-        mPositioner.resizeDrag(0.0f, r.bottom + 100);
-        assertNotEquals(r.left, mPositioner.getWindowDragBounds().left);
-        assertEquals(r.right, mPositioner.getWindowDragBounds().right);
-        assertEquals(r.top, mPositioner.getWindowDragBounds().top);
-        assertNotEquals(r.bottom, mPositioner.getWindowDragBounds().bottom);
-
-        // Drag left.
-        mPositioner.startDrag(true /* resizing */, false /* preserveOrientation */,
-                r.left - MOUSE_DELTA_X, midY);
-        mPositioner.resizeDrag(0.0f, r.bottom + 100);
-        assertNotEquals(r.left, mPositioner.getWindowDragBounds().left);
-        assertEquals(r.right, mPositioner.getWindowDragBounds().right);
-        assertEquals(r.top, mPositioner.getWindowDragBounds().top);
-        assertEquals(r.bottom, mPositioner.getWindowDragBounds().bottom);
-    }
-
-    /**
-     * This tests that a constrained landscape window will keep the aspect and do the
-     * right things upon resizing when dragged from the top left corner.
-     */
-    @Test
-    public void testLandscapePreservedWindowResizingDragTopLeft() {
-        final Rect r = new Rect(100, 220, 700, 520);
-        mPositioner.mTask.setBounds(r, true);
-
-        mPositioner.startDrag(true /* resizing */, true /* preserveOrientation */,
-                r.left - MOUSE_DELTA_X, r.top - MOUSE_DELTA_Y);
-        assertBoundsEquals(r, mPositioner.getWindowDragBounds());
-
-        // Drag to a good landscape size.
-        mPositioner.resizeDrag(0.0f, 0.0f);
-        assertBoundsEquals(new Rect(MOUSE_DELTA_X, MOUSE_DELTA_Y, r.right, r.bottom),
-                mPositioner.getWindowDragBounds());
-
-        // Drag to a good portrait size.
-        mPositioner.resizeDrag(400.0f, 0.0f);
-        int width = Math.round((float) (r.bottom - MOUSE_DELTA_Y) * MIN_ASPECT);
-        assertBoundsEquals(new Rect(r.right - width, MOUSE_DELTA_Y, r.right, r.bottom),
-                mPositioner.getWindowDragBounds());
-
-        // Drag to a too small size for the width.
-        mPositioner.resizeDrag(2000.0f, r.top);
-        final int w = mMinVisibleWidth;
-        final int h = Math.round(w / MIN_ASPECT);
-        assertBoundsEquals(new Rect(r.right - w, r.bottom - h, r.right, r.bottom),
-                mPositioner.getWindowDragBounds());
-
-        // Drag to a too small size for the height.
-        mPositioner.resizeDrag(r.left, 2000.0f);
-        assertBoundsEquals(
-                new Rect(r.left + MOUSE_DELTA_X, r.bottom - mMinVisibleHeight, r.right, r.bottom),
-                mPositioner.getWindowDragBounds());
-    }
-
-    /**
-     * This tests that a constrained landscape window will keep the aspect and do the
-     * right things upon resizing when dragged from the left corner.
-     */
-    @Test
-    public void testLandscapePreservedWindowResizingDragLeft() {
-        final Rect r = new Rect(100, 220, 700, 520);
-        final int midY = (r.top + r.bottom) / 2;
-        mPositioner.mTask.setBounds(r, true);
-
-        mPositioner.startDrag(true /* resizing */, true /* preserveOrientation */,
-                r.left - MOUSE_DELTA_X, midY);
-
-        // Drag to the left.
-        mPositioner.resizeDrag(0.0f, midY);
-        assertBoundsEquals(new Rect(MOUSE_DELTA_X, r.top, r.right, r.bottom),
-                mPositioner.getWindowDragBounds());
-
-        // Drag to the right.
-        mPositioner.resizeDrag(200.0f, midY);
-        assertBoundsEquals(new Rect(200 + MOUSE_DELTA_X, r.top, r.right, r.bottom),
-                mPositioner.getWindowDragBounds());
-
-        // Drag all the way to the right and see the height also shrinking.
-        mPositioner.resizeDrag(2000.0f, midY);
-        final int w = mMinVisibleWidth;
-        final int h = Math.round((float) w / MIN_ASPECT);
-        assertBoundsEquals(new Rect(r.right - w, r.top, r.right, r.top + h),
-                mPositioner.getWindowDragBounds());
-
-        // Drag to the top.
-        mPositioner.resizeDrag(r.left, 0.0f);
-        assertBoundsEquals(new Rect(r.left + MOUSE_DELTA_X, r.top, r.right, r.bottom),
-                mPositioner.getWindowDragBounds());
-
-        // Drag to the bottom.
-        mPositioner.resizeDrag(r.left, 1000.0f);
-        assertBoundsEquals(new Rect(r.left + MOUSE_DELTA_X, r.top, r.right, r.bottom),
-                mPositioner.getWindowDragBounds());
-    }
-
-    /**
-     * This tests that a constrained landscape window will keep the aspect and do the
-     * right things upon resizing when dragged from the top corner.
-     */
-    @Test
-    public void testLandscapePreservedWindowResizingDragTop() {
-        final Rect r = new Rect(100, 220, 700, 520);
-        final int midX = (r.left + r.right) / 2;
-        mPositioner.mTask.setBounds(r, true);
-
-        mPositioner.startDrag(true /*resizing*/, true /*preserveOrientation*/, midX,
-                r.top - MOUSE_DELTA_Y);
-
-        // Drag to the left (no change).
-        mPositioner.resizeDrag(0.0f, r.top);
-        assertBoundsEquals(new Rect(r.left, r.top + MOUSE_DELTA_Y, r.right, r.bottom),
-                mPositioner.getWindowDragBounds());
-
-        // Drag to the right (no change).
-        mPositioner.resizeDrag(2000.0f, r.top);
-        assertBoundsEquals(new Rect(r.left , r.top + MOUSE_DELTA_Y, r.right, r.bottom),
-                mPositioner.getWindowDragBounds());
-
-        // Drag to the top.
-        mPositioner.resizeDrag(300.0f, 0.0f);
-        int h = r.bottom - MOUSE_DELTA_Y;
-        int w = Math.max(r.right - r.left, Math.round(h * MIN_ASPECT));
-        assertBoundsEquals(new Rect(r.left, MOUSE_DELTA_Y, r.left + w, r.bottom),
-                mPositioner.getWindowDragBounds());
-
-        // Drag to the bottom.
-        mPositioner.resizeDrag(r.left, 1000.0f);
-        h = mMinVisibleHeight;
-        assertBoundsEquals(new Rect(r.left, r.bottom - h, r.right, r.bottom),
-                mPositioner.getWindowDragBounds());
-    }
-
-    /**
-     * This tests that a constrained portrait window will keep the aspect and do the
-     * right things upon resizing when dragged from the top left corner.
-     */
-    @Test
-    public void testPortraitPreservedWindowResizingDragTopLeft() {
-        final Rect r = new Rect(330, 100, 630, 600);
-        mPositioner.mTask.setBounds(r, true);
-
-        mPositioner.startDrag(true /*resizing*/, true /*preserveOrientation*/,
-                r.left - MOUSE_DELTA_X, r.top - MOUSE_DELTA_Y);
-        assertBoundsEquals(r, mPositioner.getWindowDragBounds());
-
-        // Drag to a good landscape size.
-        mPositioner.resizeDrag(0.0f, 0.0f);
-        int height = Math.round((float) (r.right - MOUSE_DELTA_X) * MIN_ASPECT);
-        assertBoundsEquals(new Rect(MOUSE_DELTA_X, r.bottom - height, r.right, r.bottom),
-                mPositioner.getWindowDragBounds());
-
-        // Drag to a good portrait size.
-        mPositioner.resizeDrag(400.0f, 0.0f);
-        assertBoundsEquals(new Rect(400 + MOUSE_DELTA_X, MOUSE_DELTA_Y, r.right, r.bottom),
-                mPositioner.getWindowDragBounds());
-
-        // Drag to a too small size for the height and the the width shrinking.
-        mPositioner.resizeDrag(r.left + MOUSE_DELTA_X, 2000.0f);
-        final int w = Math.max(mMinVisibleWidth, Math.round(mMinVisibleHeight / MIN_ASPECT));
-        final int h = Math.max(mMinVisibleHeight, Math.round(w * MIN_ASPECT));
-        assertBoundsEquals(
-                new Rect(r.right - w, r.bottom - h, r.right, r.bottom),
-                mPositioner.getWindowDragBounds());
-    }
-
-    /**
-     * This tests that a constrained portrait window will keep the aspect and do the
-     * right things upon resizing when dragged from the left corner.
-     */
-    @Test
-    public void testPortraitPreservedWindowResizingDragLeft() {
-        final Rect r = new Rect(330, 100, 630, 600);
-        final int midY = (r.top + r.bottom) / 2;
-        mPositioner.mTask.setBounds(r, true);
-
-        mPositioner.startDrag(true /* resizing */, true /* preserveOrientation */,
-                r.left - MOUSE_DELTA_X, midY);
-
-        // Drag to the left.
-        mPositioner.resizeDrag(0.0f, midY);
-        int w = r.right - MOUSE_DELTA_X;
-        int h = Math.round(w * MIN_ASPECT);
-        assertBoundsEquals(new Rect(MOUSE_DELTA_X, r.top, r.right, r.top + h),
-                mPositioner.getWindowDragBounds());
-
-        // Drag to the right.
-        mPositioner.resizeDrag(450.0f, midY);
-        assertBoundsEquals(new Rect(450 + MOUSE_DELTA_X, r.top, r.right, r.bottom),
-                mPositioner.getWindowDragBounds());
-
-        // Drag all the way to the right.
-        mPositioner.resizeDrag(2000.0f, midY);
-        w = mMinVisibleWidth;
-        h = Math.max(Math.round((float) w * MIN_ASPECT), r.height());
-        assertBoundsEquals(new Rect(r.right - w, r.top, r.right, r.top + h),
-                mPositioner.getWindowDragBounds());
-
-        // Drag to the top.
-        mPositioner.resizeDrag(r.left, 0.0f);
-        assertBoundsEquals(new Rect(r.left + MOUSE_DELTA_X, r.top, r.right, r.bottom),
-                mPositioner.getWindowDragBounds());
-
-        // Drag to the bottom.
-        mPositioner.resizeDrag(r.left, 1000.0f);
-        assertBoundsEquals(new Rect(r.left + MOUSE_DELTA_X, r.top, r.right, r.bottom),
-                mPositioner.getWindowDragBounds());
-    }
-
-    /**
-     * This tests that a constrained portrait window will keep the aspect and do the
-     * right things upon resizing when dragged from the top corner.
-     */
-    @Test
-    public void testPortraitPreservedWindowResizingDragTop() {
-        final Rect r = new Rect(330, 100, 630, 600);
-        final int midX = (r.left + r.right) / 2;
-        mPositioner.mTask.setBounds(r, true);
-
-        mPositioner.startDrag(true /* resizing */, true /* preserveOrientation */, midX,
-                r.top - MOUSE_DELTA_Y);
-
-        // Drag to the left (no change).
-        mPositioner.resizeDrag(0.0f, r.top);
-        assertBoundsEquals(new Rect(r.left, r.top + MOUSE_DELTA_Y, r.right, r.bottom),
-                mPositioner.getWindowDragBounds());
-
-        // Drag to the right (no change).
-        mPositioner.resizeDrag(2000.0f, r.top);
-        assertBoundsEquals(new Rect(r.left , r.top + MOUSE_DELTA_Y, r.right, r.bottom),
-                mPositioner.getWindowDragBounds());
-
-        // Drag to the top.
-        mPositioner.resizeDrag(300.0f, 0.0f);
-        int h = r.bottom - MOUSE_DELTA_Y;
-        int w = Math.min(r.width(), Math.round(h / MIN_ASPECT));
-        assertBoundsEquals(new Rect(r.left, MOUSE_DELTA_Y, r.left + w, r.bottom),
-                mPositioner.getWindowDragBounds());
-
-        // Drag to the bottom.
-        mPositioner.resizeDrag(r.left, 1000.0f);
-        h = Math.max(mMinVisibleHeight, Math.round(mMinVisibleWidth * MIN_ASPECT));
-        w = Math.round(h / MIN_ASPECT);
-        assertBoundsEquals(new Rect(r.left, r.bottom - h, r.left + w, r.bottom),
-                mPositioner.getWindowDragBounds());
-    }
-
-    private static void assertBoundsEquals(Rect expected, Rect actual) {
-        if (DEBUGGING) {
-            if (!expected.equals(actual)) {
-                Log.e(TAG, "rect(" + actual.toString() + ") != isRect(" + actual.toString()
-                        + ") " + Log.getStackTraceString(new Throwable()));
-            }
-        }
-        assertEquals(expected, actual);
-    }
-
-    @Test
-    public void testFinishingMovingWhenBinderDied() {
-        spyOn(mWm.mTaskPositioningController);
-
-        mPositioner.startDrag(false, false, 0 /* startX */, 0 /* startY */);
-        verify(mWm.mTaskPositioningController, never()).finishTaskPositioning();
-        mPositioner.binderDied();
-        verify(mWm.mTaskPositioningController).finishTaskPositioning();
-    }
-}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java
deleted file mode 100644
index bfc13d3..0000000
--- a/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm;
-
-import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
-import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
-import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
-
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
-
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-
-import android.platform.test.annotations.Presubmit;
-import android.view.InputChannel;
-
-import androidx.test.filters.FlakyTest;
-import androidx.test.filters.SmallTest;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * Tests for the {@link TaskPositioningController} class.
- *
- * Build/Install/Run:
- *  atest WmTests:TaskPositioningControllerTests
- */
-@SmallTest
-@Presubmit
-@RunWith(WindowTestRunner.class)
-public class TaskPositioningControllerTests extends WindowTestsBase {
-    private static final int TIMEOUT_MS = 1000;
-
-    private TaskPositioningController mTarget;
-    private WindowState mWindow;
-
-    @Before
-    public void setUp() throws Exception {
-        assertNotNull(mWm.mTaskPositioningController);
-        mTarget = mWm.mTaskPositioningController;
-
-        when(mWm.mInputManager.transferTouchGesture(any(), any())).thenReturn(true);
-
-        mWindow = createWindow(null, TYPE_BASE_APPLICATION, "window");
-        mWindow.getTask().setResizeMode(RESIZE_MODE_RESIZEABLE);
-        mWindow.mInputChannel = new InputChannel();
-        mWm.mWindowMap.put(mWindow.mClient.asBinder(), mWindow);
-        doReturn(mock(InputMonitor.class)).when(mDisplayContent).getInputMonitor();
-    }
-
-    @FlakyTest(bugId = 291067614)
-    @Test
-    public void testStartAndFinishPositioning() {
-        assertFalse(mTarget.isPositioningLocked());
-        assertNull(mTarget.getDragWindowHandleLocked());
-
-        assertTrue(mTarget.startMovingTask(mWindow.mClient, 0, 0));
-
-        assertTrue(mTarget.isPositioningLocked());
-        assertNotNull(mTarget.getDragWindowHandleLocked());
-
-        mTarget.finishTaskPositioning();
-        // Wait until the looper processes finishTaskPositioning.
-        assertTrue(waitHandlerIdle(mWm.mAnimationHandler, TIMEOUT_MS));
-
-        assertFalse(mTarget.isPositioningLocked());
-        assertNull(mTarget.getDragWindowHandleLocked());
-    }
-
-    @Test
-    public void testFinishPositioningWhenAppRequested() {
-        assertFalse(mTarget.isPositioningLocked());
-        assertNull(mTarget.getDragWindowHandleLocked());
-
-        assertTrue(mTarget.startMovingTask(mWindow.mClient, 0, 0));
-
-        assertTrue(mTarget.isPositioningLocked());
-        assertNotNull(mTarget.getDragWindowHandleLocked());
-
-        mTarget.finishTaskPositioning(mWindow.mClient);
-        // Wait until the looper processes finishTaskPositioning.
-        assertTrue(waitHandlerIdle(mWm.mAnimationHandler, TIMEOUT_MS));
-
-        assertFalse(mTarget.isPositioningLocked());
-        assertNull(mTarget.getDragWindowHandleLocked());
-    }
-
-    @Test
-    public void testHandleTapOutsideTask() {
-        assertFalse(mTarget.isPositioningLocked());
-        assertNull(mTarget.getDragWindowHandleLocked());
-
-        final DisplayContent content = mock(DisplayContent.class);
-        doReturn(mWindow.getTask()).when(content).findTaskForResizePoint(anyInt(), anyInt());
-        assertNotNull(mWindow.getTask().getTopVisibleAppMainWindow());
-
-        mTarget.handleTapOutsideTask(content, 0, 0);
-        // Wait until the looper processes handleTapOutsideTask.
-        assertTrue(waitHandlerIdle(mWm.mH, TIMEOUT_MS));
-
-        assertTrue(mTarget.isPositioningLocked());
-        assertNotNull(mTarget.getDragWindowHandleLocked());
-
-        mTarget.finishTaskPositioning();
-        // Wait until the looper processes finishTaskPositioning.
-        assertTrue(waitHandlerIdle(mWm.mAnimationHandler, TIMEOUT_MS));
-
-        assertFalse(mTarget.isPositioningLocked());
-        assertNull(mTarget.getDragWindowHandleLocked());
-    }
-
-    @Test
-    public void testHandleTapOutsideNonResizableTask() {
-        assertFalse(mTarget.isPositioningLocked());
-        assertNull(mTarget.getDragWindowHandleLocked());
-
-        final DisplayContent content = mock(DisplayContent.class);
-        doReturn(mWindow.getTask()).when(content).findTaskForResizePoint(anyInt(), anyInt());
-        assertNotNull(mWindow.getTask().getTopVisibleAppMainWindow());
-
-        mWindow.getTask().setResizeMode(RESIZE_MODE_UNRESIZEABLE);
-
-        mTarget.handleTapOutsideTask(content, 0, 0);
-        // Wait until the looper processes handleTapOutsideTask.
-        assertTrue(waitHandlerIdle(mWm.mH, TIMEOUT_MS));
-
-        assertFalse(mTarget.isPositioningLocked());
-    }
-
-}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
index 0bf850a..a232ff0 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
@@ -46,6 +46,7 @@
 import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_TASK_ORG;
 import static com.android.server.wm.TaskFragment.EMBEDDED_DIM_AREA_PARENT_TASK;
 import static com.android.server.wm.TaskFragment.TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT;
+import static com.android.server.wm.WindowContainer.POSITION_TOP;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -78,6 +79,7 @@
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.res.Configuration;
+import android.graphics.Color;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.os.IBinder;
@@ -390,6 +392,15 @@
         rootTask.ensureActivitiesVisible(null /* starting */);
         assertTrue(activity1.isVisible());
         assertTrue(activity2.isVisible());
+
+        // If notifyClients is false, it should only update the state without starting the client.
+        activity1.setVisible(false);
+        activity1.setVisibleRequested(false);
+        activity1.detachFromProcess();
+        rootTask.ensureActivitiesVisible(null /* starting */, false /* notifyClients */);
+        verify(mSupervisor, never()).startSpecificActivity(eq(activity1),
+                anyBoolean() /* andResume */, anyBoolean() /* checkConfig */);
+        assertTrue(activity1.isVisibleRequested());
     }
 
     @Test
@@ -2022,6 +2033,47 @@
                 task.getTaskInfo().appCompatTaskInfo.cameraCompatTaskInfo.freeformCameraCompatMode);
     }
 
+    @Test
+    public void testUpdateTaskDescriptionOnReparent() {
+        final Task rootTask1 = createTask(mDisplayContent);
+        final Task rootTask2 = createTask(mDisplayContent);
+        final Task childTask = createTaskInRootTask(rootTask1, 0 /* userId */);
+        final ActivityRecord activity = createActivityRecord(mDisplayContent, childTask);
+        final String testLabel = "test_task_description_label";
+        final ActivityManager.TaskDescription td = new ActivityManager.TaskDescription(testLabel);
+        activity.setTaskDescription(td);
+
+        // Ensure the td is set for the original root task
+        assertEquals(testLabel, rootTask1.getTaskDescription().getLabel());
+        assertNull(rootTask2.getTaskDescription().getLabel());
+
+        childTask.reparent(rootTask2, POSITION_TOP, false /* moveParents */, "reparent");
+
+        // Ensure the td is set for the new root task
+        assertEquals(testLabel, rootTask2.getTaskDescription().getLabel());
+    }
+
+    @Test
+    public void testUpdateTaskDescriptionOnReorder() {
+        final Task task = createTask(mDisplayContent);
+        final ActivityRecord activity1 = createActivityRecord(mDisplayContent, task);
+        final ActivityRecord activity2 = createActivityRecord(mDisplayContent, task);
+        final ActivityManager.TaskDescription td1 = new ActivityManager.TaskDescription();
+        td1.setBackgroundColor(Color.RED);
+        activity1.setTaskDescription(td1);
+        final ActivityManager.TaskDescription td2 = new ActivityManager.TaskDescription();
+        td2.setBackgroundColor(Color.BLUE);
+        activity2.setTaskDescription(td2);
+
+        // Ensure the td is set for the original root task
+        assertEquals(Color.BLUE, task.getTaskDescription().getBackgroundColor());
+
+        task.positionChildAt(POSITION_TOP, activity1, false /* includeParents */);
+
+        // Ensure the td is set for the original root task
+        assertEquals(Color.RED, task.getTaskDescription().getBackgroundColor());
+    }
+
     private Task getTestTask() {
         return new TaskBuilder(mSupervisor).setCreateActivity(true).build();
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
index 7d01b79..49e349c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -1573,7 +1573,8 @@
         enteringAnimReports.clear();
         doCallRealMethod().when(mWm.mRoot).ensureActivitiesVisible(any(), anyBoolean());
         final boolean[] wasInFinishingTransition = { false };
-        controller.registerLegacyListener(new WindowManagerInternal.AppTransitionListener() {
+        controller.registerLegacyListener(new WindowManagerInternal.AppTransitionListener(
+                mDisplayContent.mDisplayId) {
             @Override
             public void onAppTransitionFinishedLocked(IBinder token) {
                 final ActivityRecord r = ActivityRecord.forToken(token);
@@ -1582,6 +1583,14 @@
                 }
             }
         });
+        final boolean[] calledListenerOnOtherDisplay = { false };
+        controller.registerLegacyListener(new WindowManagerInternal.AppTransitionListener(
+                mDisplayContent.mDisplayId + 1234) {
+            @Override
+            public void onAppTransitionFinishedLocked(IBinder token) {
+                calledListenerOnOtherDisplay[0] = true;
+            }
+        });
         assertTrue(activity1.isVisible());
         doReturn(false).when(task1).isTranslucent(null);
         doReturn(false).when(task1).isTranslucentForTransition();
@@ -1592,6 +1601,7 @@
 
         controller.finishTransition(closeTransition);
         assertTrue(wasInFinishingTransition[0]);
+        assertFalse(calledListenerOnOtherDisplay[0]);
         assertNull(controller.mFinishingTransition);
 
         assertTrue(activity2.isVisible());
@@ -2011,7 +2021,7 @@
 
     @EnableFlags(Flags.FLAG_MOVE_ANIMATION_OPTIONS_TO_CHANGE)
     @Test
-    public void testOverrideAnimationOptionsToInfoIfNecessary_nonCustomAnimOptions() {
+    public void testOverrideAnimationOptionsToInfoIfNecessary_fromStyleAnimOptions() {
         initializeOverrideAnimationOptionsTest();
         TransitionInfo.AnimationOptions options = TransitionInfo.AnimationOptions
                 .makeCommonAnimOptions("testPackage");
@@ -2037,6 +2047,32 @@
 
     @EnableFlags(Flags.FLAG_MOVE_ANIMATION_OPTIONS_TO_CHANGE)
     @Test
+    public void testOverrideAnimationOptionsToInfoIfNecessary_sceneAnimOptions() {
+        initializeOverrideAnimationOptionsTest();
+        TransitionInfo.AnimationOptions options = TransitionInfo.AnimationOptions
+                .makeSceneTransitionAnimOptions();
+        mTransition.setOverrideAnimation(options, null /* startCallback */,
+                null /* finishCallback */);
+
+        mTransition.overrideAnimationOptionsToInfoIfNecessary(mInfo);
+
+        final TransitionInfo.Change displayChange = mInfo.getChanges().get(0);
+        final TransitionInfo.Change taskChange = mInfo.getChanges().get(1);
+        final TransitionInfo.Change embeddedTfChange = mInfo.getChanges().get(2);
+        final TransitionInfo.Change activityChange = mInfo.getChanges().get(3);
+
+        assertNull("Display change's AnimationOptions must not be overridden.",
+                displayChange.getAnimationOptions());
+        assertEquals("Task change's AnimationOptions must be overridden.",
+                options, taskChange.getAnimationOptions());
+        assertNull("Embedded TF change's AnimationOptions must not be overridden.",
+                embeddedTfChange.getAnimationOptions());
+        assertEquals("Activity change's AnimationOptions must be overridden.",
+                options, activityChange.getAnimationOptions());
+    }
+
+    @EnableFlags(Flags.FLAG_MOVE_ANIMATION_OPTIONS_TO_CHANGE)
+    @Test
     public void testOverrideAnimationOptionsToInfoIfNecessary_crossProfileAnimOptions() {
         initializeOverrideAnimationOptionsTest();
         TransitionInfo.AnimationOptions options = TransitionInfo.AnimationOptions
diff --git a/services/tests/wmtests/src/com/android/server/wm/TrustedOverlayTests.java b/services/tests/wmtests/src/com/android/server/wm/TrustedOverlayTests.java
index f1d84cf..529e9b7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TrustedOverlayTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TrustedOverlayTests.java
@@ -49,6 +49,7 @@
 import org.junit.Test;
 import org.junit.rules.TestName;
 
+import java.time.Duration;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
@@ -144,7 +145,7 @@
                         }
                     }
                     return false;
-                }, TIMEOUT_S, TimeUnit.SECONDS);
+                }, Duration.ofSeconds(TIMEOUT_S));
 
         assertAndDumpWindowState(TAG, "Failed to find window or was not marked trusted",
                 foundTrusted[0]);
@@ -209,7 +210,7 @@
                         }
                     }
                     return foundTrusted[0] && foundTrusted[1];
-                }, TIMEOUT_S, TimeUnit.SECONDS);
+                }, Duration.ofSeconds(TIMEOUT_S));
 
         if (!foundTrusted[0] || !foundTrusted[1]) {
             CtsWindowInfoUtils.dumpWindowsOnScreen(TAG, mName.getMethodName());
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
index e6648da..0cb22ad 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
@@ -18,6 +18,7 @@
 
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.content.pm.ActivityInfo.INSETS_DECOUPLED_CONFIGURATION_ENFORCED;
 import static android.content.res.Configuration.GRAMMATICAL_GENDER_NOT_SPECIFIED;
 import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
 import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
@@ -86,6 +87,7 @@
 
         ApplicationInfo info = mock(ApplicationInfo.class);
         info.packageName = "test.package.name";
+        doReturn(true).when(info).isChangeEnabled(INSETS_DECOUPLED_CONFIGURATION_ENFORCED);
         mWpc = new WindowProcessController(
                 mAtm, info, null, 0, -1, null, mMockListener);
         mWpc.setThread(mock(IApplicationThread.class));
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 e13376b..b46189c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -472,7 +472,7 @@
         app.setRequestedVisibleTypes(0, statusBars());
         mDisplayContent.getInsetsStateController()
                 .getOrCreateSourceProvider(statusBarId, statusBars())
-                .updateClientVisibility(app);
+                .updateClientVisibility(app, null /* statsToken */);
         waitUntilHandlersIdle();
         assertFalse(statusBar.isVisible());
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/utils/DesktopModeFlagsUtilTest.java b/services/tests/wmtests/src/com/android/server/wm/utils/DesktopModeFlagsUtilTest.java
index e5f2f89..381e9e4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/utils/DesktopModeFlagsUtilTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/utils/DesktopModeFlagsUtilTest.java
@@ -20,7 +20,7 @@
 import static com.android.server.wm.utils.DesktopModeFlagsUtil.ToggleOverride.OVERRIDE_OFF;
 import static com.android.server.wm.utils.DesktopModeFlagsUtil.ToggleOverride.OVERRIDE_ON;
 import static com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE;
-import static com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY;
+import static com.android.window.flags.Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS;
 import static com.android.window.flags.Flags.FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION;
 
 import static com.google.common.truth.Truth.assertThat;
@@ -63,9 +63,6 @@
         resetCache();
     }
 
-    private static final String SYSTEM_PROPERTY_OVERRIDE_KEY =
-            "sys.wmshell.desktopmode.dev_toggle_override";
-
     @Test
     @DisableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION)
     @EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
@@ -190,250 +187,146 @@
     }
 
     @Test
-    @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION)
-    @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
-    public void isEnabled_noProperty_overrideOn_featureFlagOff_returnsTrueAndPropertyOn() {
-        System.clearProperty(SYSTEM_PROPERTY_OVERRIDE_KEY);
-        setOverride(OVERRIDE_ON.getSetting());
-
-        assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isTrue();
-        // Store System Property if not present
-        assertThat(System.getProperty(SYSTEM_PROPERTY_OVERRIDE_KEY))
-                .isEqualTo(String.valueOf(OVERRIDE_ON.getSetting()));
-    }
-
-    @Test
-    @EnableFlags({FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE})
-    public void isEnabled_noProperty_overrideUnset_featureFlagOn_returnsTrueAndPropertyUnset() {
-        System.clearProperty(SYSTEM_PROPERTY_OVERRIDE_KEY);
-        setOverride(DesktopModeFlagsUtil.ToggleOverride.OVERRIDE_UNSET.getSetting());
-
-        assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isTrue();
-        // Store System Property if not present
-        assertThat(System.getProperty(SYSTEM_PROPERTY_OVERRIDE_KEY))
-                .isEqualTo(String.valueOf(
-                        DesktopModeFlagsUtil.ToggleOverride.OVERRIDE_UNSET.getSetting()));
-    }
-
-    @Test
-    @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION)
-    @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
-    public void isEnabled_noProperty_overrideUnset_featureFlagOff_returnsFalseAndPropertyUnset() {
-        System.clearProperty(SYSTEM_PROPERTY_OVERRIDE_KEY);
-        setOverride(DesktopModeFlagsUtil.ToggleOverride.OVERRIDE_UNSET.getSetting());
-
-        assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isFalse();
-        // Store System Property if not present
-        assertThat(System.getProperty(SYSTEM_PROPERTY_OVERRIDE_KEY))
-                .isEqualTo(String.valueOf(
-                        DesktopModeFlagsUtil.ToggleOverride.OVERRIDE_UNSET.getSetting()));
-    }
-
-    @Test
-    @EnableFlags({FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE})
-    public void isEnabled_propertyNotInt_overrideOff_featureFlagOn_returnsFalseAndPropertyOff() {
-        System.setProperty(SYSTEM_PROPERTY_OVERRIDE_KEY, "abc");
-        setOverride(OVERRIDE_OFF.getSetting());
-
-        assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isFalse();
-        // Store System Property if currently invalid
-        assertThat(System.getProperty(SYSTEM_PROPERTY_OVERRIDE_KEY))
-                .isEqualTo(String.valueOf(OVERRIDE_OFF.getSetting()));
-    }
-
-    @Test
-    @EnableFlags({FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE})
-    public void isEnabled_propertyInvalid_overrideOff_featureFlagOn_returnsFalseAndPropertyOff() {
-        System.setProperty(SYSTEM_PROPERTY_OVERRIDE_KEY, "-2");
-        setOverride(OVERRIDE_OFF.getSetting());
-
-        assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isFalse();
-        // Store System Property if currently invalid
-        assertThat(System.getProperty(SYSTEM_PROPERTY_OVERRIDE_KEY))
-                .isEqualTo(String.valueOf(OVERRIDE_OFF.getSetting()));
-    }
-
-    @Test
-    @EnableFlags({FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE})
-    public void isEnabled_propertyOff_overrideOn_featureFlagOn_returnsFalseAndnoPropertyUpdate() {
-        System.setProperty(SYSTEM_PROPERTY_OVERRIDE_KEY, String.valueOf(
-                OVERRIDE_OFF.getSetting()));
-        setOverride(OVERRIDE_ON.getSetting());
-
-        // Have a consistent override until reboot
-        assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isFalse();
-        assertThat(System.getProperty(SYSTEM_PROPERTY_OVERRIDE_KEY))
-                .isEqualTo(String.valueOf(OVERRIDE_OFF.getSetting()));
-    }
-
-    @Test
-    @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION)
-    @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
-    public void isEnabled_propertyOn_overrideOff_featureFlagOff_returnsTrueAndnoPropertyUpdate() {
-        System.setProperty(SYSTEM_PROPERTY_OVERRIDE_KEY, String.valueOf(OVERRIDE_ON.getSetting()));
-        setOverride(OVERRIDE_OFF.getSetting());
-
-        // Have a consistent override until reboot
-        assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isTrue();
-        assertThat(System.getProperty(SYSTEM_PROPERTY_OVERRIDE_KEY))
-                .isEqualTo(String.valueOf(OVERRIDE_ON.getSetting()));
-    }
-
-    @Test
-    @EnableFlags({FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE})
-    public void isEnabled_propertyUnset_overrideOff_featureFlagOn_returnsTrueAndnoPropertyUpdate() {
-        System.setProperty(SYSTEM_PROPERTY_OVERRIDE_KEY,
-                String.valueOf(DesktopModeFlagsUtil.ToggleOverride.OVERRIDE_UNSET.getSetting()));
-        setOverride(OVERRIDE_OFF.getSetting());
-
-        // Have a consistent override until reboot
-        assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isTrue();
-        assertThat(System.getProperty(SYSTEM_PROPERTY_OVERRIDE_KEY))
-                .isEqualTo(String.valueOf(
-                        DesktopModeFlagsUtil.ToggleOverride.OVERRIDE_UNSET.getSetting()));
-    }
-
-    @Test
     @EnableFlags({FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
-            FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY})
+            FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS})
     public void isEnabled_dwFlagOn_overrideUnset_featureFlagOn_returnsTrue() {
         setOverride(DesktopModeFlagsUtil.ToggleOverride.OVERRIDE_UNSET.getSetting());
 
         // For unset overrides, follow flag
-        assertThat(DesktopModeFlagsUtil.WALLPAPER_ACTIVITY.isEnabled(mContext)).isTrue();
+        assertThat(DesktopModeFlagsUtil.DYNAMIC_INITIAL_BOUNDS.isEnabled(mContext)).isTrue();
     }
 
     @Test
     @EnableFlags({FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE})
-    @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+    @DisableFlags(FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
     public void isEnabled_dwFlagOn_overrideUnset_featureFlagOff_returnsFalse() {
         setOverride(DesktopModeFlagsUtil.ToggleOverride.OVERRIDE_UNSET.getSetting());
         // For unset overrides, follow flag
-        assertThat(DesktopModeFlagsUtil.WALLPAPER_ACTIVITY.isEnabled(mContext)).isFalse();
+        assertThat(DesktopModeFlagsUtil.DYNAMIC_INITIAL_BOUNDS.isEnabled(mContext)).isFalse();
     }
 
     @Test
     @EnableFlags({
             FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION,
             FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
-            FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY
+            FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS
     })
     public void isEnabled_dwFlagOn_overrideOn_featureFlagOn_returnsTrue() {
         setOverride(OVERRIDE_ON.getSetting());
 
         // When toggle override matches its default state (dw flag), don't override flags
-        assertThat(DesktopModeFlagsUtil.WALLPAPER_ACTIVITY.isEnabled(mContext)).isTrue();
+        assertThat(DesktopModeFlagsUtil.DYNAMIC_INITIAL_BOUNDS.isEnabled(mContext)).isTrue();
     }
 
     @Test
     @EnableFlags({FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE})
-    @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+    @DisableFlags(FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
     public void isEnabled_dwFlagOn_overrideOn_featureFlagOff_returnsFalse() {
         setOverride(OVERRIDE_ON.getSetting());
 
         // When toggle override matches its default state (dw flag), don't override flags
-        assertThat(DesktopModeFlagsUtil.WALLPAPER_ACTIVITY.isEnabled(mContext)).isFalse();
+        assertThat(DesktopModeFlagsUtil.DYNAMIC_INITIAL_BOUNDS.isEnabled(mContext)).isFalse();
     }
 
     @Test
     @EnableFlags({
             FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION,
             FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
-            FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY
+            FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS
     })
     public void isEnabled_dwFlagOn_overrideOff_featureFlagOn_returnsFalse() {
         setOverride(OVERRIDE_OFF.getSetting());
 
         // Follow override if they exist, and is not equal to default toggle state (dw flag)
-        assertThat(DesktopModeFlagsUtil.WALLPAPER_ACTIVITY.isEnabled(mContext)).isFalse();
+        assertThat(DesktopModeFlagsUtil.DYNAMIC_INITIAL_BOUNDS.isEnabled(mContext)).isFalse();
     }
 
     @Test
     @EnableFlags({FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE})
-    @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+    @DisableFlags(FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
     public void isEnabled_dwFlagOn_overrideOff_featureFlagOff_returnsFalse() {
         setOverride(OVERRIDE_OFF.getSetting());
 
         // Follow override if they exist, and is not equal to default toggle state (dw flag)
-        assertThat(DesktopModeFlagsUtil.WALLPAPER_ACTIVITY.isEnabled(mContext)).isFalse();
+        assertThat(DesktopModeFlagsUtil.DYNAMIC_INITIAL_BOUNDS.isEnabled(mContext)).isFalse();
     }
 
     @Test
     @EnableFlags({
             FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION,
-            FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY
+            FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS
     })
     @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
     public void isEnabled_dwFlagOff_overrideUnset_featureFlagOn_returnsTrue() {
         setOverride(DesktopModeFlagsUtil.ToggleOverride.OVERRIDE_UNSET.getSetting());
 
         // For unset overrides, follow flag
-        assertThat(DesktopModeFlagsUtil.WALLPAPER_ACTIVITY.isEnabled(mContext)).isTrue();
+        assertThat(DesktopModeFlagsUtil.DYNAMIC_INITIAL_BOUNDS.isEnabled(mContext)).isTrue();
     }
 
     @Test
     @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION)
     @DisableFlags({
             FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
-            FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY
+            FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS
     })
     public void isEnabled_dwFlagOff_overrideUnset_featureFlagOff_returnsFalse() {
         setOverride(DesktopModeFlagsUtil.ToggleOverride.OVERRIDE_UNSET.getSetting());
 
         // For unset overrides, follow flag
-        assertThat(DesktopModeFlagsUtil.WALLPAPER_ACTIVITY.isEnabled(mContext)).isFalse();
+        assertThat(DesktopModeFlagsUtil.DYNAMIC_INITIAL_BOUNDS.isEnabled(mContext)).isFalse();
     }
 
     @Test
     @EnableFlags({
             FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION,
-            FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY
+            FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS
     })
     @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
     public void isEnabled_dwFlagOff_overrideOn_featureFlagOn_returnsTrue() {
         setOverride(OVERRIDE_ON.getSetting());
 
         // Follow override if they exist, and is not equal to default toggle state (dw flag)
-        assertThat(DesktopModeFlagsUtil.WALLPAPER_ACTIVITY.isEnabled(mContext)).isTrue();
+        assertThat(DesktopModeFlagsUtil.DYNAMIC_INITIAL_BOUNDS.isEnabled(mContext)).isTrue();
     }
 
     @Test
     @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION)
     @DisableFlags({
             FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
-            FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY
+            FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS
     })
     public void isEnabled_dwFlagOff_overrideOn_featureFlagOff_returnTrue() {
         setOverride(OVERRIDE_ON.getSetting());
 
         // Follow override if they exist, and is not equal to default toggle state (dw flag)
-        assertThat(DesktopModeFlagsUtil.WALLPAPER_ACTIVITY.isEnabled(mContext)).isTrue();
+        assertThat(DesktopModeFlagsUtil.DYNAMIC_INITIAL_BOUNDS.isEnabled(mContext)).isTrue();
     }
 
     @Test
     @EnableFlags({
             FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION,
-            FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY
+            FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS
     })
     @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
     public void isEnabled_dwFlagOff_overrideOff_featureFlagOn_returnsTrue() {
         setOverride(OVERRIDE_OFF.getSetting());
 
         // When toggle override matches its default state (dw flag), don't override flags
-        assertThat(DesktopModeFlagsUtil.WALLPAPER_ACTIVITY.isEnabled(mContext)).isTrue();
+        assertThat(DesktopModeFlagsUtil.DYNAMIC_INITIAL_BOUNDS.isEnabled(mContext)).isTrue();
     }
 
     @Test
     @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION)
     @DisableFlags({
             FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
-            FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY
+            FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS
     })
     public void isEnabled_dwFlagOff_overrideOff_featureFlagOff_returnsFalse() {
         setOverride(OVERRIDE_OFF.getSetting());
 
         // When toggle override matches its default state (dw flag), don't override flags
-        assertThat(DesktopModeFlagsUtil.WALLPAPER_ACTIVITY.isEnabled(mContext)).isFalse();
+        assertThat(DesktopModeFlagsUtil.DYNAMIC_INITIAL_BOUNDS.isEnabled(mContext)).isFalse();
     }
 
     private void setOverride(Integer setting) {
@@ -452,8 +345,5 @@
                 "sCachedToggleOverride");
         cachedToggleOverride.setAccessible(true);
         cachedToggleOverride.set(null, null);
-
-        // Clear override cache stored in System property
-        System.clearProperty(SYSTEM_PROPERTY_OVERRIDE_KEY);
     }
 }
diff --git a/telecomm/java/android/telecom/CallerInfoAsyncQuery.java b/telecomm/java/android/telecom/CallerInfoAsyncQuery.java
index 93cd291..a03d7e2 100644
--- a/telecomm/java/android/telecom/CallerInfoAsyncQuery.java
+++ b/telecomm/java/android/telecom/CallerInfoAsyncQuery.java
@@ -483,18 +483,28 @@
 
         // check to see if these are recognized numbers, and use shortcuts if we can.
         TelephonyManager tm = context.getSystemService(TelephonyManager.class);
+
         boolean isEmergencyNumber = false;
         try {
             isEmergencyNumber = tm.isEmergencyNumber(number);
-        } catch (IllegalStateException ise) {
+        } catch (IllegalStateException | UnsupportedOperationException ise) {
             // Ignore the exception that Telephony is not up. Use PhoneNumberUtils API now.
             // Ideally the PhoneNumberUtils API needs to be removed once the
             // telphony service not up issue can be fixed (b/187412989)
+            // UnsupportedOperationException: telephony.calling may not be supported on this device
             isEmergencyNumber = PhoneNumberUtils.isLocalEmergencyNumber(context, number);
         }
+
+        boolean isVoicemailNumber;
+        try {
+            isVoicemailNumber = PhoneNumberUtils.isVoiceMailNumber(context, subId, number);
+        } catch (UnsupportedOperationException ex) {
+            isVoicemailNumber = false;
+        }
+
         if (isEmergencyNumber) {
             cw.event = EVENT_EMERGENCY_NUMBER;
-        } else if (PhoneNumberUtils.isVoiceMailNumber(context, subId, number)) {
+        } else if (isVoicemailNumber) {
             cw.event = EVENT_VOICEMAIL_NUMBER;
         } else {
             cw.event = EVENT_NEW_QUERY;
diff --git a/telephony/common/com/android/internal/telephony/SmsApplication.java b/telephony/common/com/android/internal/telephony/SmsApplication.java
index 94d4d22..9b83719 100644
--- a/telephony/common/com/android/internal/telephony/SmsApplication.java
+++ b/telephony/common/com/android/internal/telephony/SmsApplication.java
@@ -528,7 +528,7 @@
                 // current SMS app will already be the preferred activity - but checking whether or
                 // not this is true is just as expensive as reconfiguring the preferred activity so
                 // we just reconfigure every time.
-                defaultSmsAppChanged(context);
+                grantPermissionsToSystemApps(context);
             }
         }
         if (DEBUG_MULTIUSER) {
@@ -542,9 +542,9 @@
     }
 
     /**
-     * Grants various permissions and appops on sms app change
+     * Grants various permissions and appops, e.g. on sms app change
      */
-    private static void defaultSmsAppChanged(Context context) {
+    public static void grantPermissionsToSystemApps(Context context) {
         PackageManager packageManager = context.getPackageManager();
         AppOpsManager appOps = context.getSystemService(AppOpsManager.class);
 
@@ -680,7 +680,7 @@
                 return;
             }
 
-            defaultSmsAppChanged(context);
+            grantPermissionsToSystemApps(context);
         }
     }
 
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 61698db..c6959ae 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -51,6 +51,7 @@
 import android.telephony.ims.RcsUceAdapter;
 import android.telephony.ims.feature.MmTelFeature;
 import android.telephony.ims.feature.RcsFeature;
+import android.telephony.satellite.SatelliteManager;
 
 import com.android.internal.telephony.ICarrierConfigLoader;
 import com.android.internal.telephony.flags.Flags;
@@ -9992,6 +9993,80 @@
     @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
     public static final String KEY_SATELLITE_ESOS_SUPPORTED_BOOL = "satellite_esos_supported_bool";
 
+    /** @hide */
+    @IntDef({
+            CARRIER_ROAMING_NTN_CONNECT_AUTOMATIC,
+            CARRIER_ROAMING_NTN_CONNECT_MANUAL,
+    })
+    public @interface CARRIER_ROAMING_NTN_CONNECT_TYPE {}
+
+    /**
+     * Device can connect to carrier roaming non-terrestrial network automatically.
+     * @hide
+     */
+    public static final int CARRIER_ROAMING_NTN_CONNECT_AUTOMATIC = 0;
+    /**
+     * Device can connect to carrier roaming non-terrestrial network only if user manually triggers
+     * satellite connection.
+     * @hide
+     */
+    public static final int CARRIER_ROAMING_NTN_CONNECT_MANUAL = 1;
+    /**
+     * Indicates carrier roaming non-terrestrial network connect type that the device can use to
+     * perform satellite communication.
+     * If this key is set to CARRIER_ROAMING_NTN_CONNECT_MANUAL then connect button will be
+     * displayed to user when the device is eligible to use carrier roaming
+     * non-terrestrial network.
+     * @hide
+     */
+    public static final String KEY_CARRIER_ROAMING_NTN_CONNECT_TYPE_INT =
+            "carrier_roaming_ntn_connect_type_int";
+
+    /**
+     * Indicates carrier roaming non-terrestrial network emergency call handover type that the
+     * device will use to perform a handover between ESOS or T911.
+     * If this key is set to {@link SatelliteManager#EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_SOS}
+     * then the handover will be made to ESOS. If this key is set to
+     * {@link SatelliteManager#EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_T911} then the handover
+     * will be made to T911.
+     *
+     * The default value is {@link SatelliteManager#EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_T911}.
+     *
+     */
+    @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
+    public static final String
+            KEY_CARRIER_ROAMING_NTN_EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_INT =
+            "carrier_roaming_ntn_emergency_call_to_satellite_handover_type_int";
+
+    /**
+     * The carrier roaming non-terrestrial network hysteresis time in seconds.
+     *
+     * If the device supports P2P satellite messaging which is defined by
+     * {@link CarrierConfigManager#KEY_CARRIER_SUPPORTED_SATELLITE_SERVICES_PER_PROVIDER_BUNDLE}
+     * and the device is in {@link ServiceState#STATE_OUT_OF_SERVICE}, not connected to Wi-Fi,
+     * then hysteresis timer defined by this key will start.
+     * After the timer is expired, device is marked as eligible for satellite communication.
+     *
+     * The default value is 180 seconds.
+     *
+     * @hide
+     */
+    public static final String KEY_CARRIER_SUPPORTED_SATELLITE_NOTIFICATION_HYSTERESIS_SEC_INT =
+            "carrier_supported_satellite_notification_hysteresis_sec_int";
+
+    /**
+     * An integer key holds the timeout duration in seconds used to determine whether to exit
+     * carrier-roaming NB-IOT satellite mode.
+     *
+     * The timer is started when the device screen is turned off during a satellite session.
+     * When the timer expires, the device exits Carrier Roaming NB IOT NTN.
+     *
+     * The default value is 30 seconds.
+     */
+    @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
+    public static final String KEY_SATELLITE_SCREEN_OFF_INACTIVITY_TIMEOUT_SEC_INT =
+            "satellite_screen_off_inactivity_timeout_duration_sec_int";
+
     /**
      * Indicating whether DUN APN should be disabled when the device is roaming. In that case,
      * the default APN (i.e. internet) will be used for tethering.
@@ -11150,6 +11225,11 @@
         sDefaults.putInt(KEY_EMERGENCY_CALL_TO_SATELLITE_T911_HANDOVER_TIMEOUT_MILLIS_INT,
                 (int) TimeUnit.SECONDS.toMillis(30));
         sDefaults.putBoolean(KEY_SATELLITE_ESOS_SUPPORTED_BOOL, false);
+        sDefaults.putInt(KEY_CARRIER_ROAMING_NTN_CONNECT_TYPE_INT, 0);
+        sDefaults.putInt(KEY_CARRIER_ROAMING_NTN_EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_INT,
+                SatelliteManager.EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_T911);
+        sDefaults.putInt(KEY_CARRIER_SUPPORTED_SATELLITE_NOTIFICATION_HYSTERESIS_SEC_INT, 180);
+        sDefaults.putInt(KEY_SATELLITE_SCREEN_OFF_INACTIVITY_TIMEOUT_SEC_INT, 30);
         sDefaults.putString(KEY_DEFAULT_PREFERRED_APN_NAME_STRING, "");
         sDefaults.putBoolean(KEY_SUPPORTS_CALL_COMPOSER_BOOL, false);
         sDefaults.putBoolean(KEY_SUPPORTS_BUSINESS_CALL_COMPOSER_BOOL, false);
diff --git a/telephony/java/android/telephony/satellite/ISatelliteModemStateCallback.aidl b/telephony/java/android/telephony/satellite/ISatelliteModemStateCallback.aidl
index 9ff73e2..66a20ae 100644
--- a/telephony/java/android/telephony/satellite/ISatelliteModemStateCallback.aidl
+++ b/telephony/java/android/telephony/satellite/ISatelliteModemStateCallback.aidl
@@ -27,4 +27,11 @@
      * @param state The current satellite modem state.
      */
     void onSatelliteModemStateChanged(in int state);
+
+    /**
+     * Indicates that the satellite emergency mode has changed.
+     *
+     * @param isEmergency True means satellite enabled for emergency mode, false otherwise.
+     */
+    void onEmergencyModeChanged(in boolean isEmergency);
 }
diff --git a/telephony/java/android/telephony/satellite/SatelliteManager.java b/telephony/java/android/telephony/satellite/SatelliteManager.java
index 4b83b65..0bd9270 100644
--- a/telephony/java/android/telephony/satellite/SatelliteManager.java
+++ b/telephony/java/android/telephony/satellite/SatelliteManager.java
@@ -1531,6 +1531,12 @@
                         executor.execute(() -> Binder.withCleanCallingIdentity(() ->
                                 callback.onSatelliteModemStateChanged(state)));
                     }
+
+                    @Override
+                    public void onEmergencyModeChanged(boolean isEmergency) {
+                        executor.execute(() -> Binder.withCleanCallingIdentity(() ->
+                                callback.onEmergencyModeChanged(isEmergency)));
+                    }
                 };
                 sSatelliteModemStateCallbackMap.put(callback, internalCallback);
                 return telephony.registerForSatelliteModemStateChanged(mSubId, internalCallback);
diff --git a/telephony/java/android/telephony/satellite/SatelliteModemStateCallback.java b/telephony/java/android/telephony/satellite/SatelliteModemStateCallback.java
index 8d33c88..423a785 100644
--- a/telephony/java/android/telephony/satellite/SatelliteModemStateCallback.java
+++ b/telephony/java/android/telephony/satellite/SatelliteModemStateCallback.java
@@ -35,4 +35,14 @@
      */
     @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
     void onSatelliteModemStateChanged(@SatelliteManager.SatelliteModemState int state);
+
+    /**
+     * Called when the satellite emergency mode has changed.
+     *
+     * @param isEmergency {@code true} enabled for emergency mode, {@code false} otherwise.
+     *
+     * @hide
+     */
+    @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
+    default void onEmergencyModeChanged(boolean isEmergency) {};
 }
diff --git a/tests/FlickerTests/AppLaunch/OWNERS b/tests/FlickerTests/AppLaunch/OWNERS
index 2c414a2..d16b57d 100644
--- a/tests/FlickerTests/AppLaunch/OWNERS
+++ b/tests/FlickerTests/AppLaunch/OWNERS
@@ -1,4 +1,2 @@
-# System UI > ... > Overview (recent apps) > UI
-# Bug template url: https://b.corp.google.com/issues/new?component=807991&template=1390280 = per-file *Overview*
 # window manager > animations/transitions
 # Bug template url: https://b.corp.google.com/issues/new?component=316275&template=1018192
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_pip.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_pip.xml
index 36cbf1a..365a0ea 100644
--- a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_pip.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_pip.xml
@@ -27,15 +27,6 @@
          where things are arranged differently and to circle back up to the top once we reach the
          bottom. -->
 
-    <!-- View used for testing sourceRectHint. -->
-    <View
-        android:id="@+id/source_rect"
-        android:layout_width="320dp"
-        android:layout_height="180dp"
-        android:visibility="gone"
-        android:background="@android:color/holo_green_light"
-        />
-
     <Button
         android:id="@+id/enter_pip"
         android:layout_width="wrap_content"
@@ -122,12 +113,11 @@
             android:onClick="onRatioSelected"/>
     </RadioGroup>
 
-    <Button
+    <CheckBox
         android:id="@+id/set_source_rect_hint"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:text="Set SourceRectHint"
-        android:onClick="setSourceRectHint"/>
+        android:text="Set SourceRectHint"/>
 
     <TextView
         android:layout_width="wrap_content"
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 27eb5a0..13d7f7f 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
@@ -43,10 +43,10 @@
 import android.media.session.MediaSession;
 import android.media.session.PlaybackState;
 import android.os.Bundle;
+import android.util.DisplayMetrics;
 import android.util.Log;
 import android.util.Rational;
 import android.view.View;
-import android.view.ViewTreeObserver;
 import android.view.Window;
 import android.view.WindowManager;
 import android.widget.CheckBox;
@@ -70,7 +70,7 @@
      */
     private static final String TITLE_STATE_PAUSED = "TestApp media is paused";
 
-    private static final Rational RATIO_DEFAULT = null;
+    private static final Rational RATIO_DEFAULT = new Rational(16, 9);
     private static final Rational RATIO_SQUARE = new Rational(1, 1);
     private static final Rational RATIO_WIDE = new Rational(2, 1);
     private static final Rational RATIO_TALL = new Rational(1, 2);
@@ -88,8 +88,7 @@
             "com.android.wm.shell.flicker.testapp.ASPECT_RATIO";
 
     private final PictureInPictureParams.Builder mPipParamsBuilder =
-            new PictureInPictureParams.Builder()
-                    .setAspectRatio(RATIO_DEFAULT);
+            new PictureInPictureParams.Builder();
     private MediaSession mMediaSession;
     private final PlaybackState.Builder mPlaybackStateBuilder = new PlaybackState.Builder()
             .setActions(ACTION_PLAY | ACTION_PAUSE | ACTION_STOP)
@@ -139,6 +138,9 @@
         }
     };
 
+    private Rational mAspectRatio = RATIO_DEFAULT;
+    private boolean mEnableSourceRectHint;
+
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
@@ -156,6 +158,14 @@
         findViewById(R.id.media_session_stop)
                 .setOnClickListener(v -> updateMediaSessionState(STATE_STOPPED));
 
+        final CheckBox setSourceRectHintCheckBox = findViewById(R.id.set_source_rect_hint);
+        setSourceRectHintCheckBox.setOnCheckedChangeListener((v, isChecked) -> {
+            if (mEnableSourceRectHint != isChecked) {
+                mEnableSourceRectHint = isChecked;
+                updateSourceRectHint();
+            }
+        });
+
         mMediaSession = new MediaSession(this, "WMShell_TestApp");
         mMediaSession.setPlaybackState(mPlaybackStateBuilder.build());
         mMediaSession.setCallback(new MediaSession.Callback() {
@@ -250,47 +260,64 @@
         }
     }
 
+    private void updateSourceRectHint() {
+        if (!mEnableSourceRectHint) return;
+        // Similar to PipUtils#getEnterPipWithOverlaySrcRectHint, crop the display bounds
+        // as source rect hint based on the current aspect ratio.
+        final DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
+        final Rect displayBounds = new Rect(0, 0,
+                displayMetrics.widthPixels, displayMetrics.heightPixels);
+        final Rect sourceRectHint = getEnterPipWithOverlaySrcRectHint(
+                displayBounds, mAspectRatio.floatValue());
+        mPipParamsBuilder
+                .setAspectRatio(mAspectRatio)
+                .setSourceRectHint(sourceRectHint);
+        setPictureInPictureParams(mPipParamsBuilder.build());
+    }
+
     /**
-     * Adds a temporary view used for testing sourceRectHint.
-     *
+     * Crop a Rect matches the aspect ratio and pivots at the center point.
+     * This is a counterpart of {@link PipUtils#getEnterPipWithOverlaySrcRectHint}
      */
-    public void setSourceRectHint(View v) {
-        View rectView = findViewById(R.id.source_rect);
-        if (rectView != null) {
-            rectView.setVisibility(View.VISIBLE);
-            rectView.getViewTreeObserver().addOnGlobalLayoutListener(
-                    new ViewTreeObserver.OnGlobalLayoutListener() {
-                        @Override
-                        public void onGlobalLayout() {
-                            Rect boundingRect = new Rect();
-                            rectView.getGlobalVisibleRect(boundingRect);
-                            mPipParamsBuilder.setSourceRectHint(boundingRect);
-                            setPictureInPictureParams(mPipParamsBuilder.build());
-                            rectView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
-                        }
-                    });
-            rectView.invalidate(); // changing the visibility, invalidating to redraw the view
+    private Rect getEnterPipWithOverlaySrcRectHint(Rect appBounds, float aspectRatio) {
+        final float appBoundsAspectRatio = appBounds.width() / (float) appBounds.height();
+        final int width, height;
+        int left = appBounds.left;
+        int top = appBounds.top;
+        if (appBoundsAspectRatio < aspectRatio) {
+            width = appBounds.width();
+            height = (int) (width / aspectRatio);
+            top = appBounds.top + (appBounds.height() - height) / 2;
+        } else {
+            height = appBounds.height();
+            width = (int) (height * aspectRatio);
+            left = appBounds.left + (appBounds.width() - width) / 2;
         }
+        return new Rect(left, top, left + width, top + height);
     }
 
     public void onRatioSelected(View v) {
         switch (v.getId()) {
             case R.id.ratio_default:
-                mPipParamsBuilder.setAspectRatio(RATIO_DEFAULT);
+                mAspectRatio = RATIO_DEFAULT;
                 break;
 
             case R.id.ratio_square:
-                mPipParamsBuilder.setAspectRatio(RATIO_SQUARE);
+                mAspectRatio = RATIO_SQUARE;
                 break;
 
             case R.id.ratio_wide:
-                mPipParamsBuilder.setAspectRatio(RATIO_WIDE);
+                mAspectRatio = RATIO_WIDE;
                 break;
 
             case R.id.ratio_tall:
-                mPipParamsBuilder.setAspectRatio(RATIO_TALL);
+                mAspectRatio = RATIO_TALL;
                 break;
         }
+        setPictureInPictureParams(mPipParamsBuilder.setAspectRatio(mAspectRatio).build());
+        if (mEnableSourceRectHint) {
+            updateSourceRectHint();
+        }
     }
 
     private void updateMediaSessionState(int newState) {
diff --git a/tests/Input/Android.bp b/tests/Input/Android.bp
index f367c38..06c2651 100644
--- a/tests/Input/Android.bp
+++ b/tests/Input/Android.bp
@@ -48,6 +48,7 @@
         "testables",
         "testng",
         "truth",
+        "ui-trace-collector",
     ],
     libs: [
         "android.test.mock",
diff --git a/tests/Input/AndroidTest.xml b/tests/Input/AndroidTest.xml
index 4a99bd4..bc9322f 100644
--- a/tests/Input/AndroidTest.xml
+++ b/tests/Input/AndroidTest.xml
@@ -22,6 +22,10 @@
         <option name="shell-timeout" value="660s" />
         <option name="test-timeout" value="600s" />
         <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+        <option name="device-listeners" value="android.tools.collectors.DefaultUITraceListener"/>
+        <!-- DefaultUITraceListener args -->
+        <option name="instrumentation-arg" key="skip_test_success_metrics" value="true"/>
+        <option name="instrumentation-arg" key="per_class" value="true"/>
     </test>
     <object class="com.android.tradefed.testtype.suite.module.TestFailureModuleController"
             type="module_controller">
@@ -32,6 +36,8 @@
         <option name="pull-pattern-keys" value="input_.*" />
         <!-- Pull files created by tests, like the output of screenshot tests -->
         <option name="directory-keys" value="/sdcard/Download/InputTests" />
+        <!-- Pull perfetto traces from DefaultUITraceListener -->
+        <option name="pull-pattern-keys" value="perfetto_file_path*" />
         <option name="collect-on-run-ended-only" value="false" />
     </metrics_collector>
 </configuration>
diff --git a/tests/Input/src/com/android/test/input/AnrTest.kt b/tests/Input/src/com/android/test/input/AnrTest.kt
index 8d1fc50..d32cedb 100644
--- a/tests/Input/src/com/android/test/input/AnrTest.kt
+++ b/tests/Input/src/com/android/test/input/AnrTest.kt
@@ -40,7 +40,7 @@
 import com.android.cts.input.DebugInputRule
 import com.android.cts.input.UinputTouchScreen
 
-import java.util.concurrent.TimeUnit
+import java.time.Duration
 
 import org.junit.After
 import org.junit.Assert.assertEquals
@@ -193,6 +193,6 @@
         val flags = " -W -n "
         val startCmd = "am start $flags $PACKAGE_NAME/.UnresponsiveGestureMonitorActivity"
         instrumentation.uiAutomation.executeShellCommand(startCmd)
-        waitForStableWindowGeometry(5L, TimeUnit.SECONDS)
+        waitForStableWindowGeometry(Duration.ofSeconds(5))
     }
 }
diff --git a/tests/Input/src/com/android/test/input/InputEventAssignerTest.kt b/tests/Input/src/com/android/test/input/InputEventAssignerTest.kt
index c1a86b3..015e188 100644
--- a/tests/Input/src/com/android/test/input/InputEventAssignerTest.kt
+++ b/tests/Input/src/com/android/test/input/InputEventAssignerTest.kt
@@ -18,12 +18,20 @@
 
 import android.view.InputDevice.SOURCE_MOUSE
 import android.view.InputDevice.SOURCE_TOUCHSCREEN
+import android.view.InputDevice.SOURCE_STYLUS
+import android.view.InputDevice.SOURCE_TOUCHPAD
+
 import android.view.InputEventAssigner
 import android.view.KeyEvent
 import android.view.MotionEvent
 import org.junit.Assert.assertEquals
 import org.junit.Test
 
+sealed class StreamEvent
+private data object Vsync : StreamEvent()
+data class MotionEventData(val action: Int, val source: Int, val id: Int, val expectedId: Int) :
+    StreamEvent()
+
 /**
  * Create a MotionEvent with the provided action, eventTime, and source
  */
@@ -49,64 +57,164 @@
     return KeyEvent(eventTime, eventTime, action, code, repeat)
 }
 
+/**
+ * Check that the correct eventIds are assigned in a stream. The stream consists of motion
+ * events or vsync (processed frame)
+ * Each streamEvent should have unique ids when writing tests
+ * The test passes even if two events get assigned the same eventId, since the mapping is
+ * streamEventId -> motionEventId and streamEvents have unique ids
+ */
+private fun checkEventStream(vararg streamEvents: StreamEvent) {
+    val assigner = InputEventAssigner()
+    var eventTime = 10L
+    // Maps MotionEventData.id to MotionEvent.id
+    // We can't control the event id of the generated motion events but for testing it's easier
+    // to label the events with a custom id for readability
+    val eventIdMap: HashMap<Int, Int> = HashMap()
+    for (streamEvent in streamEvents) {
+        when (streamEvent) {
+            is MotionEventData -> {
+                val event = createMotionEvent(streamEvent.action, eventTime, streamEvent.source)
+                eventIdMap[streamEvent.id] = event.id
+                val eventId = assigner.processEvent(event)
+                assertEquals(eventIdMap[streamEvent.expectedId], eventId)
+            }
+            is Vsync -> assigner.notifyFrameProcessed()
+        }
+        eventTime += 1
+    }
+}
+
 class InputEventAssignerTest {
     companion object {
         private const val TAG = "InputEventAssignerTest"
     }
 
     /**
-     * A single MOVE event should be assigned to the next available frame.
+     * A single event should be assigned to the next available frame.
      */
     @Test
-    fun testTouchGesture() {
-        val assigner = InputEventAssigner()
-        val event = createMotionEvent(MotionEvent.ACTION_MOVE, 10, SOURCE_TOUCHSCREEN)
-        val eventId = assigner.processEvent(event)
-        assertEquals(event.id, eventId)
+    fun testTouchMove() {
+        checkEventStream(
+            MotionEventData(MotionEvent.ACTION_MOVE, SOURCE_TOUCHSCREEN, id = 1, expectedId = 1)
+        )
+    }
+
+    @Test
+    fun testMouseMove() {
+        checkEventStream(
+            MotionEventData(MotionEvent.ACTION_MOVE, SOURCE_MOUSE, id = 1, expectedId = 1)
+        )
+    }
+
+    @Test
+    fun testMouseScroll() {
+        checkEventStream(
+            MotionEventData(MotionEvent.ACTION_SCROLL, SOURCE_MOUSE, id = 1, expectedId = 1)
+        )
+    }
+
+    @Test
+    fun testStylusMove() {
+        checkEventStream(
+            MotionEventData(MotionEvent.ACTION_MOVE, SOURCE_STYLUS, id = 1, expectedId = 1)
+        )
+    }
+
+    @Test
+    fun testStylusHover() {
+        checkEventStream(
+            MotionEventData(MotionEvent.ACTION_HOVER_MOVE, SOURCE_STYLUS, id = 1, expectedId = 1)
+        )
+    }
+
+    @Test
+    fun testTouchpadMove() {
+        checkEventStream(
+            MotionEventData(MotionEvent.ACTION_MOVE, SOURCE_STYLUS, id = 1, expectedId = 1)
+        )
     }
 
     /**
-     * DOWN event should be used until a vsync comes in. After vsync, the latest event should be
-     * produced.
+     * Test that before a VSYNC the event id generated by input event assigner for move events is
+     * the id of the down event. Move events coming after a VSYNC should be assigned their own event
+     * id
      */
-    @Test
-    fun testTouchDownWithMove() {
-        val assigner = InputEventAssigner()
-        val down = createMotionEvent(MotionEvent.ACTION_DOWN, 10, SOURCE_TOUCHSCREEN)
-        val move1 = createMotionEvent(MotionEvent.ACTION_MOVE, 12, SOURCE_TOUCHSCREEN)
-        val move2 = createMotionEvent(MotionEvent.ACTION_MOVE, 13, SOURCE_TOUCHSCREEN)
-        val move3 = createMotionEvent(MotionEvent.ACTION_MOVE, 14, SOURCE_TOUCHSCREEN)
-        val move4 = createMotionEvent(MotionEvent.ACTION_MOVE, 15, SOURCE_TOUCHSCREEN)
-        var eventId = assigner.processEvent(down)
-        assertEquals(down.id, eventId)
-        eventId = assigner.processEvent(move1)
-        assertEquals(down.id, eventId)
-        eventId = assigner.processEvent(move2)
-        // Even though we already had 2 move events, there was no choreographer callback yet.
-        // Therefore, we should still get the id of the down event
-        assertEquals(down.id, eventId)
+    private fun testDownAndMove(source: Int) {
+        checkEventStream(
+            MotionEventData(MotionEvent.ACTION_DOWN, source, id = 1, expectedId = 1),
+            MotionEventData(MotionEvent.ACTION_MOVE, source, id = 2, expectedId = 1),
+            Vsync,
+            MotionEventData(MotionEvent.ACTION_MOVE, source, id = 4, expectedId = 4)
+        )
+    }
 
-        // Now send CALLBACK_INPUT to the assigner. It should provide the latest motion event
-        assigner.notifyFrameProcessed()
-        eventId = assigner.processEvent(move3)
-        assertEquals(move3.id, eventId)
-        eventId = assigner.processEvent(move4)
-        assertEquals(move4.id, eventId)
+    @Test
+    fun testTouchDownAndMove() {
+        testDownAndMove(SOURCE_TOUCHSCREEN)
+    }
+
+    @Test
+    fun testMouseDownAndMove() {
+        testDownAndMove(SOURCE_MOUSE)
+    }
+
+    @Test
+    fun testStylusDownAndMove() {
+        testDownAndMove(SOURCE_STYLUS)
+    }
+
+    @Test
+    fun testTouchpadDownAndMove() {
+        testDownAndMove(SOURCE_TOUCHPAD)
     }
 
     /**
-     * Similar to the above test, but with SOURCE_MOUSE. Since we don't have down latency
-     * concept for non-touchscreens, the latest input event will be used.
+     * After an up event, motion events should be assigned their own event id
      */
     @Test
-    fun testMouseDownWithMove() {
-        val assigner = InputEventAssigner()
-        val down = createMotionEvent(MotionEvent.ACTION_DOWN, 10, SOURCE_MOUSE)
-        val move1 = createMotionEvent(MotionEvent.ACTION_MOVE, 12, SOURCE_MOUSE)
-        var eventId = assigner.processEvent(down)
-        assertEquals(down.id, eventId)
-        eventId = assigner.processEvent(move1)
-        assertEquals(move1.id, eventId)
+    fun testMouseDownUpAndScroll() {
+        checkEventStream(
+            MotionEventData(MotionEvent.ACTION_DOWN, SOURCE_MOUSE, id = 1, expectedId = 1),
+            MotionEventData(MotionEvent.ACTION_UP, SOURCE_MOUSE, id = 2, expectedId = 2),
+            MotionEventData(MotionEvent.ACTION_SCROLL, SOURCE_MOUSE, id = 3, expectedId = 3)
+        )
+    }
+
+    /**
+     * After an up event, motion events should be assigned their own event id
+     */
+    @Test
+    fun testStylusDownUpAndHover() {
+        checkEventStream(
+            MotionEventData(MotionEvent.ACTION_DOWN, SOURCE_STYLUS, id = 1, expectedId = 1),
+            MotionEventData(MotionEvent.ACTION_UP, SOURCE_STYLUS, id = 2, expectedId = 2),
+            MotionEventData(MotionEvent.ACTION_HOVER_ENTER, SOURCE_STYLUS, id = 3, expectedId = 3)
+        )
+    }
+
+    /**
+     * After a cancel event, motion events should be assigned their own event id
+     */
+    @Test
+    fun testMouseDownCancelAndScroll() {
+        checkEventStream(
+            MotionEventData(MotionEvent.ACTION_DOWN, SOURCE_MOUSE, id = 1, expectedId = 1),
+            MotionEventData(MotionEvent.ACTION_CANCEL, SOURCE_MOUSE, id = 2, expectedId = 2),
+            MotionEventData(MotionEvent.ACTION_SCROLL, SOURCE_MOUSE, id = 3, expectedId = 3)
+        )
+    }
+
+    /**
+     * After a cancel event, motion events should be assigned their own event id
+     */
+    @Test
+    fun testStylusDownCancelAndHover() {
+        checkEventStream(
+            MotionEventData(MotionEvent.ACTION_DOWN, SOURCE_STYLUS, id = 1, expectedId = 1),
+            MotionEventData(MotionEvent.ACTION_CANCEL, SOURCE_STYLUS, id = 2, expectedId = 2),
+            MotionEventData(MotionEvent.ACTION_HOVER_ENTER, SOURCE_STYLUS, id = 3, expectedId = 3)
+        )
     }
 
     /**
diff --git a/tests/Internal/src/com/android/internal/protolog/PerfettoProtoLogImplTest.java b/tests/Internal/src/com/android/internal/protolog/PerfettoProtoLogImplTest.java
index b6672a0..fad94d4 100644
--- a/tests/Internal/src/com/android/internal/protolog/PerfettoProtoLogImplTest.java
+++ b/tests/Internal/src/com/android/internal/protolog/PerfettoProtoLogImplTest.java
@@ -698,7 +698,7 @@
             traceMonitor.start();
 
             mProtoLog.log(LogLevel.DEBUG, TestProtoLogGroup.TEST_GROUP,
-                    "My test null string: %s", null);
+                    "My test null string: %s", (Object) null);
         } finally {
             traceMonitor.stop(mWriter);
         }
diff --git a/tests/PackageWatchdog/src/com/android/server/CrashRecoveryTest.java b/tests/PackageWatchdog/src/com/android/server/CrashRecoveryTest.java
index 3722fef..c0e90f9 100644
--- a/tests/PackageWatchdog/src/com/android/server/CrashRecoveryTest.java
+++ b/tests/PackageWatchdog/src/com/android/server/CrashRecoveryTest.java
@@ -35,6 +35,7 @@
 
 import android.Manifest;
 import android.content.Context;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.VersionedPackage;
@@ -77,6 +78,7 @@
 import java.io.File;
 import java.lang.reflect.Field;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
@@ -413,6 +415,311 @@
         verify(rescuePartyObserver, never()).executeBootLoopMitigation(2);
     }
 
+    @Test
+    @DisableFlags(Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS)
+    public void testCrashLoopWithRescuePartyAndRollbackObserver() throws Exception {
+        PackageWatchdog watchdog = createWatchdog();
+        RescuePartyObserver rescuePartyObserver = setUpRescuePartyObserver(watchdog);
+        RollbackPackageHealthObserver rollbackObserver =
+                setUpRollbackPackageHealthObserver(watchdog);
+        VersionedPackage versionedPackageA = new VersionedPackage(APP_A, VERSION_CODE);
+
+        when(mMockPackageManager.getApplicationInfo(anyString(), anyInt())).then(inv -> {
+            ApplicationInfo info = new ApplicationInfo();
+            info.flags |= ApplicationInfo.FLAG_PERSISTENT
+                    | ApplicationInfo.FLAG_SYSTEM;
+            return info;
+        });
+
+        raiseFatalFailureAndDispatch(watchdog,
+                Arrays.asList(versionedPackageA), PackageWatchdog.FAILURE_REASON_APP_CRASH);
+
+        // Mitigation: SCOPED_DEVICE_CONFIG_RESET
+        verify(rescuePartyObserver).execute(versionedPackageA,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 1);
+        verify(rescuePartyObserver, never()).execute(versionedPackageA,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 2);
+        verify(rollbackObserver, never()).execute(versionedPackageA,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 1);
+
+        raiseFatalFailureAndDispatch(watchdog,
+                Arrays.asList(versionedPackageA), PackageWatchdog.FAILURE_REASON_APP_CRASH);
+
+        // Mitigation: ALL_DEVICE_CONFIG_RESET
+        verify(rescuePartyObserver).execute(versionedPackageA,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 2);
+        verify(rescuePartyObserver, never()).execute(versionedPackageA,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 3);
+        verify(rollbackObserver, never()).execute(versionedPackageA,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 1);
+
+        raiseFatalFailureAndDispatch(watchdog,
+                Arrays.asList(versionedPackageA), PackageWatchdog.FAILURE_REASON_APP_CRASH);
+
+        // Mitigation: WARM_REBOOT
+        verify(rescuePartyObserver).execute(versionedPackageA,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 3);
+        verify(rescuePartyObserver, never()).execute(versionedPackageA,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 4);
+        verify(rollbackObserver, never()).execute(versionedPackageA,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 1);
+
+        raiseFatalFailureAndDispatch(watchdog,
+                Arrays.asList(versionedPackageA), PackageWatchdog.FAILURE_REASON_APP_CRASH);
+
+        // Mitigation: Low impact rollback
+        verify(rollbackObserver).execute(versionedPackageA,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 1);
+        verify(rescuePartyObserver, never()).execute(versionedPackageA,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 4);
+
+        // update available rollbacks to mock rollbacks being applied after the call to
+        // rollbackObserver.execute
+        when(mRollbackManager.getAvailableRollbacks()).thenReturn(
+                List.of(ROLLBACK_INFO_HIGH, ROLLBACK_INFO_MANUAL));
+
+        raiseFatalFailureAndDispatch(watchdog,
+                Arrays.asList(versionedPackageA), PackageWatchdog.FAILURE_REASON_APP_CRASH);
+
+        // DEFAULT_MAJOR_USER_IMPACT_LEVEL_THRESHOLD reached. No more mitigations applied
+        verify(rescuePartyObserver, never()).execute(versionedPackageA,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 4);
+        verify(rollbackObserver, never()).execute(versionedPackageA,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 2);
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS)
+    public void testCrashLoopWithRescuePartyAndRollbackObserverEnableDeprecateFlagReset()
+            throws Exception {
+        PackageWatchdog watchdog = createWatchdog();
+        RescuePartyObserver rescuePartyObserver = setUpRescuePartyObserver(watchdog);
+        RollbackPackageHealthObserver rollbackObserver =
+                setUpRollbackPackageHealthObserver(watchdog);
+        VersionedPackage versionedPackageA = new VersionedPackage(APP_A, VERSION_CODE);
+
+        when(mMockPackageManager.getApplicationInfo(anyString(), anyInt())).then(inv -> {
+            ApplicationInfo info = new ApplicationInfo();
+            info.flags |= ApplicationInfo.FLAG_PERSISTENT
+                    | ApplicationInfo.FLAG_SYSTEM;
+            return info;
+        });
+
+
+        raiseFatalFailureAndDispatch(watchdog,
+                Arrays.asList(versionedPackageA), PackageWatchdog.FAILURE_REASON_APP_CRASH);
+
+        // Mitigation: WARM_REBOOT
+        verify(rescuePartyObserver).execute(versionedPackageA,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 1);
+        verify(rescuePartyObserver, never()).execute(versionedPackageA,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 2);
+        verify(rollbackObserver, never()).execute(versionedPackageA,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 1);
+
+        raiseFatalFailureAndDispatch(watchdog,
+                Arrays.asList(versionedPackageA), PackageWatchdog.FAILURE_REASON_APP_CRASH);
+
+        // Mitigation: Low impact rollback
+        verify(rollbackObserver).execute(versionedPackageA,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 1);
+        verify(rescuePartyObserver, never()).execute(versionedPackageA,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 2);
+
+        // update available rollbacks to mock rollbacks being applied after the call to
+        // rollbackObserver.execute
+        when(mRollbackManager.getAvailableRollbacks()).thenReturn(
+                List.of(ROLLBACK_INFO_HIGH, ROLLBACK_INFO_MANUAL));
+
+        raiseFatalFailureAndDispatch(watchdog,
+                Arrays.asList(versionedPackageA), PackageWatchdog.FAILURE_REASON_APP_CRASH);
+
+        // DEFAULT_MAJOR_USER_IMPACT_LEVEL_THRESHOLD reached. No more mitigations applied
+        verify(rescuePartyObserver, never()).execute(versionedPackageA,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 2);
+        verify(rollbackObserver, never()).execute(versionedPackageA,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 2);
+    }
+
+    @Test
+    @DisableFlags(Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS)
+    public void testCrashLoopSystemUIWithRescuePartyAndRollbackObserver() throws Exception {
+        PackageWatchdog watchdog = createWatchdog();
+        RescuePartyObserver rescuePartyObserver = setUpRescuePartyObserver(watchdog);
+        RollbackPackageHealthObserver rollbackObserver =
+                setUpRollbackPackageHealthObserver(watchdog);
+        String systemUi = "com.android.systemui";
+        VersionedPackage versionedPackageUi = new VersionedPackage(
+                systemUi, VERSION_CODE);
+        RollbackInfo rollbackInfoUi = getRollbackInfo(systemUi, VERSION_CODE, 1,
+                PackageManager.ROLLBACK_USER_IMPACT_LOW);
+        when(mRollbackManager.getAvailableRollbacks()).thenReturn(List.of(ROLLBACK_INFO_LOW,
+                ROLLBACK_INFO_HIGH, ROLLBACK_INFO_MANUAL, rollbackInfoUi));
+
+        when(mMockPackageManager.getApplicationInfo(anyString(), anyInt())).then(inv -> {
+            ApplicationInfo info = new ApplicationInfo();
+            info.flags |= ApplicationInfo.FLAG_PERSISTENT
+                    | ApplicationInfo.FLAG_SYSTEM;
+            return info;
+        });
+
+        raiseFatalFailureAndDispatch(watchdog,
+                Arrays.asList(versionedPackageUi), PackageWatchdog.FAILURE_REASON_APP_CRASH);
+
+        // Mitigation: SCOPED_DEVICE_CONFIG_RESET
+        verify(rescuePartyObserver).execute(versionedPackageUi,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 1);
+        verify(rescuePartyObserver, never()).execute(versionedPackageUi,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 2);
+        verify(rollbackObserver, never()).execute(versionedPackageUi,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 1);
+
+        raiseFatalFailureAndDispatch(watchdog,
+                Arrays.asList(versionedPackageUi), PackageWatchdog.FAILURE_REASON_APP_CRASH);
+
+        // Mitigation: ALL_DEVICE_CONFIG_RESET
+        verify(rescuePartyObserver).execute(versionedPackageUi,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 2);
+        verify(rescuePartyObserver, never()).execute(versionedPackageUi,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 3);
+        verify(rollbackObserver, never()).execute(versionedPackageUi,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 1);
+
+        raiseFatalFailureAndDispatch(watchdog,
+                Arrays.asList(versionedPackageUi), PackageWatchdog.FAILURE_REASON_APP_CRASH);
+
+        // Mitigation: WARM_REBOOT
+        verify(rescuePartyObserver).execute(versionedPackageUi,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 3);
+        verify(rescuePartyObserver, never()).execute(versionedPackageUi,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 4);
+        verify(rollbackObserver, never()).execute(versionedPackageUi,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 1);
+
+        raiseFatalFailureAndDispatch(watchdog,
+                Arrays.asList(versionedPackageUi), PackageWatchdog.FAILURE_REASON_APP_CRASH);
+
+        // Mitigation: Low impact rollback
+        verify(rollbackObserver).execute(versionedPackageUi,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 1);
+        verify(rescuePartyObserver, never()).execute(versionedPackageUi,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 4);
+        verify(rollbackObserver, never()).execute(versionedPackageUi,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 2);
+
+        // update available rollbacks to mock rollbacks being applied after the call to
+        // rollbackObserver.execute
+        when(mRollbackManager.getAvailableRollbacks()).thenReturn(
+                List.of(ROLLBACK_INFO_HIGH, ROLLBACK_INFO_MANUAL));
+
+        raiseFatalFailureAndDispatch(watchdog,
+                Arrays.asList(versionedPackageUi), PackageWatchdog.FAILURE_REASON_APP_CRASH);
+
+        // Mitigation: RESET_SETTINGS_UNTRUSTED_DEFAULTS
+        verify(rescuePartyObserver).execute(versionedPackageUi,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 4);
+        verify(rescuePartyObserver, never()).execute(versionedPackageUi,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 5);
+        verify(rollbackObserver, never()).execute(versionedPackageUi,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 2);
+
+        raiseFatalFailureAndDispatch(watchdog,
+                Arrays.asList(versionedPackageUi), PackageWatchdog.FAILURE_REASON_APP_CRASH);
+
+        // Mitigation: RESET_SETTINGS_UNTRUSTED_CHANGES
+        verify(rescuePartyObserver).execute(versionedPackageUi,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 5);
+        verify(rescuePartyObserver, never()).execute(versionedPackageUi,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 6);
+        verify(rollbackObserver, never()).execute(versionedPackageUi,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 2);
+
+        raiseFatalFailureAndDispatch(watchdog,
+                Arrays.asList(versionedPackageUi), PackageWatchdog.FAILURE_REASON_APP_CRASH);
+
+        // Mitigation: RESET_SETTINGS_TRUSTED_DEFAULTS
+        verify(rescuePartyObserver).execute(versionedPackageUi,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 6);
+        verify(rescuePartyObserver, never()).execute(versionedPackageUi,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 7);
+        verify(rollbackObserver, never()).execute(versionedPackageUi,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 2);
+
+        raiseFatalFailureAndDispatch(watchdog,
+                Arrays.asList(versionedPackageUi), PackageWatchdog.FAILURE_REASON_APP_CRASH);
+
+        // Mitigation: Factory reset. High impact rollbacks are performed only for boot loops.
+        verify(rescuePartyObserver).execute(versionedPackageUi,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 7);
+        verify(rescuePartyObserver, never()).execute(versionedPackageUi,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 8);
+        verify(rollbackObserver, never()).execute(versionedPackageUi,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 2);
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS)
+    public void testCrashLoopSystemUIWithRescuePartyAndRollbackObserverEnableDeprecateFlagReset()
+            throws Exception {
+        PackageWatchdog watchdog = createWatchdog();
+        RescuePartyObserver rescuePartyObserver = setUpRescuePartyObserver(watchdog);
+        RollbackPackageHealthObserver rollbackObserver =
+                setUpRollbackPackageHealthObserver(watchdog);
+        String systemUi = "com.android.systemui";
+        VersionedPackage versionedPackageUi = new VersionedPackage(
+                systemUi, VERSION_CODE);
+        RollbackInfo rollbackInfoUi = getRollbackInfo(systemUi, VERSION_CODE, 1,
+                PackageManager.ROLLBACK_USER_IMPACT_LOW);
+        when(mRollbackManager.getAvailableRollbacks()).thenReturn(List.of(ROLLBACK_INFO_LOW,
+                ROLLBACK_INFO_HIGH, ROLLBACK_INFO_MANUAL, rollbackInfoUi));
+
+        when(mMockPackageManager.getApplicationInfo(anyString(), anyInt())).then(inv -> {
+            ApplicationInfo info = new ApplicationInfo();
+            info.flags |= ApplicationInfo.FLAG_PERSISTENT
+                    | ApplicationInfo.FLAG_SYSTEM;
+            return info;
+        });
+
+
+        raiseFatalFailureAndDispatch(watchdog,
+                Arrays.asList(versionedPackageUi), PackageWatchdog.FAILURE_REASON_APP_CRASH);
+
+        // Mitigation: WARM_REBOOT
+        verify(rescuePartyObserver).execute(versionedPackageUi,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 1);
+        verify(rescuePartyObserver, never()).execute(versionedPackageUi,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 2);
+        verify(rollbackObserver, never()).execute(versionedPackageUi,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 1);
+
+        raiseFatalFailureAndDispatch(watchdog,
+                Arrays.asList(versionedPackageUi), PackageWatchdog.FAILURE_REASON_APP_CRASH);
+
+        // Mitigation: Low impact rollback
+        verify(rollbackObserver).execute(versionedPackageUi,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 1);
+        verify(rescuePartyObserver, never()).execute(versionedPackageUi,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 2);
+        verify(rollbackObserver, never()).execute(versionedPackageUi,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 2);
+
+        // update available rollbacks to mock rollbacks being applied after the call to
+        // rollbackObserver.execute
+        when(mRollbackManager.getAvailableRollbacks()).thenReturn(
+                List.of(ROLLBACK_INFO_HIGH, ROLLBACK_INFO_MANUAL));
+
+        raiseFatalFailureAndDispatch(watchdog,
+                Arrays.asList(versionedPackageUi), PackageWatchdog.FAILURE_REASON_APP_CRASH);
+
+        // Mitigation: Factory reset. High impact rollbacks are performed only for boot loops.
+        verify(rescuePartyObserver).execute(versionedPackageUi,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 2);
+        verify(rescuePartyObserver, never()).execute(versionedPackageUi,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 3);
+        verify(rollbackObserver, never()).execute(versionedPackageUi,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 2);
+    }
+
     RollbackPackageHealthObserver setUpRollbackPackageHealthObserver(PackageWatchdog watchdog) {
         RollbackPackageHealthObserver rollbackObserver =
                 spy(new RollbackPackageHealthObserver(mSpyContext, mApexManager));
@@ -424,7 +731,6 @@
         watchdog.registerHealthObserver(rollbackObserver);
         return rollbackObserver;
     }
-
     RescuePartyObserver setUpRescuePartyObserver(PackageWatchdog watchdog) {
         setCrashRecoveryPropRescueBootCount(0);
         RescuePartyObserver rescuePartyObserver = spy(RescuePartyObserver.getInstance(mSpyContext));
@@ -686,4 +992,20 @@
         mTestLooper.moveTimeForward(milliSeconds);
         mTestLooper.dispatchAll();
     }
+
+    private void raiseFatalFailureAndDispatch(PackageWatchdog watchdog,
+            List<VersionedPackage> packages, int failureReason) {
+        long triggerFailureCount = watchdog.getTriggerFailureCount();
+        if (failureReason == PackageWatchdog.FAILURE_REASON_EXPLICIT_HEALTH_CHECK
+                || failureReason == PackageWatchdog.FAILURE_REASON_NATIVE_CRASH) {
+            triggerFailureCount = 1;
+        }
+        for (int i = 0; i < triggerFailureCount; i++) {
+            watchdog.onPackageFailure(packages, failureReason);
+        }
+        mTestLooper.dispatchAll();
+        if (Flags.recoverabilityDetection()) {
+            moveTimeForwardAndDispatch(watchdog.DEFAULT_MITIGATION_WINDOW_MS);
+        }
+    }
 }
diff --git a/tests/inputmethod/ConcurrentMultiSessionImeTest/Android.bp b/tests/inputmethod/ConcurrentMultiSessionImeTest/Android.bp
index 4c531b8..a4085e5 100644
--- a/tests/inputmethod/ConcurrentMultiSessionImeTest/Android.bp
+++ b/tests/inputmethod/ConcurrentMultiSessionImeTest/Android.bp
@@ -23,6 +23,7 @@
     resource_dirs: ["res"],
     libs: ["android.test.runner"],
     static_libs: [
+        "androidx.core_core",
         "androidx.test.ext.junit",
         "androidx.test.rules",
         "compatibility-device-util-axt",
diff --git a/tools/aapt2/Android.bp b/tools/aapt2/Android.bp
index 4c81939..3f9016b 100644
--- a/tools/aapt2/Android.bp
+++ b/tools/aapt2/Android.bp
@@ -55,7 +55,10 @@
             cflags: ["-D_DARWIN_UNLIMITED_STREAMS"],
         },
     },
-    header_libs: ["jni_headers"],
+    header_libs: [
+        "jni_headers",
+        "native_headers",
+    ],
     static_libs: [
         "libandroidfw",
         "libutils",
diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp
index 2df9418..9444dd9 100644
--- a/tools/aapt2/ResourceParser.cpp
+++ b/tools/aapt2/ResourceParser.cpp
@@ -107,7 +107,7 @@
   Visibility::Level visibility_level = Visibility::Level::kUndefined;
   bool staged_api = false;
   bool allow_new = false;
-  FlagStatus flag_status;
+  FlagStatus flag_status = FlagStatus::NoFlag;
   std::optional<OverlayableItem> overlayable_item;
   std::optional<StagedId> staged_alias;
 
@@ -690,7 +690,9 @@
         resource_format = item_iter->second.format;
       }
 
-      if (!ParseItem(parser, out_resource, resource_format)) {
+      // Don't bother parsing the item if it is behind a disabled flag
+      if (out_resource->flag_status != FlagStatus::Disabled &&
+          !ParseItem(parser, out_resource, resource_format)) {
         return false;
       }
       return true;
diff --git a/tools/aapt2/ResourceParser_test.cpp b/tools/aapt2/ResourceParser_test.cpp
index b59b165..2e6ad13 100644
--- a/tools/aapt2/ResourceParser_test.cpp
+++ b/tools/aapt2/ResourceParser_test.cpp
@@ -69,8 +69,13 @@
     return TestParse(str, ConfigDescription{});
   }
 
-  ::testing::AssertionResult TestParse(StringPiece str, const ConfigDescription& config) {
-    ResourceParserOptions parserOptions;
+  ::testing::AssertionResult TestParse(StringPiece str, ResourceParserOptions parserOptions) {
+    return TestParse(str, ConfigDescription{}, parserOptions);
+  }
+
+  ::testing::AssertionResult TestParse(
+      StringPiece str, const ConfigDescription& config,
+      ResourceParserOptions parserOptions = ResourceParserOptions()) {
     ResourceParser parser(context_->GetDiagnostics(), &table_, android::Source{"test"}, config,
                           parserOptions);
 
@@ -242,6 +247,19 @@
   EXPECT_FALSE(TestParse(R"(<string name="foo4" translatable="yes">Translate</string>)"));
 }
 
+TEST_F(ResourceParserTest, ParseStringBehindDisabledFlag) {
+  FeatureFlagProperties flag_properties(true, false);
+  ResourceParserOptions options;
+  options.feature_flag_values = {{"falseFlag", flag_properties}};
+  ASSERT_TRUE(TestParse(
+      R"(<string name="foo" android:featureFlag="falseFlag"
+              xmlns:android="http://schemas.android.com/apk/res/android">foo</string>)",
+      options));
+
+  String* str = test::GetValue<String>(&table_, "string/foo");
+  ASSERT_THAT(str, IsNull());
+}
+
 TEST_F(ResourceParserTest, IgnoreXliffTagsOtherThanG) {
   std::string input = R"(
       <string name="foo" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
diff --git a/tools/aapt2/ResourceTable.h b/tools/aapt2/ResourceTable.h
index 9530c17..4f76e7d 100644
--- a/tools/aapt2/ResourceTable.h
+++ b/tools/aapt2/ResourceTable.h
@@ -104,7 +104,7 @@
   // The actual Value.
   std::unique_ptr<Value> value;
 
-  FlagStatus flag_status;
+  FlagStatus flag_status = FlagStatus::NoFlag;
 
   ResourceConfigValue(const android::ConfigDescription& config, android::StringPiece product)
       : config(config), product(product) {
@@ -271,7 +271,7 @@
   std::optional<AllowNew> allow_new;
   std::optional<StagedId> staged_id;
   bool allow_mangled = false;
-  FlagStatus flag_status;
+  FlagStatus flag_status = FlagStatus::NoFlag;
 };
 
 struct NewResourceBuilder {
diff --git a/tools/aapt2/link/TableMerger.cpp b/tools/aapt2/link/TableMerger.cpp
index 67a4828..1942fc11 100644
--- a/tools/aapt2/link/TableMerger.cpp
+++ b/tools/aapt2/link/TableMerger.cpp
@@ -212,7 +212,11 @@
     collision_result =
         ResolveMergeCollision(override_styles_instead_of_overlaying, dst_value, src_value, pool);
   } else {
-    collision_result = ResourceTable::ResolveValueCollision(dst_value, src_value);
+    collision_result = ResourceTable::ResolveFlagCollision(dst_config_value->flag_status,
+                                                           src_config_value->flag_status);
+    if (collision_result == CollisionResult::kConflict) {
+      collision_result = ResourceTable::ResolveValueCollision(dst_value, src_value);
+    }
   }
 
   if (collision_result == CollisionResult::kConflict) {
@@ -291,6 +295,7 @@
         } else {
           dst_config_value =
               dst_entry->FindOrCreateValue(src_config_value->config, src_config_value->product);
+          dst_config_value->flag_status = src_config_value->flag_status;
         }
 
         // Continue if we're taking the new resource.
diff --git a/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/AppInfoTest.java b/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/AppInfoTest.java
index 7806061..b4a7663 100644
--- a/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/AppInfoTest.java
+++ b/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/AppInfoTest.java
@@ -86,11 +86,12 @@
     @Test
     public void testAllFieldsValidV1() throws Exception {
         System.out.println("starting testAllFieldsValidV1.");
-        new AppInfoFactory()
-                .createFromOdElement(
-                        TestUtils.getElementFromResource(
-                                Paths.get(APP_INFO_OD_PATH, ALL_FIELDS_VALID_V1_FILE_NAME)),
-                        1L);
+        var unused =
+                new AppInfoFactory()
+                        .createFromOdElement(
+                                TestUtils.getElementFromResource(
+                                        Paths.get(APP_INFO_OD_PATH, ALL_FIELDS_VALID_V1_FILE_NAME)),
+                                1L);
     }
 
     /** Test for unrecognized field v1. */
@@ -133,7 +134,7 @@
                     TestUtils.getElementFromResource(
                             Paths.get(APP_INFO_OD_PATH, ALL_FIELDS_VALID_V1_FILE_NAME));
             TestUtils.removeOdChildEleWithName(appInfoEle, optField);
-            new AppInfoFactory().createFromOdElement(appInfoEle, 1L);
+            var unused = new AppInfoFactory().createFromOdElement(appInfoEle, 1L);
         }
     }
 
@@ -202,7 +203,7 @@
                             Paths.get(APP_INFO_HR_PATH, ALL_FIELDS_VALID_FILE_NAME));
             ele.removeAttribute(optField);
             AppInfo appInfo = new AppInfoFactory().createFromHrElement(ele, DEFAULT_VERSION);
-            appInfo.toOdDomElement(TestUtils.document());
+            var unused = appInfo.toOdDomElement(TestUtils.document());
         }
 
         for (String optField : OPTIONAL_FIELD_NAMES_OD) {
@@ -211,7 +212,7 @@
                             Paths.get(APP_INFO_OD_PATH, ALL_FIELDS_VALID_FILE_NAME));
             TestUtils.removeOdChildEleWithName(ele, optField);
             AppInfo appInfo = new AppInfoFactory().createFromOdElement(ele, DEFAULT_VERSION);
-            appInfo.toHrDomElement(TestUtils.document());
+            var unused = appInfo.toHrDomElement(TestUtils.document());
         }
     }
 
diff --git a/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/DeveloperInfoTest.java b/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/DeveloperInfoTest.java
index a4472b1..2746800 100644
--- a/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/DeveloperInfoTest.java
+++ b/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/DeveloperInfoTest.java
@@ -99,7 +99,7 @@
             developerInfoEle.removeAttribute(optField);
             DeveloperInfo developerInfo =
                     new DeveloperInfoFactory().createFromHrElement(developerInfoEle);
-            developerInfo.toOdDomElement(TestUtils.document());
+            var unused = developerInfo.toOdDomElement(TestUtils.document());
         }
 
         for (String optField : OPTIONAL_FIELD_NAMES_OD) {
@@ -109,7 +109,7 @@
             TestUtils.removeOdChildEleWithName(developerInfoEle, optField);
             DeveloperInfo developerInfo =
                     new DeveloperInfoFactory().createFromOdElement(developerInfoEle);
-            developerInfo.toHrDomElement(TestUtils.document());
+            var unused = developerInfo.toHrDomElement(TestUtils.document());
         }
     }
 
diff --git a/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/SecurityLabelsTest.java b/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/SecurityLabelsTest.java
index 9d197a2..27f8720 100644
--- a/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/SecurityLabelsTest.java
+++ b/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/SecurityLabelsTest.java
@@ -64,7 +64,7 @@
                             Paths.get(SECURITY_LABELS_HR_PATH, ALL_FIELDS_VALID_FILE_NAME));
             ele.removeAttribute(optField);
             SecurityLabels securityLabels = new SecurityLabelsFactory().createFromHrElement(ele);
-            securityLabels.toOdDomElement(TestUtils.document());
+            var unused = securityLabels.toOdDomElement(TestUtils.document());
         }
         for (String optField : OPTIONAL_FIELD_NAMES_OD) {
             var ele =
@@ -72,7 +72,7 @@
                             Paths.get(SECURITY_LABELS_OD_PATH, ALL_FIELDS_VALID_FILE_NAME));
             TestUtils.removeOdChildEleWithName(ele, optField);
             SecurityLabels securityLabels = new SecurityLabelsFactory().createFromOdElement(ele);
-            securityLabels.toHrDomElement(TestUtils.document());
+            var unused = securityLabels.toHrDomElement(TestUtils.document());
         }
     }
 
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/PackageFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/PackageFilter.kt
new file mode 100644
index 0000000..c67e671
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/PackageFilter.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.hoststubgen.filters
+
+import com.android.hoststubgen.asm.toHumanReadableClassName
+import com.android.hoststubgen.utils.Trie
+
+/**
+ * Filter to apply a policy to classes inside a package, either directly or indirectly.
+ */
+class PackageFilter(
+    fallback: OutputFilter
+) : DelegatingFilter(fallback) {
+
+    private val mPackagePolicies = PackagePolicyTrie()
+
+    // We want to pick the most specific filter for a package name.
+    // Since any package with a matching prefix is a valid match, we can use a prefix tree
+    // to help us find the nearest matching filter.
+    private class PackagePolicyTrie : Trie<String, String, FilterPolicyWithReason>() {
+        // Split package name into individual component
+        override fun splitToComponents(key: String): Iterator<String> {
+            return key.split('.').iterator()
+        }
+    }
+
+    private fun getPackageKey(packageName: String): String {
+        return packageName.toHumanReadableClassName()
+    }
+
+    private fun getPackageKeyFromClass(className: String): String {
+        val clazz = className.toHumanReadableClassName()
+        val idx = clazz.lastIndexOf('.')
+        return if (idx >= 0) clazz.substring(0, idx) else ""
+    }
+
+    /**
+     * Add a policy to all classes inside a package, either directly or indirectly.
+     */
+    fun addPolicy(packageName: String, policy: FilterPolicyWithReason) {
+        mPackagePolicies[getPackageKey(packageName)] = policy
+    }
+
+    override fun getPolicyForClass(className: String): FilterPolicyWithReason {
+        return mPackagePolicies[getPackageKeyFromClass(className)]
+            ?: super.getPolicyForClass(className)
+    }
+}
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt
index c5acd81..a89824e 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt
@@ -64,7 +64,8 @@
     log.i("Loading offloaded annotations from $filename ...")
     log.withIndent {
         val subclassFilter = SubclassFilter(classes, fallback)
-        val imf = InMemoryOutputFilter(classes, subclassFilter)
+        val packageFilter = PackageFilter(subclassFilter)
+        val imf = InMemoryOutputFilter(classes, packageFilter)
 
         var lineNo = 0
 
@@ -78,10 +79,7 @@
                 var className = ""
 
                 while (true) {
-                    var line = reader.readLine()
-                    if (line == null) {
-                        break
-                    }
+                    var line = reader.readLine() ?: break
                     lineNo++
 
                     line = normalizeTextLine(line)
@@ -95,6 +93,31 @@
 
                     val fields = line.split(whitespaceRegex).toTypedArray()
                     when (fields[0].lowercase()) {
+                        "p", "package" -> {
+                            if (fields.size < 3) {
+                                throw ParseException("Package ('p') expects 2 fields.")
+                            }
+                            val name = fields[1]
+                            val rawPolicy = fields[2]
+                            if (resolveExtendingClass(name) != null) {
+                                throw ParseException("Package can't be a super class type")
+                            }
+                            if (resolveSpecialClass(name) != SpecialClass.NotSpecial) {
+                                throw ParseException("Package can't be a special class type")
+                            }
+                            if (rawPolicy.startsWith("!")) {
+                                throw ParseException("Package can't have a substitution")
+                            }
+                            if (rawPolicy.startsWith("~")) {
+                                throw ParseException("Package can't have a class load hook")
+                            }
+                            val policy = parsePolicy(rawPolicy)
+                            if (!policy.isUsableWithClasses) {
+                                throw ParseException("Package can't have policy '$policy'")
+                            }
+                            packageFilter.addPolicy(name, policy.withReason(FILTER_REASON))
+                        }
+
                         "c", "class" -> {
                             if (fields.size < 3) {
                                 throw ParseException("Class ('c') expects 2 fields.")
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/utils/Trie.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/utils/Trie.kt
new file mode 100644
index 0000000..1b3d79c
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/utils/Trie.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.hoststubgen.utils
+
+abstract class Trie<Key, Component, Value> {
+
+    private val root = TrieNode<Component, Value>()
+
+    abstract fun splitToComponents(key: Key): Iterator<Component>
+
+    operator fun set(key: Key, value: Value) {
+        val node = root.getExactNode(splitToComponents(key))
+        node.value = value
+    }
+
+    operator fun get(key: Key): Value? {
+        return root.getNearestValue(null, splitToComponents(key))
+    }
+
+    private class TrieNode<Component, Value> {
+        private val children = mutableMapOf<Component, TrieNode<Component, Value>>()
+        var value: Value? = null
+
+        fun getExactNode(components: Iterator<Component>): TrieNode<Component, Value> {
+            val n = components.next()
+            val child = children.getOrPut(n) { TrieNode() }
+            return if (components.hasNext()) {
+                child.getExactNode(components)
+            } else {
+                child
+            }
+        }
+
+        fun getNearestValue(current: Value?, components: Iterator<Component>): Value? {
+            val n = components.next()
+            val child = children[n] ?: return current
+            val newValue = child.value ?: current
+            return if (components.hasNext()) {
+                child.getNearestValue(newValue, components)
+            } else {
+                newValue
+            }
+        }
+    }
+}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt
index dd63892..3ef1175 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt
@@ -2706,6 +2706,98 @@
 RuntimeInvisibleAnnotations:
   x: #x()
     android.hosttest.annotation.HostSideTestWholeClassStub
+## Class: com/android/hoststubgen/test/tinyframework/packagetest/A.class
+  Compiled from "A.java"
+public class com.android.hoststubgen.test.tinyframework.packagetest.A
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/packagetest/A
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 0, methods: 1, attributes: 1
+  public com.android.hoststubgen.test.tinyframework.packagetest.A();
+    descriptor: ()V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=1, locals=1, args_size=1
+         x: aload_0
+         x: invokespecial #x                  // Method java/lang/Object."<init>":()V
+         x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/packagetest/A;
+}
+SourceFile: "A.java"
+## Class: com/android/hoststubgen/test/tinyframework/packagetest/B.class
+  Compiled from "B.java"
+public class com.android.hoststubgen.test.tinyframework.packagetest.B
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/packagetest/B
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 0, methods: 1, attributes: 1
+  public com.android.hoststubgen.test.tinyframework.packagetest.B();
+    descriptor: ()V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=1, locals=1, args_size=1
+         x: aload_0
+         x: invokespecial #x                  // Method java/lang/Object."<init>":()V
+         x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/packagetest/B;
+}
+SourceFile: "B.java"
+## Class: com/android/hoststubgen/test/tinyframework/packagetest/sub/A.class
+  Compiled from "A.java"
+public class com.android.hoststubgen.test.tinyframework.packagetest.sub.A
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/packagetest/sub/A
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 0, methods: 1, attributes: 1
+  public com.android.hoststubgen.test.tinyframework.packagetest.sub.A();
+    descriptor: ()V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=1, locals=1, args_size=1
+         x: aload_0
+         x: invokespecial #x                  // Method java/lang/Object."<init>":()V
+         x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/packagetest/sub/A;
+}
+SourceFile: "A.java"
+## Class: com/android/hoststubgen/test/tinyframework/packagetest/sub/B.class
+  Compiled from "B.java"
+public class com.android.hoststubgen.test.tinyframework.packagetest.sub.B
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/packagetest/sub/B
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 0, methods: 1, attributes: 1
+  public com.android.hoststubgen.test.tinyframework.packagetest.sub.B();
+    descriptor: ()V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=1, locals=1, args_size=1
+         x: aload_0
+         x: invokespecial #x                  // Method java/lang/Object."<init>":()V
+         x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/packagetest/sub/B;
+}
+SourceFile: "B.java"
 ## Class: com/android/hoststubgen/test/tinyframework/subclasstest/C1.class
   Compiled from "C1.java"
 public class com.android.hoststubgen.test.tinyframework.subclasstest.C1
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt
index 906a81c..0bbb418 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt
@@ -2177,6 +2177,38 @@
 RuntimeInvisibleAnnotations:
   x: #x()
     android.hosttest.annotation.HostSideTestWholeClassStub
+## Class: com/android/hoststubgen/test/tinyframework/packagetest/A.class
+  Compiled from "A.java"
+public class com.android.hoststubgen.test.tinyframework.packagetest.A
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/packagetest/A
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "A.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/packagetest/sub/A.class
+  Compiled from "A.java"
+public class com.android.hoststubgen.test.tinyframework.packagetest.sub.A
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/packagetest/sub/A
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "A.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
 ## Class: com/android/hoststubgen/test/tinyframework/subclasstest/C1.class
   Compiled from "C1.java"
 public class com.android.hoststubgen.test.tinyframework.subclasstest.C1
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt
index 10bc91d..57f3783 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt
@@ -3540,6 +3540,38 @@
 RuntimeInvisibleAnnotations:
   x: #x()
     android.hosttest.annotation.HostSideTestWholeClassStub
+## Class: com/android/hoststubgen/test/tinyframework/packagetest/A.class
+  Compiled from "A.java"
+public class com.android.hoststubgen.test.tinyframework.packagetest.A
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/packagetest/A
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "A.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/packagetest/sub/A.class
+  Compiled from "A.java"
+public class com.android.hoststubgen.test.tinyframework.packagetest.sub.A
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/packagetest/sub/A
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "A.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
 ## Class: com/android/hoststubgen/test/tinyframework/subclasstest/C1.class
   Compiled from "C1.java"
 public class com.android.hoststubgen.test.tinyframework.subclasstest.C1
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt
index 906a81c..0bbb418 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt
@@ -2177,6 +2177,38 @@
 RuntimeInvisibleAnnotations:
   x: #x()
     android.hosttest.annotation.HostSideTestWholeClassStub
+## Class: com/android/hoststubgen/test/tinyframework/packagetest/A.class
+  Compiled from "A.java"
+public class com.android.hoststubgen.test.tinyframework.packagetest.A
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/packagetest/A
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "A.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/packagetest/sub/A.class
+  Compiled from "A.java"
+public class com.android.hoststubgen.test.tinyframework.packagetest.sub.A
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/packagetest/sub/A
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "A.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
 ## Class: com/android/hoststubgen/test/tinyframework/subclasstest/C1.class
   Compiled from "C1.java"
 public class com.android.hoststubgen.test.tinyframework.subclasstest.C1
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt
index fcf9a8c..91104de 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt
@@ -4408,6 +4408,56 @@
 RuntimeInvisibleAnnotations:
   x: #x()
     android.hosttest.annotation.HostSideTestWholeClassStub
+## Class: com/android/hoststubgen/test/tinyframework/packagetest/A.class
+  Compiled from "A.java"
+public class com.android.hoststubgen.test.tinyframework.packagetest.A
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/packagetest/A
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 0, methods: 1, attributes: 2
+  private static {};
+    descriptor: ()V
+    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+    Code:
+      stack=2, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/packagetest/A
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+         x: return
+}
+SourceFile: "A.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/packagetest/sub/A.class
+  Compiled from "A.java"
+public class com.android.hoststubgen.test.tinyframework.packagetest.sub.A
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/packagetest/sub/A
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 0, methods: 1, attributes: 2
+  private static {};
+    descriptor: ()V
+    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+    Code:
+      stack=2, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/packagetest/sub/A
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+         x: return
+}
+SourceFile: "A.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
 ## Class: com/android/hoststubgen/test/tinyframework/subclasstest/C1.class
   Compiled from "C1.java"
 public class com.android.hoststubgen.test.tinyframework.subclasstest.C1
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt
index 696b6d0..530de43 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt
@@ -40,3 +40,11 @@
 
 class *com.android.hoststubgen.test.tinyframework.subclasstest.I1 keep
 class *com.android.hoststubgen.test.tinyframework.subclasstest.IA remove
+
+# Test package directive
+package com.android.hoststubgen.test.tinyframework.packagetest stub
+class com.android.hoststubgen.test.tinyframework.packagetest.B remove
+class com.android.hoststubgen.test.tinyframework.packagetest.sub.B remove
+# The following rules are the same as above
+# class com.android.hoststubgen.test.tinyframework.packagetest.A stub
+# class com.android.hoststubgen.test.tinyframework.packagetest.sub.A stub
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/education/GestureType.kt b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/A.java
similarity index 79%
rename from packages/SystemUI/shared/src/com/android/systemui/shared/education/GestureType.kt
rename to tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/A.java
index 9a5c77a..6a52e44 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/education/GestureType.kt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/A.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2024 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -13,9 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package com.android.hoststubgen.test.tinyframework.packagetest;
 
-package com.android.systemui.shared.education
-
-enum class GestureType {
-    BACK_GESTURE,
+public class A {
 }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/education/GestureType.kt b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/B.java
similarity index 79%
copy from packages/SystemUI/shared/src/com/android/systemui/shared/education/GestureType.kt
copy to tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/B.java
index 9a5c77a..1374a28 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/education/GestureType.kt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/B.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2024 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -13,9 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package com.android.hoststubgen.test.tinyframework.packagetest;
 
-package com.android.systemui.shared.education
-
-enum class GestureType {
-    BACK_GESTURE,
+public class B {
 }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/education/GestureType.kt b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/sub/A.java
similarity index 79%
copy from packages/SystemUI/shared/src/com/android/systemui/shared/education/GestureType.kt
copy to tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/sub/A.java
index 9a5c77a..361a7fd 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/education/GestureType.kt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/sub/A.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2024 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -13,9 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package com.android.hoststubgen.test.tinyframework.packagetest.sub;
 
-package com.android.systemui.shared.education
-
-enum class GestureType {
-    BACK_GESTURE,
+public class A {
 }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/education/GestureType.kt b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/sub/B.java
similarity index 79%
copy from packages/SystemUI/shared/src/com/android/systemui/shared/education/GestureType.kt
copy to tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/sub/B.java
index 9a5c77a..716595a 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/education/GestureType.kt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/sub/B.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2024 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -13,9 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package com.android.hoststubgen.test.tinyframework.packagetest.sub;
 
-package com.android.systemui.shared.education
-
-enum class GestureType {
-    BACK_GESTURE,
+public class B {
 }
diff --git a/tools/hoststubgen/hoststubgen/test/com/android/hoststubgen/utils/TrieTest.kt b/tools/hoststubgen/hoststubgen/test/com/android/hoststubgen/utils/TrieTest.kt
new file mode 100644
index 0000000..081d039
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test/com/android/hoststubgen/utils/TrieTest.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.hoststubgen.utils
+
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNull
+import org.junit.Test
+
+class TrieTest {
+
+    private class TestTrie : Trie<String, Char, Int>() {
+        override fun splitToComponents(key: String): Iterator<Char> {
+            return key.toCharArray().iterator()
+        }
+    }
+
+    @Test
+    fun testPrefixTree() {
+        val trie = TestTrie()
+        trie["ab"] = 1
+        trie["abc"] = 2
+        trie["ab123"] = 3
+        assertNull(trie["a"])
+        assertNull(trie["x"])
+        assertNull(trie["a1"])
+        assertEquals(1, trie["ab"])
+        assertEquals(2, trie["abc"])
+        assertEquals(2, trie["abcd"])
+        assertEquals(1, trie["ab1"])
+        assertEquals(1, trie["ab12"])
+        assertEquals(3, trie["ab123"])
+        assertEquals(1, trie["ab@"])
+    }
+}
diff --git a/wifi/java/src/android/net/wifi/WifiMigration.java b/wifi/java/src/android/net/wifi/WifiMigration.java
index 1a20a12..6ea20ec 100644
--- a/wifi/java/src/android/net/wifi/WifiMigration.java
+++ b/wifi/java/src/android/net/wifi/WifiMigration.java
@@ -574,7 +574,7 @@
      *
      * @hide
      */
-    @FlaggedApi(Flags.FLAG_LEGACY_KEYSTORE_TO_WIFI_BLOBSTORE_MIGRATION)
+    @FlaggedApi(Flags.FLAG_LEGACY_KEYSTORE_TO_WIFI_BLOBSTORE_MIGRATION_READ_ONLY)
     @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
     public static void migrateLegacyKeystoreToWifiBlobstore() {
         final long identity = Binder.clearCallingIdentity();
diff --git a/wifi/wifi.aconfig b/wifi/wifi.aconfig
index f7162f6..90d13e6 100644
--- a/wifi/wifi.aconfig
+++ b/wifi/wifi.aconfig
@@ -19,11 +19,12 @@
 }
 
 flag {
-    name: "legacy_keystore_to_wifi_blobstore_migration"
+    name: "legacy_keystore_to_wifi_blobstore_migration_read_only"
     is_exported: true
     namespace: "wifi"
     description: "Add API to migrate all values from Legacy Keystore to the new Wifi Blobstore database"
     bug: "332560152"
+    is_fixed_read_only: true
 }
 
 flag {
@@ -35,3 +36,14 @@
         purpose: PURPOSE_BUGFIX
     }
 }
+
+flag {
+    name: "hotspot_network_connecting_state_for_details_page"
+    namespace: "wifi"
+    description: "Update getConnectedState in HotspotNetworkEntry so that details page displays correctly."
+    bug: "321096462"
+    is_fixed_read_only: true
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}